Source Code
Latest 25 from a total of 112 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Harvest | 11272288 | 1890 days ago | IN | 0 ETH | 0.02801384 | ||||
| Harvest | 11270836 | 1890 days ago | IN | 0 ETH | 0.02703993 | ||||
| Harvest | 11269504 | 1891 days ago | IN | 0 ETH | 0.03513687 | ||||
| Harvest | 11268173 | 1891 days ago | IN | 0 ETH | 0.02514 | ||||
| Harvest | 11266964 | 1891 days ago | IN | 0 ETH | 0.01419596 | ||||
| Harvest | 11265632 | 1891 days ago | IN | 0 ETH | 0.01757595 | ||||
| Harvest | 11262793 | 1892 days ago | IN | 0 ETH | 0.01813581 | ||||
| Harvest | 11261461 | 1892 days ago | IN | 0 ETH | 0.01313971 | ||||
| Harvest | 11260135 | 1892 days ago | IN | 0 ETH | 0.01419596 | ||||
| Harvest | 11259526 | 1892 days ago | IN | 0 ETH | 0.01013997 | ||||
| Harvest | 11258921 | 1892 days ago | IN | 0 ETH | 0.00811198 | ||||
| Harvest | 11258194 | 1892 days ago | IN | 0 ETH | 0.00844717 | ||||
| Harvest | 11257590 | 1892 days ago | IN | 0 ETH | 0.01188216 | ||||
| Harvest | 11256990 | 1893 days ago | IN | 0 ETH | 0.01493956 | ||||
| Harvest | 11256380 | 1893 days ago | IN | 0 ETH | 0.01827887 | ||||
| Harvest | 11255776 | 1893 days ago | IN | 0 ETH | 0.01171229 | ||||
| Harvest | 11255073 | 1893 days ago | IN | 0 ETH | 0.00878798 | ||||
| Harvest | 11254323 | 1893 days ago | IN | 0 ETH | 0.00953157 | ||||
| Harvest | 11253660 | 1893 days ago | IN | 0 ETH | 0.01377916 | ||||
| Harvest | 11253055 | 1893 days ago | IN | 0 ETH | 0.02183211 | ||||
| Harvest | 11252329 | 1893 days ago | IN | 0 ETH | 0.01852421 | ||||
| Harvest | 11251724 | 1893 days ago | IN | 0 ETH | 0.01487196 | ||||
| Harvest | 11250997 | 1893 days ago | IN | 0 ETH | 0.02636393 | ||||
| Harvest | 11250271 | 1894 days ago | IN | 0 ETH | 0.04822708 | ||||
| Harvest | 11249545 | 1894 days ago | IN | 0 ETH | 0.03440211 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
StrategyUniEthDaiLpV4
Compiler Version
v0.6.7+commit.b8d736ae
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "../strategy-uni-farm-base.sol";
contract StrategyUniEthDaiLpV4 is StrategyUniFarmBase {
// Token addresses
address public uni_rewards = 0xa1484C3aa22a66C62b77E0AE78E15258bd0cB711;
address public uni_eth_dai_lp = 0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11;
address public dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
constructor(
address _governance,
address _strategist,
address _controller,
address _timelock
)
public
StrategyUniFarmBase(
dai,
uni_rewards,
uni_eth_dai_lp,
_governance,
_strategist,
_controller,
_timelock
)
{}
// **** Views ****
function getName() external override pure returns (string memory) {
return "StrategyUniEthDaiLpV4";
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "./lib/erc20.sol";
import "./interfaces/uniswapv2.sol";
contract PickleSwap {
using SafeERC20 for IERC20;
UniswapRouterV2 router = UniswapRouterV2(
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
);
address public constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
function convertWETHPair(
address fromLP,
address toLP,
uint256 value
) public {
IUniswapV2Pair fromPair = IUniswapV2Pair(fromLP);
IUniswapV2Pair toPair = IUniswapV2Pair(toLP);
// Only for WETH/<TOKEN> pairs
if (!(fromPair.token0() == weth || fromPair.token1() == weth)) {
revert("!eth-from");
}
if (!(toPair.token0() == weth || toPair.token1() == weth)) {
revert("!eth-to");
}
// Get non-eth token from pairs
address _from = fromPair.token0() != weth
? fromPair.token0()
: fromPair.token1();
address _to = toPair.token0() != weth
? toPair.token0()
: toPair.token1();
// Transfer
IERC20(fromLP).safeTransferFrom(msg.sender, address(this), value);
// Remove liquidity
IERC20(fromLP).safeApprove(address(router), 0);
IERC20(fromLP).safeApprove(address(router), value);
router.removeLiquidity(
fromPair.token0(),
fromPair.token1(),
value,
0,
0,
address(this),
now + 60
);
// Convert to target token
address[] memory path = new address[](3);
path[0] = _from;
path[1] = weth;
path[2] = _to;
IERC20(_from).safeApprove(address(router), 0);
IERC20(_from).safeApprove(address(router), uint256(-1));
router.swapExactTokensForTokens(
IERC20(_from).balanceOf(address(this)),
0,
path,
address(this),
now + 60
);
// Supply liquidity
IERC20(weth).safeApprove(address(router), 0);
IERC20(weth).safeApprove(address(router), uint256(-1));
IERC20(_to).safeApprove(address(router), 0);
IERC20(_to).safeApprove(address(router), uint256(-1));
router.addLiquidity(
weth,
_to,
IERC20(weth).balanceOf(address(this)),
IERC20(_to).balanceOf(address(this)),
0,
0,
msg.sender,
now + 60
);
// Refund sender any remaining tokens
IERC20(weth).safeTransfer(
msg.sender,
IERC20(weth).balanceOf(address(this))
);
IERC20(_to).safeTransfer(msg.sender, IERC20(_to).balanceOf(address(this)));
}
}pragma solidity ^0.6.0;
import "./careful-math.sol";
/**
* @title Exponential module for storing fixed-precision decimals
* @author Compound
* @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
* Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
* `Exp({mantissa: 5100000000000000000})`.
*/
contract Exponential is CarefulMath {
uint constant expScale = 1e18;
uint constant doubleScale = 1e36;
uint constant halfExpScale = expScale/2;
uint constant mantissaOne = expScale;
struct Exp {
uint mantissa;
}
struct Double {
uint mantissa;
}
/**
* @dev Creates an exponential from numerator and denominator values.
* Note: Returns an error if (`num` * 10e18) > MAX_INT,
* or if `denom` is zero.
*/
function getExp(uint num, uint denom) pure internal returns (MathError, Exp memory) {
(MathError err0, uint scaledNumerator) = mulUInt(num, expScale);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
(MathError err1, uint rational) = divUInt(scaledNumerator, denom);
if (err1 != MathError.NO_ERROR) {
return (err1, Exp({mantissa: 0}));
}
return (MathError.NO_ERROR, Exp({mantissa: rational}));
}
/**
* @dev Adds two exponentials, returning a new exponential.
*/
function addExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
(MathError error, uint result) = addUInt(a.mantissa, b.mantissa);
return (error, Exp({mantissa: result}));
}
/**
* @dev Subtracts two exponentials, returning a new exponential.
*/
function subExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
(MathError error, uint result) = subUInt(a.mantissa, b.mantissa);
return (error, Exp({mantissa: result}));
}
/**
* @dev Multiply an Exp by a scalar, returning a new Exp.
*/
function mulScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) {
(MathError err0, uint scaledMantissa) = mulUInt(a.mantissa, scalar);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
return (MathError.NO_ERROR, Exp({mantissa: scaledMantissa}));
}
/**
* @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
*/
function mulScalarTruncate(Exp memory a, uint scalar) pure internal returns (MathError, uint) {
(MathError err, Exp memory product) = mulScalar(a, scalar);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return (MathError.NO_ERROR, truncate(product));
}
/**
* @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
*/
function mulScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (MathError, uint) {
(MathError err, Exp memory product) = mulScalar(a, scalar);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return addUInt(truncate(product), addend);
}
/**
* @dev Divide an Exp by a scalar, returning a new Exp.
*/
function divScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) {
(MathError err0, uint descaledMantissa) = divUInt(a.mantissa, scalar);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
return (MathError.NO_ERROR, Exp({mantissa: descaledMantissa}));
}
/**
* @dev Divide a scalar by an Exp, returning a new Exp.
*/
function divScalarByExp(uint scalar, Exp memory divisor) pure internal returns (MathError, Exp memory) {
/*
We are doing this as:
getExp(mulUInt(expScale, scalar), divisor.mantissa)
How it works:
Exp = a / b;
Scalar = s;
`s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale`
*/
(MathError err0, uint numerator) = mulUInt(expScale, scalar);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
return getExp(numerator, divisor.mantissa);
}
/**
* @dev Divide a scalar by an Exp, then truncate to return an unsigned integer.
*/
function divScalarByExpTruncate(uint scalar, Exp memory divisor) pure internal returns (MathError, uint) {
(MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return (MathError.NO_ERROR, truncate(fraction));
}
/**
* @dev Multiplies two exponentials, returning a new exponential.
*/
function mulExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
(MathError err0, uint doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
// We add half the scale before dividing so that we get rounding instead of truncation.
// See "Listing 6" and text above it at https://accu.org/index.php/journals/1717
// Without this change, a result like 6.6...e-19 will be truncated to 0 instead of being rounded to 1e-18.
(MathError err1, uint doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct);
if (err1 != MathError.NO_ERROR) {
return (err1, Exp({mantissa: 0}));
}
(MathError err2, uint product) = divUInt(doubleScaledProductWithHalfScale, expScale);
// The only error `div` can return is MathError.DIVISION_BY_ZERO but we control `expScale` and it is not zero.
assert(err2 == MathError.NO_ERROR);
return (MathError.NO_ERROR, Exp({mantissa: product}));
}
/**
* @dev Multiplies two exponentials given their mantissas, returning a new exponential.
*/
function mulExp(uint a, uint b) pure internal returns (MathError, Exp memory) {
return mulExp(Exp({mantissa: a}), Exp({mantissa: b}));
}
/**
* @dev Multiplies three exponentials, returning a new exponential.
*/
function mulExp3(Exp memory a, Exp memory b, Exp memory c) pure internal returns (MathError, Exp memory) {
(MathError err, Exp memory ab) = mulExp(a, b);
if (err != MathError.NO_ERROR) {
return (err, ab);
}
return mulExp(ab, c);
}
/**
* @dev Divides two exponentials, returning a new exponential.
* (a/scale) / (b/scale) = (a/scale) * (scale/b) = a/b,
* which we can scale as an Exp by calling getExp(a.mantissa, b.mantissa)
*/
function divExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
return getExp(a.mantissa, b.mantissa);
}
/**
* @dev Truncates the given exp to a whole number value.
* For example, truncate(Exp{mantissa: 15 * expScale}) = 15
*/
function truncate(Exp memory exp) pure internal returns (uint) {
// Note: We are not using careful math here as we're performing a division that cannot fail
return exp.mantissa / expScale;
}
/**
* @dev Checks if first Exp is less than second Exp.
*/
function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa < right.mantissa;
}
/**
* @dev Checks if left Exp <= right Exp.
*/
function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa <= right.mantissa;
}
/**
* @dev Checks if left Exp > right Exp.
*/
function greaterThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa > right.mantissa;
}
/**
* @dev returns true if Exp is exactly zero
*/
function isZeroExp(Exp memory value) pure internal returns (bool) {
return value.mantissa == 0;
}
function safe224(uint n, string memory errorMessage) pure internal returns (uint224) {
require(n < 2**224, errorMessage);
return uint224(n);
}
function safe32(uint n, string memory errorMessage) pure internal returns (uint32) {
require(n < 2**32, errorMessage);
return uint32(n);
}
function add_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: add_(a.mantissa, b.mantissa)});
}
function add_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: add_(a.mantissa, b.mantissa)});
}
function add_(uint a, uint b) pure internal returns (uint) {
return add_(a, b, "addition overflow");
}
function add_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
uint c = a + b;
require(c >= a, errorMessage);
return c;
}
function sub_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: sub_(a.mantissa, b.mantissa)});
}
function sub_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: sub_(a.mantissa, b.mantissa)});
}
function sub_(uint a, uint b) pure internal returns (uint) {
return sub_(a, b, "subtraction underflow");
}
function sub_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
require(b <= a, errorMessage);
return a - b;
}
function mul_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale});
}
function mul_(Exp memory a, uint b) pure internal returns (Exp memory) {
return Exp({mantissa: mul_(a.mantissa, b)});
}
function mul_(uint a, Exp memory b) pure internal returns (uint) {
return mul_(a, b.mantissa) / expScale;
}
function mul_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale});
}
function mul_(Double memory a, uint b) pure internal returns (Double memory) {
return Double({mantissa: mul_(a.mantissa, b)});
}
function mul_(uint a, Double memory b) pure internal returns (uint) {
return mul_(a, b.mantissa) / doubleScale;
}
function mul_(uint a, uint b) pure internal returns (uint) {
return mul_(a, b, "multiplication overflow");
}
function mul_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
if (a == 0 || b == 0) {
return 0;
}
uint c = a * b;
require(c / a == b, errorMessage);
return c;
}
function div_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)});
}
function div_(Exp memory a, uint b) pure internal returns (Exp memory) {
return Exp({mantissa: div_(a.mantissa, b)});
}
function div_(uint a, Exp memory b) pure internal returns (uint) {
return div_(mul_(a, expScale), b.mantissa);
}
function div_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)});
}
function div_(Double memory a, uint b) pure internal returns (Double memory) {
return Double({mantissa: div_(a.mantissa, b)});
}
function div_(uint a, Double memory b) pure internal returns (uint) {
return div_(mul_(a, doubleScale), b.mantissa);
}
function div_(uint a, uint b) pure internal returns (uint) {
return div_(a, b, "divide by zero");
}
function div_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
require(b > 0, errorMessage);
return a / b;
}
function fraction(uint a, uint b) pure internal returns (Double memory) {
return Double({mantissa: div_(mul_(a, doubleScale), b)});
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
* (`UintSet`) are supported.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) { // Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(value)));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(value)));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(value)));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint256(_at(set._inner, index)));
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}pragma solidity ^0.6.7;
// https://docs.synthetix.io/contracts/Owned
contract Owned {
address public owner;
address public nominatedOwner;
constructor(address _owner) public {
require(_owner != address(0), "Owner address cannot be 0");
owner = _owner;
emit OwnerChanged(address(0), _owner);
}
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
function acceptOwnership() external {
require(
msg.sender == nominatedOwner,
"You must be nominated before you can accept ownership"
);
emit OwnerChanged(owner, nominatedOwner);
owner = nominatedOwner;
nominatedOwner = address(0);
}
modifier onlyOwner {
_onlyOwner();
_;
}
function _onlyOwner() private view {
require(
msg.sender == owner,
"Only the contract owner may perform this action"
);
}
event OwnerNominated(address newOwner);
event OwnerChanged(address oldOwner, address newOwner);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "./context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}// File: contracts/GSN/Context.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "./safe-math.sol";
import "./context.sol";
// File: contracts/token/ERC20/IERC20.sol
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: contracts/utils/Address.sol
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// File: contracts/token/ERC20/ERC20.sol
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20 {
using SafeMath for uint256;
using Address for address;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
* a default value of 18.
*
* To select a different value for {decimals}, use {_setupDecimals}.
*
* All three of these values are immutable: they can only be set once during
* construction.
*/
constructor (string memory name, string memory symbol) public {
_name = name;
_symbol = symbol;
_decimals = 18;
}
/**
* @dev Returns the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
* called.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view returns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Sets {decimals} to a value other than the default one of 18.
*
* WARNING: This function should only be called from the constructor. Most
* applications that interact with token contracts will not expect
* {decimals} to ever change, and may work incorrectly if it does.
*/
function _setupDecimals(uint8 decimals_) internal {
_decimals = decimals_;
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be to transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}pragma solidity ^0.6.7;
// Inheritance
import "./owned.sol";
// https://docs.synthetix.io/contracts/Pausable
abstract contract Pausable is Owned {
uint256 public lastPauseTime;
bool public paused;
constructor() internal {
// This contract is abstract, and thus cannot be instantiated directly
require(owner != address(0), "Owner must be set");
// Paused will be false, and lastPauseTime will be 0 upon initialisation
}
/**
* @notice Change the paused state of the contract
* @dev Only the contract owner may call this.
*/
function setPaused(bool _paused) external onlyOwner {
// Ensure we're actually changing the state before we do anything
if (_paused == paused) {
return;
}
// Set our paused state.
paused = _paused;
// If applicable, set the last pause time.
if (paused) {
lastPauseTime = now;
}
// Let everyone know that our pause state has changed.
emit PauseChanged(paused);
}
event PauseChanged(bool isPaused);
modifier notPaused {
require(
!paused,
"This action cannot be performed while the contract is paused"
);
_;
}
}pragma solidity ^0.6.0;
/**
* @title Careful Math
* @author Compound
* @notice Derived from OpenZeppelin's SafeMath library
* https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol
*/
contract CarefulMath {
/**
* @dev Possible error codes that we can return
*/
enum MathError {
NO_ERROR,
DIVISION_BY_ZERO,
INTEGER_OVERFLOW,
INTEGER_UNDERFLOW
}
/**
* @dev Multiplies two numbers, returns an error on overflow.
*/
function mulUInt(uint a, uint b) internal pure returns (MathError, uint) {
if (a == 0) {
return (MathError.NO_ERROR, 0);
}
uint c = a * b;
if (c / a != b) {
return (MathError.INTEGER_OVERFLOW, 0);
} else {
return (MathError.NO_ERROR, c);
}
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function divUInt(uint a, uint b) internal pure returns (MathError, uint) {
if (b == 0) {
return (MathError.DIVISION_BY_ZERO, 0);
}
return (MathError.NO_ERROR, a / b);
}
/**
* @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend).
*/
function subUInt(uint a, uint b) internal pure returns (MathError, uint) {
if (b <= a) {
return (MathError.NO_ERROR, a - b);
} else {
return (MathError.INTEGER_UNDERFLOW, 0);
}
}
/**
* @dev Adds two numbers, returns an error on overflow.
*/
function addUInt(uint a, uint b) internal pure returns (MathError, uint) {
uint c = a + b;
if (c >= a) {
return (MathError.NO_ERROR, c);
} else {
return (MathError.INTEGER_OVERFLOW, 0);
}
}
/**
* @dev add a and b and then subtract c
*/
function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) {
(MathError err0, uint sum) = addUInt(a, b);
if (err0 != MathError.NO_ERROR) {
return (err0, 0);
}
return subUInt(sum, c);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}// SPDX-License-Identifier: MIT
// https://forum.openzeppelin.com/t/can-not-call-the-function-approve-of-the-usdt-contract/2130/2
// USDT is gay and should be ashamed
pragma solidity ^0.6.0;
interface USDT {
function approve(address guy, uint256 wad) external;
function transfer(address _to, uint256 _value) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
import "../lib/erc20.sol";
interface IJar is IERC20 {
function token() external view returns (address);
function claimInsurance() external; // NOTE: Only yDelegatedVault implements this
function getRatio() external view returns (uint256);
function deposit(uint256) external;
function withdraw(uint256) external;
function earn() external;
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
interface Converter {
function convert(address) external returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
interface ICToken {
function totalSupply() external view returns (uint256);
function totalBorrows() external returns (uint256);
function borrowIndex() external returns (uint256);
function repayBorrow(uint256 repayAmount) external returns (uint256);
function redeemUnderlying(uint256 redeemAmount) external returns (uint256);
function borrow(uint256 borrowAmount) external returns (uint256);
function mint(uint256 mintAmount) external returns (uint256);
function transfer(address dst, uint256 amount) external returns (bool);
function transferFrom(
address src,
address dst,
uint256 amount
) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function allowance(address owner, address spender)
external
view
returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function balanceOfUnderlying(address owner) external returns (uint256);
function getAccountSnapshot(address account)
external
view
returns (
uint256,
uint256,
uint256,
uint256
);
function borrowRatePerBlock() external view returns (uint256);
function supplyRatePerBlock() external view returns (uint256);
function totalBorrowsCurrent() external returns (uint256);
function borrowBalanceCurrent(address account) external returns (uint256);
function borrowBalanceStored(address account)
external
view
returns (uint256);
function exchangeRateCurrent() external returns (uint256);
function exchangeRateStored() external view returns (uint256);
function getCash() external view returns (uint256);
function accrueInterest() external returns (uint256);
function seize(
address liquidator,
address borrower,
uint256 seizeTokens
) external returns (uint256);
}
interface ICEther {
function mint() external payable;
/**
* @notice Sender redeems cTokens in exchange for the underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemTokens The number of cTokens to redeem into underlying
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function redeem(uint256 redeemTokens) external returns (uint256);
/**
* @notice Sender redeems cTokens in exchange for a specified amount of underlying asset
* @dev Accrues interest whether or not the operation succeeds, unless reverted
* @param redeemAmount The amount of underlying to redeem
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function redeemUnderlying(uint256 redeemAmount) external returns (uint256);
/**
* @notice Sender borrows assets from the protocol to their own address
* @param borrowAmount The amount of the underlying asset to borrow
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/
function borrow(uint256 borrowAmount) external returns (uint256);
/**
* @notice Sender repays their own borrow
* @dev Reverts upon any failure
*/
function repayBorrow() external payable;
/**
* @notice Sender repays a borrow belonging to borrower
* @dev Reverts upon any failure
* @param borrower the account with the debt being payed off
*/
function repayBorrowBehalf(address borrower) external payable;
/**
* @notice The sender liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @dev Reverts upon any failure
* @param borrower The borrower of this cToken to be liquidated
* @param cTokenCollateral The market in which to seize collateral from the borrower
*/
function liquidateBorrow(address borrower, address cTokenCollateral)
external
payable;
}
interface IComptroller {
function compAccrued(address) external view returns (uint256);
function compSupplierIndex(address, address)
external
view
returns (uint256);
function compBorrowerIndex(address, address)
external
view
returns (uint256);
function compSpeeds(address) external view returns (uint256);
function compBorrowState(address) external view returns (uint224, uint32);
function compSupplyState(address) external view returns (uint224, uint32);
/*** Assets You Are In ***/
function enterMarkets(address[] calldata cTokens)
external
returns (uint256[] memory);
function exitMarket(address cToken) external returns (uint256);
/*** Policy Hooks ***/
function mintAllowed(
address cToken,
address minter,
uint256 mintAmount
) external returns (uint256);
function mintVerify(
address cToken,
address minter,
uint256 mintAmount,
uint256 mintTokens
) external;
function redeemAllowed(
address cToken,
address redeemer,
uint256 redeemTokens
) external returns (uint256);
function redeemVerify(
address cToken,
address redeemer,
uint256 redeemAmount,
uint256 redeemTokens
) external;
function borrowAllowed(
address cToken,
address borrower,
uint256 borrowAmount
) external returns (uint256);
function borrowVerify(
address cToken,
address borrower,
uint256 borrowAmount
) external;
function repayBorrowAllowed(
address cToken,
address payer,
address borrower,
uint256 repayAmount
) external returns (uint256);
function repayBorrowVerify(
address cToken,
address payer,
address borrower,
uint256 repayAmount,
uint256 borrowerIndex
) external;
function liquidateBorrowAllowed(
address cTokenBorrowed,
address cTokenCollateral,
address liquidator,
address borrower,
uint256 repayAmount
) external returns (uint256);
function liquidateBorrowVerify(
address cTokenBorrowed,
address cTokenCollateral,
address liquidator,
address borrower,
uint256 repayAmount,
uint256 seizeTokens
) external;
function seizeAllowed(
address cTokenCollateral,
address cTokenBorrowed,
address liquidator,
address borrower,
uint256 seizeTokens
) external returns (uint256);
function seizeVerify(
address cTokenCollateral,
address cTokenBorrowed,
address liquidator,
address borrower,
uint256 seizeTokens
) external;
function transferAllowed(
address cToken,
address src,
address dst,
uint256 transferTokens
) external returns (uint256);
function transferVerify(
address cToken,
address src,
address dst,
uint256 transferTokens
) external;
/*** Liquidity/Liquidation Calculations ***/
function liquidateCalculateSeizeTokens(
address cTokenBorrowed,
address cTokenCollateral,
uint256 repayAmount
) external view returns (uint256, uint256);
// Claim all the COMP accrued by holder in all markets
function claimComp(address holder) external;
// Claim all the COMP accrued by holder in specific markets
function claimComp(address holder, address[] calldata cTokens) external;
// Claim all the COMP accrued by specific holders in specific markets for their supplies and/or borrows
function claimComp(
address[] calldata holders,
address[] calldata cTokens,
bool borrowers,
bool suppliers
) external;
function markets(address cTokenAddress)
external
view
returns (bool, uint256);
}
interface ICompoundLens {
function getCompBalanceMetadataExt(
address comp,
address comptroller,
address account
)
external
returns (
uint256 balance,
uint256 votes,
address delegate,
uint256 allocated
);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
interface Proxy {
function execute(
address to,
uint256 value,
bytes calldata data
) external returns (bool, bytes memory);
function increaseAmount(uint256) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
interface ICurveFi_2 {
function get_virtual_price() external view returns (uint256);
function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount)
external;
function remove_liquidity_imbalance(
uint256[2] calldata amounts,
uint256 max_burn_amount
) external;
function remove_liquidity(uint256 _amount, uint256[2] calldata amounts)
external;
function exchange(
int128 from,
int128 to,
uint256 _from_amount,
uint256 _min_to_amount
) external;
function balances(int128) external view returns (uint256);
}
interface ICurveFi_3 {
function get_virtual_price() external view returns (uint256);
function add_liquidity(uint256[3] calldata amounts, uint256 min_mint_amount)
external;
function remove_liquidity_imbalance(
uint256[3] calldata amounts,
uint256 max_burn_amount
) external;
function remove_liquidity(uint256 _amount, uint256[3] calldata amounts)
external;
function exchange(
int128 from,
int128 to,
uint256 _from_amount,
uint256 _min_to_amount
) external;
function balances(uint256) external view returns (uint256);
}
interface ICurveFi_4 {
function get_virtual_price() external view returns (uint256);
function add_liquidity(uint256[4] calldata amounts, uint256 min_mint_amount)
external;
function remove_liquidity_imbalance(
uint256[4] calldata amounts,
uint256 max_burn_amount
) external;
function remove_liquidity(uint256 _amount, uint256[4] calldata amounts)
external;
function exchange(
int128 from,
int128 to,
uint256 _from_amount,
uint256 _min_to_amount
) external;
function balances(int128) external view returns (uint256);
}
interface ICurveZap_4 {
function add_liquidity(
uint256[4] calldata uamounts,
uint256 min_mint_amount
) external;
function remove_liquidity(uint256 _amount, uint256[4] calldata min_uamounts)
external;
function remove_liquidity_imbalance(
uint256[4] calldata uamounts,
uint256 max_burn_amount
) external;
function calc_withdraw_one_coin(uint256 _token_amount, int128 i)
external
returns (uint256);
function remove_liquidity_one_coin(
uint256 _token_amount,
int128 i,
uint256 min_uamount
) external;
function remove_liquidity_one_coin(
uint256 _token_amount,
int128 i,
uint256 min_uamount,
bool donate_dust
) external;
function withdraw_donated_dust() external;
function coins(int128 arg0) external returns (address);
function underlying_coins(int128 arg0) external returns (address);
function curve() external returns (address);
function token() external returns (address);
}
interface ICurveZap {
function remove_liquidity_one_coin(
uint256 _token_amount,
int128 i,
uint256 min_uamount
) external;
}
interface ICurveGauge {
function deposit(uint256 _value) external;
function deposit(uint256 _value, address addr) external;
function balanceOf(address arg0) external view returns (uint256);
function withdraw(uint256 _value) external;
function withdraw(uint256 _value, bool claim_rewards) external;
function claim_rewards() external;
function claim_rewards(address addr) external;
function claimable_tokens(address addr) external returns (uint256);
function claimable_reward(address addr) external view returns (uint256);
function integrate_fraction(address arg0) external view returns (uint256);
}
interface ICurveMintr {
function mint(address) external;
function minted(address arg0, address arg1) external view returns (uint256);
}
interface ICurveVotingEscrow {
function locked(address arg0)
external
view
returns (int128 amount, uint256 end);
function locked__end(address _addr) external view returns (uint256);
function create_lock(uint256, uint256) external;
function increase_amount(uint256) external;
function increase_unlock_time(uint256 _unlock_time) external;
function withdraw() external;
function smart_wallet_checker() external returns (address);
}
interface ICurveSmartContractChecker {
function wallets(address) external returns (bool);
function approveWallet(address _wallet) external;
}// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
interface UniswapRouterV2 {
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
)
external
returns (
uint256 amountA,
uint256 amountB,
uint256 liquidity
);
function addLiquidityETH(
address token,
uint256 amountTokenDesired,
uint256 amountTokenMin,
uint256 amountETHMin,
address to,
uint256 deadline
)
external
payable
returns (
uint256 amountToken,
uint256 amountETH,
uint256 liquidity
);
function removeLiquidity(
address tokenA,
address tokenB,
uint256 liquidity,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external returns (uint256 amountA, uint256 amountB);
function getAmountsOut(uint256 amountIn, address[] calldata path)
external
view
returns (uint256[] memory amounts);
function getAmountsIn(uint256 amountOut, address[] calldata path)
external
view
returns (uint256[] memory amounts);
function swapETHForExactTokens(
uint256 amountOut,
address[] calldata path,
address to,
uint256 deadline
) external payable returns (uint256[] memory amounts);
function swapExactETHForTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable returns (uint256[] memory amounts);
}
interface IUniswapV2Pair {
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
event Transfer(address indexed from, address indexed to, uint256 value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function allowance(address owner, address spender)
external
view
returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint256);
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Burn(
address indexed sender,
uint256 amount0,
uint256 amount1,
address indexed to
);
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint256);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves()
external
view
returns (
uint112 reserve0,
uint112 reserve1,
uint32 blockTimestampLast
);
function price0CumulativeLast() external view returns (uint256);
function price1CumulativeLast() external view returns (uint256);
function kLast() external view returns (uint256);
function mint(address to) external returns (uint256 liquidity);
function burn(address to)
external
returns (uint256 amount0, uint256 amount1);
function swap(
uint256 amount0Out,
uint256 amount1Out,
address to,
bytes calldata data
) external;
function skim(address to) external;
function sync() external;
}
interface IUniswapV2Factory {
event PairCreated(
address indexed token0,
address indexed token1,
address pair,
uint256
);
function getPair(address tokenA, address tokenB)
external
view
returns (address pair);
function allPairs(uint256) external view returns (address pair);
function allPairsLength() external view returns (uint256);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function createPair(address tokenA, address tokenB)
external
returns (address pair);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
interface IJarConverter {
function convert(
address _refundExcess, // address to send the excess amount when adding liquidity
uint256 _amount, // UNI LP Amount
bytes calldata _data
) external returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
interface IController {
function jars(address) external view returns (address);
function rewards() external view returns (address);
function devfund() external view returns (address);
function treasury() external view returns (address);
function balanceOf(address) external view returns (uint256);
function withdraw(address, uint256) external;
function earn(address, uint256) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
interface IMasterchef {
function BONUS_MULTIPLIER() external view returns (uint256);
function add(
uint256 _allocPoint,
address _lpToken,
bool _withUpdate
) external;
function bonusEndBlock() external view returns (uint256);
function deposit(uint256 _pid, uint256 _amount) external;
function dev(address _devaddr) external;
function devFundDivRate() external view returns (uint256);
function devaddr() external view returns (address);
function emergencyWithdraw(uint256 _pid) external;
function getMultiplier(uint256 _from, uint256 _to)
external
view
returns (uint256);
function massUpdatePools() external;
function owner() external view returns (address);
function pendingPickle(uint256 _pid, address _user)
external
view
returns (uint256);
function pickle() external view returns (address);
function picklePerBlock() external view returns (uint256);
function poolInfo(uint256)
external
view
returns (
address lpToken,
uint256 allocPoint,
uint256 lastRewardBlock,
uint256 accPicklePerShare
);
function poolLength() external view returns (uint256);
function renounceOwnership() external;
function set(
uint256 _pid,
uint256 _allocPoint,
bool _withUpdate
) external;
function setBonusEndBlock(uint256 _bonusEndBlock) external;
function setDevFundDivRate(uint256 _devFundDivRate) external;
function setPicklePerBlock(uint256 _picklePerBlock) external;
function startBlock() external view returns (uint256);
function totalAllocPoint() external view returns (uint256);
function transferOwnership(address newOwner) external;
function updatePool(uint256 _pid) external;
function userInfo(uint256, address)
external
view
returns (uint256 amount, uint256 rewardDebt);
function withdraw(uint256 _pid, uint256 _amount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
interface IStakingRewards {
function balanceOf(address account) external view returns (uint256);
function earned(address account) external view returns (uint256);
function exit() external;
function getReward() external;
function getRewardForDuration() external view returns (uint256);
function lastTimeRewardApplicable() external view returns (uint256);
function lastUpdateTime() external view returns (uint256);
function notifyRewardAmount(uint256 reward) external;
function periodFinish() external view returns (uint256);
function rewardPerToken() external view returns (uint256);
function rewardPerTokenStored() external view returns (uint256);
function rewardRate() external view returns (uint256);
function rewards(address) external view returns (uint256);
function rewardsDistribution() external view returns (address);
function rewardsDuration() external view returns (uint256);
function rewardsToken() external view returns (address);
function stake(uint256 amount) external;
function stakeWithPermit(
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function stakingToken() external view returns (address);
function totalSupply() external view returns (uint256);
function userRewardPerTokenPaid(address) external view returns (uint256);
function withdraw(uint256 amount) external;
}
interface IStakingRewardsFactory {
function deploy(address stakingToken, uint256 rewardAmount) external;
function isOwner() external view returns (bool);
function notifyRewardAmount(address stakingToken) external;
function notifyRewardAmounts() external;
function owner() external view returns (address);
function renounceOwnership() external;
function rewardsToken() external view returns (address);
function stakingRewardsGenesis() external view returns (uint256);
function stakingRewardsInfoByStakingToken(address)
external
view
returns (address stakingRewards, uint256 rewardAmount);
function stakingTokens(uint256) external view returns (address);
function transferOwnership(address newOwner) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
interface WETH {
function name() external view returns (string memory);
function approve(address guy, uint256 wad) external returns (bool);
function totalSupply() external view returns (uint256);
function transferFrom(
address src,
address dst,
uint256 wad
) external returns (bool);
function withdraw(uint256 wad) external;
function decimals() external view returns (uint8);
function balanceOf(address) external view returns (uint256);
function symbol() external view returns (string memory);
function transfer(address dst, uint256 wad) external returns (bool);
function deposit() external payable;
function allowance(address, address) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
interface IStrategy {
function rewards() external view returns (address);
function gauge() external view returns (address);
function want() external view returns (address);
function timelock() external view returns (address);
function deposit() external;
function withdrawForSwap(uint256) external returns (uint256);
function withdraw(address) external;
function withdraw(uint256) external;
function skim() external;
function withdrawAll() external returns (uint256);
function balanceOf() external view returns (uint256);
function harvest() external;
function setTimelock(address) external;
function setController(address _controller) external;
function execute(address _target, bytes calldata _data)
external
payable
returns (bytes memory response);
function execute(bytes calldata _data)
external
payable
returns (bytes memory response);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
interface OneSplitAudit {
function getExpectedReturn(
address fromToken,
address toToken,
uint256 amount,
uint256 parts,
uint256 featureFlags
)
external
view
returns (uint256 returnAmount, uint256[] memory distribution);
function swap(
address fromToken,
address toToken,
uint256 amount,
uint256 minReturn,
uint256[] calldata distribution,
uint256 featureFlags
) external payable;
}// https://github.com/iearn-finance/jars/blob/master/contracts/controllers/StrategyControllerV1.sol
pragma solidity ^0.6.7;
pragma experimental ABIEncoderV2;
import "./interfaces/controller.sol";
import "./lib/erc20.sol";
import "./lib/safe-math.sol";
import "./interfaces/jar.sol";
import "./interfaces/jar-converter.sol";
import "./interfaces/onesplit.sol";
import "./interfaces/strategy.sol";
import "./interfaces/converter.sol";
contract ControllerV4 {
using SafeERC20 for IERC20;
using Address for address;
using SafeMath for uint256;
address public constant burn = 0x000000000000000000000000000000000000dEaD;
address public onesplit = 0xC586BeF4a0992C495Cf22e1aeEE4E446CECDee0E;
address public governance;
address public strategist;
address public devfund;
address public treasury;
address public timelock;
// Convenience fee 0.1%
uint256 public convenienceFee = 100;
uint256 public constant convenienceFeeMax = 100000;
mapping(address => address) public jars;
mapping(address => address) public strategies;
mapping(address => mapping(address => address)) public converters;
mapping(address => mapping(address => bool)) public approvedStrategies;
mapping(address => bool) public approvedJarConverters;
uint256 public split = 500;
uint256 public constant max = 10000;
constructor(
address _governance,
address _strategist,
address _timelock,
address _devfund,
address _treasury
) public {
governance = _governance;
strategist = _strategist;
timelock = _timelock;
devfund = _devfund;
treasury = _treasury;
}
function setDevFund(address _devfund) public {
require(msg.sender == governance, "!governance");
devfund = _devfund;
}
function setTreasury(address _treasury) public {
require(msg.sender == governance, "!governance");
treasury = _treasury;
}
function setStrategist(address _strategist) public {
require(msg.sender == governance, "!governance");
strategist = _strategist;
}
function setSplit(uint256 _split) public {
require(msg.sender == governance, "!governance");
split = _split;
}
function setOneSplit(address _onesplit) public {
require(msg.sender == governance, "!governance");
onesplit = _onesplit;
}
function setGovernance(address _governance) public {
require(msg.sender == governance, "!governance");
governance = _governance;
}
function setTimelock(address _timelock) public {
require(msg.sender == timelock, "!timelock");
timelock = _timelock;
}
function setJar(address _token, address _jar) public {
require(
msg.sender == strategist || msg.sender == governance,
"!strategist"
);
require(jars[_token] == address(0), "jar");
jars[_token] = _jar;
}
function approveJarConverter(address _converter) public {
require(msg.sender == governance, "!governance");
approvedJarConverters[_converter] = true;
}
function revokeJarConverter(address _converter) public {
require(msg.sender == governance, "!governance");
approvedJarConverters[_converter] = false;
}
function approveStrategy(address _token, address _strategy) public {
require(msg.sender == timelock, "!timelock");
approvedStrategies[_token][_strategy] = true;
}
function revokeStrategy(address _token, address _strategy) public {
require(msg.sender == governance, "!governance");
approvedStrategies[_token][_strategy] = false;
}
function setConvenienceFee(uint256 _convenienceFee) external {
require(msg.sender == timelock, "!timelock");
convenienceFee = _convenienceFee;
}
function setStrategy(address _token, address _strategy) public {
require(
msg.sender == strategist || msg.sender == governance,
"!strategist"
);
require(approvedStrategies[_token][_strategy] == true, "!approved");
address _current = strategies[_token];
if (_current != address(0)) {
IStrategy(_current).withdrawAll();
}
strategies[_token] = _strategy;
}
function earn(address _token, uint256 _amount) public {
address _strategy = strategies[_token];
address _want = IStrategy(_strategy).want();
if (_want != _token) {
address converter = converters[_token][_want];
IERC20(_token).safeTransfer(converter, _amount);
_amount = Converter(converter).convert(_strategy);
IERC20(_want).safeTransfer(_strategy, _amount);
} else {
IERC20(_token).safeTransfer(_strategy, _amount);
}
IStrategy(_strategy).deposit();
}
function balanceOf(address _token) external view returns (uint256) {
return IStrategy(strategies[_token]).balanceOf();
}
function withdrawAll(address _token) public {
require(
msg.sender == strategist || msg.sender == governance,
"!strategist"
);
IStrategy(strategies[_token]).withdrawAll();
}
function inCaseTokensGetStuck(address _token, uint256 _amount) public {
require(
msg.sender == strategist || msg.sender == governance,
"!governance"
);
IERC20(_token).safeTransfer(msg.sender, _amount);
}
function inCaseStrategyTokenGetStuck(address _strategy, address _token)
public
{
require(
msg.sender == strategist || msg.sender == governance,
"!governance"
);
IStrategy(_strategy).withdraw(_token);
}
function getExpectedReturn(
address _strategy,
address _token,
uint256 parts
) public view returns (uint256 expected) {
uint256 _balance = IERC20(_token).balanceOf(_strategy);
address _want = IStrategy(_strategy).want();
(expected, ) = OneSplitAudit(onesplit).getExpectedReturn(
_token,
_want,
_balance,
parts,
0
);
}
// Only allows to withdraw non-core strategy tokens ~ this is over and above normal yield
function yearn(
address _strategy,
address _token,
uint256 parts
) public {
require(
msg.sender == strategist || msg.sender == governance,
"!governance"
);
// This contract should never have value in it, but just incase since this is a public call
uint256 _before = IERC20(_token).balanceOf(address(this));
IStrategy(_strategy).withdraw(_token);
uint256 _after = IERC20(_token).balanceOf(address(this));
if (_after > _before) {
uint256 _amount = _after.sub(_before);
address _want = IStrategy(_strategy).want();
uint256[] memory _distribution;
uint256 _expected;
_before = IERC20(_want).balanceOf(address(this));
IERC20(_token).safeApprove(onesplit, 0);
IERC20(_token).safeApprove(onesplit, _amount);
(_expected, _distribution) = OneSplitAudit(onesplit)
.getExpectedReturn(_token, _want, _amount, parts, 0);
OneSplitAudit(onesplit).swap(
_token,
_want,
_amount,
_expected,
_distribution,
0
);
_after = IERC20(_want).balanceOf(address(this));
if (_after > _before) {
_amount = _after.sub(_before);
uint256 _treasury = _amount.mul(split).div(max);
earn(_want, _amount.sub(_treasury));
IERC20(_want).safeTransfer(treasury, _treasury);
}
}
}
function withdraw(address _token, uint256 _amount) public {
require(msg.sender == jars[_token], "!jar");
IStrategy(strategies[_token]).withdraw(_amount);
}
// Function to swap between jars
function swapExactJarForJar(
address _fromJar, // From which Jar
address _toJar, // To which Jar
uint256 _fromJarAmount, // How much jar tokens to swap
uint256 _toJarMinAmount, // How much jar tokens you'd like at a minimum
address payable[] calldata _targets,
bytes[] calldata _data
) external returns (uint256) {
require(_targets.length == _data.length, "!length");
// Only return last response
for (uint256 i = 0; i < _targets.length; i++) {
require(_targets[i] != address(0), "!converter");
require(approvedJarConverters[_targets[i]], "!converter");
}
address _fromJarToken = IJar(_fromJar).token();
address _toJarToken = IJar(_toJar).token();
// Get pTokens from msg.sender
IERC20(_fromJar).safeTransferFrom(
msg.sender,
address(this),
_fromJarAmount
);
// Calculate how much underlying
// is the amount of pTokens worth
uint256 _fromJarUnderlyingAmount = _fromJarAmount
.mul(IJar(_fromJar).getRatio())
.div(10**uint256(IJar(_fromJar).decimals()));
// Call 'withdrawForSwap' on Jar's current strategy if Jar
// doesn't have enough initial capital.
// This has moves the funds from the strategy to the Jar's
// 'earnable' amount. Enabling 'free' withdrawals
uint256 _fromJarAvailUnderlying = IERC20(_fromJarToken).balanceOf(
_fromJar
);
if (_fromJarAvailUnderlying < _fromJarUnderlyingAmount) {
IStrategy(strategies[_fromJarToken]).withdrawForSwap(
_fromJarUnderlyingAmount.sub(_fromJarAvailUnderlying)
);
}
// Withdraw from Jar
// Note: this is free since its still within the "earnable" amount
// as we transferred the access
IERC20(_fromJar).safeApprove(_fromJar, 0);
IERC20(_fromJar).safeApprove(_fromJar, _fromJarAmount);
IJar(_fromJar).withdraw(_fromJarAmount);
// Calculate fee
uint256 _fromUnderlyingBalance = IERC20(_fromJarToken).balanceOf(
address(this)
);
uint256 _convenienceFee = _fromUnderlyingBalance.mul(convenienceFee).div(
convenienceFeeMax
);
if (_convenienceFee > 1) {
IERC20(_fromJarToken).safeTransfer(devfund, _convenienceFee.div(2));
IERC20(_fromJarToken).safeTransfer(treasury, _convenienceFee.div(2));
}
// Executes sequence of logic
for (uint256 i = 0; i < _targets.length; i++) {
_execute(_targets[i], _data[i]);
}
// Deposit into new Jar
uint256 _toBal = IERC20(_toJarToken).balanceOf(address(this));
IERC20(_toJarToken).safeApprove(_toJar, 0);
IERC20(_toJarToken).safeApprove(_toJar, _toBal);
IJar(_toJar).deposit(_toBal);
// Send Jar Tokens to user
uint256 _toJarBal = IJar(_toJar).balanceOf(address(this));
if (_toJarBal < _toJarMinAmount) {
revert("!min-jar-amount");
}
IJar(_toJar).transfer(msg.sender, _toJarBal);
return _toJarBal;
}
function _execute(address _target, bytes memory _data)
internal
returns (bytes memory response)
{
require(_target != address(0), "!target");
// call contract in current context
assembly {
let succeeded := delegatecall(
sub(gas(), 5000),
_target,
add(_data, 0x20),
mload(_data),
0,
0
)
let size := returndatasize()
response := mload(0x40)
mstore(
0x40,
add(response, and(add(add(size, 0x20), 0x1f), not(0x1f)))
)
mstore(response, size)
returndatacopy(add(response, 0x20), 0, size)
switch iszero(succeeded)
case 1 {
// throw if delegatecall failed
revert(add(response, 0x20), size)
}
}
}
}pragma solidity ^0.6.7;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount)
external
returns (bool);
function allowance(address owner, address spender)
external
view
returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
interface MasterChef {
function userInfo(uint256, address)
external
view
returns (uint256, uint256);
}
contract PickleVoteProxy {
// ETH/PICKLE token
IERC20 public constant votes = IERC20(
0xdc98556Ce24f007A5eF6dC1CE96322d65832A819
);
// Pickle's masterchef contract
MasterChef public constant chef = MasterChef(
0xbD17B1ce622d73bD438b9E658acA5996dc394b0d
);
// Pool 0 is the ETH/PICKLE pool
uint256 public constant pool = uint256(0);
// Using 9 decimals as we're square rooting the votes
function decimals() external pure returns (uint8) {
return uint8(9);
}
function name() external pure returns (string memory) {
return "PICKLEs In The Citadel";
}
function symbol() external pure returns (string memory) {
return "PICKLE C";
}
function totalSupply() external view returns (uint256) {
return sqrt(votes.totalSupply());
}
function balanceOf(address _voter) external view returns (uint256) {
(uint256 _votes, ) = chef.userInfo(pool, _voter);
return sqrt(_votes);
}
function sqrt(uint256 x) public pure returns (uint256 y) {
uint256 z = (x + 1) / 2;
y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
}
constructor() public {}
}pragma solidity ^0.6.7;
import "../lib/safe-math.sol";
import "../lib/erc20.sol";
import "../interfaces/uniswapv2.sol";
import "../interfaces/curve.sol";
// Converts Curve LP Tokens to UNI LP Tokens
contract CurveProxyLogic {
using SafeMath for uint256;
using SafeERC20 for IERC20;
function remove_liquidity_one_coin(
address curve,
address curveLp,
int128 index
) public {
uint256 lpAmount = IERC20(curveLp).balanceOf(address(this));
IERC20(curveLp).safeApprove(curve, 0);
IERC20(curveLp).safeApprove(curve, lpAmount);
ICurveZap(curve).remove_liquidity_one_coin(lpAmount, index, 0);
}
function add_liquidity(
address curve,
bytes4 curveFunctionSig,
uint256 curvePoolSize,
uint256 curveUnderlyingIndex,
address underlying
) public {
uint256 underlyingAmount = IERC20(underlying).balanceOf(address(this));
// curveFunctionSig should be the abi.encodedFormat of
// add_liquidity(uint256[N_COINS],uint256)
// The reason why its here is because different curve pools
// have a different function signature
uint256[] memory liquidity = new uint256[](curvePoolSize);
liquidity[curveUnderlyingIndex] = underlyingAmount;
bytes memory callData = abi.encodePacked(
curveFunctionSig,
liquidity,
uint256(0)
);
IERC20(underlying).safeApprove(curve, 0);
IERC20(underlying).safeApprove(curve, underlyingAmount);
(bool success, ) = curve.call(callData);
require(success, "!success");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
pragma experimental ABIEncoderV2;
import "../lib/safe-math.sol";
import "../lib/erc20.sol";
import "../interfaces/uniswapv2.sol";
import "../interfaces/curve.sol";
// Converts Curve LP Tokens to UNI LP Tokens
contract UniswapV2ProxyLogic {
using SafeMath for uint256;
using SafeERC20 for IERC20;
IUniswapV2Factory public constant factory = IUniswapV2Factory(
0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f
);
UniswapRouterV2 public constant router = UniswapRouterV2(
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
);
address public constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
function getSwapAmt(uint256 amtA, uint256 resA)
internal
pure
returns (uint256)
{
return
sqrt(amtA.mul(resA.mul(3988000).add(amtA.mul(3988009))))
.sub(amtA.mul(1997))
.div(1994);
}
// https://blog.alphafinance.io/onesideduniswap/
// https://github.com/AlphaFinanceLab/alphahomora/blob/88a8dfe4d4fa62b13b40f7983ee2c646f83e63b5/contracts/StrategyAddETHOnly.sol#L39
// AlphaFinance is gripbook licensed
function optimalOneSideSupply(
IUniswapV2Pair pair,
address from,
address to
) public {
address[] memory path = new address[](2);
// 1. Compute optimal amount of WETH to be converted
(uint256 r0, uint256 r1, ) = pair.getReserves();
uint256 rIn = pair.token0() == from ? r0 : r1;
uint256 aIn = getSwapAmt(rIn, IERC20(from).balanceOf(address(this)));
// 2. Convert that from -> to
path[0] = from;
path[1] = to;
IERC20(from).safeApprove(address(router), 0);
IERC20(from).safeApprove(address(router), aIn);
router.swapExactTokensForTokens(aIn, 0, path, address(this), now + 60);
}
function swapUniswap(address from, address to) public {
require(to != address(0));
address[] memory path;
if (from == weth || to == weth) {
path = new address[](2);
path[0] = from;
path[1] = to;
} else {
path = new address[](3);
path[0] = from;
path[1] = weth;
path[2] = to;
}
uint256 amount = IERC20(from).balanceOf(address(this));
IERC20(from).safeApprove(address(router), 0);
IERC20(from).safeApprove(address(router), amount);
router.swapExactTokensForTokens(
amount,
0,
path,
address(this),
now + 60
);
}
function removeLiquidity(IUniswapV2Pair pair) public {
uint256 _balance = pair.balanceOf(address(this));
pair.approve(address(router), _balance);
router.removeLiquidity(
pair.token0(),
pair.token1(),
_balance,
0,
0,
address(this),
now + 60
);
}
function supplyLiquidity(
address token0,
address token1
) public returns (uint256) {
// Add liquidity to uniswap
IERC20(token0).safeApprove(address(router), 0);
IERC20(token0).safeApprove(
address(router),
IERC20(token0).balanceOf(address(this))
);
IERC20(token1).safeApprove(address(router), 0);
IERC20(token1).safeApprove(
address(router),
IERC20(token1).balanceOf(address(this))
);
(, , uint256 _to) = router.addLiquidity(
token0,
token1,
IERC20(token0).balanceOf(address(this)),
IERC20(token1).balanceOf(address(this)),
0,
0,
address(this),
now + 60
);
return _to;
}
function refundDust(IUniswapV2Pair pair, address recipient) public {
address token0 = pair.token0();
address token1 = pair.token1();
IERC20(token0).safeTransfer(
recipient,
IERC20(token0).balanceOf(address(this))
);
IERC20(token1).safeTransfer(
recipient,
IERC20(token1).balanceOf(address(this))
);
}
function lpTokensToPrimitive(
IUniswapV2Pair from,
address to
) public {
if (from.token0() != weth && from.token1() != weth) {
revert("!from-weth-pair");
}
address fromOther = from.token0() == weth ? from.token1() : from.token0();
// Removes liquidity
removeLiquidity(from);
// Swap from WETH to other
swapUniswap(weth, to);
// If from is not to, we swap them too
if (fromOther != to) {
swapUniswap(fromOther, to);
}
}
function primitiveToLpTokens(
address from,
IUniswapV2Pair to,
address dustRecipient
) public {
if (to.token0() != weth && to.token1() != weth) {
revert("!to-weth-pair");
}
address toOther = to.token0() == weth ? to.token1() : to.token0();
// Swap to WETH
swapUniswap(from, weth);
// Optimal supply from WETH to
optimalOneSideSupply(to, weth, toOther);
// Supply tokens
supplyLiquidity(weth, toOther);
// Dust
refundDust(to, dustRecipient);
}
function swapUniLPTokens(
IUniswapV2Pair from,
IUniswapV2Pair to,
address dustRecipient
) public {
if (from.token0() != weth && from.token1() != weth) {
revert("!from-weth-pair");
}
if (to.token0() != weth && to.token1() != weth) {
revert("!to-weth-pair");
}
address fromOther = from.token0() == weth
? from.token1()
: from.token0();
address toOther = to.token0() == weth ? to.token1() : to.token0();
// Remove weth-<token> pair
removeLiquidity(from);
// Swap <token> to WETH
swapUniswap(fromOther, weth);
// Optimal supply from WETH to <other-token>
optimalOneSideSupply(to, weth, toOther);
// Supply weth-<other-token> pair
supplyLiquidity(weth, toOther);
// Refund dust
refundDust(to, dustRecipient);
}
}pragma solidity ^0.6.0;
interface Hevm {
function warp(uint256) external;
function roll(uint x) external;
function store(address c, bytes32 loc, bytes32 val) external;
}pragma solidity ^0.6.7;
import "../../lib/safe-math.sol";
import "../../lib/erc20.sol";
import "./hevm.sol";
import "./user.sol";
import "./test-approx.sol";
import "../../interfaces/usdt.sol";
import "../../interfaces/weth.sol";
import "../../interfaces/strategy.sol";
import "../../interfaces/curve.sol";
import "../../interfaces/uniswapv2.sol";
contract DSTestDefiBase is DSTestApprox {
using SafeERC20 for IERC20;
using SafeMath for uint256;
address pickle = 0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5;
address burn = 0x000000000000000000000000000000000000dEaD;
address susdv2_deposit = 0xFCBa3E75865d2d561BE8D220616520c171F12851;
address susdv2_pool = 0xA5407eAE9Ba41422680e2e00537571bcC53efBfD;
address three_pool = 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7;
address ren_pool = 0x93054188d876f558f4a66B2EF1d97d16eDf0895B;
address scrv = 0xC25a3A3b969415c80451098fa907EC722572917F;
address three_crv = 0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490;
address ren_crv = 0x49849C98ae39Fff122806C06791Fa73784FB3675;
address eth = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address crv = 0xD533a949740bb3306d119CC777fa900bA034cd52;
address snx = 0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F;
address dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
address susd = 0x57Ab1ec28D129707052df4dF418D58a2D46d5f51;
address uni = 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984;
address wbtc = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
address renbtc = 0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D;
Hevm hevm = Hevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
UniswapRouterV2 univ2 = UniswapRouterV2(
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
);
IUniswapV2Factory univ2Factory = IUniswapV2Factory(
0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f
);
ICurveFi_4 curveSusdV2 = ICurveFi_4(
0xA5407eAE9Ba41422680e2e00537571bcC53efBfD
);
uint256 startTime = block.timestamp;
receive() external payable {}
fallback () external payable {}
function _swap(
address _from,
address _to,
uint256 _amount
) internal {
address[] memory path;
if (_from == eth || _from == weth) {
path = new address[](2);
path[0] = weth;
path[1] = _to;
univ2.swapExactETHForTokens{value: _amount}(
0,
path,
address(this),
now + 60
);
} else {
path = new address[](3);
path[0] = _from;
path[1] = weth;
path[2] = _to;
IERC20(_from).safeApprove(address(univ2), 0);
IERC20(_from).safeApprove(address(univ2), _amount);
univ2.swapExactTokensForTokens(
_amount,
0,
path,
address(this),
now + 60
);
}
}
function _getERC20(address token, uint256 _amount) internal {
address[] memory path = new address[](2);
path[0] = weth;
path[1] = token;
uint256[] memory ins = univ2.getAmountsIn(_amount, path);
uint256 ethAmount = ins[0];
univ2.swapETHForExactTokens{value: ethAmount}(
_amount,
path,
address(this),
now + 60
);
}
function _getERC20WithETH(address token, uint256 _ethAmount) internal {
address[] memory path = new address[](2);
path[0] = weth;
path[1] = token;
univ2.swapExactETHForTokens{value: _ethAmount}(
0,
path,
address(this),
now + 60
);
}
function _getUniV2LPToken(address lpToken, uint256 _ethAmount) internal {
address token0 = IUniswapV2Pair(lpToken).token0();
address token1 = IUniswapV2Pair(lpToken).token1();
if (token0 != weth) {
_getERC20WithETH(token0, _ethAmount.div(2));
} else {
WETH(weth).deposit{value: _ethAmount.div(2)}();
}
if (token1 != weth) {
_getERC20WithETH(token1, _ethAmount.div(2));
} else {
WETH(weth).deposit{value: _ethAmount.div(2)}();
}
IERC20(token0).safeApprove(address(univ2), uint256(0));
IERC20(token0).safeApprove(address(univ2), uint256(-1));
IERC20(token1).safeApprove(address(univ2), uint256(0));
IERC20(token1).safeApprove(address(univ2), uint256(-1));
univ2.addLiquidity(
token0,
token1,
IERC20(token0).balanceOf(address(this)),
IERC20(token1).balanceOf(address(this)),
0,
0,
address(this),
now + 60
);
}
function _getUniV2LPToken(
address token0,
address token1,
uint256 _ethAmount
) internal {
_getUniV2LPToken(univ2Factory.getPair(token0, token1), _ethAmount);
}
function _getFunctionSig(string memory sig) internal pure returns (bytes4) {
return bytes4(keccak256(bytes(sig)));
}
function _getDynamicArray(address payable one)
internal
pure
returns (address payable[] memory)
{
address payable[] memory targets = new address payable[](1);
targets[0] = one;
return targets;
}
function _getDynamicArray(bytes memory one)
internal
pure
returns (bytes[] memory)
{
bytes[] memory data = new bytes[](1);
data[0] = one;
return data;
}
function _getDynamicArray(address payable one, address payable two)
internal
pure
returns (address payable[] memory)
{
address payable[] memory targets = new address payable[](2);
targets[0] = one;
targets[1] = two;
return targets;
}
function _getDynamicArray(bytes memory one, bytes memory two)
internal
pure
returns (bytes[] memory)
{
bytes[] memory data = new bytes[](2);
data[0] = one;
data[1] = two;
return data;
}
function _getDynamicArray(
address payable one,
address payable two,
address payable three
) internal pure returns (address payable[] memory) {
address payable[] memory targets = new address payable[](3);
targets[0] = one;
targets[1] = two;
targets[2] = three;
return targets;
}
function _getDynamicArray(
bytes memory one,
bytes memory two,
bytes memory three
) internal pure returns (bytes[] memory) {
bytes[] memory data = new bytes[](3);
data[0] = one;
data[1] = two;
data[2] = three;
return data;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "../lib/hevm.sol";
import "../lib/user.sol";
import "../lib/test-approx.sol";
import "../lib/test-defi-base.sol";
import "../../interfaces/strategy.sol";
import "../../interfaces/curve.sol";
import "../../interfaces/uniswapv2.sol";
import "../../pickle-jar.sol";
import "../../controller-v4.sol";
contract StrategyCurveFarmTestBase is DSTestDefiBase {
address governance;
address strategist;
address timelock;
address devfund;
address treasury;
address want;
PickleJar pickleJar;
ControllerV4 controller;
IStrategy strategy;
// **** Tests ****
function _test_withdraw() internal {
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
// Deposits to strategy
pickleJar.earn();
// Fast forwards
hevm.warp(block.timestamp + 1 weeks);
strategy.harvest();
// Withdraws back to pickleJar
uint256 _before = IERC20(want).balanceOf(address(pickleJar));
controller.withdrawAll(want);
uint256 _after = IERC20(want).balanceOf(address(pickleJar));
assertTrue(_after > _before);
_before = IERC20(want).balanceOf(address(this));
pickleJar.withdrawAll();
_after = IERC20(want).balanceOf(address(this));
assertTrue(_after > _before);
// Gained some interest
assertTrue(_after > _want);
}
function _test_get_earn_harvest_rewards() internal {
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
pickleJar.earn();
// Fast forward one week
hevm.warp(block.timestamp + 1 weeks);
// Call the harvest function
uint256 _before = pickleJar.balance();
uint256 _treasuryBefore = IERC20(want).balanceOf(treasury);
strategy.harvest();
uint256 _after = pickleJar.balance();
uint256 _treasuryAfter = IERC20(want).balanceOf(treasury);
uint256 earned = _after.sub(_before).mul(1000).div(955);
uint256 earnedRewards = earned.mul(45).div(1000); // 4.5%
uint256 actualRewardsEarned = _treasuryAfter.sub(_treasuryBefore);
// 4.5% performance fee is given
assertEqApprox(earnedRewards, actualRewardsEarned);
// Withdraw
uint256 _devBefore = IERC20(want).balanceOf(devfund);
_treasuryBefore = IERC20(want).balanceOf(treasury);
uint256 _stratBal = strategy.balanceOf();
pickleJar.withdrawAll();
uint256 _devAfter = IERC20(want).balanceOf(devfund);
_treasuryAfter = IERC20(want).balanceOf(treasury);
// 0.175% goes to dev
uint256 _devFund = _devAfter.sub(_devBefore);
assertEq(_devFund, _stratBal.mul(175).div(100000));
// 0.325% goes to treasury
uint256 _treasuryFund = _treasuryAfter.sub(_treasuryBefore);
assertEq(_treasuryFund, _stratBal.mul(325).div(100000));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
// Contract account to simulate another user
contract User {
function execute(
address target,
uint256 value,
string memory signature,
bytes memory data
) public payable returns (bytes memory) {
bytes memory callData;
if (bytes(signature).length == 0) {
callData = data;
} else {
callData = abi.encodePacked(
bytes4(keccak256(bytes(signature))),
data
);
}
(bool success, bytes memory returnData) = target.call{value: value}(
callData
);
require(success, "!user-execute");
return returnData;
}
}pragma solidity ^0.6.7;
import "./test.sol";
contract DSTestApprox is DSTest {
function assertEqApprox(uint256 a, uint256 b) internal {
if (a == 0 && b == 0) {
return;
}
// +/- 5%
uint256 bMax = (b * 105) / 100;
uint256 bMin = (b * 95) / 100;
if (!(a > bMin && a < bMax)) {
emit log_bytes32("Error: Wrong `a-uint` value!");
emit log_named_uint(" Expected", b);
emit log_named_uint(" Actual", a);
fail();
}
}
function assertEqVerbose(bool a, bytes memory b) internal {
if (!a) {
emit log_bytes32("Error: assertion error!");
emit logs(b);
fail();
}
}
}pragma solidity ^0.6.7;
import "../lib/hevm.sol";
import "../lib/user.sol";
import "../lib/test-approx.sol";
import "../lib/test-defi-base.sol";
import "../../interfaces/strategy.sol";
import "../../interfaces/curve.sol";
import "../../interfaces/uniswapv2.sol";
import "../../pickle-jar.sol";
import "../../controller-v4.sol";
contract StrategyUniFarmTestBase is DSTestDefiBase {
address want;
address token1;
address governance;
address strategist;
address timelock;
address devfund;
address treasury;
PickleJar pickleJar;
ControllerV4 controller;
IStrategy strategy;
function _getWant(uint256 ethAmount, uint256 amount) internal {
_getERC20(token1, amount);
uint256 _token1 = IERC20(token1).balanceOf(address(this));
IERC20(token1).safeApprove(address(univ2), 0);
IERC20(token1).safeApprove(address(univ2), _token1);
univ2.addLiquidityETH{value: ethAmount}(
token1,
_token1,
0,
0,
address(this),
now + 60
);
}
// **** Tests ****
function _test_timelock() internal {
assertTrue(strategy.timelock() == timelock);
strategy.setTimelock(address(1));
assertTrue(strategy.timelock() == address(1));
}
function _test_withdraw_release() internal {
uint256 decimals = ERC20(token1).decimals();
_getWant(10 ether, 4000 * (10**decimals));
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).safeApprove(address(pickleJar), 0);
IERC20(want).safeApprove(address(pickleJar), _want);
pickleJar.deposit(_want);
pickleJar.earn();
hevm.warp(block.timestamp + 1 weeks);
strategy.harvest();
// Checking withdraw
uint256 _before = IERC20(want).balanceOf(address(pickleJar));
controller.withdrawAll(want);
uint256 _after = IERC20(want).balanceOf(address(pickleJar));
assertTrue(_after > _before);
_before = IERC20(want).balanceOf(address(this));
pickleJar.withdrawAll();
_after = IERC20(want).balanceOf(address(this));
assertTrue(_after > _before);
// Check if we gained interest
assertTrue(_after > _want);
}
function _test_get_earn_harvest_rewards() internal {
uint256 decimals = ERC20(token1).decimals();
_getWant(10 ether, 4000 * (10**decimals));
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).safeApprove(address(pickleJar), 0);
IERC20(want).safeApprove(address(pickleJar), _want);
pickleJar.deposit(_want);
pickleJar.earn();
hevm.warp(block.timestamp + 1 weeks);
// Call the harvest function
uint256 _before = pickleJar.balance();
uint256 _treasuryBefore = IERC20(want).balanceOf(treasury);
strategy.harvest();
uint256 _after = pickleJar.balance();
uint256 _treasuryAfter = IERC20(want).balanceOf(treasury);
uint256 earned = _after.sub(_before).mul(1000).div(955);
uint256 earnedRewards = earned.mul(45).div(1000); // 4.5%
uint256 actualRewardsEarned = _treasuryAfter.sub(_treasuryBefore);
// 4.5% performance fee is given
assertEqApprox(earnedRewards, actualRewardsEarned);
// Withdraw
uint256 _devBefore = IERC20(want).balanceOf(devfund);
_treasuryBefore = IERC20(want).balanceOf(treasury);
uint256 _stratBal = strategy.balanceOf();
pickleJar.withdrawAll();
uint256 _devAfter = IERC20(want).balanceOf(devfund);
_treasuryAfter = IERC20(want).balanceOf(treasury);
// 0.175% goes to dev
uint256 _devFund = _devAfter.sub(_devBefore);
assertEq(_devFund, _stratBal.mul(175).div(100000));
// 0.325% goes to treasury
uint256 _treasuryFund = _treasuryAfter.sub(_treasuryBefore);
assertEq(_treasuryFund, _stratBal.mul(325).div(100000));
}
}// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity >=0.4.23;
contract DSTest {
event eventListener (address target, bool exact);
event logs (bytes);
event log_bytes32 (bytes32);
event log_named_address (bytes32 key, address val);
event log_named_bytes32 (bytes32 key, bytes32 val);
event log_named_decimal_int (bytes32 key, int val, uint decimals);
event log_named_decimal_uint (bytes32 key, uint val, uint decimals);
event log_named_int (bytes32 key, int val);
event log_named_uint (bytes32 key, uint val);
event log_named_string (bytes32 key, string val);
bool public IS_TEST;
bool public failed;
constructor() internal {
IS_TEST = true;
}
function fail() internal {
failed = true;
}
function expectEventsExact(address target) internal {
emit eventListener(target, true);
}
modifier logs_gas() {
uint startGas = gasleft();
_;
uint endGas = gasleft();
emit log_named_uint("gas", startGas - endGas);
}
function assertTrue(bool condition) internal {
if (!condition) {
emit log_bytes32("Assertion failed");
fail();
}
}
function assertEq(address a, address b) internal {
if (a != b) {
emit log_bytes32("Error: Wrong `address' value");
emit log_named_address(" Expected", b);
emit log_named_address(" Actual", a);
fail();
}
}
function assertEq32(bytes32 a, bytes32 b) internal {
assertEq(a, b);
}
function assertEq(bytes32 a, bytes32 b) internal {
if (a != b) {
emit log_bytes32("Error: Wrong `bytes32' value");
emit log_named_bytes32(" Expected", b);
emit log_named_bytes32(" Actual", a);
fail();
}
}
function assertEqDecimal(int a, int b, uint decimals) internal {
if (a != b) {
emit log_bytes32("Error: Wrong fixed-point decimal");
emit log_named_decimal_int(" Expected", b, decimals);
emit log_named_decimal_int(" Actual", a, decimals);
fail();
}
}
function assertEqDecimal(uint a, uint b, uint decimals) internal {
if (a != b) {
emit log_bytes32("Error: Wrong fixed-point decimal");
emit log_named_decimal_uint(" Expected", b, decimals);
emit log_named_decimal_uint(" Actual", a, decimals);
fail();
}
}
function assertEq(int a, int b) internal {
if (a != b) {
emit log_bytes32("Error: Wrong `int' value");
emit log_named_int(" Expected", b);
emit log_named_int(" Actual", a);
fail();
}
}
function assertEq(uint a, uint b) internal {
if (a != b) {
emit log_bytes32("Error: Wrong `uint' value");
emit log_named_uint(" Expected", b);
emit log_named_uint(" Actual", a);
fail();
}
}
function assertEq(string memory a, string memory b) internal {
if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) {
emit log_bytes32("Error: Wrong `string' value");
emit log_named_string(" Expected", b);
emit log_named_string(" Actual", a);
fail();
}
}
function assertEq0(bytes memory a, bytes memory b) internal {
bool ok = true;
if (a.length == b.length) {
for (uint i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
ok = false;
}
}
} else {
ok = false;
}
if (!ok) {
emit log_bytes32("Error: Wrong `bytes' value");
emit log_named_bytes32(" Expected", "[cannot show `bytes' value]");
emit log_named_bytes32(" Actual", "[cannot show `bytes' value]");
fail();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "../../lib/erc20.sol";
contract MockERC20 is ERC20 {
constructor(string memory name, string memory symbol)
public
ERC20(name, symbol)
{}
function mint(address recipient, uint256 amount) public {
_mint(recipient, amount);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "./lib/test-defi-base.sol";
import "../lib/safe-math.sol";
import "../pickle-swap.sol";
contract PickleSwapTest is DSTestDefiBase {
PickleSwap pickleSwap;
function setUp() public {
pickleSwap = new PickleSwap();
}
function _test_uni_lp_swap(address lp1, address lp2) internal {
_getUniV2LPToken(lp1, 20 ether);
uint256 _balance = IERC20(lp1).balanceOf(address(this));
uint256 _before = IERC20(lp2).balanceOf(address(this));
IERC20(lp1).safeIncreaseAllowance(address(pickleSwap), _balance);
pickleSwap.convertWETHPair(lp1, lp2, _balance);
uint256 _after = IERC20(lp2).balanceOf(address(this));
assertTrue(_after > _before);
assertTrue(_after > 0);
}
function test_pickleswap_dai_usdc() public {
_test_uni_lp_swap(
univ2Factory.getPair(weth, dai),
univ2Factory.getPair(weth, usdc)
);
}
function test_pickleswap_dai_usdt() public {
_test_uni_lp_swap(
univ2Factory.getPair(weth, dai),
univ2Factory.getPair(weth, usdt)
);
}
function test_pickleswap_usdt_susd() public {
_test_uni_lp_swap(
univ2Factory.getPair(weth, usdt),
univ2Factory.getPair(weth, susd)
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "./lib/test-defi-base.sol";
import "../lib/safe-math.sol";
import "../uni-curve-converter.sol";
contract UniCurveConverterTest is DSTestDefiBase {
UniCurveConverter uniCurveConverter;
function setUp() public {
uniCurveConverter = new UniCurveConverter();
}
function _test_uni_curve_converter(address token0, address token1)
internal
{
address lp = univ2Factory.getPair(token0, token1);
_getUniV2LPToken(lp, 100 ether);
uint256 _balance = IERC20(lp).balanceOf(address(this));
IERC20(lp).safeApprove(address(uniCurveConverter), 0);
IERC20(lp).safeApprove(address(uniCurveConverter), uint256(-1));
uint256 _before = IERC20(scrv).balanceOf(address(this));
uniCurveConverter.convert(lp, _balance);
uint256 _after = IERC20(scrv).balanceOf(address(this));
// Gets scrv
assertTrue(_after > _before);
assertTrue(_after > 0);
// No token left behind in router
assertEq(IERC20(token0).balanceOf(address(uniCurveConverter)), 0);
assertEq(IERC20(token1).balanceOf(address(uniCurveConverter)), 0);
assertEq(IERC20(weth).balanceOf(address(uniCurveConverter)), 0);
assertEq(IERC20(dai).balanceOf(address(uniCurveConverter)), 0);
assertEq(IERC20(usdc).balanceOf(address(uniCurveConverter)), 0);
assertEq(IERC20(usdt).balanceOf(address(uniCurveConverter)), 0);
assertEq(IERC20(susd).balanceOf(address(uniCurveConverter)), 0);
}
function test_uni_curve_convert_dai_weth() public {
_test_uni_curve_converter(dai, weth);
}
function test_uni_curve_convert_usdt_weth() public {
_test_uni_curve_converter(usdt, weth);
}
function test_uni_curve_convert_wbtc_weth() public {
_test_uni_curve_converter(wbtc, weth);
}
}pragma solidity ^0.6.7;
import "../../lib/hevm.sol";
import "../../lib/user.sol";
import "../../lib/test-approx.sol";
import "../../lib/test-defi-base.sol";
import "../../../interfaces/strategy.sol";
import "../../../interfaces/curve.sol";
import "../../../interfaces/uniswapv2.sol";
import "../../../pickle-jar.sol";
import "../../../controller-v4.sol";
import "../../../strategies/curve/strategy-curve-scrv-v4_1.sol";
import "../../../strategies/curve/scrv-voter.sol";
import "../../../strategies/curve/crv-locker.sol";
contract StrategyCurveSCRVv4Test is DSTestDefiBase {
address escrow = 0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2;
address curveSmartContractChecker = 0xca719728Ef172d0961768581fdF35CB116e0B7a4;
address governance;
address strategist;
address timelock;
address devfund;
address treasury;
PickleJar pickleJar;
ControllerV4 controller;
StrategyCurveSCRVv4_1 strategy;
SCRVVoter scrvVoter;
CRVLocker crvLocker;
function setUp() public {
governance = address(this);
strategist = address(new User());
timelock = address(this);
devfund = address(new User());
treasury = address(new User());
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
crvLocker = new CRVLocker(governance);
scrvVoter = new SCRVVoter(governance, address(crvLocker));
strategy = new StrategyCurveSCRVv4_1(
address(scrvVoter),
address(crvLocker),
governance,
strategist,
address(controller),
timelock
);
pickleJar = new PickleJar(
strategy.want(),
governance,
timelock,
address(controller)
);
controller.setJar(strategy.want(), address(pickleJar));
controller.approveStrategy(strategy.want(), address(strategy));
controller.setStrategy(strategy.want(), address(strategy));
scrvVoter.approveStrategy(address(strategy));
scrvVoter.approveStrategy(governance);
crvLocker.addVoter(address(scrvVoter));
hevm.warp(startTime);
// Approve our strategy on smartContractWhitelist
// Modify storage value so we are approved by the smart-wallet-white-list
// storage in solidity - https://ethereum.stackexchange.com/a/41304
bytes32 key = bytes32(uint256(address(crvLocker)));
bytes32 pos = bytes32(0); // pos 0 as its the first state variable
bytes32 loc = keccak256(abi.encodePacked(key, pos));
hevm.store(curveSmartContractChecker, loc, bytes32(uint256(1)));
// Make sure our crvLocker is whitelisted
assertTrue(
ICurveSmartContractChecker(curveSmartContractChecker).wallets(
address(crvLocker)
)
);
}
function _getSCRV(uint256 daiAmount) internal {
_getERC20(dai, daiAmount);
uint256[4] memory liquidity;
liquidity[0] = IERC20(dai).balanceOf(address(this));
IERC20(dai).approve(susdv2_pool, liquidity[0]);
ICurveFi_4(susdv2_pool).add_liquidity(liquidity, 0);
}
// **** Tests ****
function test_scrv_v4_1_withdraw() public {
_getSCRV(10000000 ether); // 1 million DAI
uint256 _scrv = IERC20(scrv).balanceOf(address(this));
IERC20(scrv).approve(address(pickleJar), _scrv);
pickleJar.deposit(_scrv);
// Deposits to strategy
pickleJar.earn();
// Fast forwards
hevm.warp(block.timestamp + 1 weeks);
strategy.harvest();
// Withdraws back to pickleJar
uint256 _before = IERC20(scrv).balanceOf(address(pickleJar));
controller.withdrawAll(scrv);
uint256 _after = IERC20(scrv).balanceOf(address(pickleJar));
assertTrue(_after > _before);
_before = IERC20(scrv).balanceOf(address(this));
pickleJar.withdrawAll();
_after = IERC20(scrv).balanceOf(address(this));
assertTrue(_after > _before);
// Gained some interest
assertTrue(_after > _scrv);
}
function test_scrv_v4_1_get_earn_harvest_rewards() public {
address dev = controller.devfund();
// Deposit sCRV, and earn
_getSCRV(10000000 ether); // 1 million DAI
uint256 _scrv = IERC20(scrv).balanceOf(address(this));
IERC20(scrv).approve(address(pickleJar), _scrv);
pickleJar.deposit(_scrv);
pickleJar.earn();
// Fast forward one week
hevm.warp(block.timestamp + 1 weeks);
// Call the harvest function
uint256 _before = pickleJar.balance();
uint256 _rewardsBefore = IERC20(scrv).balanceOf(treasury);
User(strategist).execute(address(strategy), 0, "harvest()", "");
uint256 _after = pickleJar.balance();
uint256 _rewardsAfter = IERC20(scrv).balanceOf(treasury);
uint256 earned = _after.sub(_before).mul(1000).div(955);
uint256 earnedRewards = earned.mul(45).div(1000); // 4.5%
uint256 actualRewardsEarned = _rewardsAfter.sub(_rewardsBefore);
// 4.5% performance fee is given
assertEqApprox(earnedRewards, actualRewardsEarned);
// Withdraw
uint256 _devBefore = IERC20(scrv).balanceOf(dev);
uint256 _stratBal = strategy.balanceOf();
pickleJar.withdrawAll();
uint256 _devAfter = IERC20(scrv).balanceOf(dev);
// 0.175% goes to dev
uint256 _devFund = _devAfter.sub(_devBefore);
assertEq(_devFund, _stratBal.mul(175).div(100000));
}
function test_scrv_v4_1_lock() public {
// Deposit sCRV, and earn
_getSCRV(10000000 ether); // 1 million DAI
uint256 _scrv = IERC20(scrv).balanceOf(address(this));
IERC20(scrv).approve(address(pickleJar), _scrv);
pickleJar.deposit(_scrv);
pickleJar.earn();
// Fast forward one week
hevm.warp(block.timestamp + 1 weeks);
uint256 _before = IERC20(crv).balanceOf(address(crvLocker));
// Call the harvest function
strategy.harvest();
// Make sure we can open lock
uint256 _after = IERC20(crv).balanceOf(address(crvLocker));
assertTrue(_after > _before);
// Create a lock
crvLocker.createLock(_after, block.timestamp + 5 weeks);
// Harvest etc
hevm.warp(block.timestamp + 1 weeks);
strategy.harvest();
// Increase amount
crvLocker.increaseAmount(IERC20(crv).balanceOf(address(crvLocker)));
// Increase unlockTime
crvLocker.increaseUnlockTime(block.timestamp + 5 weeks);
// Fast forward
hevm.warp(block.timestamp + 5 weeks + 1 hours);
// Withdraw
_before = IERC20(crv).balanceOf(address(crvLocker));
crvLocker.release();
_after = IERC20(crv).balanceOf(address(crvLocker));
assertTrue(_after > _before);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "../../lib/hevm.sol";
import "../../lib/user.sol";
import "../../lib/test-approx.sol";
import "../../lib/test-defi-base.sol";
import "../../lib/test-strategy-curve-farm-base.sol";
import "../../../interfaces/strategy.sol";
import "../../../interfaces/curve.sol";
import "../../../interfaces/uniswapv2.sol";
import "../../../pickle-jar.sol";
import "../../../controller-v4.sol";
import "../../../strategies/curve/strategy-curve-scrv-v3_2.sol";
contract StrategyCurveSCRVv3_2Test is StrategyCurveFarmTestBase {
function setUp() public {
governance = address(this);
strategist = address(this);
devfund = address(new User());
treasury = address(new User());
timelock = address(this);
want = scrv;
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
strategy = IStrategy(
address(
new StrategyCurveSCRVv3_2(
governance,
strategist,
address(controller),
timelock
)
)
);
pickleJar = new PickleJar(
strategy.want(),
governance,
timelock,
address(controller)
);
controller.setJar(strategy.want(), address(pickleJar));
controller.approveStrategy(strategy.want(), address(strategy));
controller.setStrategy(strategy.want(), address(strategy));
hevm.warp(startTime);
_getWant(10000000 ether);
}
function _getWant(uint256 daiAmount) internal {
_getERC20(dai, daiAmount);
uint256[4] memory liquidity;
liquidity[0] = IERC20(dai).balanceOf(address(this));
IERC20(dai).approve(susdv2_pool, liquidity[0]);
ICurveFi_4(susdv2_pool).add_liquidity(liquidity, 0);
}
// **** Tests **** //
function test_scrv_v3_1_withdraw() public {
_test_withdraw();
}
function test_scrv_v3_1_earn_harvest_rewards() public {
_test_get_earn_harvest_rewards();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "../../lib/hevm.sol";
import "../../lib/user.sol";
import "../../lib/test-approx.sol";
import "../../lib/test-defi-base.sol";
import "../../lib/test-strategy-curve-farm-base.sol";
import "../../../interfaces/strategy.sol";
import "../../../interfaces/curve.sol";
import "../../../interfaces/uniswapv2.sol";
import "../../../pickle-jar.sol";
import "../../../controller-v4.sol";
import "../../../strategies/curve/strategy-curve-3crv-v2.sol";
contract StrategyCurve3CRVv2Test is StrategyCurveFarmTestBase {
function setUp() public {
governance = address(this);
strategist = address(this);
devfund = address(new User());
treasury = address(new User());
timelock = address(this);
want = three_crv;
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
strategy = IStrategy(
address(
new StrategyCurve3CRVv2(
governance,
strategist,
address(controller),
timelock
)
)
);
pickleJar = new PickleJar(
strategy.want(),
governance,
timelock,
address(controller)
);
controller.setJar(strategy.want(), address(pickleJar));
controller.approveStrategy(strategy.want(), address(strategy));
controller.setStrategy(strategy.want(), address(strategy));
hevm.warp(startTime);
_getWant(10000000 ether);
}
function _getWant(uint256 daiAmount) internal {
_getERC20(dai, daiAmount);
uint256[3] memory liquidity;
liquidity[0] = IERC20(dai).balanceOf(address(this));
IERC20(dai).approve(three_pool, liquidity[0]);
ICurveFi_3(three_pool).add_liquidity(liquidity, 0);
}
// **** Tests **** //
function test_3crv_v1_withdraw() public {
_test_withdraw();
}
function test_3crv_v1_earn_harvest_rewards() public {
_test_get_earn_harvest_rewards();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "../../lib/hevm.sol";
import "../../lib/user.sol";
import "../../lib/test-approx.sol";
import "../../lib/test-defi-base.sol";
import "../../lib/test-strategy-curve-farm-base.sol";
import "../../../interfaces/strategy.sol";
import "../../../interfaces/curve.sol";
import "../../../interfaces/uniswapv2.sol";
import "../../../pickle-jar.sol";
import "../../../controller-v4.sol";
import "../../../strategies/curve/strategy-curve-rencrv-v2.sol";
contract StrategyCurveRenCRVv2Test is StrategyCurveFarmTestBase {
function setUp() public {
governance = address(this);
strategist = address(this);
devfund = address(new User());
treasury = address(new User());
timelock = address(this);
want = ren_crv;
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
strategy = IStrategy(
address(
new StrategyCurveRenCRVv2(
governance,
strategist,
address(controller),
timelock
)
)
);
pickleJar = new PickleJar(
strategy.want(),
governance,
timelock,
address(controller)
);
controller.setJar(strategy.want(), address(pickleJar));
controller.approveStrategy(strategy.want(), address(strategy));
controller.setStrategy(strategy.want(), address(strategy));
hevm.warp(startTime);
_getWant(10e8); // 10 wbtc
}
function _getWant(uint256 btcAmount) internal {
_getERC20(wbtc, btcAmount);
uint256[2] memory liquidity;
liquidity[1] = IERC20(wbtc).balanceOf(address(this));
IERC20(wbtc).approve(ren_pool, liquidity[1]);
ICurveFi_2(ren_pool).add_liquidity(liquidity, 0);
}
// **** Tests **** //
function test_rencrv_v1_withdraw() public {
_test_withdraw();
}
function test_rencrv_v1_earn_harvest_rewards() public {
_test_get_earn_harvest_rewards();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "../../lib/hevm.sol";
import "../../lib/user.sol";
import "../../lib/test-approx.sol";
import "../../lib/test-defi-base.sol";
import "../../../interfaces/compound.sol";
import "../../../pickle-jar.sol";
import "../../../controller-v4.sol";
import "../../../strategies/compound/strategy-cmpd-dai-v2.sol";
contract StrategyCmpndDaiV1 is DSTestDefiBase {
StrategyCmpdDaiV2 strategy;
ControllerV4 controller;
PickleJar pickleJar;
address governance;
address strategist;
address timelock;
address devfund;
address treasury;
address want;
function setUp() public {
want = dai;
governance = address(this);
strategist = address(new User());
timelock = address(this);
devfund = address(new User());
treasury = address(new User());
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
strategy = new StrategyCmpdDaiV2(
governance,
strategist,
address(controller),
timelock
);
pickleJar = new PickleJar(
strategy.want(),
governance,
timelock,
address(controller)
);
controller.setJar(strategy.want(), address(pickleJar));
controller.approveStrategy(strategy.want(), address(strategy));
controller.setStrategy(strategy.want(), address(strategy));
}
function testFail_cmpnd_dai_v1_onlyKeeper_leverage() public {
_getERC20(want, 100e18);
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
User randomUser = new User();
randomUser.execute(address(strategy), 0, "leverageToMax()", "");
}
function testFail_cmpnd_dai_v1_onlyKeeper_deleverage() public {
_getERC20(want, 100e18);
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
strategy.leverageToMax();
User randomUser = new User();
randomUser.execute(address(strategy), 0, "deleverageToMin()", "");
}
function test_cmpnd_dai_v1_comp_accrued() public {
_getERC20(want, 1000000e18);
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
pickleJar.earn();
strategy.leverageToMax();
uint256 compAccrued = strategy.getCompAccrued();
assertEq(compAccrued, 0);
hevm.warp(block.timestamp + 1 days);
hevm.roll(block.number + 6171); // Roughly number of blocks per day
compAccrued = strategy.getCompAccrued();
assertTrue(compAccrued > 0);
}
function test_cmpnd_dai_v1_comp_sync() public {
_getERC20(want, 1000000e18);
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
pickleJar.earn();
// Sets colFactor Buffer to be 3% (safeSync is 5%)
strategy.setColFactorLeverageBuffer(30);
strategy.leverageToMax();
// Back to 10%
strategy.setColFactorLeverageBuffer(100);
uint256 colFactor = strategy.getColFactor();
uint256 safeColFactor = strategy.getSafeLeverageColFactor();
assertTrue(colFactor > safeColFactor);
// Sync automatically fixes the colFactor for us
bool shouldSync = strategy.sync();
assertTrue(shouldSync);
colFactor = strategy.getColFactor();
assertEqApprox(colFactor, safeColFactor);
shouldSync = strategy.sync();
assertTrue(!shouldSync);
}
function test_cmpnd_dai_v1_leverage() public {
_getERC20(want, 100e18);
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
pickleJar.earn();
uint256 _stratInitialBal = strategy.balanceOf();
uint256 _beforeCR = strategy.getColFactor();
uint256 _beforeLev = strategy.getCurrentLeverage();
strategy.leverageToMax();
uint256 _afterCR = strategy.getColFactor();
uint256 _afterLev = strategy.getCurrentLeverage();
uint256 _safeLeverageColFactor = strategy.getSafeLeverageColFactor();
assertTrue(_afterCR > _beforeCR);
assertTrue(_afterLev > _beforeLev);
assertEqApprox(_safeLeverageColFactor, _afterCR);
uint256 _maxLeverage = strategy.getMaxLeverage();
assertTrue(_maxLeverage > 2e18); // Should be ~2.6, depending on colFactorLeverageBuffer
uint256 leverageTarget = strategy.getLeveragedSupplyTarget(
_stratInitialBal
);
uint256 leverageSupplied = strategy.getSupplied();
assertEqApprox(
leverageSupplied,
_stratInitialBal.mul(_maxLeverage).div(1e18)
);
assertEqApprox(leverageSupplied, leverageTarget);
uint256 unleveragedSupplied = strategy.getSuppliedUnleveraged();
assertEqApprox(unleveragedSupplied, _stratInitialBal);
}
function test_cmpnd_dai_v1_deleverage() public {
_getERC20(want, 100e18);
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
pickleJar.earn();
strategy.leverageToMax();
uint256 _beforeCR = strategy.getColFactor();
uint256 _beforeLev = strategy.getCurrentLeverage();
strategy.deleverageToMin();
uint256 _afterCR = strategy.getColFactor();
uint256 _afterLev = strategy.getCurrentLeverage();
assertTrue(_afterCR < _beforeCR);
assertTrue(_afterLev < _beforeLev);
assertEq(0, _afterCR); // 0 since we're not borrowing anything
uint256 unleveragedSupplied = strategy.getSuppliedUnleveraged();
uint256 supplied = strategy.getSupplied();
assertEqApprox(unleveragedSupplied, supplied);
}
function test_cmpnd_dai_v1_withdrawSome() public {
_getERC20(want, 100e18);
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
pickleJar.earn();
strategy.leverageToMax();
uint256 _before = IERC20(want).balanceOf(address(this));
pickleJar.withdraw(25e18);
uint256 _after = IERC20(want).balanceOf(address(this));
assertTrue(_after > _before);
assertEqApprox(_after.sub(_before), 25e18);
_before = IERC20(want).balanceOf(address(this));
pickleJar.withdraw(10e18);
_after = IERC20(want).balanceOf(address(this));
assertTrue(_after > _before);
assertEqApprox(_after.sub(_before), 10e18);
_before = IERC20(want).balanceOf(address(this));
pickleJar.withdraw(30e18);
_after = IERC20(want).balanceOf(address(this));
assertTrue(_after > _before);
assertEqApprox(_after.sub(_before), 30e18);
// Make sure we're still leveraging
uint256 _leverage = strategy.getCurrentLeverage();
assertTrue(_leverage > 1e18);
}
function test_cmpnd_dai_v1_withdrawAll() public {
_getERC20(want, 100e18);
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
pickleJar.earn();
strategy.leverageToMax();
hevm.warp(block.timestamp + 1 days);
hevm.roll(block.number + 6171); // Roughly number of blocks per day
strategy.harvest();
// Withdraws back to pickleJar
uint256 _before = IERC20(want).balanceOf(address(pickleJar));
controller.withdrawAll(want);
uint256 _after = IERC20(want).balanceOf(address(pickleJar));
assertTrue(_after > _before);
_before = IERC20(want).balanceOf(address(this));
pickleJar.withdrawAll();
_after = IERC20(want).balanceOf(address(this));
assertTrue(_after > _before);
// Gained some interest
assertTrue(_after > _want);
}
function test_cmpnd_dai_v1_earn_harvest_rewards() public {
_getERC20(want, 100e18);
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
pickleJar.earn();
// Fast forward one week
hevm.warp(block.timestamp + 1 days);
hevm.roll(block.number + 6171); // Roughly number of blocks per day
// Call the harvest function
uint256 _before = strategy.getSupplied();
uint256 _treasuryBefore = IERC20(want).balanceOf(treasury);
strategy.harvest();
uint256 _after = strategy.getSupplied();
uint256 _treasuryAfter = IERC20(want).balanceOf(treasury);
uint256 earned = _after.sub(_before).mul(1000).div(955);
uint256 earnedRewards = earned.mul(45).div(1000); // 4.5%
uint256 actualRewardsEarned = _treasuryAfter.sub(_treasuryBefore);
// 4.5% performance fee is given
assertEqApprox(earnedRewards, actualRewardsEarned);
// Withdraw
uint256 _devBefore = IERC20(want).balanceOf(devfund);
_treasuryBefore = IERC20(want).balanceOf(treasury);
uint256 _stratBal = strategy.balanceOf();
pickleJar.withdrawAll();
uint256 _devAfter = IERC20(want).balanceOf(devfund);
_treasuryAfter = IERC20(want).balanceOf(treasury);
// 0.175% goes to dev
uint256 _devFund = _devAfter.sub(_devBefore);
assertEq(_devFund, _stratBal.mul(175).div(100000));
// 0.325% goes to treasury
uint256 _treasuryFund = _treasuryAfter.sub(_treasuryBefore);
assertEq(_treasuryFund, _stratBal.mul(325).div(100000));
}
function test_cmpnd_dai_v1_functions() public {
_getERC20(want, 100e18);
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
pickleJar.earn();
uint256 initialSupplied = strategy.getSupplied();
uint256 initialBorrowed = strategy.getBorrowed();
uint256 initialBorrowable = strategy.getBorrowable();
uint256 marketColFactor = strategy.getMarketColFactor();
uint256 maxLeverage = strategy.getMaxLeverage();
// Earn deposits 95% into strategy
assertEqApprox(initialSupplied, 95e18);
assertEqApprox(
initialBorrowable,
initialSupplied.mul(marketColFactor).div(1e18)
);
assertEqApprox(initialBorrowed, 0);
// Leverage to Max
strategy.leverageToMax();
uint256 supplied = strategy.getSupplied();
uint256 borrowed = strategy.getBorrowed();
uint256 borrowable = strategy.getBorrowable();
uint256 currentColFactor = strategy.getColFactor();
uint256 safeLeverageColFactor = strategy.getSafeLeverageColFactor();
assertEqApprox(supplied, initialSupplied.mul(maxLeverage).div(1e18));
assertEqApprox(borrowed, supplied.mul(safeLeverageColFactor).div(1e18));
assertEqApprox(
borrowable,
supplied.mul(marketColFactor.sub(currentColFactor)).div(1e18)
);
assertEqApprox(currentColFactor, safeLeverageColFactor);
assertTrue(marketColFactor > currentColFactor);
assertTrue(marketColFactor > safeLeverageColFactor);
// Deleverage
strategy.deleverageToMin();
uint256 deleverageSupplied = strategy.getSupplied();
uint256 deleverageBorrowed = strategy.getBorrowed();
uint256 deleverageBorrowable = strategy.getBorrowable();
assertEqApprox(deleverageSupplied, initialSupplied);
assertEqApprox(deleverageBorrowed, initialBorrowed);
assertEqApprox(deleverageBorrowable, initialBorrowable);
}
function test_cmpnd_dai_v1_deleverage_stepping() public {
_getERC20(want, 100e18);
uint256 _want = IERC20(want).balanceOf(address(this));
IERC20(want).approve(address(pickleJar), _want);
pickleJar.deposit(_want);
pickleJar.earn();
strategy.leverageToMax();
strategy.deleverageUntil(200e18);
uint256 supplied = strategy.getSupplied();
assertEqApprox(supplied, 200e18);
strategy.deleverageUntil(180e18);
supplied = strategy.getSupplied();
assertEqApprox(supplied, 180e18);
strategy.deleverageUntil(120e18);
supplied = strategy.getSupplied();
assertEqApprox(supplied, 120e18);
}
}pragma solidity ^0.6.7;
import "../../lib/test-strategy-uni-farm-base.sol";
import "../../../interfaces/strategy.sol";
import "../../../interfaces/curve.sol";
import "../../../interfaces/uniswapv2.sol";
import "../../../pickle-jar.sol";
import "../../../controller-v4.sol";
import "../../../strategies/uniswapv2/strategy-uni-eth-usdc-lp-v4.sol";
contract StrategyUniEthUsdcLpV4Test is StrategyUniFarmTestBase {
function setUp() public {
want = 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc;
token1 = usdc;
governance = address(this);
strategist = address(this);
devfund = address(new User());
treasury = address(new User());
timelock = address(this);
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
strategy = IStrategy(
address(
new StrategyUniEthUsdcLpV4(
governance,
strategist,
address(controller),
timelock
)
)
);
pickleJar = new PickleJar(
strategy.want(),
governance,
timelock,
address(controller)
);
controller.setJar(strategy.want(), address(pickleJar));
controller.approveStrategy(strategy.want(), address(strategy));
controller.setStrategy(strategy.want(), address(strategy));
// Set time
hevm.warp(startTime);
}
// **** Tests ****
function test_ethusdcv3_1_timelock() public {
_test_timelock();
}
function test_ethusdcv3_1_withdraw_release() public {
_test_withdraw_release();
}
function test_ethusdcv3_1_get_earn_harvest_rewards() public {
_test_get_earn_harvest_rewards();
}
}pragma solidity ^0.6.7;
import "../../lib/test-strategy-uni-farm-base.sol";
import "../../../interfaces/strategy.sol";
import "../../../interfaces/curve.sol";
import "../../../interfaces/uniswapv2.sol";
import "../../../pickle-jar.sol";
import "../../../controller-v4.sol";
import "../../../strategies/uniswapv2/strategy-uni-eth-dai-lp-v4.sol";
contract StrategyUniEthDaiLpV4Test is StrategyUniFarmTestBase {
function setUp() public {
want = 0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11;
token1 = dai;
governance = address(this);
strategist = address(this);
devfund = address(new User());
treasury = address(new User());
timelock = address(this);
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
strategy = IStrategy(
address(
new StrategyUniEthDaiLpV4(
governance,
strategist,
address(controller),
timelock
)
)
);
pickleJar = new PickleJar(
strategy.want(),
governance,
timelock,
address(controller)
);
controller.setJar(strategy.want(), address(pickleJar));
controller.approveStrategy(strategy.want(), address(strategy));
controller.setStrategy(strategy.want(), address(strategy));
// Set time
hevm.warp(startTime);
}
// **** Tests ****
function test_ethdaiv3_1_timelock() public {
_test_timelock();
}
function test_ethdaiv3_1_withdraw_release() public {
_test_withdraw_release();
}
function test_ethdaiv3_1_get_earn_harvest_rewards() public {
_test_get_earn_harvest_rewards();
}
}pragma solidity ^0.6.7;
import "../../lib/test-strategy-uni-farm-base.sol";
import "../../../interfaces/strategy.sol";
import "../../../interfaces/curve.sol";
import "../../../interfaces/uniswapv2.sol";
import "../../../pickle-jar.sol";
import "../../../controller-v4.sol";
import "../../../strategies/uniswapv2/strategy-uni-eth-usdt-lp-v4.sol";
contract StrategyUniEthUsdtLpV4Test is StrategyUniFarmTestBase {
function setUp() public {
want = 0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852;
token1 = usdt;
governance = address(this);
strategist = address(this);
devfund = address(new User());
treasury = address(new User());
timelock = address(this);
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
strategy = IStrategy(
address(
new StrategyUniEthUsdtLpV4(
governance,
strategist,
address(controller),
timelock
)
)
);
pickleJar = new PickleJar(
strategy.want(),
governance,
timelock,
address(controller)
);
controller.setJar(strategy.want(), address(pickleJar));
controller.approveStrategy(strategy.want(), address(strategy));
controller.setStrategy(strategy.want(), address(strategy));
// Set time
hevm.warp(startTime);
}
// **** Tests ****
function test_ethusdtv3_1_timelock() public {
_test_timelock();
}
function test_ethusdtv3_1_withdraw_release() public {
_test_withdraw_release();
}
function test_ethusdtv3_1_get_earn_harvest_rewards() public {
_test_get_earn_harvest_rewards();
}
}pragma solidity ^0.6.7;
import "../../lib/test-strategy-uni-farm-base.sol";
import "../../../interfaces/strategy.sol";
import "../../../interfaces/curve.sol";
import "../../../interfaces/uniswapv2.sol";
import "../../../pickle-jar.sol";
import "../../../controller-v4.sol";
import "../../../strategies/uniswapv2/strategy-uni-eth-wbtc-lp-v2.sol";
contract StrategyUniEthWBtcLpV2Test is StrategyUniFarmTestBase {
function setUp() public {
want = 0xBb2b8038a1640196FbE3e38816F3e67Cba72D940;
token1 = wbtc;
governance = address(this);
strategist = address(this);
devfund = address(new User());
treasury = address(new User());
timelock = address(this);
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
strategy = IStrategy(
address(
new StrategyUniEthWBtcLpV2(
governance,
strategist,
address(controller),
timelock
)
)
);
pickleJar = new PickleJar(
strategy.want(),
governance,
timelock,
address(controller)
);
controller.setJar(strategy.want(), address(pickleJar));
controller.approveStrategy(strategy.want(), address(strategy));
controller.setStrategy(strategy.want(), address(strategy));
// Set time
hevm.warp(startTime);
}
// **** Tests ****
function test_ethwbtcv1_timelock() public {
_test_timelock();
}
function test_ethwbtcv1_withdraw_release() public {
_test_withdraw_release();
}
function test_ethwbtcv1_get_earn_harvest_rewards() public {
_test_get_earn_harvest_rewards();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "../lib/safe-math.sol";
import "../staking-rewards.sol";
import "./lib/test.sol";
import "./lib/mock-erc20.sol";
import "./lib/hevm.sol";
contract StakngRewardsTest is DSTest {
using SafeMath for uint256;
MockERC20 stakingToken;
MockERC20 rewardsToken;
StakingRewards stakingRewards;
address owner;
Hevm hevm = Hevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function setUp() public {
owner = address(this);
stakingToken = new MockERC20("staking", "STAKE");
rewardsToken = new MockERC20("rewards", "RWD");
stakingRewards = new StakingRewards(
owner,
address(rewardsToken),
address(stakingToken)
);
}
function test_staking() public {
uint256 stakeAmount = 100 ether;
uint256 rewardAmount = 100 ether;
stakingToken.mint(owner, stakeAmount);
rewardsToken.mint(owner, rewardAmount);
stakingToken.approve(address(stakingRewards), stakeAmount);
stakingRewards.stake(stakeAmount);
// // Make sure nothing is earned
uint256 _before = stakingRewards.earned(owner);
assertEq(_before, 0);
// Fast forward
hevm.warp(block.timestamp + 1 days);
// No funds until we actually supply funds
uint256 _after = stakingRewards.earned(owner);
assertEq(_after, _before);
// Give rewards
rewardsToken.transfer(address(stakingRewards), rewardAmount);
stakingRewards.notifyRewardAmount(rewardAmount);
uint256 _rateBefore = stakingRewards.getRewardForDuration();
assertTrue(_rateBefore > 0);
// Fast forward
_before = stakingRewards.earned(owner);
hevm.warp(block.timestamp + 1 days);
_after = stakingRewards.earned(owner);
assertTrue(_after > _before);
assertTrue(_after > 0);
// Add more rewards, rate should increase
rewardsToken.mint(owner, rewardAmount);
rewardsToken.transfer(address(stakingRewards), rewardAmount);
stakingRewards.notifyRewardAmount(rewardAmount);
uint256 _rateAfter = stakingRewards.getRewardForDuration();
assertTrue(_rateAfter > _rateBefore);
// Warp to period finish
hevm.warp(stakingRewards.periodFinish() + 1 days);
// Retrieve tokens
stakingRewards.getReward();
_before = stakingRewards.earned(owner);
hevm.warp(block.timestamp + 1 days);
_after = stakingRewards.earned(owner);
// Earn 0 after period finished
assertEq(_before, 0);
assertEq(_after, 0);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
pragma experimental ABIEncoderV2;
import "../lib/hevm.sol";
import "../lib/user.sol";
import "../lib/test-approx.sol";
import "../lib/test-defi-base.sol";
import "../../interfaces/strategy.sol";
import "../../interfaces/curve.sol";
import "../../interfaces/uniswapv2.sol";
import "../../pickle-jar.sol";
import "../../controller-v4.sol";
import "../../proxy-logic/curve.sol";
import "../../proxy-logic/uniswapv2.sol";
import "../../strategies/uniswapv2/strategy-uni-eth-dai-lp-v4.sol";
import "../../strategies/uniswapv2/strategy-uni-eth-usdt-lp-v4.sol";
import "../../strategies/uniswapv2/strategy-uni-eth-usdc-lp-v4.sol";
import "../../strategies/uniswapv2/strategy-uni-eth-wbtc-lp-v2.sol";
import "../../strategies/curve/strategy-curve-scrv-v3_2.sol";
import "../../strategies/curve/strategy-curve-rencrv-v2.sol";
import "../../strategies/curve/strategy-curve-3crv-v2.sol";
contract StrategyCurveUniJarSwapTest is DSTestDefiBase {
address governance;
address strategist;
address devfund;
address treasury;
address timelock;
IStrategy[] curveStrategies;
IStrategy[] uniStrategies;
PickleJar[] curvePickleJars;
PickleJar[] uniPickleJars;
ControllerV4 controller;
CurveProxyLogic curveProxyLogic;
UniswapV2ProxyLogic uniswapV2ProxyLogic;
address[] curvePools;
address[] curveLps;
address[] uniUnderlying;
// Contract wide variable to avoid stack too deep errors
uint256 temp;
function setUp() public {
governance = address(this);
strategist = address(this);
devfund = address(new User());
treasury = address(new User());
timelock = address(this);
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
// Curve Strategies
curveStrategies = new IStrategy[](3);
curvePickleJars = new PickleJar[](curveStrategies.length);
curveLps = new address[](curveStrategies.length);
curvePools = new address[](curveStrategies.length);
curveLps[0] = three_crv;
curvePools[0] = three_pool;
curveStrategies[0] = IStrategy(
address(
new StrategyCurve3CRVv2(
governance,
strategist,
address(controller),
timelock
)
)
);
curveLps[1] = scrv;
curvePools[1] = susdv2_pool;
curveStrategies[1] = IStrategy(
address(
new StrategyCurveSCRVv3_2(
governance,
strategist,
address(controller),
timelock
)
)
);
curveLps[2] = ren_crv;
curvePools[2] = ren_pool;
curveStrategies[2] = IStrategy(
address(
new StrategyCurveRenCRVv2(
governance,
strategist,
address(controller),
timelock
)
)
);
// Create PICKLE Jars
for (uint256 i = 0; i < curvePickleJars.length; i++) {
curvePickleJars[i] = new PickleJar(
curveStrategies[i].want(),
governance,
timelock,
address(controller)
);
controller.setJar(
curveStrategies[i].want(),
address(curvePickleJars[i])
);
controller.approveStrategy(
curveStrategies[i].want(),
address(curveStrategies[i])
);
controller.setStrategy(
curveStrategies[i].want(),
address(curveStrategies[i])
);
}
// Uni strategies
uniStrategies = new IStrategy[](4);
uniUnderlying = new address[](uniStrategies.length);
uniPickleJars = new PickleJar[](uniStrategies.length);
uniUnderlying[0] = dai;
uniStrategies[0] = IStrategy(
address(
new StrategyUniEthDaiLpV4(
governance,
strategist,
address(controller),
timelock
)
)
);
uniUnderlying[1] = usdc;
uniStrategies[1] = IStrategy(
address(
new StrategyUniEthUsdcLpV4(
governance,
strategist,
address(controller),
timelock
)
)
);
uniUnderlying[2] = usdt;
uniStrategies[2] = IStrategy(
address(
new StrategyUniEthUsdtLpV4(
governance,
strategist,
address(controller),
timelock
)
)
);
uniUnderlying[3] = wbtc;
uniStrategies[3] = IStrategy(
address(
new StrategyUniEthWBtcLpV2(
governance,
strategist,
address(controller),
timelock
)
)
);
for (uint256 i = 0; i < uniStrategies.length; i++) {
uniPickleJars[i] = new PickleJar(
uniStrategies[i].want(),
governance,
timelock,
address(controller)
);
controller.setJar(
uniStrategies[i].want(),
address(uniPickleJars[i])
);
controller.approveStrategy(
uniStrategies[i].want(),
address(uniStrategies[i])
);
controller.setStrategy(
uniStrategies[i].want(),
address(uniStrategies[i])
);
}
curveProxyLogic = new CurveProxyLogic();
uniswapV2ProxyLogic = new UniswapV2ProxyLogic();
controller.approveJarConverter(address(curveProxyLogic));
controller.approveJarConverter(address(uniswapV2ProxyLogic));
hevm.warp(startTime);
}
function _getCurveLP(address curve, uint256 amount) internal {
if (curve == ren_pool) {
_getERC20(wbtc, amount);
uint256 _wbtc = IERC20(wbtc).balanceOf(address(this));
IERC20(wbtc).approve(curve, _wbtc);
uint256[2] memory liquidity;
liquidity[1] = _wbtc;
ICurveFi_2(curve).add_liquidity(liquidity, 0);
} else {
_getERC20(dai, amount);
uint256 _dai = IERC20(dai).balanceOf(address(this));
IERC20(dai).approve(curve, _dai);
if (curve == three_pool) {
uint256[3] memory liquidity;
liquidity[0] = _dai;
ICurveFi_3(curve).add_liquidity(liquidity, 0);
} else {
uint256[4] memory liquidity;
liquidity[0] = _dai;
ICurveFi_4(curve).add_liquidity(liquidity, 0);
}
}
}
function _get_primitive_to_lp_data(
address from,
address to,
address dustRecipient
) internal pure returns (bytes memory) {
return
abi.encodeWithSignature(
"primitiveToLpTokens(address,address,address)",
from,
to,
dustRecipient
);
}
function _get_curve_remove_liquidity_data(
address curve,
address curveLP,
int128 index
) internal pure returns (bytes memory) {
return
abi.encodeWithSignature(
"remove_liquidity_one_coin(address,address,int128)",
curve,
curveLP,
index
);
}
// Some post swap checks
// Checks if there's any leftover funds in the converter contract
function _post_swap_check(uint256 fromIndex, uint256 toIndex) internal {
IERC20 token0 = curvePickleJars[fromIndex].token();
IUniswapV2Pair token1 = IUniswapV2Pair(
address(uniPickleJars[toIndex].token())
);
uint256 MAX_DUST = 1000;
// No funds left behind
assertEq(curvePickleJars[fromIndex].balanceOf(address(controller)), 0);
assertEq(uniPickleJars[toIndex].balanceOf(address(controller)), 0);
assertTrue(token0.balanceOf(address(controller)) < MAX_DUST);
assertTrue(token1.balanceOf(address(controller)) < MAX_DUST);
// Curve -> UNI LP should be optimal supply
// Note: We refund the access, which is why its checking this balance
assertTrue(IERC20(token1.token0()).balanceOf(address(this)) < MAX_DUST);
assertTrue(IERC20(token1.token1()).balanceOf(address(this)) < MAX_DUST);
// Make sure only controller can call 'withdrawForSwap'
try curveStrategies[fromIndex].withdrawForSwap(0) {
revert("!withdraw-for-swap-only-controller");
} catch {}
}
function _test_check_treasury_fee(uint256 _amount, uint256 earned)
internal
{
assertEqApprox(
_amount.mul(controller.convenienceFee()).div(
controller.convenienceFeeMax()
),
earned.mul(2)
);
}
function _test_swap_and_check_balances(
address fromPickleJar,
address toPickleJar,
address fromPickleJarUnderlying,
uint256 fromPickleJarUnderlyingAmount,
address payable[] memory targets,
bytes[] memory data
) internal {
uint256 _beforeTo = IERC20(toPickleJar).balanceOf(address(this));
uint256 _beforeFrom = IERC20(fromPickleJar).balanceOf(address(this));
uint256 _beforeDev = IERC20(fromPickleJarUnderlying).balanceOf(devfund);
uint256 _beforeTreasury = IERC20(fromPickleJarUnderlying).balanceOf(
treasury
);
uint256 _ret = controller.swapExactJarForJar(
fromPickleJar,
toPickleJar,
fromPickleJarUnderlyingAmount,
0, // Min receive amount
targets,
data
);
uint256 _afterTo = IERC20(toPickleJar).balanceOf(address(this));
uint256 _afterFrom = IERC20(fromPickleJar).balanceOf(address(this));
uint256 _afterDev = IERC20(fromPickleJarUnderlying).balanceOf(devfund);
uint256 _afterTreasury = IERC20(fromPickleJarUnderlying).balanceOf(
treasury
);
uint256 treasuryEarned = _afterTreasury.sub(_beforeTreasury);
assertEq(treasuryEarned, _afterDev.sub(_beforeDev));
assertTrue(treasuryEarned > 0);
_test_check_treasury_fee(fromPickleJarUnderlyingAmount, treasuryEarned);
assertTrue(_afterFrom < _beforeFrom);
assertTrue(_afterTo > _beforeTo);
assertTrue(_afterTo.sub(_beforeTo) > 0);
assertEq(_afterTo.sub(_beforeTo), _ret);
assertEq(_afterFrom, 0);
}
function _test_curve_uni_swap(
uint256 fromIndex,
uint256 toIndex,
uint256 amount,
address payable[] memory targets,
bytes[] memory data
) internal {
// Deposit into PickleJars
address from = address(curvePickleJars[fromIndex].token());
_getCurveLP(curvePools[fromIndex], amount);
uint256 _from = IERC20(from).balanceOf(address(this));
IERC20(from).approve(address(curvePickleJars[fromIndex]), _from);
curvePickleJars[fromIndex].deposit(_from);
curvePickleJars[fromIndex].earn();
// Swap!
uint256 _fromPickleJar = IERC20(address(curvePickleJars[fromIndex]))
.balanceOf(address(this));
IERC20(address(curvePickleJars[fromIndex])).approve(
address(controller),
_fromPickleJar
);
// Check minimum amount
try
controller.swapExactJarForJar(
address(curvePickleJars[fromIndex]),
address(uniPickleJars[toIndex]),
_fromPickleJar,
uint256(-1), // Min receive amount
targets,
data
)
{
revert("min-amount-should-fail");
} catch {}
_test_swap_and_check_balances(
address(curvePickleJars[fromIndex]),
address(uniPickleJars[toIndex]),
from,
_fromPickleJar,
targets,
data
);
_post_swap_check(fromIndex, toIndex);
}
// **** Tests **** //
function test_jar_converter_curve_uni_0_0() public {
uint256 fromIndex = 0;
uint256 toIndex = 0;
uint256 amount = 400e18;
address fromUnderlying = dai;
int128 fromUnderlyingIndex = 0;
address curvePool = curvePools[fromIndex];
address toUnderlying = uniUnderlying[toIndex];
address toWant = univ2Factory.getPair(weth, toUnderlying);
bytes memory data0 = _get_curve_remove_liquidity_data(
curvePool,
curveLps[fromIndex],
fromUnderlyingIndex
);
bytes memory data1 = _get_primitive_to_lp_data(
fromUnderlying,
toWant,
treasury
);
_test_curve_uni_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_curve_uni_0_1() public {
uint256 fromIndex = 0;
uint256 toIndex = 1;
uint256 amount = 400e18;
address fromUnderlying = usdc;
int128 fromUnderlyingIndex = 1;
address curvePool = curvePools[fromIndex];
address toUnderlying = uniUnderlying[toIndex];
address toWant = univ2Factory.getPair(weth, toUnderlying);
bytes memory data0 = _get_curve_remove_liquidity_data(
curvePool,
curveLps[fromIndex],
fromUnderlyingIndex
);
bytes memory data1 = _get_primitive_to_lp_data(
fromUnderlying,
toWant,
treasury
);
_test_curve_uni_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_curve_uni_0_2() public {
uint256 fromIndex = 0;
uint256 toIndex = 2;
uint256 amount = 400e18;
address fromUnderlying = usdt;
int128 fromUnderlyingIndex = 2;
address curvePool = curvePools[fromIndex];
address toUnderlying = uniUnderlying[toIndex];
address toWant = univ2Factory.getPair(weth, toUnderlying);
bytes memory data0 = _get_curve_remove_liquidity_data(
curvePool,
curveLps[fromIndex],
fromUnderlyingIndex
);
bytes memory data1 = _get_primitive_to_lp_data(
fromUnderlying,
toWant,
treasury
);
_test_curve_uni_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_curve_uni_0_3() public {
uint256 fromIndex = 0;
uint256 toIndex = 3;
uint256 amount = 400e18;
address fromUnderlying = usdt;
int128 fromUnderlyingIndex = 2;
address curvePool = curvePools[fromIndex];
address toUnderlying = uniUnderlying[toIndex];
address toWant = univ2Factory.getPair(weth, toUnderlying);
bytes memory data0 = _get_curve_remove_liquidity_data(
curvePool,
curveLps[fromIndex],
fromUnderlyingIndex
);
bytes memory data1 = _get_primitive_to_lp_data(
fromUnderlying,
toWant,
treasury
);
_test_curve_uni_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_curve_uni_1_0() public {
uint256 fromIndex = 1;
uint256 toIndex = 0;
uint256 amount = 400e18;
address fromUnderlying = usdt;
int128 fromUnderlyingIndex = 2;
address curvePool = susdv2_deposit; // curvePools[fromIndex];
address toUnderlying = uniUnderlying[toIndex];
address toWant = univ2Factory.getPair(weth, toUnderlying);
bytes memory data0 = _get_curve_remove_liquidity_data(
curvePool,
curveLps[fromIndex],
fromUnderlyingIndex
);
bytes memory data1 = _get_primitive_to_lp_data(
fromUnderlying,
toWant,
treasury
);
_test_curve_uni_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_curve_uni_1_1() public {
uint256 fromIndex = 1;
uint256 toIndex = 1;
uint256 amount = 400e18;
address fromUnderlying = dai;
int128 fromUnderlyingIndex = 0;
address curvePool = susdv2_deposit; // curvePools[fromIndex];
address toUnderlying = uniUnderlying[toIndex];
address toWant = univ2Factory.getPair(weth, toUnderlying);
bytes memory data0 = _get_curve_remove_liquidity_data(
curvePool,
curveLps[fromIndex],
fromUnderlyingIndex
);
bytes memory data1 = _get_primitive_to_lp_data(
fromUnderlying,
toWant,
treasury
);
_test_curve_uni_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_curve_uni_1_2() public {
uint256 fromIndex = 1;
uint256 toIndex = 2;
uint256 amount = 400e18;
address fromUnderlying = dai;
int128 fromUnderlyingIndex = 0;
address curvePool = susdv2_deposit; // curvePools[fromIndex];
address toUnderlying = uniUnderlying[toIndex];
address toWant = univ2Factory.getPair(weth, toUnderlying);
bytes memory data0 = _get_curve_remove_liquidity_data(
curvePool,
curveLps[fromIndex],
fromUnderlyingIndex
);
bytes memory data1 = _get_primitive_to_lp_data(
fromUnderlying,
toWant,
treasury
);
_test_curve_uni_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_curve_uni_1_3() public {
uint256 fromIndex = 1;
uint256 toIndex = 3;
uint256 amount = 400e18;
address fromUnderlying = dai;
int128 fromUnderlyingIndex = 0;
address curvePool = susdv2_deposit; // curvePools[fromIndex];
address toUnderlying = uniUnderlying[toIndex];
address toWant = univ2Factory.getPair(weth, toUnderlying);
bytes memory data0 = _get_curve_remove_liquidity_data(
curvePool,
curveLps[fromIndex],
fromUnderlyingIndex
);
bytes memory data1 = _get_primitive_to_lp_data(
fromUnderlying,
toWant,
treasury
);
_test_curve_uni_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_curve_uni_2_3() public {
uint256 fromIndex = 2;
uint256 toIndex = 3;
uint256 amount = 4e6;
address fromUnderlying = wbtc;
int128 fromUnderlyingIndex = 1;
address curvePool = curvePools[fromIndex];
address toUnderlying = uniUnderlying[toIndex];
address toWant = univ2Factory.getPair(weth, toUnderlying);
bytes memory data0 = _get_curve_remove_liquidity_data(
curvePool,
curveLps[fromIndex],
fromUnderlyingIndex
);
bytes memory data1 = _get_primitive_to_lp_data(
fromUnderlying,
toWant,
treasury
);
_test_curve_uni_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
pragma experimental ABIEncoderV2;
import "../lib/hevm.sol";
import "../lib/user.sol";
import "../lib/test-approx.sol";
import "../lib/test-defi-base.sol";
import "../../interfaces/strategy.sol";
import "../../interfaces/curve.sol";
import "../../interfaces/uniswapv2.sol";
import "../../pickle-jar.sol";
import "../../controller-v4.sol";
import "../../proxy-logic/curve.sol";
import "../../proxy-logic/uniswapv2.sol";
import "../../strategies/curve/strategy-curve-scrv-v3_2.sol";
import "../../strategies/curve/strategy-curve-rencrv-v2.sol";
import "../../strategies/curve/strategy-curve-3crv-v2.sol";
contract StrategyCurveCurveJarSwapTest is DSTestDefiBase {
address governance;
address strategist;
address devfund;
address treasury;
address timelock;
IStrategy[] curveStrategies;
PickleJar[] curvePickleJars;
ControllerV4 controller;
CurveProxyLogic curveProxyLogic;
UniswapV2ProxyLogic uniswapV2ProxyLogic;
address[] curvePools;
address[] curveLps;
function setUp() public {
governance = address(this);
strategist = address(this);
devfund = address(new User());
treasury = address(new User());
timelock = address(this);
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
// Curve Strategies
curveStrategies = new IStrategy[](3);
curvePickleJars = new PickleJar[](curveStrategies.length);
curveLps = new address[](curveStrategies.length);
curvePools = new address[](curveStrategies.length);
curveLps[0] = three_crv;
curvePools[0] = three_pool;
curveStrategies[0] = IStrategy(
address(
new StrategyCurve3CRVv2(
governance,
strategist,
address(controller),
timelock
)
)
);
curveLps[1] = scrv;
curvePools[1] = susdv2_pool;
curveStrategies[1] = IStrategy(
address(
new StrategyCurveSCRVv3_2(
governance,
strategist,
address(controller),
timelock
)
)
);
curveLps[2] = ren_crv;
curvePools[2] = ren_pool;
curveStrategies[2] = IStrategy(
address(
new StrategyCurveRenCRVv2(
governance,
strategist,
address(controller),
timelock
)
)
);
// Create PICKLE Jars
for (uint256 i = 0; i < curvePickleJars.length; i++) {
curvePickleJars[i] = new PickleJar(
curveStrategies[i].want(),
governance,
timelock,
address(controller)
);
controller.setJar(
curveStrategies[i].want(),
address(curvePickleJars[i])
);
controller.approveStrategy(
curveStrategies[i].want(),
address(curveStrategies[i])
);
controller.setStrategy(
curveStrategies[i].want(),
address(curveStrategies[i])
);
}
curveProxyLogic = new CurveProxyLogic();
uniswapV2ProxyLogic = new UniswapV2ProxyLogic();
controller.approveJarConverter(address(curveProxyLogic));
controller.approveJarConverter(address(uniswapV2ProxyLogic));
hevm.warp(startTime);
}
function _getCurveLP(address curve, uint256 amount) internal {
if (curve == ren_pool) {
_getERC20(wbtc, amount);
uint256 _wbtc = IERC20(wbtc).balanceOf(address(this));
IERC20(wbtc).approve(curve, _wbtc);
uint256[2] memory liquidity;
liquidity[1] = _wbtc;
ICurveFi_2(curve).add_liquidity(liquidity, 0);
} else {
_getERC20(dai, amount);
uint256 _dai = IERC20(dai).balanceOf(address(this));
IERC20(dai).approve(curve, _dai);
if (curve == three_pool) {
uint256[3] memory liquidity;
liquidity[0] = _dai;
ICurveFi_3(curve).add_liquidity(liquidity, 0);
} else {
uint256[4] memory liquidity;
liquidity[0] = _dai;
ICurveFi_4(curve).add_liquidity(liquidity, 0);
}
}
}
// **** Internal functions **** //
// Theres so many internal functions due to stack blowing up
// Some post swap checks
// Checks if there's any leftover funds in the converter contract
function _post_swap_check(uint256 fromIndex, uint256 toIndex) internal {
IERC20 token0 = curvePickleJars[fromIndex].token();
IERC20 token1 = curvePickleJars[toIndex].token();
uint256 MAX_DUST = 10;
// No funds left behind
assertEq(curvePickleJars[fromIndex].balanceOf(address(controller)), 0);
assertEq(curvePickleJars[toIndex].balanceOf(address(controller)), 0);
assertTrue(token0.balanceOf(address(controller)) < MAX_DUST);
assertTrue(token1.balanceOf(address(controller)) < MAX_DUST);
// Make sure only controller can call 'withdrawForSwap'
try curveStrategies[fromIndex].withdrawForSwap(0) {
revert("!withdraw-for-swap-only-controller");
} catch {}
}
function _test_check_treasury_fee(uint256 _amount, uint256 earned)
internal
{
assertEqApprox(
_amount.mul(controller.convenienceFee()).div(
controller.convenienceFeeMax()
),
earned.mul(2)
);
}
function _test_swap_and_check_balances(
address fromPickleJar,
address toPickleJar,
address fromPickleJarUnderlying,
uint256 fromPickleJarUnderlyingAmount,
address payable[] memory targets,
bytes[] memory data
) internal {
uint256 _beforeTo = IERC20(toPickleJar).balanceOf(address(this));
uint256 _beforeFrom = IERC20(fromPickleJar).balanceOf(address(this));
uint256 _beforeDev = IERC20(fromPickleJarUnderlying).balanceOf(devfund);
uint256 _beforeTreasury = IERC20(fromPickleJarUnderlying).balanceOf(
treasury
);
uint256 _ret = controller.swapExactJarForJar(
fromPickleJar,
toPickleJar,
fromPickleJarUnderlyingAmount,
0, // Min receive amount
targets,
data
);
uint256 _afterTo = IERC20(toPickleJar).balanceOf(address(this));
uint256 _afterFrom = IERC20(fromPickleJar).balanceOf(address(this));
uint256 _afterDev = IERC20(fromPickleJarUnderlying).balanceOf(devfund);
uint256 _afterTreasury = IERC20(fromPickleJarUnderlying).balanceOf(
treasury
);
uint256 treasuryEarned = _afterTreasury.sub(_beforeTreasury);
assertEq(treasuryEarned, _afterDev.sub(_beforeDev));
assertTrue(treasuryEarned > 0);
_test_check_treasury_fee(fromPickleJarUnderlyingAmount, treasuryEarned);
assertTrue(_afterFrom < _beforeFrom);
assertTrue(_afterTo > _beforeTo);
assertTrue(_afterTo.sub(_beforeTo) > 0);
assertEq(_afterTo.sub(_beforeTo), _ret);
assertEq(_afterFrom, 0);
}
function _get_uniswap_pl_swap_data(address from, address to)
internal pure
returns (bytes memory)
{
return
abi.encodeWithSignature("swapUniswap(address,address)", from, to);
}
function _test_curve_curve(
uint256 fromIndex,
uint256 toIndex,
uint256 amount,
address payable[] memory targets,
bytes[] memory data
) public {
// Get LP
_getCurveLP(curvePools[fromIndex], amount);
// Deposit into pickle jars
address from = address(curvePickleJars[fromIndex].token());
uint256 _from = IERC20(from).balanceOf(address(this));
IERC20(from).approve(address(curvePickleJars[fromIndex]), _from);
curvePickleJars[fromIndex].deposit(_from);
curvePickleJars[fromIndex].earn();
// Approve controller
uint256 _fromPickleJar = IERC20(address(curvePickleJars[fromIndex]))
.balanceOf(address(this));
IERC20(address(curvePickleJars[fromIndex])).approve(
address(controller),
_fromPickleJar
);
// Swap
try
controller.swapExactJarForJar(
address(curvePickleJars[fromIndex]),
address(curvePickleJars[toIndex]),
_fromPickleJar,
uint256(-1), // Min receive amount
targets,
data
)
{
revert("min-receive-amount");
} catch {}
_test_swap_and_check_balances(
address(curvePickleJars[fromIndex]),
address(curvePickleJars[toIndex]),
from,
_fromPickleJar,
targets,
data
);
_post_swap_check(fromIndex, toIndex);
}
// **** Tests ****
function test_jar_converter_curve_curve_0() public {
uint256 fromIndex = 0;
uint256 toIndex = 1;
uint256 amount = 400e18;
int128 fromCurveUnderlyingIndex = 0;
bytes4 toCurveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[4],uint256)"
);
uint256 toCurvePoolSize = 4;
uint256 toCurveUnderlyingIndex = 0;
address toCurveUnderlying = dai;
// Remove liquidity
address fromCurve = curvePools[fromIndex];
address fromCurveLp = curveLps[fromIndex];
address payable target0 = payable(address(curveProxyLogic));
bytes memory data0 = abi.encodeWithSignature(
"remove_liquidity_one_coin(address,address,int128)",
fromCurve,
fromCurveLp,
fromCurveUnderlyingIndex
);
// Add liquidity
address toCurve = curvePools[toIndex];
address payable target1 = payable(address(curveProxyLogic));
bytes memory data1 = abi.encodeWithSignature(
"add_liquidity(address,bytes4,uint256,uint256,address)",
toCurve,
toCurveFunctionSig,
toCurvePoolSize,
toCurveUnderlyingIndex,
toCurveUnderlying
);
// Swap
_test_curve_curve(
fromIndex,
toIndex,
amount,
_getDynamicArray(target0, target1),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_curve_curve_1() public {
uint256 fromIndex = 0;
uint256 toIndex = 2;
uint256 amount = 400e18;
int128 fromCurveUnderlyingIndex = 0;
bytes4 toCurveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[2],uint256)"
);
uint256 toCurvePoolSize = 2;
uint256 toCurveUnderlyingIndex = 1;
address toCurveUnderlying = wbtc;
// Remove liquidity
address fromCurve = curvePools[fromIndex];
address fromCurveLp = curveLps[fromIndex];
bytes memory data0 = abi.encodeWithSignature(
"remove_liquidity_one_coin(address,address,int128)",
fromCurve,
fromCurveLp,
fromCurveUnderlyingIndex
);
// Swap
bytes memory data1 = _get_uniswap_pl_swap_data(dai, toCurveUnderlying);
// Add liquidity
address toCurve = curvePools[toIndex];
bytes memory data2 = abi.encodeWithSignature(
"add_liquidity(address,bytes4,uint256,uint256,address)",
toCurve,
toCurveFunctionSig,
toCurvePoolSize,
toCurveUnderlyingIndex,
toCurveUnderlying
);
_test_curve_curve(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1, data2)
);
}
function test_jar_converter_curve_curve_2() public {
uint256 fromIndex = 1;
uint256 toIndex = 0;
uint256 amount = 400e18;
int128 fromCurveUnderlyingIndex = 1;
bytes4 toCurveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[3],uint256)"
);
uint256 toCurvePoolSize = 3;
uint256 toCurveUnderlyingIndex = 2;
address toCurveUnderlying = usdt;
// Remove liquidity
address fromCurve = susdv2_deposit; // curvePools[fromIndex];
address fromCurveLp = curveLps[fromIndex];
bytes memory data0 = abi.encodeWithSignature(
"remove_liquidity_one_coin(address,address,int128)",
fromCurve,
fromCurveLp,
fromCurveUnderlyingIndex
);
// Swap
bytes memory data1 = _get_uniswap_pl_swap_data(usdc, usdt);
// Add liquidity
address toCurve = curvePools[toIndex];
bytes memory data2 = abi.encodeWithSignature(
"add_liquidity(address,bytes4,uint256,uint256,address)",
toCurve,
toCurveFunctionSig,
toCurvePoolSize,
toCurveUnderlyingIndex,
toCurveUnderlying
);
_test_curve_curve(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1, data2)
);
}
function test_jar_converter_curve_curve_3() public {
uint256 fromIndex = 2;
uint256 toIndex = 0;
uint256 amount = 4e6;
int128 fromCurveUnderlyingIndex = 1;
bytes4 toCurveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[3],uint256)"
);
uint256 toCurvePoolSize = 3;
uint256 toCurveUnderlyingIndex = 1;
address toCurveUnderlying = usdc;
// Remove liquidity
address fromCurve = curvePools[fromIndex];
address fromCurveLp = curveLps[fromIndex];
bytes memory data0 = abi.encodeWithSignature(
"remove_liquidity_one_coin(address,address,int128)",
fromCurve,
fromCurveLp,
fromCurveUnderlyingIndex
);
// Swap
bytes memory data1 = _get_uniswap_pl_swap_data(wbtc, usdc);
// Add liquidity
address toCurve = curvePools[toIndex];
bytes memory data2 = abi.encodeWithSignature(
"add_liquidity(address,bytes4,uint256,uint256,address)",
toCurve,
toCurveFunctionSig,
toCurvePoolSize,
toCurveUnderlyingIndex,
toCurveUnderlying
);
_test_curve_curve(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1, data2)
);
}
function test_jar_converter_curve_curve_4() public {
uint256 fromIndex = 1;
uint256 toIndex = 0;
uint256 amount = 400e18;
int128 fromCurveUnderlyingIndex = 2;
bytes4 toCurveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[3],uint256)"
);
uint256 toCurvePoolSize = 3;
uint256 toCurveUnderlyingIndex = 1;
address toCurveUnderlying = usdc;
// Remove liquidity
address fromCurve = susdv2_deposit;
address fromCurveLp = curveLps[fromIndex];
bytes memory data0 = abi.encodeWithSignature(
"remove_liquidity_one_coin(address,address,int128)",
fromCurve,
fromCurveLp,
fromCurveUnderlyingIndex
);
// Swap
bytes memory data1 = _get_uniswap_pl_swap_data(usdt, usdc);
// Add liquidity
address toCurve = curvePools[toIndex];
bytes memory data2 = abi.encodeWithSignature(
"add_liquidity(address,bytes4,uint256,uint256,address)",
toCurve,
toCurveFunctionSig,
toCurvePoolSize,
toCurveUnderlyingIndex,
toCurveUnderlying
);
_test_curve_curve(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(curveProxyLogic)),
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1, data2)
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
pragma experimental ABIEncoderV2;
import "../lib/hevm.sol";
import "../lib/user.sol";
import "../lib/test-approx.sol";
import "../lib/test-defi-base.sol";
import "../../interfaces/strategy.sol";
import "../../interfaces/curve.sol";
import "../../interfaces/uniswapv2.sol";
import "../../pickle-jar.sol";
import "../../controller-v4.sol";
import "../../proxy-logic/curve.sol";
import "../../proxy-logic/uniswapv2.sol";
import "../../strategies/uniswapv2/strategy-uni-eth-dai-lp-v4.sol";
import "../../strategies/uniswapv2/strategy-uni-eth-usdt-lp-v4.sol";
import "../../strategies/uniswapv2/strategy-uni-eth-usdc-lp-v4.sol";
import "../../strategies/uniswapv2/strategy-uni-eth-wbtc-lp-v2.sol";
contract StrategyUniUniJarSwapTest is DSTestDefiBase {
address governance;
address strategist;
address devfund;
address treasury;
address timelock;
IStrategy[] uniStrategies;
PickleJar[] uniPickleJars;
ControllerV4 controller;
CurveProxyLogic curveProxyLogic;
UniswapV2ProxyLogic uniswapV2ProxyLogic;
address[] uniUnderlying;
function setUp() public {
governance = address(this);
strategist = address(this);
devfund = address(new User());
treasury = address(new User());
timelock = address(this);
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
// Uni strategies
uniStrategies = new IStrategy[](4);
uniUnderlying = new address[](uniStrategies.length);
uniPickleJars = new PickleJar[](uniStrategies.length);
uniUnderlying[0] = dai;
uniStrategies[0] = IStrategy(
address(
new StrategyUniEthDaiLpV4(
governance,
strategist,
address(controller),
timelock
)
)
);
uniUnderlying[1] = usdc;
uniStrategies[1] = IStrategy(
address(
new StrategyUniEthUsdcLpV4(
governance,
strategist,
address(controller),
timelock
)
)
);
uniUnderlying[2] = usdt;
uniStrategies[2] = IStrategy(
address(
new StrategyUniEthUsdtLpV4(
governance,
strategist,
address(controller),
timelock
)
)
);
uniUnderlying[3] = wbtc;
uniStrategies[3] = IStrategy(
address(
new StrategyUniEthWBtcLpV2(
governance,
strategist,
address(controller),
timelock
)
)
);
for (uint256 i = 0; i < uniStrategies.length; i++) {
uniPickleJars[i] = new PickleJar(
uniStrategies[i].want(),
governance,
timelock,
address(controller)
);
controller.setJar(
uniStrategies[i].want(),
address(uniPickleJars[i])
);
controller.approveStrategy(
uniStrategies[i].want(),
address(uniStrategies[i])
);
controller.setStrategy(
uniStrategies[i].want(),
address(uniStrategies[i])
);
}
curveProxyLogic = new CurveProxyLogic();
uniswapV2ProxyLogic = new UniswapV2ProxyLogic();
controller.approveJarConverter(address(curveProxyLogic));
controller.approveJarConverter(address(uniswapV2ProxyLogic));
hevm.warp(startTime);
}
function _getUniLP(
address lp,
uint256 ethAmount,
uint256 otherAmount
) internal {
IUniswapV2Pair fromPair = IUniswapV2Pair(lp);
address other = fromPair.token0() != weth
? fromPair.token0()
: fromPair.token1();
_getERC20(other, otherAmount);
uint256 _other = IERC20(other).balanceOf(address(this));
IERC20(other).safeApprove(address(univ2), 0);
IERC20(other).safeApprove(address(univ2), _other);
univ2.addLiquidityETH{value: ethAmount}(
other,
_other,
0,
0,
address(this),
now + 60
);
}
function _get_swap_lp_data(
address from,
address to,
address dustRecipient
) internal pure returns (bytes memory) {
return
abi.encodeWithSignature(
"swapUniLPTokens(address,address,address)",
from,
to,
dustRecipient
);
}
function _post_swap_check(uint256 fromIndex, uint256 toIndex) internal {
IERC20 token0 = uniPickleJars[fromIndex].token();
IERC20 token1 = uniPickleJars[toIndex].token();
uint256 MAX_DUST = 10;
// No funds left behind
assertEq(uniPickleJars[fromIndex].balanceOf(address(controller)), 0);
assertEq(uniPickleJars[toIndex].balanceOf(address(controller)), 0);
assertTrue(token0.balanceOf(address(controller)) < MAX_DUST);
assertTrue(token1.balanceOf(address(controller)) < MAX_DUST);
// Make sure only controller can call 'withdrawForSwap'
try uniStrategies[fromIndex].withdrawForSwap(0) {
revert("!withdraw-for-swap-only-controller");
} catch {}
}
function _test_check_treasury_fee(uint256 _amount, uint256 earned)
internal
{
assertEqApprox(
_amount.mul(controller.convenienceFee()).div(
controller.convenienceFeeMax()
),
earned.mul(2)
);
}
function _test_swap_and_check_balances(
address fromPickleJar,
address toPickleJar,
address fromPickleJarUnderlying,
uint256 fromPickleJarUnderlyingAmount,
address payable[] memory targets,
bytes[] memory data
) internal {
uint256 _beforeTo = IERC20(toPickleJar).balanceOf(address(this));
uint256 _beforeFrom = IERC20(fromPickleJar).balanceOf(address(this));
uint256 _beforeDev = IERC20(fromPickleJarUnderlying).balanceOf(devfund);
uint256 _beforeTreasury = IERC20(fromPickleJarUnderlying).balanceOf(
treasury
);
uint256 _ret = controller.swapExactJarForJar(
fromPickleJar,
toPickleJar,
fromPickleJarUnderlyingAmount,
0, // Min receive amount
targets,
data
);
uint256 _afterTo = IERC20(toPickleJar).balanceOf(address(this));
uint256 _afterFrom = IERC20(fromPickleJar).balanceOf(address(this));
uint256 _afterDev = IERC20(fromPickleJarUnderlying).balanceOf(devfund);
uint256 _afterTreasury = IERC20(fromPickleJarUnderlying).balanceOf(
treasury
);
uint256 treasuryEarned = _afterTreasury.sub(_beforeTreasury);
assertEq(treasuryEarned, _afterDev.sub(_beforeDev));
assertTrue(treasuryEarned > 0);
_test_check_treasury_fee(fromPickleJarUnderlyingAmount, treasuryEarned);
assertTrue(_afterFrom < _beforeFrom);
assertTrue(_afterTo > _beforeTo);
assertTrue(_afterTo.sub(_beforeTo) > 0);
assertEq(_afterTo.sub(_beforeTo), _ret);
assertEq(_afterFrom, 0);
}
function _test_uni_uni(
uint256 fromIndex,
uint256 toIndex,
uint256 amount,
address payable[] memory targets,
bytes[] memory data
) internal {
address from = address(uniPickleJars[fromIndex].token());
_getUniLP(from, 1e18, amount);
uint256 _from = IERC20(from).balanceOf(address(this));
IERC20(from).approve(address(uniPickleJars[fromIndex]), _from);
uniPickleJars[fromIndex].deposit(_from);
uniPickleJars[fromIndex].earn();
// Swap!
uint256 _fromPickleJar = IERC20(address(uniPickleJars[fromIndex]))
.balanceOf(address(this));
IERC20(address(uniPickleJars[fromIndex])).approve(
address(controller),
_fromPickleJar
);
// Check minimum amount
try
controller.swapExactJarForJar(
address(uniPickleJars[fromIndex]),
address(uniPickleJars[toIndex]),
_fromPickleJar,
uint256(-1), // Min receive amount
targets,
data
)
{
revert("min-amount-should-fail");
} catch {}
_test_swap_and_check_balances(
address(uniPickleJars[fromIndex]),
address(uniPickleJars[toIndex]),
from,
_fromPickleJar,
targets,
data
);
_post_swap_check(fromIndex, toIndex);
}
// **** Tests ****
function test_jar_converter_uni_uni_0() public {
uint256 fromIndex = 0;
uint256 toIndex = 1;
uint256 amount = 400e18;
address fromUnderlying = uniUnderlying[fromIndex];
address from = univ2Factory.getPair(weth, fromUnderlying);
address toUnderlying = uniUnderlying[toIndex];
address to = univ2Factory.getPair(weth, toUnderlying);
_test_uni_uni(
fromIndex,
toIndex,
amount,
_getDynamicArray(payable(address(uniswapV2ProxyLogic))),
_getDynamicArray(_get_swap_lp_data(from, to, treasury))
);
}
function test_jar_converter_uni_uni_1() public {
uint256 fromIndex = 0;
uint256 toIndex = 2;
uint256 amount = 400e18;
address fromUnderlying = uniUnderlying[fromIndex];
address from = univ2Factory.getPair(weth, fromUnderlying);
address toUnderlying = uniUnderlying[toIndex];
address to = univ2Factory.getPair(weth, toUnderlying);
_test_uni_uni(
fromIndex,
toIndex,
amount,
_getDynamicArray(payable(address(uniswapV2ProxyLogic))),
_getDynamicArray(_get_swap_lp_data(from, to, treasury))
);
}
function test_jar_converter_uni_uni_2() public {
uint256 fromIndex = 2;
uint256 toIndex = 3;
uint256 amount = 400e6;
address fromUnderlying = uniUnderlying[fromIndex];
address from = univ2Factory.getPair(weth, fromUnderlying);
address toUnderlying = uniUnderlying[toIndex];
address to = univ2Factory.getPair(weth, toUnderlying);
_test_uni_uni(
fromIndex,
toIndex,
amount,
_getDynamicArray(payable(address(uniswapV2ProxyLogic))),
_getDynamicArray(_get_swap_lp_data(from, to, treasury))
);
}
function test_jar_converter_uni_uni_3() public {
uint256 fromIndex = 3;
uint256 toIndex = 2;
uint256 amount = 4e6;
address fromUnderlying = uniUnderlying[fromIndex];
address from = univ2Factory.getPair(weth, fromUnderlying);
address toUnderlying = uniUnderlying[toIndex];
address to = univ2Factory.getPair(weth, toUnderlying);
_test_uni_uni(
fromIndex,
toIndex,
amount,
_getDynamicArray(payable(address(uniswapV2ProxyLogic))),
_getDynamicArray(_get_swap_lp_data(from, to, treasury))
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
pragma experimental ABIEncoderV2;
import "../lib/hevm.sol";
import "../lib/user.sol";
import "../lib/test-approx.sol";
import "../lib/test-defi-base.sol";
import "../../interfaces/strategy.sol";
import "../../interfaces/curve.sol";
import "../../interfaces/uniswapv2.sol";
import "../../pickle-jar.sol";
import "../../controller-v4.sol";
import "../../proxy-logic/curve.sol";
import "../../proxy-logic/uniswapv2.sol";
import "../../strategies/uniswapv2/strategy-uni-eth-dai-lp-v4.sol";
import "../../strategies/uniswapv2/strategy-uni-eth-usdt-lp-v4.sol";
import "../../strategies/uniswapv2/strategy-uni-eth-usdc-lp-v4.sol";
import "../../strategies/uniswapv2/strategy-uni-eth-wbtc-lp-v2.sol";
import "../../strategies/curve/strategy-curve-scrv-v3_2.sol";
import "../../strategies/curve/strategy-curve-rencrv-v2.sol";
import "../../strategies/curve/strategy-curve-3crv-v2.sol";
contract StrategyUniCurveJarSwapTest is DSTestDefiBase {
address governance;
address strategist;
address devfund;
address treasury;
address timelock;
IStrategy[] curveStrategies;
IStrategy[] uniStrategies;
PickleJar[] curvePickleJars;
PickleJar[] uniPickleJars;
ControllerV4 controller;
CurveProxyLogic curveProxyLogic;
UniswapV2ProxyLogic uniswapV2ProxyLogic;
address[] curvePools;
address[] curveLps;
address[] uniUnderlying;
// Contract wide variable to avoid stack too deep errors
uint256 temp;
function setUp() public {
governance = address(this);
strategist = address(this);
devfund = address(new User());
treasury = address(new User());
timelock = address(this);
controller = new ControllerV4(
governance,
strategist,
timelock,
devfund,
treasury
);
// Curve Strategies
curveStrategies = new IStrategy[](3);
curvePickleJars = new PickleJar[](curveStrategies.length);
curveLps = new address[](curveStrategies.length);
curvePools = new address[](curveStrategies.length);
curveLps[0] = three_crv;
curvePools[0] = three_pool;
curveStrategies[0] = IStrategy(
address(
new StrategyCurve3CRVv2(
governance,
strategist,
address(controller),
timelock
)
)
);
curveLps[1] = scrv;
curvePools[1] = susdv2_pool;
curveStrategies[1] = IStrategy(
address(
new StrategyCurveSCRVv3_2(
governance,
strategist,
address(controller),
timelock
)
)
);
curveLps[2] = ren_crv;
curvePools[2] = ren_pool;
curveStrategies[2] = IStrategy(
address(
new StrategyCurveRenCRVv2(
governance,
strategist,
address(controller),
timelock
)
)
);
// Create PICKLE Jars
for (uint256 i = 0; i < curvePickleJars.length; i++) {
curvePickleJars[i] = new PickleJar(
curveStrategies[i].want(),
governance,
timelock,
address(controller)
);
controller.setJar(
curveStrategies[i].want(),
address(curvePickleJars[i])
);
controller.approveStrategy(
curveStrategies[i].want(),
address(curveStrategies[i])
);
controller.setStrategy(
curveStrategies[i].want(),
address(curveStrategies[i])
);
}
// Uni strategies
uniStrategies = new IStrategy[](4);
uniUnderlying = new address[](uniStrategies.length);
uniPickleJars = new PickleJar[](uniStrategies.length);
uniUnderlying[0] = dai;
uniStrategies[0] = IStrategy(
address(
new StrategyUniEthDaiLpV4(
governance,
strategist,
address(controller),
timelock
)
)
);
uniUnderlying[1] = usdc;
uniStrategies[1] = IStrategy(
address(
new StrategyUniEthUsdcLpV4(
governance,
strategist,
address(controller),
timelock
)
)
);
uniUnderlying[2] = usdt;
uniStrategies[2] = IStrategy(
address(
new StrategyUniEthUsdtLpV4(
governance,
strategist,
address(controller),
timelock
)
)
);
uniUnderlying[3] = wbtc;
uniStrategies[3] = IStrategy(
address(
new StrategyUniEthWBtcLpV2(
governance,
strategist,
address(controller),
timelock
)
)
);
for (uint256 i = 0; i < uniStrategies.length; i++) {
uniPickleJars[i] = new PickleJar(
uniStrategies[i].want(),
governance,
timelock,
address(controller)
);
controller.setJar(
uniStrategies[i].want(),
address(uniPickleJars[i])
);
controller.approveStrategy(
uniStrategies[i].want(),
address(uniStrategies[i])
);
controller.setStrategy(
uniStrategies[i].want(),
address(uniStrategies[i])
);
}
curveProxyLogic = new CurveProxyLogic();
uniswapV2ProxyLogic = new UniswapV2ProxyLogic();
controller.approveJarConverter(address(curveProxyLogic));
controller.approveJarConverter(address(uniswapV2ProxyLogic));
hevm.warp(startTime);
}
function _getUniLP(
address lp,
uint256 ethAmount,
uint256 otherAmount
) internal {
IUniswapV2Pair fromPair = IUniswapV2Pair(lp);
address other = fromPair.token0() != weth
? fromPair.token0()
: fromPair.token1();
_getERC20(other, otherAmount);
uint256 _other = IERC20(other).balanceOf(address(this));
IERC20(other).safeApprove(address(univ2), 0);
IERC20(other).safeApprove(address(univ2), _other);
univ2.addLiquidityETH{value: ethAmount}(
other,
_other,
0,
0,
address(this),
now + 60
);
}
function _get_uniswap_remove_liquidity_data(address pair)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSignature("removeLiquidity(address)", pair);
}
function _get_uniswap_lp_tokens_to_primitive(address from, address to)
internal
pure
returns (bytes memory)
{
return
abi.encodeWithSignature(
"lpTokensToPrimitive(address,address)",
from,
to
);
}
function _get_curve_add_liquidity_data(
address curve,
bytes4 curveFunctionSig,
uint256 curvePoolSize,
uint256 curveUnderlyingIndex,
address underlying
) internal pure returns (bytes memory) {
return
abi.encodeWithSignature(
"add_liquidity(address,bytes4,uint256,uint256,address)",
curve,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
underlying
);
}
// Some post swap checks
// Checks if there's any leftover funds in the converter contract
function _post_swap_check(uint256 fromIndex, uint256 toIndex) internal {
IERC20 token0 = uniPickleJars[fromIndex].token();
IERC20 token1 = curvePickleJars[toIndex].token();
// No funds left behind
assertEq(uniPickleJars[fromIndex].balanceOf(address(controller)), 0);
assertEq(curvePickleJars[toIndex].balanceOf(address(controller)), 0);
assertEq(token0.balanceOf(address(controller)), 0);
assertEq(token1.balanceOf(address(controller)), 0);
assertEq(IERC20(wbtc).balanceOf(address(controller)), 0);
// assertEq(IERC20(usdt).balanceOf(address(controller)), 0);
// assertEq(IERC20(usdc).balanceOf(address(controller)), 0);
// assertEq(IERC20(susd).balanceOf(address(controller)), 0);
// assertEq(IERC20(dai).balanceOf(address(controller)), 0);
// No balance left behind!
assertEq(token1.balanceOf(address(this)), 0);
// Make sure only controller can call 'withdrawForSwap'
try uniStrategies[fromIndex].withdrawForSwap(0) {
revert("!withdraw-for-swap-only-controller");
} catch {}
}
function _test_check_treasury_fee(uint256 _amount, uint256 earned)
internal
{
assertEqApprox(
_amount.mul(controller.convenienceFee()).div(
controller.convenienceFeeMax()
),
earned.mul(2)
);
}
function _test_swap_and_check_balances(
address fromPickleJar,
address toPickleJar,
address fromPickleJarUnderlying,
uint256 fromPickleJarUnderlyingAmount,
address payable[] memory targets,
bytes[] memory data
) internal {
uint256 _beforeTo = IERC20(toPickleJar).balanceOf(address(this));
uint256 _beforeFrom = IERC20(fromPickleJar).balanceOf(address(this));
uint256 _beforeDev = IERC20(fromPickleJarUnderlying).balanceOf(devfund);
uint256 _beforeTreasury = IERC20(fromPickleJarUnderlying).balanceOf(
treasury
);
uint256 _ret = controller.swapExactJarForJar(
fromPickleJar,
toPickleJar,
fromPickleJarUnderlyingAmount,
0, // Min receive amount
targets,
data
);
uint256 _afterTo = IERC20(toPickleJar).balanceOf(address(this));
uint256 _afterFrom = IERC20(fromPickleJar).balanceOf(address(this));
uint256 _afterDev = IERC20(fromPickleJarUnderlying).balanceOf(devfund);
uint256 _afterTreasury = IERC20(fromPickleJarUnderlying).balanceOf(
treasury
);
uint256 treasuryEarned = _afterTreasury.sub(_beforeTreasury);
assertEq(treasuryEarned, _afterDev.sub(_beforeDev));
assertTrue(treasuryEarned > 0);
_test_check_treasury_fee(fromPickleJarUnderlyingAmount, treasuryEarned);
assertTrue(_afterFrom < _beforeFrom);
assertTrue(_afterTo > _beforeTo);
assertTrue(_afterTo.sub(_beforeTo) > 0);
assertEq(_afterTo.sub(_beforeTo), _ret);
assertEq(_afterFrom, 0);
}
function _test_uni_curve_swap(
uint256 fromIndex,
uint256 toIndex,
uint256 amount,
address payable[] memory targets,
bytes[] memory data
) internal {
// Deposit into PickleJars
address from = address(uniPickleJars[fromIndex].token());
_getUniLP(from, 1e18, amount);
uint256 _from = IERC20(from).balanceOf(address(this));
IERC20(from).approve(address(uniPickleJars[fromIndex]), _from);
uniPickleJars[fromIndex].deposit(_from);
uniPickleJars[fromIndex].earn();
// Swap!
uint256 _fromPickleJar = IERC20(address(uniPickleJars[fromIndex]))
.balanceOf(address(this));
IERC20(address(uniPickleJars[fromIndex])).approve(
address(controller),
_fromPickleJar
);
// Check minimum amount
try
controller.swapExactJarForJar(
address(uniPickleJars[fromIndex]),
address(curvePickleJars[toIndex]),
_fromPickleJar,
uint256(-1), // Min receive amount
targets,
data
)
{
revert("min-amount-should-fail");
} catch {}
_test_swap_and_check_balances(
address(uniPickleJars[fromIndex]),
address(curvePickleJars[toIndex]),
from,
_fromPickleJar,
targets,
data
);
_post_swap_check(fromIndex, toIndex);
}
// **** Tests **** //
function test_jar_converter_uni_curve_0_0() public {
uint256 fromIndex = 0;
uint256 toIndex = 0;
uint256 amount = 400e18;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 3;
address curveUnderlying = dai;
uint256 curveUnderlyingIndex = 0;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[3],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_uni_curve_1_0() public {
uint256 fromIndex = 1;
uint256 toIndex = 0;
uint256 amount = 400e6;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 3;
address curveUnderlying = dai;
uint256 curveUnderlyingIndex = 0;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[3],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_uni_curve_2_0() public {
uint256 fromIndex = 2;
uint256 toIndex = 0;
uint256 amount = 400e6;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 3;
address curveUnderlying = dai;
uint256 curveUnderlyingIndex = 0;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[3],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_uni_curve_3_0() public {
uint256 fromIndex = 3;
uint256 toIndex = 0;
uint256 amount = 4e6;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 3;
address curveUnderlying = dai;
uint256 curveUnderlyingIndex = 0;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[3],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_uni_curve_0_1() public {
uint256 fromIndex = 0;
uint256 toIndex = 1;
uint256 amount = 400e18;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 4;
address curveUnderlying = usdt;
uint256 curveUnderlyingIndex = 2;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[4],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_uni_curve_1_1() public {
uint256 fromIndex = 1;
uint256 toIndex = 1;
uint256 amount = 400e6;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 4;
address curveUnderlying = usdt;
uint256 curveUnderlyingIndex = 2;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[4],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_uni_curve_2_1() public {
uint256 fromIndex = 2;
uint256 toIndex = 1;
uint256 amount = 400e6;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 4;
address curveUnderlying = usdt;
uint256 curveUnderlyingIndex = 2;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[4],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_uni_curve_3_1() public {
uint256 fromIndex = 3;
uint256 toIndex = 1;
uint256 amount = 4e6;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 4;
address curveUnderlying = usdt;
uint256 curveUnderlyingIndex = 2;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[4],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_uni_curve_4_1() public {
uint256 fromIndex = 3;
uint256 toIndex = 1;
uint256 amount = 4e6;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 4;
address curveUnderlying = usdt;
uint256 curveUnderlyingIndex = 2;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[4],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_uni_curve_0_2() public {
uint256 fromIndex = 0;
uint256 toIndex = 2;
uint256 amount = 400e18;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 2;
address curveUnderlying = wbtc;
uint256 curveUnderlyingIndex = 1;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[2],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_uni_curve_1_2() public {
uint256 fromIndex = 1;
uint256 toIndex = 2;
uint256 amount = 400e6;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 2;
address curveUnderlying = wbtc;
uint256 curveUnderlyingIndex = 1;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[2],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_uni_curve_2_2() public {
uint256 fromIndex = 2;
uint256 toIndex = 2;
uint256 amount = 400e6;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 2;
address curveUnderlying = wbtc;
uint256 curveUnderlyingIndex = 1;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[2],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
function test_jar_converter_uni_curve_3_2() public {
uint256 fromIndex = 3;
uint256 toIndex = 2;
uint256 amount = 4e6;
address fromUnderlying = uniUnderlying[fromIndex];
address curvePool = curvePools[toIndex];
uint256 curvePoolSize = 2;
address curveUnderlying = wbtc;
uint256 curveUnderlyingIndex = 1;
bytes4 curveFunctionSig = _getFunctionSig(
"add_liquidity(uint256[2],uint256)"
);
bytes memory data0 = _get_uniswap_lp_tokens_to_primitive(
univ2Factory.getPair(weth, fromUnderlying),
curveUnderlying
);
bytes memory data1 = _get_curve_add_liquidity_data(
curvePool,
curveFunctionSig,
curvePoolSize,
curveUnderlyingIndex,
curveUnderlying
);
_test_uni_curve_swap(
fromIndex,
toIndex,
amount,
_getDynamicArray(
payable(address(uniswapV2ProxyLogic)),
payable(address(curveProxyLogic))
),
_getDynamicArray(data0, data1)
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "./lib/safe-math.sol";
import "./lib/erc20.sol";
import "./interfaces/uniswapv2.sol";
import "./interfaces/curve.sol";
// Converts UNI LP Tokens to Curve LP Tokens
// Mainly for treasury
contract UniCurveConverter {
using SafeMath for uint256;
using SafeERC20 for IERC20;
UniswapRouterV2 public router = UniswapRouterV2(
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
);
// Stablecoins
address public constant dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address public constant usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
address public constant susd = 0x57Ab1ec28D129707052df4dF418D58a2D46d5f51;
// Wrapped stablecoins
address public constant scrv = 0xC25a3A3b969415c80451098fa907EC722572917F;
// Weth
address public constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// susd v2 pool
ICurveFi_4 public curve = ICurveFi_4(
0xA5407eAE9Ba41422680e2e00537571bcC53efBfD
);
// UNI LP -> Curve LP
// Assume th
function convert(address _lp, uint256 _amount) public {
// Get LP Tokens
IERC20(_lp).safeTransferFrom(msg.sender, address(this), _amount);
// Get Uniswap pair
IUniswapV2Pair fromPair = IUniswapV2Pair(_lp);
// Only for WETH/<TOKEN> pairs
if (!(fromPair.token0() == weth || fromPair.token1() == weth)) {
revert("!eth-from");
}
// Remove liquidity
IERC20(_lp).safeApprove(address(router), 0);
IERC20(_lp).safeApprove(address(router), _amount);
router.removeLiquidity(
fromPair.token0(),
fromPair.token1(),
_amount,
0,
0,
address(this),
now + 60
);
// Most premium stablecoin
(address premiumStablecoin, ) = getMostPremium();
// Convert weth -> most premium stablecoin
address[] memory path = new address[](2);
path[0] = weth;
path[1] = premiumStablecoin;
IERC20(weth).safeApprove(address(router), 0);
IERC20(weth).safeApprove(address(router), uint256(-1));
router.swapExactTokensForTokens(
IERC20(weth).balanceOf(address(this)),
0,
path,
address(this),
now + 60
);
// Convert the other asset into stablecoin if its not a stablecoin
address _from = fromPair.token0() != weth
? fromPair.token0()
: fromPair.token1();
if (_from != dai && _from != usdc && _from != usdt && _from != susd) {
path = new address[](3);
path[0] = _from;
path[1] = weth;
path[2] = premiumStablecoin;
IERC20(_from).safeApprove(address(router), 0);
IERC20(_from).safeApprove(address(router), uint256(-1));
router.swapExactTokensForTokens(
IERC20(_from).balanceOf(address(this)),
0,
path,
address(this),
now + 60
);
}
// Add liquidity to curve
IERC20(dai).safeApprove(address(curve), 0);
IERC20(dai).safeApprove(address(curve), uint256(-1));
IERC20(usdc).safeApprove(address(curve), 0);
IERC20(usdc).safeApprove(address(curve), uint256(-1));
IERC20(usdt).safeApprove(address(curve), 0);
IERC20(usdt).safeApprove(address(curve), uint256(-1));
IERC20(susd).safeApprove(address(curve), 0);
IERC20(susd).safeApprove(address(curve), uint256(-1));
curve.add_liquidity(
[
IERC20(dai).balanceOf(address(this)),
IERC20(usdc).balanceOf(address(this)),
IERC20(usdt).balanceOf(address(this)),
IERC20(susd).balanceOf(address(this))
],
0
);
// Sends token back to user
IERC20(scrv).transfer(
msg.sender,
IERC20(scrv).balanceOf(address(this))
);
}
function getMostPremium() public view returns (address, uint256) {
uint256[] memory balances = new uint256[](4);
balances[0] = ICurveFi_4(curve).balances(0); // DAI
balances[1] = ICurveFi_4(curve).balances(1).mul(10**12); // USDC
balances[2] = ICurveFi_4(curve).balances(2).mul(10**12); // USDT
balances[3] = ICurveFi_4(curve).balances(3); // sUSD
// DAI
if (
balances[0] < balances[1] &&
balances[0] < balances[2] &&
balances[0] < balances[3]
) {
return (dai, 0);
}
// USDC
if (
balances[1] < balances[0] &&
balances[1] < balances[2] &&
balances[1] < balances[3]
) {
return (usdc, 1);
}
// USDT
if (
balances[2] < balances[0] &&
balances[2] < balances[1] &&
balances[2] < balances[3]
) {
return (usdt, 2);
}
// SUSD
if (
balances[3] < balances[0] &&
balances[3] < balances[1] &&
balances[3] < balances[2]
) {
return (susd, 3);
}
// If they're somehow equal, we just want DAI
return (dai, 0);
}
}// CurveYCRVVoter: https://etherscan.io/address/0xF147b8125d2ef93FB6965Db97D6746952a133934#code
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
import "../../lib/erc20.sol";
import "../../lib/safe-math.sol";
import "../../interfaces/curve.sol";
contract CRVLocker {
using SafeERC20 for IERC20;
using Address for address;
using SafeMath for uint256;
address public constant mintr = 0xd061D61a4d941c39E5453435B6345Dc261C2fcE0;
address public constant crv = 0xD533a949740bb3306d119CC777fa900bA034cd52;
address public constant escrow = 0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2;
address public governance;
mapping(address => bool) public voters;
constructor(address _governance) public {
governance = _governance;
}
function getName() external pure returns (string memory) {
return "CRVLocker";
}
function addVoter(address _voter) external {
require(msg.sender == governance, "!governance");
voters[_voter] = true;
}
function removeVoter(address _voter) external {
require(msg.sender == governance, "!governance");
voters[_voter] = false;
}
function withdraw(address _asset) external returns (uint256 balance) {
require(voters[msg.sender], "!voter");
balance = IERC20(_asset).balanceOf(address(this));
IERC20(_asset).safeTransfer(msg.sender, balance);
}
function createLock(uint256 _value, uint256 _unlockTime) external {
require(voters[msg.sender] || msg.sender == governance, "!authorized");
IERC20(crv).safeApprove(escrow, 0);
IERC20(crv).safeApprove(escrow, _value);
ICurveVotingEscrow(escrow).create_lock(_value, _unlockTime);
}
function increaseAmount(uint256 _value) external {
require(voters[msg.sender] || msg.sender == governance, "!authorized");
IERC20(crv).safeApprove(escrow, 0);
IERC20(crv).safeApprove(escrow, _value);
ICurveVotingEscrow(escrow).increase_amount(_value);
}
function increaseUnlockTime(uint256 _unlockTime) external {
require(voters[msg.sender] || msg.sender == governance, "!authorized");
ICurveVotingEscrow(escrow).increase_unlock_time(_unlockTime);
}
function release() external {
require(voters[msg.sender] || msg.sender == governance, "!authorized");
ICurveVotingEscrow(escrow).withdraw();
}
function setGovernance(address _governance) external {
require(msg.sender == governance, "!governance");
governance = _governance;
}
function execute(
address to,
uint256 value,
bytes calldata data
) external returns (bool, bytes memory) {
require(voters[msg.sender] || msg.sender == governance, "!governance");
(bool success, bytes memory result) = to.call{value: value}(data);
require(success, "!execute-success");
return (success, result);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
import "../../lib/erc20.sol";
import "../../lib/safe-math.sol";
import "../../interfaces/jar.sol";
import "../../interfaces/curve.sol";
import "../../interfaces/uniswapv2.sol";
import "../../interfaces/controller.sol";
import "../strategy-curve-base.sol";
contract StrategyCurveRenCRVv2 is StrategyCurveBase {
// https://www.curve.fi/ren
// Curve stuff
address public ren_pool = 0x93054188d876f558f4a66B2EF1d97d16eDf0895B;
address public ren_gauge = 0xB1F2cdeC61db658F091671F5f199635aEF202CAC;
address public ren_crv = 0x49849C98ae39Fff122806C06791Fa73784FB3675;
constructor(
address _governance,
address _strategist,
address _controller,
address _timelock
)
public
StrategyCurveBase(
ren_pool,
ren_gauge,
ren_crv,
_governance,
_strategist,
_controller,
_timelock
)
{}
// **** Views ****
function getMostPremium() public override view returns (address, uint256) {
// Both 8 decimals, so doesn't matter
uint256[] memory balances = new uint256[](3);
balances[0] = ICurveFi_2(curve).balances(0); // RENBTC
balances[1] = ICurveFi_2(curve).balances(1); // WBTC
// renbtc
if (balances[0] < balances[1]) {
return (renbtc, 0);
}
// WBTC
if (balances[1] < balances[0]) {
return (wbtc, 1);
}
// If they're somehow equal, we just want RENBTC
return (renbtc, 0);
}
function getName() external override pure returns (string memory) {
return "StrategyCurveRenCRVv2";
}
// **** State Mutations ****
function harvest() public override onlyBenevolent {
// Anyone can harvest it at any given time.
// I understand the possibility of being frontrun
// But ETH is a dark forest, and I wanna see how this plays out
// i.e. will be be heavily frontrunned?
// if so, a new strategy will be deployed.
// stablecoin we want to convert to
(address to, uint256 toIndex) = getMostPremium();
// Collects crv tokens
// Don't bother voting in v1
ICurveMintr(mintr).mint(gauge);
uint256 _crv = IERC20(crv).balanceOf(address(this));
if (_crv > 0) {
// x% is sent back to the rewards holder
// to be used to lock up in as veCRV in a future date
uint256 _keepCRV = _crv.mul(keepCRV).div(keepCRVMax);
if (_keepCRV > 0) {
IERC20(crv).safeTransfer(
IController(controller).treasury(),
_keepCRV
);
}
_crv = _crv.sub(_keepCRV);
_swapUniswap(crv, to, _crv);
}
// Adds liquidity to curve.fi's pool
// to get back want (scrv)
uint256 _to = IERC20(to).balanceOf(address(this));
if (_to > 0) {
IERC20(to).safeApprove(curve, 0);
IERC20(to).safeApprove(curve, _to);
uint256[2] memory liquidity;
liquidity[toIndex] = _to;
ICurveFi_2(curve).add_liquidity(liquidity, 0);
}
_distributePerformanceFeesAndDeposit();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
import "../../lib/erc20.sol";
import "../../lib/safe-math.sol";
import "../../interfaces/jar.sol";
import "../../interfaces/curve.sol";
import "../../interfaces/uniswapv2.sol";
import "../../interfaces/controller.sol";
import "../strategy-curve-base.sol";
contract StrategyCurve3CRVv2 is StrategyCurveBase {
// Curve stuff
address public three_pool = 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7;
address public three_gauge = 0xbFcF63294aD7105dEa65aA58F8AE5BE2D9d0952A;
address public three_crv = 0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490;
constructor(
address _governance,
address _strategist,
address _controller,
address _timelock
)
public
StrategyCurveBase(
three_pool,
three_gauge,
three_crv,
_governance,
_strategist,
_controller,
_timelock
)
{}
// **** Views ****
function getMostPremium()
public
override
view
returns (address, uint256)
{
uint256[] memory balances = new uint256[](3);
balances[0] = ICurveFi_3(curve).balances(0); // DAI
balances[1] = ICurveFi_3(curve).balances(1).mul(10**12); // USDC
balances[2] = ICurveFi_3(curve).balances(2).mul(10**12); // USDT
// DAI
if (
balances[0] < balances[1] &&
balances[0] < balances[2]
) {
return (dai, 0);
}
// USDC
if (
balances[1] < balances[0] &&
balances[1] < balances[2]
) {
return (usdc, 1);
}
// USDT
if (
balances[2] < balances[0] &&
balances[2] < balances[1]
) {
return (usdt, 2);
}
// If they're somehow equal, we just want DAI
return (dai, 0);
}
function getName() external override pure returns (string memory) {
return "StrategyCurve3CRVv2";
}
// **** State Mutations ****
function harvest() public onlyBenevolent override {
// Anyone can harvest it at any given time.
// I understand the possibility of being frontrun
// But ETH is a dark forest, and I wanna see how this plays out
// i.e. will be be heavily frontrunned?
// if so, a new strategy will be deployed.
// stablecoin we want to convert to
(address to, uint256 toIndex) = getMostPremium();
// Collects crv tokens
// Don't bother voting in v1
ICurveMintr(mintr).mint(gauge);
uint256 _crv = IERC20(crv).balanceOf(address(this));
if (_crv > 0) {
// x% is sent back to the rewards holder
// to be used to lock up in as veCRV in a future date
uint256 _keepCRV = _crv.mul(keepCRV).div(keepCRVMax);
if (_keepCRV > 0) {
IERC20(crv).safeTransfer(
IController(controller).treasury(),
_keepCRV
);
}
_crv = _crv.sub(_keepCRV);
_swapUniswap(crv, to, _crv);
}
// Adds liquidity to curve.fi's pool
// to get back want (scrv)
uint256 _to = IERC20(to).balanceOf(address(this));
if (_to > 0) {
IERC20(to).safeApprove(curve, 0);
IERC20(to).safeApprove(curve, _to);
uint256[3] memory liquidity;
liquidity[toIndex] = _to;
ICurveFi_3(curve).add_liquidity(liquidity, 0);
}
_distributePerformanceFeesAndDeposit();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
import "../../lib/erc20.sol";
import "../../lib/safe-math.sol";
import "./scrv-voter.sol";
import "./crv-locker.sol";
import "../../interfaces/jar.sol";
import "../../interfaces/curve.sol";
import "../../interfaces/uniswapv2.sol";
import "../../interfaces/controller.sol";
import "../strategy-curve-base.sol";
contract StrategyCurveSCRVv3_2 is StrategyCurveBase {
// Curve stuff
address public susdv2_pool = 0xA5407eAE9Ba41422680e2e00537571bcC53efBfD;
address public susdv2_gauge = 0xA90996896660DEcC6E997655E065b23788857849;
address public scrv = 0xC25a3A3b969415c80451098fa907EC722572917F;
// Harvesting
address public snx = 0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F;
constructor(
address _governance,
address _strategist,
address _controller,
address _timelock
)
public
StrategyCurveBase(
susdv2_pool,
susdv2_gauge,
scrv,
_governance,
_strategist,
_controller,
_timelock
)
{}
// **** Views ****
function getMostPremium()
public
override
view
returns (address, uint256)
{
uint256[] memory balances = new uint256[](4);
balances[0] = ICurveFi_4(curve).balances(0); // DAI
balances[1] = ICurveFi_4(curve).balances(1).mul(10**12); // USDC
balances[2] = ICurveFi_4(curve).balances(2).mul(10**12); // USDT
balances[3] = ICurveFi_4(curve).balances(3); // sUSD
// DAI
if (
balances[0] < balances[1] &&
balances[0] < balances[2] &&
balances[0] < balances[3]
) {
return (dai, 0);
}
// USDC
if (
balances[1] < balances[0] &&
balances[1] < balances[2] &&
balances[1] < balances[3]
) {
return (usdc, 1);
}
// USDT
if (
balances[2] < balances[0] &&
balances[2] < balances[1] &&
balances[2] < balances[3]
) {
return (usdt, 2);
}
// SUSD
if (
balances[3] < balances[0] &&
balances[3] < balances[1] &&
balances[3] < balances[2]
) {
return (susd, 3);
}
// If they're somehow equal, we just want DAI
return (dai, 0);
}
function getName() external override pure returns (string memory) {
return "StrategyCurveSCRVv3_2";
}
// **** State Mutations ****
function harvest() public onlyBenevolent override {
// Anyone can harvest it at any given time.
// I understand the possibility of being frontrun
// But ETH is a dark forest, and I wanna see how this plays out
// i.e. will be be heavily frontrunned?
// if so, a new strategy will be deployed.
// stablecoin we want to convert to
(address to, uint256 toIndex) = getMostPremium();
// Collects crv tokens
// Don't bother voting in v1
ICurveMintr(mintr).mint(gauge);
uint256 _crv = IERC20(crv).balanceOf(address(this));
if (_crv > 0) {
// x% is sent back to the rewards holder
// to be used to lock up in as veCRV in a future date
uint256 _keepCRV = _crv.mul(keepCRV).div(keepCRVMax);
if (_keepCRV > 0) {
IERC20(crv).safeTransfer(
IController(controller).treasury(),
_keepCRV
);
}
_crv = _crv.sub(_keepCRV);
_swapUniswap(crv, to, _crv);
}
// Collects SNX tokens
ICurveGauge(gauge).claim_rewards(address(this));
uint256 _snx = IERC20(snx).balanceOf(address(this));
if (_snx > 0) {
_swapUniswap(snx, to, _snx);
}
// Adds liquidity to curve.fi's susd pool
// to get back want (scrv)
uint256 _to = IERC20(to).balanceOf(address(this));
if (_to > 0) {
IERC20(to).safeApprove(curve, 0);
IERC20(to).safeApprove(curve, _to);
uint256[4] memory liquidity;
liquidity[toIndex] = _to;
ICurveFi_4(curve).add_liquidity(liquidity, 0);
}
// We want to get back sCRV
_distributePerformanceFeesAndDeposit();
}
}// StrategyProxy: https://etherscan.io/address/0x5886e475e163f78cf63d6683abc7fe8516d12081#code
pragma solidity ^0.6.7;
import "../../lib/erc20.sol";
import "../../lib/safe-math.sol";
import "./crv-locker.sol";
import "../../interfaces/curve.sol";
contract SCRVVoter {
using SafeERC20 for IERC20;
using Address for address;
using SafeMath for uint256;
CRVLocker public crvLocker;
address public constant want = 0xC25a3A3b969415c80451098fa907EC722572917F;
address public constant mintr = 0xd061D61a4d941c39E5453435B6345Dc261C2fcE0;
address public constant crv = 0xD533a949740bb3306d119CC777fa900bA034cd52;
address public constant snx = 0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F;
address
public constant gaugeController = 0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB;
address
public constant scrvGauge = 0xA90996896660DEcC6E997655E065b23788857849;
mapping(address => bool) public strategies;
address public governance;
constructor(address _governance, address _crvLocker) public {
governance = _governance;
crvLocker = CRVLocker(_crvLocker);
}
function setGovernance(address _governance) external {
require(msg.sender == governance, "!governance");
governance = _governance;
}
function approveStrategy(address _strategy) external {
require(msg.sender == governance, "!governance");
strategies[_strategy] = true;
}
function revokeStrategy(address _strategy) external {
require(msg.sender == governance, "!governance");
strategies[_strategy] = false;
}
function lock() external {
crvLocker.increaseAmount(IERC20(crv).balanceOf(address(crvLocker)));
}
function vote(address _gauge, uint256 _amount) public {
require(strategies[msg.sender], "!strategy");
crvLocker.execute(
gaugeController,
0,
abi.encodeWithSignature(
"vote_for_gauge_weights(address,uint256)",
_gauge,
_amount
)
);
}
function max() external {
require(strategies[msg.sender], "!strategy");
vote(scrvGauge, 10000);
}
function withdraw(
address _gauge,
address _token,
uint256 _amount
) public returns (uint256) {
require(strategies[msg.sender], "!strategy");
uint256 _before = IERC20(_token).balanceOf(address(crvLocker));
crvLocker.execute(
_gauge,
0,
abi.encodeWithSignature("withdraw(uint256)", _amount)
);
uint256 _after = IERC20(_token).balanceOf(address(crvLocker));
uint256 _net = _after.sub(_before);
crvLocker.execute(
_token,
0,
abi.encodeWithSignature(
"transfer(address,uint256)",
msg.sender,
_net
)
);
return _net;
}
function balanceOf(address _gauge) public view returns (uint256) {
return IERC20(_gauge).balanceOf(address(crvLocker));
}
function withdrawAll(address _gauge, address _token)
external
returns (uint256)
{
require(strategies[msg.sender], "!strategy");
return withdraw(_gauge, _token, balanceOf(_gauge));
}
function deposit(address _gauge, address _token) external {
uint256 _balance = IERC20(_token).balanceOf(address(this));
IERC20(_token).safeTransfer(address(crvLocker), _balance);
_balance = IERC20(_token).balanceOf(address(crvLocker));
crvLocker.execute(
_token,
0,
abi.encodeWithSignature("approve(address,uint256)", _gauge, 0)
);
crvLocker.execute(
_token,
0,
abi.encodeWithSignature(
"approve(address,uint256)",
_gauge,
_balance
)
);
crvLocker.execute(
_gauge,
0,
abi.encodeWithSignature("deposit(uint256)", _balance)
);
}
function harvest(address _gauge) external {
require(strategies[msg.sender], "!strategy");
uint256 _before = IERC20(crv).balanceOf(address(crvLocker));
crvLocker.execute(
mintr,
0,
abi.encodeWithSignature("mint(address)", _gauge)
);
uint256 _after = IERC20(crv).balanceOf(address(crvLocker));
uint256 _balance = _after.sub(_before);
crvLocker.execute(
crv,
0,
abi.encodeWithSignature(
"transfer(address,uint256)",
msg.sender,
_balance
)
);
}
function claimRewards() external {
require(strategies[msg.sender], "!strategy");
uint256 _before = IERC20(snx).balanceOf(address(crvLocker));
crvLocker.execute(scrvGauge, 0, abi.encodeWithSignature("claim_rewards()"));
uint256 _after = IERC20(snx).balanceOf(address(crvLocker));
uint256 _balance = _after.sub(_before);
crvLocker.execute(
snx,
0,
abi.encodeWithSignature(
"transfer(address,uint256)",
msg.sender,
_balance
)
);
}
}// https://etherscan.io/address/0x594a198048501a304267e63b3bad0f0638da7628#code
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
import "../../lib/erc20.sol";
import "../../lib/safe-math.sol";
import "./scrv-voter.sol";
import "./crv-locker.sol";
import "../../interfaces/jar.sol";
import "../../interfaces/curve.sol";
import "../../interfaces/uniswapv2.sol";
import "../../interfaces/controller.sol";
import "../strategy-base.sol";
contract StrategyCurveSCRVv4_1 is StrategyBase {
// Curve
address public scrv = 0xC25a3A3b969415c80451098fa907EC722572917F;
address public susdv2_gauge = 0xA90996896660DEcC6E997655E065b23788857849;
address public susdv2_pool = 0xA5407eAE9Ba41422680e2e00537571bcC53efBfD;
address public escrow = 0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2;
// curve dao
address public gauge;
address public curve;
address public mintr = 0xd061D61a4d941c39E5453435B6345Dc261C2fcE0;
// tokens we're farming
address public constant crv = 0xD533a949740bb3306d119CC777fa900bA034cd52;
address public constant snx = 0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F;
// stablecoins
address public dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address public usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
address public susd = 0x57Ab1ec28D129707052df4dF418D58a2D46d5f51;
// How much CRV tokens to keep
uint256 public keepCRV = 500;
uint256 public keepCRVMax = 10000;
// crv-locker and voter
address public scrvVoter;
address public crvLocker;
constructor(
address _scrvVoter,
address _crvLocker,
address _governance,
address _strategist,
address _controller,
address _timelock
)
public
StrategyBase(scrv, _governance, _strategist, _controller, _timelock)
{
curve = susdv2_pool;
gauge = susdv2_gauge;
scrvVoter = _scrvVoter;
crvLocker = _crvLocker;
}
// **** Getters ****
function balanceOfPool() public override view returns (uint256) {
return SCRVVoter(scrvVoter).balanceOf(gauge);
}
function getName() external override pure returns (string memory) {
return "StrategyCurveSCRVv4_1";
}
function getHarvestable() external returns (uint256) {
return ICurveGauge(gauge).claimable_tokens(crvLocker);
}
function getMostPremium() public view returns (address, uint8) {
uint256[] memory balances = new uint256[](4);
balances[0] = ICurveFi_4(curve).balances(0); // DAI
balances[1] = ICurveFi_4(curve).balances(1).mul(10**12); // USDC
balances[2] = ICurveFi_4(curve).balances(2).mul(10**12); // USDT
balances[3] = ICurveFi_4(curve).balances(3); // sUSD
// DAI
if (
balances[0] < balances[1] &&
balances[0] < balances[2] &&
balances[0] < balances[3]
) {
return (dai, 0);
}
// USDC
if (
balances[1] < balances[0] &&
balances[1] < balances[2] &&
balances[1] < balances[3]
) {
return (usdc, 1);
}
// USDT
if (
balances[2] < balances[0] &&
balances[2] < balances[1] &&
balances[2] < balances[3]
) {
return (usdt, 2);
}
// SUSD
if (
balances[3] < balances[0] &&
balances[3] < balances[1] &&
balances[3] < balances[2]
) {
return (susd, 3);
}
// If they're somehow equal, we just want DAI
return (dai, 0);
}
// **** Setters ****
function setKeepCRV(uint256 _keepCRV) external {
require(msg.sender == governance, "!governance");
keepCRV = _keepCRV;
}
// **** State Mutations ****
function deposit() public override {
uint256 _want = IERC20(want).balanceOf(address(this));
if (_want > 0) {
IERC20(want).safeTransfer(scrvVoter, _want);
SCRVVoter(scrvVoter).deposit(gauge, want);
}
}
function _withdrawSome(uint256 _amount)
internal
override
returns (uint256)
{
return SCRVVoter(scrvVoter).withdraw(gauge, want, _amount);
}
function harvest() public override onlyBenevolent {
// Anyone can harvest it at any given time.
// I understand the possibility of being frontrun / sandwiched
// But ETH is a dark forest, and I wanna see how this plays out
// i.e. will be be heavily frontrunned/sandwiched?
// if so, a new strategy will be deployed.
// stablecoin we want to convert to
(address to, uint256 toIndex) = getMostPremium();
// Collects crv tokens
// Don't bother voting in v1
SCRVVoter(scrvVoter).harvest(gauge);
uint256 _crv = IERC20(crv).balanceOf(address(this));
if (_crv > 0) {
// How much CRV to keep to restake?
uint256 _keepCRV = _crv.mul(keepCRV).div(keepCRVMax);
IERC20(crv).safeTransfer(address(crvLocker), _keepCRV);
// How much CRV to swap?
_crv = _crv.sub(_keepCRV);
_swapUniswap(crv, to, _crv);
}
// Collects SNX tokens
SCRVVoter(scrvVoter).claimRewards();
uint256 _snx = IERC20(snx).balanceOf(address(this));
if (_snx > 0) {
_swapUniswap(snx, to, _snx);
}
// Adds liquidity to curve.fi's susd pool
// to get back want (scrv)
uint256 _to = IERC20(to).balanceOf(address(this));
if (_to > 0) {
IERC20(to).safeApprove(curve, 0);
IERC20(to).safeApprove(curve, _to);
uint256[4] memory liquidity;
liquidity[toIndex] = _to;
ICurveFi_4(curve).add_liquidity(liquidity, 0);
}
// We want to get back sCRV
_distributePerformanceFeesAndDeposit();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "./strategy-base.sol";
import "../interfaces/curve.sol";
// Base contract for Curve based staking contract interfaces
abstract contract StrategyCurveBase is StrategyBase {
// curve dao
address public gauge;
address public curve;
address public mintr = 0xd061D61a4d941c39E5453435B6345Dc261C2fcE0;
// stablecoins
address public dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address public usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
address public susd = 0x57Ab1ec28D129707052df4dF418D58a2D46d5f51;
// bitcoins
address public wbtc = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
address public renbtc = 0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D;
// rewards
address public crv = 0xD533a949740bb3306d119CC777fa900bA034cd52;
// How much CRV tokens to keep
uint256 public keepCRV = 0;
uint256 public keepCRVMax = 10000;
constructor(
address _curve,
address _gauge,
address _want,
address _governance,
address _strategist,
address _controller,
address _timelock
)
public
StrategyBase(_want, _governance, _strategist, _controller, _timelock)
{
curve = _curve;
gauge = _gauge;
}
// **** Getters ****
function balanceOfPool() public override view returns (uint256) {
return ICurveGauge(gauge).balanceOf(address(this));
}
function getHarvestable() external returns (uint256) {
return ICurveGauge(gauge).claimable_tokens(address(this));
}
function getMostPremium() public virtual view returns (address, uint256);
// **** Setters ****
function setKeepCRV(uint256 _keepCRV) external {
require(msg.sender == governance, "!governance");
keepCRV = _keepCRV;
}
// **** State Mutation functions ****
function deposit() public override {
uint256 _want = IERC20(want).balanceOf(address(this));
if (_want > 0) {
IERC20(want).safeApprove(gauge, 0);
IERC20(want).safeApprove(gauge, _want);
ICurveGauge(gauge).deposit(_want);
}
}
function _withdrawSome(uint256 _amount)
internal
override
returns (uint256)
{
ICurveGauge(gauge).withdraw(_amount);
return _amount;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
import "../../lib/erc20.sol";
import "../../lib/safe-math.sol";
import "../../lib/exponential.sol";
import "../strategy-base.sol";
import "../../interfaces/jar.sol";
import "../../interfaces/uniswapv2.sol";
import "../../interfaces/controller.sol";
import "../../interfaces/compound.sol";
contract StrategyCmpdDaiV2 is StrategyBase, Exponential {
address
public constant comptroller = 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B;
address public constant lens = 0xd513d22422a3062Bd342Ae374b4b9c20E0a9a074;
address public constant dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant comp = 0xc00e94Cb662C3520282E6f5717214004A7f26888;
address public constant cdai = 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643;
address public constant cether = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;
// Require a 0.1 buffer between
// market collateral factor and strategy's collateral factor
// when leveraging
uint256 colFactorLeverageBuffer = 100;
uint256 colFactorLeverageBufferMax = 1000;
// Allow a 0.05 buffer
// between market collateral factor and strategy's collateral factor
// until we have to deleverage
// This is so we can hit max leverage and keep accruing interest
uint256 colFactorSyncBuffer = 50;
uint256 colFactorSyncBufferMax = 1000;
// Keeper bots
// Maintain leverage within buffer
mapping(address => bool) keepers;
constructor(
address _governance,
address _strategist,
address _controller,
address _timelock
)
public
StrategyBase(dai, _governance, _strategist, _controller, _timelock)
{
// Enter cDAI Market
address[] memory ctokens = new address[](1);
ctokens[0] = cdai;
IComptroller(comptroller).enterMarkets(ctokens);
}
// **** Modifiers **** //
modifier onlyKeepers {
require(
keepers[msg.sender] ||
msg.sender == address(this) ||
msg.sender == strategist ||
msg.sender == governance,
"!keepers"
);
_;
}
// **** Views **** //
function getName() external override pure returns (string memory) {
return "StrategyCmpdDaiV2";
}
function getSuppliedView() public view returns (uint256) {
(, uint256 cTokenBal, , uint256 exchangeRate) = ICToken(cdai)
.getAccountSnapshot(address(this));
(, uint256 bal) = mulScalarTruncate(
Exp({mantissa: exchangeRate}),
cTokenBal
);
return bal;
}
function getBorrowedView() public view returns (uint256) {
return ICToken(cdai).borrowBalanceStored(address(this));
}
function balanceOfPool() public override view returns (uint256) {
uint256 supplied = getSuppliedView();
uint256 borrowed = getBorrowedView();
return supplied.sub(borrowed);
}
// Given an unleveraged supply balance, return the target
// leveraged supply balance which is still within the safety buffer
function getLeveragedSupplyTarget(uint256 supplyBalance)
public
view
returns (uint256)
{
uint256 leverage = getMaxLeverage();
return supplyBalance.mul(leverage).div(1e18);
}
function getSafeLeverageColFactor() public view returns (uint256) {
uint256 colFactor = getMarketColFactor();
// Collateral factor within the buffer
uint256 safeColFactor = colFactor.sub(
colFactorLeverageBuffer.mul(1e18).div(colFactorLeverageBufferMax)
);
return safeColFactor;
}
function getSafeSyncColFactor() public view returns (uint256) {
uint256 colFactor = getMarketColFactor();
// Collateral factor within the buffer
uint256 safeColFactor = colFactor.sub(
colFactorSyncBuffer.mul(1e18).div(colFactorSyncBufferMax)
);
return safeColFactor;
}
function getMarketColFactor() public view returns (uint256) {
(, uint256 colFactor) = IComptroller(comptroller).markets(cdai);
return colFactor;
}
// Max leverage we can go up to, w.r.t safe buffer
function getMaxLeverage() public view returns (uint256) {
uint256 safeLeverageColFactor = getSafeLeverageColFactor();
// Infinite geometric series
uint256 leverage = uint256(1e36).div(1e18 - safeLeverageColFactor);
return leverage;
}
// **** Pseudo-view functions (use `callStatic` on these) **** //
/* The reason why these exists is because of the nature of the
interest accruing supply + borrow balance. The "view" methods
are technically snapshots and don't represent the real value.
As such there are pseudo view methods where you can retrieve the
results by calling `callStatic`.
*/
function getCompAccrued() public returns (uint256) {
(, , , uint256 accrued) = ICompoundLens(lens).getCompBalanceMetadataExt(
comp,
comptroller,
address(this)
);
return accrued;
}
function getColFactor() public returns (uint256) {
uint256 supplied = getSupplied();
uint256 borrowed = getBorrowed();
return borrowed.mul(1e18).div(supplied);
}
function getSuppliedUnleveraged() public returns (uint256) {
uint256 supplied = getSupplied();
uint256 borrowed = getBorrowed();
return supplied.sub(borrowed);
}
function getSupplied() public returns (uint256) {
return ICToken(cdai).balanceOfUnderlying(address(this));
}
function getBorrowed() public returns (uint256) {
return ICToken(cdai).borrowBalanceCurrent(address(this));
}
function getBorrowable() public returns (uint256) {
uint256 supplied = getSupplied();
uint256 borrowed = getBorrowed();
(, uint256 colFactor) = IComptroller(comptroller).markets(cdai);
// 99.99% just in case some dust accumulates
return
supplied.mul(colFactor).div(1e18).sub(borrowed).mul(9999).div(
10000
);
}
function getCurrentLeverage() public returns (uint256) {
uint256 supplied = getSupplied();
uint256 borrowed = getBorrowed();
return supplied.mul(1e18).div(supplied.sub(borrowed));
}
// **** Setters **** //
function addKeeper(address _keeper) public {
require(
msg.sender == governance || msg.sender == strategist,
"!governance"
);
keepers[_keeper] = true;
}
function removeKeeper(address _keeper) public {
require(
msg.sender == governance || msg.sender == strategist,
"!governance"
);
keepers[_keeper] = false;
}
function setColFactorLeverageBuffer(uint256 _colFactorLeverageBuffer)
public
{
require(
msg.sender == governance || msg.sender == strategist,
"!governance"
);
colFactorLeverageBuffer = _colFactorLeverageBuffer;
}
function setColFactorSyncBuffer(uint256 _colFactorSyncBuffer) public {
require(
msg.sender == governance || msg.sender == strategist,
"!governance"
);
colFactorSyncBuffer = _colFactorSyncBuffer;
}
// **** State mutations **** //
// Do a `callStatic` on this.
// If it returns true then run it for realz. (i.e. eth_signedTx, not eth_call)
function sync() public returns (bool) {
uint256 colFactor = getColFactor();
uint256 safeSyncColFactor = getSafeSyncColFactor();
// If we're not safe
if (colFactor > safeSyncColFactor) {
uint256 unleveragedSupply = getSuppliedUnleveraged();
uint256 idealSupply = getLeveragedSupplyTarget(unleveragedSupply);
deleverageUntil(idealSupply);
return true;
}
return false;
}
function leverageToMax() public {
uint256 unleveragedSupply = getSuppliedUnleveraged();
uint256 idealSupply = getLeveragedSupplyTarget(unleveragedSupply);
leverageUntil(idealSupply);
}
// Leverages until we're supplying <x> amount
// 1. Redeem <x> DAI
// 2. Repay <x> DAI
function leverageUntil(uint256 _supplyAmount) public onlyKeepers {
// 1. Borrow out <X> DAI
// 2. Supply <X> DAI
uint256 leverage = getMaxLeverage();
uint256 unleveragedSupply = getSuppliedUnleveraged();
require(
_supplyAmount >= unleveragedSupply &&
_supplyAmount <= unleveragedSupply.mul(leverage).div(1e18),
"!leverage"
);
// Since we're only leveraging one asset
// Supplied = borrowed
uint256 _borrowAndSupply;
uint256 supplied = getSupplied();
while (supplied < _supplyAmount) {
_borrowAndSupply = getBorrowable();
if (supplied.add(_borrowAndSupply) > _supplyAmount) {
_borrowAndSupply = _supplyAmount.sub(supplied);
}
ICToken(cdai).borrow(_borrowAndSupply);
deposit();
supplied = supplied.add(_borrowAndSupply);
}
}
function deleverageToMin() public {
uint256 unleveragedSupply = getSuppliedUnleveraged();
deleverageUntil(unleveragedSupply);
}
// Deleverages until we're supplying <x> amount
// 1. Redeem <x> DAI
// 2. Repay <x> DAI
function deleverageUntil(uint256 _supplyAmount) public onlyKeepers {
uint256 unleveragedSupply = getSuppliedUnleveraged();
uint256 supplied = getSupplied();
require(
_supplyAmount >= unleveragedSupply && _supplyAmount <= supplied,
"!deleverage"
);
// Since we're only leveraging on 1 asset
// redeemable = borrowable
uint256 _redeemAndRepay = getBorrowable();
do {
if (supplied.sub(_redeemAndRepay) < _supplyAmount) {
_redeemAndRepay = supplied.sub(_supplyAmount);
}
require(
ICToken(cdai).redeemUnderlying(_redeemAndRepay) == 0,
"!redeem"
);
IERC20(dai).safeApprove(cdai, 0);
IERC20(dai).safeApprove(cdai, _redeemAndRepay);
require(ICToken(cdai).repayBorrow(_redeemAndRepay) == 0, "!repay");
supplied = supplied.sub(_redeemAndRepay);
} while (supplied > _supplyAmount);
}
function harvest() public override onlyBenevolent {
address[] memory ctokens = new address[](1);
ctokens[0] = cdai;
IComptroller(comptroller).claimComp(address(this), ctokens);
uint256 _comp = IERC20(comp).balanceOf(address(this));
if (_comp > 0) {
_swapUniswap(comp, want, _comp);
}
_distributePerformanceFeesAndDeposit();
}
function deposit() public override {
uint256 _want = IERC20(want).balanceOf(address(this));
if (_want > 0) {
IERC20(want).safeApprove(cdai, 0);
IERC20(want).safeApprove(cdai, _want);
require(ICToken(cdai).mint(_want) == 0, "!deposit");
}
}
function _withdrawSome(uint256 _amount)
internal
override
returns (uint256)
{
uint256 _want = balanceOfWant();
if (_want < _amount) {
uint256 _redeem = _amount.sub(_want);
// Make sure market can cover liquidity
require(ICToken(cdai).getCash() >= _redeem, "!cash-liquidity");
// How much borrowed amount do we need to free?
uint256 borrowed = getBorrowed();
uint256 supplied = getSupplied();
uint256 curLeverage = getCurrentLeverage();
uint256 borrowedToBeFree = _redeem.mul(curLeverage).div(1e18);
// If the amount we need to free is > borrowed
// Just free up all the borrowed amount
if (borrowedToBeFree > borrowed) {
this.deleverageToMin();
} else {
// Otherwise just keep freeing up borrowed amounts until
// we hit a safe number to redeem our underlying
this.deleverageUntil(supplied.sub(borrowedToBeFree));
}
// Redeems underlying
require(ICToken(cdai).redeemUnderlying(_redeem) == 0, "!redeem");
}
return _amount;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
import "../strategy-uni-farm-base.sol";
contract StrategyUniEthWBtcLpV2 is StrategyUniFarmBase {
// Token addresses
address public uni_rewards = 0xCA35e32e7926b96A9988f61d510E038108d8068e;
address public uni_eth_wbtc_lp = 0xBb2b8038a1640196FbE3e38816F3e67Cba72D940;
address public wbtc = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
constructor(
address _governance,
address _strategist,
address _controller,
address _timelock
)
public
StrategyUniFarmBase(
wbtc,
uni_rewards,
uni_eth_wbtc_lp,
_governance,
_strategist,
_controller,
_timelock
)
{}
// **** Views ****
function getName() external override pure returns (string memory) {
return "StrategyUniEthWBtcLpV2";
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
import "../strategy-uni-farm-base.sol";
contract StrategyUniEthUsdtLpV4 is StrategyUniFarmBase {
// Token addresses
address public uni_rewards = 0x6C3e4cb2E96B01F4b866965A91ed4437839A121a;
address public uni_eth_usdt_lp = 0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852;
address public usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
constructor(
address _governance,
address _strategist,
address _controller,
address _timelock
)
public
StrategyUniFarmBase(
usdt,
uni_rewards,
uni_eth_usdt_lp,
_governance,
_strategist,
_controller,
_timelock
)
{}
// **** Views ****
function getName() external override pure returns (string memory) {
return "StrategyUniEthUsdtLpV4";
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.2;
import "../strategy-uni-farm-base.sol";
contract StrategyUniEthUsdcLpV4 is StrategyUniFarmBase {
// Token addresses
address public uni_rewards = 0x7FBa4B8Dc5E7616e59622806932DBea72537A56b;
address public uni_eth_usdc_lp = 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc;
address public usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
constructor(
address _governance,
address _strategist,
address _controller,
address _timelock
)
public
StrategyUniFarmBase(
usdc,
uni_rewards,
uni_eth_usdc_lp,
_governance,
_strategist,
_controller,
_timelock
)
{}
// **** Views ****
function getName() external override pure returns (string memory) {
return "StrategyUniEthUsdcLpV4";
}
}pragma solidity ^0.6.7;
import "../lib/erc20.sol";
import "../lib/safe-math.sol";
import "../interfaces/jar.sol";
import "../interfaces/staking-rewards.sol";
import "../interfaces/uniswapv2.sol";
import "../interfaces/controller.sol";
// Strategy Contract Basics
abstract contract StrategyBase {
using SafeERC20 for IERC20;
using Address for address;
using SafeMath for uint256;
// Perfomance fees - start with 4.5%
uint256 public performanceTreasuryFee = 450;
uint256 public constant performanceTreasuryMax = 10000;
uint256 public performanceDevFee = 0;
uint256 public constant performanceDevMax = 10000;
// Withdrawal fee 0.5%
// - 0.325% to treasury
// - 0.175% to dev fund
uint256 public withdrawalTreasuryFee = 325;
uint256 public constant withdrawalTreasuryMax = 100000;
uint256 public withdrawalDevFundFee = 175;
uint256 public constant withdrawalDevFundMax = 100000;
// Tokens
address public want;
address public constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// User accounts
address public governance;
address public controller;
address public strategist;
address public timelock;
// Dex
address public univ2Router2 = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
constructor(
address _want,
address _governance,
address _strategist,
address _controller,
address _timelock
) public {
require(_want != address(0));
require(_governance != address(0));
require(_strategist != address(0));
require(_controller != address(0));
require(_timelock != address(0));
want = _want;
governance = _governance;
strategist = _strategist;
controller = _controller;
timelock = _timelock;
}
// **** Modifiers **** //
modifier onlyBenevolent {
require(
msg.sender == tx.origin ||
msg.sender == governance ||
msg.sender == strategist
);
_;
}
// **** Views **** //
function balanceOfWant() public view returns (uint256) {
return IERC20(want).balanceOf(address(this));
}
function balanceOfPool() public virtual view returns (uint256);
function balanceOf() public view returns (uint256) {
return balanceOfWant().add(balanceOfPool());
}
function getName() external virtual pure returns (string memory);
// **** Setters **** //
function setWithdrawalDevFundFee(uint256 _withdrawalDevFundFee) external {
require(msg.sender == timelock, "!timelock");
withdrawalDevFundFee = _withdrawalDevFundFee;
}
function setWithdrawalTreasuryFee(uint256 _withdrawalTreasuryFee) external {
require(msg.sender == timelock, "!timelock");
withdrawalTreasuryFee = _withdrawalTreasuryFee;
}
function setPerformanceDevFee(uint256 _performanceDevFee) external {
require(msg.sender == timelock, "!timelock");
performanceDevFee = _performanceDevFee;
}
function setPerformanceTreasuryFee(uint256 _performanceTreasuryFee)
external
{
require(msg.sender == timelock, "!timelock");
performanceTreasuryFee = _performanceTreasuryFee;
}
function setStrategist(address _strategist) external {
require(msg.sender == governance, "!governance");
strategist = _strategist;
}
function setGovernance(address _governance) external {
require(msg.sender == governance, "!governance");
governance = _governance;
}
function setTimelock(address _timelock) external {
require(msg.sender == timelock, "!timelock");
timelock = _timelock;
}
function setController(address _controller) external {
require(msg.sender == timelock, "!timelock");
controller = _controller;
}
// **** State mutations **** //
function deposit() public virtual;
// Controller only function for creating additional rewards from dust
function withdraw(IERC20 _asset) external returns (uint256 balance) {
require(msg.sender == controller, "!controller");
require(want != address(_asset), "want");
balance = _asset.balanceOf(address(this));
_asset.safeTransfer(controller, balance);
}
// Withdraw partial funds, normally used with a jar withdrawal
function withdraw(uint256 _amount) external {
require(msg.sender == controller, "!controller");
uint256 _balance = IERC20(want).balanceOf(address(this));
if (_balance < _amount) {
_amount = _withdrawSome(_amount.sub(_balance));
_amount = _amount.add(_balance);
}
uint256 _feeDev = _amount.mul(withdrawalDevFundFee).div(
withdrawalDevFundMax
);
IERC20(want).safeTransfer(IController(controller).devfund(), _feeDev);
uint256 _feeTreasury = _amount.mul(withdrawalTreasuryFee).div(
withdrawalTreasuryMax
);
IERC20(want).safeTransfer(
IController(controller).treasury(),
_feeTreasury
);
address _jar = IController(controller).jars(address(want));
require(_jar != address(0), "!jar"); // additional protection so we don't burn the funds
IERC20(want).safeTransfer(_jar, _amount.sub(_feeDev).sub(_feeTreasury));
}
// Withdraw funds, used to swap between strategies
function withdrawForSwap(uint256 _amount)
external
returns (uint256 balance)
{
require(msg.sender == controller, "!controller");
_withdrawSome(_amount);
balance = IERC20(want).balanceOf(address(this));
address _jar = IController(controller).jars(address(want));
require(_jar != address(0), "!jar");
IERC20(want).safeTransfer(_jar, balance);
}
// Withdraw all funds, normally used when migrating strategies
function withdrawAll() external returns (uint256 balance) {
require(msg.sender == controller, "!controller");
_withdrawAll();
balance = IERC20(want).balanceOf(address(this));
address _jar = IController(controller).jars(address(want));
require(_jar != address(0), "!jar"); // additional protection so we don't burn the funds
IERC20(want).safeTransfer(_jar, balance);
}
function _withdrawAll() internal {
_withdrawSome(balanceOfPool());
}
function _withdrawSome(uint256 _amount) internal virtual returns (uint256);
function harvest() public virtual;
// **** Emergency functions ****
function execute(address _target, bytes memory _data)
public
payable
returns (bytes memory response)
{
require(msg.sender == timelock, "!timelock");
require(_target != address(0), "!target");
// call contract in current context
assembly {
let succeeded := delegatecall(
sub(gas(), 5000),
_target,
add(_data, 0x20),
mload(_data),
0,
0
)
let size := returndatasize()
response := mload(0x40)
mstore(
0x40,
add(response, and(add(add(size, 0x20), 0x1f), not(0x1f)))
)
mstore(response, size)
returndatacopy(add(response, 0x20), 0, size)
switch iszero(succeeded)
case 1 {
// throw if delegatecall failed
revert(add(response, 0x20), size)
}
}
}
// **** Internal functions ****
function _swapUniswap(
address _from,
address _to,
uint256 _amount
) internal {
require(_to != address(0));
// Swap with uniswap
IERC20(_from).safeApprove(univ2Router2, 0);
IERC20(_from).safeApprove(univ2Router2, _amount);
address[] memory path;
if (_from == weth || _to == weth) {
path = new address[](2);
path[0] = _from;
path[1] = _to;
} else {
path = new address[](3);
path[0] = _from;
path[1] = weth;
path[2] = _to;
}
UniswapRouterV2(univ2Router2).swapExactTokensForTokens(
_amount,
0,
path,
address(this),
now.add(60)
);
}
function _distributePerformanceFeesAndDeposit() internal {
uint256 _want = IERC20(want).balanceOf(address(this));
if (_want > 0) {
// Treasury fees
IERC20(want).safeTransfer(
IController(controller).treasury(),
_want.mul(performanceTreasuryFee).div(performanceTreasuryMax)
);
// Performance fee
IERC20(want).safeTransfer(
IController(controller).devfund(),
_want.mul(performanceDevFee).div(performanceDevMax)
);
deposit();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "./strategy-staking-rewards-base.sol";
abstract contract StrategyUniFarmBase is StrategyStakingRewardsBase {
// Token addresses
address public uni = 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984;
// WETH/<token1> pair
address public token1;
// How much UNI tokens to keep?
uint256 public keepUNI = 0;
uint256 public constant keepUNIMax = 10000;
constructor(
address _token1,
address _rewards,
address _lp,
address _governance,
address _strategist,
address _controller,
address _timelock
)
public
StrategyStakingRewardsBase(
_rewards,
_lp,
_governance,
_strategist,
_controller,
_timelock
)
{
token1 = _token1;
}
// **** Setters ****
function setKeepUNI(uint256 _keepUNI) external {
require(msg.sender == timelock, "!timelock");
keepUNI = _keepUNI;
}
// **** State Mutations ****
function harvest() public override onlyBenevolent {
// Anyone can harvest it at any given time.
// I understand the possibility of being frontrun
// But ETH is a dark forest, and I wanna see how this plays out
// i.e. will be be heavily frontrunned?
// if so, a new strategy will be deployed.
// Collects UNI tokens
IStakingRewards(rewards).getReward();
uint256 _uni = IERC20(uni).balanceOf(address(this));
if (_uni > 0) {
// 10% is locked up for future gov
uint256 _keepUNI = _uni.mul(keepUNI).div(keepUNIMax);
IERC20(uni).safeTransfer(
IController(controller).treasury(),
_keepUNI
);
_swapUniswap(uni, weth, _uni.sub(_keepUNI));
}
// Swap half WETH for DAI
uint256 _weth = IERC20(weth).balanceOf(address(this));
if (_weth > 0) {
_swapUniswap(weth, token1, _weth.div(2));
}
// Adds in liquidity for ETH/DAI
_weth = IERC20(weth).balanceOf(address(this));
uint256 _token1 = IERC20(token1).balanceOf(address(this));
if (_weth > 0 && _token1 > 0) {
IERC20(weth).safeApprove(univ2Router2, 0);
IERC20(weth).safeApprove(univ2Router2, _weth);
IERC20(token1).safeApprove(univ2Router2, 0);
IERC20(token1).safeApprove(univ2Router2, _token1);
UniswapRouterV2(univ2Router2).addLiquidity(
weth,
token1,
_weth,
_token1,
0,
0,
address(this),
now + 60
);
// Donates DUST
IERC20(weth).transfer(
IController(controller).treasury(),
IERC20(weth).balanceOf(address(this))
);
IERC20(token1).safeTransfer(
IController(controller).treasury(),
IERC20(token1).balanceOf(address(this))
);
}
// We want to get back UNI LP tokens
_distributePerformanceFeesAndDeposit();
}
}pragma solidity ^0.6.7;
import "./strategy-base.sol";
// Base contract for SNX Staking rewards contract interfaces
abstract contract StrategyStakingRewardsBase is StrategyBase {
address public rewards;
// **** Getters ****
constructor(
address _rewards,
address _want,
address _governance,
address _strategist,
address _controller,
address _timelock
)
public
StrategyBase(_want, _governance, _strategist, _controller, _timelock)
{
rewards = _rewards;
}
function balanceOfPool() public override view returns (uint256) {
return IStakingRewards(rewards).balanceOf(address(this));
}
function getHarvestable() external view returns (uint256) {
return IStakingRewards(rewards).earned(address(this));
}
// **** Setters ****
function deposit() public override {
uint256 _want = IERC20(want).balanceOf(address(this));
if (_want > 0) {
IERC20(want).safeApprove(rewards, 0);
IERC20(want).safeApprove(rewards, _want);
IStakingRewards(rewards).stake(_want);
}
}
function _withdrawSome(uint256 _amount)
internal
override
returns (uint256)
{
IStakingRewards(rewards).withdraw(_amount);
return _amount;
}
}pragma solidity 0.6.7;
import "../lib/enumerableSet.sol";
import "../lib/safe-math.sol";
import "../lib/erc20.sol";
import "../lib/ownable.sol";
import "./pickle-token.sol";
// MasterChef was the master of pickle. He now governs over PICKLES. He can make Pickles and he is a fair guy.
//
// Note that it's ownable and the owner wields tremendous power. The ownership
// will be transferred to a governance smart contract once PICKLES is sufficiently
// distributed and the community can show to govern itself.
//
// Have fun reading it. Hopefully it's bug-free. God bless.
contract MasterChef is Ownable {
using SafeMath for uint256;
using SafeERC20 for IERC20;
// Info of each user.
struct UserInfo {
uint256 amount; // How many LP tokens the user has provided.
uint256 rewardDebt; // Reward debt. See explanation below.
//
// We do some fancy math here. Basically, any point in time, the amount of PICKLEs
// entitled to a user but is pending to be distributed is:
//
// pending reward = (user.amount * pool.accPicklePerShare) - user.rewardDebt
//
// Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens:
// 1. The pool's `accPicklePerShare` (and `lastRewardBlock`) gets updated.
// 2. User receives the pending reward sent to his/her address.
// 3. User's `amount` gets updated.
// 4. User's `rewardDebt` gets updated.
}
// Info of each pool.
struct PoolInfo {
IERC20 lpToken; // Address of LP token contract.
uint256 allocPoint; // How many allocation points assigned to this pool. PICKLEs to distribute per block.
uint256 lastRewardBlock; // Last block number that PICKLEs distribution occurs.
uint256 accPicklePerShare; // Accumulated PICKLEs per share, times 1e12. See below.
}
// The PICKLE TOKEN!
PickleToken public pickle;
// Dev fund (2%, initially)
uint256 public devFundDivRate = 50;
// Dev address.
address public devaddr;
// Block number when bonus PICKLE period ends.
uint256 public bonusEndBlock;
// PICKLE tokens created per block.
uint256 public picklePerBlock;
// Bonus muliplier for early pickle makers.
uint256 public constant BONUS_MULTIPLIER = 10;
// Info of each pool.
PoolInfo[] public poolInfo;
// Info of each user that stakes LP tokens.
mapping(uint256 => mapping(address => UserInfo)) public userInfo;
// Total allocation points. Must be the sum of all allocation points in all pools.
uint256 public totalAllocPoint = 0;
// The block number when PICKLE mining starts.
uint256 public startBlock;
// Events
event Recovered(address token, uint256 amount);
event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
event EmergencyWithdraw(
address indexed user,
uint256 indexed pid,
uint256 amount
);
constructor(
PickleToken _pickle,
address _devaddr,
uint256 _picklePerBlock,
uint256 _startBlock,
uint256 _bonusEndBlock
) public {
pickle = _pickle;
devaddr = _devaddr;
picklePerBlock = _picklePerBlock;
bonusEndBlock = _bonusEndBlock;
startBlock = _startBlock;
}
function poolLength() external view returns (uint256) {
return poolInfo.length;
}
// Add a new lp to the pool. Can only be called by the owner.
// XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do.
function add(
uint256 _allocPoint,
IERC20 _lpToken,
bool _withUpdate
) public onlyOwner {
if (_withUpdate) {
massUpdatePools();
}
uint256 lastRewardBlock = block.number > startBlock
? block.number
: startBlock;
totalAllocPoint = totalAllocPoint.add(_allocPoint);
poolInfo.push(
PoolInfo({
lpToken: _lpToken,
allocPoint: _allocPoint,
lastRewardBlock: lastRewardBlock,
accPicklePerShare: 0
})
);
}
// Update the given pool's PICKLE allocation point. Can only be called by the owner.
function set(
uint256 _pid,
uint256 _allocPoint,
bool _withUpdate
) public onlyOwner {
if (_withUpdate) {
massUpdatePools();
}
totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(
_allocPoint
);
poolInfo[_pid].allocPoint = _allocPoint;
}
// Return reward multiplier over the given _from to _to block.
function getMultiplier(uint256 _from, uint256 _to)
public
view
returns (uint256)
{
if (_to <= bonusEndBlock) {
return _to.sub(_from).mul(BONUS_MULTIPLIER);
} else if (_from >= bonusEndBlock) {
return _to.sub(_from);
} else {
return
bonusEndBlock.sub(_from).mul(BONUS_MULTIPLIER).add(
_to.sub(bonusEndBlock)
);
}
}
// View function to see pending PICKLEs on frontend.
function pendingPickle(uint256 _pid, address _user)
external
view
returns (uint256)
{
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][_user];
uint256 accPicklePerShare = pool.accPicklePerShare;
uint256 lpSupply = pool.lpToken.balanceOf(address(this));
if (block.number > pool.lastRewardBlock && lpSupply != 0) {
uint256 multiplier = getMultiplier(
pool.lastRewardBlock,
block.number
);
uint256 pickleReward = multiplier
.mul(picklePerBlock)
.mul(pool.allocPoint)
.div(totalAllocPoint);
accPicklePerShare = accPicklePerShare.add(
pickleReward.mul(1e12).div(lpSupply)
);
}
return
user.amount.mul(accPicklePerShare).div(1e12).sub(user.rewardDebt);
}
// Update reward vairables for all pools. Be careful of gas spending!
function massUpdatePools() public {
uint256 length = poolInfo.length;
for (uint256 pid = 0; pid < length; ++pid) {
updatePool(pid);
}
}
// Update reward variables of the given pool to be up-to-date.
function updatePool(uint256 _pid) public {
PoolInfo storage pool = poolInfo[_pid];
if (block.number <= pool.lastRewardBlock) {
return;
}
uint256 lpSupply = pool.lpToken.balanceOf(address(this));
if (lpSupply == 0) {
pool.lastRewardBlock = block.number;
return;
}
uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
uint256 pickleReward = multiplier
.mul(picklePerBlock)
.mul(pool.allocPoint)
.div(totalAllocPoint);
pickle.mint(devaddr, pickleReward.div(devFundDivRate));
pickle.mint(address(this), pickleReward);
pool.accPicklePerShare = pool.accPicklePerShare.add(
pickleReward.mul(1e12).div(lpSupply)
);
pool.lastRewardBlock = block.number;
}
// Deposit LP tokens to MasterChef for PICKLE allocation.
function deposit(uint256 _pid, uint256 _amount) public {
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][msg.sender];
updatePool(_pid);
if (user.amount > 0) {
uint256 pending = user
.amount
.mul(pool.accPicklePerShare)
.div(1e12)
.sub(user.rewardDebt);
safePickleTransfer(msg.sender, pending);
}
pool.lpToken.safeTransferFrom(
address(msg.sender),
address(this),
_amount
);
user.amount = user.amount.add(_amount);
user.rewardDebt = user.amount.mul(pool.accPicklePerShare).div(1e12);
emit Deposit(msg.sender, _pid, _amount);
}
// Withdraw LP tokens from MasterChef.
function withdraw(uint256 _pid, uint256 _amount) public {
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][msg.sender];
require(user.amount >= _amount, "withdraw: not good");
updatePool(_pid);
uint256 pending = user.amount.mul(pool.accPicklePerShare).div(1e12).sub(
user.rewardDebt
);
safePickleTransfer(msg.sender, pending);
user.amount = user.amount.sub(_amount);
user.rewardDebt = user.amount.mul(pool.accPicklePerShare).div(1e12);
pool.lpToken.safeTransfer(address(msg.sender), _amount);
emit Withdraw(msg.sender, _pid, _amount);
}
// Withdraw without caring about rewards. EMERGENCY ONLY.
function emergencyWithdraw(uint256 _pid) public {
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][msg.sender];
pool.lpToken.safeTransfer(address(msg.sender), user.amount);
emit EmergencyWithdraw(msg.sender, _pid, user.amount);
user.amount = 0;
user.rewardDebt = 0;
}
// Safe pickle transfer function, just in case if rounding error causes pool to not have enough PICKLEs.
function safePickleTransfer(address _to, uint256 _amount) internal {
uint256 pickleBal = pickle.balanceOf(address(this));
if (_amount > pickleBal) {
pickle.transfer(_to, pickleBal);
} else {
pickle.transfer(_to, _amount);
}
}
// Update dev address by the previous dev.
function dev(address _devaddr) public {
require(msg.sender == devaddr, "dev: wut?");
devaddr = _devaddr;
}
// **** Additional functions separate from the original masterchef contract ****
function setPicklePerBlock(uint256 _picklePerBlock) public onlyOwner {
require(_picklePerBlock > 0, "!picklePerBlock-0");
picklePerBlock = _picklePerBlock;
}
function setBonusEndBlock(uint256 _bonusEndBlock) public onlyOwner {
bonusEndBlock = _bonusEndBlock;
}
function setDevFundDivRate(uint256 _devFundDivRate) public onlyOwner {
require(_devFundDivRate > 0, "!devFundDivRate-0");
devFundDivRate = _devFundDivRate;
}
}pragma solidity 0.6.7;
import "../lib/erc20.sol";
import "../lib/ownable.sol";
// PickleToken with Governance.
contract PickleToken is ERC20("PickleToken", "PICKLE"), Ownable {
/// @notice Creates `_amount` token to `_to`. Must only be called by the owner (MasterChef).
function mint(address _to, uint256 _amount) public onlyOwner {
_mint(_to, _amount);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "./lib/reentrancy-guard.sol";
import "./lib/pausable.sol";
import "./lib/erc20.sol";
import "./lib/safe-math.sol";
contract StakingRewards is ReentrancyGuard, Pausable {
using SafeMath for uint256;
using SafeERC20 for IERC20;
/* ========== STATE VARIABLES ========== */
IERC20 public rewardsToken;
IERC20 public stakingToken;
uint256 public periodFinish = 0;
uint256 public rewardRate = 0;
uint256 public rewardsDuration = 7 days;
uint256 public lastUpdateTime;
uint256 public rewardPerTokenStored;
mapping(address => uint256) public userRewardPerTokenPaid;
mapping(address => uint256) public rewards;
uint256 private _totalSupply;
mapping(address => uint256) private _balances;
/* ========== CONSTRUCTOR ========== */
constructor(
address _owner,
address _rewardsToken,
address _stakingToken
) public Owned(_owner) {
rewardsToken = IERC20(_rewardsToken);
stakingToken = IERC20(_stakingToken);
}
/* ========== VIEWS ========== */
function totalSupply() external view returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) external view returns (uint256) {
return _balances[account];
}
function lastTimeRewardApplicable() public view returns (uint256) {
return min(block.timestamp, periodFinish);
}
function rewardPerToken() public view returns (uint256) {
if (_totalSupply == 0) {
return rewardPerTokenStored;
}
return
rewardPerTokenStored.add(
lastTimeRewardApplicable()
.sub(lastUpdateTime)
.mul(rewardRate)
.mul(1e18)
.div(_totalSupply)
);
}
function earned(address account) public view returns (uint256) {
return
_balances[account]
.mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
.div(1e18)
.add(rewards[account]);
}
function getRewardForDuration() external view returns (uint256) {
return rewardRate.mul(rewardsDuration);
}
function min(uint256 a, uint256 b) public pure returns (uint256) {
return a < b ? a : b;
}
/* ========== MUTATIVE FUNCTIONS ========== */
function stake(uint256 amount)
external
nonReentrant
notPaused
updateReward(msg.sender)
{
require(amount > 0, "Cannot stake 0");
_totalSupply = _totalSupply.add(amount);
_balances[msg.sender] = _balances[msg.sender].add(amount);
stakingToken.safeTransferFrom(msg.sender, address(this), amount);
emit Staked(msg.sender, amount);
}
function withdraw(uint256 amount)
public
nonReentrant
updateReward(msg.sender)
{
require(amount > 0, "Cannot withdraw 0");
_totalSupply = _totalSupply.sub(amount);
_balances[msg.sender] = _balances[msg.sender].sub(amount);
stakingToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}
function getReward() public nonReentrant updateReward(msg.sender) {
uint256 reward = rewards[msg.sender];
if (reward > 0) {
rewards[msg.sender] = 0;
rewardsToken.safeTransfer(msg.sender, reward);
emit RewardPaid(msg.sender, reward);
}
}
function exit() external {
withdraw(_balances[msg.sender]);
getReward();
}
/* ========== RESTRICTED FUNCTIONS ========== */
function notifyRewardAmount(uint256 reward)
external
onlyOwner
updateReward(address(0))
{
if (block.timestamp >= periodFinish) {
rewardRate = reward.div(rewardsDuration);
} else {
uint256 remaining = periodFinish.sub(block.timestamp);
uint256 leftover = remaining.mul(rewardRate);
rewardRate = reward.add(leftover).div(rewardsDuration);
}
// Ensure the provided reward amount is not more than the balance in the contract.
// This keeps the reward rate in the right range, preventing overflows due to
// very high values of rewardRate in the earned and rewardsPerToken functions;
// Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.
uint256 balance = rewardsToken.balanceOf(address(this));
require(
rewardRate <= balance.div(rewardsDuration),
"Provided reward too high"
);
lastUpdateTime = block.timestamp;
periodFinish = block.timestamp.add(rewardsDuration);
emit RewardAdded(reward);
}
// Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders
function recoverERC20(address tokenAddress, uint256 tokenAmount)
external
onlyOwner
{
// Cannot recover the staking token or the rewards token
require(
tokenAddress != address(stakingToken) &&
tokenAddress != address(rewardsToken),
"Cannot withdraw the staking or rewards tokens"
);
IERC20(tokenAddress).safeTransfer(owner, tokenAmount);
emit Recovered(tokenAddress, tokenAmount);
}
function setRewardsDuration(uint256 _rewardsDuration) external onlyOwner {
require(
block.timestamp > periodFinish,
"Previous rewards period must be complete before changing the duration for the new period"
);
rewardsDuration = _rewardsDuration;
emit RewardsDurationUpdated(rewardsDuration);
}
/* ========== MODIFIERS ========== */
modifier updateReward(address account) {
rewardPerTokenStored = rewardPerToken();
lastUpdateTime = lastTimeRewardApplicable();
if (account != address(0)) {
rewards[account] = earned(account);
userRewardPerTokenPaid[account] = rewardPerTokenStored;
}
_;
}
/* ========== EVENTS ========== */
event RewardAdded(uint256 reward);
event Staked(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
event RewardPaid(address indexed user, uint256 reward);
event RewardsDurationUpdated(uint256 newDuration);
event Recovered(address token, uint256 amount);
}// https://github.com/iearn-finance/vaults/blob/master/contracts/vaults/yVault.sol
pragma solidity ^0.6.7;
import "./interfaces/controller.sol";
import "./lib/erc20.sol";
import "./lib/safe-math.sol";
contract PickleJar is ERC20 {
using SafeERC20 for IERC20;
using Address for address;
using SafeMath for uint256;
IERC20 public token;
uint256 public min = 9500;
uint256 public constant max = 10000;
address public governance;
address public timelock;
address public controller;
constructor(address _token, address _governance, address _timelock, address _controller)
public
ERC20(
string(abi.encodePacked("pickling ", ERC20(_token).name())),
string(abi.encodePacked("p", ERC20(_token).symbol()))
)
{
_setupDecimals(ERC20(_token).decimals());
token = IERC20(_token);
governance = _governance;
timelock = _timelock;
controller = _controller;
}
function balance() public view returns (uint256) {
return
token.balanceOf(address(this)).add(
IController(controller).balanceOf(address(token))
);
}
function setMin(uint256 _min) external {
require(msg.sender == governance, "!governance");
min = _min;
}
function setGovernance(address _governance) public {
require(msg.sender == governance, "!governance");
governance = _governance;
}
function setTimelock(address _timelock) public {
require(msg.sender == timelock, "!timelock");
timelock = _timelock;
}
function setController(address _controller) public {
require(msg.sender == timelock, "!timelock");
controller = _controller;
}
// Custom logic in here for how much the jars allows to be borrowed
// Sets minimum required on-hand to keep small withdrawals cheap
function available() public view returns (uint256) {
return token.balanceOf(address(this)).mul(min).div(max);
}
function earn() public {
uint256 _bal = available();
token.safeTransfer(controller, _bal);
IController(controller).earn(address(token), _bal);
}
function depositAll() external {
deposit(token.balanceOf(msg.sender));
}
function deposit(uint256 _amount) public {
uint256 _pool = balance();
uint256 _before = token.balanceOf(address(this));
token.safeTransferFrom(msg.sender, address(this), _amount);
uint256 _after = token.balanceOf(address(this));
_amount = _after.sub(_before); // Additional check for deflationary tokens
uint256 shares = 0;
if (totalSupply() == 0) {
shares = _amount;
} else {
shares = (_amount.mul(totalSupply())).div(_pool);
}
_mint(msg.sender, shares);
}
function withdrawAll() external {
withdraw(balanceOf(msg.sender));
}
// Used to swap any borrowed reserve over the debt limit to liquidate to 'token'
function harvest(address reserve, uint256 amount) external {
require(msg.sender == controller, "!controller");
require(reserve != address(token), "token");
IERC20(reserve).safeTransfer(controller, amount);
}
// No rebalance implementation for lower fees and faster swaps
function withdraw(uint256 _shares) public {
uint256 r = (balance().mul(_shares)).div(totalSupply());
_burn(msg.sender, _shares);
// Check balance
uint256 b = token.balanceOf(address(this));
if (b < r) {
uint256 _withdraw = r.sub(b);
IController(controller).withdraw(address(token), _withdraw);
uint256 _after = token.balanceOf(address(this));
uint256 _diff = _after.sub(b);
if (_diff < _withdraw) {
r = b.add(_diff);
}
}
token.safeTransfer(msg.sender, r);
}
function getRatio() public view returns (uint256) {
return balance().mul(1e18).div(totalSupply());
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.7;
import "../lib/safe-math.sol";
contract Timelock {
using SafeMath for uint;
event NewAdmin(address indexed newAdmin);
event NewPendingAdmin(address indexed newPendingAdmin);
event NewDelay(uint indexed newDelay);
event CancelTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta);
event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta);
event QueueTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta);
uint public constant GRACE_PERIOD = 14 days;
uint public constant MINIMUM_DELAY = 2 days;
uint public constant MAXIMUM_DELAY = 30 days;
address public admin;
address public pendingAdmin;
uint public delay;
bool public admin_initialized;
mapping (bytes32 => bool) public queuedTransactions;
constructor(address admin_, uint delay_) public {
require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay.");
require(delay_ <= MAXIMUM_DELAY, "Timelock::constructor: Delay must not exceed maximum delay.");
admin = admin_;
delay = delay_;
admin_initialized = false;
}
// XXX: function() external payable { }
receive() external payable { }
function setDelay(uint delay_) public {
require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock.");
require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay.");
require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
delay = delay_;
emit NewDelay(delay);
}
function acceptAdmin() public {
require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin.");
admin = msg.sender;
pendingAdmin = address(0);
emit NewAdmin(admin);
}
function setPendingAdmin(address pendingAdmin_) public {
// allows one time setting of admin for deployment purposes
if (admin_initialized) {
require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock.");
} else {
require(msg.sender == admin, "Timelock::setPendingAdmin: First call must come from admin.");
admin_initialized = true;
}
pendingAdmin = pendingAdmin_;
emit NewPendingAdmin(pendingAdmin);
}
function queueTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public returns (bytes32) {
require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin.");
require(eta >= getBlockTimestamp().add(delay), "Timelock::queueTransaction: Estimated execution block must satisfy delay.");
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
queuedTransactions[txHash] = true;
emit QueueTransaction(txHash, target, value, signature, data, eta);
return txHash;
}
function cancelTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public {
require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin.");
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
queuedTransactions[txHash] = false;
emit CancelTransaction(txHash, target, value, signature, data, eta);
}
function executeTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public payable returns (bytes memory) {
require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale.");
queuedTransactions[txHash] = false;
bytes memory callData;
if (bytes(signature).length == 0) {
callData = data;
} else {
callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
}
// solium-disable-next-line security/no-call-value
(bool success, bytes memory returnData) = target.call.value(value)(callData);
require(success, "Timelock::executeTransaction: Transaction execution reverted.");
emit ExecuteTransaction(txHash, target, value, signature, data, eta);
return returnData;
}
function getBlockTimestamp() internal view returns (uint) {
// solium-disable-next-line security/no-block-members
return block.timestamp;
}
}{
"metadata": {
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_governance","type":"address"},{"internalType":"address","name":"_strategist","type":"address"},{"internalType":"address","name":"_controller","type":"address"},{"internalType":"address","name":"_timelock","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balanceOfPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balanceOfWant","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dai","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_target","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"execute","outputs":[{"internalType":"bytes","name":"response","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getHarvestable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"keepUNI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keepUNIMax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"performanceDevFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"performanceDevMax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"performanceTreasuryFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"performanceTreasuryMax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewards","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_controller","type":"address"}],"name":"setController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_governance","type":"address"}],"name":"setGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_keepUNI","type":"uint256"}],"name":"setKeepUNI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_performanceDevFee","type":"uint256"}],"name":"setPerformanceDevFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_performanceTreasuryFee","type":"uint256"}],"name":"setPerformanceTreasuryFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategist","type":"address"}],"name":"setStrategist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_timelock","type":"address"}],"name":"setTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_withdrawalDevFundFee","type":"uint256"}],"name":"setWithdrawalDevFundFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_withdrawalTreasuryFee","type":"uint256"}],"name":"setWithdrawalTreasuryFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"strategist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timelock","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uni","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uni_eth_dai_lp","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"uni_rewards","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"univ2Router2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"want","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_asset","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawForSwap","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalDevFundFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalDevFundMax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalTreasuryFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalTreasuryMax","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60806040526101c26000908155600181905561014560025560af600355600980546001600160a01b0319908116737a250d5630b4cf539739df2c5dacb4c659f2488d17909155600b80548216731f9840a85d5af5bf1d1762f925bdaddc4201f984179055600d91909155600e8054821673a1484c3aa22a66c62b77e0ae78e15258bd0cb711179055600f8054821673a478c2975ab1ea89e8196811f51a7b7ade33eb1117905560108054909116736b175474e89094c44da98b954eedeac495271d0f179055348015620000d157600080fd5b5060405162002df038038062002df083398181016040526080811015620000f757600080fd5b50805160208201516040830151606090930151601054600e54600f54949593946001600160a01b03928316929182169116868686868585858585858484848484846200014257600080fd5b6001600160a01b0384166200015657600080fd5b6001600160a01b0383166200016a57600080fd5b6001600160a01b0382166200017e57600080fd5b6001600160a01b0381166200019257600080fd5b600480546001600160a01b03199081166001600160a01b03978816179091556005805482169587169590951790945560078054851693861693909317909255600680548416918516919091179055600880548316918416919091179055600a8054821698831698909817909755600c80549097169d169c909c1790945550612bbe9d508d9c50620002329b5060009a5050505050505050505050396000f3fe6080604052600436106102675760003560e01c806388993f2211610144578063c65e3242116100b6578063d925c76e1161007a578063d925c76e146107ba578063dec45641146107cf578063edc9af95146107f9578063f4b9fa751461080e578063f77c479114610823578063fe1f8f7a146104c357610267565b8063c65e32421461071e578063c7b9d53014610748578063d0e30db01461077b578063d21220a714610790578063d33219b4146107a557610267565b8063ab73e43311610108578063ab73e43314610658578063b23ea5bd14610682578063b9e3748914610697578063bdacb303146106ac578063c1a3d44c146106df578063c6223e26146106f457610267565b806388993f22146105b35780638ccdbb70146105c857806392eefe9b146105dd5780639ec5a89414610610578063ab033ea91461062557610267565b80634641257d116101dd57806359739ec4116101a157806359739ec4146105205780635aa6e675146105355780636742426b1461054a578063722713f71461055f5780638237859414610574578063853828b61461059e57610267565b80634641257d146104ae578063479119be146104c35780634fe809cc1461029357806351cff8d9146104d857806351f3d0b81461050b57610267565b80631f1fcd511161022f5780631f1fcd51146103fd5780631fe4a6861461042e578063249fb9b41461044357806326e886c6146102935780632e1a7d4d1461046f5780633fc8cef31461049957610267565b80630547104d1461026c5780630afad5c41461029357806311588086146102a857806317d7de7c146102bd5780631cff79cd14610347575b600080fd5b34801561027857600080fd5b50610281610838565b60408051918252519081900360200190f35b34801561029f57600080fd5b506102816108b2565b3480156102b457600080fd5b506102816108b8565b3480156102c957600080fd5b506102d2610903565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561030c5781810151838201526020016102f4565b50505050905090810190601f1680156103395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102d26004803603604081101561035d57600080fd5b6001600160a01b03823516919081019060408101602082013564010000000081111561038857600080fd5b82018360208201111561039a57600080fd5b803590602001918460018302840111640100000000831117156103bc57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610932945050505050565b34801561040957600080fd5b50610412610a15565b604080516001600160a01b039092168252519081900360200190f35b34801561043a57600080fd5b50610412610a24565b34801561044f57600080fd5b5061046d6004803603602081101561046657600080fd5b5035610a33565b005b34801561047b57600080fd5b5061046d6004803603602081101561049257600080fd5b5035610a83565b3480156104a557600080fd5b50610412610dc0565b3480156104ba57600080fd5b5061046d610dd2565b3480156104cf57600080fd5b50610281611581565b3480156104e457600080fd5b50610281600480360360208110156104fb57600080fd5b50356001600160a01b0316611588565b34801561051757600080fd5b506102816116be565b34801561052c57600080fd5b506102816116c4565b34801561054157600080fd5b506104126116ca565b34801561055657600080fd5b506104126116d9565b34801561056b57600080fd5b506102816116e8565b34801561058057600080fd5b5061046d6004803603602081101561059757600080fd5b503561170e565b3480156105aa57600080fd5b5061028161175e565b3480156105bf57600080fd5b50610281611918565b3480156105d457600080fd5b5061028161191e565b3480156105e957600080fd5b5061046d6004803603602081101561060057600080fd5b50356001600160a01b0316611924565b34801561061c57600080fd5b50610412611991565b34801561063157600080fd5b5061046d6004803603602081101561064857600080fd5b50356001600160a01b03166119a0565b34801561066457600080fd5b5061046d6004803603602081101561067b57600080fd5b5035611a0f565b34801561068e57600080fd5b50610412611a5f565b3480156106a357600080fd5b50610412611a6e565b3480156106b857600080fd5b5061046d600480360360208110156106cf57600080fd5b50356001600160a01b0316611a7d565b3480156106eb57600080fd5b50610281611aea565b34801561070057600080fd5b506102816004803603602081101561071757600080fd5b5035611b39565b34801561072a57600080fd5b5061046d6004803603602081101561074157600080fd5b5035611cf7565b34801561075457600080fd5b5061046d6004803603602081101561076b57600080fd5b50356001600160a01b0316611d47565b34801561078757600080fd5b5061046d611db6565b34801561079c57600080fd5b50610412611ee4565b3480156107b157600080fd5b50610412611ef3565b3480156107c657600080fd5b50610281611f02565b3480156107db57600080fd5b5061046d600480360360208110156107f257600080fd5b5035611f08565b34801561080557600080fd5b50610412611f58565b34801561081a57600080fd5b50610412611f67565b34801561082f57600080fd5b50610412611f76565b600a54604080516246613160e11b815230600482015290516000926001600160a01b031691628cc262916024808301926020929190829003018186803b15801561088157600080fd5b505afa158015610895573d6000803e3d6000fd5b505050506040513d60208110156108ab57600080fd5b5051905090565b61271081565b600a54604080516370a0823160e01b815230600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b15801561088157600080fd5b60408051808201909152601581527414dd1c985d1959de555b9a515d1a11185a531c158d605a1b602082015290565b6008546060906001600160a01b03163314610980576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b6001600160a01b0383166109c5576040805162461bcd60e51b8152602060048201526007602482015266085d185c99d95d60ca1b604482015290519081900360640190fd5b600080835160208501866113885a03f43d6040519250601f19601f6020830101168301604052808352806000602085013e811560018114610a0557610a0c565b8160208501fd5b50505092915050565b6004546001600160a01b031681565b6007546001600160a01b031681565b6008546001600160a01b03163314610a7e576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600055565b6006546001600160a01b03163314610ad0576040805162461bcd60e51b815260206004820152600b60248201526a10b1b7b73a3937b63632b960a91b604482015290519081900360640190fd5b60048054604080516370a0823160e01b81523093810193909352516000926001600160a01b03909216916370a08231916024808301926020929190829003018186803b158015610b1f57600080fd5b505afa158015610b33573d6000803e3d6000fd5b505050506040513d6020811015610b4957600080fd5b5051905081811015610b8257610b6d610b68838363ffffffff611f8516565b611fd0565b9150610b7f828263ffffffff61203a16565b91505b6000610bac620186a0610ba06003548661209490919063ffffffff16565b9063ffffffff6120ed16565b9050610c44600660009054906101000a90046001600160a01b03166001600160a01b0316638d8f1e676040518163ffffffff1660e01b815260040160206040518083038186803b158015610bff57600080fd5b505afa158015610c13573d6000803e3d6000fd5b505050506040513d6020811015610c2957600080fd5b50516004546001600160a01b0316908363ffffffff61212f16565b6000610c62620186a0610ba06002548761209490919063ffffffff16565b9050610cb5600660009054906101000a90046001600160a01b03166001600160a01b03166361d027b36040518163ffffffff1660e01b815260040160206040518083038186803b158015610bff57600080fd5b6006546004805460408051636535246160e11b81526001600160a01b039283169381019390935251600093919091169163ca6a48c2916024808301926020929190829003018186803b158015610d0a57600080fd5b505afa158015610d1e573d6000803e3d6000fd5b505050506040513d6020811015610d3457600080fd5b505190506001600160a01b038116610d7c576040805162461bcd60e51b8152602060048083019190915260248201526310b530b960e11b604482015290519081900360640190fd5b610db981610da084610d94898863ffffffff611f8516565b9063ffffffff611f8516565b6004546001600160a01b0316919063ffffffff61212f16565b5050505050565b600080516020612ae883398151915281565b33321480610dea57506005546001600160a01b031633145b80610dff57506007546001600160a01b031633145b610e0857600080fd5b600a60009054906101000a90046001600160a01b03166001600160a01b0316633d18b9126040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e5857600080fd5b505af1158015610e6c573d6000803e3d6000fd5b5050600b54604080516370a0823160e01b81523060048201529051600094506001600160a01b0390921692506370a08231916024808301926020929190829003018186803b158015610ebd57600080fd5b505afa158015610ed1573d6000803e3d6000fd5b505050506040513d6020811015610ee757600080fd5b505190508015610fdc576000610f0e612710610ba0600d548561209490919063ffffffff16565b9050610fa6600660009054906101000a90046001600160a01b03166001600160a01b03166361d027b36040518163ffffffff1660e01b815260040160206040518083038186803b158015610f6157600080fd5b505afa158015610f75573d6000803e3d6000fd5b505050506040513d6020811015610f8b57600080fd5b5051600b546001600160a01b0316908363ffffffff61212f16565b600b54610fda906001600160a01b0316600080516020612ae8833981519152610fd5858563ffffffff611f8516565b612181565b505b604080516370a0823160e01b81523060048201529051600091600080516020612ae8833981519152916370a0823191602480820192602092909190829003018186803b15801561102b57600080fd5b505afa15801561103f573d6000803e3d6000fd5b505050506040513d602081101561105557600080fd5b50519050801561109057600c5461109090600080516020612ae8833981519152906001600160a01b0316610fd584600263ffffffff6120ed16565b604080516370a0823160e01b81523060048201529051600080516020612ae8833981519152916370a08231916024808301926020929190829003018186803b1580156110db57600080fd5b505afa1580156110ef573d6000803e3d6000fd5b505050506040513d602081101561110557600080fd5b5051600c54604080516370a0823160e01b815230600482015290519293506000926001600160a01b03909216916370a0823191602480820192602092909190829003018186803b15801561115857600080fd5b505afa15801561116c573d6000803e3d6000fd5b505050506040513d602081101561118257600080fd5b5051905081158015906111955750600081115b15611574576009546111c790600080516020612ae8833981519152906001600160a01b0316600063ffffffff6124eb16565b6009546111f390600080516020612ae8833981519152906001600160a01b03168463ffffffff6124eb16565b600954600c54611217916001600160a01b039182169116600063ffffffff6124eb16565b600954600c5461123a916001600160a01b0391821691168363ffffffff6124eb16565b600954600c546040805162e8e33760e81b8152600080516020612ae883398151915260048201526001600160a01b039283166024820152604481018690526064810185905260006084820181905260a482018190523060c4830152603c420160e48301529151929093169263e8e33700926101048083019360609383900390910190829087803b1580156112cd57600080fd5b505af11580156112e1573d6000803e3d6000fd5b505050506040513d60608110156112f757600080fd5b5050600654604080516361d027b360e01b81529051600080516020612ae88339815191529263a9059cbb926001600160a01b03909116916361d027b391600480820192602092909190829003018186803b15801561135457600080fd5b505afa158015611368573d6000803e3d6000fd5b505050506040513d602081101561137e57600080fd5b5051604080516370a0823160e01b81523060048201529051600080516020612ae8833981519152916370a08231916024808301926020929190829003018186803b1580156113cb57600080fd5b505afa1580156113df573d6000803e3d6000fd5b505050506040513d60208110156113f557600080fd5b5051604080516001600160e01b031960e086901b1681526001600160a01b03909316600484015260248301919091525160448083019260209291908290030181600087803b15801561144657600080fd5b505af115801561145a573d6000803e3d6000fd5b505050506040513d602081101561147057600080fd5b5050600654604080516361d027b360e01b81529051611574926001600160a01b0316916361d027b3916004808301926020929190829003018186803b1580156114b857600080fd5b505afa1580156114cc573d6000803e3d6000fd5b505050506040513d60208110156114e257600080fd5b5051600c54604080516370a0823160e01b815230600482015290516001600160a01b03909216916370a0823191602480820192602092909190829003018186803b15801561152f57600080fd5b505afa158015611543573d6000803e3d6000fd5b505050506040513d602081101561155957600080fd5b5051600c546001600160a01b0316919063ffffffff61212f16565b61157c6125fe565b505050565b620186a081565b6006546000906001600160a01b031633146115d8576040805162461bcd60e51b815260206004820152600b60248201526a10b1b7b73a3937b63632b960a91b604482015290519081900360640190fd5b6004546001600160a01b0383811691161415611624576040805162461bcd60e51b815260206004808301919091526024820152631dd85b9d60e21b604482015290519081900360640190fd5b604080516370a0823160e01b815230600482015290516001600160a01b038416916370a08231916024808301926020929190829003018186803b15801561166a57600080fd5b505afa15801561167e573d6000803e3d6000fd5b505050506040513d602081101561169457600080fd5b50516006549091506116b9906001600160a01b0384811691168363ffffffff61212f16565b919050565b60035481565b60005481565b6005546001600160a01b031681565b600f546001600160a01b031681565b60006117096116f56108b8565b6116fd611aea565b9063ffffffff61203a16565b905090565b6008546001600160a01b03163314611759576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600255565b6006546000906001600160a01b031633146117ae576040805162461bcd60e51b815260206004820152600b60248201526a10b1b7b73a3937b63632b960a91b604482015290519081900360640190fd5b6117b66127a5565b60048054604080516370a0823160e01b81523093810193909352516001600160a01b03909116916370a08231916024808301926020929190829003018186803b15801561180257600080fd5b505afa158015611816573d6000803e3d6000fd5b505050506040513d602081101561182c57600080fd5b50516006546004805460408051636535246160e11b81526001600160a01b03928316938101939093525193945060009392169163ca6a48c291602480820192602092909190829003018186803b15801561188557600080fd5b505afa158015611899573d6000803e3d6000fd5b505050506040513d60208110156118af57600080fd5b505190506001600160a01b0381166118f7576040805162461bcd60e51b8152602060048083019190915260248201526310b530b960e11b604482015290519081900360640190fd5b600454611914906001600160a01b0316828463ffffffff61212f16565b5090565b60015481565b60025481565b6008546001600160a01b0316331461196f576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600680546001600160a01b0319166001600160a01b0392909216919091179055565b600a546001600160a01b031681565b6005546001600160a01b031633146119ed576040805162461bcd60e51b815260206004820152600b60248201526a21676f7665726e616e636560a81b604482015290519081900360640190fd5b600580546001600160a01b0319166001600160a01b0392909216919091179055565b6008546001600160a01b03163314611a5a576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600355565b600e546001600160a01b031681565b6009546001600160a01b031681565b6008546001600160a01b03163314611ac8576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600880546001600160a01b0319166001600160a01b0392909216919091179055565b60048054604080516370a0823160e01b81523093810193909352516000926001600160a01b03909216916370a08231916024808301926020929190829003018186803b15801561088157600080fd5b6006546000906001600160a01b03163314611b89576040805162461bcd60e51b815260206004820152600b60248201526a10b1b7b73a3937b63632b960a91b604482015290519081900360640190fd5b611b9282611fd0565b5060048054604080516370a0823160e01b81523093810193909352516001600160a01b03909116916370a08231916024808301926020929190829003018186803b158015611bdf57600080fd5b505afa158015611bf3573d6000803e3d6000fd5b505050506040513d6020811015611c0957600080fd5b50516006546004805460408051636535246160e11b81526001600160a01b03928316938101939093525193945060009392169163ca6a48c291602480820192602092909190829003018186803b158015611c6257600080fd5b505afa158015611c76573d6000803e3d6000fd5b505050506040513d6020811015611c8c57600080fd5b505190506001600160a01b038116611cd4576040805162461bcd60e51b8152602060048083019190915260248201526310b530b960e11b604482015290519081900360640190fd5b600454611cf1906001600160a01b0316828463ffffffff61212f16565b50919050565b6008546001600160a01b03163314611d42576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600155565b6005546001600160a01b03163314611d94576040805162461bcd60e51b815260206004820152600b60248201526a21676f7665726e616e636560a81b604482015290519081900360640190fd5b600780546001600160a01b0319166001600160a01b0392909216919091179055565b60048054604080516370a0823160e01b81523093810193909352516000926001600160a01b03909216916370a08231916024808301926020929190829003018186803b158015611e0557600080fd5b505afa158015611e19573d6000803e3d6000fd5b505050506040513d6020811015611e2f57600080fd5b505190508015611ee157600a54600454611e5d916001600160a01b039182169116600063ffffffff6124eb16565b600a54600454611e80916001600160a01b0391821691168363ffffffff6124eb16565b600a546040805163534a7e1d60e11b81526004810184905290516001600160a01b039092169163a694fc3a9160248082019260009290919082900301818387803b158015611ecd57600080fd5b505af1158015610db9573d6000803e3d6000fd5b50565b600c546001600160a01b031681565b6008546001600160a01b031681565b600d5481565b6008546001600160a01b03163314611f53576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600d55565b600b546001600160a01b031681565b6010546001600160a01b031681565b6006546001600160a01b031681565b6000611fc783836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506127b0565b90505b92915050565b600a5460408051632e1a7d4d60e01b81526004810184905290516000926001600160a01b031691632e1a7d4d916024808301928692919082900301818387803b15801561201c57600080fd5b505af1158015612030573d6000803e3d6000fd5b5093949350505050565b600082820183811015611fc7576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000826120a357506000611fca565b828202828482816120b057fe5b0414611fc75760405162461bcd60e51b8152600401808060200182810382526021815260200180612b086021913960400191505060405180910390fd5b6000611fc783836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612847565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261157c9084906128ac565b6001600160a01b03821661219457600080fd5b6009546121b5906001600160a01b038581169116600063ffffffff6124eb16565b6009546121d5906001600160a01b0385811691168363ffffffff6124eb16565b60606001600160a01b038416600080516020612ae8833981519152148061221257506001600160a01b038316600080516020612ae8833981519152145b15612298576040805160028082526060820183529091602083019080368337019050509050838160008151811061224557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050828160018151811061227357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050612352565b60408051600380825260808201909252906020820160608036833701905050905083816000815181106122c757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600080516020612ae88339815191528160018151811061230357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050828160028151811061233157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b6009546001600160a01b03166338ed1739836000843061237942603c63ffffffff61203a16565b6040518663ffffffff1660e01b81526004018086815260200185815260200180602001846001600160a01b03166001600160a01b03168152602001838152602001828103825285818151815260200191508051906020019060200280838360005b838110156123f25781810151838201526020016123da565b505050509050019650505050505050600060405180830381600087803b15801561241b57600080fd5b505af115801561242f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561245857600080fd5b810190808051604051939291908464010000000082111561247857600080fd5b90830190602082018581111561248d57600080fd5b82518660208202830111640100000000821117156124aa57600080fd5b82525081516020918201928201910280838360005b838110156124d75781810151838201526020016124bf565b505050509050016040525050505050505050565b801580612571575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b15801561254357600080fd5b505afa158015612557573d6000803e3d6000fd5b505050506040513d602081101561256d57600080fd5b5051155b6125ac5760405162461bcd60e51b8152600401808060200182810382526036815260200180612b536036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261157c9084906128ac565b60048054604080516370a0823160e01b81523093810193909352516000926001600160a01b03909216916370a08231916024808301926020929190829003018186803b15801561264d57600080fd5b505afa158015612661573d6000803e3d6000fd5b505050506040513d602081101561267757600080fd5b505190508015611ee157600654604080516361d027b360e01b8152905161270f926001600160a01b0316916361d027b3916004808301926020929190829003018186803b1580156126c757600080fd5b505afa1580156126db573d6000803e3d6000fd5b505050506040513d60208110156126f157600080fd5b5051600054610da09061271090610ba090869063ffffffff61209416565b60065460408051638d8f1e6760e01b8152905161279d926001600160a01b031691638d8f1e67916004808301926020929190829003018186803b15801561275557600080fd5b505afa158015612769573d6000803e3d6000fd5b505050506040513d602081101561277f57600080fd5b5051600154610da09061271090610ba090869063ffffffff61209416565b611ee1611db6565b611ee1610b686108b8565b6000818484111561283f5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156128045781810151838201526020016127ec565b50505050905090810190601f1680156128315780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600081836128965760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156128045781810151838201526020016127ec565b5060008385816128a257fe5b0495945050505050565b6060612901826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661295d9092919063ffffffff16565b80519091501561157c5780806020019051602081101561292057600080fd5b505161157c5760405162461bcd60e51b815260040180806020018281038252602a815260200180612b29602a913960400191505060405180910390fd5b606061296c8484600085612974565b949350505050565b606061297f85612ae1565b6129d0576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310612a0f5780518252601f1990920191602091820191016129f0565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114612a71576040519150601f19603f3d011682016040523d82523d6000602084013e612a76565b606091505b50915091508115612a8a57915061296c9050565b805115612a9a5780518082602001fd5b60405162461bcd60e51b81526020600482018181528651602484015286518793919283926044019190850190808383600083156128045781810151838201526020016127ec565b3b15159056fe000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a2646970667358221220fe42bf33490725424f09b3f7ab4438219ed0b66274f8408710d93c35f5660a8964736f6c634300060700330000000000000000000000009d074e37d408542fd38be78848e8814afb38db17000000000000000000000000907d9b32654b8d43e8737e0291ad9bfcce01dad60000000000000000000000006847259b2b3a4c17e7c43c54409810af48ba5210000000000000000000000000d92c7faa0ca0e6ae4918f3a83d9832d9caeaa0d3
Deployed Bytecode
0x6080604052600436106102675760003560e01c806388993f2211610144578063c65e3242116100b6578063d925c76e1161007a578063d925c76e146107ba578063dec45641146107cf578063edc9af95146107f9578063f4b9fa751461080e578063f77c479114610823578063fe1f8f7a146104c357610267565b8063c65e32421461071e578063c7b9d53014610748578063d0e30db01461077b578063d21220a714610790578063d33219b4146107a557610267565b8063ab73e43311610108578063ab73e43314610658578063b23ea5bd14610682578063b9e3748914610697578063bdacb303146106ac578063c1a3d44c146106df578063c6223e26146106f457610267565b806388993f22146105b35780638ccdbb70146105c857806392eefe9b146105dd5780639ec5a89414610610578063ab033ea91461062557610267565b80634641257d116101dd57806359739ec4116101a157806359739ec4146105205780635aa6e675146105355780636742426b1461054a578063722713f71461055f5780638237859414610574578063853828b61461059e57610267565b80634641257d146104ae578063479119be146104c35780634fe809cc1461029357806351cff8d9146104d857806351f3d0b81461050b57610267565b80631f1fcd511161022f5780631f1fcd51146103fd5780631fe4a6861461042e578063249fb9b41461044357806326e886c6146102935780632e1a7d4d1461046f5780633fc8cef31461049957610267565b80630547104d1461026c5780630afad5c41461029357806311588086146102a857806317d7de7c146102bd5780631cff79cd14610347575b600080fd5b34801561027857600080fd5b50610281610838565b60408051918252519081900360200190f35b34801561029f57600080fd5b506102816108b2565b3480156102b457600080fd5b506102816108b8565b3480156102c957600080fd5b506102d2610903565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561030c5781810151838201526020016102f4565b50505050905090810190601f1680156103395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102d26004803603604081101561035d57600080fd5b6001600160a01b03823516919081019060408101602082013564010000000081111561038857600080fd5b82018360208201111561039a57600080fd5b803590602001918460018302840111640100000000831117156103bc57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610932945050505050565b34801561040957600080fd5b50610412610a15565b604080516001600160a01b039092168252519081900360200190f35b34801561043a57600080fd5b50610412610a24565b34801561044f57600080fd5b5061046d6004803603602081101561046657600080fd5b5035610a33565b005b34801561047b57600080fd5b5061046d6004803603602081101561049257600080fd5b5035610a83565b3480156104a557600080fd5b50610412610dc0565b3480156104ba57600080fd5b5061046d610dd2565b3480156104cf57600080fd5b50610281611581565b3480156104e457600080fd5b50610281600480360360208110156104fb57600080fd5b50356001600160a01b0316611588565b34801561051757600080fd5b506102816116be565b34801561052c57600080fd5b506102816116c4565b34801561054157600080fd5b506104126116ca565b34801561055657600080fd5b506104126116d9565b34801561056b57600080fd5b506102816116e8565b34801561058057600080fd5b5061046d6004803603602081101561059757600080fd5b503561170e565b3480156105aa57600080fd5b5061028161175e565b3480156105bf57600080fd5b50610281611918565b3480156105d457600080fd5b5061028161191e565b3480156105e957600080fd5b5061046d6004803603602081101561060057600080fd5b50356001600160a01b0316611924565b34801561061c57600080fd5b50610412611991565b34801561063157600080fd5b5061046d6004803603602081101561064857600080fd5b50356001600160a01b03166119a0565b34801561066457600080fd5b5061046d6004803603602081101561067b57600080fd5b5035611a0f565b34801561068e57600080fd5b50610412611a5f565b3480156106a357600080fd5b50610412611a6e565b3480156106b857600080fd5b5061046d600480360360208110156106cf57600080fd5b50356001600160a01b0316611a7d565b3480156106eb57600080fd5b50610281611aea565b34801561070057600080fd5b506102816004803603602081101561071757600080fd5b5035611b39565b34801561072a57600080fd5b5061046d6004803603602081101561074157600080fd5b5035611cf7565b34801561075457600080fd5b5061046d6004803603602081101561076b57600080fd5b50356001600160a01b0316611d47565b34801561078757600080fd5b5061046d611db6565b34801561079c57600080fd5b50610412611ee4565b3480156107b157600080fd5b50610412611ef3565b3480156107c657600080fd5b50610281611f02565b3480156107db57600080fd5b5061046d600480360360208110156107f257600080fd5b5035611f08565b34801561080557600080fd5b50610412611f58565b34801561081a57600080fd5b50610412611f67565b34801561082f57600080fd5b50610412611f76565b600a54604080516246613160e11b815230600482015290516000926001600160a01b031691628cc262916024808301926020929190829003018186803b15801561088157600080fd5b505afa158015610895573d6000803e3d6000fd5b505050506040513d60208110156108ab57600080fd5b5051905090565b61271081565b600a54604080516370a0823160e01b815230600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b15801561088157600080fd5b60408051808201909152601581527414dd1c985d1959de555b9a515d1a11185a531c158d605a1b602082015290565b6008546060906001600160a01b03163314610980576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b6001600160a01b0383166109c5576040805162461bcd60e51b8152602060048201526007602482015266085d185c99d95d60ca1b604482015290519081900360640190fd5b600080835160208501866113885a03f43d6040519250601f19601f6020830101168301604052808352806000602085013e811560018114610a0557610a0c565b8160208501fd5b50505092915050565b6004546001600160a01b031681565b6007546001600160a01b031681565b6008546001600160a01b03163314610a7e576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600055565b6006546001600160a01b03163314610ad0576040805162461bcd60e51b815260206004820152600b60248201526a10b1b7b73a3937b63632b960a91b604482015290519081900360640190fd5b60048054604080516370a0823160e01b81523093810193909352516000926001600160a01b03909216916370a08231916024808301926020929190829003018186803b158015610b1f57600080fd5b505afa158015610b33573d6000803e3d6000fd5b505050506040513d6020811015610b4957600080fd5b5051905081811015610b8257610b6d610b68838363ffffffff611f8516565b611fd0565b9150610b7f828263ffffffff61203a16565b91505b6000610bac620186a0610ba06003548661209490919063ffffffff16565b9063ffffffff6120ed16565b9050610c44600660009054906101000a90046001600160a01b03166001600160a01b0316638d8f1e676040518163ffffffff1660e01b815260040160206040518083038186803b158015610bff57600080fd5b505afa158015610c13573d6000803e3d6000fd5b505050506040513d6020811015610c2957600080fd5b50516004546001600160a01b0316908363ffffffff61212f16565b6000610c62620186a0610ba06002548761209490919063ffffffff16565b9050610cb5600660009054906101000a90046001600160a01b03166001600160a01b03166361d027b36040518163ffffffff1660e01b815260040160206040518083038186803b158015610bff57600080fd5b6006546004805460408051636535246160e11b81526001600160a01b039283169381019390935251600093919091169163ca6a48c2916024808301926020929190829003018186803b158015610d0a57600080fd5b505afa158015610d1e573d6000803e3d6000fd5b505050506040513d6020811015610d3457600080fd5b505190506001600160a01b038116610d7c576040805162461bcd60e51b8152602060048083019190915260248201526310b530b960e11b604482015290519081900360640190fd5b610db981610da084610d94898863ffffffff611f8516565b9063ffffffff611f8516565b6004546001600160a01b0316919063ffffffff61212f16565b5050505050565b600080516020612ae883398151915281565b33321480610dea57506005546001600160a01b031633145b80610dff57506007546001600160a01b031633145b610e0857600080fd5b600a60009054906101000a90046001600160a01b03166001600160a01b0316633d18b9126040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e5857600080fd5b505af1158015610e6c573d6000803e3d6000fd5b5050600b54604080516370a0823160e01b81523060048201529051600094506001600160a01b0390921692506370a08231916024808301926020929190829003018186803b158015610ebd57600080fd5b505afa158015610ed1573d6000803e3d6000fd5b505050506040513d6020811015610ee757600080fd5b505190508015610fdc576000610f0e612710610ba0600d548561209490919063ffffffff16565b9050610fa6600660009054906101000a90046001600160a01b03166001600160a01b03166361d027b36040518163ffffffff1660e01b815260040160206040518083038186803b158015610f6157600080fd5b505afa158015610f75573d6000803e3d6000fd5b505050506040513d6020811015610f8b57600080fd5b5051600b546001600160a01b0316908363ffffffff61212f16565b600b54610fda906001600160a01b0316600080516020612ae8833981519152610fd5858563ffffffff611f8516565b612181565b505b604080516370a0823160e01b81523060048201529051600091600080516020612ae8833981519152916370a0823191602480820192602092909190829003018186803b15801561102b57600080fd5b505afa15801561103f573d6000803e3d6000fd5b505050506040513d602081101561105557600080fd5b50519050801561109057600c5461109090600080516020612ae8833981519152906001600160a01b0316610fd584600263ffffffff6120ed16565b604080516370a0823160e01b81523060048201529051600080516020612ae8833981519152916370a08231916024808301926020929190829003018186803b1580156110db57600080fd5b505afa1580156110ef573d6000803e3d6000fd5b505050506040513d602081101561110557600080fd5b5051600c54604080516370a0823160e01b815230600482015290519293506000926001600160a01b03909216916370a0823191602480820192602092909190829003018186803b15801561115857600080fd5b505afa15801561116c573d6000803e3d6000fd5b505050506040513d602081101561118257600080fd5b5051905081158015906111955750600081115b15611574576009546111c790600080516020612ae8833981519152906001600160a01b0316600063ffffffff6124eb16565b6009546111f390600080516020612ae8833981519152906001600160a01b03168463ffffffff6124eb16565b600954600c54611217916001600160a01b039182169116600063ffffffff6124eb16565b600954600c5461123a916001600160a01b0391821691168363ffffffff6124eb16565b600954600c546040805162e8e33760e81b8152600080516020612ae883398151915260048201526001600160a01b039283166024820152604481018690526064810185905260006084820181905260a482018190523060c4830152603c420160e48301529151929093169263e8e33700926101048083019360609383900390910190829087803b1580156112cd57600080fd5b505af11580156112e1573d6000803e3d6000fd5b505050506040513d60608110156112f757600080fd5b5050600654604080516361d027b360e01b81529051600080516020612ae88339815191529263a9059cbb926001600160a01b03909116916361d027b391600480820192602092909190829003018186803b15801561135457600080fd5b505afa158015611368573d6000803e3d6000fd5b505050506040513d602081101561137e57600080fd5b5051604080516370a0823160e01b81523060048201529051600080516020612ae8833981519152916370a08231916024808301926020929190829003018186803b1580156113cb57600080fd5b505afa1580156113df573d6000803e3d6000fd5b505050506040513d60208110156113f557600080fd5b5051604080516001600160e01b031960e086901b1681526001600160a01b03909316600484015260248301919091525160448083019260209291908290030181600087803b15801561144657600080fd5b505af115801561145a573d6000803e3d6000fd5b505050506040513d602081101561147057600080fd5b5050600654604080516361d027b360e01b81529051611574926001600160a01b0316916361d027b3916004808301926020929190829003018186803b1580156114b857600080fd5b505afa1580156114cc573d6000803e3d6000fd5b505050506040513d60208110156114e257600080fd5b5051600c54604080516370a0823160e01b815230600482015290516001600160a01b03909216916370a0823191602480820192602092909190829003018186803b15801561152f57600080fd5b505afa158015611543573d6000803e3d6000fd5b505050506040513d602081101561155957600080fd5b5051600c546001600160a01b0316919063ffffffff61212f16565b61157c6125fe565b505050565b620186a081565b6006546000906001600160a01b031633146115d8576040805162461bcd60e51b815260206004820152600b60248201526a10b1b7b73a3937b63632b960a91b604482015290519081900360640190fd5b6004546001600160a01b0383811691161415611624576040805162461bcd60e51b815260206004808301919091526024820152631dd85b9d60e21b604482015290519081900360640190fd5b604080516370a0823160e01b815230600482015290516001600160a01b038416916370a08231916024808301926020929190829003018186803b15801561166a57600080fd5b505afa15801561167e573d6000803e3d6000fd5b505050506040513d602081101561169457600080fd5b50516006549091506116b9906001600160a01b0384811691168363ffffffff61212f16565b919050565b60035481565b60005481565b6005546001600160a01b031681565b600f546001600160a01b031681565b60006117096116f56108b8565b6116fd611aea565b9063ffffffff61203a16565b905090565b6008546001600160a01b03163314611759576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600255565b6006546000906001600160a01b031633146117ae576040805162461bcd60e51b815260206004820152600b60248201526a10b1b7b73a3937b63632b960a91b604482015290519081900360640190fd5b6117b66127a5565b60048054604080516370a0823160e01b81523093810193909352516001600160a01b03909116916370a08231916024808301926020929190829003018186803b15801561180257600080fd5b505afa158015611816573d6000803e3d6000fd5b505050506040513d602081101561182c57600080fd5b50516006546004805460408051636535246160e11b81526001600160a01b03928316938101939093525193945060009392169163ca6a48c291602480820192602092909190829003018186803b15801561188557600080fd5b505afa158015611899573d6000803e3d6000fd5b505050506040513d60208110156118af57600080fd5b505190506001600160a01b0381166118f7576040805162461bcd60e51b8152602060048083019190915260248201526310b530b960e11b604482015290519081900360640190fd5b600454611914906001600160a01b0316828463ffffffff61212f16565b5090565b60015481565b60025481565b6008546001600160a01b0316331461196f576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600680546001600160a01b0319166001600160a01b0392909216919091179055565b600a546001600160a01b031681565b6005546001600160a01b031633146119ed576040805162461bcd60e51b815260206004820152600b60248201526a21676f7665726e616e636560a81b604482015290519081900360640190fd5b600580546001600160a01b0319166001600160a01b0392909216919091179055565b6008546001600160a01b03163314611a5a576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600355565b600e546001600160a01b031681565b6009546001600160a01b031681565b6008546001600160a01b03163314611ac8576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600880546001600160a01b0319166001600160a01b0392909216919091179055565b60048054604080516370a0823160e01b81523093810193909352516000926001600160a01b03909216916370a08231916024808301926020929190829003018186803b15801561088157600080fd5b6006546000906001600160a01b03163314611b89576040805162461bcd60e51b815260206004820152600b60248201526a10b1b7b73a3937b63632b960a91b604482015290519081900360640190fd5b611b9282611fd0565b5060048054604080516370a0823160e01b81523093810193909352516001600160a01b03909116916370a08231916024808301926020929190829003018186803b158015611bdf57600080fd5b505afa158015611bf3573d6000803e3d6000fd5b505050506040513d6020811015611c0957600080fd5b50516006546004805460408051636535246160e11b81526001600160a01b03928316938101939093525193945060009392169163ca6a48c291602480820192602092909190829003018186803b158015611c6257600080fd5b505afa158015611c76573d6000803e3d6000fd5b505050506040513d6020811015611c8c57600080fd5b505190506001600160a01b038116611cd4576040805162461bcd60e51b8152602060048083019190915260248201526310b530b960e11b604482015290519081900360640190fd5b600454611cf1906001600160a01b0316828463ffffffff61212f16565b50919050565b6008546001600160a01b03163314611d42576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600155565b6005546001600160a01b03163314611d94576040805162461bcd60e51b815260206004820152600b60248201526a21676f7665726e616e636560a81b604482015290519081900360640190fd5b600780546001600160a01b0319166001600160a01b0392909216919091179055565b60048054604080516370a0823160e01b81523093810193909352516000926001600160a01b03909216916370a08231916024808301926020929190829003018186803b158015611e0557600080fd5b505afa158015611e19573d6000803e3d6000fd5b505050506040513d6020811015611e2f57600080fd5b505190508015611ee157600a54600454611e5d916001600160a01b039182169116600063ffffffff6124eb16565b600a54600454611e80916001600160a01b0391821691168363ffffffff6124eb16565b600a546040805163534a7e1d60e11b81526004810184905290516001600160a01b039092169163a694fc3a9160248082019260009290919082900301818387803b158015611ecd57600080fd5b505af1158015610db9573d6000803e3d6000fd5b50565b600c546001600160a01b031681565b6008546001600160a01b031681565b600d5481565b6008546001600160a01b03163314611f53576040805162461bcd60e51b81526020600482015260096024820152682174696d656c6f636b60b81b604482015290519081900360640190fd5b600d55565b600b546001600160a01b031681565b6010546001600160a01b031681565b6006546001600160a01b031681565b6000611fc783836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506127b0565b90505b92915050565b600a5460408051632e1a7d4d60e01b81526004810184905290516000926001600160a01b031691632e1a7d4d916024808301928692919082900301818387803b15801561201c57600080fd5b505af1158015612030573d6000803e3d6000fd5b5093949350505050565b600082820183811015611fc7576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000826120a357506000611fca565b828202828482816120b057fe5b0414611fc75760405162461bcd60e51b8152600401808060200182810382526021815260200180612b086021913960400191505060405180910390fd5b6000611fc783836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612847565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261157c9084906128ac565b6001600160a01b03821661219457600080fd5b6009546121b5906001600160a01b038581169116600063ffffffff6124eb16565b6009546121d5906001600160a01b0385811691168363ffffffff6124eb16565b60606001600160a01b038416600080516020612ae8833981519152148061221257506001600160a01b038316600080516020612ae8833981519152145b15612298576040805160028082526060820183529091602083019080368337019050509050838160008151811061224557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050828160018151811061227357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050612352565b60408051600380825260808201909252906020820160608036833701905050905083816000815181106122c757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050600080516020612ae88339815191528160018151811061230357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050828160028151811061233157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b6009546001600160a01b03166338ed1739836000843061237942603c63ffffffff61203a16565b6040518663ffffffff1660e01b81526004018086815260200185815260200180602001846001600160a01b03166001600160a01b03168152602001838152602001828103825285818151815260200191508051906020019060200280838360005b838110156123f25781810151838201526020016123da565b505050509050019650505050505050600060405180830381600087803b15801561241b57600080fd5b505af115801561242f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561245857600080fd5b810190808051604051939291908464010000000082111561247857600080fd5b90830190602082018581111561248d57600080fd5b82518660208202830111640100000000821117156124aa57600080fd5b82525081516020918201928201910280838360005b838110156124d75781810151838201526020016124bf565b505050509050016040525050505050505050565b801580612571575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b15801561254357600080fd5b505afa158015612557573d6000803e3d6000fd5b505050506040513d602081101561256d57600080fd5b5051155b6125ac5760405162461bcd60e51b8152600401808060200182810382526036815260200180612b536036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261157c9084906128ac565b60048054604080516370a0823160e01b81523093810193909352516000926001600160a01b03909216916370a08231916024808301926020929190829003018186803b15801561264d57600080fd5b505afa158015612661573d6000803e3d6000fd5b505050506040513d602081101561267757600080fd5b505190508015611ee157600654604080516361d027b360e01b8152905161270f926001600160a01b0316916361d027b3916004808301926020929190829003018186803b1580156126c757600080fd5b505afa1580156126db573d6000803e3d6000fd5b505050506040513d60208110156126f157600080fd5b5051600054610da09061271090610ba090869063ffffffff61209416565b60065460408051638d8f1e6760e01b8152905161279d926001600160a01b031691638d8f1e67916004808301926020929190829003018186803b15801561275557600080fd5b505afa158015612769573d6000803e3d6000fd5b505050506040513d602081101561277f57600080fd5b5051600154610da09061271090610ba090869063ffffffff61209416565b611ee1611db6565b611ee1610b686108b8565b6000818484111561283f5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156128045781810151838201526020016127ec565b50505050905090810190601f1680156128315780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600081836128965760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156128045781810151838201526020016127ec565b5060008385816128a257fe5b0495945050505050565b6060612901826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661295d9092919063ffffffff16565b80519091501561157c5780806020019051602081101561292057600080fd5b505161157c5760405162461bcd60e51b815260040180806020018281038252602a815260200180612b29602a913960400191505060405180910390fd5b606061296c8484600085612974565b949350505050565b606061297f85612ae1565b6129d0576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b60208310612a0f5780518252601f1990920191602091820191016129f0565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114612a71576040519150601f19603f3d011682016040523d82523d6000602084013e612a76565b606091505b50915091508115612a8a57915061296c9050565b805115612a9a5780518082602001fd5b60405162461bcd60e51b81526020600482018181528651602484015286518793919283926044019190850190808383600083156128045781810151838201526020016127ec565b3b15159056fe000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a2646970667358221220fe42bf33490725424f09b3f7ab4438219ed0b66274f8408710d93c35f5660a8964736f6c63430006070033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000009d074e37d408542fd38be78848e8814afb38db17000000000000000000000000907d9b32654b8d43e8737e0291ad9bfcce01dad60000000000000000000000006847259b2b3a4c17e7c43c54409810af48ba5210000000000000000000000000d92c7faa0ca0e6ae4918f3a83d9832d9caeaa0d3
-----Decoded View---------------
Arg [0] : _governance (address): 0x9d074E37d408542FD38be78848e8814AFB38db17
Arg [1] : _strategist (address): 0x907D9B32654B8D43e8737E0291Ad9bfcce01DAD6
Arg [2] : _controller (address): 0x6847259b2B3A4c17e7c43C54409810aF48bA5210
Arg [3] : _timelock (address): 0xD92c7fAa0Ca0e6AE4918f3a83d9832d9CAEAA0d3
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000009d074e37d408542fd38be78848e8814afb38db17
Arg [1] : 000000000000000000000000907d9b32654b8d43e8737e0291ad9bfcce01dad6
Arg [2] : 0000000000000000000000006847259b2b3a4c17e7c43c54409810af48ba5210
Arg [3] : 000000000000000000000000d92c7faa0ca0e6ae4918f3a83d9832d9caeaa0d3
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.