Source Code
Latest 25 from a total of 69 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Auto Join Smart ... | 382811843 | 127 days ago | IN | 0 ETH | 0.00000315 | ||||
| Auto Exit Smart ... | 368626885 | 168 days ago | IN | 0 ETH | 0.00000357 | ||||
| Auto Join Smart ... | 368626514 | 168 days ago | IN | 0 ETH | 0.00000398 | ||||
| Auto Join Smart ... | 368617644 | 168 days ago | IN | 0 ETH | 0.00000402 | ||||
| Exit Pool | 368267650 | 169 days ago | IN | 0 ETH | 0.00000358 | ||||
| Auto Join Smart ... | 368257845 | 170 days ago | IN | 0 ETH | 0.00000693 | ||||
| Create Smart Poo... | 367922885 | 170 days ago | IN | 0 ETH | 0.00013084 | ||||
| Exit Pool | 353396227 | 213 days ago | IN | 0 ETH | 0.00000298 | ||||
| Exit Pool | 342618970 | 244 days ago | IN | 0 ETH | 0.00000483 | ||||
| Auto Exit Smart ... | 335297367 | 265 days ago | IN | 0 ETH | 0.0000153 | ||||
| Exit Pool | 334084929 | 269 days ago | IN | 0 ETH | 0.00000237 | ||||
| Auto Join Smart ... | 334049813 | 269 days ago | IN | 0 ETH | 0.00000338 | ||||
| Exit Pool | 334049005 | 269 days ago | IN | 0 ETH | 0.00000241 | ||||
| Auto Join Smart ... | 334043740 | 269 days ago | IN | 0 ETH | 0.00000318 | ||||
| Exit Pool | 333924993 | 269 days ago | IN | 0 ETH | 0.00000234 | ||||
| Auto Join Smart ... | 333923260 | 269 days ago | IN | 0 ETH | 0.0000033 | ||||
| Auto Join Smart ... | 333202420 | 271 days ago | IN | 0 ETH | 0.00000355 | ||||
| Auto Exit Smart ... | 332672728 | 273 days ago | IN | 0 ETH | 0.00000243 | ||||
| Auto Join Smart ... | 331669492 | 276 days ago | IN | 0 ETH | 0.00000344 | ||||
| Auto Join Smart ... | 331393473 | 276 days ago | IN | 0 ETH | 0.00000391 | ||||
| Create Smart Poo... | 329376954 | 282 days ago | IN | 0 ETH | 0.00013936 | ||||
| Auto Exit Smart ... | 316949435 | 318 days ago | IN | 0 ETH | 0.00000251 | ||||
| Create Smart Poo... | 314881893 | 324 days ago | IN | 0 ETH | 0.00036297 | ||||
| Exit Pool | 314830717 | 325 days ago | IN | 0 ETH | 0.00000219 | ||||
| Create Smart Poo... | 314546898 | 325 days ago | IN | 0 ETH | 0.00013072 |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Actions
Compiler Version
v0.6.12+commit.27d51765
Optimization Enabled:
Yes with 20 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import {RightsManager} from "../libraries/RightsManager.sol";
import {SmartPoolManager} from "../libraries/SmartPoolManager.sol";
import {DesynSafeMath} from "../libraries/DesynSafeMath.sol";
import "../libraries/SafeERC20.sol";
import "../interfaces/IWETH.sol";
import "../utils/DesynReentrancyGuard.sol";
abstract contract IAggregator {
enum SwapType {
UNISWAPV2,
UNISWAPV3,
ONEINCH,
CURVE
}
struct SwapInfoBase {
address aggregator; // the swap router to use
address rebalanceAdapter;
SwapType swapType;
}
struct SwapData {
uint256 quantity;
bytes data; // v3: (uint,uint256[]) v2: (uint256,address[])
}
function swapExactTokensForTokens(uint256, uint256, address[] calldata, address, uint256) external virtual;
function uniswapV3Swap(uint256, uint256, uint256[] calldata) external virtual;
}
abstract contract DesynOwnable {
function setController(address controller) external virtual;
function setManagersInfo(address[] memory _owners, uint[] memory _ownerPercentage) external virtual;
}
abstract contract AbstractPool is IERC20, DesynOwnable {
function setPublicSwap(bool public_) external virtual;
function joinPool(
uint poolAmountOut,
uint[] calldata maxAmountsIn,
address kol,
address user
) external virtual;
function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut, address user) external virtual;
}
abstract contract LiquidityPoolActions is AbstractPool {
function finalize() external virtual;
function bind(
address token,
uint balance,
uint denorm
) external virtual;
function rebind(
address token,
uint balance,
uint denorm
) external virtual;
function unbind(address token) external virtual;
function isBound(address t) external view virtual returns (bool);
function getCurrentTokens() external view virtual returns (address[] memory);
function getFinalTokens() external view virtual returns (address[] memory);
function getBalance(address token) external view virtual returns (uint);
}
abstract contract FactoryActions {
function newLiquidityPool() external virtual returns (LiquidityPoolActions);
function getModuleStatus(address etf, address module) external view virtual returns (bool);
function isTokenWhitelistedForVerify(address token) external view virtual returns (bool);
}
abstract contract RebalaceAdapter {
enum SwapType {
UNISWAPV2,
UNISWAPV3,
ONEINCH
}
struct RebalanceInfo {
address etf; // etf address
address token0;
address token1;
address aggregator; // the swap router to use
SwapType swapType;
uint256 quantity;
bytes data; // v3: (uint,uint256[]) v2: (uint256,address[])
}
function rebalance(RebalanceInfo calldata rebalanceInfo) external virtual;
function approve(address etf, address token, address spender, uint256 amount) external virtual;
function isRouterApproved(address router) external virtual returns (bool);
}
abstract contract IConfigurableRightsPool is AbstractPool {
struct PoolParams {
string poolTokenSymbol;
string poolTokenName;
address[] constituentTokens;
uint[] tokenBalances;
uint[] tokenWeights;
uint managerFee;
uint redeemFee;
uint issueFee;
uint perfermanceFee;
SmartPoolManager.Etypes etype;
}
struct CrpParams {
uint initialSupply;
uint collectPeriod;
SmartPoolManager.Period period;
}
function createPool(
address creator,
uint initialSupply,
uint collectPeriod,
SmartPoolManager.Period period,
SmartPoolManager.PoolTokenRange memory tokenRange
) external virtual;
function createPool(uint initialSupply) external virtual;
function setCap(uint newCap) external virtual;
function commitAddToken(
address token,
uint balance,
uint denormalizedWeight
) external virtual;
function applyAddToken() external virtual;
function whitelistLiquidityProvider(address provider) external virtual;
function removeWhitelistedLiquidityProvider(address provider) external virtual;
function bPool() external view virtual returns (LiquidityPoolActions);
function addTokenToWhitelist(uint[] memory sort, address[] memory token) external virtual;
function claimManagerFee() external virtual;
function etype() external virtual returns(SmartPoolManager.Etypes);
function vaultAddress() external virtual view returns(address);
function snapshotBeginAssets() external virtual;
function snapshotEndAssets() external virtual;
function etfStatus() external virtual returns(SmartPoolManager.Status memory);
}
abstract contract ICRPFactory {
function newCrp(
address factoryAddress,
IConfigurableRightsPool.PoolParams calldata params,
RightsManager.Rights calldata rights,
SmartPoolManager.KolPoolParams calldata kolPoolParams,
address[] memory owners,
uint[] memory ownerPercentage
) external virtual returns (IConfigurableRightsPool);
}
abstract contract IVault {
function userVault() external virtual returns(address);
}
abstract contract IUserVault {
function kolClaim(address pool) external virtual;
function managerClaim(address pool) external virtual;
function getManagerClaimBool(address pool) external view virtual returns(bool);
}
interface ICurve {
function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) external;
function coins(uint i) external view returns(address);
}
/********************************** WARNING **********************************/
// //
// This contract is only meant to be used in conjunction with ds-proxy. //
// Calling this contract directly will lead to loss of funds. //
// //
/********************************** WARNING **********************************/
contract Actions is DesynReentrancyGuard {
using SafeERC20 for IERC20;
using DesynSafeMath for uint256;
IERC20 public constant STBT = IERC20(0x530824DA86689C9C17CdC2871Ff29B058345b44a);
address public constant FACTORY = 0xdE6b117384452b21F5a643E56952593B88110e78;
address constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address constant WETH = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
event SmartJoinPool(address caller, address token, uint amount, address shareReturn, uint amountReturn);
event SmartExitPool(address caller, address share, uint shareAmount, address tokenReturn, uint amountReturn);
// --- Pool Creation ---
function create(
FactoryActions factory,
address[] calldata tokens,
uint[] calldata balances,
uint[] calldata weights,
bool finalize
) external returns (LiquidityPoolActions pool) {
require(tokens.length == balances.length, "ERR_LENGTH_MISMATCH");
require(tokens.length == weights.length, "ERR_LENGTH_MISMATCH");
pool = factory.newLiquidityPool();
for (uint i = 0; i < tokens.length; i++) {
IERC20 token = IERC20(tokens[i]);
token.safeTransferFrom(msg.sender, address(this), balances[i]);
token.safeApprove(address(pool), 0);
token.safeApprove(address(pool), balances[i]);
pool.bind(tokens[i], balances[i], weights[i]);
}
if (finalize) {
pool.finalize();
require(pool.transfer(msg.sender, pool.balanceOf(address(this))), "ERR_TRANSFER_FAILED");
} else {
pool.setPublicSwap(true);
}
}
function createSmartPool(
ICRPFactory factory,
FactoryActions coreFactory,
IConfigurableRightsPool.PoolParams calldata poolParams,
IConfigurableRightsPool.CrpParams calldata crpParams,
RightsManager.Rights calldata rights,
SmartPoolManager.KolPoolParams calldata kolPoolParams,
address[] memory owners,
uint[] memory ownerPercentage,
SmartPoolManager.PoolTokenRange memory tokenRange
) external returns (IConfigurableRightsPool crp) {
require(poolParams.constituentTokens.length == poolParams.tokenBalances.length, "ERR_LENGTH_MISMATCH");
require(poolParams.constituentTokens.length == poolParams.tokenWeights.length, "ERR_LENGTH_MISMATCH");
crp = factory.newCrp(address(coreFactory), poolParams, rights, kolPoolParams, owners, ownerPercentage);
for (uint i = 0; i < poolParams.constituentTokens.length; i++) {
IERC20 token = IERC20(poolParams.constituentTokens[i]);
token.safeTransferFrom(msg.sender, address(this), poolParams.tokenBalances[i]);
token.safeApprove(address(crp), 0);
token.safeApprove(address(crp), poolParams.tokenBalances[i]);
}
crp.createPool(msg.sender,crpParams.initialSupply, crpParams.collectPeriod, crpParams.period, tokenRange);
require(crp.transfer(msg.sender, crpParams.initialSupply), "ERR_TRANSFER_FAILED");
crp.setController(msg.sender);
// DSProxy instance keeps pool ownership to enable management
}
// --- Joins ---
function joinPool(
LiquidityPoolActions pool,
uint poolAmountOut,
uint[] calldata maxAmountsIn
) external {
address[] memory tokens = pool.getFinalTokens();
_join(pool, tokens, poolAmountOut, maxAmountsIn, msg.sender);
}
function autoExitSmartPool(
IConfigurableRightsPool pool,
uint poolAmountIn,
uint[] memory minAmountsOut,
uint minSwapReturn,
address handleToken,
IAggregator.SwapInfoBase calldata swapBase,
IAggregator.SwapData[] memory swapDatas) external payable lock {
address[] memory tokens = pool.bPool().getCurrentTokens();
uint len = swapDatas.length;
require(FactoryActions(FACTORY).isTokenWhitelistedForVerify(handleToken),"ERR_TOKEN_NOT_WHITELISTED");
require(tokens.length == swapDatas.length,"ERR_TOKENLENGTH_MISMATCH");
require(tokens.length == minAmountsOut.length,"ERR_TOKENLENGTH_MISMATCH");
IERC20 receiveToken = IERC20(handleToken);
if (handleToken == NATIVE_TOKEN) {
receiveToken = IERC20(WETH);
}
uint preReceiveAmount = receiveToken.balanceOf(address(this));
uint[] memory initialAmounts = new uint[](len);
for(uint i = 0; i < len; i++){
initialAmounts[i] = IERC20(tokens[i]).balanceOf(address(this));
}
_exit(pool, poolAmountIn, minAmountsOut, tokens, true);
for(uint j = 0; j < len; j++) {
IAggregator.SwapData memory swapData = swapDatas[j];
IERC20 swapToken = IERC20(tokens[j]);
if(tokens[j] != address(receiveToken)){
swapData.quantity = SafeMath.sub(swapToken.balanceOf(address(this)), initialAmounts[j]);
swapToken.safeApprove(swapBase.aggregator, 0);
swapToken.safeApprove(swapBase.aggregator, swapData.quantity);
_makeSwap(swapBase, swapData, receiveToken);
}
}
uint receiveAmount = SafeMath.sub(receiveToken.balanceOf(address(this)), preReceiveAmount);
require(minSwapReturn <= receiveAmount,"ERR_RECEIVE_AMOUNT_TO_SMALL");
if (handleToken == NATIVE_TOKEN) {
IWETH(WETH).withdraw(receiveAmount);
msg.sender.transfer(receiveAmount);
} else {
receiveToken.safeTransfer(msg.sender, receiveAmount);
}
emit SmartExitPool(msg.sender, address(pool) ,poolAmountIn, handleToken, receiveAmount);
}
function joinSmartPool(
IConfigurableRightsPool pool,
uint poolAmountOut,
uint[] calldata maxAmountsIn,
address kol
) external {
address[] memory tokens = pool.bPool().getCurrentTokens();
_join(pool, tokens, poolAmountOut, maxAmountsIn, kol);
}
struct JoinVar {
uint actualShareAmountOut;
uint issueFee;
uint totalPoolShares;
LiquidityPoolActions bPool;
}
function autoJoinSmartPool(
IConfigurableRightsPool pool,
address kol,
uint issueAmount,
uint minPoolAmountOut,
address handleToken,
IAggregator.SwapInfoBase calldata swapBase,
IAggregator.SwapData[] memory swapDatas
) external payable lock {
require(FactoryActions(FACTORY).isTokenWhitelistedForVerify(handleToken), "ERR_TOKEN_NOT_WHITELISTED");
address[] memory poolTokens = pool.bPool().getCurrentTokens();
require(poolTokens.length == swapDatas.length,"ERR_TOKENLENGTH_MISMATCH");
uint[] memory maxAmountsIn = new uint[](poolTokens.length);
address user = msg.sender;
// Transfer ETH into the contract and authorize the aggregator to operate
IERC20 issueToken;
if (handleToken == NATIVE_TOKEN) {
require(msg.value > 0 && msg.value == issueAmount, 'ERROR_ETH');
IWETH(WETH).deposit{value: msg.value}();
issueToken = IERC20(WETH);
} else {
issueToken = IERC20(handleToken);
issueToken.safeTransferFrom(user, address(this), issueAmount);
}
issueToken.safeApprove(swapBase.aggregator, 0);
issueToken.safeApprove(swapBase.aggregator, issueAmount);
JoinVar memory joinVar = JoinVar({
actualShareAmountOut: type(uint).max,
issueFee:pool.etfStatus().issueFee,
totalPoolShares:pool.totalSupply(),
bPool:pool.bPool()
});
// Perform a swap and authorize the pool call
for (uint i; i < poolTokens.length; i++) {
IAggregator.SwapData memory swapData = swapDatas[i];
poolTokens[i] == address(issueToken)
? maxAmountsIn[i] = swapData.quantity
: maxAmountsIn[i] = _makeSwap(swapBase, swapData, IERC20(poolTokens[i]));
uint shareAmountOutPerToken = _calculateShare(joinVar.bPool,joinVar.totalPoolShares,joinVar.issueFee, poolTokens[i], maxAmountsIn[i]);
if(shareAmountOutPerToken < joinVar.actualShareAmountOut) joinVar.actualShareAmountOut = shareAmountOutPerToken;
IERC20(poolTokens[i]).safeApprove(address(pool), 0);
IERC20(poolTokens[i]).safeApprove(address(pool), maxAmountsIn[i]);
}
// Adding Tokens to a Pool
require(minPoolAmountOut <= joinVar.actualShareAmountOut,"ERR_SHARE_AMOUNT_TO_SMALL");
pool.joinPool(joinVar.actualShareAmountOut, maxAmountsIn, kol, user);
// Return excess funds to users
for (uint i; i < poolTokens.length; i++) {
IERC20 token = IERC20(poolTokens[i]);
if (token.balanceOf(address(this)) > 0 && token != STBT) token.safeTransfer(user, token.balanceOf(address(this)));
}
issueToken.safeTransfer(user, issueToken.balanceOf(address(this)));
require(pool.transfer(user, pool.balanceOf(address(this))), "ERR_TRANSFER_FAILED");
emit SmartJoinPool(msg.sender, handleToken, issueAmount, address(pool), joinVar.actualShareAmountOut);
}
function exitPool(
IConfigurableRightsPool pool,
uint poolAmountIn,
uint[] memory minAmountsOut
) external {
address[] memory tokens = pool.bPool().getCurrentTokens();
_exit(pool, poolAmountIn, minAmountsOut, tokens, false);
}
// --- Pool management (common) ---
function setPublicSwap(AbstractPool pool, bool publicSwap) external {
pool.setPublicSwap(publicSwap);
}
function setController(AbstractPool pool, address newController) external {
_beforeOwnerChange(address(pool));
pool.setController(newController);
}
function setManagersInfo(AbstractPool pool ,address[] memory _owners, uint[] memory _ownerPercentage) public {
_beforeOwnerChange(address(pool));
pool.setManagersInfo(_owners, _ownerPercentage);
}
function _beforeOwnerChange(address pool) internal {
claimManagementFee(IConfigurableRightsPool(pool));
_claimManagersReward(pool);
}
function snapshotBeginAssets(IConfigurableRightsPool pool) external virtual {
pool.snapshotBeginAssets();
}
function snapshotEndAssets(IConfigurableRightsPool pool) external virtual {
pool.snapshotEndAssets();
}
function approveUnderlying(RebalaceAdapter rebalanceAdapter, address etf, address token, address spender, uint amount) external {
rebalanceAdapter.approve(etf, token, spender, amount);
}
function rebalance(RebalaceAdapter rebalanceAdapter, RebalaceAdapter.RebalanceInfo calldata rebalanceInfo) external {
rebalanceAdapter.rebalance(rebalanceInfo);
}
// --- Private pool management ---
function finalize(LiquidityPoolActions pool) external {
pool.finalize();
require(pool.transfer(msg.sender, pool.balanceOf(address(this))), "ERR_TRANSFER_FAILED");
}
// --- Smart pool management ---
function setCap(IConfigurableRightsPool crp, uint newCap) external {
crp.setCap(newCap);
}
function whitelistLiquidityProvider(IConfigurableRightsPool crp, address provider) external {
crp.whitelistLiquidityProvider(provider);
}
function removeWhitelistedLiquidityProvider(IConfigurableRightsPool crp, address provider) external {
crp.removeWhitelistedLiquidityProvider(provider);
}
function addTokenToWhitelist(IConfigurableRightsPool crp, uint[] memory sort, address[] memory token) public {
crp.addTokenToWhitelist(sort, token);
}
function claimManagementFee(IConfigurableRightsPool crp) public {
crp.claimManagerFee();
}
// --- Internals ---
function _safeApprove(
IERC20 token,
address spender,
uint amount
) internal {
if (token.allowance(address(this), spender) > 0) {
token.approve(spender, 0);
}
token.approve(spender, amount);
}
function _join(
AbstractPool pool,
address[] memory tokens,
uint poolAmountOut,
uint[] memory maxAmountsIn,
address kol
) internal {
require(maxAmountsIn.length == tokens.length, "ERR_LENGTH_MISMATCH");
for (uint i = 0; i < tokens.length; i++) {
IERC20 token = IERC20(tokens[i]);
token.safeTransferFrom(msg.sender, address(this), maxAmountsIn[i]);
token.safeApprove(address(pool), 0);
token.safeApprove(address(pool), maxAmountsIn[i]);
}
pool.joinPool(poolAmountOut, maxAmountsIn, kol, msg.sender);
for (uint i = 0; i < tokens.length; i++) {
IERC20 token = IERC20(tokens[i]);
if (token.balanceOf(address(this)) > 0) {
token.safeTransfer(msg.sender, token.balanceOf(address(this)));
}
}
require(pool.transfer(msg.sender, pool.balanceOf(address(this))), "ERR_TRANSFER_FAILED");
}
function _exit(
IConfigurableRightsPool pool,
uint poolAmountIn,
uint[] memory minAmountsOut,
address[] memory tokens,
bool isSmartMode
) internal {
uint shareToBurn = poolAmountIn;
if (pool.etype() == SmartPoolManager.Etypes.CLOSED) {
shareToBurn = pool.balanceOf(msg.sender);
}
require(pool.transferFrom(msg.sender, address(this), shareToBurn), "ERR_TRANSFER_FAILED");
// _safeApprove(pool, address(pool), shareToBurn);
pool.exitPool(shareToBurn, minAmountsOut, msg.sender);
if(!isSmartMode){
for (uint i = 0; i < tokens.length; i++) {
IERC20 token = IERC20(tokens[i]);
if (token.balanceOf(address(this)) > 0) {
token.safeTransfer(msg.sender, token.balanceOf(address(this)));
}
}
}
}
function claimKolReward(address pool) public {
address uservault = _getUserVault(pool);
IUserVault(uservault).kolClaim(pool);
}
function claimManagersReward(address vault_,address pool) external {
IUserVault(vault_).managerClaim(pool);
}
function _claimManagersReward(address pool) internal {
address vault = _getVault(pool);
address uservault = _getUserVault(pool);
bool vaultCanClaim = IUserVault(vault).getManagerClaimBool(pool);
bool uservaultCanClaim = IUserVault(uservault).getManagerClaimBool(pool);
SmartPoolManager.Etypes type_ = IConfigurableRightsPool(pool).etype();
if(type_ == SmartPoolManager.Etypes.OPENED && vaultCanClaim) IUserVault(vault).managerClaim(pool);
if(type_ == SmartPoolManager.Etypes.CLOSED && uservaultCanClaim) IUserVault(uservault).managerClaim(pool);
}
function _makeSwap(IAggregator.SwapInfoBase calldata swapBase, IAggregator.SwapData memory swapData, IERC20 swapAcceptToken) internal returns (uint256 postSwap) {
require(FactoryActions(FACTORY).getModuleStatus(address(0), swapBase.rebalanceAdapter), 'MODULE_ILLEGAL');
require(RebalaceAdapter(swapBase.rebalanceAdapter).isRouterApproved(swapBase.aggregator), 'ROUTER_ILLEGAL');
uint preSwap = swapAcceptToken.balanceOf(address(this));
if (swapBase.swapType == IAggregator.SwapType.UNISWAPV3) {
(uint256 minReturn, uint256[] memory pools) = abi.decode(swapData.data, (uint256, uint256[]));
IAggregator(swapBase.aggregator).uniswapV3Swap(swapData.quantity, minReturn, pools);
} else if (swapBase.swapType == IAggregator.SwapType.UNISWAPV2) {
(uint256 minReturn, address[] memory paths) = abi.decode(swapData.data, (uint256, address[]));
IAggregator(swapBase.aggregator).swapExactTokensForTokens(swapData.quantity, minReturn, paths, address(this), SafeMath.add(block.timestamp, 1800));
} else if (swapBase.swapType == IAggregator.SwapType.ONEINCH){
Address.functionCallWithValue(swapBase.aggregator, swapData.data, 0);
// _validateData(swapBase.quantity, swapBase.data, address(this));
} else if (swapBase.swapType == IAggregator.SwapType.CURVE){
// only supports stbt/usdc/usdt/dai curve pool
(int128 i, int128 j, uint256 dx, uint256 minDy) = abi.decode(swapData.data, (int128, int128, uint256, uint256));
ICurve(swapBase.aggregator).exchange_underlying(i, j, dx, minDy);
} else {
revert("ERR_INVALID_SWAP_TYPE");
}
postSwap = SafeMath.sub(swapAcceptToken.balanceOf(address(this)), preSwap);
}
function _getVault(address pool) internal view returns(address){
return IConfigurableRightsPool(pool).vaultAddress();
}
function _getUserVault(address pool) internal returns(address){
address vault = _getVault(pool);
return IVault(vault).userVault();
}
function _calculateShare(LiquidityPoolActions bPool,uint totalPoolShares, uint issueFee, address t, uint actualTokenAmountIn) internal view returns(uint) {
uint totalTokenBalance = bPool.getBalance(t);
uint issueFeeRate = issueFee.bmul(1000);
uint share = (totalPoolShares.bsub(1).bmul(actualTokenAmountIn) * (uint(1000).bsub(issueFeeRate))).bdiv((1000 * totalTokenBalance.badd(1)));
return share;
}
receive() external payable {}
}// 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/>.
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
import "../base/Math.sol";
import "../base/Num.sol";
// Contract to wrap internal functions for testing
contract TMath is Math {
function calc_btoi(uint a) external pure returns (uint) {
return btoi(a);
}
function calc_bfloor(uint a) external pure returns (uint) {
return bfloor(a);
}
function calc_badd(uint a, uint b) external pure returns (uint) {
return badd(a, b);
}
function calc_bsub(uint a, uint b) external pure returns (uint) {
return bsub(a, b);
}
function calc_bsubSign(uint a, uint b) external pure returns (uint, bool) {
return bsubSign(a, b);
}
function calc_bmul(uint a, uint b) external pure returns (uint) {
return bmul(a, b);
}
function calc_bdiv(uint a, uint b) external pure returns (uint) {
return bdiv(a, b);
}
function calc_bpowi(uint a, uint n) external pure returns (uint) {
return bpowi(a, n);
}
function calc_bpow(uint base, uint exp) external pure returns (uint) {
return bpow(base, exp);
}
function calc_bpowApprox(
uint base,
uint exp,
uint precision
) external pure returns (uint) {
return bpowApprox(base, exp, precision);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
import "./Num.sol";
contract Math is BBronze, Const, Num {
/**********************************************************************************************
// calcSpotPrice //
// sP = spotPrice //
// bI = tokenBalanceIn ( bI / wI ) 1 //
// bO = tokenBalanceOut sP = ----------- * ---------- //
// wI = tokenWeightIn ( bO / wO ) ( 1 - sF ) //
// wO = tokenWeightOut //
// sF = swapFee //
**********************************************************************************************/
function calcSpotPrice(
uint tokenBalanceIn,
uint tokenWeightIn,
uint tokenBalanceOut,
uint tokenWeightOut,
uint swapFee
) external pure returns (uint spotPrice) {
uint numer = bdiv(tokenBalanceIn, tokenWeightIn);
uint denom = bdiv(tokenBalanceOut, tokenWeightOut);
uint ratio = bdiv(numer, denom);
uint scale = bdiv(BONE, bsub(BONE, swapFee));
return (spotPrice = bmul(ratio, scale));
}
/**********************************************************************************************
// calcOutGivenIn //
// aO = tokenAmountOut //
// bO = tokenBalanceOut //
// bI = tokenBalanceIn / / bI \ (wI / wO) \ //
// aI = tokenAmountIn aO = bO * | 1 - | -------------------------- | ^ | //
// wI = tokenWeightIn \ \ ( bI + ( aI * ( 1 - sF )) / / //
// wO = tokenWeightOut //
// sF = swapFee //
**********************************************************************************************/
function calcOutGivenIn(
uint tokenBalanceIn,
uint tokenWeightIn,
uint tokenBalanceOut,
uint tokenWeightOut,
uint tokenAmountIn,
uint swapFee
) external pure returns (uint tokenAmountOut) {
uint weightRatio = bdiv(tokenWeightIn, tokenWeightOut);
uint adjustedIn = bsub(BONE, swapFee);
adjustedIn = bmul(tokenAmountIn, adjustedIn);
uint y = bdiv(tokenBalanceIn, badd(tokenBalanceIn, adjustedIn));
uint foo = bpow(y, weightRatio);
uint bar = bsub(BONE, foo);
tokenAmountOut = bmul(tokenBalanceOut, bar);
return tokenAmountOut;
}
/**********************************************************************************************
// calcInGivenOut //
// aI = tokenAmountIn //
// bO = tokenBalanceOut / / bO \ (wO / wI) \ //
// bI = tokenBalanceIn bI * | | ------------ | ^ - 1 | //
// aO = tokenAmountOut aI = \ \ ( bO - aO ) / / //
// wI = tokenWeightIn -------------------------------------------- //
// wO = tokenWeightOut ( 1 - sF ) //
// sF = swapFee //
**********************************************************************************************/
function calcInGivenOut(
uint tokenBalanceIn,
uint tokenWeightIn,
uint tokenBalanceOut,
uint tokenWeightOut,
uint tokenAmountOut,
uint swapFee
) external pure returns (uint tokenAmountIn) {
uint weightRatio = bdiv(tokenWeightOut, tokenWeightIn);
uint diff = bsub(tokenBalanceOut, tokenAmountOut);
uint y = bdiv(tokenBalanceOut, diff);
uint foo = bpow(y, weightRatio);
foo = bsub(foo, BONE);
tokenAmountIn = bsub(BONE, swapFee);
tokenAmountIn = bdiv(bmul(tokenBalanceIn, foo), tokenAmountIn);
return tokenAmountIn;
}
/**********************************************************************************************
// calcPoolOutGivenSingleIn //
// pAo = poolAmountOut / \ //
// tAi = tokenAmountIn /// / // wI \ \\ \ wI \ //
// wI = tokenWeightIn //| tAi *| 1 - || 1 - -- | * sF || + tBi \ -- \ //
// tW = totalWeight pAo=|| \ \ \\ tW / // | ^ tW | * pS - pS //
// tBi = tokenBalanceIn \\ ------------------------------------- / / //
// pS = poolSupply \\ tBi / / //
// sF = swapFee \ / //
**********************************************************************************************/
function calcPoolOutGivenSingleIn(
uint tokenBalanceIn,
uint tokenWeightIn,
uint poolSupply,
uint totalWeight,
uint tokenAmountIn,
uint swapFee
) external pure returns (uint poolAmountOut) {
// Charge the trading fee for the proportion of tokenAi
// which is implicitly traded to the other pool tokens.
// That proportion is (1- weightTokenIn)
// tokenAiAfterFee = tAi * (1 - (1-weightTi) * poolFee);
uint normalizedWeight = bdiv(tokenWeightIn, totalWeight);
uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee);
uint tokenAmountInAfterFee = bmul(tokenAmountIn, bsub(BONE, zaz));
uint newTokenBalanceIn = badd(tokenBalanceIn, tokenAmountInAfterFee);
uint tokenInRatio = bdiv(newTokenBalanceIn, tokenBalanceIn);
// uint newPoolSupply = (ratioTi ^ weightTi) * poolSupply;
uint poolRatio = bpow(tokenInRatio, normalizedWeight);
uint newPoolSupply = bmul(poolRatio, poolSupply);
poolAmountOut = bsub(newPoolSupply, poolSupply);
return poolAmountOut;
}
/**********************************************************************************************
// calcSingleInGivenPoolOut //
// tAi = tokenAmountIn //(pS + pAo)\ / 1 \\ //
// pS = poolSupply || --------- | ^ | --------- || * bI - bI //
// pAo = poolAmountOut \\ pS / \(wI / tW)// //
// bI = balanceIn tAi = -------------------------------------------- //
// wI = weightIn / wI \ //
// tW = totalWeight | 1 - ---- | * sF //
// sF = swapFee \ tW / //
**********************************************************************************************/
function calcSingleInGivenPoolOut(
uint tokenBalanceIn,
uint tokenWeightIn,
uint poolSupply,
uint totalWeight,
uint poolAmountOut,
uint swapFee
) external pure returns (uint tokenAmountIn) {
uint normalizedWeight = bdiv(tokenWeightIn, totalWeight);
uint newPoolSupply = badd(poolSupply, poolAmountOut);
uint poolRatio = bdiv(newPoolSupply, poolSupply);
//uint newBalTi = poolRatio^(1/weightTi) * balTi;
uint boo = bdiv(BONE, normalizedWeight);
uint tokenInRatio = bpow(poolRatio, boo);
uint newTokenBalanceIn = bmul(tokenInRatio, tokenBalanceIn);
uint tokenAmountInAfterFee = bsub(newTokenBalanceIn, tokenBalanceIn);
// Do reverse order of fees charged in joinswap_ExternAmountIn, this way
// ``` pAo == joinswap_ExternAmountIn(Ti, joinswap_PoolAmountOut(pAo, Ti)) ```
//uint tAi = tAiAfterFee / (1 - (1-weightTi) * swapFee) ;
uint zar = bmul(bsub(BONE, normalizedWeight), swapFee);
tokenAmountIn = bdiv(tokenAmountInAfterFee, bsub(BONE, zar));
return tokenAmountIn;
}
/**********************************************************************************************
// calcSingleOutGivenPoolIn //
// tAo = tokenAmountOut / / \\ //
// bO = tokenBalanceOut / // pS - (pAi * (1 - eF)) \ / 1 \ \\ //
// pAi = poolAmountIn | bO - || ----------------------- | ^ | --------- | * b0 || //
// ps = poolSupply \ \\ pS / \(wO / tW)/ // //
// wI = tokenWeightIn tAo = \ \ // //
// tW = totalWeight / / wO \ \ //
// sF = swapFee * | 1 - | 1 - ---- | * sF | //
// eF = exitFee \ \ tW / / //
**********************************************************************************************/
function calcSingleOutGivenPoolIn(
uint tokenBalanceOut,
uint tokenWeightOut,
uint poolSupply,
uint totalWeight,
uint poolAmountIn,
uint swapFee
) external pure returns (uint tokenAmountOut) {
uint normalizedWeight = bdiv(tokenWeightOut, totalWeight);
// charge exit fee on the pool token side
// pAiAfterExitFee = pAi*(1-exitFee)
uint poolAmountInAfterExitFee = bmul(poolAmountIn, bsub(BONE, EXIT_FEE));
uint newPoolSupply = bsub(poolSupply, poolAmountInAfterExitFee);
uint poolRatio = bdiv(newPoolSupply, poolSupply);
// newBalTo = poolRatio^(1/weightTo) * balTo;
uint tokenOutRatio = bpow(poolRatio, bdiv(BONE, normalizedWeight));
uint newTokenBalanceOut = bmul(tokenOutRatio, tokenBalanceOut);
uint tokenAmountOutBeforeSwapFee = bsub(tokenBalanceOut, newTokenBalanceOut);
// charge swap fee on the output token side
//uint tAo = tAoBeforeSwapFee * (1 - (1-weightTo) * swapFee)
uint zaz = bmul(bsub(BONE, normalizedWeight), swapFee);
tokenAmountOut = bmul(tokenAmountOutBeforeSwapFee, bsub(BONE, zaz));
return tokenAmountOut;
}
/**********************************************************************************************
// calcPoolInGivenSingleOut //
// pAi = poolAmountIn // / tAo \\ / wO \ \ //
// bO = tokenBalanceOut // | bO - -------------------------- |\ | ---- | \ //
// tAo = tokenAmountOut pS - || \ 1 - ((1 - (tO / tW)) * sF)/ | ^ \ tW / * pS | //
// ps = poolSupply \\ -----------------------------------/ / //
// wO = tokenWeightOut pAi = \\ bO / / //
// tW = totalWeight ------------------------------------------------------------- //
// sF = swapFee ( 1 - eF ) //
// eF = exitFee //
**********************************************************************************************/
function calcPoolInGivenSingleOut(
uint tokenBalanceOut,
uint tokenWeightOut,
uint poolSupply,
uint totalWeight,
uint tokenAmountOut,
uint swapFee
) external pure returns (uint poolAmountIn) {
// charge swap fee on the output token side
uint normalizedWeight = bdiv(tokenWeightOut, totalWeight);
//uint tAoBeforeSwapFee = tAo / (1 - (1-weightTo) * swapFee) ;
uint zoo = bsub(BONE, normalizedWeight);
uint zar = bmul(zoo, swapFee);
uint tokenAmountOutBeforeSwapFee = bdiv(tokenAmountOut, bsub(BONE, zar));
uint newTokenBalanceOut = bsub(tokenBalanceOut, tokenAmountOutBeforeSwapFee);
uint tokenOutRatio = bdiv(newTokenBalanceOut, tokenBalanceOut);
//uint newPoolSupply = (ratioTo ^ weightTo) * poolSupply;
uint poolRatio = bpow(tokenOutRatio, normalizedWeight);
uint newPoolSupply = bmul(poolRatio, poolSupply);
uint poolAmountInAfterExitFee = bsub(poolSupply, newPoolSupply);
// charge exit fee on the pool token side
// pAi = pAiAfterExitFee/(1-exitFee)
poolAmountIn = bdiv(poolAmountInAfterExitFee, bsub(BONE, EXIT_FEE));
return poolAmountIn;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
import "./Const.sol";
// Core contract; can't be changed. So disable solhint (reminder for v2)
/* solhint-disable private-vars-leading-underscore */
contract Num is Const {
function btoi(uint a) internal pure returns (uint) {
return a / BONE;
}
function bfloor(uint a) internal pure returns (uint) {
return btoi(a) * BONE;
}
function badd(uint a, uint b) internal pure returns (uint) {
uint c = a + b;
require(c >= a, "ERR_ADD_OVERFLOW");
return c;
}
function bsub(uint a, uint b) internal pure returns (uint) {
(uint c, bool flag) = bsubSign(a, b);
require(!flag, "ERR_SUB_UNDERFLOW");
return c;
}
function bsubSign(uint a, uint b) internal pure returns (uint, bool) {
if (a >= b) {
return (a - b, false);
} else {
return (b - a, true);
}
}
function bmul(uint a, uint b) internal pure returns (uint) {
uint c0 = a * b;
require(a == 0 || c0 / a == b, "ERR_MUL_OVERFLOW");
uint c1 = c0 + (BONE / 2);
require(c1 >= c0, "ERR_MUL_OVERFLOW");
uint c2 = c1 / BONE;
return c2;
}
function bdiv(uint a, uint b) internal pure returns (uint) {
require(b != 0, "ERR_DIV_ZERO");
uint c0 = a * BONE;
require(a == 0 || c0 / a == BONE, "ERR_DIV_INTERNAL"); // bmul overflow
uint c1 = c0 + (b / 2);
require(c1 >= c0, "ERR_DIV_INTERNAL"); // badd require
uint c2 = c1 / b;
return c2;
}
// DSMath.wpow
function bpowi(uint a, uint n) internal pure returns (uint) {
uint z = n % 2 != 0 ? a : BONE;
for (n /= 2; n != 0; n /= 2) {
a = bmul(a, a);
if (n % 2 != 0) {
z = bmul(z, a);
}
}
return z;
}
// Compute b^(e.w) by splitting it into (b^e)*(b^0.w).
// Use `bpowi` for `b^e` and `bpowK` for k iterations
// of approximation of b^0.w
function bpow(uint base, uint exp) internal pure returns (uint) {
require(base >= MIN_BPOW_BASE, "ERR_BPOW_BASE_TOO_LOW");
require(base <= MAX_BPOW_BASE, "ERR_BPOW_BASE_TOO_HIGH");
uint whole = bfloor(exp);
uint remain = bsub(exp, whole);
uint wholePow = bpowi(base, btoi(whole));
if (remain == 0) {
return wholePow;
}
uint partialResult = bpowApprox(base, remain, BPOW_PRECISION);
return bmul(wholePow, partialResult);
}
function bpowApprox(
uint base,
uint exp,
uint precision
) internal pure returns (uint) {
// term 0:
uint a = exp;
(uint x, bool xneg) = bsubSign(base, BONE);
uint term = BONE;
uint sum = term;
bool negative = false;
// term(k) = numer / denom
// = (product(a - i - 1, i=1-->k) * x^k) / (k!)
// each iteration, multiply previous term by (a-(k-1)) * x / k
// continue until term is less than precision
for (uint i = 1; term >= precision; i++) {
uint bigK = i * BONE;
(uint c, bool cneg) = bsubSign(a, bsub(bigK, BONE));
term = bmul(term, bmul(c, x));
term = bdiv(term, bigK);
if (term == 0) break;
if (xneg) negative = !negative;
if (cneg) negative = !negative;
if (negative) {
sum = bsub(sum, term);
} else {
sum = badd(sum, term);
}
}
return sum;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
import "./Color.sol";
contract Const is BBronze {
uint public constant BONE = 10**18;
uint public constant MIN_BOUND_TOKENS = 1;
uint public constant MAX_BOUND_TOKENS = 16;
uint public constant MIN_FEE = BONE / 10**6;
uint public constant MAX_FEE = BONE / 10;
uint public constant EXIT_FEE = 0;
uint public constant MIN_WEIGHT = BONE;
uint public constant MAX_WEIGHT = BONE * 50;
uint public constant MAX_TOTAL_WEIGHT = BONE * 50;
uint public constant MIN_BALANCE = 0;
uint public constant INIT_POOL_SUPPLY = BONE * 100;
uint public constant MIN_BPOW_BASE = 1 wei;
uint public constant MAX_BPOW_BASE = (2 * BONE) - 1 wei;
uint public constant BPOW_PRECISION = BONE / 10**10;
uint public constant MAX_IN_RATIO = BONE / 2;
uint public constant MAX_OUT_RATIO = (BONE / 3) + 1 wei;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
// abstract contract BColor {
// function getColor()
// external view virtual
// returns (bytes32);
// }
contract BBronze {
function getColor() external pure returns (bytes32) {
return bytes32("BRONZE");
}
}pragma solidity 0.6.12;
import "../base/Num.sol";
import "../interfaces/IERC20.sol";
import "../interfaces/AggregatorV2V3Interface.sol";
import "../interfaces/IUniswapOracle.sol";
library SafeMath {
function add(uint a, uint b) internal pure returns (uint) {
uint c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint a, uint b) internal pure returns (uint) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(
uint a,
uint b,
string memory errorMessage
) internal pure returns (uint) {
require(b <= a, errorMessage);
uint c = a - b;
return c;
}
function mul(uint a, uint b) internal pure returns (uint) {
if (a == 0) {
return 0;
}
uint c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint a, uint b) internal pure returns (uint) {
return div(a, b, "SafeMath: division by zero");
}
function div(
uint a,
uint b,
string memory errorMessage
) internal pure returns (uint) {
require(b > 0, errorMessage);
uint c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function mod(uint a, uint b) internal pure returns (uint) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(
uint a,
uint b,
string memory errorMessage
) internal pure returns (uint) {
require(b != 0, errorMessage);
return a % b;
}
function abs(uint a, uint b) internal pure returns(uint result, bool isFirstBigger) {
if(a > b){
result = a - b;
isFirstBigger = true;
} else {
result = b - a;
isFirstBigger = false;
}
}
}
contract DesynChainlinkOracle is Num {
address public admin;
using SafeMath for uint;
IUniswapOracle public twapOracle;
mapping(address => uint) internal prices;
mapping(bytes32 => AggregatorV2V3Interface) internal feeds;
event PricePosted(address asset, uint previousPriceMantissa, uint requestedPriceMantissa, uint newPriceMantissa);
event NewAdmin(address oldAdmin, address newAdmin);
event FeedSet(address feed, string symbol);
constructor(address twapOracle_) public {
admin = msg.sender;
twapOracle = IUniswapOracle(twapOracle_);
}
function getPrice(address tokenAddress) public returns (uint price) {
IERC20 token = IERC20(tokenAddress);
AggregatorV2V3Interface feed = getFeed(token.symbol());
if (prices[address(token)] != 0) {
price = prices[address(token)];
} else if (address(feed) != address(0)) {
price = getChainlinkPrice(feed);
} else {
try twapOracle.update(address(token)) {} catch {}
price = getUniswapPrice(tokenAddress);
}
(uint decimalDelta, bool isUnderFlow18) = uint(18).abs(uint(token.decimals()));
if(isUnderFlow18){
return price.mul(10**decimalDelta);
}
if(!isUnderFlow18){
return price.div(10**decimalDelta);
}
}
function getAllPrice(address[] calldata poolTokens, uint[] calldata actualAmountsOut) external returns (uint fundAll) {
require(poolTokens.length == actualAmountsOut.length, "Invalid Length");
for (uint i = 0; i < poolTokens.length; i++) {
address t = poolTokens[i];
uint tokenAmountOut = actualAmountsOut[i];
fundAll = badd(fundAll, bmul(getPrice(t), tokenAmountOut));
}
}
function getChainlinkPrice(AggregatorV2V3Interface feed) internal view returns (uint) {
// Chainlink USD-denominated feeds store answers at 8 decimals
uint decimalDelta = bsub(uint(18), feed.decimals());
// Ensure that we don't multiply the result by 0
if (decimalDelta > 0) {
return uint(feed.latestAnswer()).mul(10**decimalDelta);
} else {
return uint(feed.latestAnswer());
}
}
function getUniswapPrice(address tokenAddress) internal view returns (uint) {
IERC20 token = IERC20(tokenAddress);
uint price = twapOracle.consult(tokenAddress, uint(10) ** token.decimals());
return price;
}
function setDirectPrice(address asset, uint price) external onlyAdmin {
emit PricePosted(asset, prices[asset], price, price);
prices[asset] = price;
}
function setFeed(string calldata symbol, address feed) external onlyAdmin {
require(feed != address(0) && feed != address(this), "invalid feed address");
emit FeedSet(feed, symbol);
feeds[keccak256(abi.encodePacked(symbol))] = AggregatorV2V3Interface(feed);
}
function getFeed(string memory symbol) public view returns (AggregatorV2V3Interface) {
return feeds[keccak256(abi.encodePacked(symbol))];
}
function assetPrices(address asset) external view returns (uint) {
return prices[asset];
}
function compareStrings(string memory a, string memory b) internal pure returns (bool) {
return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b))));
}
function setAdmin(address newAdmin) external onlyAdmin {
require(newAdmin != address(0),"ERR_ZERO_ADDRESS");
address oldAdmin = admin;
admin = newAdmin;
emit NewAdmin(oldAdmin, newAdmin);
}
modifier onlyAdmin() {
require(msg.sender == admin, "only admin may call");
_;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
// Interface declarations
/* solhint-disable func-order */
interface IERC20 {
// 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, uint value);
// 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, uint value);
// Returns the amount of tokens in existence
function totalSupply() external view returns (uint);
// Returns the amount of tokens owned by account
function balanceOf(address account) external view returns (uint);
// Returns the decimals of tokens
function decimals() external view returns (uint8);
function symbol() external view returns (string memory);
// 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 (uint);
// Sets amount as the allowance of spender over the caller’s tokens
// Returns a boolean value indicating whether the operation succeeded
// Emits an Approval event.
function approve(address spender, uint amount) external returns (bool);
// 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, uint amount) external returns (bool);
// 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,
uint amount
) external returns (bool);
}pragma solidity 0.6.12;
/**
* @title The V2 & V3 Aggregator Interface
* @notice Solidity V0.5 does not allow interfaces to inherit from other
* interfaces so this contract is a combination of v0.5 AggregatorInterface.sol
* and v0.5 AggregatorV3Interface.sol.
*/
interface AggregatorV2V3Interface {
//
// V2 Interface:
//
function latestAnswer() external view returns (int);
function latestTimestamp() external view returns (uint);
function latestRound() external view returns (uint);
function getAnswer(uint roundId) external view returns (int);
function getTimestamp(uint roundId) external view returns (uint);
event AnswerUpdated(int indexed current, uint indexed roundId, uint timestamp);
event NewRound(uint indexed roundId, address indexed startedBy, uint startedAt);
//
// V3 Interface:
//
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint);
// getRoundData and latestRoundData should both raise "No data present"
// if they do not have data to report, instead of returning unset values
// which could be misinterpreted as actual reported values.
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int answer,
uint startedAt,
uint updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int answer,
uint startedAt,
uint updatedAt,
uint80 answeredInRound
);
}pragma solidity 0.6.12;
interface IUniswapOracle {
function update(address token) external;
function consult(address token, uint amountIn) external view returns (uint amountOut);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
// Needed to pass in structs
pragma experimental ABIEncoderV2;
// Imports
import "../interfaces/IERC20.sol";
import "../interfaces/IConfigurableRightsPool.sol";
import "../interfaces/IBFactory.sol"; // unused
import "./DesynSafeMath.sol";
import "./SafeMath.sol";
// import "./SafeApprove.sol";
import "../libraries/SafeERC20.sol";
/**
* @author Desyn Labs
* @title Factor out the weight updates
*/
library SmartPoolManager {
// using SafeApprove for IERC20;
using DesynSafeMath for uint;
using SafeMath for uint;
using SafeERC20 for IERC20;
//kol pool params
struct levelParams {
uint level;
uint ratio;
}
struct feeParams {
levelParams firstLevel;
levelParams secondLevel;
levelParams thirdLevel;
levelParams fourLevel;
}
struct KolPoolParams {
feeParams managerFee;
feeParams issueFee;
feeParams redeemFee;
feeParams perfermanceFee;
}
// Type declarations
enum Etypes {
OPENED,
CLOSED
}
enum Period {
DAY90,
DAY180,
DAY360,
DAY1,
DAY3,
DAY7,
DAY14,
DAY30
}
struct Fund {
uint etfAmount;
uint fundAmount;
uint snapshotTime;
address[] tokens;
uint[] tokensAmount;
}
// updateWeight and pokeWeights are unavoidably long
/* solhint-disable function-max-lines */
struct Status {
uint collectPeriod;
uint collectEndTime;
uint closurePeriod;
uint closureEndTime;
uint upperCap;
uint floorCap;
uint managerFee;
uint redeemFee;
uint issueFee;
uint perfermanceFee;
uint startClaimFeeTime;
}
struct PoolParams {
// Desyn Pool Token (representing shares of the pool)
string poolTokenSymbol;
string poolTokenName;
// Tokens inside the Pool
address[] constituentTokens;
uint[] tokenBalances;
uint[] tokenWeights;
uint managerFee;
uint redeemFee;
uint issueFee;
uint perfermanceFee;
Etypes etype;
}
struct PoolTokenRange {
uint bspFloor;
uint bspCap;
}
function initRequire(
uint managerFee,
uint issueFee,
uint redeemFee,
uint perfermanceFee,
uint tokenBalancesLength,
uint tokenWeightsLength,
uint constituentTokensLength,
bool initBool
) external pure {
// We don't have a pool yet; check now or it will fail later (in order of likelihood to fail)
// (and be unrecoverable if they don't have permission set to change it)
// Most likely to fail, so check first
require(!initBool, "Init fail");
require(managerFee >= DesynConstants.MANAGER_MIN_FEE, "ERR_INVALID_MANAGER_FEE");
require(managerFee <= DesynConstants.MANAGER_MAX_FEE, "ERR_INVALID_MANAGER_FEE");
require(issueFee >= DesynConstants.ISSUE_MIN_FEE, "ERR_INVALID_ISSUE_MIN_FEE");
require(issueFee <= DesynConstants.ISSUE_MAX_FEE, "ERR_INVALID_ISSUE_MAX_FEE");
require(redeemFee >= DesynConstants.REDEEM_MIN_FEE, "ERR_INVALID_REDEEM_MIN_FEE");
require(redeemFee <= DesynConstants.REDEEM_MAX_FEE, "ERR_INVALID_REDEEM_MAX_FEE");
require(perfermanceFee >= DesynConstants.PERFERMANCE_MIN_FEE, "ERR_INVALID_PERFERMANCE_MIN_FEE");
require(perfermanceFee <= DesynConstants.PERFERMANCE_MAX_FEE, "ERR_INVALID_PERFERMANCE_MAX_FEE");
// Arrays must be parallel
require(tokenBalancesLength == constituentTokensLength, "ERR_START_BALANCES_MISMATCH");
require(tokenWeightsLength == constituentTokensLength, "ERR_START_WEIGHTS_MISMATCH");
// Cannot have too many or too few - technically redundant, since BPool.bind() would fail later
// But if we don't check now, we could have a useless contract with no way to create a pool
require(constituentTokensLength >= DesynConstants.MIN_ASSET_LIMIT, "ERR_TOO_FEW_TOKENS");
require(constituentTokensLength <= DesynConstants.MAX_ASSET_LIMIT, "ERR_TOO_MANY_TOKENS");
// There are further possible checks (e.g., if they use the same token twice), but
// we can let bind() catch things like that (i.e., not things that might reasonably work)
}
/**
* @notice Non ERC20-conforming tokens are problematic; don't allow them in pools
* @dev Will revert if invalid
* @param token - The prospective token to verify
*/
function verifyTokenCompliance(address token) external {
verifyTokenComplianceInternal(token);
}
/**
* @notice Non ERC20-conforming tokens are problematic; don't allow them in pools
* @dev Will revert if invalid - overloaded to save space in the main contract
* @param tokens - The prospective tokens to verify
*/
function verifyTokenCompliance(address[] calldata tokens) external {
for (uint i = 0; i < tokens.length; i++) {
verifyTokenComplianceInternal(tokens[i]);
}
}
function createPoolInternalHandle(IBPool bPool, uint initialSupply) external view {
require(initialSupply >= DesynConstants.MIN_POOL_SUPPLY, "ERR_INIT_SUPPLY_MIN");
require(initialSupply <= DesynConstants.MAX_POOL_SUPPLY, "ERR_INIT_SUPPLY_MAX");
require(bPool.EXIT_FEE() == 0, "ERR_NONZERO_EXIT_FEE");
// EXIT_FEE must always be zero, or ConfigurableRightsPool._pushUnderlying will fail
require(DesynConstants.EXIT_FEE == 0, "ERR_NONZERO_EXIT_FEE");
}
function createPoolHandle(
uint collectPeriod,
uint upperCap,
uint initialSupply
) external pure {
require(collectPeriod <= DesynConstants.MAX_COLLECT_PERIOD, "ERR_EXCEEDS_FUND_RAISING_PERIOD");
require(upperCap >= initialSupply, "ERR_CAP_BIGGER_THAN_INITSUPPLY");
}
function exitPoolHandleA(
IConfigurableRightsPool self,
IBPool bPool,
address poolToken,
uint _tokenAmountOut,
uint redeemFee
)
external
returns (
uint redeemAndPerformanceFeeReceived,
uint finalAmountOut,
uint redeemFeeReceived
)
{
// redeem fee
redeemFeeReceived = DesynSafeMath.bmul(_tokenAmountOut, redeemFee);
// performance fee
uint performanceFeeReceived = 0;
// redeem fee and performance fee
redeemAndPerformanceFeeReceived = DesynSafeMath.badd(performanceFeeReceived, redeemFeeReceived);
// final amount the user got
finalAmountOut = DesynSafeMath.bsub(_tokenAmountOut, redeemAndPerformanceFeeReceived);
_pushUnderlying(bPool, poolToken, msg.sender, finalAmountOut);
if (redeemFee != 0) {
_pushUnderlying(bPool, poolToken, address(this), redeemAndPerformanceFeeReceived);
IERC20(poolToken).safeApprove(self.vaultAddress(), 0);
IERC20(poolToken).safeApprove(self.vaultAddress(), redeemAndPerformanceFeeReceived);
}
}
function exitPoolHandleB(
IConfigurableRightsPool self,
bool bools,
bool isCompletedCollect,
uint closureEndTime,
uint collectEndTime,
// uint _etfAmount,
// uint _fundAmount,
uint poolAmountIn
) external view returns (uint actualPoolAmountIn) {
actualPoolAmountIn = poolAmountIn;
if (bools) {
bool isCloseEtfCollectEndWithFailure = isCompletedCollect == false && block.timestamp >= collectEndTime;
bool isCloseEtfClosureEnd = block.timestamp >= closureEndTime;
require(isCloseEtfCollectEndWithFailure || isCloseEtfClosureEnd, "ERR_CLOSURE_TIME_NOT_ARRIVED!");
actualPoolAmountIn = self.balanceOf(msg.sender);
}
// fundAmount = _fundAmount;
// etfAmount = _etfAmount;
}
function joinPoolHandle(
bool canWhitelistLPs,
bool isList,
bool bools,
uint collectEndTime
) external view {
require(!canWhitelistLPs || isList, "ERR_NOT_ON_WHITELIST");
if (bools) {
require(block.timestamp <= collectEndTime, "ERR_COLLECT_PERIOD_FINISHED!");
}
}
/**
* @notice Join a pool
* @param self - ConfigurableRightsPool instance calling the library
* @param bPool - Core BPool the CRP is wrapping
* @param poolAmountOut - number of pool tokens to receive
* @param maxAmountsIn - Max amount of asset tokens to spend
* @return actualAmountsIn - calculated values of the tokens to pull in
*/
function joinPool(
IConfigurableRightsPool self,
IBPool bPool,
uint poolAmountOut,
uint[] calldata maxAmountsIn,
uint issueFee
) external view returns (uint[] memory actualAmountsIn) {
address[] memory tokens = bPool.getCurrentTokens();
require(maxAmountsIn.length == tokens.length, "ERR_AMOUNTS_MISMATCH");
uint poolTotal = self.totalSupply();
// Subtract 1 to ensure any rounding errors favor the pool
uint ratio = DesynSafeMath.bdiv(poolAmountOut, DesynSafeMath.bsub(poolTotal, 1));
require(ratio != 0, "ERR_MATH_APPROX");
// We know the length of the array; initialize it, and fill it below
// Cannot do "push" in memory
actualAmountsIn = new uint[](tokens.length);
// This loop contains external calls
// External calls are to math libraries or the underlying pool, so low risk
uint issueFeeRate = issueFee.bmul(1000);
for (uint i = 0; i < tokens.length; i++) {
address t = tokens[i];
uint bal = bPool.getBalance(t);
// Add 1 to ensure any rounding errors favor the pool
uint base = bal.badd(1).bmul(poolAmountOut * uint(1000));
uint tokenAmountIn = base.bdiv(poolTotal.bsub(1) * (uint(1000).bsub(issueFeeRate)));
require(tokenAmountIn != 0, "ERR_MATH_APPROX");
require(tokenAmountIn <= maxAmountsIn[i], "ERR_LIMIT_IN");
actualAmountsIn[i] = tokenAmountIn;
}
}
/**
* @notice Exit a pool - redeem pool tokens for underlying assets
* @param self - ConfigurableRightsPool instance calling the library
* @param bPool - Core BPool the CRP is wrapping
* @param poolAmountIn - amount of pool tokens to redeem
* @param minAmountsOut - minimum amount of asset tokens to receive
* @return actualAmountsOut - calculated amounts of each token to pull
*/
function exitPool(
IConfigurableRightsPool self,
IBPool bPool,
uint poolAmountIn,
uint[] calldata minAmountsOut
) external view returns (uint[] memory actualAmountsOut) {
address[] memory tokens = bPool.getCurrentTokens();
require(minAmountsOut.length == tokens.length, "ERR_AMOUNTS_MISMATCH");
uint poolTotal = self.totalSupply();
uint ratio = DesynSafeMath.bdiv(poolAmountIn, DesynSafeMath.badd(poolTotal, 1));
require(ratio != 0, "ERR_MATH_APPROX");
actualAmountsOut = new uint[](tokens.length);
// This loop contains external calls
// External calls are to math libraries or the underlying pool, so low risk
for (uint i = 0; i < tokens.length; i++) {
address t = tokens[i];
uint bal = bPool.getBalance(t);
// Subtract 1 to ensure any rounding errors favor the pool
uint tokenAmountOut = DesynSafeMath.bmul(ratio, DesynSafeMath.bsub(bal, 1));
require(tokenAmountOut != 0, "ERR_MATH_APPROX");
require(tokenAmountOut >= minAmountsOut[i], "ERR_LIMIT_OUT");
actualAmountsOut[i] = tokenAmountOut;
}
}
// Internal functions
// Check for zero transfer, and make sure it returns true to returnValue
function verifyTokenComplianceInternal(address token) internal {
IERC20(token).safeTransfer(msg.sender, 0);
}
function handleTransferInTokens(
IConfigurableRightsPool self,
IBPool bPool,
address poolToken,
uint actualAmountIn,
uint _actualIssueFee
) external returns (uint issueFeeReceived) {
issueFeeReceived = DesynSafeMath.bmul(actualAmountIn, _actualIssueFee);
uint amount = DesynSafeMath.bsub(actualAmountIn, issueFeeReceived);
_pullUnderlying(bPool, poolToken, msg.sender, amount);
if (_actualIssueFee != 0) {
IERC20(poolToken).safeTransferFrom(msg.sender, address(this), issueFeeReceived);
IERC20(poolToken).safeApprove(self.vaultAddress(), 0);
IERC20(poolToken).safeApprove(self.vaultAddress(), issueFeeReceived);
}
}
function handleClaim(
IConfigurableRightsPool self,
IBPool bPool,
address[] calldata poolTokens,
uint managerFee,
uint timeElapsed,
uint claimPeriod
) external returns (uint[] memory) {
uint[] memory tokensAmount = new uint[](poolTokens.length);
for (uint i = 0; i < poolTokens.length; i++) {
address t = poolTokens[i];
uint tokenBalance = bPool.getBalance(t);
uint tokenAmountOut = tokenBalance.bmul(managerFee).mul(timeElapsed).div(claimPeriod).div(12);
_pushUnderlying(bPool, t, address(this), tokenAmountOut);
IERC20(t).safeApprove(self.vaultAddress(), 0);
IERC20(t).safeApprove(self.vaultAddress(), tokenAmountOut);
tokensAmount[i] = tokenAmountOut;
}
return tokensAmount;
}
function handleFeeClaim(
IConfigurableRightsPool self,
IBPool bPool,
address[] calldata poolTokens,
uint feeRatio,
bool isPerfermance
) external {
if (feeRatio != 0) {
uint[] memory tokensAmount = new uint[](poolTokens.length);
for (uint i = 0; i < poolTokens.length; i++) {
address t = poolTokens[i];
uint currentAmount = bPool.getBalance(t);
uint currentAmountFee = DesynSafeMath.bmul(currentAmount, feeRatio);
_pushUnderlying(bPool, t, address(this), currentAmountFee);
tokensAmount[i] = currentAmountFee;
IERC20(t).safeApprove(self.vaultAddress(), 0);
IERC20(t).safeApprove(self.vaultAddress(), currentAmountFee);
}
if(isPerfermance) {
IVault(self.vaultAddress()).depositIssueRedeemPToken(poolTokens, tokensAmount, new uint[](poolTokens.length), isPerfermance);
} else {
IVault(self.vaultAddress()).depositIssueRedeemPToken(poolTokens, tokensAmount, tokensAmount, isPerfermance);
}
}
}
function WhitelistHandle(
bool bool1,
bool bool2,
address adr
) external pure {
require(bool1, "ERR_CANNOT_WHITELIST_LPS");
require(bool2, "ERR_LP_NOT_WHITELISTED");
require(adr != address(0), "ERR_INVALID_ADDRESS");
}
function _pullUnderlying(
IBPool bPool,
address erc20,
address from,
uint amount
) internal {
uint tokenBalance = bPool.getBalance(erc20);
uint tokenWeight = bPool.getDenormalizedWeight(erc20);
IERC20(erc20).safeTransferFrom(from, address(this), amount);
bPool.rebind(erc20, DesynSafeMath.badd(tokenBalance, amount), tokenWeight);
}
function _pushUnderlying(
IBPool bPool,
address erc20,
address to,
uint amount
) internal {
uint tokenBalance = bPool.getBalance(erc20);
uint tokenWeight = bPool.getDenormalizedWeight(erc20);
bPool.rebind(erc20, DesynSafeMath.bsub(tokenBalance, amount), tokenWeight);
IERC20(erc20).safeTransfer(to, amount);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
// Interface declarations
// Introduce to avoid circularity (otherwise, the CRP and SmartPoolManager include each other)
// Removing circularity allows flattener tools to work, which enables Etherscan verification
interface IConfigurableRightsPool {
function mintPoolShareFromLib(uint amount) external;
function pushPoolShareFromLib(address to, uint amount) external;
function pullPoolShareFromLib(address from, uint amount) external;
function burnPoolShareFromLib(uint amount) external;
function balanceOf(address account) external view returns (uint);
function totalSupply() external view returns (uint);
function adminList(address) external view returns (bool);
function getController() external view returns (address);
function vaultAddress() external view returns (address);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
interface IBPool {
function rebind(
address token,
uint balance,
uint denorm
) external;
function execute(
address _target,
uint _value,
bytes calldata _data
) external returns (bytes memory _returnValue);
function bind(
address token,
uint balance,
uint denorm
) external;
function unbind(address token) external;
function unbindPure(address token) external;
function isBound(address token) external view returns (bool);
function getBalance(address token) external view returns (uint);
function totalSupply() external view returns (uint);
function isPublicSwap() external view returns (bool);
function getDenormalizedWeight(address token) external view returns (uint);
function getTotalDenormalizedWeight() external view returns (uint);
function EXIT_FEE() external view returns (uint);
function getCurrentTokens() external view returns (address[] memory tokens);
function setController(address owner) external;
}
interface IBFactory {
function newLiquidityPool() external returns (IBPool);
function setBLabs(address b) external;
function collect(IBPool pool) external;
function isBPool(address b) external view returns (bool);
function getBLabs() external view returns (address);
function getVault() external view returns (address);
function getUserVault() external view returns (address);
function getVaultAddress() external view returns (address);
function getOracleAddress() external view returns (address);
function isTokenWhitelistedForVerify(uint sort, address token) external view returns (bool);
function isTokenWhitelistedForVerify(address token) external view returns (bool);
function getModuleStatus(address etf, address module) external view returns (bool);
function isPaused() external view returns (bool);
}
interface IVault {
function depositManagerToken(address[] calldata poolTokens, uint[] calldata tokensAmount) external;
function depositIssueRedeemPToken(
address[] calldata poolTokens,
uint[] calldata tokensAmount,
uint[] calldata tokensAmountP,
bool isPerfermance
) external;
function managerClaim(address pool) external;
function getManagerClaimBool(address pool) external view returns (bool);
}
interface IUserVault {
function recordTokenInfo(
address kol,
address user,
address[] calldata poolTokens,
uint[] calldata tokensAmount
) external;
}
interface Oracles {
function getPrice(address tokenAddress) external returns (uint price);
function getAllPrice(address[] calldata poolTokens, uint[] calldata tokensAmount) external returns (uint);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
// Imports
import "./DesynConstants.sol";
/**
* @author Desyn Labs
* @title SafeMath - wrap Solidity operators to prevent underflow/overflow
* @dev badd and bsub are basically identical to OpenZeppelin SafeMath; mul/div have extra checks
*/
library DesynSafeMath {
/**
* @notice Safe addition
* @param a - first operand
* @param b - second operand
* @dev if we are adding b to a, the resulting sum must be greater than a
* @return - sum of operands; throws if overflow
*/
function badd(uint a, uint b) internal pure returns (uint) {
uint c = a + b;
require(c >= a, "ERR_ADD_OVERFLOW");
return c;
}
/**
* @notice Safe unsigned subtraction
* @param a - first operand
* @param b - second operand
* @dev Do a signed subtraction, and check that it produces a positive value
* (i.e., a - b is valid if b <= a)
* @return - a - b; throws if underflow
*/
function bsub(uint a, uint b) internal pure returns (uint) {
(uint c, bool negativeResult) = bsubSign(a, b);
require(!negativeResult, "ERR_SUB_UNDERFLOW");
return c;
}
/**
* @notice Safe signed subtraction
* @param a - first operand
* @param b - second operand
* @dev Do a signed subtraction
* @return - difference between a and b, and a flag indicating a negative result
* (i.e., a - b if a is greater than or equal to b; otherwise b - a)
*/
function bsubSign(uint a, uint b) internal pure returns (uint, bool) {
if (b <= a) {
return (a - b, false);
} else {
return (b - a, true);
}
}
/**
* @notice Safe multiplication
* @param a - first operand
* @param b - second operand
* @dev Multiply safely (and efficiently), rounding down
* @return - product of operands; throws if overflow or rounding error
*/
function bmul(uint a, uint b) internal pure returns (uint) {
// Gas optimization (see github.com/OpenZeppelin/openzeppelin-contracts/pull/522)
if (a == 0) {
return 0;
}
// Standard overflow check: a/a*b=b
uint c0 = a * b;
require(c0 / a == b, "ERR_MUL_OVERFLOW");
// Round to 0 if x*y < BONE/2?
uint c1 = c0 + (DesynConstants.BONE / 2);
require(c1 >= c0, "ERR_MUL_OVERFLOW");
uint c2 = c1 / DesynConstants.BONE;
return c2;
}
/**
* @notice Safe division
* @param dividend - first operand
* @param divisor - second operand
* @dev Divide safely (and efficiently), rounding down
* @return - quotient; throws if overflow or rounding error
*/
function bdiv(uint dividend, uint divisor) internal pure returns (uint) {
require(divisor != 0, "ERR_DIV_ZERO");
// Gas optimization
if (dividend == 0) {
return 0;
}
uint c0 = dividend * DesynConstants.BONE;
require(c0 / dividend == DesynConstants.BONE, "ERR_DIV_INTERNAL"); // bmul overflow
uint c1 = c0 + (divisor / 2);
require(c1 >= c0, "ERR_DIV_INTERNAL"); // badd require
uint c2 = c1 / divisor;
return c2;
}
/**
* @notice Safe unsigned integer modulo
* @dev Returns the remainder of dividing two unsigned integers.
* 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).
*
* @param dividend - first operand
* @param divisor - second operand -- cannot be zero
* @return - quotient; throws if overflow or rounding error
*/
function bmod(uint dividend, uint divisor) internal pure returns (uint) {
require(divisor != 0, "ERR_MODULO_BY_ZERO");
return dividend % divisor;
}
/**
* @notice Safe unsigned integer max
* @dev Returns the greater of the two input values
*
* @param a - first operand
* @param b - second operand
* @return - the maximum of a and b
*/
function bmax(uint a, uint b) internal pure returns (uint) {
return a >= b ? a : b;
}
/**
* @notice Safe unsigned integer min
* @dev returns b, if b < a; otherwise returns a
*
* @param a - first operand
* @param b - second operand
* @return - the lesser of the two input values
*/
function bmin(uint a, uint b) internal pure returns (uint) {
return a < b ? a : b;
}
/**
* @notice Safe unsigned integer average
* @dev Guard against (a+b) overflow by dividing each operand separately
*
* @param a - first operand
* @param b - second operand
* @return - the average of the two values
*/
function baverage(uint a, uint b) internal pure returns (uint) {
// (a + b) / 2 can overflow, so we distribute
return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2);
}
/**
* @notice Babylonian square root implementation
* @dev (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
* @param y - operand
* @return z - the square root result
*/
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
/**
* @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(uint a, uint b) internal pure returns (uint) {
uint 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(uint a, uint b) internal pure returns (uint) {
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(
uint a,
uint b,
string memory errorMessage
) internal pure returns (uint) {
require(b <= a, errorMessage);
uint 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(uint a, uint b) internal pure returns (uint) {
// 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;
}
uint 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(uint a, uint b) internal pure returns (uint) {
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(
uint a,
uint b,
string memory errorMessage
) internal pure returns (uint) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint 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(uint a, uint b) internal pure returns (uint) {
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(
uint a,
uint b,
string memory errorMessage
) internal pure returns (uint) {
require(b != 0, errorMessage);
return a % b;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import {IERC20} from "../interfaces/IERC20.sol";
import {SafeMath} from "./SafeMath.sol";
import {Address} from "./Address.sol";
/**
* @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 uint;
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint value
) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint value
) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(
IERC20 token,
address spender,
uint value
) internal {
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 callOptionalReturn(IERC20 token, bytes memory data) private {
require(address(token).isContract(), "SafeERC20: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "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");
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
/**
* @author Desyn Labs
* @title Put all the constants in one place
*/
library DesynConstants {
// State variables (must be constant in a library)
// where numeric 1 = 10 ** 18
uint public constant BONE = 10**18;
uint public constant MIN_WEIGHT = BONE;
uint public constant MAX_WEIGHT = BONE * 50;
uint public constant MAX_TOTAL_WEIGHT = BONE * 50;
uint public constant MIN_BALANCE = 0;
uint public constant MAX_BALANCE = BONE * 10**12;
uint public constant MIN_POOL_SUPPLY = BONE * 100;
uint public constant MAX_POOL_SUPPLY = BONE * 10**9;
uint public constant MIN_FEE = BONE / 10**6;
uint public constant MAX_FEE = BONE / 10;
//Fee Set
uint public constant MANAGER_MIN_FEE = 0;
uint public constant MANAGER_MAX_FEE = BONE / 10;
uint public constant ISSUE_MIN_FEE = 0;
uint public constant ISSUE_MAX_FEE = BONE / 10;
uint public constant REDEEM_MIN_FEE = 0;
uint public constant REDEEM_MAX_FEE = BONE / 10;
uint public constant PERFERMANCE_MIN_FEE = 0;
uint public constant PERFERMANCE_MAX_FEE = BONE / 2;
// EXIT_FEE must always be zero, or ConfigurableRightsPool._pushUnderlying will fail
uint public constant EXIT_FEE = 0;
uint public constant MAX_IN_RATIO = BONE / 2;
uint public constant MAX_OUT_RATIO = (BONE / 3) + 1 wei;
// Must match BConst.MIN_BOUND_TOKENS and BConst.MAX_BOUND_TOKENS
uint public constant MIN_ASSET_LIMIT = 1;
uint public constant MAX_ASSET_LIMIT = 16;
uint public constant MAX_UINT = uint(-1);
uint public constant MAX_COLLECT_PERIOD = 60 days;
}// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;
/**
* @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) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly {
codehash := extcodehash(account)
}
return (codehash != accountHash && codehash != 0x0);
}
/**
* @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, uint 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");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
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,
uint 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,
uint 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,
uint 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);
}
}
}
}pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "../interfaces/IERC20.sol";
import "../openzeppelin/Ownable.sol";
import "../interfaces/IDSProxy.sol";
import "../libraries/SafeMath.sol";
import "../libraries/Address.sol";
import "../libraries/SafeERC20.sol";
import "../base/Logs.sol";
interface ICRPPool {
function getController() external view returns (address);
enum Etypes {
OPENED,
CLOSED
}
function etype() external view returns (Etypes);
}
interface IDesynOwnable {
function adminList(address adr) external view returns (bool);
function getController() external view returns (address);
function getOwners() external view returns (address[] memory);
function getOwnerPercentage() external view returns (uint[] memory);
function allOwnerPercentage() external view returns (uint);
}
interface IUserVault {
function depositToken(
address pool,
uint types,
address[] calldata poolTokens,
uint[] calldata tokensAmount
) external;
}
interface ICRPFactory {
function isCrp(address addr) external view returns (bool);
}
/**
* @author Desyn Labs
* @title Vault managerFee
*/
contract Vault is Ownable, Logs {
using SafeMath for uint;
using Address for address;
using SafeERC20 for IERC20;
ICRPFactory crpFactory;
address public userVault;
event ManagersClaim(address indexed caller,address indexed pool, address token, uint amount, uint time);
event ManagerClaim(address indexed caller, address indexed pool, address indexed manager, address token, uint amount, uint time);
// pool of tokens
struct PoolTokens {
address[] tokenList;
uint[] managerAmount;
uint[] issueAmount;
uint[] redeemAmount;
uint[] perfermanceAmount;
}
struct PoolStatus {
bool couldManagerClaim;
bool isBlackList;
}
struct ClaimTokens {
address[] tokens;
uint[] amounts;
}
// pool tokens
mapping(address => PoolTokens) poolsTokens;
mapping(address => PoolStatus) public poolsStatus;
mapping (address => ClaimTokens) manageHasClaimed;
// default ratio config
uint public TOTAL_RATIO = 1000;
uint public management_portion = 800;
uint public issuance_portion = 800;
uint public redemption_portion = 800;
uint public performance_portion = 800;
receive() external payable {}
function depositManagerToken(address[] calldata tokensIn, uint[] calldata amountsIn) external {
address pool = msg.sender;
require(crpFactory.isCrp(pool), "ERR_INVALID_POOL_ADDRESS");
require(tokensIn.length == amountsIn.length, "ERR_TOKEN_LENGTH_NOT_MATCH");
_depositTokenIM(0, pool, tokensIn, amountsIn);
poolsStatus[pool].couldManagerClaim = true;
if (_isClosePool(pool)) managerClaim(pool);
}
function depositIssueRedeemPToken(
address[] calldata tokensIn,
uint[] calldata amountsIn,
uint[] calldata tokensAmountIR,
bool isPerfermance
) external {
address pool = msg.sender;
require(crpFactory.isCrp(pool), "ERR_INVALID_POOL_ADDRESS");
require(tokensIn.length == amountsIn.length, "ERR_TOKEN_LENGTH_NOT_MATCH");
isPerfermance
// I-issuce; M-mamager; R-redeem;p-performance
? _depositTokenRP(pool, tokensIn, amountsIn, tokensAmountIR)
: _depositTokenIM(1, pool, tokensIn, amountsIn);
poolsStatus[pool].couldManagerClaim = true;
if (_isClosePool(pool)) managerClaim(pool);
}
function getManagerClaimBool(address pool) external view returns (bool) {
return poolsStatus[pool].couldManagerClaim;
}
function setBlackList(address pool, bool bools) external onlyOwner _logs_ {
poolsStatus[pool].isBlackList = bools;
}
function setUserVaultAdr(address adr) external onlyOwner _logs_ {
require(adr != address(0), "ERR_INVALID_USERVAULT_ADDRESS");
userVault = adr;
}
function setCrpFactory(address adr) external onlyOwner _logs_ {
crpFactory = ICRPFactory(adr);
}
function claimToken(
address token,
address user,
uint amount
) external onlyOwner {
IERC20(token).safeTransfer(user, amount);
}
function claimEther() external payable onlyOwner {
msg.sender.transfer(address(this).balance);
}
function setManagerRatio(uint amount) external onlyOwner _logs_ {
require(amount <= TOTAL_RATIO, "Maximum limit exceeded");
management_portion = amount;
}
function setIssueRatio(uint amount) external onlyOwner _logs_ {
require(amount <= TOTAL_RATIO, "Maximum limit exceeded");
issuance_portion = amount;
}
function setRedeemRatio(uint amount) external onlyOwner _logs_ {
require(amount <= TOTAL_RATIO, "Maximum limit exceeded");
redemption_portion = amount;
}
function setPerfermanceRatio(uint amount) external onlyOwner _logs_{
performance_portion = amount;
}
function managerClaim(address pool) public {
PoolStatus storage status = poolsStatus[pool];
if(status.couldManagerClaim){
require(crpFactory.isCrp(pool), "ERR_INVALID_POOL_ADDRESS");
address managerAddr = ICRPPool(pool).getController();
bool isCloseETF = _isClosePool(pool);
PoolTokens memory tokens = poolsTokens[pool];
address[] memory poolManageTokens = tokens.tokenList;
uint len = poolManageTokens.length;
require(!status.isBlackList, "ERR_POOL_IS_BLACKLIST");
require(len > 0, "ERR_NOT_MANGER_FEE");
require(status.couldManagerClaim, "ERR_MANAGER_COULD_NOT_CLAIM");
status.couldManagerClaim = false;
uint[] memory managerTokenAmount = new uint[](len);
uint[] memory issueTokenAmount = new uint[](len);
uint[] memory redeemTokenAmount = new uint[](len);
uint[] memory perfermanceTokenAmount = new uint[](len);
for (uint i; i < len; i++) {
address t = poolManageTokens[i];
uint b;
(b, managerTokenAmount[i], issueTokenAmount[i], redeemTokenAmount[i], perfermanceTokenAmount[i]) = _computeBalance(i, pool);
if(!isCloseETF) b = b.sub(_getManagerHasClaimed(pool,t));
if(b != 0) _transferHandle(pool, managerAddr, t, b);
}
if (isCloseETF) {
_recordUserVault(pool, poolManageTokens, managerTokenAmount, issueTokenAmount, redeemTokenAmount, perfermanceTokenAmount);
_clearPool(pool);
}
}
}
function getManagerFeeTypes(address pool) external view returns(PoolTokens memory result){
PoolTokens memory tokens = poolsTokens[pool];
address[] memory poolManageTokens = tokens.tokenList;
uint len = poolManageTokens.length;
result.tokenList = tokens.tokenList;
result.managerAmount = new uint[](len);
result.issueAmount = new uint[](len);
result.redeemAmount = new uint[](len);
result.perfermanceAmount = new uint[](len);
for(uint i; i< len; i++){
(,result.managerAmount[i],result.issueAmount[i],result.redeemAmount[i],result.perfermanceAmount[i]) = _computeBalance(i,pool);
}
}
function getUnManagerReward(address pool) external view returns (address[] memory tokensList, uint[] memory amounts) {
PoolTokens memory tokens = poolsTokens[pool];
address[] memory poolManageTokens = tokens.tokenList;
uint len = poolManageTokens.length;
tokensList = new address[](len);
amounts = new uint[](len);
for (uint i; i < len; i++) {
address t = poolManageTokens[i];
tokensList[i] = t;
(amounts[i],,,,) = _computeBalance(i,pool);
amounts[i] = amounts[i].sub(_getManagerHasClaimed(pool, t));
}
}
function _addTokenInPool(address pool, address tokenAddr) internal {
PoolTokens storage tokens = poolsTokens[pool];
tokens.tokenList.push(tokenAddr);
tokens.managerAmount.push(0);
tokens.issueAmount.push(0);
tokens.redeemAmount.push(0);
tokens.perfermanceAmount.push(0);
}
// for old token
function _updateTokenAmountInPool(uint types, address pool, uint tokenIndex, uint amount) internal {
PoolTokens storage tokens = poolsTokens[pool];
if(types == 0) tokens.managerAmount[tokenIndex] = tokens.managerAmount[tokenIndex].add(amount);
else if(types == 1) tokens.issueAmount[tokenIndex] = tokens.issueAmount[tokenIndex].add(amount);
else if(types == 2) tokens.redeemAmount[tokenIndex] = tokens.redeemAmount[tokenIndex].add(amount);
else if(types == 3) tokens.perfermanceAmount[tokenIndex] = tokens.perfermanceAmount[tokenIndex].add(amount);
}
// for new token
function _updateTokenAmountInPool(uint types, address pool, uint amount) internal {
PoolTokens storage tokens = poolsTokens[pool];
uint tokenIndex = tokens.tokenList.length - 1;
if(types == 0) tokens.managerAmount[tokenIndex] = amount;
else if(types == 1) tokens.issueAmount[tokenIndex] = amount;
else if(types == 2) tokens.redeemAmount[tokenIndex] = amount;
else if(types == 3) tokens.perfermanceAmount[tokenIndex] = amount;
}
function _depositTokenIM(
uint types,
address pool,
address[] memory tokensIn,
uint[] memory amountsIn
) internal {
PoolTokens memory tokens = poolsTokens[pool];
uint len = tokensIn.length;
for (uint i; i < len; i++) {
address t = tokensIn[i];
uint b = amountsIn[i];
IERC20(t).safeTransferFrom(msg.sender, address(this), b);
(bool isExit, uint index) = _arrIncludeAddr(tokens.tokenList,t);
if (isExit) {
_updateTokenAmountInPool(types,pool,index,b);
} else {
_addTokenInPool(pool,t);
_updateTokenAmountInPool(types,pool,b);
}
}
}
function _arrIncludeAddr(address[] memory tokens, address target) internal pure returns(bool isInclude, uint index){
for(uint i; i<tokens.length; i++){
if(tokens[i] == target){
isInclude = true;
index = i;
break;
}
}
}
function _depositTokenRP(
address pool,
address[] calldata tokenIns,
uint[] calldata tokensAmount,
uint[] calldata tokensAmountIR
) internal {
address[] memory tokenList = poolsTokens[pool].tokenList;
uint len = tokensAmount.length;
for (uint i; i < len; i++) {
address t = tokenIns[i];
uint b = tokensAmount[i];
// uint bIR = tokensAmountIR[i];
IERC20(t).safeTransferFrom(msg.sender, address(this), b);
(bool isExit,uint index) = _arrIncludeAddr(tokenList, t);
if(isExit){
_updateTokenAmountInPool(2, pool, index, tokensAmountIR[i]);
_updateTokenAmountInPool(3, pool, index, b.sub(tokensAmountIR[i]));
} else {
_addTokenInPool(pool, t);
_updateTokenAmountInPool(2,pool, tokensAmountIR[i]);
_updateTokenAmountInPool(3, pool,b.sub(tokensAmountIR[i]));
}
}
}
function _isClosePool(address pool) internal view returns (bool) {
return ICRPPool(pool).etype() == ICRPPool.Etypes.CLOSED;
}
function _computeBalance(uint i, address pool)
internal
view
returns (
uint balance,
uint bManagerAmount,
uint bIssueAmount,
uint bRedeemAmount,
uint bPerfermanceAmount
)
{
PoolTokens memory tokens = poolsTokens[pool];
if (tokens.tokenList.length != 0) {
bManagerAmount = tokens.managerAmount[i].mul(management_portion).div(TOTAL_RATIO);
bIssueAmount = tokens.issueAmount[i].mul(issuance_portion).div(TOTAL_RATIO);
bRedeemAmount = tokens.redeemAmount[i].mul(redemption_portion).div(TOTAL_RATIO);
bPerfermanceAmount = tokens.perfermanceAmount[i].mul(performance_portion).div(TOTAL_RATIO);
balance = bManagerAmount.add(bIssueAmount).add(bRedeemAmount).add(bPerfermanceAmount);
}
}
function _clearPool(address pool) internal {
delete poolsTokens[pool];
}
function _recordUserVault(
address pool,
address[] memory tokenList,
uint[] memory managerTokenAmount,
uint[] memory issueTokenAmount,
uint[] memory redeemTokenAmount,
uint[] memory perfermanceTokenAmount
) internal {
if (tokenList.length != 0) {
IUserVault(userVault).depositToken(pool, 0, tokenList, managerTokenAmount);
IUserVault(userVault).depositToken(pool, 1, tokenList, issueTokenAmount);
IUserVault(userVault).depositToken(pool, 2, tokenList, redeemTokenAmount);
IUserVault(userVault).depositToken(pool, 3, tokenList, perfermanceTokenAmount);
}
}
function _transferHandle(
address pool,
address managerAddr,
address t,
uint balance
) internal {
require(balance != 0, "ERR_ILLEGAL_BALANCE");
bool isCloseETF = _isClosePool(pool);
bool isOpenETF = !isCloseETF;
if(isCloseETF){
IERC20(t).safeTransfer(userVault, balance);
}
// if(isOpenETF && isContractManager){
if(isOpenETF) {
address[] memory managerAddressList = IDesynOwnable(pool).getOwners();
uint[] memory ownerPercentage = IDesynOwnable(pool).getOwnerPercentage();
uint allOwnerPercentage = IDesynOwnable(pool).allOwnerPercentage();
for (uint k; k < managerAddressList.length; k++) {
address reciver = address(managerAddressList[k]).isContract()? IDSProxy(managerAddressList[k]).owner(): managerAddressList[k];
uint b = balance.mul(ownerPercentage[k]).div(allOwnerPercentage);
IERC20(t).safeTransfer(reciver, b);
emit ManagerClaim(msg.sender, pool, reciver,t,b,block.timestamp);
}
_updateManageHasClaimed(pool,t,balance);
emit ManagersClaim(msg.sender, pool, t, balance, block.timestamp);
}
}
function _updateManageHasClaimed(address pool, address token, uint amount) internal {
ClaimTokens storage claimInfo = manageHasClaimed[pool];
(bool isExit, uint index) = _arrIncludeAddr(claimInfo.tokens, token);
if(isExit){
claimInfo.amounts[index] = claimInfo.amounts[index].add(amount);
} else{
claimInfo.tokens.push(token);
claimInfo.amounts.push(amount);
}
}
function _getManagerHasClaimed(address pool, address token) internal view returns(uint){
require(!_isClosePool(pool),"ERR_NOT_OPEN_POOL");
ClaimTokens memory claimInfo = manageHasClaimed[pool];
(bool isExit,uint index) = _arrIncludeAddr(claimInfo.tokens, token);
if(isExit) return claimInfo.amounts[index];
if(!isExit) return 0;
}
}pragma solidity >=0.6.0 <0.8.0;
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;
}
}
/**
* @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.
*/
abstract 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 virtual 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;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
interface IDSProxy {
function owner() external view returns(address);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
contract Logs {
event LOG_CALL(bytes4 indexed sig, address indexed caller, bytes data) anonymous;
modifier _logs_() {
emit LOG_CALL(msg.sig, msg.sender, msg.data);
_;
}
}pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "../interfaces/IERC20.sol";
import "../interfaces/IDSProxy.sol";
import "../openzeppelin/Ownable.sol";
import "../libraries/SmartPoolManager.sol";
import "../libraries/EnumerableSet.sol";
import "../libraries/SafeMath.sol";
import "../libraries/Address.sol";
import "../libraries/SafeERC20.sol";
import "../base/Logs.sol";
interface ICRPPool {
function getController() external view returns (address);
enum Etypes {
OPENED,
CLOSED
}
function etype() external view returns (Etypes);
function isCompletedCollect() external view returns (bool);
}
interface ICRPFactory {
function isCrp(address addr) external view returns (bool);
}
interface IDesynOwnable {
function adminList(address adr) external view returns (bool);
function getController() external view returns (address);
function getOwners() external view returns (address[] memory);
function getOwnerPercentage() external view returns (uint[] memory);
function allOwnerPercentage() external view returns (uint);
}
/**
* @author Desyn Labs
* @title Vault managerFee
*/
contract UserVault is Ownable, Logs {
using SafeMath for uint;
using Address for address;
using EnumerableSet for EnumerableSet.AddressSet;
using SafeERC20 for IERC20;
event ManagersClaim(address indexed caller,address indexed pool, address token, uint amount, uint time);
event ManagerClaim(address indexed caller,address indexed pool, address indexed manager, address token, uint amount, uint time);
event KolClaim(address indexed caller,address indexed kol, address token, uint amount, uint time);
event TypeAmountIn(address indexed pool, uint types, address caller, address token, uint balance);
ICRPFactory crpFactory;
address vaultAddress;
// pool of tokens
struct PoolTokens {
address[] tokenList;
uint[] managerAmount;
uint[] issueAmount;
uint[] redeemAmount;
uint[] perfermanceAmount;
}
struct PoolStatus {
bool couldManagerClaim;
bool isBlackList;
bool isSetParams;
SmartPoolManager.KolPoolParams kolPoolConfig;
}
// kol list
struct KolUserInfo {
address userAdr;
uint[] userAmount;
}
struct UserKolInfo {
address kol;
uint index;
}
struct ClaimTokens {
address[] tokens;
uint[] amounts;
}
// pool => kol => KolUserInfo[]
mapping(address => mapping(address => KolUserInfo[])) kolUserInfo;
// pool tokens
mapping(address => PoolTokens) poolsTokens;
mapping(address => PoolStatus) poolsStatus;
//pool => initTotalAmount[]
mapping(address => uint) public poolInviteTotal;
//pool => kol[]
mapping(address => EnumerableSet.AddressSet) kolsList;
//pool => kol => totalAmount[]
mapping(address => mapping(address => uint[])) public kolTotalAmountList;
// pool => user => kol
mapping(address => mapping(address => UserKolInfo)) public userKolList;
// pool=>kol=>tokens
mapping(address => mapping(address => ClaimTokens)) kolHasClaimed;
// pool=>manage=>tokens
mapping (address => ClaimTokens) manageHasClaimed;
receive() external payable {}
uint constant RATIO_BASE = 100;
function getManagerClaimBool(address pool) external view returns(bool){
return poolsStatus[pool].couldManagerClaim;
}
// one type call and receiver token
function depositToken(
address pool,
uint types,
address[] calldata tokensIn,
uint[] calldata amountsIn
) external onlyVault {
require(tokensIn.length == amountsIn.length, "ERR_TOKEN_LENGTH_NOT_MATCH");
_updatePool(pool, types, tokensIn, amountsIn);
poolsStatus[pool].couldManagerClaim = true;
}
// total tokens in pool
function getPoolReward(address pool) external view returns (address[] memory tokenList, uint[] memory balances) {
PoolTokens memory tokens = poolsTokens[pool];
uint len = tokens.tokenList.length;
balances = new uint[](len);
tokenList = tokens.tokenList;
for(uint i; i<len ;i++){
balances[i] = tokens.managerAmount[i]
.add(tokens.issueAmount[i])
.add(tokens.redeemAmount[i])
.add(tokens.perfermanceAmount[i]);
}
}
struct RewardVars {
address pool;
uint t0Ratio;
uint t1Ratio;
uint t2Ratio;
uint t3Ratio;
uint[] managementAmounts;
uint[] issueAmounts;
uint[] redemptionAmounts;
uint[] performanceAmounts;
}
// one kol total reward
function getKolReward(
address pool,
address kol
) external view returns (address[] memory tokenList, uint[] memory balances) {
uint contributionByCurKol = kolTotalAmountList[pool][kol].length > 0 ? kolTotalAmountList[pool][kol][0] : 0;
uint allContributionByKol = poolInviteTotal[pool];
SmartPoolManager.KolPoolParams memory params = poolsStatus[pool].kolPoolConfig;
PoolTokens memory tokens = poolsTokens[pool];
balances = new uint[](tokens.tokenList.length);
tokenList = tokens.tokenList;
RewardVars memory vars = RewardVars(
pool,
_levelJudge(contributionByCurKol, params.managerFee),
_levelJudge(contributionByCurKol, params.issueFee),
_levelJudge(contributionByCurKol, params.redeemFee),
_levelJudge(contributionByCurKol, params.perfermanceFee),
tokens.managerAmount,
tokens.issueAmount,
tokens.redeemAmount,
tokens.perfermanceAmount
);
for(uint i; i < tokenList.length; i++){
balances[i] = vars.managementAmounts[i].mul(vars.t0Ratio).div(RATIO_BASE)
.add(vars.issueAmounts[i].mul(vars.t1Ratio).div(RATIO_BASE))
.add(vars.redemptionAmounts[i].mul(vars.t2Ratio).div(RATIO_BASE))
.add(vars.performanceAmounts[i].mul(vars.t3Ratio).div(RATIO_BASE))
.mul(contributionByCurKol)
.div(allContributionByKol);
}
}
function kolClaim(address pool) external {
if (_isClosePool(pool)) {
require(crpFactory.isCrp(pool), "ERR_INVALID_POOL_ADDRESS");
require(ICRPPool(pool).isCompletedCollect(), "ERR_NOT_COMPLETED_COLLECT");
(address[] memory tokens, uint[] memory amounts) = this.getKolReward(pool, msg.sender);
ClaimTokens storage kolClaimedInfo = kolHasClaimed[pool][msg.sender];
// update length
kolClaimedInfo.tokens = tokens;
uint amountsLen = kolClaimedInfo.amounts.length;
uint tokensLen = tokens.length;
if(amountsLen != tokensLen){
uint delta = tokensLen - amountsLen;
for(uint i; i < delta; i++){
kolClaimedInfo.amounts.push(0);
}
}
address receiver = address(msg.sender).isContract()? IDSProxy(msg.sender).owner(): msg.sender;
for(uint i; i< tokens.length; i++) {
uint b = amounts[i] - kolClaimedInfo.amounts[i];
if(b != 0){
IERC20(tokens[i]).safeTransfer(receiver, b);
kolClaimedInfo.amounts[i] = kolClaimedInfo.amounts[i].add(b);
emit KolClaim(msg.sender,receiver,tokens[i],b,block.timestamp);
}
}
}
}
// manager claim
function managerClaim(address pool) external {
// try {} catch {}
if (_isClosePool(pool) && poolsStatus[pool].couldManagerClaim) {
bool isManager = IDesynOwnable(pool).adminList(msg.sender) || IDesynOwnable(pool).getController() == msg.sender;
bool isCollectSuccee = ICRPPool(pool).isCompletedCollect();
require(isCollectSuccee, "ERR_NOT_COMPLETED_COLLECT");
require(isManager, "ERR_NOT_MANAGER");
(address[] memory tokens, uint[] memory amounts) = this.getUnManagerReward(pool);
poolsStatus[pool].couldManagerClaim = false;
ClaimTokens storage manageHasClimed = manageHasClaimed[pool];
// update length
manageHasClimed.tokens = tokens;
uint amountsLen = manageHasClimed.amounts.length;
uint tokensLen = tokens.length;
if(amountsLen != tokensLen){
uint delta = tokensLen - amountsLen;
for(uint i; i < delta; i++){
manageHasClimed.amounts.push(0);
}
}
// update tokens
for(uint i; i< tokens.length; i++){
address t = tokens[i];
if(amounts[i]!=0){
_transferHandle(pool, t, amounts[i]);
manageHasClimed.amounts[i] = manageHasClimed.amounts[i].add(amounts[i]);
}
}
}
}
function getManagerReward(address pool) external view returns (address[] memory, uint[] memory) {
(address[] memory totalTokens, uint[] memory totalFee) = this.getPoolReward(pool);
(, uint[] memory kolFee) = this.getKolsReward(pool);
uint len = totalTokens.length;
uint[] memory balances = new uint[](len);
for(uint i; i<len; i++){
balances[i] = totalFee[i] - kolFee[i];
}
return (totalTokens, balances);
}
// for all manager
function getUnManagerReward(address pool) external returns (address[] memory, uint[] memory) {
(address[] memory totalTokens, uint[] memory totalAmounts) = this.getManagerReward(pool);
ClaimTokens storage manageHasClimed = manageHasClaimed[pool];
// update length
manageHasClimed.tokens = totalTokens;
uint amountsLen = manageHasClimed.amounts.length;
uint tokensLen = totalTokens.length;
if(amountsLen != tokensLen){
uint delta = tokensLen - amountsLen;
for(uint i; i < delta; i++){
manageHasClimed.amounts.push(0);
}
}
uint len = totalTokens.length;
uint[] memory balances = new uint[](len);
for(uint i; i < totalTokens.length; i++){
balances[i] = totalAmounts[i] - manageHasClimed.amounts[i];
}
return (totalTokens,balances);
}
function getPoolFeeTypes(address pool) external view returns(PoolTokens memory result){
return poolsTokens[pool];
}
function getManagerFeeTypes(address pool) external view returns(PoolTokens memory result){
result = this.getPoolFeeTypes(pool);
PoolTokens memory allKolFee = _getKolsFeeTypes(pool);
uint len = result.tokenList.length;
for(uint i; i< len; i++){
result.managerAmount[i] = result.managerAmount[i].sub(allKolFee.managerAmount[i]);
result.issueAmount[i] = result.issueAmount[i].sub(allKolFee.issueAmount[i]);
result.redeemAmount[i] = result.redeemAmount[i].sub(allKolFee.redeemAmount[i]);
result.perfermanceAmount[i] = result.perfermanceAmount[i].sub(allKolFee.perfermanceAmount[i]);
}
}
function _getKolsFeeTypes(address pool) internal view returns(PoolTokens memory result) {
PoolTokens memory poolInfo = poolsTokens[pool];
uint len = poolInfo.tokenList.length;
result.tokenList = poolInfo.tokenList;
EnumerableSet.AddressSet storage list = kolsList[pool];
uint kolLen = list.length();
// init result
result.managerAmount = new uint[](len);
result.issueAmount = new uint[](len);
result.redeemAmount = new uint[](len);
result.perfermanceAmount = new uint[](len);
for(uint types; types<4; types++){
for(uint i; i<len; i++){
for (uint j; j < kolLen; j++) {
if(types == 0) result.managerAmount[i] = result.managerAmount[i].add(_computeKolTotalReward(pool, list.at(j), 0, i));
else if(types == 1) result.issueAmount[i] = result.issueAmount[i].add(_computeKolTotalReward(pool, list.at(j), 1, i));
else if(types == 2) result.redeemAmount[i] = result.redeemAmount[i].add(_computeKolTotalReward(pool, list.at(j), 2, i));
else if(types == 3) result.perfermanceAmount[i] = result.perfermanceAmount[i].add(_computeKolTotalReward(pool, list.at(j), 3, i));
}
}
}
}
function getKolFeeType(address pool, address kol) external view returns(PoolTokens memory result) {
PoolTokens memory poolInfo = poolsTokens[pool];
result.tokenList = poolInfo.tokenList;
uint len = poolInfo.tokenList.length;
// init result
result.managerAmount = new uint[](len);
result.issueAmount = new uint[](len);
result.redeemAmount = new uint[](len);
result.perfermanceAmount = new uint[](len);
// more for to save gas
for(uint i; i<len; i++){
result.managerAmount[i] = result.managerAmount[i].add(_computeKolTotalReward(pool, kol, 0, i));
result.issueAmount[i] = result.issueAmount[i].add(_computeKolTotalReward(pool, kol, 1, i));
result.redeemAmount[i] = result.redeemAmount[i].add(_computeKolTotalReward(pool, kol, 2, i));
result.perfermanceAmount[i] = result.perfermanceAmount[i].add(_computeKolTotalReward(pool, kol, 3, i));
}
}
function getKolsReward(address pool) external view returns (address[] memory, uint[] memory) {
EnumerableSet.AddressSet storage list = kolsList[pool];
uint len = list.length();
address[] memory tokens = poolsTokens[pool].tokenList;
uint[] memory balances = new uint[](tokens.length);
for (uint i = 0; i < len; i++) {
(, uint[] memory singleReward) = this.getKolReward(pool, list.at(i));
for(uint k; k < singleReward.length; k++){
balances[k] = balances[k] + singleReward[k];
}
}
return (tokens,balances);
}
function getUnKolReward(address pool, address kol) external returns (address[] memory,uint[] memory) {
(address[] memory totalTokens, uint[] memory totalReward) = this.getKolReward(pool, kol);
ClaimTokens storage singleKolHasReward = kolHasClaimed[pool][kol];
// update length
singleKolHasReward.tokens = totalTokens;
uint amountsLen = singleKolHasReward.amounts.length;
uint tokensLen = totalTokens.length;
if(amountsLen != tokensLen){
uint delta = tokensLen - amountsLen;
for(uint i; i < delta; i++){
singleKolHasReward.amounts.push(0);
}
}
uint len = totalTokens.length;
uint[] memory balances = new uint[](len);
for(uint i; i<len; i++){
balances[i] = totalReward[i] - singleKolHasReward.amounts[i];
}
return (totalTokens, balances);
}
function recordTokenInfo(
address kol,
address user,
address[] calldata poolTokens,
uint[] calldata tokensAmount
) external {
address pool = msg.sender;
uint len = poolTokens.length;
require(len == tokensAmount.length, "ERR_TOKEN_LENGTH_NOT_MATCH");
require(crpFactory.isCrp(pool), "ERR_INVALID_POOL_ADDRESS");
UserKolInfo storage userKolBind = userKolList[pool][user];
if (userKolBind.kol == address(0)) {
userKolBind.kol = kol;
if (!kolsList[pool].contains(kol)) kolsList[pool].addValue(kol);
}
address newKol = userKolBind.kol;
require(newKol != address(0), "ERR_INVALID_KOL_ADDRESS");
//total amount record
poolInviteTotal[pool] = poolInviteTotal[pool].add(tokensAmount[0]);
uint[] memory totalAmounts = new uint[](len);
for (uint i; i < len; i++) {
bool kolHasInvitations = kolTotalAmountList[pool][newKol].length == 0;
kolHasInvitations
? totalAmounts[i] = tokensAmount[i]
: totalAmounts[i] = tokensAmount[i].add(kolTotalAmountList[pool][newKol][i]);
}
kolTotalAmountList[pool][newKol] = totalAmounts;
//kol user info record
KolUserInfo[] storage userInfoArray = kolUserInfo[pool][newKol];
uint index = userKolBind.index;
if (index == 0) {
KolUserInfo memory userInfo;
userInfo.userAdr = user;
userInfo.userAmount = tokensAmount;
userInfoArray.push(userInfo);
userKolBind.index = userInfoArray.length;
} else {
KolUserInfo storage userInfo = kolUserInfo[pool][newKol][index - 1];
for (uint a; a < userInfo.userAmount.length; a++) {
userInfo.userAmount[a] = userInfo.userAmount[a].add(tokensAmount[a]);
}
}
}
function setPoolParams(address pool, SmartPoolManager.KolPoolParams memory _poolParams) external onlyCrpFactory {
PoolStatus storage status = poolsStatus[pool];
require(crpFactory.isCrp(pool), "ERR_INVALID_POOL_ADDRESS");
require(!status.isSetParams, "ERR_HAS_SETED");
status.isSetParams = true;
status.kolPoolConfig = _poolParams;
}
// function _getRatioTotal(address pool, uint types) internal view returns(uint){
// SmartPoolManager.KolPoolParams memory params = poolsStatus[pool].kolPoolConfig;
// if(types == 0) return params.managerFee.firstLevel.ratio.add(params.managerFee.secondLevel.ratio).add(params.managerFee.thirdLevel.ratio).add(params.managerFee.fourLevel.ratio);
// else if(types == 1) return params.issueFee.firstLevel.ratio.add(params.issueFee.secondLevel.ratio).add(params.issueFee.thirdLevel.ratio).add(params.issueFee.fourLevel.ratio);
// else if(types == 2) return params.redeemFee.firstLevel.ratio.add(params.redeemFee.secondLevel.ratio).add(params.redeemFee.thirdLevel.ratio).add(params.redeemFee.fourLevel.ratio);
// else if(types == 3) return params.perfermanceFee.firstLevel.ratio.add(params.perfermanceFee.secondLevel.ratio).add(params.perfermanceFee.thirdLevel.ratio).add(params.perfermanceFee.fourLevel.ratio);
// }
function getKolsAdr(address pool) external view returns (address[] memory) {
return kolsList[pool].values();
}
function getPoolConfig(address pool) external view returns (SmartPoolManager.KolPoolParams memory) {
return poolsStatus[pool].kolPoolConfig;
}
function setBlackList(address pool, bool bools) external onlyOwner _logs_ {
poolsStatus[pool].isBlackList = bools;
}
function setCrpFactory(address adr) external onlyOwner _logs_ {
crpFactory = ICRPFactory(adr);
}
function claimToken(
address token,
address user,
uint amount
) external onlyOwner {
IERC20(token).safeTransfer(user, amount);
}
function claimEther() external payable onlyOwner {
msg.sender.transfer(address(this).balance);
}
function setVaultAdr(address adr) external onlyOwner _logs_ {
vaultAddress = adr;
}
function getKolHasClaimed(address pool,address kol) external view returns(ClaimTokens memory) {
return kolHasClaimed[pool][kol];
}
function getManageHasClaimed(address pool) external view returns(ClaimTokens memory) {
return manageHasClaimed[pool];
}
function getKolUserInfo(address pool, address kol) external view returns (KolUserInfo[] memory) {
return kolUserInfo[pool][kol];
}
function getUserKolInfo(address pool, address user) external view returns (UserKolInfo memory) {
return userKolList[pool][user];
}
function _updatePool(
address pool,
uint types,
address[] memory tokenIn,
uint[] memory amountIn
) internal {
PoolTokens storage tokens = poolsTokens[pool];
for(uint i; i < tokenIn.length; i++){
address t = tokenIn[i];
uint b = amountIn[i];
(bool isExit,uint index) = _arrIncludeAddr(tokens.tokenList, t);
// update token and init value
if(!isExit){
tokens.tokenList.push(t);
tokens.managerAmount.push(0);
tokens.issueAmount.push(0);
tokens.redeemAmount.push(0);
tokens.perfermanceAmount.push(0);
index = tokens.tokenList.length -1;
}
// update valut
if(b != 0){
if(types == 0) tokens.managerAmount[index] = tokens.managerAmount[index].add(b);
else if(types == 1) tokens.issueAmount[index] = tokens.issueAmount[index].add(b);
else if(types == 2) tokens.redeemAmount[index] = tokens.redeemAmount[index].add(b);
else if(types == 3) tokens.perfermanceAmount[index] = tokens.perfermanceAmount[index].add(b);
emit TypeAmountIn(pool, types, msg.sender, t, b);
}
}
}
function _arrIncludeAddr(address[] memory tokens, address target) internal pure returns(bool isInclude, uint index){
for(uint i; i<tokens.length; i++){
if(tokens[i] == target){
isInclude = true;
index = i;
break;
}
}
}
function _transferHandle(
address pool,
address t,
uint balance
) internal {
require(balance != 0, "ERR_ILLEGAL_BALANCE");
address[] memory managerAddressList = IDesynOwnable(pool).getOwners();
uint[] memory ownerPercentage = IDesynOwnable(pool).getOwnerPercentage();
uint allOwnerPercentage = IDesynOwnable(pool).allOwnerPercentage();
for (uint k = 0; k < managerAddressList.length; k++) {
address reciver = address(managerAddressList[k]).isContract()? IDSProxy(managerAddressList[k]).owner(): managerAddressList[k];
uint b = balance.mul(ownerPercentage[k]).div(allOwnerPercentage);
IERC20(t).safeTransfer(reciver, b);
emit ManagerClaim(msg.sender, pool, reciver,t,b,block.timestamp);
}
emit ManagersClaim(msg.sender,pool,t,balance,block.timestamp);
}
function _levelJudge(uint amount, SmartPoolManager.feeParams memory _feeParams) internal pure returns (uint) {
if (_feeParams.firstLevel.level <= amount && amount < _feeParams.secondLevel.level) return _feeParams.firstLevel.ratio;
else if (_feeParams.secondLevel.level <= amount && amount < _feeParams.thirdLevel.level) return _feeParams.secondLevel.ratio;
else if (_feeParams.thirdLevel.level <= amount && amount < _feeParams.fourLevel.level) return _feeParams.thirdLevel.ratio;
else if (_feeParams.fourLevel.level <= amount) return _feeParams.fourLevel.ratio;
return 0;
}
function _isClosePool(address pool) internal view returns (bool) {
return ICRPPool(pool).etype() == ICRPPool.Etypes.CLOSED;
}
function _computeKolTotalReward(
address pool,
address kol,
uint types,
uint tokenIndex
) internal view returns (uint totalFee) {
uint kolTotalAmount = kolTotalAmountList[pool][kol].length > 0 ? kolTotalAmountList[pool][kol][0] : 0;
SmartPoolManager.KolPoolParams memory params = poolsStatus[pool].kolPoolConfig;
PoolTokens memory tokens = poolsTokens[pool];
if(kolTotalAmount == 0 || tokens.tokenList.length == 0) return 0;
uint allKolTotalAmount = poolInviteTotal[pool];
if (types == 0) totalFee = tokens.managerAmount[tokenIndex].mul(_levelJudge(kolTotalAmount, params.managerFee)).div(RATIO_BASE);
else if (types == 1) totalFee = tokens.issueAmount[tokenIndex].mul(_levelJudge(kolTotalAmount, params.issueFee)).div(RATIO_BASE);
else if (types == 2) totalFee = tokens.redeemAmount[tokenIndex].mul(_levelJudge(kolTotalAmount, params.redeemFee)).div(RATIO_BASE);
else if (types == 3) totalFee = tokens.perfermanceAmount[tokenIndex].mul(_levelJudge(kolTotalAmount, params.perfermanceFee)).div(RATIO_BASE);
return totalFee.mul(kolTotalAmount).div(allKolTotalAmount);
}
modifier onlyCrpFactory() {
require(address(crpFactory) == msg.sender, "ERR_NOT_CRP_FACTORY");
_;
}
modifier onlyVault() {
require(vaultAddress == msg.sender, "ERR_NOT_CONTROLLER");
_;
}
}pragma solidity 0.6.12;
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 => uint) _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
uint 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}.
uint toDeleteIndex = valueIndex - 1;
uint lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
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] = valueIndex; // Replace lastvalue's index to valueIndex
}
// 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 (uint) {
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, uint index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// 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 addValue(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint(uint160(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(uint(uint160(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(uint(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint) {
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, uint index) internal view returns (address) {
return address(uint160(uint(_at(set._inner, index))));
}
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
import {RightsManager} from "../libraries/RightsManager.sol";
import {SmartPoolManager} from "../libraries/SmartPoolManager.sol";
import "../base/Logs.sol";
// Needed to handle structures externally
pragma experimental ABIEncoderV2;
// Imports
abstract contract IConfigurableRightsPool {
struct PoolParams {
string poolTokenSymbol;
string poolTokenName;
address[] constituentTokens;
uint[] tokenBalances;
uint[] tokenWeights;
uint managerFee;
uint redeemFee;
uint issueFee;
uint perfermanceFee;
SmartPoolManager.Etypes etype;
}
struct CrpParams {
uint initialSupply;
uint collectPeriod;
SmartPoolManager.Period period;
}
function setController(address owner) external virtual;
function init(
address factoryAddress,
IConfigurableRightsPool.PoolParams calldata poolParams,
RightsManager.Rights calldata rights
) external virtual;
function initHandle(address[] memory owners, uint[] memory ownerPercentage) external virtual;
}
interface IUserVault {
function setPoolParams(address pool, SmartPoolManager.KolPoolParams memory kolPoolParams) external;
}
interface ICRPFactory {
function isCrp(address addr) external view returns (bool);
}
// Contracts
/**
* @author Desyn Labs
* @title Configurable Rights Pool Factory - create parameterized smart pools
* @dev Rights are held in a corresponding struct in ConfigurableRightsPool
* Index values are as follows:
* by default, it is off on initialization and can only be turned on
* 4: canWhitelistLPs - if set, only whitelisted addresses can join pools
* (enables private pools with more than one LP)
* 5: canChangeCap - can change the BSP cap (max # of pool tokens)
*/
contract CRPFactory is Logs {
// State variables
// Keep a list of all Configurable Rights Pools
mapping(address => bool) private _isCrp;
// Event declarations
// Log the address of each new smart pool, and its creator
event LogNewCrp(address indexed caller, address indexed pool);
event LOG_USER_VAULT(address indexed vault, address indexed caller);
event LOG_MIDDLEWARE(address indexed middleware, address indexed caller);
event AddCRPFactory(address caller, address indexed factory);
event RemoveCRPFactory(address caller, address indexed factory);
uint private counters;
bytes public bytecodes;
address private _blabs = msg.sender;
address public userVault;
address[] public CRPFactorys;
// constructor(bytes memory _bytecode) public {
// bytecodes = _bytecode;
// _blabs = msg.sender;
// }
function createPool(IConfigurableRightsPool.PoolParams calldata poolParams) internal returns (address base) {
bytes memory bytecode = bytecodes;
bytes memory deploymentData = abi.encodePacked(bytecode, abi.encode(poolParams.poolTokenSymbol, poolParams.poolTokenName));
bytes32 salt = keccak256(abi.encodePacked(counters++));
assembly {
base := create2(0, add(deploymentData, 32), mload(deploymentData), salt)
if iszero(extcodesize(base)) {
revert(0, 0)
}
}
}
// Function declarations
/**
* @notice Create a new CRP
* @dev emits a LogNewCRP event
* @param factoryAddress - the BFactory instance used to create the underlying pool
* @param poolParams - struct containing the names, tokens, weights, balances, and swap fee
* @param rights - struct of permissions, configuring this CRP instance (see above for definitions)
*/
function newCrp(
address factoryAddress,
IConfigurableRightsPool.PoolParams calldata poolParams,
RightsManager.Rights calldata rights,
SmartPoolManager.KolPoolParams calldata kolPoolParams,
address[] memory owners,
uint[] memory ownerPercentage
) external returns (IConfigurableRightsPool) {
// require(poolParams.constituentTokens.length >= DesynConstants.MIN_ASSET_LIMIT, "ERR_TOO_FEW_TOKENS");
// Arrays must be parallel
address crp = createPool(poolParams);
emit LogNewCrp(msg.sender, crp);
_isCrp[crp] = true;
IConfigurableRightsPool(crp).init(factoryAddress, poolParams, rights);
IUserVault(userVault).setPoolParams(crp, kolPoolParams);
// The caller is the controller of the CRP
// The CRP will be the controller of the underlying Core BPool
IConfigurableRightsPool(crp).setController(msg.sender);
IConfigurableRightsPool(crp).initHandle(owners, ownerPercentage);
return IConfigurableRightsPool(crp);
}
modifier onlyBlabs() {
require(msg.sender == _blabs, "ERR_NOT_BLABS");
_;
}
function setUserVault(address newVault) external onlyBlabs {
userVault = newVault;
emit LOG_USER_VAULT(newVault, msg.sender);
}
function setByteCodes(bytes memory _bytecodes) external onlyBlabs _logs_ {
bytecodes = _bytecodes;
}
/**
* @notice Check to see if a given address is a CRP
* @param addr - address to check
* @return boolean indicating whether it is a CRP
*/
function isCrp(address addr) external view returns (bool) {
if(_isCrp[addr]) return _isCrp[addr];
for(uint i=0; i<CRPFactorys.length; i++){
if(ICRPFactory(CRPFactorys[i]).isCrp(addr)) return true;
}
}
/**
* @notice add CRPFactory
* @param _crpFactory CRPFactory address
* @dev add CRPFactory
*/
function addCRPFactory(address _crpFactory) external onlyBlabs {
require(_crpFactory != address(this),"ERR_CAN_NOT_ADD_SELF");
uint len = CRPFactorys.length;
for(uint i=0; i<len; i++){
require(CRPFactorys[i] != _crpFactory, "ERR_HAS_BEEN_ADDED");
}
CRPFactorys.push(_crpFactory);
emit AddCRPFactory(msg.sender, _crpFactory);
}
/**
* @notice remove CRPFactory
* @param _crpFactory CRPFactory address
* @dev remove CRPFactory
*/
function removeCRPFactory(address _crpFactory) external onlyBlabs {
uint len = CRPFactorys.length;
for(uint i=0; i<len; i++){
if(CRPFactorys[i] == _crpFactory){
CRPFactorys[i] = CRPFactorys[len-1];
CRPFactorys.pop();
emit RemoveCRPFactory(msg.sender, _crpFactory);
break;
}
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
// Needed to handle structures externally
pragma experimental ABIEncoderV2;
/**
* @author Desyn Labs
* @title Manage Configurable Rights for the smart pool
* by default, it is off on initialization and can only be turned on
* canWhitelistLPs - can limit liquidity providers to a given set of addresses
* canChangeCap - can change the BSP cap (max # of pool tokens)
* canChangeFloor - can change the BSP floor for Closure ETF (min # of pool tokens)
*/
library RightsManager {
// Type declarations
enum Permissions {
WHITELIST_LPS,
TOKEN_WHITELISTS
}
struct Rights {
bool canWhitelistLPs;
bool canTokenWhiteLists;
}
// State variables (can only be constants in a library)
bool public constant DEFAULT_CAN_WHITELIST_LPS = false;
bool public constant DEFAULT_CAN_TOKEN_WHITELISTS = false;
// bool public constant DEFAULT_CAN_CHANGE_CAP = false;
// bool public constant DEFAULT_CAN_CHANGE_FLOOR = false;
// Functions
/**
* @notice create a struct from an array (or return defaults)
* @dev If you pass an empty array, it will construct it using the defaults
* @param a - array input
* @return Rights struct
*/
function constructRights(bool[] calldata a) external pure returns (Rights memory) {
if (a.length < 2) {
return
Rights(
DEFAULT_CAN_WHITELIST_LPS,
DEFAULT_CAN_TOKEN_WHITELISTS
);
} else {
// return Rights(a[0], a[1], a[2], a[3], a[4], a[5], a[6]);
return Rights(a[0], a[1]);
}
}
/**
* @notice Convert rights struct to an array (e.g., for events, GUI)
* @dev avoids multiple calls to hasPermission
* @param rights - the rights struct to convert
* @return boolean array containing the rights settings
*/
function convertRights(Rights calldata rights) external pure returns (bool[] memory) {
bool[] memory result = new bool[](2);
result[0] = rights.canWhitelistLPs;
result[1] = rights.canTokenWhiteLists;
return result;
}
// Though it is actually simple, the number of branches triggers code-complexity
/* solhint-disable code-complexity */
/**
* @notice Externally check permissions using the Enum
* @param self - Rights struct containing the permissions
* @param permission - The permission to check
* @return Boolean true if it has the permission
*/
function hasPermission(Rights calldata self, Permissions permission) external pure returns (bool) {
if (Permissions.WHITELIST_LPS == permission) {
return self.canWhitelistLPs;
} else if (Permissions.TOKEN_WHITELISTS == permission) {
return self.canTokenWhiteLists;
}
}
/* solhint-enable code-complexity */
}pragma solidity 0.6.12;
interface IWETH {
function balanceOf(address account) external view returns (uint);
function deposit() external payable;
function withdraw(uint) external;
function approve(address guy, uint wad) external returns (bool);
function transferFrom(
address src,
address dst,
uint wad
) external returns (bool);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
/**
* @author Desyn Labs (and OpenZeppelin)
* @title Protect against reentrant calls (and also selectively protect view functions)
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {_lock_} 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 `_lock_` guard, functions marked as
* `_lock_` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `_lock_` entry
* points to them.
*
* Also adds a _lockview_ modifier, which doesn't create a lock, but fails
* if another _lock_ call is in progress
*/
contract DesynReentrancyGuard {
// 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.
uint private constant _NOT_ENTERED = 1;
uint private constant _ENTERED = 2;
uint private _status;
constructor() internal {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `_lock_` function from another `_lock_`
* function is not supported. It is possible to prevent this from happening
* by making the `_lock_` function external, and make it call a
* `private` function that does the actual work.
*/
modifier lock() {
// On the first call to _lock_, _notEntered will be true
require(_status != _ENTERED, "ERR_REENTRY");
// Any calls to _lock_ 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;
}
/**
* @dev Also add a modifier that doesn't create a lock, but protects functions that
* should not be called while a _lock_ function is running
*/
modifier viewlock() {
require(_status != _ENTERED, "ERR_REENTRY_VIEW");
_;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
// Needed to handle structures externally
pragma experimental ABIEncoderV2;
import "./PCToken.sol";
import "../utils/DesynReentrancyGuard.sol";
import "../utils/DesynOwnable.sol";
import "../interfaces/IBFactory.sol";
import {RightsManager} from "../libraries/RightsManager.sol";
import "../libraries/SmartPoolManager.sol";
import "../libraries/SafeApprove.sol";
import "./WhiteToken.sol";
import "../libraries/SafeERC20.sol";
import '../libraries/Address.sol';
/**
* @author Desyn Labs
* @title Smart Pool with customizable features
* @notice PCToken is the "Desyn Smart Pool" token (transferred upon finalization)
* @dev Rights are defined as follows (index values into the array)
* Note that functions called on bPool and bFactory may look like internal calls,
* but since they are contracts accessed through an interface, they are really external.
* To make this explicit, we could write "IBPool(address(bPool)).function()" everywhere,
* instead of "bPool.function()".
*/
contract ConfigurableRightsPool is PCToken, DesynOwnable, DesynReentrancyGuard, WhiteToken {
using DesynSafeMath for uint;
using SafeERC20 for IERC20;
using Address for address;
// State variables
IBFactory public bFactory;
IBPool public bPool;
// Struct holding the rights configuration
RightsManager.Rights public rights;
SmartPoolManager.Status public etfStatus;
SmartPoolManager.Fund beginFund;
SmartPoolManager.Fund endFund;
// Store the list of tokens in the pool, and balances
// NOTE that the token list is *only* used to store the pool tokens between
// construction and createPool - thereafter, use the underlying BPool's list
// (avoids synchronization issues)
address[] private _initialTokens;
uint[] private _initialBalances;
uint[] private _initialWeights;
// Whitelist of LPs (if configured)
mapping(address => bool) private _liquidityProviderWhitelist;
uint public constant CLAIM_PERIOD = 30 days;
address public vaultAddress;
bool hasSetWhiteTokens;
bool initBool;
bool public isCompletedCollect;
bool public hasSetBeginFund;
bool public hasSetEndFund;
bool public hasClaimManageFee;
SmartPoolManager.Etypes public etype;
// Event declarations
// Anonymous logger event - can only be filtered by contract address
event LogCall(bytes4 indexed sig, address indexed caller, bytes data) anonymous;
event LogJoin(address indexed caller, address indexed tokenIn, uint tokenAmountIn);
event LogExit(address indexed caller, address indexed tokenOut, uint tokenAmountOut);
event SizeChanged(address indexed caller, string indexed sizeType, uint oldSize, uint newSize);
event PoolTokenInit(address indexed caller, address pool, address initToken, uint initTokenTotal, uint initShare);
event SetManagerFee(uint indexed managerFee, uint indexed issueFee, uint indexed redeemFee, uint perfermanceFee);
// Modifiers
modifier logs() {
emit LogCall(msg.sig, msg.sender, msg.data);
_;
}
// Mark functions that require delegation to the underlying Pool
modifier needsBPool() {
require(address(bPool) != address(0), "ERR_NOT_CREATED");
_;
}
modifier notPaused() {
require(!bFactory.isPaused(), "!paused");
_;
}
constructor(string memory tokenSymbol, string memory tokenName) public PCToken(tokenSymbol, tokenName) {}
/**
* @notice Construct a new Configurable Rights Pool (wrapper around BPool)
* @dev _initialTokens are only used for temporary storage between construction
* and create pool, and should not be used thereafter! _initialTokens is destroyed in
* createPool to prevent this is kept in sync (defensively), but
* should never be used except in this constructor and createPool()
* @param factoryAddress - the BPoolFactory used to create the underlying pool
* @param poolParams - struct containing pool parameters
* @param rightsStruct - Set of permissions we are assigning to this smart pool
*/
function init(
address factoryAddress,
SmartPoolManager.PoolParams memory poolParams,
RightsManager.Rights memory rightsStruct
) public {
SmartPoolManager.initRequire(
poolParams.managerFee,
poolParams.issueFee,
poolParams.redeemFee,
poolParams.perfermanceFee,
poolParams.tokenBalances.length,
poolParams.tokenWeights.length,
poolParams.constituentTokens.length,
initBool
);
initBool = true;
rights = rightsStruct;
_initialTokens = poolParams.constituentTokens;
_initialBalances = poolParams.tokenBalances;
_initialWeights = poolParams.tokenWeights;
etfStatus = SmartPoolManager.Status({
collectPeriod: 0,
collectEndTime: 0,
closurePeriod: 0,
closureEndTime: 0,
upperCap: DesynConstants.MAX_UINT,
floorCap: 0,
managerFee: poolParams.managerFee,
redeemFee: poolParams.redeemFee,
issueFee: poolParams.issueFee,
perfermanceFee: poolParams.perfermanceFee,
startClaimFeeTime: block.timestamp
});
etype = poolParams.etype;
bFactory = IBFactory(factoryAddress);
vaultAddress = bFactory.getVault();
emit SetManagerFee(etfStatus.managerFee, etfStatus.issueFee, etfStatus.redeemFee, etfStatus.perfermanceFee);
}
/**
* @notice Set the cap (max # of pool tokens)
* @dev _bspCap defaults in the constructor to unlimited
* Can set to 0 (or anywhere below the current supply), to halt new investment
* Prevent setting it before creating a pool, since createPool sets to intialSupply
* (it does this to avoid an unlimited cap window between construction and createPool)
* Therefore setting it before then has no effect, so should not be allowed
* @param newCap - new value of the cap
*/
function setCap(uint newCap) external logs lock needsBPool onlyOwner {
require(etype == SmartPoolManager.Etypes.OPENED, "ERR_MUST_OPEN_ETF");
emit SizeChanged(msg.sender, "UPPER", etfStatus.upperCap, newCap);
etfStatus.upperCap = newCap;
}
function execute(
address _target,
uint _value,
bytes calldata _data,
bool isUnderlying
) external logs lock needsBPool returns (bytes memory _returnValue) {
require(bFactory.getModuleStatus(address(this), msg.sender), 'MODULE IS NOT REGISTER');
if (isUnderlying) {
_returnValue = bPool.execute(_target, _value, _data);
} else {
_returnValue = _target.functionCallWithValue(_data, _value);
}
}
function couldClaimManagerFee() public view returns(bool state,uint timePoint ,uint timeElapsed){
bool isCloseETF = etype == SmartPoolManager.Etypes.CLOSED;
timePoint = block.timestamp;
if(isCloseETF && timePoint > etfStatus.closureEndTime) timePoint = etfStatus.closureEndTime;
timeElapsed = DesynSafeMath.bsub(timePoint, etfStatus.startClaimFeeTime);
if(timeElapsed >= CLAIM_PERIOD) state = true;
if(isCloseETF && !isCompletedCollect) state = false;
}
function claimManagerFee() public virtual logs lock onlyAdmin needsBPool {
_claimManagerFee();
}
function _claimManagerFee() internal {
(bool state, uint timePoint ,uint timeElapsed) = couldClaimManagerFee();
if(state){
address[] memory poolTokens = bPool.getCurrentTokens();
uint[] memory tokensAmount = SmartPoolManager.handleClaim(
IConfigurableRightsPool(address(this)),
bPool,
poolTokens,
etfStatus.managerFee,
timeElapsed,
CLAIM_PERIOD
);
IVault(vaultAddress).depositManagerToken(poolTokens, tokensAmount);
etfStatus.startClaimFeeTime = timePoint;
}
}
/**
* @notice Create a new Smart Pool
* @dev Delegates to internal function
* @param initialSupply starting token balance
* @param closurePeriod the etf closure period
*/
function createPool(
address creator,
uint initialSupply,
uint collectPeriod,
SmartPoolManager.Period closurePeriod,
SmartPoolManager.PoolTokenRange memory tokenRange
) external virtual onlyOwner logs lock notPaused {
if (etype == SmartPoolManager.Etypes.CLOSED) {
SmartPoolManager.createPoolHandle(collectPeriod, etfStatus.upperCap, initialSupply);
uint oldCap = etfStatus.upperCap;
uint oldFloor = etfStatus.floorCap;
etfStatus.upperCap = initialSupply.bmul(tokenRange.bspCap).bdiv(_initialBalances[0]);
etfStatus.floorCap = initialSupply.bmul(tokenRange.bspFloor).bdiv(_initialBalances[0]);
emit PoolTokenInit(creator, address(this),_initialTokens[0], _initialBalances[0], initialSupply);
emit SizeChanged(creator, "UPPER", oldCap, etfStatus.upperCap);
emit SizeChanged(creator, "FLOOR", oldFloor, etfStatus.floorCap);
uint period;
uint collectEndTime = block.timestamp + collectPeriod;
if (closurePeriod == SmartPoolManager.Period.DAY90) {
period = 90 days;
} else if (closurePeriod == SmartPoolManager.Period.DAY1) {
period = 1 days;
} else if (closurePeriod == SmartPoolManager.Period.DAY3) {
period = 3 days;
} else if (closurePeriod == SmartPoolManager.Period.DAY7) {
period = 7 days;
} else if (closurePeriod == SmartPoolManager.Period.DAY14) {
period = 14 days;
} else if (closurePeriod == SmartPoolManager.Period.DAY30) {
period = 30 days;
} else if (closurePeriod == SmartPoolManager.Period.DAY180) {
period = 180 days;
} else {
period = 360 days;
}
uint closureEndTime = collectEndTime + period;
etfStatus.collectPeriod = collectPeriod;
etfStatus.collectEndTime = collectEndTime;
etfStatus.closurePeriod = period;
etfStatus.closureEndTime = closureEndTime;
IUserVault(bFactory.getUserVault()).recordTokenInfo(creator, creator, _initialTokens, _initialBalances);
}
createPoolInternal(initialSupply);
}
/**
* @notice Join a pool
* @dev Emits a LogJoin event (for each token)
* bPool is a contract interface; function calls on it are external
* @param poolAmountOut - number of pool tokens to receive
* @param maxAmountsIn - Max amount of asset tokens to spend
*/
function joinPool(
uint poolAmountOut,
uint[] calldata maxAmountsIn,
address kol,
address user
) external logs lock needsBPool notPaused {
SmartPoolManager.joinPoolHandle(rights.canWhitelistLPs, canProvideLiquidity(user), etype == SmartPoolManager.Etypes.CLOSED, etfStatus.collectEndTime);
if(rights.canTokenWhiteLists) {
require(_initWhiteTokenState(),"ERR_SHOULD_SET_WHITETOKEN");
}
// Delegate to library to save space
// Library computes actualAmountsIn, and does many validations
// Cannot call the push/pull/min from an external library for
// any of these pool functions. Since msg.sender can be anybody,
// they must be internal
uint[] memory actualAmountsIn = SmartPoolManager.joinPool(IConfigurableRightsPool(address(this)), bPool, poolAmountOut, maxAmountsIn, etfStatus.issueFee);
// After createPool, token list is maintained in the underlying BPool
address[] memory poolTokens = bPool.getCurrentTokens();
uint[] memory issueFeesReceived = new uint[](poolTokens.length);
uint _actualIssueFee = etfStatus.issueFee;
if (etype == SmartPoolManager.Etypes.CLOSED) {
IUserVault(bFactory.getUserVault()).recordTokenInfo(kol, user, poolTokens, actualAmountsIn);
if (!isCompletedCollect) {
_actualIssueFee = 0;
}
}
for (uint i = 0; i < poolTokens.length; i++) {
uint issueFeeReceived = SmartPoolManager.handleTransferInTokens(
IConfigurableRightsPool(address(this)),
bPool,
poolTokens[i],
actualAmountsIn[i],
_actualIssueFee
);
emit LogJoin(user, poolTokens[i], actualAmountsIn[i]);
issueFeesReceived[i] = issueFeeReceived;
}
if (_actualIssueFee != 0) {
IVault(vaultAddress).depositIssueRedeemPToken(poolTokens, issueFeesReceived, issueFeesReceived, false);
}
_mintPoolShare(poolAmountOut);
_pushPoolShare(user, poolAmountOut);
// checkout the state that elose ETF collect completed and claime fee.
bool isCompletedMoment = etype == SmartPoolManager.Etypes.CLOSED && varTotalSupply >= etfStatus.floorCap && !isCompletedCollect;
if (isCompletedMoment) {
isCompletedCollect = true;
SmartPoolManager.handleFeeClaim(
IConfigurableRightsPool(address(this)), bPool,
poolTokens,
etfStatus.issueFee,
false
);
}
}
/**
* @notice Exit a pool - redeem pool tokens for underlying assets
* @dev Emits a LogExit event for each token
* bPool is a contract interface; function calls on it are external
* @param poolAmountIn - amount of pool tokens to redeem
* @param minAmountsOut - minimum amount of asset tokens to receive
*/
function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut, address user) external logs lock needsBPool notPaused {
uint actualPoolAmountIn;
bool isCloseEtfCollectEndWithFailure;
uint _actualRedeemFee = etfStatus.redeemFee;
if(etype == SmartPoolManager.Etypes.CLOSED){
isCloseEtfCollectEndWithFailure = !isCompletedCollect && block.timestamp >= etfStatus.collectEndTime;
if(!isCloseEtfCollectEndWithFailure){
if(!hasClaimManageFee) {
_claimManagerFee();
hasClaimManageFee = true;
}
if(hasSetBeginFund && !hasSetEndFund) snapshotEndAssets();
}
if(isCloseEtfCollectEndWithFailure) _actualRedeemFee = 0;
}
actualPoolAmountIn = SmartPoolManager.exitPoolHandleB(
IConfigurableRightsPool(address(this)),
etype == SmartPoolManager.Etypes.CLOSED,
isCompletedCollect,
etfStatus.closureEndTime,
etfStatus.collectEndTime,
poolAmountIn
);
// Library computes actualAmountsOut, and does many validations
uint[] memory actualAmountsOut = SmartPoolManager.exitPool(IConfigurableRightsPool(address(this)), bPool, actualPoolAmountIn, minAmountsOut);
_pullPoolShare(msg.sender, actualPoolAmountIn);
_burnPoolShare(actualPoolAmountIn);
// After createPool, token list is maintained in the underlying BPool
address[] memory poolTokens = bPool.getCurrentTokens();
uint[] memory redeemFeesReceived = new uint[](poolTokens.length);
for (uint i = 0; i < poolTokens.length; i++) {
(, uint finalAmountOut, uint redeemFeeReceived) = SmartPoolManager.exitPoolHandleA(
IConfigurableRightsPool(address(this)),
bPool,
poolTokens[i],
actualAmountsOut[i],
_actualRedeemFee
);
redeemFeesReceived[i] = redeemFeeReceived;
emit LogExit(user, poolTokens[i], finalAmountOut);
}
if (_actualRedeemFee != 0) {
IVault(vaultAddress).depositIssueRedeemPToken(poolTokens, redeemFeesReceived, redeemFeesReceived, true);
}
}
/**
* @notice Add to the whitelist of liquidity providers (if enabled)
* @param provider - address of the liquidity provider
*/
function whitelistLiquidityProvider(address provider) external onlyOwner lock logs {
SmartPoolManager.WhitelistHandle(rights.canWhitelistLPs, true, provider);
_liquidityProviderWhitelist[provider] = true;
}
/**
* @notice Remove from the whitelist of liquidity providers (if enabled)
* @param provider - address of the liquidity provider
*/
function removeWhitelistedLiquidityProvider(address provider) external onlyOwner lock logs {
SmartPoolManager.WhitelistHandle(rights.canWhitelistLPs, _liquidityProviderWhitelist[provider], provider);
_liquidityProviderWhitelist[provider] = false;
}
/**
* @notice Check if an address is a liquidity provider
* @dev If the whitelist feature is not enabled, anyone can provide liquidity (assuming finalized)
* @return boolean value indicating whether the address can join a pool
*/
function canProvideLiquidity(address provider) public view returns (bool) {
if (rights.canWhitelistLPs) {
return _liquidityProviderWhitelist[provider] || provider == getController() ;
} else {
// Probably don't strictly need this (could just return true)
// But the null address can't provide funds
return provider != address(0);
}
}
/**
* @notice Getter for specific permissions
* @dev value of the enum is just the 0-based index in the enumeration
* @return token boolean true if we have the given permission
*/
function hasPermission(RightsManager.Permissions permission) external view virtual returns (bool) {
return RightsManager.hasPermission(rights, permission);
}
/**
* @notice Getter for the RightsManager contract
* @dev Convenience function to get the address of the RightsManager library (so clients can check version)
* @return address of the RightsManager library
*/
function getRightsManagerVersion() external pure returns (address) {
return address(RightsManager);
}
/**
* @notice Getter for the DesynSafeMath contract
* @dev Convenience function to get the address of the DesynSafeMath library (so clients can check version)
* @return address of the DesynSafeMath library
*/
function getDesynSafeMathVersion() external pure returns (address) {
return address(DesynSafeMath);
}
/**
* @notice Getter for the SmartPoolManager contract
* @dev Convenience function to get the address of the SmartPoolManager library (so clients can check version)
* @return address of the SmartPoolManager library
*/
function getSmartPoolManagerVersion() external pure returns (address) {
return address(SmartPoolManager);
}
// "Public" versions that can safely be called from SmartPoolManager
// Allows only the contract itself to call them (not the controller or any external account)
function mintPoolShareFromLib(uint amount) public {
require(msg.sender == address(this), "ERR_NOT_CONTROLLER");
_mint(amount);
}
function pushPoolShareFromLib(address to, uint amount) public {
require(msg.sender == address(this), "ERR_NOT_CONTROLLER");
_push(to, amount);
}
function pullPoolShareFromLib(address from, uint amount) public {
require(msg.sender == address(this), "ERR_NOT_CONTROLLER");
_pull(from, amount);
}
function burnPoolShareFromLib(uint amount) public {
require(msg.sender == address(this), "ERR_NOT_CONTROLLER");
_burn(amount);
}
/**
* @notice Create a new Smart Pool
* @dev Initialize the swap fee to the value provided in the CRP constructor
* @param initialSupply starting token balance
*/
function createPoolInternal(uint initialSupply) internal {
require(address(bPool) == address(0), "ERR_IS_CREATED");
// To the extent possible, modify state variables before calling functions
_mintPoolShare(initialSupply);
_pushPoolShare(msg.sender, initialSupply);
// Deploy new BPool (bFactory and bPool are interfaces; all calls are external)
bPool = bFactory.newLiquidityPool();
// EXIT_FEE must always be zero, or ConfigurableRightsPool._pushUnderlying will fail
SmartPoolManager.createPoolInternalHandle(bPool, initialSupply);
for (uint i = 0; i < _initialTokens.length; i++) {
address t = _initialTokens[i];
uint bal = _initialBalances[i];
uint denorm = _initialWeights[i];
_verifyWhiteToken(t);
IERC20(t).safeTransferFrom(msg.sender, address(this), bal);
IERC20(t).safeApprove(address(bPool), 0);
IERC20(t).safeApprove(address(bPool), DesynConstants.MAX_UINT);
bPool.bind(t, bal, denorm);
}
while (_initialTokens.length > 0) {
// Modifying state variable after external calls here,
// but not essential, so not dangerous
_initialTokens.pop();
}
}
function addTokenToWhitelist(uint[] memory sort, address[] memory token) external onlyOwner {
require(rights.canTokenWhiteLists && !hasSetWhiteTokens, "ERR_NO_RIGHTS");
require(sort.length == token.length, "ERR_SORT_TOKEN_MISMATCH");
for (uint i = 0; i < token.length; i++) {
bool inRange = bFactory.isTokenWhitelistedForVerify(sort[i], token[i]);
require(inRange, "TOKEN_MUST_IN_WHITE_LISTS");
_addTokenToWhitelist(sort[i], token[i]);
}
hasSetWhiteTokens = true;
}
function _verifyWhiteToken(address token) public view {
require(bFactory.isTokenWhitelistedForVerify(token), "ERR_NOT_WHITE_TOKEN");
if (hasSetWhiteTokens) {
require(_queryIsTokenWhitelisted(token), "ERR_NOT_WHITE_TOKEN");
}
}
// Rebind BPool and pull tokens from address
// bPool is a contract interface; function calls on it are external
function _pullUnderlying(
address erc20,
address from,
uint amount
) internal needsBPool {
// Gets current Balance of token i, Bi, and weight of token i, Wi, from BPool.
uint tokenBalance = bPool.getBalance(erc20);
uint tokenWeight = bPool.getDenormalizedWeight(erc20);
IERC20(erc20).safeTransferFrom(from, address(this), amount);
bPool.rebind(erc20, DesynSafeMath.badd(tokenBalance, amount), tokenWeight);
}
// Rebind BPool and push tokens to address
// bPool is a contract interface; function calls on it are external
function _pushUnderlying(
address erc20,
address to,
uint amount
) internal needsBPool {
// Gets current Balance of token i, Bi, and weight of token i, Wi, from BPool.
uint tokenBalance = bPool.getBalance(erc20);
uint tokenWeight = bPool.getDenormalizedWeight(erc20);
bPool.rebind(erc20, DesynSafeMath.bsub(tokenBalance, amount), tokenWeight);
IERC20(erc20).safeTransfer(to, amount);
}
// Wrappers around corresponding core functions
function _mint(uint amount) internal override {
super._mint(amount);
require(varTotalSupply <= etfStatus.upperCap, "ERR_CAP_LIMIT_REACHED");
}
function _mintPoolShare(uint amount) internal {
_mint(amount);
}
function _pushPoolShare(address to, uint amount) internal {
_push(to, amount);
}
function _pullPoolShare(address from, uint amount) internal {
_pull(from, amount);
}
function _burnPoolShare(uint amount) internal {
_burn(amount);
}
// ================ SnapshotAssets ================
function snapshotBeginAssets() external logs {
uint nowTime = block.timestamp;
require(!hasSetBeginFund && isCompletedCollect && etype == SmartPoolManager.Etypes.CLOSED && nowTime <= (etfStatus.collectEndTime + 3 days) , "ERR_CONDITIONS_NOT_MET");
bool inT1 = nowTime <= (etfStatus.collectEndTime + 1 days);
if(inT1) require(adminList[msg.sender] || msg.sender == _owner, "onlyAdmin");
beginFund = snapshotAssets();
hasSetBeginFund = true;
if(nowTime < etfStatus.collectEndTime) etfStatus.collectEndTime = block.timestamp;
}
function beginFundAssets() external view returns(SmartPoolManager.Fund memory){
return beginFund;
}
function endFundAssets() external view returns(SmartPoolManager.Fund memory){
return endFund;
}
function snapshotEndAssets() public logs {
uint nowTime = block.timestamp;
require(!hasSetEndFund && hasSetBeginFund && etype == SmartPoolManager.Etypes.CLOSED && nowTime >= etfStatus.closureEndTime, "ERR_CONDITIONS_NOT_MET");
bool inT1 = (etfStatus.closureEndTime + 1 days) >= nowTime;
if(inT1) require(adminList[msg.sender] || msg.sender == _owner, "onlyAdmin");
endFund = snapshotAssets();
uint preShareValueEnd = endFund.fundAmount.bdiv(endFund.etfAmount);
uint preShareValueBegin = beginFund.fundAmount.bdiv(beginFund.etfAmount);
if(preShareValueEnd > preShareValueBegin){
uint perfermanceRatio = etfStatus.perfermanceFee.bmul(preShareValueEnd-preShareValueBegin).bdiv(preShareValueEnd);
// claim perfomance reward
SmartPoolManager.handleFeeClaim(
IConfigurableRightsPool(address(this)), bPool,
bPool.getCurrentTokens(),
perfermanceRatio,
true);
}
hasSetEndFund = true;
}
function snapshotAssets() public returns(SmartPoolManager.Fund memory){
SmartPoolManager.Fund memory tempFund;
tempFund.etfAmount = varTotalSupply;
(tempFund.tokens, tempFund.tokensAmount) = _getPoolTokensInfo();
tempFund.fundAmount = Oracles(bFactory.getOracleAddress()).getAllPrice(tempFund.tokens, tempFund.tokensAmount);
tempFund.snapshotTime = block.timestamp;
return tempFund;
}
function _getPoolTokensInfo() internal view returns (address[] memory, uint[] memory) {
address[] memory tokens = bPool.getCurrentTokens();
uint[] memory totalBalances = new uint[](tokens.length);
for(uint i; i < tokens.length ;i++) {
totalBalances[i] = bPool.getBalance(tokens[i]);
}
return (tokens,totalBalances);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
// Imports
import "../libraries/DesynSafeMath.sol";
import "../interfaces/IERC20.sol";
// Contracts
/* solhint-disable func-order */
/**
* @author Desyn Labs
* @title Highly opinionated token implementation
*/
contract PCToken is IERC20 {
using DesynSafeMath for uint;
// State variables
string public constant NAME = "Desyn Smart Pool";
uint8 public constant DECIMALS = 18;
// No leading underscore per naming convention (non-private)
// Cannot call totalSupply (name conflict)
// solhint-disable-next-line private-vars-leading-underscore
uint internal varTotalSupply;
mapping(address => uint) private _balance;
mapping(address => mapping(address => uint)) private _allowance;
string private _symbol;
string private _name;
// Event declarations
// See definitions above; must be redeclared to be emitted from this contract
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
// Function declarations
/**
* @notice Base token constructor
* @param tokenSymbol - the token symbol
*/
constructor(string memory tokenSymbol, string memory tokenName) public {
_symbol = tokenSymbol;
_name = tokenName;
}
// External functions
/**
* @notice Getter for allowance: amount spender will be allowed to spend on behalf of owner
* @param owner - owner of the tokens
* @param spender - entity allowed to spend the tokens
* @return uint - remaining amount spender is allowed to transfer
*/
function allowance(address owner, address spender) external view override returns (uint) {
return _allowance[owner][spender];
}
/**
* @notice Getter for current account balance
* @param account - address we're checking the balance of
* @return uint - token balance in the account
*/
function balanceOf(address account) external view override returns (uint) {
return _balance[account];
}
/**
* @notice Approve owner (sender) to spend a certain amount
* @dev emits an Approval event
* @param spender - entity the owner (sender) is approving to spend his tokens
* @param amount - number of tokens being approved
* @return bool - result of the approval (will always be true if it doesn't revert)
*/
function approve(address spender, uint amount) external override returns (bool) {
/* In addition to the increase/decreaseApproval functions, could
avoid the "approval race condition" by only allowing calls to approve
when the current approval amount is 0
require(_allowance[msg.sender][spender] == 0, "ERR_RACE_CONDITION");
Some token contracts (e.g., KNC), already revert if you call approve
on a non-zero allocation. To deal with these, we use the SafeApprove library
and safeApprove function when adding tokens to the pool.
*/
_allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
/**
* @notice Increase the amount the spender is allowed to spend on behalf of the owner (sender)
* @dev emits an Approval event
* @param spender - entity the owner (sender) is approving to spend his tokens
* @param amount - number of tokens being approved
* @return bool - result of the approval (will always be true if it doesn't revert)
*/
function increaseApproval(address spender, uint amount) external returns (bool) {
_allowance[msg.sender][spender] = DesynSafeMath.badd(_allowance[msg.sender][spender], amount);
emit Approval(msg.sender, spender, _allowance[msg.sender][spender]);
return true;
}
/**
* @notice Decrease the amount the spender is allowed to spend on behalf of the owner (sender)
* @dev emits an Approval event
* @dev If you try to decrease it below the current limit, it's just set to zero (not an error)
* @param spender - entity the owner (sender) is approving to spend his tokens
* @param amount - number of tokens being approved
* @return bool - result of the approval (will always be true if it doesn't revert)
*/
function decreaseApproval(address spender, uint amount) external returns (bool) {
uint oldValue = _allowance[msg.sender][spender];
// Gas optimization - if amount == oldValue (or is larger), set to zero immediately
if (amount >= oldValue) {
_allowance[msg.sender][spender] = 0;
} else {
_allowance[msg.sender][spender] = DesynSafeMath.bsub(oldValue, amount);
}
emit Approval(msg.sender, spender, _allowance[msg.sender][spender]);
return true;
}
/**
* @notice Transfer the given amount from sender (caller) to recipient
* @dev _move emits a Transfer event if successful
* @param recipient - entity receiving the tokens
* @param amount - number of tokens being transferred
* @return bool - result of the transfer (will always be true if it doesn't revert)
*/
function transfer(address recipient, uint amount) external override returns (bool) {
require(recipient != address(0), "ERR_ZERO_ADDRESS");
_move(msg.sender, recipient, amount);
return true;
}
/**
* @notice Transfer the given amount from sender to recipient
* @dev _move emits a Transfer event if successful; may also emit an Approval event
* @param sender - entity sending the tokens (must be caller or allowed to spend on behalf of caller)
* @param recipient - recipient of the tokens
* @param amount - number of tokens being transferred
* @return bool - result of the transfer (will always be true if it doesn't revert)
*/
function transferFrom(
address sender,
address recipient,
uint amount
) external override returns (bool) {
require(recipient != address(0), "ERR_ZERO_ADDRESS");
require(msg.sender == sender || amount <= _allowance[sender][msg.sender], "ERR_PCTOKEN_BAD_CALLER");
_move(sender, recipient, amount);
// memoize for gas optimization
uint oldAllowance = _allowance[sender][msg.sender];
// If the sender is not the caller, adjust the allowance by the amount transferred
if (msg.sender != sender && oldAllowance != uint(-1)) {
_allowance[sender][msg.sender] = DesynSafeMath.bsub(oldAllowance, amount);
emit Approval(sender, msg.sender, _allowance[sender][msg.sender]);
}
return true;
}
// public functions
/**
* @notice Getter for the total supply
* @dev declared external for gas optimization
* @return uint - total number of tokens in existence
*/
function totalSupply() external view override returns (uint) {
return varTotalSupply;
}
// Public functions
/**
* @dev Returns the name of the token.
* We allow the user to set this name (as well as the symbol).
* Alternatives are 1) A fixed string (original design)
* 2) A fixed string plus the user-defined symbol
* return string(abi.encodePacked(NAME, "-", _symbol));
*/
function name() external view returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() external view override 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() external view override returns (uint8) {
return DECIMALS;
}
// internal functions
// Mint an amount of new tokens, and add them to the balance (and total supply)
// Emit a transfer amount from the null address to this contract
function _mint(uint amount) internal virtual {
_balance[address(this)] = DesynSafeMath.badd(_balance[address(this)], amount);
varTotalSupply = DesynSafeMath.badd(varTotalSupply, amount);
emit Transfer(address(0), address(this), amount);
}
// Burn an amount of new tokens, and subtract them from the balance (and total supply)
// Emit a transfer amount from this contract to the null address
function _burn(uint amount) internal virtual {
// Can't burn more than we have
// Remove require for gas optimization - bsub will revert on underflow
// require(_balance[address(this)] >= amount, "ERR_INSUFFICIENT_BAL");
_balance[address(this)] = DesynSafeMath.bsub(_balance[address(this)], amount);
varTotalSupply = DesynSafeMath.bsub(varTotalSupply, amount);
emit Transfer(address(this), address(0), amount);
}
// Transfer tokens from sender to recipient
// Adjust balances, and emit a Transfer event
function _move(
address sender,
address recipient,
uint amount
) internal virtual {
// Can't send more than sender has
// Remove require for gas optimization - bsub will revert on underflow
// require(_balance[sender] >= amount, "ERR_INSUFFICIENT_BAL");
_balance[sender] = DesynSafeMath.bsub(_balance[sender], amount);
_balance[recipient] = DesynSafeMath.badd(_balance[recipient], amount);
emit Transfer(sender, recipient, amount);
}
// Transfer from this contract to recipient
// Emits a transfer event if successful
function _push(address recipient, uint amount) internal {
_move(address(this), recipient, amount);
}
// Transfer from recipient to this contract
// Emits a transfer event if successful
function _pull(address sender, uint amount) internal {
_move(sender, address(this), amount);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
/**
* @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 DesynOwnable {
// State variables
mapping(address => bool) public adminList;
uint public allOwnerPercentage = 10000;
address _owner;
address[] owners;
uint[] ownerPercentage;
bool initialized;
// Event declarations
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event AddAdmin(address indexed newAdmin, uint indexed amount);
event RemoveAdmin(address indexed oldAdmin, uint indexed amount);
// Modifiers
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == msg.sender, "ERR_NOT_CONTROLLER");
_;
}
modifier onlyAdmin() {
require(adminList[msg.sender] || msg.sender == _owner, "onlyAdmin");
_;
}
// Function declarations
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() internal {
_owner = msg.sender;
}
function initHandle(address[] memory _owners, uint[] memory _ownerPercentage) external {
require(_owners.length == _ownerPercentage.length, "ownerP");
require(!initialized, "initialized!");
_addAdmin(_owners);
owners = _owners;
ownerPercentage = _ownerPercentage;
initialized = true;
_ownerPercentageChecker();
}
function setManagersInfo(address[] memory _owners, uint[] memory _ownerPercentage) external onlyOwner {
_beforeControllerChange();
_clearAdmin();
_addAdmin(_owners);
owners = _owners;
ownerPercentage = _ownerPercentage;
_ownerPercentageChecker();
}
function _ownerPercentageChecker() internal view {
uint totalPercentage;
for (uint i; i < ownerPercentage.length; i++) {
totalPercentage+=ownerPercentage[i];
}
require(totalPercentage == 10000, "ERR_ILLEGAL_PERCENTAGE");
}
function _addAdmin(address[] memory admins) internal {
bool hasOwner;
for (uint i; i < admins.length; i++) {
adminList[admins[i]] = true;
if(admins[i] == _owner) hasOwner = true;
}
if(initialized) require(hasOwner, "ERR_NEW_ADMINS_HAS_NO_OWNER");
}
function _clearAdmin() internal {
for(uint i; i < owners.length; i++) {
delete adminList[owners[i]];
}
}
/**
* @notice Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner
* @dev external for gas optimization
* @param newOwner - address of new owner
*/
function setController(address newOwner) external onlyOwner {
_beforeControllerChange();
require(newOwner != address(0), "ERR_ZERO_ADDRESS");
emit OwnershipTransferred(_owner, newOwner);
for (uint i;i < owners.length; i++) {
if (owners[i] == _owner) {
owners[i] = newOwner;
}
}
adminList[_owner] = false;
adminList[newOwner] = true;
_owner = newOwner;
}
// @dev Returns list of owners.
// @return List of owner addresses.
function getOwners() external view returns (address[] memory) {
return owners;
}
// @dev Returns list of owners.
// @return List of owner addresses.
function getOwnerPercentage() external view returns (uint[] memory) {
return ownerPercentage;
}
/**
* @notice Returns the address of the current owner
* @dev external for gas optimization
* @return address - of the owner (AKA controller)
*/
function getController() public view returns (address) {
return _owner;
}
function _beforeControllerChange() internal virtual {}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
// Imports
import "../interfaces/IERC20.sol";
// Libraries
/**
* @author PieDAO (ported to Desyn Labs)
* @title SafeApprove - set approval for tokens that require 0 prior approval
* @dev Perhaps to address the known ERC20 race condition issue
* See https://github.com/crytic/not-so-smart-contracts/tree/master/race_condition
* Some tokens - notably KNC - only allow approvals to be increased from 0
*/
library SafeApprove {
/**
* @notice handle approvals of tokens that require approving from a base of 0
* @param token - the token we're approving
* @param spender - entity the owner (sender) is approving to spend his tokens
* @param amount - number of tokens being approved
*/
function safeApprove(
IERC20 token,
address spender,
uint amount
) internal returns (bool) {
uint currentAllowance = token.allowance(address(this), spender);
// Do nothing if allowance is already set to this value
if (currentAllowance == amount) {
return true;
}
// If approval is not zero reset it to zero first
if (currentAllowance != 0) {
token.approve(spender, 0);
}
// do the actual approval
return token.approve(spender, amount);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
contract WhiteToken {
// add token log
event LOG_WHITELIST(address indexed spender, uint indexed sort, address indexed caller, address token);
// del token log
event LOG_DEL_WHITELIST(address indexed spender, uint indexed sort, address indexed caller, address token);
// record the number of whitelists.
uint private _whiteTokenCount;
// token address => is white token.
mapping(address => bool) private _isTokenWhitelisted;
// Multi level white token.
// type => token address => is white token.
mapping(uint => mapping(address => bool)) private _tokenWhitelistedInfo;
function _queryIsTokenWhitelisted(address token) internal view returns (bool) {
return _isTokenWhitelisted[token];
}
// for factory to verify
function _isTokenWhitelistedForVerify(uint sort, address token) internal view returns (bool) {
return _tokenWhitelistedInfo[sort][token];
}
// add sort token
function _addTokenToWhitelist(uint sort, address token) internal {
require(token != address(0), "ERR_INVALID_TOKEN_ADDRESS");
require(_queryIsTokenWhitelisted(token) == false, "ERR_HAS_BEEN_ADDED_WHITE");
_tokenWhitelistedInfo[sort][token] = true;
_isTokenWhitelisted[token] = true;
_whiteTokenCount++;
emit LOG_WHITELIST(address(this), sort, msg.sender, token);
}
// remove sort token
function _removeTokenFromWhitelist(uint sort, address token) internal {
require(_queryIsTokenWhitelisted(token) == true, "ERR_NOT_WHITE_TOKEN");
require(_tokenWhitelistedInfo[sort][token], "ERR_SORT_NOT_MATCHED");
_tokenWhitelistedInfo[sort][token] = false;
_isTokenWhitelisted[token] = false;
_whiteTokenCount--;
emit LOG_DEL_WHITELIST(address(this), sort, msg.sender, token);
}
// already has init
function _initWhiteTokenState() internal view returns (bool) {
return _whiteTokenCount == 0 ? false : true;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
// 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 disstributed 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.6.12;
import "../base/WhiteToken.sol";
import "../interfaces/IBFactory.sol";
import "../libraries/SafeERC20.sol";
contract Factory is WhiteToken {
using SafeERC20 for IERC20;
event LOG_NEW_POOL(address indexed caller, address indexed pool);
event LOG_BLABS(address indexed caller, address indexed blabs);
event LOG_ROUTER(address indexed caller, address indexed router);
event LOG_VAULT(address indexed vault, address indexed caller);
event LOG_USER_VAULT(address indexed vault, address indexed caller);
event LOG_MANAGER(address indexed manager, address indexed caller);
event LOG_ORACLE(address indexed caller, address indexed oracle);
event SYSTEM_MODULE_CHANGED(address module, bool state);
event MODULE_STATUS_CHANGE(address etf, address module, bool status);
event PAUSED_STATUS(bool state);
mapping(address => bool) private _isLiquidityPool;
mapping(address => bool) private _isSystemModule;
mapping(address => mapping(address => bool)) private _isModuleRegistered;
uint private counters;
bytes private bytecodes;
bool public isPaused;
address private _blabs;
address private _oracle;
address private _vaultAddress;
address private _userVaultAddress;
constructor(
address oracle,
address vault,
address userVault
) public {
_blabs = msg.sender;
_oracle = oracle;
_vaultAddress = vault;
_userVaultAddress = userVault;
}
function addTokenToWhitelist(uint[] memory sort, address[] memory token) external onlyBlabs {
require(sort.length == token.length, "ERR_SORT_TOKEN_MISMATCH");
for (uint i = 0; i < sort.length; i++) {
_addTokenToWhitelist(sort[i], token[i]);
}
}
function removeTokenFromWhitelist(uint[] memory sort, address[] memory token) external onlyBlabs {
require(sort.length == token.length, "ERR_SORT_TOKEN_MISMATCH");
for (uint i = 0; i < sort.length; i++) {
_removeTokenFromWhitelist(sort[i], token[i]);
}
}
function isTokenWhitelistedForVerify(uint sort, address token) external view returns (bool) {
return _isTokenWhitelistedForVerify(sort, token);
}
function isTokenWhitelistedForVerify(address token) external view returns (bool) {
return _queryIsTokenWhitelisted(token);
}
function isLiquidityPool(address b) external view returns (bool) {
return _isLiquidityPool[b];
}
function createPool() internal returns (address base) {
bytes memory bytecode = bytecodes;
bytes32 salt = keccak256(abi.encodePacked(counters++));
assembly {
base := create2(0, add(bytecode, 32), mload(bytecode), salt)
if iszero(extcodesize(base)) {
revert(0, 0)
}
}
counters++;
}
function newLiquidityPool() external returns (IBPool) {
address lpool = createPool();
_isLiquidityPool[lpool] = true;
emit LOG_NEW_POOL(msg.sender, lpool);
IBPool(lpool).setController(msg.sender);
return IBPool(lpool);
}
function getBLabs() external view returns (address) {
return _blabs;
}
function setBLabs(address b) external onlyBlabs {
require(b != address(0), "ERR_ZERO_ADDRESS");
emit LOG_BLABS(msg.sender, b);
_blabs = b;
}
function getModuleStatus(address etf, address module) external view returns (bool) {
return _isSystemModule[module] || _isModuleRegistered[etf][module];
}
function getOracleAddress() external view returns (address) {
return _oracle;
}
function setSystemModule(address module, bool state) external onlyBlabs {
require(module != address(0), "ZERO ADDRESS");
_isSystemModule[module] = state;
emit SYSTEM_MODULE_CHANGED(module, state);
}
function registerModule(address etf, address module) external onlyBlabs {
require(etf != address(0), "ZERO ETF ADDRESS");
require(module != address(0), "ZERO ADDRESS");
_isModuleRegistered[etf][module] = true;
emit MODULE_STATUS_CHANGE(etf, module, true);
}
function removeModule(address etf, address module) external onlyBlabs {
require(etf != address(0), "ZERO ETF ADDRESS");
require(module != address(0), "ZERO ADDRESS");
_isModuleRegistered[etf][module] = false;
emit MODULE_STATUS_CHANGE(etf, module, false);
}
function setOracle(address oracle) external onlyBlabs {
require(oracle != address(0), "ERR_ZERO_ADDRESS");
emit LOG_ORACLE(msg.sender, oracle);
_oracle = oracle;
}
function collect(IERC20 token) external onlyBlabs {
uint collected = token.balanceOf(address(this));
token.safeTransfer(_blabs, collected);
}
function getVault() external view returns (address) {
return _vaultAddress;
}
function setVault(address newVault) external onlyBlabs {
require(newVault != address(0), "ERR_ZERO_ADDRESS");
_vaultAddress = newVault;
emit LOG_VAULT(newVault, msg.sender);
}
function getUserVault() external view returns (address) {
return _userVaultAddress;
}
function setUserVault(address newVault) external onlyBlabs {
require(newVault != address(0), "ERR_ZERO_ADDRESS");
_userVaultAddress = newVault;
emit LOG_USER_VAULT(newVault, msg.sender);
}
function setProtocolPaused(bool state) external onlyBlabs {
isPaused = state;
emit PAUSED_STATUS(state);
}
function setByteCodes(bytes memory _bytecode) external onlyBlabs {
bytecodes = _bytecode;
}
modifier onlyBlabs() {
require(msg.sender == _blabs, "ERR_NOT_BLABS");
_;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
// 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.6.12;
import "./LpToken.sol";
import "./Math.sol";
import "../interfaces/IBFactory.sol";
import "../interfaces/IConfigurableRightsPool.sol";
import "../libraries/Address.sol";
import "../libraries/SafeERC20.sol";
contract LiquidityPool is BBronze, LpToken, Math {
using Address for address;
using SafeERC20 for IERC20;
struct Record {
bool bound; // is token bound to pool
uint index; // private
uint denorm; // denormalized weight
uint balance;
}
event LOG_JOIN(address indexed caller, address indexed tokenIn, uint tokenAmountIn);
event LOG_EXIT(address indexed caller, address indexed tokenOut, uint tokenAmountOut);
event LOG_REBALANCE(address indexed tokenA, address indexed tokenB, uint newWeightA, uint newWeightB, uint newBalanceA, uint newBalanceB, bool isSoldout);
event LOG_CALL(bytes4 indexed sig, address indexed caller, bytes data) anonymous;
modifier _logs_() {
emit LOG_CALL(msg.sig, msg.sender, msg.data);
_;
}
modifier _lock_() {
require(!_mutex, "ERR_REENTRY");
_mutex = true;
_;
_mutex = false;
}
modifier _viewlock_() {
require(!_mutex, "ERR_REENTRY");
_;
}
bool private _mutex;
IBFactory private _factory; // Factory address to push token exitFee to
address private _controller; // has CONTROL role
bool private _publicSwap; // true if PUBLIC can call SWAP functions
// `finalize` sets `PUBLIC can SWAP`, `PUBLIC can JOIN`
bool private _finalized;
address[] private _tokens;
mapping(address => Record) private _records;
uint private _totalWeight;
constructor() public {
_controller = msg.sender;
_factory = IBFactory(msg.sender);
_publicSwap = false;
_finalized = false;
}
function isPublicSwap() external view returns (bool) {
return _publicSwap;
}
function isFinalized() external view returns (bool) {
return _finalized;
}
function isBound(address t) external view returns (bool) {
return _records[t].bound;
}
function getNumTokens() external view returns (uint) {
return _tokens.length;
}
function getCurrentTokens() external view _viewlock_ returns (address[] memory tokens) {
return _tokens;
}
function getFinalTokens() external view _viewlock_ returns (address[] memory tokens) {
require(_finalized, "ERR_NOT_FINALIZED");
return _tokens;
}
function getDenormalizedWeight(address token) external view _viewlock_ returns (uint) {
// require(_records[token].bound, "ERR_NOT_BOUND");
return _records[token].denorm;
}
function getTotalDenormalizedWeight() external view _viewlock_ returns (uint) {
return _totalWeight;
}
function getNormalizedWeight(address token) external _viewlock_ returns (uint) {
require(_records[token].bound, "ERR_NOT_BOUND");
Oracles oracle = Oracles(_factory.getOracleAddress());
uint denorm = _records[token].denorm;
uint price = oracle.getPrice(token);
uint[] memory _balances = new uint[](_tokens.length);
for (uint i = 0; i < _tokens.length; i++) {
_balances[i] = getBalance(_tokens[i]);
}
uint totalValue = oracle.getAllPrice(_tokens, _balances);
uint currentValue = bmul(price, getBalance(token));
return bdiv(currentValue, totalValue);
}
function getBalance(address token) public view _viewlock_ returns (uint) {
// require(_records[token].bound, "ERR_NOT_BOUND");
return _records[token].balance;
}
function getController() external view _viewlock_ returns (address) {
return _controller;
}
function setController(address manager) external _logs_ _lock_ {
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(manager != address(0), "ERR_ZERO_ADDRESS");
_controller = manager;
}
function setPublicSwap(bool public_) external _logs_ _lock_ {
require(!_finalized, "ERR_IS_FINALIZED");
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
_publicSwap = public_;
}
function finalize() external _logs_ _lock_ {
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(!_finalized, "ERR_IS_FINALIZED");
require(_tokens.length >= MIN_BOUND_TOKENS, "ERR_MIN_TOKENS");
_finalized = true;
_publicSwap = true;
_mintPoolShare(INIT_POOL_SUPPLY);
_pushPoolShare(msg.sender, INIT_POOL_SUPPLY);
}
function bind(
address token,
uint balance,
uint denorm
)
external
_logs_ // _lock_ Bind does not lock because it jumps to `rebind`, which does
{
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(!_records[token].bound, "ERR_IS_BOUND");
require(!_finalized, "ERR_IS_FINALIZED");
require(_tokens.length < MAX_BOUND_TOKENS, "ERR_MAX_TOKENS");
_records[token] = Record({
bound: true,
index: _tokens.length,
denorm: 0, // balance and denorm will be validated
balance: 0 // and set by `rebind`
});
_tokens.push(token);
rebind(token, balance, denorm);
}
function rebind(
address token,
uint balance,
uint denorm
) public _logs_ _lock_ {
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(_records[token].bound, "ERR_NOT_BOUND");
require(!_finalized, "ERR_IS_FINALIZED");
require(denorm >= MIN_WEIGHT, "ERR_MIN_WEIGHT");
require(denorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT");
require(balance >= MIN_BALANCE, "ERR_MIN_BALANCE");
// Adjust the denorm and totalWeight
uint oldWeight = _records[token].denorm;
if (denorm > oldWeight) {
_totalWeight = badd(_totalWeight, bsub(denorm, oldWeight));
require(_totalWeight <= MAX_TOTAL_WEIGHT, "ERR_MAX_TOTAL_WEIGHT");
} else if (denorm < oldWeight) {
_totalWeight = bsub(_totalWeight, bsub(oldWeight, denorm));
}
_records[token].denorm = denorm;
// Adjust the balance record and actual token balance
uint oldBalance = _records[token].balance;
_records[token].balance = balance;
if (balance > oldBalance) {
_pullUnderlying(token, msg.sender, bsub(balance, oldBalance));
} else if (balance < oldBalance) {
// In this case liquidity is being withdrawn, so charge EXIT_FEE
uint tokenBalanceWithdrawn = bsub(oldBalance, balance);
_pushUnderlying(token, msg.sender, tokenBalanceWithdrawn);
}
}
function execute(
address _target,
uint _value,
bytes calldata _data
) external _logs_ _lock_ returns (bytes memory _returnValue) {
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(!_finalized, "ERR_IS_FINALIZED");
_returnValue = _target.functionCallWithValue(_data, _value);
return _returnValue;
}
function unbind(address token) external _logs_ _lock_ {
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(_records[token].bound, "ERR_NOT_BOUND");
require(!_finalized, "ERR_IS_FINALIZED");
uint tokenBalance = _records[token].balance;
_totalWeight = bsub(_totalWeight, _records[token].denorm);
// Swap the token-to-unbind with the last token,
// then delete the last token
uint index = _records[token].index;
uint last = _tokens.length - 1;
_tokens[index] = _tokens[last];
_records[_tokens[index]].index = index;
_tokens.pop();
_records[token] = Record({bound: false, index: 0, denorm: 0, balance: 0});
_pushUnderlying(token, msg.sender, tokenBalance);
}
function unbindPure(address token) external _logs_ _lock_ {
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(_records[token].bound, "ERR_NOT_BOUND");
require(!_finalized, "ERR_IS_FINALIZED");
// Swap the token-to-unbind with the last token,
// then delete the last token
uint index = _records[token].index;
uint last = _tokens.length - 1;
_tokens[index] = _tokens[last];
_records[_tokens[index]].index = index;
_tokens.pop();
_records[token] = Record({bound: false, index: 0, denorm: 0, balance: 0});
}
function rebindPure(
address token,
uint balance,
uint denorm,
bool isBound
) public _logs_ _lock_ {
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
if (!isBound) {
require(_tokens.length < MAX_BOUND_TOKENS, "ERR_MAX_TOKENS");
_records[token] = Record({
bound: true,
index: _tokens.length,
denorm: denorm,
balance: balance
});
_tokens.push(token);
} else {
_records[token].denorm = denorm;
_records[token].balance = balance;
}
}
// Absorb any tokens that have been sent to this contract into the pool
function gulp(address token) external _logs_ _lock_ {
require(IConfigurableRightsPool(_controller).adminList(msg.sender), 'NOT_ADMIN');
require(_records[token].bound, "ERR_NOT_BOUND");
_records[token].balance = IERC20(token).balanceOf(address(this));
}
function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn) external _logs_ _lock_ {
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(_finalized, "ERR_NOT_FINALIZED");
uint poolTotal = this.totalSupply();
uint ratio = bdiv(poolAmountOut, poolTotal);
require(ratio != 0, "ERR_MATH_APPROX");
for (uint i = 0; i < _tokens.length; i++) {
address t = _tokens[i];
uint bal = _records[t].balance;
uint tokenAmountIn = bmul(ratio, bal);
require(tokenAmountIn != 0, "ERR_MATH_APPROX");
require(tokenAmountIn <= maxAmountsIn[i], "ERR_LIMIT_IN");
_records[t].balance = badd(_records[t].balance, tokenAmountIn);
emit LOG_JOIN(msg.sender, t, tokenAmountIn);
_pullUnderlying(t, msg.sender, tokenAmountIn);
}
_mintPoolShare(poolAmountOut);
_pushPoolShare(msg.sender, poolAmountOut);
}
function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut) external _logs_ _lock_ {
require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
require(_finalized, "ERR_NOT_FINALIZED");
uint poolTotal = this.totalSupply();
uint ratio = bdiv(poolAmountIn, poolTotal);
require(ratio != 0, "ERR_MATH_APPROX");
_pullPoolShare(msg.sender, poolAmountIn);
_burnPoolShare(poolAmountIn);
for (uint i = 0; i < _tokens.length; i++) {
address t = _tokens[i];
uint bal = _records[t].balance;
uint tokenAmountOut = bmul(ratio, bal);
require(tokenAmountOut != 0, "ERR_MATH_APPROX");
require(tokenAmountOut >= minAmountsOut[i], "ERR_LIMIT_OUT");
_records[t].balance = bsub(_records[t].balance, tokenAmountOut);
emit LOG_EXIT(msg.sender, t, tokenAmountOut);
_pushUnderlying(t, msg.sender, tokenAmountOut);
}
}
function _pullUnderlying(
address erc20,
address from,
uint amount
) internal {
IERC20(erc20).safeTransferFrom(from, address(this), amount);
}
function _pushUnderlying(
address erc20,
address to,
uint amount
) internal {
IERC20(erc20).safeTransfer(to, amount);
}
function _pullPoolShare(address from, uint amount) internal {
_pull(from, amount);
}
function _pushPoolShare(address to, uint amount) internal {
_push(to, amount);
}
function _mintPoolShare(uint amount) internal {
_mint(amount);
}
function _burnPoolShare(uint amount) internal {
_burn(amount);
}
receive() external payable {}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
import "./Num.sol";
import "../interfaces/IERC20.sol";
// Highly opinionated token implementation
contract LpTokenBase is Num {
mapping(address => uint) internal _balance;
mapping(address => mapping(address => uint)) internal _allowance;
uint internal _totalSupply;
event Approval(address indexed src, address indexed dst, uint amt);
event Transfer(address indexed src, address indexed dst, uint amt);
function _mint(uint amt) internal {
_balance[address(this)] = badd(_balance[address(this)], amt);
_totalSupply = badd(_totalSupply, amt);
emit Transfer(address(0), address(this), amt);
}
function _burn(uint amt) internal {
require(_balance[address(this)] >= amt, "ERR_INSUFFICIENT_BAL");
_balance[address(this)] = bsub(_balance[address(this)], amt);
_totalSupply = bsub(_totalSupply, amt);
emit Transfer(address(this), address(0), amt);
}
function _move(
address src,
address dst,
uint amt
) internal {
require(_balance[src] >= amt, "ERR_INSUFFICIENT_BAL");
_balance[src] = bsub(_balance[src], amt);
_balance[dst] = badd(_balance[dst], amt);
emit Transfer(src, dst, amt);
}
function _push(address to, uint amt) internal {
_move(address(this), to, amt);
}
function _pull(address from, uint amt) internal {
_move(from, address(this), amt);
}
}
contract LpToken is LpTokenBase, IERC20 {
string private _name = "Desyn Pool Token";
string private _symbol = "DPT";
uint8 private _decimals = 18;
function name() external view returns (string memory) {
return _name;
}
function symbol() external view override returns (string memory) {
return _symbol;
}
function decimals() external view override returns (uint8) {
return _decimals;
}
function allowance(address src, address dst) external view override returns (uint) {
return _allowance[src][dst];
}
function balanceOf(address whom) external view override returns (uint) {
return _balance[whom];
}
function totalSupply() external view override returns (uint) {
return _totalSupply;
}
function approve(address dst, uint amt) external override returns (bool) {
_allowance[msg.sender][dst] = amt;
emit Approval(msg.sender, dst, amt);
return true;
}
function increaseApproval(address dst, uint amt) external returns (bool) {
_allowance[msg.sender][dst] = badd(_allowance[msg.sender][dst], amt);
emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
return true;
}
function decreaseApproval(address dst, uint amt) external returns (bool) {
uint oldValue = _allowance[msg.sender][dst];
if (amt > oldValue) {
_allowance[msg.sender][dst] = 0;
} else {
_allowance[msg.sender][dst] = bsub(oldValue, amt);
}
emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
return true;
}
function transfer(address dst, uint amt) external override returns (bool) {
require(dst != address(0),"ERR_BAD_RECIVER");
_move(msg.sender, dst, amt);
return true;
}
function transferFrom(
address src,
address dst,
uint amt
) external override returns (bool) {
require(dst != address(0),"ERR_BAD_RECIVER");
require(msg.sender == src || amt <= _allowance[src][msg.sender], "ERR_LPTOKEN_BAD_CALLER");
_move(src, dst, amt);
if (msg.sender != src && _allowance[src][msg.sender] != uint(-1)) {
_allowance[src][msg.sender] = bsub(_allowance[src][msg.sender], amt);
emit Approval(src, msg.sender, _allowance[src][msg.sender]);
}
return true;
}
}{
"optimizer": {
"enabled": true,
"runs": 20
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"share","type":"address"},{"indexed":false,"internalType":"uint256","name":"shareAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenReturn","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountReturn","type":"uint256"}],"name":"SmartExitPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"shareReturn","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountReturn","type":"uint256"}],"name":"SmartJoinPool","type":"event"},{"inputs":[],"name":"FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STBT","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IConfigurableRightsPool","name":"crp","type":"address"},{"internalType":"uint256[]","name":"sort","type":"uint256[]"},{"internalType":"address[]","name":"token","type":"address[]"}],"name":"addTokenToWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract RebalaceAdapter","name":"rebalanceAdapter","type":"address"},{"internalType":"address","name":"etf","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveUnderlying","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConfigurableRightsPool","name":"pool","type":"address"},{"internalType":"uint256","name":"poolAmountIn","type":"uint256"},{"internalType":"uint256[]","name":"minAmountsOut","type":"uint256[]"},{"internalType":"uint256","name":"minSwapReturn","type":"uint256"},{"internalType":"address","name":"handleToken","type":"address"},{"components":[{"internalType":"address","name":"aggregator","type":"address"},{"internalType":"address","name":"rebalanceAdapter","type":"address"},{"internalType":"enum IAggregator.SwapType","name":"swapType","type":"uint8"}],"internalType":"struct IAggregator.SwapInfoBase","name":"swapBase","type":"tuple"},{"components":[{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IAggregator.SwapData[]","name":"swapDatas","type":"tuple[]"}],"name":"autoExitSmartPool","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IConfigurableRightsPool","name":"pool","type":"address"},{"internalType":"address","name":"kol","type":"address"},{"internalType":"uint256","name":"issueAmount","type":"uint256"},{"internalType":"uint256","name":"minPoolAmountOut","type":"uint256"},{"internalType":"address","name":"handleToken","type":"address"},{"components":[{"internalType":"address","name":"aggregator","type":"address"},{"internalType":"address","name":"rebalanceAdapter","type":"address"},{"internalType":"enum IAggregator.SwapType","name":"swapType","type":"uint8"}],"internalType":"struct IAggregator.SwapInfoBase","name":"swapBase","type":"tuple"},{"components":[{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IAggregator.SwapData[]","name":"swapDatas","type":"tuple[]"}],"name":"autoJoinSmartPool","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"claimKolReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConfigurableRightsPool","name":"crp","type":"address"}],"name":"claimManagementFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault_","type":"address"},{"internalType":"address","name":"pool","type":"address"}],"name":"claimManagersReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract FactoryActions","name":"factory","type":"address"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"balances","type":"uint256[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"},{"internalType":"bool","name":"finalize","type":"bool"}],"name":"create","outputs":[{"internalType":"contract LiquidityPoolActions","name":"pool","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ICRPFactory","name":"factory","type":"address"},{"internalType":"contract FactoryActions","name":"coreFactory","type":"address"},{"components":[{"internalType":"string","name":"poolTokenSymbol","type":"string"},{"internalType":"string","name":"poolTokenName","type":"string"},{"internalType":"address[]","name":"constituentTokens","type":"address[]"},{"internalType":"uint256[]","name":"tokenBalances","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenWeights","type":"uint256[]"},{"internalType":"uint256","name":"managerFee","type":"uint256"},{"internalType":"uint256","name":"redeemFee","type":"uint256"},{"internalType":"uint256","name":"issueFee","type":"uint256"},{"internalType":"uint256","name":"perfermanceFee","type":"uint256"},{"internalType":"enum SmartPoolManager.Etypes","name":"etype","type":"uint8"}],"internalType":"struct IConfigurableRightsPool.PoolParams","name":"poolParams","type":"tuple"},{"components":[{"internalType":"uint256","name":"initialSupply","type":"uint256"},{"internalType":"uint256","name":"collectPeriod","type":"uint256"},{"internalType":"enum SmartPoolManager.Period","name":"period","type":"uint8"}],"internalType":"struct IConfigurableRightsPool.CrpParams","name":"crpParams","type":"tuple"},{"components":[{"internalType":"bool","name":"canWhitelistLPs","type":"bool"},{"internalType":"bool","name":"canTokenWhiteLists","type":"bool"}],"internalType":"struct RightsManager.Rights","name":"rights","type":"tuple"},{"components":[{"components":[{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"firstLevel","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"secondLevel","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"thirdLevel","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"fourLevel","type":"tuple"}],"internalType":"struct SmartPoolManager.feeParams","name":"managerFee","type":"tuple"},{"components":[{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"firstLevel","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"secondLevel","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"thirdLevel","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"fourLevel","type":"tuple"}],"internalType":"struct SmartPoolManager.feeParams","name":"issueFee","type":"tuple"},{"components":[{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"firstLevel","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"secondLevel","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"thirdLevel","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"fourLevel","type":"tuple"}],"internalType":"struct SmartPoolManager.feeParams","name":"redeemFee","type":"tuple"},{"components":[{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"firstLevel","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"secondLevel","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"thirdLevel","type":"tuple"},{"components":[{"internalType":"uint256","name":"level","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"internalType":"struct SmartPoolManager.levelParams","name":"fourLevel","type":"tuple"}],"internalType":"struct SmartPoolManager.feeParams","name":"perfermanceFee","type":"tuple"}],"internalType":"struct SmartPoolManager.KolPoolParams","name":"kolPoolParams","type":"tuple"},{"internalType":"address[]","name":"owners","type":"address[]"},{"internalType":"uint256[]","name":"ownerPercentage","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"bspFloor","type":"uint256"},{"internalType":"uint256","name":"bspCap","type":"uint256"}],"internalType":"struct SmartPoolManager.PoolTokenRange","name":"tokenRange","type":"tuple"}],"name":"createSmartPool","outputs":[{"internalType":"contract IConfigurableRightsPool","name":"crp","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConfigurableRightsPool","name":"pool","type":"address"},{"internalType":"uint256","name":"poolAmountIn","type":"uint256"},{"internalType":"uint256[]","name":"minAmountsOut","type":"uint256[]"}],"name":"exitPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract LiquidityPoolActions","name":"pool","type":"address"}],"name":"finalize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract LiquidityPoolActions","name":"pool","type":"address"},{"internalType":"uint256","name":"poolAmountOut","type":"uint256"},{"internalType":"uint256[]","name":"maxAmountsIn","type":"uint256[]"}],"name":"joinPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConfigurableRightsPool","name":"pool","type":"address"},{"internalType":"uint256","name":"poolAmountOut","type":"uint256"},{"internalType":"uint256[]","name":"maxAmountsIn","type":"uint256[]"},{"internalType":"address","name":"kol","type":"address"}],"name":"joinSmartPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract RebalaceAdapter","name":"rebalanceAdapter","type":"address"},{"components":[{"internalType":"address","name":"etf","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"address","name":"aggregator","type":"address"},{"internalType":"enum RebalaceAdapter.SwapType","name":"swapType","type":"uint8"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct RebalaceAdapter.RebalanceInfo","name":"rebalanceInfo","type":"tuple"}],"name":"rebalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConfigurableRightsPool","name":"crp","type":"address"},{"internalType":"address","name":"provider","type":"address"}],"name":"removeWhitelistedLiquidityProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConfigurableRightsPool","name":"crp","type":"address"},{"internalType":"uint256","name":"newCap","type":"uint256"}],"name":"setCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract AbstractPool","name":"pool","type":"address"},{"internalType":"address","name":"newController","type":"address"}],"name":"setController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract AbstractPool","name":"pool","type":"address"},{"internalType":"address[]","name":"_owners","type":"address[]"},{"internalType":"uint256[]","name":"_ownerPercentage","type":"uint256[]"}],"name":"setManagersInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract AbstractPool","name":"pool","type":"address"},{"internalType":"bool","name":"publicSwap","type":"bool"}],"name":"setPublicSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConfigurableRightsPool","name":"pool","type":"address"}],"name":"snapshotBeginAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConfigurableRightsPool","name":"pool","type":"address"}],"name":"snapshotEndAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConfigurableRightsPool","name":"crp","type":"address"},{"internalType":"address","name":"provider","type":"address"}],"name":"whitelistLiquidityProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
608060405234801561001057600080fd5b5060016000556152db806100256000396000f3fe60806040526004361061011f5760003560e01c806307f4e3471461012b5780630ebb327114610161578063135b968d146101835780631eccc185146101a3578063271b7895146101c35780632dd31000146101e3578063333fbeef146101f857806339f8a39f146102185780633e67f1571461022d57806349f77d551461024d5780634ef39b751461026d5780635135aafb1461028d578063555e129d146102ad57806363ced092146102c05780636936674d146102e05780637b7d6c681461030057806380ad2cf3146103205780638a5c57df146103405780639589da7614610360578063a7f28da714610380578063a92f00ff146103a0578063d2536ec0146103c0578063e221e73a146103d3578063fb411265146103f357610126565b3661012657005b600080fd5b34801561013757600080fd5b5061014b610146366004613ed4565b610413565b6040516101589190614784565b60405180910390f35b34801561016d57600080fd5b5061018161017c366004613caa565b610780565b005b34801561018f57600080fd5b5061018161019e3660046140f1565b6107d6565b3480156101af57600080fd5b506101816101be366004613dec565b610908565b3480156101cf57600080fd5b506101816101de366004613caa565b610962565b3480156101ef57600080fd5b5061014b61099d565b34801561020457600080fd5b50610181610213366004613d68565b6109b5565b34801561022457600080fd5b5061014b6109e1565b34801561023957600080fd5b5061018161024836600461430a565b6109f9565b34801561025957600080fd5b506101816102683660046142a7565b610a25565b34801561027957600080fd5b50610181610288366004613caa565b610a90565b34801561029957600080fd5b506101816102a8366004613d7a565b610bf7565b6101816102bb366004613fc5565b610c65565b3480156102cc57600080fd5b506101816102db366004614162565b61168b565b3480156102ec57600080fd5b506101816102fb366004613caa565b611788565b34801561030c57600080fd5b5061018161031b366004613d68565b6117c2565b34801561032c57600080fd5b5061018161033b3660046140c6565b6117f7565b34801561034c57600080fd5b5061018161035b36600461424e565b611823565b34801561036c57600080fd5b5061018161037b36600461405e565b6118dd565b34801561038c57600080fd5b5061018161039b366004613d68565b61190b565b3480156103ac57600080fd5b5061014b6103bb366004613e19565b611937565b6101816103ce3660046141ae565b611ce1565b3480156103df57600080fd5b506101816103ee366004613ce2565b6123cd565b3480156103ff57600080fd5b5061018161040e366004613caa565b6123f9565b600061042260608901896150d1565b905061043160408a018a6150d1565b9050146104595760405162461bcd60e51b815260040161045090614af4565b60405180910390fd5b61046660808901896150d1565b905061047560408a018a6150d1565b9050146104945760405162461bcd60e51b815260040161045090614af4565b604051632c60e63560e01b81526001600160a01b038b1690632c60e635906104ca908c908c908b908b908b908b90600401614895565b602060405180830381600087803b1580156104e457600080fd5b505af11580156104f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061051c9190613cc6565b905060005b61052e60408a018a6150d1565b90508110156105fd57600061054660408b018b6150d1565b8381811061055057fe5b90506020020160208101906105659190613caa565b90506105a4333061057960608e018e6150d1565b8681811061058357fe5b90506020020135846001600160a01b0316612434909392919063ffffffff16565b6105b96001600160a01b03821684600061248c565b6105f4836105ca60608d018d6150d1565b858181106105d457fe5b90506020020135836001600160a01b031661248c9092919063ffffffff16565b50600101610521565b506001600160a01b03811663eff4955733893560208b013561062560608d0160408e0161436e565b876040518663ffffffff1660e01b8152600401610646959493929190614822565b600060405180830381600087803b15801561066057600080fd5b505af1158015610674573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b038416925063a9059cbb91506106a79033908b3590600401614809565b602060405180830381600087803b1580156106c157600080fd5b505af11580156106d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f99190613d4c565b6107155760405162461bcd60e51b815260040161045090614d87565b6040516392eefe9b60e01b81526001600160a01b038216906392eefe9b90610741903390600401614784565b600060405180830381600087803b15801561075b57600080fd5b505af115801561076f573d6000803e3d6000fd5b505050509998505050505050505050565b806001600160a01b03166343774a6b6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156107bb57600080fd5b505af11580156107cf573d6000803e3d6000fd5b5050505050565b6060856001600160a01b031663b64ef17b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561081157600080fd5b505afa158015610825573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108499190613cc6565b6001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b15801561088157600080fd5b505afa158015610895573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108bd9190810190613d1a565b9050610900868287878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612554915050565b505050505050565b6040516324dacaa960e11b81526001600160a01b038316906349b5955290610934908490600401614a70565b600060405180830381600087803b15801561094e57600080fd5b505af1158015610900573d6000803e3d6000fd5b806001600160a01b0316631296548f6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156107bb57600080fd5b73de6b117384452b21f5a643e56952593b88110e7881565b60405163c83a1c2d60e01b81526001600160a01b0383169063c83a1c2d90610934908490600401614784565b73530824da86689c9c17cdc2871ff29b058345b44a81565b604051637051b85760e11b81526001600160a01b0383169063e0a370ae90610934908490600401614f4c565b60405163167ae91560e21b81526001600160a01b038616906359eba45490610a5790879087908790879060040161486b565b600060405180830381600087803b158015610a7157600080fd5b505af1158015610a85573d6000803e3d6000fd5b505050505050505050565b806001600160a01b0316634bb278f36040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610acb57600080fd5b505af1158015610adf573d6000803e3d6000fd5b50506040516370a0823160e01b81526001600160a01b038416925063a9059cbb9150339083906370a0823190610b19903090600401614784565b60206040518083038186803b158015610b3157600080fd5b505afa158015610b45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b699190614489565b6040518363ffffffff1660e01b8152600401610b86929190614809565b602060405180830381600087803b158015610ba057600080fd5b505af1158015610bb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd89190613d4c565b610bf45760405162461bcd60e51b815260040161045090614d87565b50565b610c0083612873565b6040516303c153ef60e61b81526001600160a01b0384169063f054fbc090610c2e9085908590600401614a26565b600060405180830381600087803b158015610c4857600080fd5b505af1158015610c5c573d6000803e3d6000fd5b50505050505050565b60026000541415610c885760405162461bcd60e51b815260040161045090614cb7565b600260005560405163db7ca46b60e01b815273de6b117384452b21f5a643e56952593b88110e789063db7ca46b90610cc4908690600401614784565b60206040518083038186803b158015610cdc57600080fd5b505afa158015610cf0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d149190613d4c565b610d305760405162461bcd60e51b815260040161045090614b88565b6060876001600160a01b031663b64ef17b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610d6b57600080fd5b505afa158015610d7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610da39190613cc6565b6001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b158015610ddb57600080fd5b505afa158015610def573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e179190810190613d1a565b90508151815114610e3a5760405162461bcd60e51b815260040161045090614b56565b606081516001600160401b0381118015610e5357600080fd5b50604051908082528060200260200182016040528015610e7d578160200160208202803683370190505b5090503360006001600160a01b03871673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415610f5757600034118015610eb757508834145b610ed35760405162461bcd60e51b815260040161045090614ad1565b7382af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015610f2257600080fd5b505af1158015610f36573d6000803e3d6000fd5b50505050507382af49447d8a07e3bd95bd0d56f35241523fbab19050610f6e565b5085610f6e6001600160a01b03821683308c612434565b610f90610f7e6020880188613caa565b6001600160a01b03831690600061248c565b610fb1610fa06020880188613caa565b6001600160a01b038316908b61248c565b610fb9613954565b604051806080016040528060001981526020018d6001600160a01b0316637228c87b6040518163ffffffff1660e01b815260040161016060405180830381600087803b15801561100857600080fd5b505af115801561101c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104091906143f3565b610100015181526020018d6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561108357600080fd5b505afa158015611097573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110bb9190614489565b81526020018d6001600160a01b031663b64ef17b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110f957600080fd5b505afa15801561110d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111319190613cc6565b6001600160a01b03169052905060005b855181101561129d57611152613985565b87828151811061115e57fe5b60200260200101519050836001600160a01b031687838151811061117e57fe5b60200260200101516001600160a01b0316146111ce576111b289828985815181106111a557fe5b6020026020010151612885565b8683815181106111be57fe5b60200260200101818152506111eb565b80600001518683815181106111df57fe5b60200260200101818152505b50600061122d8460600151856040015186602001518b878151811061120c57fe5b60200260200101518b888151811061122057fe5b6020026020010151612d70565b845190915081101561123d578084525b61126f8f60008a868151811061124f57fe5b60200260200101516001600160a01b031661248c9092919063ffffffff16565b6112938f88858151811061127f57fe5b60200260200101518a868151811061124f57fe5b5050600101611141565b5080518911156112bf5760405162461bcd60e51b815260040161045090614d04565b8b6001600160a01b031663276990f68260000151868e876040518563ffffffff1660e01b81526004016112f5949392919061503c565b600060405180830381600087803b15801561130f57600080fd5b505af1158015611323573d6000803e3d6000fd5b5050505060005b855181101561149257600086828151811061134157fe5b602002602001015190506000816001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016113799190614784565b60206040518083038186803b15801561139157600080fd5b505afa1580156113a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c99190614489565b1180156113f357506001600160a01b03811673530824da86689c9c17cdc2871ff29b058345b44a14155b156114895761148985826001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016114289190614784565b60206040518083038186803b15801561144057600080fd5b505afa158015611454573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114789190614489565b6001600160a01b0384169190612e53565b5060010161132a565b5061152483836001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016114c39190614784565b60206040518083038186803b1580156114db57600080fd5b505afa1580156114ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115139190614489565b6001600160a01b0385169190612e53565b6040516370a0823160e01b81526001600160a01b038d169063a9059cbb90859083906370a082319061155a903090600401614784565b60206040518083038186803b15801561157257600080fd5b505afa158015611586573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115aa9190614489565b6040518363ffffffff1660e01b81526004016115c7929190614809565b602060405180830381600087803b1580156115e157600080fd5b505af11580156115f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116199190613d4c565b6116355760405162461bcd60e51b815260040161045090614d87565b7fa1906a9ec8b18f0b22525421de8e6d162eac414db71d7a6a26ee6a349579139933898c8f85600001516040516116709594939291906147d6565b60405180910390a15050600160005550505050505050505050565b6060836001600160a01b031663b64ef17b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156116c657600080fd5b505afa1580156116da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116fe9190613cc6565b6001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b15801561173657600080fd5b505afa15801561174a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526117729190810190613d1a565b9050611782848484846000612e72565b50505050565b60006117938261315e565b60405163325a48a760e21b81529091506001600160a01b0382169063c969229c90610934908590600401614784565b6117cb82612873565b6040516392eefe9b60e01b81526001600160a01b038316906392eefe9b90610934908490600401614784565b6040516347786d3760e01b81526001600160a01b038316906347786d3790610934908490600401615002565b6060846001600160a01b031663be3bbd2e6040518163ffffffff1660e01b815260040160006040518083038186803b15801561185e57600080fd5b505afa158015611872573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261189a9190810190613d1a565b90506107cf858286868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250339250612554915050565b6040516329b0917160e21b81526001600160a01b0384169063a6c245c490610c2e9085908590600401614a4b565b60405163e2762d4b60e01b81526001600160a01b0383169063e2762d4b90610934908490600401614784565b60008685146119585760405162461bcd60e51b815260040161045090614af4565b8683146119775760405162461bcd60e51b815260040161045090614af4565b886001600160a01b03166391cefde46040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156119b257600080fd5b505af11580156119c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ea9190613cc6565b905060005b87811015611b05576000898983818110611a0557fe5b9050602002016020810190611a1a9190613caa565b9050611a2d33308a8a8681811061058357fe5b611a426001600160a01b03821684600061248c565b611a52838989858181106105d457fe5b826001600160a01b031663e4e1e5388b8b85818110611a6d57fe5b9050602002016020810190611a829190613caa565b8a8a86818110611a8e57fe5b90506020020135898987818110611aa157fe5b905060200201356040518463ffffffff1660e01b8152600401611ac693929190614a05565b600060405180830381600087803b158015611ae057600080fd5b505af1158015611af4573d6000803e3d6000fd5b5050600190930192506119ef915050565b508115611c7557806001600160a01b0316634bb278f36040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611b4757600080fd5b505af1158015611b5b573d6000803e3d6000fd5b50506040516370a0823160e01b81526001600160a01b038416925063a9059cbb9150339083906370a0823190611b95903090600401614784565b60206040518083038186803b158015611bad57600080fd5b505afa158015611bc1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611be59190614489565b6040518363ffffffff1660e01b8152600401611c02929190614809565b602060405180830381600087803b158015611c1c57600080fd5b505af1158015611c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c549190613d4c565b611c705760405162461bcd60e51b815260040161045090614d87565b611cd5565b6040516324dacaa960e11b81526001600160a01b038216906349b5955290611ca290600190600401614a70565b600060405180830381600087803b158015611cbc57600080fd5b505af1158015611cd0573d6000803e3d6000fd5b505050505b98975050505050505050565b60026000541415611d045760405162461bcd60e51b815260040161045090614cb7565b60026000819055506060876001600160a01b031663b64ef17b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611d4757600080fd5b505afa158015611d5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d7f9190613cc6565b6001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b158015611db757600080fd5b505afa158015611dcb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611df39190810190613d1a565b825160405163db7ca46b60e01b81529192509073de6b117384452b21f5a643e56952593b88110e789063db7ca46b90611e30908890600401614784565b60206040518083038186803b158015611e4857600080fd5b505afa158015611e5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e809190613d4c565b611e9c5760405162461bcd60e51b815260040161045090614b88565b8251825114611ebd5760405162461bcd60e51b815260040161045090614b56565b8651825114611ede5760405162461bcd60e51b815260040161045090614b56565b846001600160a01b03811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415611f1b57507382af49447d8a07e3bd95bd0d56f35241523fbab15b6040516370a0823160e01b81526000906001600160a01b038316906370a0823190611f4a903090600401614784565b60206040518083038186803b158015611f6257600080fd5b505afa158015611f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9a9190614489565b90506060836001600160401b0381118015611fb457600080fd5b50604051908082528060200260200182016040528015611fde578160200160208202803683370190505b50905060005b8481101561209a57858181518110611ff857fe5b60200260200101516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161202b9190614784565b60206040518083038186803b15801561204357600080fd5b505afa158015612057573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061207b9190614489565b82828151811061208757fe5b6020908102919091010152600101611fe4565b506120a98c8c8c886001612e72565b60005b848110156121fb576120bc613985565b8782815181106120c857fe5b6020026020010151905060008783815181106120e057fe5b60200260200101519050856001600160a01b031688848151811061210057fe5b60200260200101516001600160a01b0316146121f1576040516370a0823160e01b81526121af906001600160a01b038316906370a0823190612146903090600401614784565b60206040518083038186803b15801561215e57600080fd5b505afa158015612172573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121969190614489565b8585815181106121a257fe5b60200260200101516131e6565b82526121c1610f7e60208c018c613caa565b6121e46121d160208c018c613caa565b83516001600160a01b038416919061248c565b6121ef8a8388612885565b505b50506001016120ac565b506000612283846001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161222d9190614784565b60206040518083038186803b15801561224557600080fd5b505afa158015612259573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061227d9190614489565b846131e6565b9050808a11156122a55760405162461bcd60e51b815260040161045090614deb565b6001600160a01b03891673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee141561236657604051632e1a7d4d60e01b81527382af49447d8a07e3bd95bd0d56f35241523fbab190632e1a7d4d90612301908490600401615002565b600060405180830381600087803b15801561231b57600080fd5b505af115801561232f573d6000803e3d6000fd5b505060405133925083156108fc02915083906000818181858888f19350505050158015612360573d6000803e3d6000fd5b5061237a565b61237a6001600160a01b0385163383612e53565b7f2390bb834dbab89c25583d7e45b1f1b35709ffb807ccc79cd1e028c4d1d1dd89338e8e8c856040516123b19594939291906147d6565b60405180910390a1505060016000555050505050505050505050565b60405163edb12adf60e01b81526001600160a01b0383169063edb12adf90610934908490600401614784565b806001600160a01b031663d2fed42e6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156107bb57600080fd5b611782846323b872dd60e01b858585604051602401612455939291906147b2565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613231565b8015806125145750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e906124c29030908690600401614798565b60206040518083038186803b1580156124da57600080fd5b505afa1580156124ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125129190614489565b155b6125305760405162461bcd60e51b815260040161045090614ebf565b61254f8363095ea7b360e01b8484604051602401612455929190614809565b505050565b83518251146125755760405162461bcd60e51b815260040161045090614af4565b60005b845181101561261b57600085828151811061258f57fe5b602002602001015190506125cc33308685815181106125aa57fe5b6020026020010151846001600160a01b0316612434909392919063ffffffff16565b6125e16001600160a01b03821688600061248c565b612612878584815181106125f157fe5b6020026020010151836001600160a01b031661248c9092919063ffffffff16565b50600101612578565b506040516313b4c87b60e11b81526001600160a01b0386169063276990f69061264e90869086908690339060040161503c565b600060405180830381600087803b15801561266857600080fd5b505af115801561267c573d6000803e3d6000fd5b5050505060005b845181101561276157600085828151811061269a57fe5b602002602001015190506000816001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016126d29190614784565b60206040518083038186803b1580156126ea57600080fd5b505afa1580156126fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127229190614489565b11156127585761275833826001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016114289190614784565b50600101612683565b506040516370a0823160e01b81526001600160a01b0386169063a9059cbb90339083906370a0823190612798903090600401614784565b60206040518083038186803b1580156127b057600080fd5b505afa1580156127c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127e89190614489565b6040518363ffffffff1660e01b8152600401612805929190614809565b602060405180830381600087803b15801561281f57600080fd5b505af1158015612833573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128579190613d4c565b6107cf5760405162461bcd60e51b815260040161045090614d87565b61287c816123f9565b610bf481613316565b600073de6b117384452b21f5a643e56952593b88110e786338f0d245826128b26040880160208901613caa565b6040518363ffffffff1660e01b81526004016128cf929190614798565b60206040518083038186803b1580156128e757600080fd5b505afa1580156128fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291f9190613d4c565b61293b5760405162461bcd60e51b815260040161045090614bf0565b61294b6040850160208601613caa565b6001600160a01b03166337f621386129666020870187613caa565b6040518263ffffffff1660e01b81526004016129829190614784565b602060405180830381600087803b15801561299c57600080fd5b505af11580156129b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129d49190613d4c565b6129f05760405162461bcd60e51b815260040161045090614cdc565b6040516370a0823160e01b81526000906001600160a01b038416906370a0823190612a1f903090600401614784565b60206040518083038186803b158015612a3757600080fd5b505afa158015612a4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a6f9190614489565b90506001612a83606087016040880161438d565b6003811115612a8e57fe5b1415612b2d57600060608560200151806020019051810190612ab091906144e5565b9092509050612ac26020880188613caa565b8651604051637224811760e11b81526001600160a01b03929092169163e449022e91612af491869086906004016150b2565b600060405180830381600087803b158015612b0e57600080fd5b505af1158015612b22573d6000803e3d6000fd5b505050505050612ce1565b6000612b3f606087016040880161438d565b6003811115612b4a57fe5b1415612bc057600060608560200151806020019051810190612b6c91906144a1565b9092509050612b7e6020880188613caa565b6001600160a01b03166338ed17398760000151848430612ba0426107086135a6565b6040518663ffffffff1660e01b8152600401612af4959493929190615076565b6002612bd2606087016040880161438d565b6003811115612bdd57fe5b1415612c0557612bff612bf36020870187613caa565b856020015160006135cb565b50612ce1565b6003612c17606087016040880161438d565b6003811115612c2257fe5b1415612cc9576000806000808760200151806020019051810190612c4691906143ac565b92965090945092509050612c5d60208a018a613caa565b6001600160a01b031663a6417ed6858585856040518563ffffffff1660e01b8152600401612c8e9493929190614a7b565b600060405180830381600087803b158015612ca857600080fd5b505af1158015612cbc573d6000803e3d6000fd5b5050505050505050612ce1565b60405162461bcd60e51b815260040161045090614c5e565b6040516370a0823160e01b8152612d67906001600160a01b038516906370a0823190612d11903090600401614784565b60206040518083038186803b158015612d2957600080fd5b505afa158015612d3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d619190614489565b826131e6565b95945050505050565b600080866001600160a01b031663f8b2cb4f856040518263ffffffff1660e01b8152600401612d9f9190614784565b60206040518083038186803b158015612db757600080fd5b505afa158015612dcb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612def9190614489565b90506000612dff866103e86135f9565b90506000612e46612e11846001613676565b6103e802612e2a846103e861369b90919063ffffffff16565b612e3f88612e398d600161369b565b906135f9565b02906136d4565b9998505050505050505050565b61254f8363a9059cbb60e01b8484604051602401612455929190614809565b836001866001600160a01b03166373a9855c6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612eb057600080fd5b505af1158015612ec4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ee89190614352565b6001811115612ef357fe5b1415612f78576040516370a0823160e01b81526001600160a01b038716906370a0823190612f25903390600401614784565b60206040518083038186803b158015612f3d57600080fd5b505afa158015612f51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f759190614489565b90505b6040516323b872dd60e01b81526001600160a01b038716906323b872dd90612fa8903390309086906004016147b2565b602060405180830381600087803b158015612fc257600080fd5b505af1158015612fd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ffa9190613d4c565b6130165760405162461bcd60e51b815260040161045090614d87565b6040516305369ecb60e11b81526001600160a01b03871690630a6d3d96906130469084908890339060040161500b565b600060405180830381600087803b15801561306057600080fd5b505af1158015613074573d6000803e3d6000fd5b50505050816109005760005b8351811015610c5c57600084828151811061309757fe5b602002602001015190506000816001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016130cf9190614784565b60206040518083038186803b1580156130e757600080fd5b505afa1580156130fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061311f9190614489565b11156131555761315533826001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016114289190614784565b50600101613080565b60008061316a83613765565b9050806001600160a01b0316639daafec76040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156131a757600080fd5b505af11580156131bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131df9190613cc6565b9392505050565b600061322883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506137d8565b90505b92915050565b613243826001600160a01b0316613804565b61325f5760405162461bcd60e51b815260040161045090614f15565b60006060836001600160a01b03168360405161327b9190614768565b6000604051808303816000865af19150503d80600081146132b8576040519150601f19603f3d011682016040523d82523d6000602084013e6132bd565b606091505b5091509150816132df5760405162461bcd60e51b815260040161045090614bbb565b80511561178257808060200190518101906132fa9190613d4c565b6117825760405162461bcd60e51b815260040161045090614e75565b600061332182613765565b9050600061332e8361315e565b90506000826001600160a01b031663c0fcb166856040518263ffffffff1660e01b815260040161335e9190614784565b60206040518083038186803b15801561337657600080fd5b505afa15801561338a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ae9190613d4c565b90506000826001600160a01b031663c0fcb166866040518263ffffffff1660e01b81526004016133de9190614784565b60206040518083038186803b1580156133f657600080fd5b505afa15801561340a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061342e9190613d4c565b90506000856001600160a01b03166373a9855c6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561346d57600080fd5b505af1158015613481573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134a59190614352565b905060008160018111156134b557fe5b1480156134bf5750825b156135235760405163edb12adf60e01b81526001600160a01b0386169063edb12adf906134f0908990600401614784565b600060405180830381600087803b15801561350a57600080fd5b505af115801561351e573d6000803e3d6000fd5b505050505b600181600181111561353157fe5b14801561353b5750815b156109005760405163edb12adf60e01b81526001600160a01b0385169063edb12adf9061356c908990600401614784565b600060405180830381600087803b15801561358657600080fd5b505af115801561359a573d6000803e3d6000fd5b50505050505050505050565b6000828201838110156132285760405162461bcd60e51b815260040161045090614b21565b60606135f184848460405180606001604052806029815260200161527d6029913961383d565b949350505050565b6000826136085750600061322b565b8282028284828161361557fe5b04146136335760405162461bcd60e51b815260040161045090614d37565b6706f05b59d3b2000081018181101561365e5760405162461bcd60e51b815260040161045090614d37565b6000670de0b6b3a7640000825b049695505050505050565b6000828201838110156132285760405162461bcd60e51b815260040161045090614e4b565b60008060006136aa858561386b565b9150915080156136cc5760405162461bcd60e51b815260040161045090614e20565b509392505050565b6000816136f35760405162461bcd60e51b815260040161045090614d61565b826137005750600061322b565b670de0b6b3a76400008381029084828161371657fe5b04146137345760405162461bcd60e51b815260040161045090614c8d565b6002830481018181101561375a5760405162461bcd60e51b815260040161045090614c8d565b600084828161366b57fe5b6000816001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156137a057600080fd5b505afa1580156137b4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061322b9190613cc6565b600081848411156137fc5760405162461bcd60e51b81526004016104509190614a9e565b505050900390565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906135f1575050151592915050565b60608247101561385f5760405162461bcd60e51b815260040161045090614c18565b612d6785858585613890565b6000808383116138815750508082036000613889565b505081810360015b9250929050565b606061389b85613804565b6138b75760405162461bcd60e51b815260040161045090614db4565b60006060866001600160a01b031685876040516138d49190614768565b60006040518083038185875af1925050503d8060008114613911576040519150601f19603f3d011682016040523d82523d6000602084013e613916565b606091505b5091509150811561392a5791506135f19050565b80511561393a5780518082602001fd5b8360405162461bcd60e51b81526004016104509190614a9e565b604051806080016040528060008152602001600081526020016000815260200160006001600160a01b031681525090565b604051806040016040528060008152602001606081525090565b803561322b8161523d565b60008083601f8401126139bb578182fd5b5081356001600160401b038111156139d1578182fd5b602083019150836020808302850101111561388957600080fd5b600082601f8301126139fb578081fd5b8135613a0e613a098261513d565b615117565b818152915060208083019084810181840286018201871015613a2f57600080fd5b60005b84811015613a57578135613a458161523d565b84529282019290820190600101613a32565b505050505092915050565b600082601f830112613a72578081fd5b8151613a80613a098261513d565b818152915060208083019084810181840286018201871015613aa157600080fd5b60005b84811015613a57578151613ab78161523d565b84529282019290820190600101613aa4565b600082601f830112613ad9578081fd5b8135613ae7613a098261513d565b818152915060208083019084810160005b84811015613a575781358701604080601f19838c03011215613b1957600080fd5b613b2281615117565b82860135815290820135906001600160401b03821115613b4157600080fd5b613b4f8b8784860101613bc5565b81870152865250509282019290820190600101613af8565b600082601f830112613b77578081fd5b8135613b85613a098261513d565b818152915060208083019084810181840286018201871015613ba657600080fd5b60005b84811015613a5757813584529282019290820190600101613ba9565b600082601f830112613bd5578081fd5b81356001600160401b03811115613bea578182fd5b613bfd601f8201601f1916602001615117565b9150808252836020828501011115613c1457600080fd5b8060208401602084013760009082016020015292915050565b600060608284031215613c3e578081fd5b50919050565b60006104008284031215613c3e578081fd5b60006101408284031215613c3e578081fd5b600060408284031215613c79578081fd5b613c836040615117565b9050813581526020820135602082015292915050565b600060408284031215613c3e578081fd5b600060208284031215613cbb578081fd5b81356132288161523d565b600060208284031215613cd7578081fd5b81516132288161523d565b60008060408385031215613cf4578081fd5b8235613cff8161523d565b91506020830135613d0f8161523d565b809150509250929050565b600060208284031215613d2b578081fd5b81516001600160401b03811115613d40578182fd5b6135f184828501613a62565b600060208284031215613d5d578081fd5b815161322881615252565b60008060408385031215613cf4578182fd5b600080600060608486031215613d8e578081fd5b8335613d998161523d565b925060208401356001600160401b0380821115613db4578283fd5b613dc0878388016139eb565b93506040860135915080821115613dd5578283fd5b50613de286828701613b67565b9150509250925092565b60008060408385031215613dfe578182fd5b8235613e098161523d565b91506020830135613d0f81615252565b60008060008060008060008060a0898b031215613e34578586fd5b8835613e3f8161523d565b975060208901356001600160401b0380821115613e5a578788fd5b613e668c838d016139aa565b909950975060408b0135915080821115613e7e578586fd5b613e8a8c838d016139aa565b909750955060608b0135915080821115613ea2578485fd5b50613eaf8b828c016139aa565b9094509250506080890135613ec381615252565b809150509295985092959890939650565b60008060008060008060008060006105808a8c031215613ef2578283fd5b613efc8b8b61399f565b9850613f0b8b60208c0161399f565b975060408a01356001600160401b0380821115613f26578485fd5b613f328d838e01613c56565b9850613f418d60608e01613c2d565b9750613f508d60c08e01613c99565b9650613f608d6101008e01613c44565b95506105008c0135915080821115613f76578485fd5b613f828d838e016139eb565b94506105208c0135915080821115613f98578283fd5b50613fa58c828d01613b67565b925050613fb68b6105408c01613c68565b90509295985092959850929598565b6000806000806000806000610120888a031215613fe0578081fd5b8735613feb8161523d565b96506020880135613ffb8161523d565b9550604088013594506060880135935060808801356140198161523d565b92506140288960a08a01613c2d565b91506101008801356001600160401b03811115614043578182fd5b61404f8a828b01613ac9565b91505092959891949750929550565b600080600060608486031215614072578081fd5b833561407d8161523d565b925060208401356001600160401b0380821115614098578283fd5b6140a487838801613b67565b935060408601359150808211156140b9578283fd5b50613de2868287016139eb565b600080604083850312156140d8578182fd5b82356140e38161523d565b946020939093013593505050565b600080600080600060808688031215614108578283fd5b85356141138161523d565b94506020860135935060408601356001600160401b03811115614134578384fd5b614140888289016139aa565b90945092505060608601356141548161523d565b809150509295509295909350565b600080600060608486031215614176578081fd5b83356141818161523d565b92506020840135915060408401356001600160401b038111156141a2578182fd5b613de286828701613b67565b6000806000806000806000610120888a0312156141c9578081fd5b87356141d48161523d565b96506020880135955060408801356001600160401b03808211156141f6578283fd5b6142028b838c01613b67565b965060608a0135955060808a0135915061421b8261523d565b81945061422b8b60a08c01613c2d565b93506101008a0135915080821115614241578283fd5b5061404f8a828b01613ac9565b60008060008060608587031215614263578182fd5b843561426e8161523d565b93506020850135925060408501356001600160401b0381111561428f578283fd5b61429b878288016139aa565b95989497509550505050565b600080600080600060a086880312156142be578283fd5b85356142c98161523d565b945060208601356142d98161523d565b935060408601356142e98161523d565b925060608601356142f98161523d565b949793965091946080013592915050565b6000806040838503121561431c578182fd5b82356143278161523d565b915060208301356001600160401b03811115614341578182fd5b830160e08186031215613d0f578182fd5b600060208284031215614363578081fd5b815161322881615260565b60006020828403121561437f578081fd5b813560088110613228578182fd5b60006020828403121561439e578081fd5b813560048110613228578182fd5b600080600080608085870312156143c1578182fd5b84516143cc8161526d565b60208601519094506143dd8161526d565b6040860151606090960151949790965092505050565b6000610160808385031215614406578182fd5b61440f81615117565b9050825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152506101208084015181830152506101408084015181830152508091505092915050565b60006020828403121561449a578081fd5b5051919050565b600080604083850312156144b3578182fd5b8251915060208301516001600160401b038111156144cf578182fd5b6144db85828601613a62565b9150509250929050565b600080604083850312156144f7578182fd5b825191506020808401516001600160401b03811115614514578283fd5b8401601f81018613614524578283fd5b8051614532613a098261513d565b81815283810190838501858402850186018a101561454e578687fd5b8694505b83851015614570578051835260019490940193918501918501614552565b5080955050505050509250929050565b6001600160a01b03169052565b60008284526020808501945082825b858110156145ca5781356145af8161523d565b6001600160a01b03168752958201959082019060010161459c565b509495945050505050565b6000815180845260208085019450808401835b838110156145ca5781516001600160a01b0316875295820195908201906001016145e8565b81835260006001600160fb1b03831115614625578081fd5b6020830280836020870137939093016020019283525090919050565b6000815180845260208085019450808401835b838110156145ca57815187529582019590820190600101614654565b60008284528282602086013780602084860101526020601f19601f85011685010190509392505050565b600281106146a457fe5b9052565b600381106146a457fe5b6146bc828261471b565b6101006146cd81840182840161471b565b506102006146df81840182840161471b565b5061030061254f81840182840161471b565b80356146fc81615252565b15158252602081013561470e81615252565b8015156020840152505050565b6147258282614759565b6147356040830160408301614759565b6147456080830160808301614759565b61475560c0830160c08301614759565b5050565b80358252602090810135910152565b6000825161477a818460208701615211565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b039586168152938516602085015260408401929092529092166060820152608081019190915260a00190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0386168152602081018590526040810184905260c081016008841061484a57fe5b83606083015282516080830152602083015160a08301529695505050505050565b6001600160a01b039485168152928416602084015292166040820152606081019190915260800190565b6001600160a01b03871681526104c0602082018190526000906148b888806151b0565b610140838601526148ce61060086018284614670565b925050506148df60208901896151b0565b6104bf1980868503016104e08701526148f9848385614670565b935061490860408c018c615169565b93509150808685030161050087015261492284848461458d565b935061493160608c018c615169565b93509150808685030161052087015261494b84848461460d565b935061495a60808c018c615169565b9350915080868503016105408701525061497583838361460d565b9250505060a088013561056084015260c088013561058084015260e08801356105a08401526101008801356105c08401526149b46101208901896151f4565b6149c26105e085018261469a565b506149d060408401886146f1565b6149dd60808401876146b2565b8281036104808401526149f081866145d5565b90508281036104a0840152612e468185614641565b6001600160a01b039390931683526020830191909152604082015260600190565b600060408252614a3960408301856145d5565b8281036020840152612d678185614641565b600060408252614a5e6040830185614641565b8281036020840152612d6781856145d5565b901515815260200190565b600f94850b81529290930b60208301526040820152606081019190915260800190565b6000602082528251806020840152614abd816040850160208701615211565b601f01601f19169190910160400192915050565b60208082526009908201526808aa4a49ea4be8aa8960bb1b604082015260600190565b60208082526013908201527208aa4a4be988a9c8ea890be9a92a69a82a8869606b1b604082015260600190565b6020808252601b908201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604082015260600190565b60208082526018908201527708aa4a4bea89e968a9c988a9c8ea890be9a92a69a82a886960431b604082015260600190565b60208082526019908201527811549497d513d2d15397d393d517d5d2125511531254d51151603a1b604082015260600190565b6020808252818101527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604082015260600190565b6020808252600e908201526d1353d115531157d253131151d05360921b604082015260600190565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6040820152651c8818d85b1b60d21b606082015260800190565b6020808252601590820152744552525f494e56414c49445f535741505f5459504560581b604082015260600190565b60208082526010908201526f11549497d1125597d25395115493905360821b604082015260600190565b6020808252600b908201526a4552525f5245454e54525960a81b604082015260600190565b6020808252600e908201526d1493d555115497d253131151d05360921b604082015260600190565b60208082526019908201527811549497d4d210549157d05353d5539517d513d7d4d3505313603a1b604082015260600190565b60208082526010908201526f4552525f4d554c5f4f564552464c4f5760801b604082015260600190565b6020808252600c908201526b4552525f4449565f5a45524f60a01b604082015260600190565b60208082526013908201527211549497d514905394d1915497d19052531151606a1b604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252601b908201527a11549497d49150d152559157d05353d5539517d513d7d4d3505313602a1b604082015260600190565b6020808252601190820152704552525f5355425f554e444552464c4f5760781b604082015260600190565b60208082526010908201526f4552525f4144445f4f564552464c4f5760801b604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b60208082526036908201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60408201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b606082015260800190565b6020808252601f908201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604082015260600190565b6000602082528235614f5d8161523d565b6001600160a01b0390811660208481019190915284013590614f7e8261523d565b808216604085015260408501359150614f968261523d565b80821660608501525050614fad606084018461515c565b614fba6080840182614580565b50614fc86080840184615201565b614fd560a08401826146a8565b5060a083013560c0830152614fed60c08401846151b0565b60e080850152612d6761010085018284614670565b90815260200190565b6000848252606060208301526150246060830185614641565b905060018060a01b0383166040830152949350505050565b6000858252608060208301526150556080830186614641565b6001600160a01b039485166040840152929093166060909101529392505050565b600086825285602083015260a0604083015261509560a08301866145d5565b6001600160a01b0394909416606083015250608001529392505050565b600084825283602083015260606040830152612d676060830184614641565b6000808335601e198436030181126150e7578283fd5b8301803591506001600160401b03821115615100578283fd5b602090810192508102360382131561388957600080fd5b6040518181016001600160401b038111828210171561513557600080fd5b604052919050565b60006001600160401b03821115615152578081fd5b5060209081020190565b600082356132288161523d565b6000808335601e1984360301811261517f578283fd5b83016020810192503590506001600160401b0381111561519e57600080fd5b60208102360383131561388957600080fd5b6000808335601e198436030181126151c6578283fd5b83016020810192503590506001600160401b038111156151e557600080fd5b80360383131561388957600080fd5b6000823561322881615260565b6000823560038110613228578182fd5b60005b8381101561522c578181015183820152602001615214565b838111156117825750506000910152565b6001600160a01b0381168114610bf457600080fd5b8015158114610bf457600080fd5b60028110610bf457600080fd5b80600f0b8114610bf457600080fdfe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a26469706673582212203c3ca2552600b9d71ca0039f049cf11f5bee3ba14509533b77525f353ef23f8464736f6c634300060c0033
Deployed Bytecode
0x60806040526004361061011f5760003560e01c806307f4e3471461012b5780630ebb327114610161578063135b968d146101835780631eccc185146101a3578063271b7895146101c35780632dd31000146101e3578063333fbeef146101f857806339f8a39f146102185780633e67f1571461022d57806349f77d551461024d5780634ef39b751461026d5780635135aafb1461028d578063555e129d146102ad57806363ced092146102c05780636936674d146102e05780637b7d6c681461030057806380ad2cf3146103205780638a5c57df146103405780639589da7614610360578063a7f28da714610380578063a92f00ff146103a0578063d2536ec0146103c0578063e221e73a146103d3578063fb411265146103f357610126565b3661012657005b600080fd5b34801561013757600080fd5b5061014b610146366004613ed4565b610413565b6040516101589190614784565b60405180910390f35b34801561016d57600080fd5b5061018161017c366004613caa565b610780565b005b34801561018f57600080fd5b5061018161019e3660046140f1565b6107d6565b3480156101af57600080fd5b506101816101be366004613dec565b610908565b3480156101cf57600080fd5b506101816101de366004613caa565b610962565b3480156101ef57600080fd5b5061014b61099d565b34801561020457600080fd5b50610181610213366004613d68565b6109b5565b34801561022457600080fd5b5061014b6109e1565b34801561023957600080fd5b5061018161024836600461430a565b6109f9565b34801561025957600080fd5b506101816102683660046142a7565b610a25565b34801561027957600080fd5b50610181610288366004613caa565b610a90565b34801561029957600080fd5b506101816102a8366004613d7a565b610bf7565b6101816102bb366004613fc5565b610c65565b3480156102cc57600080fd5b506101816102db366004614162565b61168b565b3480156102ec57600080fd5b506101816102fb366004613caa565b611788565b34801561030c57600080fd5b5061018161031b366004613d68565b6117c2565b34801561032c57600080fd5b5061018161033b3660046140c6565b6117f7565b34801561034c57600080fd5b5061018161035b36600461424e565b611823565b34801561036c57600080fd5b5061018161037b36600461405e565b6118dd565b34801561038c57600080fd5b5061018161039b366004613d68565b61190b565b3480156103ac57600080fd5b5061014b6103bb366004613e19565b611937565b6101816103ce3660046141ae565b611ce1565b3480156103df57600080fd5b506101816103ee366004613ce2565b6123cd565b3480156103ff57600080fd5b5061018161040e366004613caa565b6123f9565b600061042260608901896150d1565b905061043160408a018a6150d1565b9050146104595760405162461bcd60e51b815260040161045090614af4565b60405180910390fd5b61046660808901896150d1565b905061047560408a018a6150d1565b9050146104945760405162461bcd60e51b815260040161045090614af4565b604051632c60e63560e01b81526001600160a01b038b1690632c60e635906104ca908c908c908b908b908b908b90600401614895565b602060405180830381600087803b1580156104e457600080fd5b505af11580156104f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061051c9190613cc6565b905060005b61052e60408a018a6150d1565b90508110156105fd57600061054660408b018b6150d1565b8381811061055057fe5b90506020020160208101906105659190613caa565b90506105a4333061057960608e018e6150d1565b8681811061058357fe5b90506020020135846001600160a01b0316612434909392919063ffffffff16565b6105b96001600160a01b03821684600061248c565b6105f4836105ca60608d018d6150d1565b858181106105d457fe5b90506020020135836001600160a01b031661248c9092919063ffffffff16565b50600101610521565b506001600160a01b03811663eff4955733893560208b013561062560608d0160408e0161436e565b876040518663ffffffff1660e01b8152600401610646959493929190614822565b600060405180830381600087803b15801561066057600080fd5b505af1158015610674573d6000803e3d6000fd5b505060405163a9059cbb60e01b81526001600160a01b038416925063a9059cbb91506106a79033908b3590600401614809565b602060405180830381600087803b1580156106c157600080fd5b505af11580156106d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f99190613d4c565b6107155760405162461bcd60e51b815260040161045090614d87565b6040516392eefe9b60e01b81526001600160a01b038216906392eefe9b90610741903390600401614784565b600060405180830381600087803b15801561075b57600080fd5b505af115801561076f573d6000803e3d6000fd5b505050509998505050505050505050565b806001600160a01b03166343774a6b6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156107bb57600080fd5b505af11580156107cf573d6000803e3d6000fd5b5050505050565b6060856001600160a01b031663b64ef17b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561081157600080fd5b505afa158015610825573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108499190613cc6565b6001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b15801561088157600080fd5b505afa158015610895573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108bd9190810190613d1a565b9050610900868287878780806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250899250612554915050565b505050505050565b6040516324dacaa960e11b81526001600160a01b038316906349b5955290610934908490600401614a70565b600060405180830381600087803b15801561094e57600080fd5b505af1158015610900573d6000803e3d6000fd5b806001600160a01b0316631296548f6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156107bb57600080fd5b73de6b117384452b21f5a643e56952593b88110e7881565b60405163c83a1c2d60e01b81526001600160a01b0383169063c83a1c2d90610934908490600401614784565b73530824da86689c9c17cdc2871ff29b058345b44a81565b604051637051b85760e11b81526001600160a01b0383169063e0a370ae90610934908490600401614f4c565b60405163167ae91560e21b81526001600160a01b038616906359eba45490610a5790879087908790879060040161486b565b600060405180830381600087803b158015610a7157600080fd5b505af1158015610a85573d6000803e3d6000fd5b505050505050505050565b806001600160a01b0316634bb278f36040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610acb57600080fd5b505af1158015610adf573d6000803e3d6000fd5b50506040516370a0823160e01b81526001600160a01b038416925063a9059cbb9150339083906370a0823190610b19903090600401614784565b60206040518083038186803b158015610b3157600080fd5b505afa158015610b45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b699190614489565b6040518363ffffffff1660e01b8152600401610b86929190614809565b602060405180830381600087803b158015610ba057600080fd5b505af1158015610bb4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd89190613d4c565b610bf45760405162461bcd60e51b815260040161045090614d87565b50565b610c0083612873565b6040516303c153ef60e61b81526001600160a01b0384169063f054fbc090610c2e9085908590600401614a26565b600060405180830381600087803b158015610c4857600080fd5b505af1158015610c5c573d6000803e3d6000fd5b50505050505050565b60026000541415610c885760405162461bcd60e51b815260040161045090614cb7565b600260005560405163db7ca46b60e01b815273de6b117384452b21f5a643e56952593b88110e789063db7ca46b90610cc4908690600401614784565b60206040518083038186803b158015610cdc57600080fd5b505afa158015610cf0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d149190613d4c565b610d305760405162461bcd60e51b815260040161045090614b88565b6060876001600160a01b031663b64ef17b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610d6b57600080fd5b505afa158015610d7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610da39190613cc6565b6001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b158015610ddb57600080fd5b505afa158015610def573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e179190810190613d1a565b90508151815114610e3a5760405162461bcd60e51b815260040161045090614b56565b606081516001600160401b0381118015610e5357600080fd5b50604051908082528060200260200182016040528015610e7d578160200160208202803683370190505b5090503360006001600160a01b03871673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415610f5757600034118015610eb757508834145b610ed35760405162461bcd60e51b815260040161045090614ad1565b7382af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015610f2257600080fd5b505af1158015610f36573d6000803e3d6000fd5b50505050507382af49447d8a07e3bd95bd0d56f35241523fbab19050610f6e565b5085610f6e6001600160a01b03821683308c612434565b610f90610f7e6020880188613caa565b6001600160a01b03831690600061248c565b610fb1610fa06020880188613caa565b6001600160a01b038316908b61248c565b610fb9613954565b604051806080016040528060001981526020018d6001600160a01b0316637228c87b6040518163ffffffff1660e01b815260040161016060405180830381600087803b15801561100857600080fd5b505af115801561101c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061104091906143f3565b610100015181526020018d6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561108357600080fd5b505afa158015611097573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110bb9190614489565b81526020018d6001600160a01b031663b64ef17b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156110f957600080fd5b505afa15801561110d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111319190613cc6565b6001600160a01b03169052905060005b855181101561129d57611152613985565b87828151811061115e57fe5b60200260200101519050836001600160a01b031687838151811061117e57fe5b60200260200101516001600160a01b0316146111ce576111b289828985815181106111a557fe5b6020026020010151612885565b8683815181106111be57fe5b60200260200101818152506111eb565b80600001518683815181106111df57fe5b60200260200101818152505b50600061122d8460600151856040015186602001518b878151811061120c57fe5b60200260200101518b888151811061122057fe5b6020026020010151612d70565b845190915081101561123d578084525b61126f8f60008a868151811061124f57fe5b60200260200101516001600160a01b031661248c9092919063ffffffff16565b6112938f88858151811061127f57fe5b60200260200101518a868151811061124f57fe5b5050600101611141565b5080518911156112bf5760405162461bcd60e51b815260040161045090614d04565b8b6001600160a01b031663276990f68260000151868e876040518563ffffffff1660e01b81526004016112f5949392919061503c565b600060405180830381600087803b15801561130f57600080fd5b505af1158015611323573d6000803e3d6000fd5b5050505060005b855181101561149257600086828151811061134157fe5b602002602001015190506000816001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016113799190614784565b60206040518083038186803b15801561139157600080fd5b505afa1580156113a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113c99190614489565b1180156113f357506001600160a01b03811673530824da86689c9c17cdc2871ff29b058345b44a14155b156114895761148985826001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016114289190614784565b60206040518083038186803b15801561144057600080fd5b505afa158015611454573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114789190614489565b6001600160a01b0384169190612e53565b5060010161132a565b5061152483836001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016114c39190614784565b60206040518083038186803b1580156114db57600080fd5b505afa1580156114ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115139190614489565b6001600160a01b0385169190612e53565b6040516370a0823160e01b81526001600160a01b038d169063a9059cbb90859083906370a082319061155a903090600401614784565b60206040518083038186803b15801561157257600080fd5b505afa158015611586573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115aa9190614489565b6040518363ffffffff1660e01b81526004016115c7929190614809565b602060405180830381600087803b1580156115e157600080fd5b505af11580156115f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116199190613d4c565b6116355760405162461bcd60e51b815260040161045090614d87565b7fa1906a9ec8b18f0b22525421de8e6d162eac414db71d7a6a26ee6a349579139933898c8f85600001516040516116709594939291906147d6565b60405180910390a15050600160005550505050505050505050565b6060836001600160a01b031663b64ef17b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156116c657600080fd5b505afa1580156116da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116fe9190613cc6565b6001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b15801561173657600080fd5b505afa15801561174a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526117729190810190613d1a565b9050611782848484846000612e72565b50505050565b60006117938261315e565b60405163325a48a760e21b81529091506001600160a01b0382169063c969229c90610934908590600401614784565b6117cb82612873565b6040516392eefe9b60e01b81526001600160a01b038316906392eefe9b90610934908490600401614784565b6040516347786d3760e01b81526001600160a01b038316906347786d3790610934908490600401615002565b6060846001600160a01b031663be3bbd2e6040518163ffffffff1660e01b815260040160006040518083038186803b15801561185e57600080fd5b505afa158015611872573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261189a9190810190613d1a565b90506107cf858286868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250339250612554915050565b6040516329b0917160e21b81526001600160a01b0384169063a6c245c490610c2e9085908590600401614a4b565b60405163e2762d4b60e01b81526001600160a01b0383169063e2762d4b90610934908490600401614784565b60008685146119585760405162461bcd60e51b815260040161045090614af4565b8683146119775760405162461bcd60e51b815260040161045090614af4565b886001600160a01b03166391cefde46040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156119b257600080fd5b505af11580156119c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ea9190613cc6565b905060005b87811015611b05576000898983818110611a0557fe5b9050602002016020810190611a1a9190613caa565b9050611a2d33308a8a8681811061058357fe5b611a426001600160a01b03821684600061248c565b611a52838989858181106105d457fe5b826001600160a01b031663e4e1e5388b8b85818110611a6d57fe5b9050602002016020810190611a829190613caa565b8a8a86818110611a8e57fe5b90506020020135898987818110611aa157fe5b905060200201356040518463ffffffff1660e01b8152600401611ac693929190614a05565b600060405180830381600087803b158015611ae057600080fd5b505af1158015611af4573d6000803e3d6000fd5b5050600190930192506119ef915050565b508115611c7557806001600160a01b0316634bb278f36040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611b4757600080fd5b505af1158015611b5b573d6000803e3d6000fd5b50506040516370a0823160e01b81526001600160a01b038416925063a9059cbb9150339083906370a0823190611b95903090600401614784565b60206040518083038186803b158015611bad57600080fd5b505afa158015611bc1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611be59190614489565b6040518363ffffffff1660e01b8152600401611c02929190614809565b602060405180830381600087803b158015611c1c57600080fd5b505af1158015611c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c549190613d4c565b611c705760405162461bcd60e51b815260040161045090614d87565b611cd5565b6040516324dacaa960e11b81526001600160a01b038216906349b5955290611ca290600190600401614a70565b600060405180830381600087803b158015611cbc57600080fd5b505af1158015611cd0573d6000803e3d6000fd5b505050505b98975050505050505050565b60026000541415611d045760405162461bcd60e51b815260040161045090614cb7565b60026000819055506060876001600160a01b031663b64ef17b6040518163ffffffff1660e01b815260040160206040518083038186803b158015611d4757600080fd5b505afa158015611d5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d7f9190613cc6565b6001600160a01b031663cc77828d6040518163ffffffff1660e01b815260040160006040518083038186803b158015611db757600080fd5b505afa158015611dcb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611df39190810190613d1a565b825160405163db7ca46b60e01b81529192509073de6b117384452b21f5a643e56952593b88110e789063db7ca46b90611e30908890600401614784565b60206040518083038186803b158015611e4857600080fd5b505afa158015611e5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e809190613d4c565b611e9c5760405162461bcd60e51b815260040161045090614b88565b8251825114611ebd5760405162461bcd60e51b815260040161045090614b56565b8651825114611ede5760405162461bcd60e51b815260040161045090614b56565b846001600160a01b03811673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415611f1b57507382af49447d8a07e3bd95bd0d56f35241523fbab15b6040516370a0823160e01b81526000906001600160a01b038316906370a0823190611f4a903090600401614784565b60206040518083038186803b158015611f6257600080fd5b505afa158015611f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9a9190614489565b90506060836001600160401b0381118015611fb457600080fd5b50604051908082528060200260200182016040528015611fde578160200160208202803683370190505b50905060005b8481101561209a57858181518110611ff857fe5b60200260200101516001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161202b9190614784565b60206040518083038186803b15801561204357600080fd5b505afa158015612057573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061207b9190614489565b82828151811061208757fe5b6020908102919091010152600101611fe4565b506120a98c8c8c886001612e72565b60005b848110156121fb576120bc613985565b8782815181106120c857fe5b6020026020010151905060008783815181106120e057fe5b60200260200101519050856001600160a01b031688848151811061210057fe5b60200260200101516001600160a01b0316146121f1576040516370a0823160e01b81526121af906001600160a01b038316906370a0823190612146903090600401614784565b60206040518083038186803b15801561215e57600080fd5b505afa158015612172573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121969190614489565b8585815181106121a257fe5b60200260200101516131e6565b82526121c1610f7e60208c018c613caa565b6121e46121d160208c018c613caa565b83516001600160a01b038416919061248c565b6121ef8a8388612885565b505b50506001016120ac565b506000612283846001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161222d9190614784565b60206040518083038186803b15801561224557600080fd5b505afa158015612259573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061227d9190614489565b846131e6565b9050808a11156122a55760405162461bcd60e51b815260040161045090614deb565b6001600160a01b03891673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee141561236657604051632e1a7d4d60e01b81527382af49447d8a07e3bd95bd0d56f35241523fbab190632e1a7d4d90612301908490600401615002565b600060405180830381600087803b15801561231b57600080fd5b505af115801561232f573d6000803e3d6000fd5b505060405133925083156108fc02915083906000818181858888f19350505050158015612360573d6000803e3d6000fd5b5061237a565b61237a6001600160a01b0385163383612e53565b7f2390bb834dbab89c25583d7e45b1f1b35709ffb807ccc79cd1e028c4d1d1dd89338e8e8c856040516123b19594939291906147d6565b60405180910390a1505060016000555050505050505050505050565b60405163edb12adf60e01b81526001600160a01b0383169063edb12adf90610934908490600401614784565b806001600160a01b031663d2fed42e6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156107bb57600080fd5b611782846323b872dd60e01b858585604051602401612455939291906147b2565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613231565b8015806125145750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e906124c29030908690600401614798565b60206040518083038186803b1580156124da57600080fd5b505afa1580156124ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125129190614489565b155b6125305760405162461bcd60e51b815260040161045090614ebf565b61254f8363095ea7b360e01b8484604051602401612455929190614809565b505050565b83518251146125755760405162461bcd60e51b815260040161045090614af4565b60005b845181101561261b57600085828151811061258f57fe5b602002602001015190506125cc33308685815181106125aa57fe5b6020026020010151846001600160a01b0316612434909392919063ffffffff16565b6125e16001600160a01b03821688600061248c565b612612878584815181106125f157fe5b6020026020010151836001600160a01b031661248c9092919063ffffffff16565b50600101612578565b506040516313b4c87b60e11b81526001600160a01b0386169063276990f69061264e90869086908690339060040161503c565b600060405180830381600087803b15801561266857600080fd5b505af115801561267c573d6000803e3d6000fd5b5050505060005b845181101561276157600085828151811061269a57fe5b602002602001015190506000816001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016126d29190614784565b60206040518083038186803b1580156126ea57600080fd5b505afa1580156126fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127229190614489565b11156127585761275833826001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016114289190614784565b50600101612683565b506040516370a0823160e01b81526001600160a01b0386169063a9059cbb90339083906370a0823190612798903090600401614784565b60206040518083038186803b1580156127b057600080fd5b505afa1580156127c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127e89190614489565b6040518363ffffffff1660e01b8152600401612805929190614809565b602060405180830381600087803b15801561281f57600080fd5b505af1158015612833573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128579190613d4c565b6107cf5760405162461bcd60e51b815260040161045090614d87565b61287c816123f9565b610bf481613316565b600073de6b117384452b21f5a643e56952593b88110e786338f0d245826128b26040880160208901613caa565b6040518363ffffffff1660e01b81526004016128cf929190614798565b60206040518083038186803b1580156128e757600080fd5b505afa1580156128fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061291f9190613d4c565b61293b5760405162461bcd60e51b815260040161045090614bf0565b61294b6040850160208601613caa565b6001600160a01b03166337f621386129666020870187613caa565b6040518263ffffffff1660e01b81526004016129829190614784565b602060405180830381600087803b15801561299c57600080fd5b505af11580156129b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129d49190613d4c565b6129f05760405162461bcd60e51b815260040161045090614cdc565b6040516370a0823160e01b81526000906001600160a01b038416906370a0823190612a1f903090600401614784565b60206040518083038186803b158015612a3757600080fd5b505afa158015612a4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a6f9190614489565b90506001612a83606087016040880161438d565b6003811115612a8e57fe5b1415612b2d57600060608560200151806020019051810190612ab091906144e5565b9092509050612ac26020880188613caa565b8651604051637224811760e11b81526001600160a01b03929092169163e449022e91612af491869086906004016150b2565b600060405180830381600087803b158015612b0e57600080fd5b505af1158015612b22573d6000803e3d6000fd5b505050505050612ce1565b6000612b3f606087016040880161438d565b6003811115612b4a57fe5b1415612bc057600060608560200151806020019051810190612b6c91906144a1565b9092509050612b7e6020880188613caa565b6001600160a01b03166338ed17398760000151848430612ba0426107086135a6565b6040518663ffffffff1660e01b8152600401612af4959493929190615076565b6002612bd2606087016040880161438d565b6003811115612bdd57fe5b1415612c0557612bff612bf36020870187613caa565b856020015160006135cb565b50612ce1565b6003612c17606087016040880161438d565b6003811115612c2257fe5b1415612cc9576000806000808760200151806020019051810190612c4691906143ac565b92965090945092509050612c5d60208a018a613caa565b6001600160a01b031663a6417ed6858585856040518563ffffffff1660e01b8152600401612c8e9493929190614a7b565b600060405180830381600087803b158015612ca857600080fd5b505af1158015612cbc573d6000803e3d6000fd5b5050505050505050612ce1565b60405162461bcd60e51b815260040161045090614c5e565b6040516370a0823160e01b8152612d67906001600160a01b038516906370a0823190612d11903090600401614784565b60206040518083038186803b158015612d2957600080fd5b505afa158015612d3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d619190614489565b826131e6565b95945050505050565b600080866001600160a01b031663f8b2cb4f856040518263ffffffff1660e01b8152600401612d9f9190614784565b60206040518083038186803b158015612db757600080fd5b505afa158015612dcb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612def9190614489565b90506000612dff866103e86135f9565b90506000612e46612e11846001613676565b6103e802612e2a846103e861369b90919063ffffffff16565b612e3f88612e398d600161369b565b906135f9565b02906136d4565b9998505050505050505050565b61254f8363a9059cbb60e01b8484604051602401612455929190614809565b836001866001600160a01b03166373a9855c6040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612eb057600080fd5b505af1158015612ec4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ee89190614352565b6001811115612ef357fe5b1415612f78576040516370a0823160e01b81526001600160a01b038716906370a0823190612f25903390600401614784565b60206040518083038186803b158015612f3d57600080fd5b505afa158015612f51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f759190614489565b90505b6040516323b872dd60e01b81526001600160a01b038716906323b872dd90612fa8903390309086906004016147b2565b602060405180830381600087803b158015612fc257600080fd5b505af1158015612fd6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ffa9190613d4c565b6130165760405162461bcd60e51b815260040161045090614d87565b6040516305369ecb60e11b81526001600160a01b03871690630a6d3d96906130469084908890339060040161500b565b600060405180830381600087803b15801561306057600080fd5b505af1158015613074573d6000803e3d6000fd5b50505050816109005760005b8351811015610c5c57600084828151811061309757fe5b602002602001015190506000816001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016130cf9190614784565b60206040518083038186803b1580156130e757600080fd5b505afa1580156130fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061311f9190614489565b11156131555761315533826001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016114289190614784565b50600101613080565b60008061316a83613765565b9050806001600160a01b0316639daafec76040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156131a757600080fd5b505af11580156131bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131df9190613cc6565b9392505050565b600061322883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506137d8565b90505b92915050565b613243826001600160a01b0316613804565b61325f5760405162461bcd60e51b815260040161045090614f15565b60006060836001600160a01b03168360405161327b9190614768565b6000604051808303816000865af19150503d80600081146132b8576040519150601f19603f3d011682016040523d82523d6000602084013e6132bd565b606091505b5091509150816132df5760405162461bcd60e51b815260040161045090614bbb565b80511561178257808060200190518101906132fa9190613d4c565b6117825760405162461bcd60e51b815260040161045090614e75565b600061332182613765565b9050600061332e8361315e565b90506000826001600160a01b031663c0fcb166856040518263ffffffff1660e01b815260040161335e9190614784565b60206040518083038186803b15801561337657600080fd5b505afa15801561338a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ae9190613d4c565b90506000826001600160a01b031663c0fcb166866040518263ffffffff1660e01b81526004016133de9190614784565b60206040518083038186803b1580156133f657600080fd5b505afa15801561340a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061342e9190613d4c565b90506000856001600160a01b03166373a9855c6040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561346d57600080fd5b505af1158015613481573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134a59190614352565b905060008160018111156134b557fe5b1480156134bf5750825b156135235760405163edb12adf60e01b81526001600160a01b0386169063edb12adf906134f0908990600401614784565b600060405180830381600087803b15801561350a57600080fd5b505af115801561351e573d6000803e3d6000fd5b505050505b600181600181111561353157fe5b14801561353b5750815b156109005760405163edb12adf60e01b81526001600160a01b0385169063edb12adf9061356c908990600401614784565b600060405180830381600087803b15801561358657600080fd5b505af115801561359a573d6000803e3d6000fd5b50505050505050505050565b6000828201838110156132285760405162461bcd60e51b815260040161045090614b21565b60606135f184848460405180606001604052806029815260200161527d6029913961383d565b949350505050565b6000826136085750600061322b565b8282028284828161361557fe5b04146136335760405162461bcd60e51b815260040161045090614d37565b6706f05b59d3b2000081018181101561365e5760405162461bcd60e51b815260040161045090614d37565b6000670de0b6b3a7640000825b049695505050505050565b6000828201838110156132285760405162461bcd60e51b815260040161045090614e4b565b60008060006136aa858561386b565b9150915080156136cc5760405162461bcd60e51b815260040161045090614e20565b509392505050565b6000816136f35760405162461bcd60e51b815260040161045090614d61565b826137005750600061322b565b670de0b6b3a76400008381029084828161371657fe5b04146137345760405162461bcd60e51b815260040161045090614c8d565b6002830481018181101561375a5760405162461bcd60e51b815260040161045090614c8d565b600084828161366b57fe5b6000816001600160a01b031663430bf08a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156137a057600080fd5b505afa1580156137b4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061322b9190613cc6565b600081848411156137fc5760405162461bcd60e51b81526004016104509190614a9e565b505050900390565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906135f1575050151592915050565b60608247101561385f5760405162461bcd60e51b815260040161045090614c18565b612d6785858585613890565b6000808383116138815750508082036000613889565b505081810360015b9250929050565b606061389b85613804565b6138b75760405162461bcd60e51b815260040161045090614db4565b60006060866001600160a01b031685876040516138d49190614768565b60006040518083038185875af1925050503d8060008114613911576040519150601f19603f3d011682016040523d82523d6000602084013e613916565b606091505b5091509150811561392a5791506135f19050565b80511561393a5780518082602001fd5b8360405162461bcd60e51b81526004016104509190614a9e565b604051806080016040528060008152602001600081526020016000815260200160006001600160a01b031681525090565b604051806040016040528060008152602001606081525090565b803561322b8161523d565b60008083601f8401126139bb578182fd5b5081356001600160401b038111156139d1578182fd5b602083019150836020808302850101111561388957600080fd5b600082601f8301126139fb578081fd5b8135613a0e613a098261513d565b615117565b818152915060208083019084810181840286018201871015613a2f57600080fd5b60005b84811015613a57578135613a458161523d565b84529282019290820190600101613a32565b505050505092915050565b600082601f830112613a72578081fd5b8151613a80613a098261513d565b818152915060208083019084810181840286018201871015613aa157600080fd5b60005b84811015613a57578151613ab78161523d565b84529282019290820190600101613aa4565b600082601f830112613ad9578081fd5b8135613ae7613a098261513d565b818152915060208083019084810160005b84811015613a575781358701604080601f19838c03011215613b1957600080fd5b613b2281615117565b82860135815290820135906001600160401b03821115613b4157600080fd5b613b4f8b8784860101613bc5565b81870152865250509282019290820190600101613af8565b600082601f830112613b77578081fd5b8135613b85613a098261513d565b818152915060208083019084810181840286018201871015613ba657600080fd5b60005b84811015613a5757813584529282019290820190600101613ba9565b600082601f830112613bd5578081fd5b81356001600160401b03811115613bea578182fd5b613bfd601f8201601f1916602001615117565b9150808252836020828501011115613c1457600080fd5b8060208401602084013760009082016020015292915050565b600060608284031215613c3e578081fd5b50919050565b60006104008284031215613c3e578081fd5b60006101408284031215613c3e578081fd5b600060408284031215613c79578081fd5b613c836040615117565b9050813581526020820135602082015292915050565b600060408284031215613c3e578081fd5b600060208284031215613cbb578081fd5b81356132288161523d565b600060208284031215613cd7578081fd5b81516132288161523d565b60008060408385031215613cf4578081fd5b8235613cff8161523d565b91506020830135613d0f8161523d565b809150509250929050565b600060208284031215613d2b578081fd5b81516001600160401b03811115613d40578182fd5b6135f184828501613a62565b600060208284031215613d5d578081fd5b815161322881615252565b60008060408385031215613cf4578182fd5b600080600060608486031215613d8e578081fd5b8335613d998161523d565b925060208401356001600160401b0380821115613db4578283fd5b613dc0878388016139eb565b93506040860135915080821115613dd5578283fd5b50613de286828701613b67565b9150509250925092565b60008060408385031215613dfe578182fd5b8235613e098161523d565b91506020830135613d0f81615252565b60008060008060008060008060a0898b031215613e34578586fd5b8835613e3f8161523d565b975060208901356001600160401b0380821115613e5a578788fd5b613e668c838d016139aa565b909950975060408b0135915080821115613e7e578586fd5b613e8a8c838d016139aa565b909750955060608b0135915080821115613ea2578485fd5b50613eaf8b828c016139aa565b9094509250506080890135613ec381615252565b809150509295985092959890939650565b60008060008060008060008060006105808a8c031215613ef2578283fd5b613efc8b8b61399f565b9850613f0b8b60208c0161399f565b975060408a01356001600160401b0380821115613f26578485fd5b613f328d838e01613c56565b9850613f418d60608e01613c2d565b9750613f508d60c08e01613c99565b9650613f608d6101008e01613c44565b95506105008c0135915080821115613f76578485fd5b613f828d838e016139eb565b94506105208c0135915080821115613f98578283fd5b50613fa58c828d01613b67565b925050613fb68b6105408c01613c68565b90509295985092959850929598565b6000806000806000806000610120888a031215613fe0578081fd5b8735613feb8161523d565b96506020880135613ffb8161523d565b9550604088013594506060880135935060808801356140198161523d565b92506140288960a08a01613c2d565b91506101008801356001600160401b03811115614043578182fd5b61404f8a828b01613ac9565b91505092959891949750929550565b600080600060608486031215614072578081fd5b833561407d8161523d565b925060208401356001600160401b0380821115614098578283fd5b6140a487838801613b67565b935060408601359150808211156140b9578283fd5b50613de2868287016139eb565b600080604083850312156140d8578182fd5b82356140e38161523d565b946020939093013593505050565b600080600080600060808688031215614108578283fd5b85356141138161523d565b94506020860135935060408601356001600160401b03811115614134578384fd5b614140888289016139aa565b90945092505060608601356141548161523d565b809150509295509295909350565b600080600060608486031215614176578081fd5b83356141818161523d565b92506020840135915060408401356001600160401b038111156141a2578182fd5b613de286828701613b67565b6000806000806000806000610120888a0312156141c9578081fd5b87356141d48161523d565b96506020880135955060408801356001600160401b03808211156141f6578283fd5b6142028b838c01613b67565b965060608a0135955060808a0135915061421b8261523d565b81945061422b8b60a08c01613c2d565b93506101008a0135915080821115614241578283fd5b5061404f8a828b01613ac9565b60008060008060608587031215614263578182fd5b843561426e8161523d565b93506020850135925060408501356001600160401b0381111561428f578283fd5b61429b878288016139aa565b95989497509550505050565b600080600080600060a086880312156142be578283fd5b85356142c98161523d565b945060208601356142d98161523d565b935060408601356142e98161523d565b925060608601356142f98161523d565b949793965091946080013592915050565b6000806040838503121561431c578182fd5b82356143278161523d565b915060208301356001600160401b03811115614341578182fd5b830160e08186031215613d0f578182fd5b600060208284031215614363578081fd5b815161322881615260565b60006020828403121561437f578081fd5b813560088110613228578182fd5b60006020828403121561439e578081fd5b813560048110613228578182fd5b600080600080608085870312156143c1578182fd5b84516143cc8161526d565b60208601519094506143dd8161526d565b6040860151606090960151949790965092505050565b6000610160808385031215614406578182fd5b61440f81615117565b9050825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152506101208084015181830152506101408084015181830152508091505092915050565b60006020828403121561449a578081fd5b5051919050565b600080604083850312156144b3578182fd5b8251915060208301516001600160401b038111156144cf578182fd5b6144db85828601613a62565b9150509250929050565b600080604083850312156144f7578182fd5b825191506020808401516001600160401b03811115614514578283fd5b8401601f81018613614524578283fd5b8051614532613a098261513d565b81815283810190838501858402850186018a101561454e578687fd5b8694505b83851015614570578051835260019490940193918501918501614552565b5080955050505050509250929050565b6001600160a01b03169052565b60008284526020808501945082825b858110156145ca5781356145af8161523d565b6001600160a01b03168752958201959082019060010161459c565b509495945050505050565b6000815180845260208085019450808401835b838110156145ca5781516001600160a01b0316875295820195908201906001016145e8565b81835260006001600160fb1b03831115614625578081fd5b6020830280836020870137939093016020019283525090919050565b6000815180845260208085019450808401835b838110156145ca57815187529582019590820190600101614654565b60008284528282602086013780602084860101526020601f19601f85011685010190509392505050565b600281106146a457fe5b9052565b600381106146a457fe5b6146bc828261471b565b6101006146cd81840182840161471b565b506102006146df81840182840161471b565b5061030061254f81840182840161471b565b80356146fc81615252565b15158252602081013561470e81615252565b8015156020840152505050565b6147258282614759565b6147356040830160408301614759565b6147456080830160808301614759565b61475560c0830160c08301614759565b5050565b80358252602090810135910152565b6000825161477a818460208701615211565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b039586168152938516602085015260408401929092529092166060820152608081019190915260a00190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0386168152602081018590526040810184905260c081016008841061484a57fe5b83606083015282516080830152602083015160a08301529695505050505050565b6001600160a01b039485168152928416602084015292166040820152606081019190915260800190565b6001600160a01b03871681526104c0602082018190526000906148b888806151b0565b610140838601526148ce61060086018284614670565b925050506148df60208901896151b0565b6104bf1980868503016104e08701526148f9848385614670565b935061490860408c018c615169565b93509150808685030161050087015261492284848461458d565b935061493160608c018c615169565b93509150808685030161052087015261494b84848461460d565b935061495a60808c018c615169565b9350915080868503016105408701525061497583838361460d565b9250505060a088013561056084015260c088013561058084015260e08801356105a08401526101008801356105c08401526149b46101208901896151f4565b6149c26105e085018261469a565b506149d060408401886146f1565b6149dd60808401876146b2565b8281036104808401526149f081866145d5565b90508281036104a0840152612e468185614641565b6001600160a01b039390931683526020830191909152604082015260600190565b600060408252614a3960408301856145d5565b8281036020840152612d678185614641565b600060408252614a5e6040830185614641565b8281036020840152612d6781856145d5565b901515815260200190565b600f94850b81529290930b60208301526040820152606081019190915260800190565b6000602082528251806020840152614abd816040850160208701615211565b601f01601f19169190910160400192915050565b60208082526009908201526808aa4a49ea4be8aa8960bb1b604082015260600190565b60208082526013908201527208aa4a4be988a9c8ea890be9a92a69a82a8869606b1b604082015260600190565b6020808252601b908201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604082015260600190565b60208082526018908201527708aa4a4bea89e968a9c988a9c8ea890be9a92a69a82a886960431b604082015260600190565b60208082526019908201527811549497d513d2d15397d393d517d5d2125511531254d51151603a1b604082015260600190565b6020808252818101527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604082015260600190565b6020808252600e908201526d1353d115531157d253131151d05360921b604082015260600190565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6040820152651c8818d85b1b60d21b606082015260800190565b6020808252601590820152744552525f494e56414c49445f535741505f5459504560581b604082015260600190565b60208082526010908201526f11549497d1125597d25395115493905360821b604082015260600190565b6020808252600b908201526a4552525f5245454e54525960a81b604082015260600190565b6020808252600e908201526d1493d555115497d253131151d05360921b604082015260600190565b60208082526019908201527811549497d4d210549157d05353d5539517d513d7d4d3505313603a1b604082015260600190565b60208082526010908201526f4552525f4d554c5f4f564552464c4f5760801b604082015260600190565b6020808252600c908201526b4552525f4449565f5a45524f60a01b604082015260600190565b60208082526013908201527211549497d514905394d1915497d19052531151606a1b604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252601b908201527a11549497d49150d152559157d05353d5539517d513d7d4d3505313602a1b604082015260600190565b6020808252601190820152704552525f5355425f554e444552464c4f5760781b604082015260600190565b60208082526010908201526f4552525f4144445f4f564552464c4f5760801b604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b60208082526036908201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60408201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b606082015260800190565b6020808252601f908201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604082015260600190565b6000602082528235614f5d8161523d565b6001600160a01b0390811660208481019190915284013590614f7e8261523d565b808216604085015260408501359150614f968261523d565b80821660608501525050614fad606084018461515c565b614fba6080840182614580565b50614fc86080840184615201565b614fd560a08401826146a8565b5060a083013560c0830152614fed60c08401846151b0565b60e080850152612d6761010085018284614670565b90815260200190565b6000848252606060208301526150246060830185614641565b905060018060a01b0383166040830152949350505050565b6000858252608060208301526150556080830186614641565b6001600160a01b039485166040840152929093166060909101529392505050565b600086825285602083015260a0604083015261509560a08301866145d5565b6001600160a01b0394909416606083015250608001529392505050565b600084825283602083015260606040830152612d676060830184614641565b6000808335601e198436030181126150e7578283fd5b8301803591506001600160401b03821115615100578283fd5b602090810192508102360382131561388957600080fd5b6040518181016001600160401b038111828210171561513557600080fd5b604052919050565b60006001600160401b03821115615152578081fd5b5060209081020190565b600082356132288161523d565b6000808335601e1984360301811261517f578283fd5b83016020810192503590506001600160401b0381111561519e57600080fd5b60208102360383131561388957600080fd5b6000808335601e198436030181126151c6578283fd5b83016020810192503590506001600160401b038111156151e557600080fd5b80360383131561388957600080fd5b6000823561322881615260565b6000823560038110613228578182fd5b60005b8381101561522c578181015183820152602001615214565b838111156117825750506000910152565b6001600160a01b0381168114610bf457600080fd5b8015158114610bf457600080fd5b60028110610bf457600080fd5b80600f0b8114610bf457600080fdfe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a26469706673582212203c3ca2552600b9d71ca0039f049cf11f5bee3ba14509533b77525f353ef23f8464736f6c634300060c0033
Loading...
Loading
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.