Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import "../../core/base/UpgradeableProxy.sol";
import "../../interfaces/IControllable.sol";
import "../../interfaces/IPlatform.sol";
import "../../interfaces/IFactory.sol";
import "../../interfaces/IVaultProxy.sol";
/// @title EIP1967 Upgradeable proxy implementation for built by factory vaults
/// @author Alien Deployer (https://github.com/a17)
contract VaultProxy is UpgradeableProxy, IVaultProxy {
/// @dev Vault type ID
bytes32 private constant _TYPE_SLOT = bytes32(uint(keccak256("eip1967.vaultProxy.type")) - 1);
/// @inheritdoc IVaultProxy
function initProxy(string memory type_) external {
bytes32 typeHash = keccak256(abi.encodePacked(type_));
//slither-disable-next-line unused-return
(, address vaultImplementation,,,) = IFactory(msg.sender).vaultConfig(typeHash);
_init(vaultImplementation);
bytes32 slot = _TYPE_SLOT;
//slither-disable-next-line assembly
assembly {
sstore(slot, typeHash)
}
}
/// @inheritdoc IVaultProxy
function upgrade() external {
if (msg.sender != IPlatform(IControllable(address(this)).platform()).factory()) {
revert ProxyForbidden();
}
bytes32 typeHash;
bytes32 slot = _TYPE_SLOT;
//slither-disable-next-line assembly
assembly {
typeHash := sload(slot)
}
//slither-disable-next-line unused-return
(, address vaultImplementation,,,) = IFactory(msg.sender).vaultConfig(typeHash);
_upgradeTo(vaultImplementation);
}
/// @inheritdoc IVaultProxy
function implementation() external view returns (address) {
return _implementation();
}
/// @inheritdoc IVaultProxy
function vaultTypeHash() external view returns (bytes32) {
bytes32 typeHash;
bytes32 slot = _TYPE_SLOT;
//slither-disable-next-line assembly
assembly {
typeHash := sload(slot)
}
return typeHash;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/// @title Simple ERC-1967 upgradeable proxy implementation
abstract contract UpgradeableProxy {
error ImplementationIsNotContract();
/// @dev This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// @dev Emitted when the implementation is upgraded.
event Upgraded(address indexed implementation);
constructor() {
assert(_IMPLEMENTATION_SLOT == bytes32(uint(keccak256("eip1967.proxy.implementation")) - 1));
}
/// @dev Post deploy initialisation for compatability with EIP-1167 factory
function _init(address _logic) internal {
// nosemgrep
require(_implementation() == address(0), "Already inited");
_setImplementation(_logic);
}
/// @dev Returns the current implementation address.
function _implementation() internal view virtual returns (address impl) {
bytes32 slot = _IMPLEMENTATION_SLOT;
// solhint-disable-next-line no-inline-assembly
//slither-disable-next-line assembly
assembly {
impl := sload(slot)
}
}
/// @dev Upgrades the proxy to a new implementation.
function _upgradeTo(address newImplementation) internal virtual {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/// @dev Stores a new address in the EIP1967 implementation slot.
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) revert ImplementationIsNotContract();
bytes32 slot = _IMPLEMENTATION_SLOT;
// solhint-disable-next-line no-inline-assembly
//slither-disable-next-line assembly
assembly {
sstore(slot, newImplementation)
}
}
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual {
//slither-disable-next-line assembly
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _fallback() internal virtual {
_delegate(_implementation());
}
/// @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
/// function in the contract matches the call data.
//slither-disable-next-line locked-ether
fallback() external payable virtual {
_fallback();
}
/// @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
/// is empty.
//slither-disable-next-line locked-ether
receive() external payable virtual {
_fallback();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/// @dev Base core interface implemented by most platform contracts.
/// Inherited contracts store an immutable Platform proxy address in the storage,
/// which provides authorization capabilities and infrastructure contract addresses.
/// @author Alien Deployer (https://github.com/a17)
/// @author JodsMigel (https://github.com/JodsMigel)
interface IControllable {
//region ----- Custom Errors -----
error IncorrectZeroArgument();
error IncorrectMsgSender();
error NotGovernance();
error NotMultisig();
error NotGovernanceAndNotMultisig();
error NotOperator();
error NotFactory();
error NotPlatform();
error NotVault();
error IncorrectArrayLength();
error AlreadyExist();
error NotExist();
error NotTheOwner();
error ETHTransferFailed();
error IncorrectInitParams();
//endregion -- Custom Errors -----
event ContractInitialized(address platform, uint ts, uint block);
/// @notice Stability Platform main contract address
function platform() external view returns (address);
/// @notice Version of contract implementation
/// @dev SemVer scheme MAJOR.MINOR.PATCH
//slither-disable-next-line naming-convention
function VERSION() external view returns (string memory);
/// @notice Block number when contract was initialized
function createdBlock() external view returns (uint);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/// @notice Interface of the main contract and entry point to the platform.
/// @author Alien Deployer (https://github.com/a17)
/// @author Jude (https://github.com/iammrjude)
/// @author JodsMigel (https://github.com/JodsMigel)
interface IPlatform {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error AlreadyAnnounced();
error SameVersion();
error NoNewVersion();
error UpgradeTimerIsNotOver(uint TimerTimestamp);
error IncorrectFee(uint minFee, uint maxFee);
error NotEnoughAllowedBBToken();
error TokenAlreadyExistsInSet(address token);
error AggregatorNotExists(address dexAggRouter);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
event PlatformVersion(string version);
event UpgradeAnnounce(
string oldVersion, string newVersion, address[] proxies, address[] newImplementations, uint timelock
);
event CancelUpgrade(string oldVersion, string newVersion);
event ProxyUpgraded(
address indexed proxy, address implementation, string oldContractVersion, string newContractVersion
);
event Addresses(
address multisig_,
address factory_,
address priceReader_,
address swapper_,
address buildingPermitToken_,
address vaultManager_,
address strategyLogic_,
address aprOracle_,
address hardWorker,
address rebalancer,
address zap,
address bridge
);
event OperatorAdded(address operator);
event OperatorRemoved(address operator);
event FeesChanged(uint fee, uint feeShareVaultManager, uint feeShareStrategyLogic, uint feeShareEcosystem);
event MinInitialBoostChanged(uint minInitialBoostPerDay, uint minInitialBoostDuration);
event NewAmmAdapter(string id, address proxy);
event EcosystemRevenueReceiver(address receiver);
event SetAllowedBBTokenVaults(address bbToken, uint vaultsToBuild, bool firstSet);
event RemoveAllowedBBToken(address bbToken);
event AddAllowedBoostRewardToken(address token);
event RemoveAllowedBoostRewardToken(address token);
event AddDefaultBoostRewardToken(address token);
event RemoveDefaultBoostRewardToken(address token);
event AddBoostTokens(address[] allowedBoostRewardToken, address[] defaultBoostRewardToken);
event AllowedBBTokenVaultUsed(address bbToken, uint vaultToUse);
event AddDexAggregator(address router);
event RemoveDexAggregator(address router);
event MinTvlForFreeHardWorkChanged(uint oldValue, uint newValue);
event CustomVaultFee(address vault, uint platformFee);
event Rebalancer(address rebalancer_);
event Bridge(address bridge_);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DATA TYPES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
struct PlatformUpgrade {
string newVersion;
address[] proxies;
address[] newImplementations;
}
struct PlatformSettings {
string networkName;
bytes32 networkExtra;
uint fee;
uint feeShareVaultManager;
uint feeShareStrategyLogic;
uint feeShareEcosystem;
uint minInitialBoostPerDay;
uint minInitialBoostDuration;
}
struct AmmAdapter {
string id;
address proxy;
}
struct SetupAddresses {
address factory;
address priceReader;
address swapper;
address buildingPermitToken;
address buildingPayPerVaultToken;
address vaultManager;
address strategyLogic;
address aprOracle;
address targetExchangeAsset;
address hardWorker;
address zap;
address bridge;
address rebalancer;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* VIEW FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Platform version in CalVer scheme: YY.MM.MINOR-tag. Updates on core contract upgrades.
function platformVersion() external view returns (string memory);
/// @notice Time delay for proxy upgrades of core contracts and changing important platform settings by multisig
//slither-disable-next-line naming-convention
function TIME_LOCK() external view returns (uint);
/// @notice DAO governance
function governance() external view returns (address);
/// @notice Core team multi signature wallet. Development and operations fund
function multisig() external view returns (address);
/// @notice This NFT allow user to build limited number of vaults per week
function buildingPermitToken() external view returns (address);
/// @notice This ERC20 token is used as payment token for vault building
function buildingPayPerVaultToken() external view returns (address);
/// @notice Receiver of ecosystem revenue
function ecosystemRevenueReceiver() external view returns (address);
/// @dev The best asset in a network for swaps between strategy assets and farms rewards assets
/// The target exchange asset is used for finding the best strategy's exchange asset.
/// Rhe fewer routes needed to swap to the target exchange asset, the better.
function targetExchangeAsset() external view returns (address);
/// @notice Platform factory assembling vaults. Stores settings, strategy logic, farms.
/// Provides the opportunity to upgrade vaults and strategies.
/// @return Address of Factory proxy
function factory() external view returns (address);
/// @notice The holders of these NFT receive a share of the vault revenue
/// @return Address of VaultManager proxy
function vaultManager() external view returns (address);
/// @notice The holders of these tokens receive a share of the revenue received in all vaults using this strategy logic.
function strategyLogic() external view returns (address);
/// @notice Combining oracle and DeX spot prices
/// @return Address of PriceReader proxy
function priceReader() external view returns (address);
/// @notice Providing underlying assets APRs on-chain
/// @return Address of AprOracle proxy
function aprOracle() external view returns (address);
/// @notice On-chain price quoter and swapper
/// @return Address of Swapper proxy
function swapper() external view returns (address);
/// @notice HardWork resolver and caller
/// @return Address of HardWorker proxy
function hardWorker() external view returns (address);
/// @notice Rebalance resolver
/// @return Address of Rebalancer proxy
function rebalancer() external view returns (address);
/// @notice ZAP feature
/// @return Address of Zap proxy
function zap() external view returns (address);
/// @notice Stability Bridge
/// @return Address of Bridge proxy
function bridge() external view returns (address);
/// @notice Name of current EVM network
function networkName() external view returns (string memory);
/// @notice Minimal initial boost rewards per day USD amount which needs to create rewarding vault
function minInitialBoostPerDay() external view returns (uint);
/// @notice Minimal boost rewards vesting duration for initial boost
function minInitialBoostDuration() external view returns (uint);
/// @notice This function provides the timestamp of the platform upgrade timelock.
/// @dev This function is an external view function, meaning it doesn't modify the state.
/// @return uint representing the timestamp of the platform upgrade timelock.
function platformUpgradeTimelock() external view returns (uint);
/// @dev Extra network data
/// @return 0-2 bytes - color
/// 3-5 bytes - background color
/// 6-31 bytes - free
function networkExtra() external view returns (bytes32);
/// @notice Pending platform upgrade data
function pendingPlatformUpgrade() external view returns (PlatformUpgrade memory);
/// @notice Get platform revenue fee settings
/// @return fee Revenue fee % (between MIN_FEE - MAX_FEE) with DENOMINATOR precision.
/// @return feeShareVaultManager Revenue fee share % of VaultManager tokenId owner
/// @return feeShareStrategyLogic Revenue fee share % of StrategyLogic tokenId owner
/// @return feeShareEcosystem Revenue fee share % of ecosystemFeeReceiver
function getFees()
external
view
returns (uint fee, uint feeShareVaultManager, uint feeShareStrategyLogic, uint feeShareEcosystem);
/// @notice Get custom vault platform fee
/// @return fee revenue fee % with DENOMINATOR precision
function getCustomVaultFee(address vault) external view returns (uint fee);
/// @notice Platform settings
function getPlatformSettings() external view returns (PlatformSettings memory);
/// @notice AMM adapters of the platform
function getAmmAdapters() external view returns (string[] memory id, address[] memory proxy);
/// @notice Get AMM adapter data by hash
/// @param ammAdapterIdHash Keccak256 hash of adapter ID string
/// @return ID string and proxy address of AMM adapter
function ammAdapter(bytes32 ammAdapterIdHash) external view returns (AmmAdapter memory);
/// @notice Allowed buy-back tokens for rewarding vaults
function allowedBBTokens() external view returns (address[] memory);
/// @notice Vaults building limit for buy-back token.
/// This limit decrements when a vault for BB-token is built.
/// @param token Allowed buy-back token
/// @return vaultsLimit Number of vaults that can be built for BB-token
function allowedBBTokenVaults(address token) external view returns (uint vaultsLimit);
/// @notice Vaults building limits for allowed buy-back tokens.
/// @return bbToken Allowed buy-back tokens
/// @return vaultsLimit Number of vaults that can be built for BB-tokens
function allowedBBTokenVaults() external view returns (address[] memory bbToken, uint[] memory vaultsLimit);
/// @notice Non-zero vaults building limits for allowed buy-back tokens.
/// @return bbToken Allowed buy-back tokens
/// @return vaultsLimit Number of vaults that can be built for BB-tokens
function allowedBBTokenVaultsFiltered()
external
view
returns (address[] memory bbToken, uint[] memory vaultsLimit);
/// @notice Check address for existance in operators list
/// @param operator Address
/// @return True if this address is Stability Operator
function isOperator(address operator) external view returns (bool);
/// @notice Tokens that can be used for boost rewards of rewarding vaults
/// @return Addresses of tokens
function allowedBoostRewardTokens() external view returns (address[] memory);
/// @notice Allowed boost reward tokens that used for unmanaged rewarding vaults creation
/// @return Addresses of tokens
function defaultBoostRewardTokens() external view returns (address[] memory);
/// @notice Allowed boost reward tokens that used for unmanaged rewarding vaults creation
/// @param addressToRemove This address will be removed from default boost reward tokens
/// @return Addresses of tokens
function defaultBoostRewardTokensFiltered(address addressToRemove) external view returns (address[] memory);
/// @notice Allowed DeX aggregators
/// @return Addresses of DeX aggregator rounters
function dexAggregators() external view returns (address[] memory);
/// @notice DeX aggregator router address is allowed to be used in the platform
/// @param dexAggRouter Address of DeX aggreagator router
/// @return Can be used
function isAllowedDexAggregatorRouter(address dexAggRouter) external view returns (bool);
/// @notice Show minimum TVL for compensate if vault has not enough ETH
/// @return Minimum TVL for compensate.
function minTvlForFreeHardWork() external view returns (uint);
/// @notice Front-end platform viewer
/// @return platformAddresses Platform core addresses
/// platformAddresses[0] factory
/// platformAddresses[1] vaultManager
/// platformAddresses[2] strategyLogic
/// platformAddresses[3] buildingPermitToken
/// platformAddresses[4] buildingPayPerVaultToken
/// platformAddresses[5] governance
/// platformAddresses[6] multisig
/// platformAddresses[7] zap
/// platformAddresses[8] bridge
/// @return bcAssets Blue chip token addresses
/// @return dexAggregators_ DeX aggregators allowed to be used entire the platform
/// @return vaultType Vault type ID strings
/// @return vaultExtra Vault color, background color and other extra data. Index of vault same as in previous array.
/// @return vaultBulldingPrice Price of creating new vault in buildingPayPerVaultToken. Index of vault same as in previous array.
/// @return strategyId Strategy logic ID strings
/// @return isFarmingStrategy True if strategy is farming strategy. Index of strategy same as in previous array.
/// @return strategyTokenURI StrategyLogic NFT tokenId metadata and on-chain image. Index of strategy same as in previous array.
/// @return strategyExtra Strategy color, background color and other extra data. Index of strategy same as in previous array.
function getData()
external
view
returns (
address[] memory platformAddresses,
address[] memory bcAssets,
address[] memory dexAggregators_,
string[] memory vaultType,
bytes32[] memory vaultExtra,
uint[] memory vaultBulldingPrice,
string[] memory strategyId,
bool[] memory isFarmingStrategy,
string[] memory strategyTokenURI,
bytes32[] memory strategyExtra
);
// todo add vaultSymbol, vaultName
/// @notice Front-end balances, prices and vault list viewer
/// @param yourAccount Address of account to query balances
/// @return token Tokens supported by the platform
/// @return tokenPrice USD price of token. Index of token same as in previous array.
/// @return tokenUserBalance User balance of token. Index of token same as in previous array.
/// @return vault Deployed vaults
/// @return vaultSharePrice Price 1.0 vault share. Index of vault same as in previous array.
/// @return vaultUserBalance User balance of vault. Index of vault same as in previous array.
/// @return nft Ecosystem NFTs
/// nft[0] BuildingPermitToken
/// nft[1] VaultManager
/// nft[2] StrategyLogic
/// @return nftUserBalance User balance of NFT. Index of NFT same as in previous array.
/// @return buildingPayPerVaultTokenBalance User balance of vault creation paying token
function getBalance(address yourAccount)
external
view
returns (
address[] memory token,
uint[] memory tokenPrice,
uint[] memory tokenUserBalance,
address[] memory vault,
uint[] memory vaultSharePrice,
uint[] memory vaultUserBalance,
address[] memory nft,
uint[] memory nftUserBalance,
uint buildingPayPerVaultTokenBalance
);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WRITE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Add platform operator.
/// Only governance and multisig can add operator.
/// @param operator Address of new operator
function addOperator(address operator) external;
/// @notice Remove platform operator.
/// Only governance and multisig can remove operator.
/// @param operator Address of operator to remove
function removeOperator(address operator) external;
/// @notice Announce upgrade of platform proxies implementations
/// Only governance and multisig can announce platform upgrades.
/// @param newVersion New platform version. Version must be changed when upgrading.
/// @param proxies Addresses of core contract proxies
/// @param newImplementations New implementation for proxy. Index of proxy same as in previous array.
function announcePlatformUpgrade(
string memory newVersion,
address[] memory proxies,
address[] memory newImplementations
) external;
/// @notice Upgrade platform
/// Only operator (multisig is operator too) can ececute pending platform upgrade
function upgrade() external;
/// @notice Cancel pending platform upgrade
/// Only operator (multisig is operator too) can ececute pending platform upgrade
function cancelUpgrade() external;
/// @notice Register AMM adapter in platform
/// @param id AMM adapter ID string from AmmAdapterIdLib
/// @param proxy Address of AMM adapter proxy
function addAmmAdapter(string memory id, address proxy) external;
// todo Only governance and multisig can set allowed bb-token vaults building limit
/// @notice Set new vaults building limit for buy-back token
/// @param bbToken Address of allowed buy-back token
/// @param vaultsToBuild Number of vaults that can be built for BB-token
function setAllowedBBTokenVaults(address bbToken, uint vaultsToBuild) external;
// todo Only governance and multisig can add allowed boost reward token
/// @notice Add new allowed boost reward token
/// @param token Address of token
function addAllowedBoostRewardToken(address token) external;
// todo Only governance and multisig can remove allowed boost reward token
/// @notice Remove allowed boost reward token
/// @param token Address of allowed boost reward token
function removeAllowedBoostRewardToken(address token) external;
// todo Only governance and multisig can add default boost reward token
/// @notice Add default boost reward token
/// @param token Address of default boost reward token
function addDefaultBoostRewardToken(address token) external;
// todo Only governance and multisig can remove default boost reward token
/// @notice Remove default boost reward token
/// @param token Address of allowed boost reward token
function removeDefaultBoostRewardToken(address token) external;
// todo Only governance and multisig can add allowed boost reward token
// todo Only governance and multisig can add default boost reward token
/// @notice Add new allowed boost reward token
/// @notice Add default boost reward token
/// @param allowedBoostRewardToken Address of allowed boost reward token
/// @param defaultBoostRewardToken Address of default boost reward token
function addBoostTokens(
address[] memory allowedBoostRewardToken,
address[] memory defaultBoostRewardToken
) external;
/// @notice Decrease allowed BB-token vault building limit when vault is built
/// Only Factory can do it.
/// @param bbToken Address of allowed buy-back token
function useAllowedBBTokenVault(address bbToken) external;
/// @notice Allow DeX aggregator routers to be used in the platform
/// @param dexAggRouter Addresses of DeX aggreagator routers
function addDexAggregators(address[] memory dexAggRouter) external;
/// @notice Remove allowed DeX aggregator router from the platform
/// @param dexAggRouter Address of DeX aggreagator router
function removeDexAggregator(address dexAggRouter) external;
/// @notice Change initial boost rewards settings
/// @param minInitialBoostPerDay_ Minimal initial boost rewards per day USD amount which needs to create rewarding vault
/// @param minInitialBoostDuration_ Minimal boost rewards vesting duration for initial boost
function setInitialBoost(uint minInitialBoostPerDay_, uint minInitialBoostDuration_) external;
/// @notice Update new minimum TVL for compensate.
/// @param value New minimum TVL for compensate.
function setMinTvlForFreeHardWork(uint value) external;
/// @notice Set custom platform fee for vault
/// @param vault Vault address
/// @param platformFee Custom platform fee
function setCustomVaultFee(address vault, uint platformFee) external;
/// @notice Setup Rebalancer.
/// Only Goverannce or Multisig can do this when Rebalancer is not set.
/// @param rebalancer_ Proxy address of Bridge
function setupRebalancer(address rebalancer_) external;
/// @notice Setup Bridge.
/// Only Goverannce or Multisig can do this when Bridge is not set.
/// @param bridge_ Proxy address of Bridge
function setupBridge(address bridge_) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
/// @notice Creating vaults, upgrading vaults and strategies, vault list, farms and strategy logics management
/// @author Alien Deployer (https://github.com/a17)
/// @author Jude (https://github.com/iammrjude)
/// @author JodsMigel (https://github.com/JodsMigel)
/// @author HCrypto7 (https://github.com/hcrypto7)
interface IFactory {
//region ----- Custom Errors -----
error VaultImplementationIsNotAvailable();
error VaultNotAllowedToDeploy();
error StrategyImplementationIsNotAvailable();
error StrategyLogicNotAllowedToDeploy();
error YouDontHaveEnoughTokens(uint userBalance, uint requireBalance, address payToken);
error SuchVaultAlreadyDeployed(bytes32 key);
error NotActiveVault();
error UpgradeDenied(bytes32 _hash);
error AlreadyLastVersion(bytes32 _hash);
error NotStrategy();
error BoostDurationTooLow();
error BoostAmountTooLow();
error BoostAmountIsZero();
//endregion ----- Custom Errors -----
//region ----- Events -----
event VaultAndStrategy(
address indexed deployer,
string vaultType,
string strategyId,
address vault,
address strategy,
string name,
string symbol,
address[] assets,
bytes32 deploymentKey,
uint vaultManagerTokenId
);
event StrategyProxyUpgraded(address proxy, address oldImplementation, address newImplementation);
event VaultProxyUpgraded(address proxy, address oldImplementation, address newImplementation);
event VaultConfigChanged(
string type_, address implementation, bool deployAllowed, bool upgradeAllowed, bool newVaultType
);
event StrategyLogicConfigChanged(
string id, address implementation, bool deployAllowed, bool upgradeAllowed, bool newStrategy
);
event VaultStatus(address indexed vault, uint newStatus);
event NewFarm(Farm[] farms);
event UpdateFarm(uint id, Farm farm);
event SetStrategyAvailableInitParams(string id, address[] initAddresses, uint[] initNums, int24[] initTicks);
event AliasNameChanged(address indexed operator, address indexed tokenAddress, string newAliasName);
//endregion -- Events -----
//region ----- Data types -----
/// @custom:storage-location erc7201:stability.Factory
struct FactoryStorage {
/// @inheritdoc IFactory
mapping(bytes32 typeHash => VaultConfig) vaultConfig;
/// @inheritdoc IFactory
mapping(bytes32 idHash => StrategyLogicConfig) strategyLogicConfig;
/// @inheritdoc IFactory
mapping(bytes32 deploymentKey => address vaultProxy) deploymentKey;
/// @inheritdoc IFactory
mapping(address vault => uint status) vaultStatus;
/// @inheritdoc IFactory
mapping(address address_ => bool isStrategy_) isStrategy;
EnumerableSet.Bytes32Set vaultTypeHashes;
EnumerableSet.Bytes32Set strategyLogicIdHashes;
mapping(uint week => mapping(uint builderPermitTokenId => uint vaultsBuilt)) vaultsBuiltByPermitTokenId;
address[] deployedVaults;
Farm[] farms;
/// @inheritdoc IFactory
mapping(bytes32 idHash => StrategyAvailableInitParams) strategyAvailableInitParams;
mapping(address tokenAddress => string aliasName) aliasNames;
}
struct VaultConfig {
string vaultType;
address implementation;
bool deployAllowed;
bool upgradeAllowed;
uint buildingPrice;
}
struct StrategyLogicConfig {
string id;
address implementation;
bool deployAllowed;
bool upgradeAllowed;
bool farming;
uint tokenId;
}
struct Farm {
uint status;
address pool;
string strategyLogicId;
address[] rewardAssets;
address[] addresses;
uint[] nums;
int24[] ticks;
}
struct StrategyAvailableInitParams {
address[] initAddresses;
uint[] initNums;
int24[] initTicks;
}
//endregion -- Data types -----
//region ----- View functions -----
/// @notice All vaults deployed by the factory
/// @return Vault proxy addresses
function deployedVaults() external view returns (address[] memory);
/// @notice Total vaults deployed
function deployedVaultsLength() external view returns (uint);
/// @notice Get vault by VaultManager tokenId
/// @param id Vault array index. Same as tokenId of VaultManager NFT
/// @return Address of VaultProxy
function deployedVault(uint id) external view returns (address);
/// @notice All farms known by the factory in current network
function farms() external view returns (Farm[] memory);
/// @notice Total farms known by the factory in current network
function farmsLength() external view returns (uint);
/// @notice Farm data by farm index
/// @param id Index of farm
function farm(uint id) external view returns (Farm memory);
/// @notice Strategy logic settings
/// @param idHash keccak256 hash of strategy logic string ID
/// @return config Strategy logic settings
function strategyLogicConfig(bytes32 idHash) external view returns (StrategyLogicConfig memory config);
/// @notice All known strategies
/// @return Array of keccak256 hashes of strategy logic string ID
function strategyLogicIdHashes() external view returns (bytes32[] memory);
// todo remove, use new function without calculating vault symbol on the fly for not initialized vaults
// factory required that special functionally only internally, not for interface
function getStrategyData(
string memory vaultType,
address strategyAddress,
address bbAsset
)
external
view
returns (
string memory strategyId,
address[] memory assets,
string[] memory assetsSymbols,
string memory specificName,
string memory vaultSymbol
);
/// @dev Get best asset of assets to be strategy exchange asset
function getExchangeAssetIndex(address[] memory assets) external view returns (uint);
/// @notice Deployment key of created vault
/// @param deploymentKey_ Hash of concatenated unique vault and strategy initialization parameters
/// @return Address of deployed vault
function deploymentKey(bytes32 deploymentKey_) external view returns (address);
/// @notice Calculating deployment key based on unique vault and strategy initialization parameters
/// @param vaultType Vault type string
/// @param strategyId Strategy logic Id string
/// @param vaultInitAddresses Vault initizlization addresses for deployVaultAndStrategy method
/// @param vaultInitNums Vault initizlization uint numbers for deployVaultAndStrategy method
/// @param strategyInitAddresses Strategy initizlization addresses for deployVaultAndStrategy method
/// @param strategyInitNums Strategy initizlization uint numbers for deployVaultAndStrategy method
/// @param strategyInitTicks Strategy initizlization int24 ticks for deployVaultAndStrategy method
function getDeploymentKey(
string memory vaultType,
string memory strategyId,
address[] memory vaultInitAddresses,
uint[] memory vaultInitNums,
address[] memory strategyInitAddresses,
uint[] memory strategyInitNums,
int24[] memory strategyInitTicks
) external returns (bytes32);
/// @notice Available variants of new vault for creating.
/// The structure of the function's output values is complex,
/// but after parsing them, the front end has all the data to generate a list of vaults to create.
/// @return desc Descriptions of the strategy for making money
/// @return vaultType Vault type strings. Output values are matched by index with previous array.
/// @return strategyId Strategy logic ID strings. Output values are matched by index with previous array.
/// @return initIndexes Map of start and end indexes in next 5 arrays. Output values are matched by index with previous array.
/// [0] Start index in vaultInitAddresses
/// [1] End index in vaultInitAddresses
/// [2] Start index in vaultInitNums
/// [3] End index in vaultInitNums
/// [4] Start index in strategyInitAddresses
/// [5] End index in strategyInitAddresses
/// [6] Start index in strategyInitNums
/// [7] End index in strategyInitNums
/// [8] Start index in strategyInitTicks
/// [9] End index in strategyInitTicks
/// @return vaultInitAddresses Vault initizlization addresses for deployVaultAndStrategy method for all building variants.
/// @return vaultInitNums Vault initizlization uint numbers for deployVaultAndStrategy method for all building variants.
/// @return strategyInitAddresses Strategy initizlization addresses for deployVaultAndStrategy method for all building variants.
/// @return strategyInitNums Strategy initizlization uint numbers for deployVaultAndStrategy method for all building variants.
/// @return strategyInitTicks Strategy initizlization int24 ticks for deployVaultAndStrategy method for all building variants.
function whatToBuild()
external
view
returns (
string[] memory desc,
string[] memory vaultType,
string[] memory strategyId,
uint[10][] memory initIndexes,
address[] memory vaultInitAddresses,
uint[] memory vaultInitNums,
address[] memory strategyInitAddresses,
uint[] memory strategyInitNums,
int24[] memory strategyInitTicks
);
/// @notice Governance and multisig can set a vault status other than Active - the default status.
/// HardWorker only works with active vaults.
/// @return status Constant from VaultStatusLib
function vaultStatus(address vault) external view returns (uint status);
/// @notice Check that strategy proxy deployed by the Factory
/// @param address_ Address of contract
/// @return This address is our strategy proxy
function isStrategy(address address_) external view returns (bool);
/// @notice How much vaults was built by builderPermitToken NFT tokenId in week
/// @param week Week index (timestamp / (86400 * 7))
/// @param builderPermitTokenId Token ID of buildingPermitToken NFT
/// @return vaultsBuilt Vaults built
function vaultsBuiltByPermitTokenId(
uint week,
uint builderPermitTokenId
) external view returns (uint vaultsBuilt);
/// @notice Data on all factory strategies.
/// The output values are matched by index in the arrays.
/// @return id Strategy logic ID strings
/// @return deployAllowed New vaults can be deployed
/// @return upgradeAllowed Strategy can be upgraded
/// @return farming It is farming strategy (earns farming/gauge rewards)
/// @return tokenId Token ID of StrategyLogic NFT
/// @return tokenURI StrategyLogic NFT tokenId metadata and on-chain image
/// @return extra Strategy color, background color and other extra data
function strategies()
external
view
returns (
string[] memory id,
bool[] memory deployAllowed,
bool[] memory upgradeAllowed,
bool[] memory farming,
uint[] memory tokenId,
string[] memory tokenURI,
bytes32[] memory extra
);
/// @notice Get config of vault type
/// @param typeHash Keccak256 hash of vault type string
/// @return vaultType Vault type string
/// @return implementation Vault implementation address
/// @return deployAllowed New vaults can be deployed
/// @return upgradeAllowed Vaults can be upgraded
/// @return buildingPrice Price of building new vault
function vaultConfig(bytes32 typeHash)
external
view
returns (
string memory vaultType,
address implementation,
bool deployAllowed,
bool upgradeAllowed,
uint buildingPrice
);
/// @notice Data on all factory vault types
/// The output values are matched by index in the arrays.
/// @return vaultType Vault type string
/// @return implementation Address of vault implemented logic
/// @return deployAllowed New vaults can be deployed
/// @return upgradeAllowed Vaults can be upgraded
/// @return buildingPrice Price of building new vault
/// @return extra Vault type color, background color and other extra data
function vaultTypes()
external
view
returns (
string[] memory vaultType,
address[] memory implementation,
bool[] memory deployAllowed,
bool[] memory upgradeAllowed,
uint[] memory buildingPrice,
bytes32[] memory extra
);
/// @notice Initialization strategy params store
function strategyAvailableInitParams(bytes32 idHash) external view returns (StrategyAvailableInitParams memory);
/// @notice Retrieves the alias name associated with a given address
/// @param tokenAddress_ The address to query for its alias name
/// @return The alias name associated with the provided address
function getAliasName(address tokenAddress_) external view returns (string memory);
//endregion -- View functions -----
//region ----- Write functions -----
/// @notice Main method of the Factory - new vault creation by user.
/// @param vaultType Vault type ID string
/// @param strategyId Strategy logic ID string
/// Different types of vaults and strategies have different lengths of input arrays.
/// @param vaultInitAddresses Addresses for vault initialization
/// @param vaultInitNums Numbers for vault initialization
/// @param strategyInitAddresses Addresses for strategy initialization
/// @param strategyInitNums Numbers for strategy initialization
/// @param strategyInitTicks Ticks for strategy initialization
/// @return vault Deployed VaultProxy address
/// @return strategy Deployed StrategyProxy address
function deployVaultAndStrategy(
string memory vaultType,
string memory strategyId,
address[] memory vaultInitAddresses,
uint[] memory vaultInitNums,
address[] memory strategyInitAddresses,
uint[] memory strategyInitNums,
int24[] memory strategyInitTicks
) external returns (address vault, address strategy);
/// @notice Upgrade vault proxy. Can be called by any address.
/// @param vault Address of vault proxy for upgrade
function upgradeVaultProxy(address vault) external;
/// @notice Upgrade strategy proxy. Can be called by any address.
/// @param strategy Address of strategy proxy for upgrade
function upgradeStrategyProxy(address strategy) external;
/// @notice Add farm to factory
/// @param farms_ Settings and data required to work with the farm.
function addFarms(Farm[] memory farms_) external;
/// @notice Update farm
/// @param id Farm index
/// @param farm_ Settings and data required to work with the farm.
function updateFarm(uint id, Farm memory farm_) external;
/// @notice Initial addition or change of vault type settings.
/// Operator can add new vault type. Governance or multisig can change existing vault type config.
/// @param vaultConfig_ Vault type settings
function setVaultConfig(VaultConfig memory vaultConfig_) external;
/// @notice Initial addition or change of strategy logic settings.
/// Operator can add new strategy logic. Governance or multisig can change existing logic config.
/// @param config Strategy logic settings
/// @param developer Strategy developer is receiver of minted StrategyLogic NFT on initial addition
function setStrategyLogicConfig(StrategyLogicConfig memory config, address developer) external;
/// @notice Governance and multisig can set a vault status other than Active - the default status.
/// @param vaults Addresses of vault proxy
/// @param statuses New vault statuses. Constant from VaultStatusLib
function setVaultStatus(address[] memory vaults, uint[] memory statuses) external;
/// @notice Initial addition or change of strategy available init params
/// @param id Strategy ID string
/// @param initParams Init params variations that will be parsed by strategy
function setStrategyAvailableInitParams(string memory id, StrategyAvailableInitParams memory initParams) external;
/// @notice Assigns a new alias name to a specific address
/// @dev This function may require certain permissions to be called successfully.
/// @param tokenAddress_ The address to assign an alias name to
/// @param aliasName_ The alias name to assign to the given address
function setAliasName(address tokenAddress_, string memory aliasName_) external;
//endregion -- Write functions -----
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/// @dev Interface of proxy contract for a vault implementation
interface IVaultProxy {
//region ----- Custom Errors -----
error ProxyForbidden();
//endregion -- Custom Errors -----
/// @notice Initialize vault proxy by Factory
/// @param type_ Vault type ID string
function initProxy(string memory type_) external;
/// @notice Upgrade vault implementation if available and allowed
/// Anyone can execute vault upgrade
function upgrade() external;
/// @notice Current vault implementation
/// @return Address of vault implementation contract
function implementation() external view returns (address);
/// @notice Vault type hash
/// @return keccan256 hash of vault type ID string
function vaultTypeHash() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
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 is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @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._positions[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 cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[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._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
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;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, 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(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, 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(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(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(uint256(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(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, 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(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @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(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}