Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 5 internal transactions
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
17512368 | 569 days ago | Contract Creation | 0 ETH | |||
17393035 | 586 days ago | Contract Creation | 0 ETH | |||
17392960 | 586 days ago | Contract Creation | 0 ETH | |||
17392950 | 586 days ago | Contract Creation | 0 ETH | |||
17392944 | 586 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
SiloConvexFactory
Compiler Version
v0.8.13+commit.abaa5c0e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "../SiloConvex.sol"; import "./SiloFactoryV2.sol"; /// @title SiloConvexFactory /// @notice Silo Factory has one job, deploy Silo implementation /// @dev Silo Factory is registered within SiloRepository contract and it's given a version. Each version /// is different Silo Factory that deploys different Silo implementation. Many Factory contracts can be /// registered with the Repository contract. /// @custom:security-contact [email protected] contract SiloConvexFactory is SiloFactoryV2 { // solhint-disable-next-line var-name-mixedcase ISiloConvexStateChangesHandler internal immutable _STATE_CHANGES_HANDLER; /// @param _repository A silo repository address constructor( address _repository, ISiloConvexStateChangesHandler _stateChangesHandler ) SiloFactoryV2(_repository) { // Ping library call is not possible here, because the contract will exceed the gas limit. _STATE_CHANGES_HANDLER = _stateChangesHandler; } /// @inheritdoc ISiloFactoryV2 function createSilo( address _siloAsset, uint128 _version, bytes memory ) external virtual override returns (address silo) { // Only allow silo repository if (msg.sender != siloRepository) revert OnlyRepository(); silo = address(new SiloConvex( ISiloRepository(msg.sender), _STATE_CHANGES_HANDLER, _siloAsset, _version )); emit NewSiloCreated(silo, _siloAsset, _version); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * The default value of {decimals} is 18. To select a different value for * {decimals} you should overload it. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual 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 this function is * overridden; * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); uint256 currentAllowance = _allowances[sender][_msgSender()]; require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); unchecked { _approve(sender, _msgSender(), currentAllowance - amount); } return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { uint256 currentAllowance = _allowances[_msgSender()][spender]; require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(_msgSender(), spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `sender` to `recipient`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); uint256 senderBalance = _balances[sender]; require(senderBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[sender] = senderBalance - amount; } _balances[recipient] += amount; emit Transfer(sender, recipient, amount); _afterTokenTransfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; _balances[account] += amount; emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; } _totalSupply -= amount; emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/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 Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Address.sol) pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { 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 assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "./utils/LiquidationReentrancyGuard.sol"; import "./interfaces/IBaseSilo.sol"; import "./interfaces/IGuardedLaunch.sol"; import "./interfaces/ISiloRepository.sol"; import "./interfaces/IPriceProvidersRepository.sol"; import "./interfaces/IInterestRateModel.sol"; import "./interfaces/IShareToken.sol"; import "./lib/Ping.sol"; import "./lib/EasyMath.sol"; import "./lib/TokenHelper.sol"; import "./lib/Solvency.sol"; /// @title BaseSilo /// @dev Base contract for Silo core logic. /// @custom:security-contact [email protected] abstract contract BaseSilo is IBaseSilo, ReentrancyGuard, LiquidationReentrancyGuard { using SafeERC20 for ERC20; using EasyMath for uint256; ISiloRepository immutable public override siloRepository; // asset address for which Silo was created address public immutable siloAsset; /// @dev version of silo /// @notice It tells us which `SiloRepository.siloFactory(version)` created this Silo uint128 public immutable VERSION; // solhint-disable-line var-name-mixedcase // solhint-disable-next-line var-name-mixedcase uint256 private immutable _ASSET_DECIMAL_POINTS; /// @dev stores all *synced* assets (bridge assets + removed bridge assets + siloAsset) address[] private _allSiloAssets; /// @dev asset => AssetStorage mapping(address => AssetStorage) private _assetStorage; /// @dev asset => AssetInterestData mapping(address => AssetInterestData) private _interestData; error AssetDoesNotExist(); error BorrowNotPossible(); error DepositNotPossible(); error DepositsExceedLimit(); error InvalidRepository(); error InvalidSiloVersion(); error MaximumLTVReached(); error NotEnoughLiquidity(); error NotEnoughDeposits(); error NotSolvent(); error OnlyRouter(); error Paused(); error UnexpectedEmptyReturn(); error UserIsZero(); modifier onlyExistingAsset(address _asset) { if (_interestData[_asset].status == AssetStatus.Undefined) { revert AssetDoesNotExist(); } _; } modifier onlyRouter() { if (msg.sender != siloRepository.router()) revert OnlyRouter(); _; } modifier validateMaxDepositsAfter(address _asset) { _; IPriceProvidersRepository priceProviderRepo = siloRepository.priceProvidersRepository(); AssetStorage storage _assetState = _assetStorage[_asset]; uint256 allDeposits = _assetState.totalDeposits + _assetState.collateralOnlyDeposits; if ( priceProviderRepo.getPrice(_asset) * allDeposits / (10 ** IERC20Metadata(_asset).decimals()) > IGuardedLaunch(address(siloRepository)).getMaxSiloDepositsValue(address(this), _asset) ) { revert DepositsExceedLimit(); } } constructor (ISiloRepository _repository, address _siloAsset, uint128 _version) { if (!Ping.pong(_repository.siloRepositoryPing)) revert InvalidRepository(); if (_version == 0) revert InvalidSiloVersion(); uint256 decimals = TokenHelper.assertAndGetDecimals(_siloAsset); VERSION = _version; siloRepository = _repository; siloAsset = _siloAsset; _ASSET_DECIMAL_POINTS = 10**decimals; } /// @dev this is exposed only for test purposes, but it is safe to leave it like that function initAssetsTokens() external nonReentrant { _initAssetsTokens(); } /// @inheritdoc IBaseSilo function syncBridgeAssets() external override nonReentrant { // sync removed assets address[] memory removedBridgeAssets = siloRepository.getRemovedBridgeAssets(); for (uint256 i = 0; i < removedBridgeAssets.length; i++) { // If removed bridge asset is the silo asset for this silo, do not remove it address removedBridgeAsset = removedBridgeAssets[i]; if (removedBridgeAsset != siloAsset) { _interestData[removedBridgeAsset].status = AssetStatus.Removed; emit AssetStatusUpdate(removedBridgeAsset, AssetStatus.Removed); } } // must be called at the end, because we overriding `_assetStorage[removedBridgeAssets[i]].removed` _initAssetsTokens(); } /// @inheritdoc IBaseSilo function assetStorage(address _asset) external view override returns (AssetStorage memory) { return _assetStorage[_asset]; } /// @inheritdoc IBaseSilo function interestData(address _asset) external view override returns (AssetInterestData memory) { return _interestData[_asset]; } /// @inheritdoc IBaseSilo function utilizationData(address _asset) external view override returns (UtilizationData memory data) { AssetStorage storage _assetState = _assetStorage[_asset]; return UtilizationData( _assetState.totalDeposits, _assetState.totalBorrowAmount, _interestData[_asset].interestRateTimestamp ); } /// @inheritdoc IBaseSilo function getAssets() public view override returns (address[] memory assets) { return _allSiloAssets; } /// @inheritdoc IBaseSilo function getAssetsWithState() public view override returns ( address[] memory assets, AssetStorage[] memory assetsStorage ) { assets = _allSiloAssets; assetsStorage = new AssetStorage[](assets.length); for (uint256 i = 0; i < assets.length; i++) { assetsStorage[i] = _assetStorage[assets[i]]; } } /// @inheritdoc IBaseSilo function isSolvent(address _user) public view override returns (bool) { if (_user == address(0)) revert UserIsZero(); (address[] memory assets, AssetStorage[] memory assetsStates) = getAssetsWithState(); (uint256 userLTV, uint256 liquidationThreshold) = Solvency.calculateLTVs( Solvency.SolvencyParams( siloRepository, ISilo(address(this)), assets, assetsStates, _user ), Solvency.TypeofLTV.LiquidationThreshold ); return userLTV <= liquidationThreshold; } /// @inheritdoc IBaseSilo function depositPossible(address _asset, address _depositor) public view override returns (bool) { return _assetStorage[_asset].debtToken.balanceOf(_depositor) == 0 && _interestData[_asset].status == AssetStatus.Active; } /// @inheritdoc IBaseSilo function borrowPossible(address _asset, address _borrower) public view override returns (bool) { AssetStorage storage _assetState = _assetStorage[_asset]; return _assetState.collateralToken.balanceOf(_borrower) == 0 && _assetState.collateralOnlyToken.balanceOf(_borrower) == 0 && _interestData[_asset].status == AssetStatus.Active; } /// @inheritdoc IBaseSilo function liquidity(address _asset) public view returns (uint256) { return ERC20(_asset).balanceOf(address(this)) - _assetStorage[_asset].collateralOnlyDeposits; } /// @dev Initiate asset by deploying accounting EC20 tokens for collateral and debt /// @param _tokensFactory factory contract that deploys collateral and debt tokens /// @param _asset which asset to initialize /// @param _isBridgeAsset true if initialized asset is a bridge asset function _initAsset(ITokensFactory _tokensFactory, address _asset, bool _isBridgeAsset) internal { AssetSharesMetadata memory metadata = _generateSharesNames(_asset, _isBridgeAsset); AssetStorage storage _assetState = _assetStorage[_asset]; _assetState.collateralToken = _tokensFactory.createShareCollateralToken( metadata.collateralName, metadata.collateralSymbol, _asset ); _assetState.collateralOnlyToken = _tokensFactory.createShareCollateralToken( metadata.protectedName, metadata.protectedSymbol, _asset ); _assetState.debtToken = _tokensFactory.createShareDebtToken( metadata.debtName, metadata.debtSymbol, _asset ); // keep synced asset in storage array _allSiloAssets.push(_asset); _interestData[_asset].status = AssetStatus.Active; emit AssetStatusUpdate(_asset, AssetStatus.Active); } /// @dev Initializes all assets (bridge assets + unique asset) for Silo but only if asset has not been /// initialized already. It's safe to call it multiple times. It's safe for anyone to call it at any time. function _initAssetsTokens() internal { ITokensFactory tokensFactory = siloRepository.tokensFactory(); // init silo asset if needed if (address(_assetStorage[siloAsset].collateralToken) == address(0)) { _initAsset(tokensFactory, siloAsset, false); } // sync active assets address[] memory bridgeAssets = siloRepository.getBridgeAssets(); for (uint256 i = 0; i < bridgeAssets.length; i++) { address bridgeAsset = bridgeAssets[i]; // In case a bridge asset is added that already has a Silo, // do not initiate that asset in its Silo if (address(_assetStorage[bridgeAsset].collateralToken) == address(0)) { _initAsset(tokensFactory, bridgeAsset, true); } else { _interestData[bridgeAsset].status = AssetStatus.Active; emit AssetStatusUpdate(bridgeAsset, AssetStatus.Active); } } } /// @dev Generate asset shares tokens names and symbols /// @param _asset asset for which shares tokens will be initializaed /// @param _isBridgeAsset true if initialized asset is a bridge asset function _generateSharesNames(address _asset, bool _isBridgeAsset) internal view returns (AssetSharesMetadata memory metadata) { // Naming convention in UNI example: // - for siloAsset: sUNI, dUNI, spUNI // - for bridgeAsset: sWETH-UNI, dWETH-UNI, spWETH-UNI string memory assetSymbol = TokenHelper.symbol(_asset); metadata = AssetSharesMetadata({ collateralName: string.concat("Silo Finance Borrowable ", assetSymbol, " Deposit"), collateralSymbol: string.concat("s", assetSymbol), protectedName: string.concat("Silo Finance Protected ", assetSymbol, " Deposit"), protectedSymbol: string.concat("sp", assetSymbol), debtName: string.concat("Silo Finance ", assetSymbol, " Debt"), debtSymbol: string.concat("d", assetSymbol) }); if (_isBridgeAsset) { string memory baseSymbol = TokenHelper.symbol(siloAsset); metadata.collateralName = string.concat(metadata.collateralName, " in ", baseSymbol, " Silo"); metadata.collateralSymbol = string.concat(metadata.collateralSymbol, "-", baseSymbol); metadata.protectedName = string.concat(metadata.protectedName, " in ", baseSymbol, " Silo"); metadata.protectedSymbol = string.concat(metadata.protectedSymbol, "-", baseSymbol); metadata.debtName = string.concat(metadata.debtName, " in ", baseSymbol, " Silo"); metadata.debtSymbol = string.concat(metadata.debtSymbol, "-", baseSymbol); } } /// @dev Main deposit function that handles all deposit logic and validation /// @param _asset asset address that is being deposited /// @param _from wallet address form which to pull asset tokens /// @param _depositor wallet address that will be granted ownership of deposited tokens. Keep in mind /// that deposit can be made by Router contract but the owner of the deposit should be user. /// @param _amount deposit amount /// @param _collateralOnly true if deposit should be used for collateral only. Otherwise false. /// Collateral only deposit cannot be borrowed by anyone and does not earn any interest. However, /// it can be used as collateral and can be subject to liquidation. /// @return collateralAmount deposited amount /// @return collateralShare `_depositor` collateral shares based on deposited amount function _deposit( address _asset, address _from, address _depositor, uint256 _amount, bool _collateralOnly ) internal nonReentrant validateMaxDepositsAfter(_asset) returns (uint256 collateralAmount, uint256 collateralShare) { // MUST BE CALLED AS FIRST METHOD! _accrueInterest(_asset); if (!depositPossible(_asset, _depositor)) revert DepositNotPossible(); AssetStorage storage _state = _assetStorage[_asset]; collateralAmount = _amount; uint256 totalDepositsCached = _collateralOnly ? _state.collateralOnlyDeposits : _state.totalDeposits; if (_collateralOnly) { collateralShare = _amount.toShare(totalDepositsCached, _state.collateralOnlyToken.totalSupply()); _state.collateralOnlyDeposits = totalDepositsCached + _amount; _state.collateralOnlyToken.mint(_depositor, collateralShare); } else { collateralShare = _amount.toShare(totalDepositsCached, _state.collateralToken.totalSupply()); _state.totalDeposits = totalDepositsCached + _amount; _state.collateralToken.mint(_depositor, collateralShare); } ERC20(_asset).safeTransferFrom(_from, address(this), _amount); emit Deposit(_asset, _depositor, _amount, _collateralOnly); } /// @dev Main withdraw function that handles all withdraw logic and validation /// @param _asset asset address that is being withdrawn /// @param _depositor wallet address that is an owner of the deposited tokens /// @param _receiver wallet address that will receive withdrawn tokens. It's possible that Router /// contract is the owner of deposited tokens but we want user to get these tokens directly. /// @param _amount amount to withdraw. If amount is equal to maximum value stored by uint256 type /// (type(uint256).max), it will be assumed that user wants to withdraw all tokens and final account /// will be dynamically calculated including interest. /// @param _collateralOnly true if collateral only tokens are to be withdrawn. Otherwise false. /// User can deposit the same asset as collateral only and as regular deposit. During withdraw, /// it must be specified which tokens are to be withdrawn. /// @return withdrawnAmount withdrawn amount that was transferred to user /// @return withdrawnShare burned share based on `withdrawnAmount` function _withdraw(address _asset, address _depositor, address _receiver, uint256 _amount, bool _collateralOnly) internal nonReentrant // because we transferring tokens onlyExistingAsset(_asset) returns (uint256 withdrawnAmount, uint256 withdrawnShare) { // MUST BE CALLED AS FIRST METHOD! _accrueInterest(_asset); (withdrawnAmount, withdrawnShare) = _withdrawAsset( _asset, _amount, _depositor, _receiver, _collateralOnly, 0 // do not apply any fees on regular withdraw ); if (withdrawnAmount == 0) revert UnexpectedEmptyReturn(); if (!isSolvent(_depositor)) revert NotSolvent(); emit Withdraw(_asset, _depositor, _receiver, withdrawnAmount, _collateralOnly); } /// @dev Main borrow function that handles all borrow logic and validation /// @param _asset asset address that is being borrowed /// @param _borrower wallet address that will own debt /// @param _receiver wallet address that will receive borrowed tokens. It's possible that Router /// contract is executing borrowing for user and should be the one receiving tokens, however, /// the owner of the debt should be user himself. /// @param _amount amount of asset to borrow /// @return debtAmount borrowed amount /// @return debtShare user debt share based on borrowed amount function _borrow(address _asset, address _borrower, address _receiver, uint256 _amount) internal nonReentrant returns (uint256 debtAmount, uint256 debtShare) { // MUST BE CALLED AS FIRST METHOD! _accrueInterest(_asset); if (!borrowPossible(_asset, _borrower)) revert BorrowNotPossible(); if (liquidity(_asset) < _amount) revert NotEnoughLiquidity(); AssetStorage storage _state = _assetStorage[_asset]; uint256 totalBorrowAmount = _state.totalBorrowAmount; uint256 entryFee = siloRepository.entryFee(); uint256 fee = entryFee == 0 ? 0 : _amount * entryFee / Solvency._PRECISION_DECIMALS; debtShare = (_amount + fee).toShareRoundUp(totalBorrowAmount, _state.debtToken.totalSupply()); debtAmount = _amount; _state.totalBorrowAmount = totalBorrowAmount + _amount + fee; _interestData[_asset].protocolFees += fee; _state.debtToken.mint(_borrower, debtShare); emit Borrow(_asset, _borrower, _amount); ERC20(_asset).safeTransfer(_receiver, _amount); // IMPORTANT - keep `validateBorrowAfter` at the end _validateBorrowAfter(_borrower); } /// @dev Main repay function that handles all repay logic and validation /// @param _asset asset address that is being repaid /// @param _borrower wallet address for which debt is being repaid /// @param _repayer wallet address that will pay the debt. It's possible that Router /// contract is executing repay for user and should be the one paying the debt. /// @param _amount amount of asset to repay /// @return repaidAmount amount repaid /// @return repaidShare burned debt share function _repay(address _asset, address _borrower, address _repayer, uint256 _amount) internal onlyExistingAsset(_asset) nonReentrant returns (uint256 repaidAmount, uint256 repaidShare) { // MUST BE CALLED AS FIRST METHOD! _accrueInterest(_asset); AssetStorage storage _state = _assetStorage[_asset]; (repaidAmount, repaidShare) = _calculateDebtAmountAndShare(_state, _borrower, _amount); if (repaidShare == 0) revert UnexpectedEmptyReturn(); emit Repay(_asset, _borrower, repaidAmount); ERC20(_asset).safeTransferFrom(_repayer, address(this), repaidAmount); // change debt state before, because share token state is changes the same way (notification is after burn) _state.totalBorrowAmount -= repaidAmount; _state.debtToken.burn(_borrower, repaidShare); } /// @param _assets all current assets, this is an optimization, so we don't have to read it from storage few times /// @param _user user to liquidate /// @param _flashReceiver address which will get all collaterals and will be notified once collaterals will be send /// @param _flashReceiverData custom data to forward to receiver /// @return receivedCollaterals amounts of collaterals transferred to `_flashReceiver` /// @return shareAmountsToRepay expected amounts to repay function _userLiquidation( address[] memory _assets, address _user, IFlashLiquidationReceiver _flashReceiver, bytes memory _flashReceiverData ) internal // we can not use `nonReentrant` because we are using it in `_repay`, // and `_repay` needs to be reentered as part of a liquidation liquidationNonReentrant returns (uint256[] memory receivedCollaterals, uint256[] memory shareAmountsToRepay) { // gracefully fail if _user is solvent if (isSolvent(_user)) { uint256[] memory empty = new uint256[](_assets.length); return (empty, empty); } (receivedCollaterals, shareAmountsToRepay) = _flashUserLiquidation(_assets, _user, address(_flashReceiver)); // _flashReceiver needs to repayFor user _flashReceiver.siloLiquidationCallback( _user, _assets, receivedCollaterals, shareAmountsToRepay, _flashReceiverData ); for (uint256 i = 0; i < _assets.length; i++) { if (receivedCollaterals[i] != 0 || shareAmountsToRepay[i] != 0) { emit Liquidate(_assets[i], _user, shareAmountsToRepay[i], receivedCollaterals[i]); } } if (!isSolvent(_user)) revert NotSolvent(); } function _flashUserLiquidation(address[] memory _assets, address _borrower, address _liquidator) internal returns (uint256[] memory receivedCollaterals, uint256[] memory amountsToRepay) { uint256 assetsLength = _assets.length; receivedCollaterals = new uint256[](assetsLength); amountsToRepay = new uint256[](assetsLength); uint256 protocolLiquidationFee = siloRepository.protocolLiquidationFee(); for (uint256 i = 0; i < assetsLength; i++) { _accrueInterest(_assets[i]); AssetStorage storage _state = _assetStorage[_assets[i]]; // we do not allow for partial repayment on liquidation, that's why max (amountsToRepay[i],) = _calculateDebtAmountAndShare(_state, _borrower, type(uint256).max); (uint256 withdrawnOnlyAmount,) = _withdrawAsset( _assets[i], type(uint256).max, _borrower, _liquidator, true, // collateral only protocolLiquidationFee ); (uint256 withdrawnAmount,) = _withdrawAsset( _assets[i], type(uint256).max, _borrower, _liquidator, false, // collateral only protocolLiquidationFee ); receivedCollaterals[i] = withdrawnOnlyAmount + withdrawnAmount; } } /// @dev Utility function for withdrawing an asset /// @param _asset asset to withdraw /// @param _assetAmount amount of asset to withdraw /// @param _depositor wallet address that is an owner of the deposit /// @param _receiver wallet address that is receiving the token /// @param _collateralOnly true if withdraw collateral only. /// @param _protocolLiquidationFee if provided (!=0) liquidation fees will be applied and returned /// `withdrawnAmount` will be decreased /// @return withdrawnAmount amount of asset that has been sent to receiver /// @return burnedShare burned share based on `withdrawnAmount` function _withdrawAsset( address _asset, uint256 _assetAmount, address _depositor, address _receiver, bool _collateralOnly, uint256 _protocolLiquidationFee ) internal returns (uint256 withdrawnAmount, uint256 burnedShare) { (uint256 assetTotalDeposits, IShareToken shareToken, uint256 availableLiquidity) = _getWithdrawAssetData(_asset, _collateralOnly); if (_assetAmount == type(uint256).max) { burnedShare = shareToken.balanceOf(_depositor); withdrawnAmount = burnedShare.toAmount(assetTotalDeposits, shareToken.totalSupply()); } else { burnedShare = _assetAmount.toShareRoundUp(assetTotalDeposits, shareToken.totalSupply()); withdrawnAmount = _assetAmount; } if (withdrawnAmount == 0) { // we can not revert here, because liquidation will fail when one of collaterals will be empty return (0, 0); } if (assetTotalDeposits < withdrawnAmount) revert NotEnoughDeposits(); unchecked { // can be unchecked because of the `if` above assetTotalDeposits -= withdrawnAmount; } uint256 amountToTransfer = _applyLiquidationFee(_asset, withdrawnAmount, _protocolLiquidationFee); if (availableLiquidity < amountToTransfer) revert NotEnoughLiquidity(); AssetStorage storage _state = _assetStorage[_asset]; if (_collateralOnly) { _state.collateralOnlyDeposits = assetTotalDeposits; } else { _state.totalDeposits = assetTotalDeposits; } shareToken.burn(_depositor, burnedShare); // in case token sent in fee-on-transfer type of token we do not care when withdrawing ERC20(_asset).safeTransfer(_receiver, amountToTransfer); } /// @notice Calculates liquidations fee and returns amount of asset transferred to liquidator /// @param _asset asset address /// @param _amount amount on which we will apply fee /// @param _protocolLiquidationFee liquidation fee in Solvency._PRECISION_DECIMALS /// @return change amount left after subtracting liquidation fee function _applyLiquidationFee(address _asset, uint256 _amount, uint256 _protocolLiquidationFee) internal returns (uint256 change) { if (_protocolLiquidationFee == 0) { return _amount; } uint256 liquidationFeeAmount; ( liquidationFeeAmount, _interestData[_asset].protocolFees ) = Solvency.calculateLiquidationFee(_interestData[_asset].protocolFees, _amount, _protocolLiquidationFee); unchecked { // if fees will not be higher than 100% this will not underflow, this is responsibility of siloRepository // in case we do underflow, we can expect liquidator reject tx because of too little change change = _amount - liquidationFeeAmount; } } /// @dev harvest protocol fees from particular asset /// @param _asset asset we want to harvest fees from /// @param _receiver address of fees receiver /// @return harvestedFees harvested fee function _harvestProtocolFees(address _asset, address _receiver) internal nonReentrant returns (uint256 harvestedFees) { AssetInterestData storage data = _interestData[_asset]; harvestedFees = data.protocolFees - data.harvestedProtocolFees; uint256 currentLiquidity = liquidity(_asset); if (harvestedFees > currentLiquidity) { harvestedFees = currentLiquidity; } if (harvestedFees == 0) { return 0; } unchecked { // This can't overflow because this addition is less than or equal to data.protocolFees data.harvestedProtocolFees += harvestedFees; } ERC20(_asset).safeTransfer(_receiver, harvestedFees); } /// @notice Accrue interest for asset /// @dev Silo Interest Rate Model implements dynamic interest rate that changes every second. Returned /// interest rate by the model is compounded rate so it can be used in math calculations as if it was /// static. Rate is calculated for the time range between last update and current timestamp. /// @param _asset address of the asset for which interest should be accrued /// @return accruedInterest total accrued interest function _accrueInterest(address _asset) internal returns (uint256 accruedInterest) { /// @dev `_accrueInterest` is called on every user action, including liquidation. It's enough to check /// if Silo is paused in this function. if (IGuardedLaunch(address(siloRepository)).isSiloPaused(address(this), _asset)) { revert Paused(); } AssetStorage storage _state = _assetStorage[_asset]; AssetInterestData storage _assetInterestData = _interestData[_asset]; uint256 lastTimestamp = _assetInterestData.interestRateTimestamp; // This is the first time, so we can return early and save some gas if (lastTimestamp == 0) { _assetInterestData.interestRateTimestamp = uint64(block.timestamp); return 0; } // Interest has already been accrued this block if (lastTimestamp == block.timestamp) { return 0; } uint256 rcomp = _getModel(_asset).getCompoundInterestRateAndUpdate(_asset, block.timestamp); uint256 protocolShareFee = siloRepository.protocolShareFee(); uint256 totalBorrowAmountCached = _state.totalBorrowAmount; uint256 protocolFeesCached = _assetInterestData.protocolFees; uint256 newProtocolFees; uint256 protocolShare; uint256 depositorsShare; accruedInterest = totalBorrowAmountCached * rcomp / Solvency._PRECISION_DECIMALS; unchecked { // If we overflow on multiplication it should not revert tx, we will get lower fees protocolShare = accruedInterest * protocolShareFee / Solvency._PRECISION_DECIMALS; newProtocolFees = protocolFeesCached + protocolShare; if (newProtocolFees < protocolFeesCached) { protocolShare = type(uint256).max - protocolFeesCached; newProtocolFees = type(uint256).max; } depositorsShare = accruedInterest - protocolShare; } // update contract state _state.totalBorrowAmount = totalBorrowAmountCached + accruedInterest; _state.totalDeposits = _state.totalDeposits + depositorsShare; _assetInterestData.protocolFees = newProtocolFees; _assetInterestData.interestRateTimestamp = uint64(block.timestamp); } /// @dev gets interest rates model object /// @param _asset asset for which to calculate interest rate /// @return IInterestRateModel interest rates model object function _getModel(address _asset) internal view returns (IInterestRateModel) { return IInterestRateModel(siloRepository.getInterestRateModel(address(this), _asset)); } /// @dev calculates amount to repay based on user shares, we do not apply virtual balances here, /// if needed, they need to be apply beforehand /// @param _state asset storage struct /// @param _borrower borrower address /// @param _amount proposed amount of asset to repay. Based on that,`repayShare` is calculated. /// @return amount amount of asset to repay /// @return repayShare amount of debt token representing debt ownership function _calculateDebtAmountAndShare(AssetStorage storage _state, address _borrower, uint256 _amount) internal view returns (uint256 amount, uint256 repayShare) { uint256 borrowerDebtShare = _state.debtToken.balanceOf(_borrower); uint256 debtTokenTotalSupply = _state.debtToken.totalSupply(); uint256 totalBorrowed = _state.totalBorrowAmount; uint256 maxAmount = borrowerDebtShare.toAmountRoundUp(totalBorrowed, debtTokenTotalSupply); if (_amount >= maxAmount) { amount = maxAmount; repayShare = borrowerDebtShare; } else { amount = _amount; repayShare = _amount.toShare(totalBorrowed, debtTokenTotalSupply); } } /// @dev verifies if user did not borrow more than allowed maximum function _validateBorrowAfter(address _user) private view { (address[] memory assets, AssetStorage[] memory assetsStates) = getAssetsWithState(); (uint256 userLTV, uint256 maximumAllowedLTV) = Solvency.calculateLTVs( Solvency.SolvencyParams( siloRepository, ISilo(address(this)), assets, assetsStates, _user ), Solvency.TypeofLTV.MaximumLTV ); if (userLTV > maximumAllowedLTV) revert MaximumLTVReached(); } function _getWithdrawAssetData(address _asset, bool _collateralOnly) private view returns(uint256 assetTotalDeposits, IShareToken shareToken, uint256 availableLiquidity) { AssetStorage storage _state = _assetStorage[_asset]; if (_collateralOnly) { assetTotalDeposits = _state.collateralOnlyDeposits; shareToken = _state.collateralOnlyToken; availableLiquidity = assetTotalDeposits; } else { assetTotalDeposits = _state.totalDeposits; shareToken = _state.collateralToken; availableLiquidity = liquidity(_asset); } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "./IShareToken.sol"; import "./IFlashLiquidationReceiver.sol"; import "./ISiloRepository.sol"; interface IBaseSilo { enum AssetStatus { Undefined, Active, Removed } /// @dev Storage struct that holds all required data for a single token market struct AssetStorage { /// @dev Token that represents a share in totalDeposits of Silo IShareToken collateralToken; /// @dev Token that represents a share in collateralOnlyDeposits of Silo IShareToken collateralOnlyToken; /// @dev Token that represents a share in totalBorrowAmount of Silo IShareToken debtToken; /// @dev COLLATERAL: Amount of asset token that has been deposited to Silo with interest earned by depositors. /// It also includes token amount that has been borrowed. uint256 totalDeposits; /// @dev COLLATERAL ONLY: Amount of asset token that has been deposited to Silo that can be ONLY used /// as collateral. These deposits do NOT earn interest and CANNOT be borrowed. uint256 collateralOnlyDeposits; /// @dev DEBT: Amount of asset token that has been borrowed with accrued interest. uint256 totalBorrowAmount; } /// @dev Storage struct that holds data related to fees and interest struct AssetInterestData { /// @dev Total amount of already harvested protocol fees uint256 harvestedProtocolFees; /// @dev Total amount (ever growing) of asset token that has been earned by the protocol from /// generated interest. uint256 protocolFees; /// @dev Timestamp of the last time `interestRate` has been updated in storage. uint64 interestRateTimestamp; /// @dev True if asset was removed from the protocol. If so, deposit and borrow functions are disabled /// for that asset AssetStatus status; } /// @notice data that InterestModel needs for calculations struct UtilizationData { uint256 totalDeposits; uint256 totalBorrowAmount; /// @dev timestamp of last interest accrual uint64 interestRateTimestamp; } /// @dev Shares names and symbols that are generated while asset initialization struct AssetSharesMetadata { /// @dev Name for the collateral shares token string collateralName; /// @dev Symbol for the collateral shares token string collateralSymbol; /// @dev Name for the collateral only (protected collateral) shares token string protectedName; /// @dev Symbol for the collateral only (protected collateral) shares token string protectedSymbol; /// @dev Name for the debt shares token string debtName; /// @dev Symbol for the debt shares token string debtSymbol; } /// @notice Emitted when deposit is made /// @param asset asset address that was deposited /// @param depositor wallet address that deposited asset /// @param amount amount of asset that was deposited /// @param collateralOnly type of deposit, true if collateralOnly deposit was used event Deposit(address indexed asset, address indexed depositor, uint256 amount, bool collateralOnly); /// @notice Emitted when withdraw is made /// @param asset asset address that was withdrawn /// @param depositor wallet address that deposited asset /// @param receiver wallet address that received asset /// @param amount amount of asset that was withdrew /// @param collateralOnly type of withdraw, true if collateralOnly deposit was used event Withdraw( address indexed asset, address indexed depositor, address indexed receiver, uint256 amount, bool collateralOnly ); /// @notice Emitted on asset borrow /// @param asset asset address that was borrowed /// @param user wallet address that borrowed asset /// @param amount amount of asset that was borrowed event Borrow(address indexed asset, address indexed user, uint256 amount); /// @notice Emitted on asset repay /// @param asset asset address that was repaid /// @param user wallet address that repaid asset /// @param amount amount of asset that was repaid event Repay(address indexed asset, address indexed user, uint256 amount); /// @notice Emitted on user liquidation /// @param asset asset address that was liquidated /// @param user wallet address that was liquidated /// @param shareAmountRepaid amount of collateral-share token that was repaid. This is collateral token representing /// ownership of underlying deposit. /// @param seizedCollateral amount of underlying token that was seized by liquidator event Liquidate(address indexed asset, address indexed user, uint256 shareAmountRepaid, uint256 seizedCollateral); /// @notice Emitted when the status for an asset is updated /// @param asset asset address that was updated /// @param status new asset status event AssetStatusUpdate(address indexed asset, AssetStatus indexed status); /// @return version of the silo contract function VERSION() external returns (uint128); // solhint-disable-line func-name-mixedcase /// @notice Synchronize current bridge assets with Silo /// @dev This function needs to be called on Silo deployment to setup all assets for Silo. It needs to be /// called every time a bridged asset is added or removed. When bridge asset is removed, depositing and borrowing /// should be disabled during asset sync. function syncBridgeAssets() external; /// @notice Get Silo Repository contract address /// @return Silo Repository contract address function siloRepository() external view returns (ISiloRepository); /// @notice Get asset storage data /// @param _asset asset address /// @return AssetStorage struct function assetStorage(address _asset) external view returns (AssetStorage memory); /// @notice Get asset interest data /// @param _asset asset address /// @return AssetInterestData struct function interestData(address _asset) external view returns (AssetInterestData memory); /// @dev helper method for InterestRateModel calculations function utilizationData(address _asset) external view returns (UtilizationData memory data); /// @notice Calculates solvency of an account /// @param _user wallet address for which solvency is calculated /// @return true if solvent, false otherwise function isSolvent(address _user) external view returns (bool); /// @notice Returns all initialized (synced) assets of Silo including current and removed bridge assets /// @return assets array of initialized assets of Silo function getAssets() external view returns (address[] memory assets); /// @notice Returns all initialized (synced) assets of Silo including current and removed bridge assets /// with corresponding state /// @return assets array of initialized assets of Silo /// @return assetsStorage array of assets state corresponding to `assets` array function getAssetsWithState() external view returns (address[] memory assets, AssetStorage[] memory assetsStorage); /// @notice Check if depositing an asset for given account is possible /// @dev Depositing an asset that has been already borrowed (and vice versa) is disallowed /// @param _asset asset we want to deposit /// @param _depositor depositor address /// @return true if asset can be deposited by depositor function depositPossible(address _asset, address _depositor) external view returns (bool); /// @notice Check if borrowing an asset for given account is possible /// @dev Borrowing an asset that has been already deposited (and vice versa) is disallowed /// @param _asset asset we want to deposit /// @param _borrower borrower address /// @return true if asset can be borrowed by borrower function borrowPossible(address _asset, address _borrower) external view returns (bool); /// @dev Amount of token that is available for borrowing /// @param _asset asset to get liquidity for /// @return Silo liquidity function liquidity(address _asset) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.13; /// @dev when performing Silo flash liquidation, FlashReceiver contract will receive all collaterals interface IFlashLiquidationReceiver { /// @dev this method is called when doing Silo flash liquidation /// one can NOT assume, that if _seizedCollateral[i] != 0, then _shareAmountsToRepaid[i] must be 0 /// one should assume, that any combination of amounts is possible /// on callback, one must call `Silo.repayFor` because at the end of transaction, /// Silo will check if borrower is solvent. /// @param _user user address, that is liquidated /// @param _assets array of collateral assets received during user liquidation /// this array contains all assets (collateral borrowed) without any order /// @param _receivedCollaterals array of collateral amounts received during user liquidation /// indexes of amounts are related to `_assets`, /// @param _shareAmountsToRepaid array of amounts to repay for each asset /// indexes of amounts are related to `_assets`, /// @param _flashReceiverData data that are passed from sender that executes liquidation function siloLiquidationCallback( address _user, address[] calldata _assets, uint256[] calldata _receivedCollaterals, uint256[] calldata _shareAmountsToRepaid, bytes memory _flashReceiverData ) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; interface IGuardedLaunch { /// @dev Stores info about maximum allowed liquidity in a Silo. This limit applies to deposit only. struct MaxLiquidityLimit { /// @dev flag to turn on/off all limits for all Silos bool globalLimit; /// @dev default value represents maximum allowed liquidity in Silo uint256 defaultMaxLiquidity; /// @notice siloMaxLiquidity maps silo => asset => maximum allowed deposit liquidity. /// @dev Deposit liquidity limit is denominated in quote token. For example, if set to 1e18, it means that any /// given Silo is allowed for deposits up to 1 quote token of value. Value is calculated using prices from the /// Oracle. mapping(address => mapping(address => uint256)) siloMaxLiquidity; } /// @dev Stores info about paused Silos /// if `globalPause` == `true`, all Silo are paused /// if `globalPause` == `false` and `siloPause[silo][0x0]` == `true`, all assets in a `silo` are paused /// if `globalPause` == `false` and `siloPause[silo][asset]` == `true`, only `asset` in a `silo` is paused struct Paused { bool globalPause; /// @dev maps silo address to asset address to bool mapping(address => mapping(address => bool)) siloPause; } /// @notice Emitted when all Silos are paused or unpaused /// @param globalPause current value of `globalPause` event GlobalPause(bool globalPause); /// @notice Emitted when a single Silo or single asset in a Silo is paused or unpaused /// @param silo address of Silo which is paused /// @param asset address of an asset which is paused /// @param pauseValue true when paused, otherwise false event SiloPause(address silo, address asset, bool pauseValue); /// @notice Emitted when max liquidity toggle is switched /// @param newLimitedMaxLiquidityState new value for max liquidity toggle event LimitedMaxLiquidityToggled(bool newLimitedMaxLiquidityState); /// @notice Emitted when deposit liquidity limit is changed for Silo and asset /// @param silo Silo address for which to set limit /// @param asset Silo asset for which to set limit /// @param newMaxDeposits deposit limit amount in quote token event SiloMaxDepositsLimitsUpdate(address indexed silo, address indexed asset, uint256 newMaxDeposits); /// @notice Emitted when default max liquidity limit is changed /// @param newMaxDeposits new deposit limit in quote token event DefaultSiloMaxDepositsLimitUpdate(uint256 newMaxDeposits); /// @notice Sets limited liquidity to provided value function setLimitedMaxLiquidity(bool _globalLimit) external; /// @notice Sets default deposit limit for all Silos /// @param _maxDeposits deposit limit amount in quote token function setDefaultSiloMaxDepositsLimit(uint256 _maxDeposits) external; /// @notice Sets deposit limit for Silo /// @param _silo Silo address for which to set limit /// @param _asset Silo asset for which to set limit /// @param _maxDeposits deposit limit amount in quote token function setSiloMaxDepositsLimit( address _silo, address _asset, uint256 _maxDeposits ) external; /// @notice Pause all Silos /// @dev Callable only by owner. /// @param _globalPause true to pause all Silos, otherwise false function setGlobalPause(bool _globalPause) external; /// @notice Pause single asset in a single Silo /// @dev Callable only by owner. /// @param _silo address of Silo in which `_asset` is being paused /// @param _asset address of an asset that is being paused /// @param _pauseValue true to pause, false to unpause function setSiloPause(address _silo, address _asset, bool _pauseValue) external; /// @notice Check given asset in a Silo is paused /// @param _silo address of Silo /// @param _asset address of an asset /// @return true if given asset in a Silo is paused, otherwise false function isSiloPaused(address _silo, address _asset) external view returns (bool); /// @notice Gets deposit limit for Silo /// @param _silo Silo address for which to set limit /// @param _asset Silo asset for which to set limit /// @return deposit limit for Silo function getMaxSiloDepositsValue(address _silo, address _asset) external view returns (uint256); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; interface IInterestRateModel { /* solhint-disable */ struct Config { // uopt ∈ (0, 1) – optimal utilization; int256 uopt; // ucrit ∈ (uopt, 1) – threshold of large utilization; int256 ucrit; // ulow ∈ (0, uopt) – threshold of low utilization int256 ulow; // ki > 0 – integrator gain int256 ki; // kcrit > 0 – proportional gain for large utilization int256 kcrit; // klow ≥ 0 – proportional gain for low utilization int256 klow; // klin ≥ 0 – coefficient of the lower linear bound int256 klin; // beta ≥ 0 - a scaling factor int256 beta; // ri ≥ 0 – initial value of the integrator int256 ri; // Tcrit ≥ 0 - the time during which the utilization exceeds the critical value int256 Tcrit; } /* solhint-enable */ /// @dev Set dedicated config for given asset in a Silo. Config is per asset per Silo so different assets /// in different Silo can have different configs. /// It will try to call `_silo.accrueInterest(_asset)` before updating config, but it is not guaranteed, /// that this call will be successful, if it fail config will be set anyway. /// @param _silo Silo address for which config should be set /// @param _asset asset address for which config should be set function setConfig(address _silo, address _asset, Config calldata _config) external; /// @dev get compound interest rate and update model storage /// @param _asset address of an asset in Silo for which interest rate should be calculated /// @param _blockTimestamp current block timestamp /// @return rcomp compounded interest rate from last update until now (1e18 == 100%) function getCompoundInterestRateAndUpdate( address _asset, uint256 _blockTimestamp ) external returns (uint256 rcomp); /// @dev Get config for given asset in a Silo. If dedicated config is not set, default one will be returned. /// @param _silo Silo address for which config should be set /// @param _asset asset address for which config should be set /// @return Config struct for asset in Silo function getConfig(address _silo, address _asset) external view returns (Config memory); /// @dev get compound interest rate /// @param _silo address of Silo /// @param _asset address of an asset in Silo for which interest rate should be calculated /// @param _blockTimestamp current block timestamp /// @return rcomp compounded interest rate from last update until now (1e18 == 100%) function getCompoundInterestRate( address _silo, address _asset, uint256 _blockTimestamp ) external view returns (uint256 rcomp); /// @dev get current annual interest rate /// @param _silo address of Silo /// @param _asset address of an asset in Silo for which interest rate should be calculated /// @param _blockTimestamp current block timestamp /// @return rcur current annual interest rate (1e18 == 100%) function getCurrentInterestRate( address _silo, address _asset, uint256 _blockTimestamp ) external view returns (uint256 rcur); /// @notice get the flag to detect rcomp restriction (zero current interest) due to overflow /// overflow boolean flag to detect rcomp restriction function overflowDetected( address _silo, address _asset, uint256 _blockTimestamp ) external view returns (bool overflow); /// @dev pure function that calculates current annual interest rate /// @param _c configuration object, InterestRateModel.Config /// @param _totalBorrowAmount current total borrows for asset /// @param _totalDeposits current total deposits for asset /// @param _interestRateTimestamp timestamp of last interest rate update /// @param _blockTimestamp current block timestamp /// @return rcur current annual interest rate (1e18 == 100%) function calculateCurrentInterestRate( Config memory _c, uint256 _totalDeposits, uint256 _totalBorrowAmount, uint256 _interestRateTimestamp, uint256 _blockTimestamp ) external pure returns (uint256 rcur); /// @dev pure function that calculates interest rate based on raw input data /// @param _c configuration object, InterestRateModel.Config /// @param _totalBorrowAmount current total borrows for asset /// @param _totalDeposits current total deposits for asset /// @param _interestRateTimestamp timestamp of last interest rate update /// @param _blockTimestamp current block timestamp /// @return rcomp compounded interest rate from last update until now (1e18 == 100%) /// @return ri current integral part of the rate /// @return Tcrit time during which the utilization exceeds the critical value /// @return overflow boolean flag to detect rcomp restriction function calculateCompoundInterestRateWithOverflowDetection( Config memory _c, uint256 _totalDeposits, uint256 _totalBorrowAmount, uint256 _interestRateTimestamp, uint256 _blockTimestamp ) external pure returns ( uint256 rcomp, int256 ri, int256 Tcrit, // solhint-disable-line var-name-mixedcase bool overflow ); /// @dev pure function that calculates interest rate based on raw input data /// @param _c configuration object, InterestRateModel.Config /// @param _totalBorrowAmount current total borrows for asset /// @param _totalDeposits current total deposits for asset /// @param _interestRateTimestamp timestamp of last interest rate update /// @param _blockTimestamp current block timestamp /// @return rcomp compounded interest rate from last update until now (1e18 == 100%) /// @return ri current integral part of the rate /// @return Tcrit time during which the utilization exceeds the critical value function calculateCompoundInterestRate( Config memory _c, uint256 _totalDeposits, uint256 _totalBorrowAmount, uint256 _interestRateTimestamp, uint256 _blockTimestamp ) external pure returns ( uint256 rcomp, int256 ri, int256 Tcrit // solhint-disable-line var-name-mixedcase ); /// @dev returns decimal points used by model function DP() external pure returns (uint256); // solhint-disable-line func-name-mixedcase /// @dev just a helper method to see if address is a InterestRateModel /// @return always true function interestRateModelPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; /// @title Common interface for Silo Incentive Contract interface INotificationReceiver { /// @dev Informs the contract about token transfer /// @param _token address of the token that was transferred /// @param _from sender /// @param _to receiver /// @param _amount amount that was transferred function onAfterTransfer(address _token, address _from, address _to, uint256 _amount) external; /// @dev Sanity check function /// @return always true function notificationReceiverPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.7.6 <0.9.0; /// @title Common interface for Silo Price Providers interface IPriceProvider { /// @notice Returns "Time-Weighted Average Price" for an asset. Calculates TWAP price for quote/asset. /// It unifies all tokens decimal to 18, examples: /// - if asses == quote it returns 1e18 /// - if asset is USDC and quote is ETH and ETH costs ~$3300 then it returns ~0.0003e18 WETH per 1 USDC /// @param _asset address of an asset for which to read price /// @return price of asses with 18 decimals, throws when pool is not ready yet to provide price function getPrice(address _asset) external view returns (uint256 price); /// @dev Informs if PriceProvider is setup for asset. It does not means PriceProvider can provide price right away. /// Some providers implementations need time to "build" buffer for TWAP price, /// so price may not be available yet but this method will return true. /// @param _asset asset in question /// @return TRUE if asset has been setup, otherwise false function assetSupported(address _asset) external view returns (bool); /// @notice Gets token address in which prices are quoted /// @return quoteToken address function quoteToken() external view returns (address); /// @notice Helper method that allows easily detects, if contract is PriceProvider /// @dev this can save us from simple human errors, in case we use invalid address /// but this should NOT be treated as security check /// @return always true function priceProviderPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.7.6 <0.9.0; import "./IPriceProvider.sol"; interface IPriceProvidersRepository { /// @notice Emitted when price provider is added /// @param newPriceProvider new price provider address event NewPriceProvider(IPriceProvider indexed newPriceProvider); /// @notice Emitted when price provider is removed /// @param priceProvider removed price provider address event PriceProviderRemoved(IPriceProvider indexed priceProvider); /// @notice Emitted when asset is assigned to price provider /// @param asset assigned asset address /// @param priceProvider price provider address event PriceProviderForAsset(address indexed asset, IPriceProvider indexed priceProvider); /// @notice Register new price provider /// @param _priceProvider address of price provider function addPriceProvider(IPriceProvider _priceProvider) external; /// @notice Unregister price provider /// @param _priceProvider address of price provider to be removed function removePriceProvider(IPriceProvider _priceProvider) external; /// @notice Sets price provider for asset /// @dev Request for asset price is forwarded to the price provider assigned to that asset /// @param _asset address of an asset for which price provider will be used /// @param _priceProvider address of price provider function setPriceProviderForAsset(address _asset, IPriceProvider _priceProvider) external; /// @notice Returns "Time-Weighted Average Price" for an asset /// @param _asset address of an asset for which to read price /// @return price TWAP price of a token with 18 decimals function getPrice(address _asset) external view returns (uint256 price); /// @notice Gets price provider assigned to an asset /// @param _asset address of an asset for which to get price provider /// @return priceProvider address of price provider function priceProviders(address _asset) external view returns (IPriceProvider priceProvider); /// @notice Gets token address in which prices are quoted /// @return quoteToken address function quoteToken() external view returns (address); /// @notice Gets manager role address /// @return manager role address function manager() external view returns (address); /// @notice Checks if providers are available for an asset /// @param _asset asset address to check /// @return returns TRUE if price feed is ready, otherwise false function providersReadyForAsset(address _asset) external view returns (bool); /// @notice Returns true if address is a registered price provider /// @param _provider address of price provider to be removed /// @return true if address is a registered price provider, otherwise false function isPriceProvider(IPriceProvider _provider) external view returns (bool); /// @notice Gets number of price providers registered /// @return number of price providers registered function providersCount() external view returns (uint256); /// @notice Gets an array of price providers /// @return array of price providers function providerList() external view returns (address[] memory); /// @notice Sanity check function /// @return returns always TRUE function priceProvidersRepositoryPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "./INotificationReceiver.sol"; interface IShareToken is IERC20Metadata { /// @notice Emitted every time receiver is notified about token transfer /// @param notificationReceiver receiver address /// @param success false if TX reverted on `notificationReceiver` side, otherwise true event NotificationSent( INotificationReceiver indexed notificationReceiver, bool success ); /// @notice Mint method for Silo to create debt position /// @param _account wallet for which to mint token /// @param _amount amount of token to be minted function mint(address _account, uint256 _amount) external; /// @notice Burn method for Silo to close debt position /// @param _account wallet for which to burn token /// @param _amount amount of token to be burned function burn(address _account, uint256 _amount) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "./IBaseSilo.sol"; interface ISilo is IBaseSilo { /// @notice Deposit `_amount` of `_asset` tokens from `msg.sender` to the Silo /// @param _asset The address of the token to deposit /// @param _amount The amount of the token to deposit /// @param _collateralOnly True if depositing collateral only /// @return collateralAmount deposited amount /// @return collateralShare user collateral shares based on deposited amount function deposit(address _asset, uint256 _amount, bool _collateralOnly) external returns (uint256 collateralAmount, uint256 collateralShare); /// @notice Router function to deposit `_amount` of `_asset` tokens to the Silo for the `_depositor` /// @param _asset The address of the token to deposit /// @param _depositor The address of the recipient of collateral tokens /// @param _amount The amount of the token to deposit /// @param _collateralOnly True if depositing collateral only /// @return collateralAmount deposited amount /// @return collateralShare `_depositor` collateral shares based on deposited amount function depositFor(address _asset, address _depositor, uint256 _amount, bool _collateralOnly) external returns (uint256 collateralAmount, uint256 collateralShare); /// @notice Withdraw `_amount` of `_asset` tokens from the Silo to `msg.sender` /// @param _asset The address of the token to withdraw /// @param _amount The amount of the token to withdraw /// @param _collateralOnly True if withdrawing collateral only deposit /// @return withdrawnAmount withdrawn amount that was transferred to user /// @return withdrawnShare burned share based on `withdrawnAmount` function withdraw(address _asset, uint256 _amount, bool _collateralOnly) external returns (uint256 withdrawnAmount, uint256 withdrawnShare); /// @notice Router function to withdraw `_amount` of `_asset` tokens from the Silo for the `_depositor` /// @param _asset The address of the token to withdraw /// @param _depositor The address that originally deposited the collateral tokens being withdrawn, /// it should be the one initiating the withdrawal through the router /// @param _receiver The address that will receive the withdrawn tokens /// @param _amount The amount of the token to withdraw /// @param _collateralOnly True if withdrawing collateral only deposit /// @return withdrawnAmount withdrawn amount that was transferred to `_receiver` /// @return withdrawnShare burned share based on `withdrawnAmount` function withdrawFor( address _asset, address _depositor, address _receiver, uint256 _amount, bool _collateralOnly ) external returns (uint256 withdrawnAmount, uint256 withdrawnShare); /// @notice Borrow `_amount` of `_asset` tokens from the Silo to `msg.sender` /// @param _asset The address of the token to borrow /// @param _amount The amount of the token to borrow /// @return debtAmount borrowed amount /// @return debtShare user debt share based on borrowed amount function borrow(address _asset, uint256 _amount) external returns (uint256 debtAmount, uint256 debtShare); /// @notice Router function to borrow `_amount` of `_asset` tokens from the Silo for the `_receiver` /// @param _asset The address of the token to borrow /// @param _borrower The address that will take the loan, /// it should be the one initiating the borrowing through the router /// @param _receiver The address of the asset receiver /// @param _amount The amount of the token to borrow /// @return debtAmount borrowed amount /// @return debtShare `_receiver` debt share based on borrowed amount function borrowFor(address _asset, address _borrower, address _receiver, uint256 _amount) external returns (uint256 debtAmount, uint256 debtShare); /// @notice Repay `_amount` of `_asset` tokens from `msg.sender` to the Silo /// @param _asset The address of the token to repay /// @param _amount amount of asset to repay, includes interests /// @return repaidAmount amount repaid /// @return burnedShare burned debt share function repay(address _asset, uint256 _amount) external returns (uint256 repaidAmount, uint256 burnedShare); /// @notice Allows to repay in behalf of borrower to execute liquidation /// @param _asset The address of the token to repay /// @param _borrower The address of the user to have debt tokens burned /// @param _amount amount of asset to repay, includes interests /// @return repaidAmount amount repaid /// @return burnedShare burned debt share function repayFor(address _asset, address _borrower, uint256 _amount) external returns (uint256 repaidAmount, uint256 burnedShare); /// @dev harvest protocol fees from an array of assets /// @return harvestedAmounts amount harvested during tx execution for each of silo asset function harvestProtocolFees() external returns (uint256[] memory harvestedAmounts); /// @notice Function to update interests for `_asset` token since the last saved state /// @param _asset The address of the token to be updated /// @return interest accrued interest function accrueInterest(address _asset) external returns (uint256 interest); /// @notice this methods does not requires to have tokens in order to liquidate user /// @dev during liquidation process, msg.sender will be notified once all collateral will be send to him /// msg.sender needs to be `IFlashLiquidationReceiver` /// @param _users array of users to liquidate /// @param _flashReceiverData this data will be forward to msg.sender on notification /// @return assets array of all processed assets (collateral + debt, including removed) /// @return receivedCollaterals receivedCollaterals[userId][assetId] => amount /// amounts of collaterals send to `_flashReceiver` /// @return shareAmountsToRepaid shareAmountsToRepaid[userId][assetId] => amount /// required amounts of debt to be repaid function flashLiquidate(address[] memory _users, bytes memory _flashReceiverData) external returns ( address[] memory assets, uint256[][] memory receivedCollaterals, uint256[][] memory shareAmountsToRepaid ); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; interface ISiloConvexStateChangesHandler { /// @dev This function checkpoints two users rewards. This part of code can not be implemented in the Silo /// because of the smart contract bytecode limit. Can be called from the Silo only. /// @param _firstToCheckpoint address to checkpoint, can be zero. /// @param _secondToCheckpoint address to checkpoint, can be zero. function beforeBalanceUpdate(address _firstToCheckpoint, address _secondToCheckpoint) external; /// @dev This function checks ConvexSiloWrapper `_wrapper`. Returns false if `_wrapper` is not registered in /// `ConvexSiloWrapperFactory`. Returns false if Curve pool can not be fetched for `_wrapper` underlying /// Curve LP token. Otherwise, returns true. /// @param _wrapper address. /// @return If the return argument is false, Silo contract must revert. function wrapperSetupVerification(address _wrapper) external view returns (bool); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; interface ISiloFactory { /// @notice Emitted when Silo is deployed /// @param silo address of deployed Silo /// @param asset address of asset for which Silo was deployed /// @param version version of silo implementation event NewSiloCreated(address indexed silo, address indexed asset, uint128 version); /// @notice Must be called by repository on constructor /// @param _siloRepository the SiloRepository to set function initRepository(address _siloRepository) external; /// @notice Deploys Silo /// @param _siloAsset unique asset for which Silo is deployed /// @param _version version of silo implementation /// @param _data (optional) data that may be needed during silo creation /// @return silo deployed Silo address function createSilo(address _siloAsset, uint128 _version, bytes memory _data) external returns (address silo); /// @dev just a helper method to see if address is a factory function siloFactoryPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; interface ISiloFactoryV2 { /// @notice Emitted when Silo is deployed /// @param silo address of deployed Silo /// @param asset address of asset for which Silo was deployed /// @param version version of silo implementation event NewSiloCreated(address indexed silo, address indexed asset, uint128 version); /// @notice Deploys Silo /// @param _siloAsset unique asset for which Silo is deployed /// @param _version version of silo implementation /// @param _data (optional) data that may be needed during silo creation /// @return silo deployed Silo address function createSilo(address _siloAsset, uint128 _version, bytes memory _data) external returns (address silo); /// @dev just a helper method to see if address is a factory function siloFactoryPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "./ISiloFactory.sol"; import "./ITokensFactory.sol"; import "./IPriceProvidersRepository.sol"; import "./INotificationReceiver.sol"; import "./IInterestRateModel.sol"; interface ISiloRepository { /// @dev protocol fees in precision points (Solvency._PRECISION_DECIMALS), we do allow for fee == 0 struct Fees { /// @dev One time protocol fee for opening a borrow position in precision points (Solvency._PRECISION_DECIMALS) uint64 entryFee; /// @dev Protocol revenue share in interest paid in precision points (Solvency._PRECISION_DECIMALS) uint64 protocolShareFee; /// @dev Protocol share in liquidation profit in precision points (Solvency._PRECISION_DECIMALS). /// It's calculated from total collateral amount to be transferred to liquidator. uint64 protocolLiquidationFee; } struct SiloVersion { /// @dev Default version of Silo. If set to 0, it means it is not set. By default it is set to 1 uint128 byDefault; /// @dev Latest added version of Silo. If set to 0, it means it is not set. By default it is set to 1 uint128 latest; } /// @dev AssetConfig struct represents configurable parameters for each Silo struct AssetConfig { /// @dev Loan-to-Value ratio represents the maximum borrowing power of a specific collateral. /// For example, if the collateral asset has an LTV of 75%, the user can borrow up to 0.75 worth /// of quote token in the principal currency for every quote token worth of collateral. /// value uses 18 decimals eg. 100% == 1e18 /// max valid value is 1e18 so it needs storage of 60 bits uint64 maxLoanToValue; /// @dev Liquidation Threshold represents the threshold at which a borrow position will be considered /// undercollateralized and subject to liquidation for each collateral. For example, /// if a collateral has a liquidation threshold of 80%, it means that the loan will be /// liquidated when the borrowAmount value is worth 80% of the collateral value. /// value uses 18 decimals eg. 100% == 1e18 uint64 liquidationThreshold; /// @dev interest rate model address IInterestRateModel interestRateModel; } event NewDefaultMaximumLTV(uint64 defaultMaximumLTV); event NewDefaultLiquidationThreshold(uint64 defaultLiquidationThreshold); /// @notice Emitted on new Silo creation /// @param silo deployed Silo address /// @param asset unique asset for deployed Silo /// @param siloVersion version of deployed Silo event NewSilo(address indexed silo, address indexed asset, uint128 siloVersion); /// @notice Emitted when new Silo (or existing one) becomes a bridge pool (pool with only bridge tokens). /// @param pool address of the bridge pool, It can be zero address when bridge asset is removed and pool no longer /// is treated as bridge pool event BridgePool(address indexed pool); /// @notice Emitted on new bridge asset /// @param newBridgeAsset address of added bridge asset event BridgeAssetAdded(address indexed newBridgeAsset); /// @notice Emitted on removed bridge asset /// @param bridgeAssetRemoved address of removed bridge asset event BridgeAssetRemoved(address indexed bridgeAssetRemoved); /// @notice Emitted when default interest rate model is changed /// @param newModel address of new interest rate model event InterestRateModel(IInterestRateModel indexed newModel); /// @notice Emitted on price provider repository address update /// @param newProvider address of new oracle repository event PriceProvidersRepositoryUpdate( IPriceProvidersRepository indexed newProvider ); /// @notice Emitted on token factory address update /// @param newTokensFactory address of new token factory event TokensFactoryUpdate(address indexed newTokensFactory); /// @notice Emitted on router address update /// @param newRouter address of new router event RouterUpdate(address indexed newRouter); /// @notice Emitted on INotificationReceiver address update /// @param newIncentiveContract address of new INotificationReceiver event NotificationReceiverUpdate(INotificationReceiver indexed newIncentiveContract); /// @notice Emitted when new Silo version is registered /// @param factory factory address that deploys registered Silo version /// @param siloLatestVersion Silo version of registered Silo /// @param siloDefaultVersion current default Silo version event RegisterSiloVersion(address indexed factory, uint128 siloLatestVersion, uint128 siloDefaultVersion); /// @notice Emitted when Silo version is unregistered /// @param factory factory address that deploys unregistered Silo version /// @param siloVersion version that was unregistered event UnregisterSiloVersion(address indexed factory, uint128 siloVersion); /// @notice Emitted when default Silo version is updated /// @param newDefaultVersion new default version event SiloDefaultVersion(uint128 newDefaultVersion); /// @notice Emitted when default fee is updated /// @param newEntryFee new entry fee /// @param newProtocolShareFee new protocol share fee /// @param newProtocolLiquidationFee new protocol liquidation fee event FeeUpdate( uint64 newEntryFee, uint64 newProtocolShareFee, uint64 newProtocolLiquidationFee ); /// @notice Emitted when asset config is updated for a silo /// @param silo silo for which asset config is being set /// @param asset asset for which asset config is being set /// @param assetConfig new asset config event AssetConfigUpdate(address indexed silo, address indexed asset, AssetConfig assetConfig); /// @notice Emitted when silo (silo factory) version is set for asset /// @param asset asset for which asset config is being set /// @param version Silo version event VersionForAsset(address indexed asset, uint128 version); /// @param _siloAsset silo asset /// @return version of Silo that is assigned for provided asset, if not assigned it returns zero (default) function getVersionForAsset(address _siloAsset) external returns (uint128); /// @notice setter for `getVersionForAsset` mapping /// @param _siloAsset silo asset /// @param _version version of Silo that will be assigned for `_siloAsset`, zero (default) is acceptable function setVersionForAsset(address _siloAsset, uint128 _version) external; /// @notice use this method only when off-chain verification is OFF /// @dev Silo does NOT support rebase and deflationary tokens /// @param _siloAsset silo asset /// @param _siloData (optional) data that may be needed during silo creation /// @return createdSilo address of created silo function newSilo(address _siloAsset, bytes memory _siloData) external returns (address createdSilo); /// @notice use this method to deploy new version of Silo for an asset that already has Silo deployed. /// Only owner (DAO) can replace. /// @dev Silo does NOT support rebase and deflationary tokens /// @param _siloAsset silo asset /// @param _siloVersion version of silo implementation. Use 0 for default version which is fine /// for 99% of cases. /// @param _siloData (optional) data that may be needed during silo creation /// @return createdSilo address of created silo function replaceSilo( address _siloAsset, uint128 _siloVersion, bytes memory _siloData ) external returns (address createdSilo); /// @notice Set factory contract for debt and collateral tokens for each Silo asset /// @dev Callable only by owner /// @param _tokensFactory address of TokensFactory contract that deploys debt and collateral tokens function setTokensFactory(address _tokensFactory) external; /// @notice Set default fees /// @dev Callable only by owner /// @param _fees: /// - _entryFee one time protocol fee for opening a borrow position in precision points /// (Solvency._PRECISION_DECIMALS) /// - _protocolShareFee protocol revenue share in interest paid in precision points /// (Solvency._PRECISION_DECIMALS) /// - _protocolLiquidationFee protocol share in liquidation profit in precision points /// (Solvency._PRECISION_DECIMALS). It's calculated from total collateral amount to be transferred /// to liquidator. function setFees(Fees calldata _fees) external; /// @notice Set configuration for given asset in given Silo /// @dev Callable only by owner /// @param _silo Silo address for which config applies /// @param _asset asset address for which config applies /// @param _assetConfig: /// - _maxLoanToValue maximum Loan-to-Value, for details see `Repository.AssetConfig.maxLoanToValue` /// - _liquidationThreshold liquidation threshold, for details see `Repository.AssetConfig.maxLoanToValue` /// - _interestRateModel interest rate model address, for details see `Repository.AssetConfig.interestRateModel` function setAssetConfig( address _silo, address _asset, AssetConfig calldata _assetConfig ) external; /// @notice Set default interest rate model /// @dev Callable only by owner /// @param _defaultInterestRateModel default interest rate model function setDefaultInterestRateModel(IInterestRateModel _defaultInterestRateModel) external; /// @notice Set default maximum LTV /// @dev Callable only by owner /// @param _defaultMaxLTV default maximum LTV in precision points (Solvency._PRECISION_DECIMALS) function setDefaultMaximumLTV(uint64 _defaultMaxLTV) external; /// @notice Set default liquidation threshold /// @dev Callable only by owner /// @param _defaultLiquidationThreshold default liquidation threshold in precision points /// (Solvency._PRECISION_DECIMALS) function setDefaultLiquidationThreshold(uint64 _defaultLiquidationThreshold) external; /// @notice Set price provider repository /// @dev Callable only by owner /// @param _repository price provider repository address function setPriceProvidersRepository(IPriceProvidersRepository _repository) external; /// @notice Set router contract /// @dev Callable only by owner /// @param _router router address function setRouter(address _router) external; /// @notice Set NotificationReceiver contract /// @dev Callable only by owner /// @param _silo silo address for which to set `_notificationReceiver` /// @param _notificationReceiver NotificationReceiver address function setNotificationReceiver(address _silo, INotificationReceiver _notificationReceiver) external; /// @notice Adds new bridge asset /// @dev New bridge asset must be unique. Duplicates in bridge assets are not allowed. It's possible to add /// bridge asset that has been removed in the past. Note that all Silos must be synced manually. Callable /// only by owner. /// @param _newBridgeAsset bridge asset address function addBridgeAsset(address _newBridgeAsset) external; /// @notice Removes bridge asset /// @dev Note that all Silos must be synced manually. Callable only by owner. /// @param _bridgeAssetToRemove bridge asset address to be removed function removeBridgeAsset(address _bridgeAssetToRemove) external; /// @notice Registers new Silo version /// @dev User can choose which Silo version he wants to deploy. It's possible to have multiple versions of Silo. /// Callable only by owner. /// @param _factory factory contract that deploys new version of Silo /// @param _isDefault true if this version should be used as default function registerSiloVersion(ISiloFactory _factory, bool _isDefault) external; /// @notice Unregisters Silo version /// @dev Callable only by owner. /// @param _siloVersion Silo version to be unregistered function unregisterSiloVersion(uint128 _siloVersion) external; /// @notice Sets default Silo version /// @dev Callable only by owner. /// @param _defaultVersion Silo version to be set as default function setDefaultSiloVersion(uint128 _defaultVersion) external; /// @notice Check if contract address is a Silo deployment /// @param _silo address of expected Silo /// @return true if address is Silo deployment, otherwise false function isSilo(address _silo) external view returns (bool); /// @notice Get Silo address of asset /// @param _asset address of asset /// @return address of corresponding Silo deployment function getSilo(address _asset) external view returns (address); /// @notice Get Silo Factory for given version /// @param _siloVersion version of Silo implementation /// @return ISiloFactory contract that deploys Silos of given version function siloFactory(uint256 _siloVersion) external view returns (ISiloFactory); /// @notice Get debt and collateral Token Factory /// @return ITokensFactory contract that deploys debt and collateral tokens function tokensFactory() external view returns (ITokensFactory); /// @notice Get Router contract /// @return address of router contract function router() external view returns (address); /// @notice Get current bridge assets /// @dev Keep in mind that not all Silos may be synced with current bridge assets so it's possible that some /// assets in that list are not part of given Silo. /// @return address array of bridge assets function getBridgeAssets() external view returns (address[] memory); /// @notice Get removed bridge assets /// @dev Keep in mind that not all Silos may be synced with bridge assets so it's possible that some /// assets in that list are still part of given Silo. /// @return address array of bridge assets function getRemovedBridgeAssets() external view returns (address[] memory); /// @notice Get maximum LTV for asset in given Silo /// @dev If dedicated config is not set, method returns default config /// @param _silo address of Silo /// @param _asset address of an asset /// @return maximum LTV in precision points (Solvency._PRECISION_DECIMALS) function getMaximumLTV(address _silo, address _asset) external view returns (uint256); /// @notice Get Interest Rate Model address for asset in given Silo /// @dev If dedicated config is not set, method returns default config /// @param _silo address of Silo /// @param _asset address of an asset /// @return address of interest rate model function getInterestRateModel(address _silo, address _asset) external view returns (IInterestRateModel); /// @notice Get liquidation threshold for asset in given Silo /// @dev If dedicated config is not set, method returns default config /// @param _silo address of Silo /// @param _asset address of an asset /// @return liquidation threshold in precision points (Solvency._PRECISION_DECIMALS) function getLiquidationThreshold(address _silo, address _asset) external view returns (uint256); /// @notice Get incentive contract address. Incentive contracts are responsible for distributing rewards /// to debt and/or collateral token holders of given Silo /// @param _silo address of Silo /// @return incentive contract address function getNotificationReceiver(address _silo) external view returns (INotificationReceiver); /// @notice Get owner role address of Repository /// @return owner role address function owner() external view returns (address); /// @notice get PriceProvidersRepository contract that manages price providers implementations /// @return IPriceProvidersRepository address function priceProvidersRepository() external view returns (IPriceProvidersRepository); /// @dev Get protocol fee for opening a borrow position /// @return fee in precision points (Solvency._PRECISION_DECIMALS == 100%) function entryFee() external view returns (uint256); /// @dev Get protocol share fee /// @return protocol share fee in precision points (Solvency._PRECISION_DECIMALS == 100%) function protocolShareFee() external view returns (uint256); /// @dev Get protocol liquidation fee /// @return protocol liquidation fee in precision points (Solvency._PRECISION_DECIMALS == 100%) function protocolLiquidationFee() external view returns (uint256); /// @dev Checks all conditions for new silo creation and throws when not possible to create /// @param _asset address of asset for which you want to create silo /// @param _assetIsABridge bool TRUE when `_asset` is bridge asset, FALSE when it is not function ensureCanCreateSiloFor(address _asset, bool _assetIsABridge) external view; function siloRepositoryPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "./IShareToken.sol"; interface ITokensFactory { /// @notice Emitted when collateral token is deployed /// @param token address of deployed collateral token event NewShareCollateralTokenCreated(address indexed token); /// @notice Emitted when collateral token is deployed /// @param token address of deployed debt token event NewShareDebtTokenCreated(address indexed token); ///@notice Must be called by repository on constructor /// @param _siloRepository the SiloRepository to set function initRepository(address _siloRepository) external; /// @notice Deploys collateral token /// @param _name name of the token /// @param _symbol symbol of the token /// @param _asset underlying asset for which token is deployed /// @return address of deployed collateral share token function createShareCollateralToken( string memory _name, string memory _symbol, address _asset ) external returns (IShareToken); /// @notice Deploys debt token /// @param _name name of the token /// @param _symbol symbol of the token /// @param _asset underlying asset for which token is deployed /// @return address of deployed debt share token function createShareDebtToken( string memory _name, string memory _symbol, address _asset ) external returns (IShareToken); /// @dev just a helper method to see if address is a factory /// @return always true function tokensFactoryPing() external pure returns (bytes4); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; library EasyMath { error ZeroAssets(); error ZeroShares(); function toShare(uint256 amount, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256) { if (totalShares == 0 || totalAmount == 0) { return amount; } uint256 result = amount * totalShares / totalAmount; // Prevent rounding error if (result == 0 && amount != 0) { revert ZeroShares(); } return result; } function toShareRoundUp(uint256 amount, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256) { if (totalShares == 0 || totalAmount == 0) { return amount; } uint256 numerator = amount * totalShares; uint256 result = numerator / totalAmount; // Round up if (numerator % totalAmount != 0) { result += 1; } return result; } function toAmount(uint256 share, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256) { if (totalShares == 0 || totalAmount == 0) { return 0; } uint256 result = share * totalAmount / totalShares; // Prevent rounding error if (result == 0 && share != 0) { revert ZeroAssets(); } return result; } function toAmountRoundUp(uint256 share, uint256 totalAmount, uint256 totalShares) internal pure returns (uint256) { if (totalShares == 0 || totalAmount == 0) { return 0; } uint256 numerator = share * totalAmount; uint256 result = numerator / totalShares; // Round up if (numerator % totalShares != 0) { result += 1; } return result; } function toValue(uint256 _assetAmount, uint256 _assetPrice, uint256 _assetDecimals) internal pure returns (uint256) { return _assetAmount * _assetPrice / 10 ** _assetDecimals; } function sum(uint256[] memory _numbers) internal pure returns (uint256 s) { for(uint256 i; i < _numbers.length; i++) { s += _numbers[i]; } } /// @notice Calculates fraction between borrowed and deposited amount of tokens denominated in percentage /// @dev It assumes `_dp` = 100%. /// @param _dp decimal points used by model /// @param _totalDeposits current total deposits for assets /// @param _totalBorrowAmount current total borrows for assets /// @return utilization value function calculateUtilization(uint256 _dp, uint256 _totalDeposits, uint256 _totalBorrowAmount) internal pure returns (uint256) { if (_totalDeposits == 0 || _totalBorrowAmount == 0) return 0; return _totalBorrowAmount * _dp / _totalDeposits; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.7.6 <0.9.0; library Ping { function pong(function() external pure returns(bytes4) pingFunction) internal pure returns (bool) { return pingFunction.address != address(0) && pingFunction.selector == pingFunction(); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "../interfaces/IPriceProvidersRepository.sol"; import "../interfaces/ISilo.sol"; import "../interfaces/IInterestRateModel.sol"; import "../interfaces/ISiloRepository.sol"; import "./EasyMath.sol"; library Solvency { using EasyMath for uint256; /// @notice /// MaximumLTV - Maximum Loan-to-Value ratio represents the maximum borrowing power of all user's collateral /// positions in a Silo /// LiquidationThreshold - Liquidation Threshold represents the threshold at which all user's borrow positions /// in a Silo will be considered under collateralized and subject to liquidation enum TypeofLTV { MaximumLTV, LiquidationThreshold } error DifferentArrayLength(); error UnsupportedLTVType(); struct SolvencyParams { /// @param siloRepository SiloRepository address ISiloRepository siloRepository; /// @param silo Silo address ISilo silo; /// @param assets array with assets address[] assets; /// @param assetStates array of states for each asset, where index match the `assets` index ISilo.AssetStorage[] assetStates; /// @param user wallet address for which to read debt address user; } /// @dev is value that used for integer calculations and decimal points for utilization ratios, LTV, protocol fees uint256 internal constant _PRECISION_DECIMALS = 1e18; uint256 internal constant _INFINITY = type(uint256).max; /// @notice Returns current user LTV and second LTV chosen in params /// @dev This function is optimized for protocol use. In some cases there is no need to keep the calculation /// going and predefined results can be returned. /// @param _params `Solvency.SolvencyParams` struct with needed params for calculation /// @param _secondLtvType type of LTV to be returned as second value /// @return currentUserLTV Loan-to-Value ratio represents current user's proportion of debt to collateral /// @return secondLTV second type of LTV which depends on _secondLtvType, zero is returned if the value of the loan /// or the collateral are zero function calculateLTVs(SolvencyParams memory _params, TypeofLTV _secondLtvType) internal view returns (uint256 currentUserLTV, uint256 secondLTV) { uint256[] memory totalBorrowAmounts = getBorrowAmounts(_params); // this return avoids eg. additional checks on withdraw, when user did not borrow any asset if (EasyMath.sum(totalBorrowAmounts) == 0) return (0, 0); IPriceProvidersRepository priceProvidersRepository = _params.siloRepository.priceProvidersRepository(); uint256[] memory borrowValues = convertAmountsToValues( priceProvidersRepository, _params.assets, totalBorrowAmounts ); // value of user's total debt uint256 borrowTotalValue = EasyMath.sum(borrowValues); if (borrowTotalValue == 0) return (0, 0); uint256[] memory collateralValues = getUserCollateralValues(priceProvidersRepository, _params); // value of user's collateral uint256 collateralTotalValue = EasyMath.sum(collateralValues); if (collateralTotalValue == 0) return (_INFINITY, 0); // value of theoretical debt user can have depending on TypeofLTV uint256 borrowAvailableTotalValue = _getTotalAvailableToBorrowValue( _params.siloRepository, address(_params.silo), _params.assets, _secondLtvType, collateralValues ); currentUserLTV = borrowTotalValue * _PRECISION_DECIMALS / collateralTotalValue; // one of Solvency.TypeofLTV secondLTV = borrowAvailableTotalValue * _PRECISION_DECIMALS / collateralTotalValue; } /// @notice Calculates chosen LTV limit /// @dev This function should be used by external actors like SiloLens and UI/subgraph. `calculateLTVs` is /// optimized for protocol use and may not return second LVT calculation when they are not needed. /// @param _params `Solvency.SolvencyParams` struct with needed params for calculation /// @param _ltvType acceptable values are only TypeofLTV.MaximumLTV or TypeofLTV.LiquidationThreshold /// @return limit theoretical LTV limit of `_ltvType` function calculateLTVLimit(SolvencyParams memory _params, TypeofLTV _ltvType) internal view returns (uint256 limit) { IPriceProvidersRepository priceProvidersRepository = _params.siloRepository.priceProvidersRepository(); uint256[] memory collateralValues = getUserCollateralValues(priceProvidersRepository, _params); // value of user's collateral uint256 collateralTotalValue = EasyMath.sum(collateralValues); if (collateralTotalValue == 0) return 0; // value of theoretical debt user can have depending on TypeofLTV uint256 borrowAvailableTotalValue = _getTotalAvailableToBorrowValue( _params.siloRepository, address(_params.silo), _params.assets, _ltvType, collateralValues ); limit = borrowAvailableTotalValue * _PRECISION_DECIMALS / collateralTotalValue; } /// @notice Returns worth (in quote token) of each collateral deposit of a user /// @param _priceProvidersRepository address of IPriceProvidersRepository where prices are read /// @param _params `Solvency.SolvencyParams` struct with needed params for calculation /// @return collateralValues worth of each collateral deposit of a user as an array function getUserCollateralValues(IPriceProvidersRepository _priceProvidersRepository, SolvencyParams memory _params) internal view returns(uint256[] memory collateralValues) { uint256[] memory collateralAmounts = getCollateralAmounts(_params); collateralValues = convertAmountsToValues(_priceProvidersRepository, _params.assets, collateralAmounts); } /// @notice Convert assets amounts to values in quote token (amount * price) /// @param _priceProviderRepo address of IPriceProvidersRepository where prices are read /// @param _assets array with assets for which prices are read /// @param _amounts array of amounts /// @return values array of values for corresponding assets function convertAmountsToValues( IPriceProvidersRepository _priceProviderRepo, address[] memory _assets, uint256[] memory _amounts ) internal view returns (uint256[] memory values) { if (_assets.length != _amounts.length) revert DifferentArrayLength(); values = new uint256[](_assets.length); for (uint256 i = 0; i < _assets.length; i++) { if (_amounts[i] == 0) continue; uint256 assetPrice = _priceProviderRepo.getPrice(_assets[i]); uint8 assetDecimals = ERC20(_assets[i]).decimals(); values[i] = _amounts[i].toValue(assetPrice, assetDecimals); } } /// @notice Get amount of collateral for each asset /// @param _params `Solvency.SolvencyParams` struct with needed params for calculation /// @return collateralAmounts array of amounts for each token in Silo. May contain zero values if user /// did not deposit given collateral token. function getCollateralAmounts(SolvencyParams memory _params) internal view returns (uint256[] memory collateralAmounts) { if (_params.assets.length != _params.assetStates.length) { revert DifferentArrayLength(); } collateralAmounts = new uint256[](_params.assets.length); for (uint256 i = 0; i < _params.assets.length; i++) { uint256 userCollateralTokenBalance = _params.assetStates[i].collateralToken.balanceOf(_params.user); uint256 userCollateralOnlyTokenBalance = _params.assetStates[i].collateralOnlyToken.balanceOf(_params.user); if (userCollateralTokenBalance + userCollateralOnlyTokenBalance == 0) continue; uint256 rcomp = getRcomp(_params.silo, _params.siloRepository, _params.assets[i], block.timestamp); collateralAmounts[i] = getUserCollateralAmount( _params.assetStates[i], userCollateralTokenBalance, userCollateralOnlyTokenBalance, rcomp, _params.siloRepository ); } } /// @notice Get amount of debt for each asset /// @param _params `Solvency.SolvencyParams` struct with needed params for calculation /// @return totalBorrowAmounts array of amounts for each token in Silo. May contain zero values if user /// did not borrow given token. function getBorrowAmounts(SolvencyParams memory _params) internal view returns (uint256[] memory totalBorrowAmounts) { if (_params.assets.length != _params.assetStates.length) { revert DifferentArrayLength(); } totalBorrowAmounts = new uint256[](_params.assets.length); for (uint256 i = 0; i < _params.assets.length; i++) { uint256 rcomp = getRcomp(_params.silo, _params.siloRepository, _params.assets[i], block.timestamp); totalBorrowAmounts[i] = getUserBorrowAmount(_params.assetStates[i], _params.user, rcomp); } } /// @notice Get amount of deposited token, including collateralOnly deposits /// @param _assetStates state of deposited asset in Silo /// @param _userCollateralTokenBalance balance of user's share collateral token /// @param _userCollateralOnlyTokenBalance balance of user's share collateralOnly token /// @param _rcomp compounded interest rate to account for during calculations, could be 0 /// @param _siloRepository SiloRepository address /// @return amount of underlying token deposited, including collateralOnly deposit function getUserCollateralAmount( ISilo.AssetStorage memory _assetStates, uint256 _userCollateralTokenBalance, uint256 _userCollateralOnlyTokenBalance, uint256 _rcomp, ISiloRepository _siloRepository ) internal view returns (uint256) { uint256 assetAmount = _userCollateralTokenBalance == 0 ? 0 : _userCollateralTokenBalance.toAmount( totalDepositsWithInterest(_assetStates.totalDeposits, _siloRepository.protocolShareFee(), _rcomp), _assetStates.collateralToken.totalSupply() ); uint256 assetCollateralOnlyAmount = _userCollateralOnlyTokenBalance == 0 ? 0 : _userCollateralOnlyTokenBalance.toAmount( _assetStates.collateralOnlyDeposits, _assetStates.collateralOnlyToken.totalSupply() ); return assetAmount + assetCollateralOnlyAmount; } /// @notice Get amount of borrowed token /// @param _assetStates state of borrowed asset in Silo /// @param _user user wallet address for which to read debt /// @param _rcomp compounded interest rate to account for during calculations, could be 0 /// @return amount of borrowed token function getUserBorrowAmount(ISilo.AssetStorage memory _assetStates, address _user, uint256 _rcomp) internal view returns (uint256) { uint256 balance = _assetStates.debtToken.balanceOf(_user); if (balance == 0) return 0; uint256 totalBorrowAmountCached = totalBorrowAmountWithInterest(_assetStates.totalBorrowAmount, _rcomp); return balance.toAmountRoundUp(totalBorrowAmountCached, _assetStates.debtToken.totalSupply()); } /// @notice Get compounded interest rate from the model /// @param _silo Silo address /// @param _siloRepository SiloRepository address /// @param _asset address of asset for which to read interest rate /// @param _timestamp used to determine amount of time from last rate update /// @return rcomp compounded interest rate for an asset function getRcomp(ISilo _silo, ISiloRepository _siloRepository, address _asset, uint256 _timestamp) internal view returns (uint256 rcomp) { IInterestRateModel model = _siloRepository.getInterestRateModel(address(_silo), _asset); rcomp = model.getCompoundInterestRate(address(_silo), _asset, _timestamp); } /// @notice Returns total deposits with interest dynamically calculated with the provided rComp /// @param _assetTotalDeposits total deposits for asset /// @param _protocolShareFee `siloRepository.protocolShareFee()` /// @param _rcomp compounded interest rate /// @return _totalDepositsWithInterests total deposits amount with interest function totalDepositsWithInterest(uint256 _assetTotalDeposits, uint256 _protocolShareFee, uint256 _rcomp) internal pure returns (uint256 _totalDepositsWithInterests) { uint256 depositorsShare = _PRECISION_DECIMALS - _protocolShareFee; return _assetTotalDeposits + _assetTotalDeposits * _rcomp / _PRECISION_DECIMALS * depositorsShare / _PRECISION_DECIMALS; } /// @notice Returns total borrow amount with interest dynamically calculated with the provided rComp /// @param _totalBorrowAmount total borrow amount /// @param _rcomp compounded interest rate /// @return totalBorrowAmountWithInterests total borrow amount with interest function totalBorrowAmountWithInterest(uint256 _totalBorrowAmount, uint256 _rcomp) internal pure returns (uint256 totalBorrowAmountWithInterests) { totalBorrowAmountWithInterests = _totalBorrowAmount + _totalBorrowAmount * _rcomp / _PRECISION_DECIMALS; } /// @notice Calculates protocol liquidation fee and new protocol total fees collected /// @param _protocolEarnedFees amount of total collected fees so far /// @param _amount amount on which we will apply fee /// @param _liquidationFee liquidation fee in Solvency._PRECISION_DECIMALS /// @return liquidationFeeAmount calculated interest /// @return newProtocolEarnedFees the new total amount of protocol fees function calculateLiquidationFee(uint256 _protocolEarnedFees, uint256 _amount, uint256 _liquidationFee) internal pure returns (uint256 liquidationFeeAmount, uint256 newProtocolEarnedFees) { unchecked { // If we overflow on multiplication it should not revert tx, we will get lower fees liquidationFeeAmount = _amount * _liquidationFee / Solvency._PRECISION_DECIMALS; if (_protocolEarnedFees > type(uint256).max - liquidationFeeAmount) { newProtocolEarnedFees = type(uint256).max; liquidationFeeAmount = type(uint256).max - _protocolEarnedFees; } else { newProtocolEarnedFees = _protocolEarnedFees + liquidationFeeAmount; } } } /// @notice Calculates theoretical value (in quote token) that user could borrow based given collateral value /// @param _siloRepository SiloRepository address /// @param _silo Silo address /// @param _asset address of collateral token /// @param _type type of LTV limit to use for calculations /// @param _collateralValue value of collateral deposit (in quote token) /// @return availableToBorrow value (in quote token) that user can borrow against collateral value function _getAvailableToBorrowValue( ISiloRepository _siloRepository, address _silo, address _asset, TypeofLTV _type, uint256 _collateralValue ) private view returns (uint256 availableToBorrow) { uint256 assetLTV; if (_type == TypeofLTV.MaximumLTV) { assetLTV = _siloRepository.getMaximumLTV(_silo, _asset); } else if (_type == TypeofLTV.LiquidationThreshold) { assetLTV = _siloRepository.getLiquidationThreshold(_silo, _asset); } else { revert UnsupportedLTVType(); } // value that can be borrowed against the deposit // ie. for assetLTV = 50%, 1 ETH * 50% = 0.5 ETH of available to borrow availableToBorrow = _collateralValue * assetLTV / _PRECISION_DECIMALS; } /// @notice Calculates theoretical value (in quote token) that user can borrow based on deposited collateral /// @param _siloRepository SiloRepository address /// @param _silo Silo address /// @param _assets array with assets /// @param _ltvType type of LTV limit to use for calculations /// acceptable values are only TypeofLTV.MaximumLTV or TypeofLTV.LiquidationThreshold /// @param _collateralValues value (worth in quote token) of each deposit made by user /// @return totalAvailableToBorrowValue value (in quote token) that user can borrow against collaterals function _getTotalAvailableToBorrowValue( ISiloRepository _siloRepository, address _silo, address[] memory _assets, TypeofLTV _ltvType, uint256[] memory _collateralValues ) private view returns (uint256 totalAvailableToBorrowValue) { if (_assets.length != _collateralValues.length) revert DifferentArrayLength(); for (uint256 i = 0; i < _assets.length; i++) { totalAvailableToBorrowValue += _getAvailableToBorrowValue( _siloRepository, _silo, _assets[i], _ltvType, _collateralValues[i] ); } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; library TokenHelper { uint256 private constant _BYTES32_SIZE = 32; error TokenIsNotAContract(); function assertAndGetDecimals(address _token) internal view returns (uint256) { (bool hasMetadata, bytes memory data) = _tokenMetadataCall(_token, abi.encodeCall(IERC20Metadata.decimals,())); // decimals() is optional in the ERC20 standard, so if metadata is not accessible // we assume there are no decimals and use 0. if (!hasMetadata) { return 0; } return abi.decode(data, (uint8)); } /// @dev Returns the symbol for the provided ERC20 token. /// An empty string is returned if the call to the token didn't succeed. /// @param _token address of the token to get the symbol for /// @return assetSymbol the token symbol function symbol(address _token) internal view returns (string memory assetSymbol) { (bool hasMetadata, bytes memory data) = _tokenMetadataCall(_token, abi.encodeCall(IERC20Metadata.symbol,())); if (!hasMetadata || data.length == 0) { return "?"; } else if (data.length == _BYTES32_SIZE) { return string(removeZeros(data)); } else { return abi.decode(data, (string)); } } /// @dev Removes bytes with value equal to 0 from the provided byte array. /// @param _data byte array from which to remove zeroes /// @return result byte array with zeroes removed function removeZeros(bytes memory _data) internal pure returns (bytes memory result) { uint256 n = _data.length; unchecked { for (uint256 i; i < n; i++) { if (_data[i] == 0) continue; result = abi.encodePacked(result, _data[i]); } } } /// @dev Performs a staticcall to the token to get its metadata (symbol, decimals, name) function _tokenMetadataCall(address _token, bytes memory _data) private view returns(bool, bytes memory) { // We need to do this before the call, otherwise the call will succeed even for EOAs if (!Address.isContract(_token)) revert TokenIsNotAContract(); (bool success, bytes memory result) = _token.staticcall(_data); // If the call reverted we assume the token doesn't follow the metadata extension if (!success) { return (false, ""); } return (true, result); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "./SiloV2.sol"; import "./interfaces/ISiloConvexStateChangesHandler.sol"; /// @notice Modification of the Silo for ConvexSiloWrapper tokens. /// The Silo asset can be used only as a collateral only asset and can't be borrowed. Convex rewards are checkpointed /// for the user on every collateral state change. contract SiloConvex is SiloV2 { /// @dev Checkpoints user's rewards, verifies the ConvexSiloWrapper setup. This code can not be implemented in /// Silo contract because of the smart contract bytecode limit. // solhint-disable-next-line var-name-mixedcase ISiloConvexStateChangesHandler internal immutable _STATE_CHANGES_HANDLER; /// @dev Revert when Silo asset is not a ConvexSiloWrapper. Also reverts when the Curve pool can not be fetched for /// the underlying Curve LP token. error InvalidWrapper(); /// @dev Revert when Silo asset is being borrowed, that is prohibited. error SiloAssetBorrowed(); constructor ( ISiloRepository _repository, ISiloConvexStateChangesHandler _stateChangesHandler, address _siloAsset, uint128 _version ) SiloV2(_repository, _siloAsset, _version) { _STATE_CHANGES_HANDLER = _stateChangesHandler; if (!_stateChangesHandler.wrapperSetupVerification(_siloAsset)) revert InvalidWrapper(); } /// @inheritdoc ISilo function deposit(address _asset, uint256 _amount, bool _collateralOnly) external virtual override returns (uint256 collateralAmount, uint256 collateralShare) { // IMPORTANT - keep `_beforeBalanceUpdate` at the beginning of the function _beforeBalanceUpdate(msg.sender, address(0)); _assetIsCollateralOnly(_asset, _collateralOnly); return _deposit(_asset, msg.sender, msg.sender, _amount, _collateralOnly); } /// @inheritdoc ISilo function depositFor( address _asset, address _depositor, uint256 _amount, bool _collateralOnly ) external virtual override returns (uint256 collateralAmount, uint256 collateralShare) { // IMPORTANT - keep `_beforeBalanceUpdate` at the beginning of the function _beforeBalanceUpdate(msg.sender, _depositor); _assetIsCollateralOnly(_asset, _collateralOnly); return _deposit(_asset, msg.sender, _depositor, _amount, _collateralOnly); } /// @inheritdoc ISilo function withdraw(address _asset, uint256 _amount, bool _collateralOnly) external virtual override returns (uint256 withdrawnAmount, uint256 withdrawnShare) { // IMPORTANT - keep `_beforeBalanceUpdate` at the beginning of the function _beforeBalanceUpdate(msg.sender, address(0)); return _withdraw(_asset, msg.sender, msg.sender, _amount, _collateralOnly); } /// @inheritdoc ISilo function withdrawFor( address _asset, address _depositor, address _receiver, uint256 _amount, bool _collateralOnly ) external virtual override onlyRouter returns (uint256 withdrawnAmount, uint256 withdrawnShare) { // IMPORTANT - keep `_beforeBalanceUpdate` at the beginning of the function _beforeBalanceUpdate(_depositor, _receiver); return _withdraw(_asset, _depositor, _receiver, _amount, _collateralOnly); } /// @inheritdoc ISilo function borrow(address _asset, uint256 _amount) external virtual override returns (uint256 debtAmount, uint256 debtShare) { // Revert on a attempt to borrow a Silo asset. if (_isSiloAsset(_asset)) revert SiloAssetBorrowed(); return _borrow(_asset, msg.sender, msg.sender, _amount); } /// @inheritdoc ISilo function borrowFor(address _asset, address _borrower, address _receiver, uint256 _amount) external virtual override onlyRouter returns (uint256 debtAmount, uint256 debtShare) { // Revert on a attempt to borrow a Silo asset. if (_isSiloAsset(_asset)) revert SiloAssetBorrowed(); return _borrow(_asset, _borrower, _receiver, _amount); } /// @inheritdoc ISilo function flashLiquidate(address[] calldata _users, bytes calldata _flashReceiverData) external virtual override returns ( address[] memory assets, uint256[][] memory receivedCollaterals, uint256[][] memory shareAmountsToRepay ) { assets = getAssets(); uint256 usersLength = _users.length; receivedCollaterals = new uint256[][](usersLength); shareAmountsToRepay = new uint256[][](usersLength); for (uint256 i = 0; i < usersLength;) { // IMPORTANT - keep `_beforeBalanceUpdate` here and do not add any new actions before // this function call. This function can not be moved outside of a loop, otherwise the contract size limit // will be exceeded. _beforeBalanceUpdate(_users[i], address(0)); ( receivedCollaterals[i], shareAmountsToRepay[i] ) = _userLiquidation(assets, _users[i], IFlashLiquidationReceiver(msg.sender), _flashReceiverData); // `i` has the same type as `usersLength`. // Because of the condition `i < usersLength` overflow is not possible unchecked { i++; } } } /// @notice Rewards checkpoint /// @dev It is not possible to pass an array of addresses to checkpoint because of the smart contract size limit. /// Both parameters can be zero, checkpointing will be skipped in this case. /// @param _firstToCheckpoint address to checkpoint, can be zero. /// @param _secondToCheckpoint address to checkpoint, can be zero. function _beforeBalanceUpdate(address _firstToCheckpoint, address _secondToCheckpoint) internal virtual { _STATE_CHANGES_HANDLER.beforeBalanceUpdate(_firstToCheckpoint, _secondToCheckpoint); } /// @dev Revert on a deposit with a silo asset marked as not collateral only /// @dev Revert on a withdrawal if a silo asset will be withdrawn as not collateral only /// @param _asset Asset to be deposited/withdrawn into/from the silo /// @param _collateralOnly Flag whether the deposit/withdrawal is collateral only or not function _assetIsCollateralOnly(address _asset, bool _collateralOnly) internal virtual view { // We can't revert with reason string, because the revert reason will cause the size of this contract to exceed // the gas limit. if (_isSiloAsset(_asset) && !_collateralOnly) revert(); // solhint-disable-line reason-string } /// @dev Check if the asset is the silo asset /// @param _asset Asset to be deposited/withdrawn into/from the silo function _isSiloAsset(address _asset) internal view returns (bool) { return _asset == siloAsset; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "../lib/Ping.sol"; import "../SiloV2.sol"; import "../interfaces/ISiloFactoryV2.sol"; import "../interfaces/ISiloRepository.sol"; /// @title SiloFactory /// @notice Silo Factory has one job, deploy Silo implementation /// @dev Silo Factory is registered within SiloRepository contract and it's given a version. Each version /// is different Silo Factory that deploys different Silo implementation. Many Factory contracts can be /// registered with the Repository contract. /// @custom:security-contact [email protected] contract SiloFactoryV2 is ISiloFactoryV2 { address public siloRepository; /// @dev Write info to the log about the Silo Repository initialization event InitSiloRepository(); /// @dev Revert on a silo creation if a msg.sender is not a silo repository error OnlyRepository(); /// @dev Revert on a false sanity check with `Ping` library error InvalidSiloRepository(); /// @param _repository A silo repository address constructor(address _repository) { if (!Ping.pong(ISiloRepository(_repository).siloRepositoryPing)) { revert InvalidSiloRepository(); } siloRepository = _repository; emit InitSiloRepository(); } /// @inheritdoc ISiloFactoryV2 function createSilo( address _siloAsset, uint128 _version, bytes memory ) external virtual override returns (address silo) { // Only allow silo repository if (msg.sender != siloRepository) revert OnlyRepository(); silo = address(new SiloV2(ISiloRepository(msg.sender), _siloAsset, _version)); emit NewSiloCreated(silo, _siloAsset, _version); } function siloFactoryPing() external pure virtual override returns (bytes4) { return this.siloFactoryPing.selector; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./interfaces/ISilo.sol"; import "./BaseSilo.sol"; /// @title Silo with virtual methods /// @notice Silo is the main component of the protocol. It implements lending logic, manages and isolates /// risk, acts as a vault for assets, and performs liquidations. Each Silo is composed of the unique asset /// for which it was created (ie. UNI) and bridge assets (ie. ETH and SiloDollar). There may be multiple /// bridge assets at any given time. /// @dev Main Silo contact that inherits from Base contract. It implements all user/UI facing methods. /// @custom:security-contact [email protected] contract SiloV2 is ISilo, BaseSilo { constructor (ISiloRepository _repository, address _siloAsset, uint128 _version) BaseSilo(_repository, _siloAsset, _version) { // initial setup is done in BaseSilo, nothing to do here } /// @inheritdoc ISilo function deposit(address _asset, uint256 _amount, bool _collateralOnly) external virtual override returns (uint256 collateralAmount, uint256 collateralShare) { return _deposit(_asset, msg.sender, msg.sender, _amount, _collateralOnly); } /// @inheritdoc ISilo function depositFor( address _asset, address _depositor, uint256 _amount, bool _collateralOnly ) external virtual override returns (uint256 collateralAmount, uint256 collateralShare) { return _deposit(_asset, msg.sender, _depositor, _amount, _collateralOnly); } /// @inheritdoc ISilo function withdraw(address _asset, uint256 _amount, bool _collateralOnly) external virtual override returns (uint256 withdrawnAmount, uint256 withdrawnShare) { return _withdraw(_asset, msg.sender, msg.sender, _amount, _collateralOnly); } /// @inheritdoc ISilo function withdrawFor(address _asset, address _depositor, address _receiver, uint256 _amount, bool _collateralOnly) external virtual override onlyRouter returns (uint256 withdrawnAmount, uint256 withdrawnShare) { return _withdraw(_asset, _depositor, _receiver, _amount, _collateralOnly); } /// @inheritdoc ISilo function borrow( address _asset, uint256 _amount ) external virtual override returns (uint256 debtAmount, uint256 debtShare) { return _borrow(_asset, msg.sender, msg.sender, _amount); } /// @inheritdoc ISilo function borrowFor(address _asset, address _borrower, address _receiver, uint256 _amount) external virtual override onlyRouter returns (uint256 debtAmount, uint256 debtShare) { return _borrow(_asset, _borrower, _receiver, _amount); } /// @inheritdoc ISilo function repay(address _asset, uint256 _amount) external virtual override returns (uint256 repaidAmount, uint256 repaidShare) { return _repay(_asset, msg.sender, msg.sender, _amount); } /// @inheritdoc ISilo function repayFor(address _asset, address _borrower, uint256 _amount) external virtual override returns (uint256 repaidAmount, uint256 repaidShare) { return _repay(_asset, _borrower, msg.sender, _amount); } /// @inheritdoc ISilo function flashLiquidate(address[] memory _users, bytes memory _flashReceiverData) external virtual override returns ( address[] memory assets, uint256[][] memory receivedCollaterals, uint256[][] memory shareAmountsToRepay ) { assets = getAssets(); uint256 usersLength = _users.length; receivedCollaterals = new uint256[][](usersLength); shareAmountsToRepay = new uint256[][](usersLength); for (uint256 i = 0; i < usersLength; i++) { ( receivedCollaterals[i], shareAmountsToRepay[i] ) = _userLiquidation(assets, _users[i], IFlashLiquidationReceiver(msg.sender), _flashReceiverData); } } /// @inheritdoc ISilo function harvestProtocolFees() external virtual override returns (uint256[] memory harvestedAmounts) { address[] memory assets = getAssets(); harvestedAmounts = new uint256[](assets.length); address repositoryOwner = siloRepository.owner(); for (uint256 i; i < assets.length;) { unchecked { // it will not overflow because fee is much lower than any other amounts harvestedAmounts[i] = _harvestProtocolFees(assets[i], repositoryOwner); // we run out of gas before we overflow i i++; } } } /// @inheritdoc ISilo function accrueInterest(address _asset) public virtual override returns (uint256 interest) { return _accrueInterest(_asset); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.13; /// @dev This is cloned solution of @openzeppelin/contracts/security/ReentrancyGuard.sol abstract contract LiquidationReentrancyGuard { error LiquidationReentrancyCall(); uint256 private constant _LIQUIDATION_NOT_ENTERED = 1; uint256 private constant _LIQUIDATION_ENTERED = 2; uint256 private _liquidationStatus; modifier liquidationNonReentrant() { if (_liquidationStatus == _LIQUIDATION_ENTERED) { revert LiquidationReentrancyCall(); } _liquidationStatus = _LIQUIDATION_ENTERED; _; _liquidationStatus = _LIQUIDATION_NOT_ENTERED; } constructor() { _liquidationStatus = _LIQUIDATION_NOT_ENTERED; } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_repository","type":"address"},{"internalType":"contract ISiloConvexStateChangesHandler","name":"_stateChangesHandler","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidSiloRepository","type":"error"},{"inputs":[],"name":"OnlyRepository","type":"error"},{"anonymous":false,"inputs":[],"name":"InitSiloRepository","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"silo","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint128","name":"version","type":"uint128"}],"name":"NewSiloCreated","type":"event"},{"inputs":[{"internalType":"address","name":"_siloAsset","type":"address"},{"internalType":"uint128","name":"_version","type":"uint128"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"createSilo","outputs":[{"internalType":"address","name":"silo","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"siloFactoryPing","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"siloRepository","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60a060405234801561001057600080fd5b506040516161d13803806161d183398101604081905261002f91610160565b81610051816001600160a01b031663e99ed41d6100c260201b61019c1760201c565b61006d576040516295241d60e21b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b0383161781556040517f62b7374e631dbf873146b0bd9f3390235a4a47128c3336ec4344c32bc28fe2929190a1506001600160a01b0316608052506101c4565b60006001600160a01b03831615801590610141575082826040518163ffffffff1660e01b8152600401602060405180830381865afa158015610108573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061012c919061019a565b60e083901b6001600160e01b03199081169116145b9392505050565b6001600160a01b038116811461015d57600080fd5b50565b6000806040838503121561017357600080fd5b825161017e81610148565b602084015190925061018f81610148565b809150509250929050565b6000602082840312156101ac57600080fd5b81516001600160e01b03198116811461014157600080fd5b608051615ff36101de600039600060cd0152615ff36000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063573bbca5146100465780635a0c4de414610076578063bde127181461008b575b600080fd5b610059610054366004610246565b61009e565b6040516001600160a01b0390911681526020015b60405180910390f35b604051631683137960e21b815260200161006d565b600054610059906001600160a01b031681565b600080546001600160a01b031633146100ca576040516397691a2f60e01b815260040160405180910390fd5b337f000000000000000000000000000000000000000000000000000000000000000085856040516100fa90610222565b6001600160a01b0394851681529284166020840152921660408201526001600160801b039091166060820152608001604051809103906000f080158015610145573d6000803e3d6000fd5b506040516001600160801b03851681529091506001600160a01b0380861691908316907f3d603ed158e689891fb302f8dcdd070ca09f651c8b61183dda2db71384cca1579060200160405180910390a39392505050565b60006001600160a01b0383161580159061021b575082826040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102069190610334565b60e083901b6001600160e01b03199081169116145b9392505050565b615c5f806200035f83390190565b634e487b7160e01b600052604160045260246000fd5b60008060006060848603121561025b57600080fd5b83356001600160a01b038116811461027257600080fd5b925060208401356001600160801b038116811461028e57600080fd5b9150604084013567ffffffffffffffff808211156102ab57600080fd5b818601915086601f8301126102bf57600080fd5b8135818111156102d1576102d1610230565b604051601f8201601f19908116603f011681019083821181831017156102f9576102f9610230565b8160405282815289602084870101111561031257600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561034657600080fd5b81516001600160e01b03198116811461021b57600080fdfe6101206040523480156200001257600080fd5b5060405162005c5f38038062005c5f833981016040819052620000359162000396565b8382828282826001600081905550600180819055506200006e836001600160a01b031663e99ed41d620001a560201b620012781760201c565b6200008c57604051639f45596360e01b815260040160405180910390fd5b806001600160801b0316600003620000b757604051634eb171af60e01b815260040160405180910390fd5b6000620000cf836200023160201b620012fe1760201c565b6001600160801b03831660c0526001600160a01b03808616608052841660a0529050620000fe81600a6200051c565b60e052505050506001600160a01b038681166101008190526040516303f9c36b60e01b8152918716600483015293506303f9c36b92506024019050602060405180830381865afa15801562000157573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200017d91906200052a565b6200019b57604051631252497b60e11b815260040160405180910390fd5b50505050620005e4565b60006001600160a01b0383161580159062000228575082826040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001ed573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000213919062000555565b60e083901b6001600160e01b03199081169116145b90505b92915050565b6040805160048152602481019091526020810180516001600160e01b0390811663313ce56760e01b17909152600091829182916200027391869190620002a816565b915091508162000287575060009392505050565b808060200190518101906200029d919062000581565b60ff16949350505050565b60006060620002c2846200037760201b6200136b1760201c565b620002e0576040516373d39f9d60e01b815260040160405180910390fd5b600080856001600160a01b031685604051620002fd9190620005a6565b600060405180830381855afa9150503d80600081146200033a576040519150601f19603f3d011682016040523d82523d6000602084013e6200033f565b606091505b509150915081620003685760006040518060200160405280600081525093509350505062000370565b600193509150505b9250929050565b3b151590565b6001600160a01b03811681146200039357600080fd5b50565b60008060008060808587031215620003ad57600080fd5b8451620003ba816200037d565b6020860151909450620003cd816200037d565b6040860151909350620003e0816200037d565b60608601519092506001600160801b0381168114620003fe57600080fd5b939692955090935050565b634e487b7160e01b600052601160045260246000fd5b600181815b808511156200046057816000190482111562000444576200044462000409565b808516156200045257918102915b93841c939080029062000424565b509250929050565b60008262000479575060016200022b565b8162000488575060006200022b565b8160018114620004a15760028114620004ac57620004cc565b60019150506200022b565b60ff841115620004c057620004c062000409565b50506001821b6200022b565b5060208310610133831016604e8410600b8410161715620004f1575081810a6200022b565b620004fd83836200041f565b806000190482111562000514576200051462000409565b029392505050565b600062000228838362000468565b6000602082840312156200053d57600080fd5b815180151581146200054e57600080fd5b9392505050565b6000602082840312156200056857600080fd5b81516001600160e01b0319811681146200054e57600080fd5b6000602082840312156200059457600080fd5b815160ff811681146200054e57600080fd5b6000825160005b81811015620005c95760208186018101518583015201620005ad565b81811115620005d9576000828501525b509190910192915050565b60805160a05160c05160e051610100516155ad620006b2600039600061177a01526000505060006104080152600081816101f901528181610b7301528181611d3701528181612642015281816126800152614160015260008181610304015281816104a2015281816105e401528181610ac101528181610df701528181610f0801528181611acd01528181611ba601528181611e21015281816120bd01528181612241015281816125b8015281816126aa015281816130f5015281816131a501526132a501526155ad6000f3fe608060405234801561001057600080fd5b50600436106101735760003560e01c8063a388991b116100de578063ead5d35911610097578063f87c3aa811610071578063f87c3aa814610392578063fb255703146103d0578063fbf178db146103f0578063ffa1ad741461040357600080fd5b8063ead5d35914610359578063f364181c1461036c578063f3d470c21461037f57600080fd5b8063a388991b146102d1578063a6e08aa1146102d9578063b8c876b1146102ec578063bde12718146102ff578063bf27304114610326578063dbc5b4811461034657600080fd5b806364654cf51161013057806364654cf51461024657806367e4ac2c1461025c5780639198e5151461027157806393a94ca314610292578063976ce495146102b4578063a1dfa423146102c757600080fd5b8063151541f01461017857806322867d781461019657806338b51ce1146101be5780633edd1128146101e15780634521c019146101f45780634b8a352914610233575b600080fd5b61018061044b565b60405161018d91906148e1565b60405180910390f35b6101a96101a436600461490c565b61057e565b6040805192835260208301919091520161018d565b6101d16101cc366004614938565b610599565b604051901515815260200161018d565b6101a96101ef366004614963565b61064c565b61021b7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161018d565b6101a961024136600461490c565b61067e565b61024e6106b4565b60405161018d929190614a25565b610264610834565b60405161018d9190614a86565b61028461027f366004614938565b610896565b60405190815260200161018d565b6102a56102a0366004614a99565b6108a7565b60405161018d93929190614bd6565b6101a96102c2366004614c0f565b610a45565b6102cf610a54565b005b6102cf610a93565b6101d16102e7366004614c50565b610c2b565b6102846102fa366004614938565b610cf6565b61021b7f000000000000000000000000000000000000000000000000000000000000000081565b610339610334366004614938565b610d81565b60405161018d9190614c89565b6101a9610354366004614c97565b610df2565b6101a9610367366004614963565b610ee8565b6101a961037a366004614ce8565b610f03565b6101d161038d366004614c50565b610fde565b6103a56103a0366004614938565b611124565b604080518251815260208084015190820152918101516001600160401b03169082015260600161018d565b6103e36103de366004614938565b6111a4565b60405161018d9190614d66565b6101a96103fe366004614dbf565b611254565b61042a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516fffffffffffffffffffffffffffffffff909116815260200161018d565b60606000610457610834565b905080516001600160401b0381111561047257610472614e12565b60405190808252806020026020018201604052801561049b578160200160208202803683370190505b50915060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105229190614e28565b905060005b82518110156105785761055383828151811061054557610545614e45565b602002602001015183611371565b84828151811061056557610565614e45565b6020908102919091010152600101610527565b50505090565b60008061058d84333386611418565b915091505b9250929050565b60006001600160a01b0382166105c257604051630b328ad960e31b815260040160405180910390fd5b6000806105cd6106b4565b915091506000806106406040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602001306001600160a01b03168152602001868152602001858152602001886001600160a01b031681525060016115e4565b10159695505050505050565b60008061065a336000611753565b61066485846117da565b61067185333387876117fb565b915091505b935093915050565b60008061068a84611d35565b156106a85760405163f3b4e6a160e01b815260040160405180910390fd5b61058d84333386611d67565b606080600280548060200260200160405190810160405280929190818152602001828054801561070d57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116106ef575b5050505050915081516001600160401b0381111561072d5761072d614e12565b60405190808252806020026020018201604052801561076657816020015b610753614855565b81526020019060019003908161074b5790505b50905060005b825181101561082f576003600084838151811061078b5761078b614e45565b6020908102919091018101516001600160a01b039081168352828201939093526040918201600020825160c0810184528154851681526001820154851692810192909252600281015490931691810191909152600382015460608201526004820154608082015260059091015460a0820152825183908390811061081157610811614e45565b6020026020010181905250808061082790614e71565b91505061076c565b509091565b6060600280548060200260200160405190810160405280929190818152602001828054801561088c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161086e575b5050505050905090565b60006108a182612095565b92915050565b60608060606108b4610834565b925085806001600160401b038111156108cf576108cf614e12565b60405190808252806020026020018201604052801561090257816020015b60608152602001906001900390816108ed5790505b509250806001600160401b0381111561091d5761091d614e12565b60405190808252806020026020018201604052801561095057816020015b606081526020019060019003908161093b5790505b50915060005b81811015610a395761098f89898381811061097357610973614e45565b90506020020160208101906109889190614938565b6000611753565b6109f7858a8a848181106109a5576109a5614e45565b90506020020160208101906109ba9190614938565b338a8a8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061237692505050565b858381518110610a0957610a09614e45565b60200260200101858481518110610a2257610a22614e45565b602090810291909101019190915252600101610956565b50509450945094915050565b60008061067185853386611418565b600260005403610a7f5760405162461bcd60e51b8152600401610a7690614e8a565b60405180910390fd5b6002600055610a8c6125b4565b6001600055565b600260005403610ab55760405162461bcd60e51b8152600401610a7690614e8a565b600260008190555060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663296041ea6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610b1d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b459190810190614ef1565b905060005b8151811015610c1a576000828281518110610b6757610b67614e45565b602002602001015190507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614610c07576001600160a01b0381166000818152600460205260408082206002908101805460ff60401b19166802000000000000000017905590519092917f51efec4e8c6ee88d51e1ec000985e267b4296e493de77b4aa7a38315981390bf91a35b5080610c1281614e71565b915050610b4a565b50610c236125b4565b506001600055565b6001600160a01b038281166000908152600360205260408082206002015490516370a0823160e01b81528484166004820152919216906370a0823190602401602060405180830381865afa158015610c87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cab9190614f96565b158015610cef575060016001600160a01b0384166000908152600460205260409020600290810154600160401b900460ff1690811115610ced57610ced614d50565b145b9392505050565b6001600160a01b03811660008181526003602052604080822060049081015491516370a0823160e01b81523091810191909152919290916370a0823190602401602060405180830381865afa158015610d53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d779190614f96565b6108a19190614faf565b610d89614855565b506001600160a01b03908116600090815260036020818152604092839020835160c081018552815486168152600182015486169281019290925260028101549094169281019290925282015460608201526004820154608082015260059091015460a082015290565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e779190614e28565b6001600160a01b0316336001600160a01b031614610ea857604051639e41bdd760e01b815260040160405180910390fd5b610eb186611d35565b15610ecf5760405163f3b4e6a160e01b815260040160405180910390fd5b610edb86868686611d67565b9150915094509492505050565b600080610ef6336000611753565b61067185333387876127fe565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f889190614e28565b6001600160a01b0316336001600160a01b031614610fb957604051639e41bdd760e01b815260040160405180910390fd5b610fc38686611753565b610fd087878787876127fe565b915091509550959350505050565b6001600160a01b03828116600090815260036020526040808220805491516370a0823160e01b81528585166004820152929390929116906370a0823190602401602060405180830381865afa15801561103b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105f9190614f96565b1580156110d9575060018101546040516370a0823160e01b81526001600160a01b038581166004830152909116906370a0823190602401602060405180830381865afa1580156110b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d79190614f96565b155b801561111c575060016001600160a01b0385166000908152600460205260409020600290810154600160401b900460ff169081111561111a5761111a614d50565b145b949350505050565b6111516040518060600160405280600081526020016000815260200160006001600160401b031681525090565b506001600160a01b03166000818152600360208181526040808420815160608101835293810154845260050154838301529383526004905290829020600201546001600160401b03169181019190915290565b6111cc6040805160808101825260008082526020820181905291810182905290606082015290565b6001600160a01b0382166000908152600460209081526040918290208251608081018452815481526001820154928101929092526002808201546001600160401b03811694840194909452919290916060840191600160401b900460ff169081111561123a5761123a614d50565b600281111561124b5761124b614d50565b90525092915050565b6000806112613386611753565b61126b86846117da565b610edb86338787876117fb565b60006001600160a01b03831615801590610cef575082826040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e29190614fc6565b60e083901b6001600160e01b0319908116911614905092915050565b6040805160048152602481019091526020810180516001600160e01b031663313ce56760e01b1790526000908190819061133990859061295c565b915091508161134c575060009392505050565b808060200190518101906113609190614ff0565b60ff16949350505050565b3b151590565b60006002600054036113955760405162461bcd60e51b8152600401610a7690614e8a565b600260009081556001600160a01b0384168152600460205260409020805460018201546113c29190614faf565b915060006113cf85610cf6565b9050808311156113dd578092505b826000036113f05760009250505061140d565b81548301825561140a6001600160a01b0386168585612a0e565b50505b600160005592915050565b60008085816001600160a01b0382166000908152600460205260409020600290810154600160401b900460ff169081111561145557611455614d50565b03611473576040516367fa94e760e01b815260040160405180910390fd5b6002600054036114955760405162461bcd60e51b8152600401610a7690614e8a565b60026000556114a387612095565b506001600160a01b03871660009081526003602052604090206114c7818887612a71565b909450925060008390036114ee5760405163faf4da9160e01b815260040160405180910390fd5b866001600160a01b0316886001600160a01b03167f05f2eeda0e08e4b437f487c8d7d29b14537d15e3488170dc3de5dbdf8dac46848660405161153391815260200190565b60405180910390a36115506001600160a01b038916873087612bab565b838160050160008282546115649190614faf565b90915550506002810154604051632770a7eb60e21b81526001600160a01b0389811660048301526024820186905290911690639dc29fac90604401600060405180830381600087803b1580156115b957600080fd5b505af11580156115cd573d6000803e3d6000fd5b505050505060016000819055505094509492505050565b60008060006115f285612be9565b90506115fd81612d05565b600003611611576000809250925050610592565b600085600001516001600160a01b0316635ddf2be36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611655573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116799190614e28565b9050600061168c82886040015185612d4b565b9050600061169982612d05565b9050806000036116b3576000809550955050505050610592565b60006116bf848a612f66565b905060006116cc82612d05565b9050806000036116ea57600019600097509750505050505050610592565b60006117058b600001518c602001518d604001518d87612f84565b90508161171a670de0b6b3a764000086615013565b6117249190615048565b985081611739670de0b6b3a764000083615013565b6117439190615048565b9750505050505050509250929050565b6040516330bd295b60e01b81526001600160a01b03838116600483015282811660248301527f000000000000000000000000000000000000000000000000000000000000000016906330bd295b90604401600060405180830381600087803b1580156117be57600080fd5b505af11580156117d2573d6000803e3d6000fd5b505050505050565b6117e382611d35565b80156117ed575080155b156117f757600080fd5b5050565b6000806002600054036118205760405162461bcd60e51b8152600401610a7690614e8a565b60026000558661182f81612095565b5061183a8887610c2b565b6118575760405163206e46bd60e21b815260040160405180910390fd5b6001600160a01b03881660009081526003602052604081208694509085611882578160030154611888565b81600401545b9050851561199857611915818360010160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061190d9190614f96565b89919061301b565b9350611921878261505c565b60048381019190915560018301546040516340c10f1960e01b81526001600160a01b038b811693820193909352602481018790529116906340c10f1990604401600060405180830381600087803b15801561197b57600080fd5b505af115801561198f573d6000803e3d6000fd5b50505050611a68565b6119f1818360000160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118e9573d6000803e3d6000fd5b93506119fd878261505c565b600383015581546040516340c10f1960e01b81526001600160a01b038a8116600483015260248201879052909116906340c10f1990604401600060405180830381600087803b158015611a4f57600080fd5b505af1158015611a63573d6000803e3d6000fd5b505050505b611a7d6001600160a01b038b168a308a612bab565b6040805188815287151560208201526001600160a01b03808b1692908d16917fdd160bb401ec5b5e5ca443d41e8e7182f3fe72d70a04b9c0ba844483d212bcb5910160405180910390a3505060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635ddf2be36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b4d9190614e28565b6001600160a01b0383166000908152600360208190526040822060048101549181015493945092611b7e919061505c565b604051630968252160e11b81523060048201526001600160a01b0386811660248301529192507f0000000000000000000000000000000000000000000000000000000000000000909116906312d04a4290604401602060405180830381865afa158015611bef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c139190614f96565b846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c759190614ff0565b611c8090600a615158565b6040516341976e0960e01b81526001600160a01b0387811660048301528491908716906341976e0990602401602060405180830381865afa158015611cc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ced9190614f96565b611cf79190615013565b611d019190615048565b1115611d205760405163221e43cf60e11b815260040160405180910390fd5b50506001600055509197909650945050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b600080600260005403611d8c5760405162461bcd60e51b8152600401610a7690614e8a565b6002600055611d9a86612095565b50611da58686610fde565b611dc2576040516301c08c7960e11b815260040160405180910390fd5b82611dcc87610cf6565b1015611deb57604051634323a55560e01b815260040160405180910390fd5b600060036000886001600160a01b03166001600160a01b03168152602001908152602001600020905060008160050154905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663072ea61c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea19190614f96565b905060008115611ecd57670de0b6b3a7640000611ebe8389615013565b611ec89190615048565b611ed0565b60005b9050611f60838560020160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f4f9190614f96565b611f59848b61505c565b9190613078565b879650945080611f70878561505c565b611f7a919061505c565b60058501556001600160a01b038a1660009081526004602052604081206001018054839290611faa90849061505c565b909155505060028401546040516340c10f1960e01b81526001600160a01b038b8116600483015260248201889052909116906340c10f1990604401600060405180830381600087803b158015611fff57600080fd5b505af1158015612013573d6000803e3d6000fd5b50505050886001600160a01b03168a6001600160a01b03167f312a5e5e1079f5dda4e95dbbd0b908b291fd5b992ef22073643ab691572c5b528960405161205c91815260200190565b60405180910390a36120786001600160a01b038b168989612a0e565b612081896130d3565b505060016000555091969095509350505050565b6040516344cf3e9360e01b81523060048201526001600160a01b0382811660248301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906344cf3e9390604401602060405180830381865afa158015612106573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061212a9190615167565b15612148576040516313d0ff5960e31b815260040160405180910390fd5b6001600160a01b0382166000908152600360209081526040808320600490925282206002810154919290916001600160401b0316908190036121ab5750600201805467ffffffffffffffff1916426001600160401b031617905550600092915050565b4281036121bd57506000949350505050565b60006121c88661317d565b60405162f704bf60e21b81526001600160a01b03888116600483015242602483015291909116906303dc12fc906044016020604051808303816000875af1158015612217573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061223b9190614f96565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166325ed3d446040518163ffffffff1660e01b8152600401602060405180830381865afa15801561229d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c19190614f96565b600586015460018601549192509060008080670de0b6b3a76400006122e68887615013565b6122f09190615048565b9a50670de0b6b3a7640000868c02049150818401925083831015612318576000199250831991505b50808a036123268b8661505c565b60058b015560038a015461233b90829061505c565b60039a909a01999099555060018701555050506002909201805467ffffffffffffffff1916426001600160401b031617905550919392505050565b60608060026001540361239c57604051634fe29f1760e01b815260040160405180910390fd5b60026001556123aa85610599565b1561240057600086516001600160401b038111156123ca576123ca614e12565b6040519080825280602002602001820160405280156123f3578160200160208202803683370190505b5092508291506125a59050565b61240b868686613212565b60405163e7b43da560e01b815291935091506001600160a01b0385169063e7b43da5906124449088908a90879087908a906004016151dc565b600060405180830381600087803b15801561245e57600080fd5b505af1158015612472573d6000803e3d6000fd5b5050505060005b865181101561257e5782818151811061249457612494614e45565b602002602001015160001415806124c557508181815181106124b8576124b8614e45565b6020026020010151600014155b1561256c57856001600160a01b03168782815181106124e6576124e6614e45565b60200260200101516001600160a01b03167ff3fa0eaee8f258c23b013654df25d1527f98a5c7ccd5e951dd77caca400ef97284848151811061252a5761252a614e45565b602002602001015186858151811061254457612544614e45565b6020026020010151604051612563929190918252602082015260400190565b60405180910390a35b8061257681614e71565b915050612479565b5061258885610599565b6125a55760405163e081c8f360e01b815260040160405180910390fd5b60018055909590945092505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663cc1fdf166040518163ffffffff1660e01b8152600401602060405180830381865afa158015612614573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126389190614e28565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600090815260036020526040902054919250166126a6576126a6817f0000000000000000000000000000000000000000000000000000000000000000600061346a565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ee306a346040518163ffffffff1660e01b8152600401600060405180830381865afa158015612706573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261272e9190810190614ef1565b905060005b81518110156127f957600082828151811061275057612750614e45565b6020908102919091018101516001600160a01b0380821660009081526003909352604090922054909250166127905761278b8482600161346a565b6127e6565b6001600160a01b038116600081815260046020526040808220600201805460ff60401b1916600160401b17905551600192917f51efec4e8c6ee88d51e1ec000985e267b4296e493de77b4aa7a38315981390bf91a35b50806127f181614e71565b915050612733565b505050565b6000806002600054036128235760405162461bcd60e51b8152600401610a7690614e8a565b6002600090815587906001600160a01b0382166000908152600460205260409020600290810154600160401b900460ff169081111561286457612864614d50565b03612882576040516367fa94e760e01b815260040160405180910390fd5b61288b88612095565b5061289b888689898860006136d3565b909350915060008390036128c25760405163faf4da9160e01b815260040160405180910390fd5b6128cb87610599565b6128e85760405163e081c8f360e01b815260040160405180910390fd5b856001600160a01b0316876001600160a01b0316896001600160a01b03167f3b5f15635b488fe265654176726b3222080f3d6500a562f4664233b3ea2f028386886040516129429291909182521515602082015260400190565b60405180910390a450600160005590969095509350505050565b60006060833b61297f576040516373d39f9d60e01b815260040160405180910390fd5b600080856001600160a01b03168560405161299a919061523a565b600060405180830381855afa9150503d80600081146129d5576040519150601f19603f3d011682016040523d82523d6000602084013e6129da565b606091505b509150915081612a0157600060405180602001604052806000815250935093505050610592565b6001969095509350505050565b6040516001600160a01b0383166024820152604481018290526127f990849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613970565b60028301546040516370a0823160e01b81526001600160a01b03848116600483015260009283928392909116906370a0823190602401602060405180830381865afa158015612ac4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae89190614f96565b905060008660020160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b659190614f96565b60058801549091506000612b7a848385613a42565b9050808710612b8e57809550839450612b9f565b869550612b9c86838561301b565b94505b50505050935093915050565b6040516001600160a01b0380851660248301528316604482015260648101829052612be39085906323b872dd60e01b90608401612a3a565b50505050565b606081606001515182604001515114612c155760405163dba17af560e01b815260040160405180910390fd5b8160400151516001600160401b03811115612c3257612c32614e12565b604051908082528060200260200182016040528015612c5b578160200160208202803683370190505b50905060005b826040015151811015612cff576000612ca18460200151856000015186604001518581518110612c9357612c93614e45565b602002602001015142613a82565b9050612ccf84606001518381518110612cbc57612cbc614e45565b6020026020010151856080015183613b80565b838381518110612ce157612ce1614e45565b60209081029190910101525080612cf781614e71565b915050612c61565b50919050565b6000805b8251811015612cff57828181518110612d2457612d24614e45565b602002602001015182612d37919061505c565b915080612d4381614e71565b915050612d09565b60608151835114612d6f5760405163dba17af560e01b815260040160405180910390fd5b82516001600160401b03811115612d8857612d88614e12565b604051908082528060200260200182016040528015612db1578160200160208202803683370190505b50905060005b8351811015612f5e57828181518110612dd257612dd2614e45565b602002602001015160000315612f4c576000856001600160a01b03166341976e09868481518110612e0557612e05614e45565b60200260200101516040518263ffffffff1660e01b8152600401612e3891906001600160a01b0391909116815260200190565b602060405180830381865afa158015612e55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e799190614f96565b90506000858381518110612e8f57612e8f614e45565b60200260200101516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ed4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ef89190614ff0565b9050612f2b828260ff16878681518110612f1457612f14614e45565b6020026020010151613c8c9092919063ffffffff16565b848481518110612f3d57612f3d614e45565b60200260200101818152505050505b80612f5681614e71565b915050612db7565b509392505050565b60606000612f7383613cad565b905061111c84846040015183612d4b565b60008151845114612fa85760405163dba17af560e01b815260040160405180910390fd5b60005b845181101561301157612ff38787878481518110612fcb57612fcb614e45565b602002602001015187878681518110612fe657612fe6614e45565b6020026020010151613eff565b612ffd908361505c565b91508061300981614e71565b915050612fab565b5095945050505050565b6000811580613028575082155b15613034575082610cef565b6000836130418487615013565b61304b9190615048565b90508015801561305a57508415155b1561111c57604051639811e0c760e01b815260040160405180910390fd5b6000811580613085575082155b15613091575082610cef565b600061309d8386615013565b905060006130ab8583615048565b90506130b78583615256565b156130ca576130c760018261505c565b90505b95945050505050565b6000806130de6106b4565b915091506000806131516040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602001306001600160a01b03168152602001868152602001858152602001876001600160a01b031681525060006115e4565b915091508082111561317657604051630655b2eb60e31b815260040160405180910390fd5b5050505050565b60405163122cfaaf60e21b81523060048201526001600160a01b0382811660248301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906348b3eabc90604401602060405180830381865afa1580156131ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a19190614e28565b82516060908190806001600160401b0381111561323157613231614e12565b60405190808252806020026020018201604052801561325a578160200160208202803683370190505b509250806001600160401b0381111561327557613275614e12565b60405190808252806020026020018201604052801561329e578160200160208202803683370190505b50915060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663eafecffa6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613301573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133259190614f96565b905060005b8281101561345f5761335488828151811061334757613347614e45565b6020026020010151612095565b506000600360008a848151811061336d5761336d614e45565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002090506133a48189600019612a71565b508583815181106133b7576133b7614e45565b60200260200101818152505060006133ef8a84815181106133da576133da614e45565b60200260200101516000198b8b6001896136d3565b509050600061341e8b858151811061340957613409614e45565b60200260200101516000198c8c60008a6136d3565b50905061342b818361505c565b88858151811061343d5761343d614e45565b602002602001018181525050505050808061345790614e71565b91505061332a565b505050935093915050565b60006134768383614024565b6001600160a01b03808516600090815260036020908152604091829020845191850151925163f8f38d1b60e01b8152949550939288169263f8f38d1b926134c29291899060040161526a565b6020604051808303816000875af11580156134e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135059190614e28565b81546001600160a01b0319166001600160a01b039182161782556040838101516060850151915163f8f38d1b60e01b81529288169263f8f38d1b9261354f9291899060040161526a565b6020604051808303816000875af115801561356e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135929190614e28565b6001820180546001600160a01b0319166001600160a01b03928316179055608083015160a0840151604051633b0eb45d60e21b81529288169263ec3ad174926135e1929091899060040161526a565b6020604051808303816000875af1158015613600573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136249190614e28565b600282810180546001600160a01b039384166001600160a01b0319918216179091558154600181810184557f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9091018054948916949092168417909155600083815260046020526040808220909301805460ff60401b1916600160401b17905591519092917f51efec4e8c6ee88d51e1ec000985e267b4296e493de77b4aa7a38315981390bf91a35050505050565b60008060008060006136e58b88614298565b9250925092506000198a036137d5576040516370a0823160e01b81526001600160a01b038a811660048301528316906370a0823190602401602060405180830381865afa15801561373a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061375e9190614f96565b93506137ce83836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137c69190614f96565b869190614303565b9450613849565b61384383836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613817573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061383b9190614f96565b8c9190613078565b93508994505b846000036138605760008094509450505050613965565b8483101561388157604051631648a98f60e31b815260040160405180910390fd5b848303925060006138938c8789614361565b9050808210156138b657604051634323a55560e01b815260040160405180910390fd5b6001600160a01b038c16600090815260036020526040902088156138e057600481018590556138e8565b600381018590555b604051632770a7eb60e21b81526001600160a01b038c8116600483015260248201889052851690639dc29fac90604401600060405180830381600087803b15801561393257600080fd5b505af1158015613946573d6000803e3d6000fd5b5061395f925050506001600160a01b038e168b84612a0e565b50505050505b965096945050505050565b60006139c5826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166143c19092919063ffffffff16565b8051909150156127f957808060200190518101906139e39190615167565b6127f95760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a76565b6000811580613a4f575082155b15613a5c57506000610cef565b6000613a688486615013565b90506000613a768483615048565b90506130b78483615256565b60405163122cfaaf60e21b81526001600160a01b038581166004830152838116602483015260009182918616906348b3eabc90604401602060405180830381865afa158015613ad5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af99190614e28565b60405163b1e0176560e01b81526001600160a01b0388811660048301528681166024830152604482018690529192509082169063b1e0176590606401602060405180830381865afa158015613b52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b769190614f96565b9695505050505050565b60408084015190516370a0823160e01b81526001600160a01b03848116600483015260009283929116906370a0823190602401602060405180830381865afa158015613bd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bf49190614f96565b905080600003613c08576000915050610cef565b6000613c188660a00151856143d0565b9050613b768187604001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c849190614f96565b849190613a42565b6000613c9982600a6152a8565b613ca38486615013565b61111c9190615048565b606081606001515182604001515114613cd95760405163dba17af560e01b815260040160405180910390fd5b8160400151516001600160401b03811115613cf657613cf6614e12565b604051908082528060200260200182016040528015613d1f578160200160208202803683370190505b50905060005b826040015151811015612cff57600083606001518281518110613d4a57613d4a614e45565b60209081029190910101515160808501516040516370a0823160e01b81526001600160a01b0391821660048201529116906370a0823190602401602060405180830381865afa158015613da1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dc59190614f96565b9050600084606001518381518110613ddf57613ddf614e45565b602090810291909101810151015160808601516040516370a0823160e01b81526001600160a01b0391821660048201529116906370a0823190602401602060405180830381865afa158015613e38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e5c9190614f96565b9050613e68818361505c565b600003613e76575050613eed565b6000613e9b8660200151876000015188604001518781518110612c9357612c93614e45565b9050613ecb86606001518581518110613eb657613eb6614e45565b60200260200101518484848a600001516143f9565b858581518110613edd57613edd614e45565b6020026020010181815250505050505b80613ef781614e71565b915050613d25565b60008080846001811115613f1557613f15614d50565b03613f94576040516330be9d2560e21b81526001600160a01b038781166004830152868116602483015288169063c2fa7494906044015b602060405180830381865afa158015613f69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f8d9190614f96565b9050613ffc565b6001846001811115613fa857613fa8614d50565b03613fe357604051630ca4db1160e21b81526001600160a01b03878116600483015286811660248301528816906332936c4490604401613f4c565b6040516353c3731560e01b815260040160405180910390fd5b670de0b6b3a764000061400f8285615013565b6140199190615048565b979650505050505050565b61405d6040518060c001604052806060815260200160608152602001606081526020016060815260200160608152602001606081525090565b60006140688461455f565b90506040518060c001604052808260405160200161408691906152b4565b6040516020818303038152906040528152602001826040516020016140ab919061530b565b6040516020818303038152906040528152602001826040516020016140d09190615334565b6040516020818303038152906040528152602001826040516020016140f5919061538b565b60405160208183030381529060405281526020018260405160200161411a91906153b5565b60405160208183030381529060405281526020018260405160200161413f91906153f9565b604051602081830303815290604052815250915082156142915760006141847f000000000000000000000000000000000000000000000000000000000000000061455f565b835160405191925061419a918390602001615415565b60408051601f1981840301815291815290845260208481015191516141c29291849101615466565b60405160208183030381529060405283602001819052508260400151816040516020016141f0929190615415565b604051602081830303815290604052836040018190525082606001518160405160200161421e929190615466565b604051602081830303815290604052836060018190525082608001518160405160200161424c929190615415565b60405160208183030381529060405283608001819052508260a001518160405160200161427a929190615466565b60408051601f1981840301815291905260a0840152505b5092915050565b6001600160a01b03821660009081526003602052604081208190819084156142da57600481015460018201549094506001600160a01b031692508391506142fb565b600381015481549094506001600160a01b031692506142f886610cf6565b91505b509250925092565b6000811580614310575082155b1561431d57506000610cef565b60008261432a8587615013565b6143349190615048565b90508015801561434357508415155b1561111c57604051630cb65c7760e21b815260040160405180910390fd5b600081600003614372575081610cef565b6001600160a01b0384166000908152600460205260408120600101546143999085856145fd565b6001600160a01b03871660009081526004602052604090206001015584039150509392505050565b606061111c848460008561462c565b6000670de0b6b3a76400006143e58385615013565b6143ef9190615048565b610cef908461505c565b60008085156144e8576144e36144758860600151856001600160a01b03166325ed3d446040518163ffffffff1660e01b8152600401602060405180830381865afa15801561444b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061446f9190614f96565b87614749565b88600001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156144b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144db9190614f96565b889190614303565b6144eb565b60005b9050600085156145445761453f886080015189602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156144b7573d6000803e3d6000fd5b614547565b60005b9050614553818361505c565b98975050505050505050565b6040805160048152602481019091526020810180516001600160e01b03166395d89b4160e01b179052606090600090819061459b90859061295c565b915091508115806145ab57508051155b156145d05750506040805180820190915260018152603f60f81b602082015292915050565b60208151036145e25761111c8161479d565b8080602001905181019061111c91906154a2565b5050919050565b670de0b6b3a764000081830204600081198511156146215750508219600019610676565b509392840192915050565b60608247101561468d5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a76565b843b6146db5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a76565b600080866001600160a01b031685876040516146f7919061523a565b60006040518083038185875af1925050503d8060008114614734576040519150601f19603f3d011682016040523d82523d6000602084013e614739565b606091505b509150915061401982828661481c565b60008061475e84670de0b6b3a7640000614faf565b9050670de0b6b3a764000081816147758689615013565b61477f9190615048565b6147899190615013565b6147939190615048565b6130ca908661505c565b805160609060005b818110156145f6578381815181106147bf576147bf614e45565b01602001516001600160f81b0319161561481457828482815181106147e6576147e6614e45565b602001015160f81c60f81b604051602001614802929190615535565b60405160208183030381529060405292505b6001016147a5565b6060831561482b575081610cef565b82511561483b5782518084602001fd5b8160405162461bcd60e51b8152600401610a769190615564565b6040518060c0016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b600081518084526020808501945080840160005b838110156148d6578151875295820195908201906001016148ba565b509495945050505050565b602081526000610cef60208301846148a6565b6001600160a01b038116811461490957600080fd5b50565b6000806040838503121561491f57600080fd5b823561492a816148f4565b946020939093013593505050565b60006020828403121561494a57600080fd5b8135610cef816148f4565b801515811461490957600080fd5b60008060006060848603121561497857600080fd5b8335614983816148f4565b925060208401359150604084013561499a81614955565b809150509250925092565b600081518084526020808501945080840160005b838110156148d65781516001600160a01b0316875295820195908201906001016149b9565b80516001600160a01b03908116835260208083015182169084015260408083015190911690830152606080820151908301526080808201519083015260a090810151910152565b604081526000614a3860408301856149a5565b82810360208481019190915284518083528582019282019060005b81811015614a7957614a668386516149de565b9383019360c09290920191600101614a53565b5090979650505050505050565b602081526000610cef60208301846149a5565b60008060008060408587031215614aaf57600080fd5b84356001600160401b0380821115614ac657600080fd5b818701915087601f830112614ada57600080fd5b813581811115614ae957600080fd5b8860208260051b8501011115614afe57600080fd5b602092830196509450908601359080821115614b1957600080fd5b818701915087601f830112614b2d57600080fd5b813581811115614b3c57600080fd5b886020828501011115614b4e57600080fd5b95989497505060200194505050565b6000815180845260208085019450848260051b86018286016000805b86811015614bc8578484038a52825180518086529087019087860190845b81811015614bb357835183529289019291890191600101614b97565b50509a87019a94505091850191600101614b79565b509198975050505050505050565b606081526000614be960608301866149a5565b8281036020840152614bfb8186614b5d565b90508281036040840152613b768185614b5d565b600080600060608486031215614c2457600080fd5b8335614c2f816148f4565b92506020840135614c3f816148f4565b929592945050506040919091013590565b60008060408385031215614c6357600080fd5b8235614c6e816148f4565b91506020830135614c7e816148f4565b809150509250929050565b60c081016108a182846149de565b60008060008060808587031215614cad57600080fd5b8435614cb8816148f4565b93506020850135614cc8816148f4565b92506040850135614cd8816148f4565b9396929550929360600135925050565b600080600080600060a08688031215614d0057600080fd5b8535614d0b816148f4565b94506020860135614d1b816148f4565b93506040860135614d2b816148f4565b9250606086013591506080860135614d4281614955565b809150509295509295909350565b634e487b7160e01b600052602160045260246000fd5b600060808201905082518252602083015160208301526001600160401b036040840151166040830152606083015160038110614db257634e487b7160e01b600052602160045260246000fd5b8060608401525092915050565b60008060008060808587031215614dd557600080fd5b8435614de0816148f4565b93506020850135614df0816148f4565b9250604085013591506060850135614e0781614955565b939692955090935050565b634e487b7160e01b600052604160045260246000fd5b600060208284031215614e3a57600080fd5b8151610cef816148f4565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201614e8357614e83614e5b565b5060010190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b604051601f8201601f191681016001600160401b0381118282101715614ee957614ee9614e12565b604052919050565b60006020808385031215614f0457600080fd5b82516001600160401b0380821115614f1b57600080fd5b818501915085601f830112614f2f57600080fd5b815181811115614f4157614f41614e12565b8060051b9150614f52848301614ec1565b8181529183018401918481019088841115614f6c57600080fd5b938501935b838510156145535784519250614f86836148f4565b8282529385019390850190614f71565b600060208284031215614fa857600080fd5b5051919050565b600082821015614fc157614fc1614e5b565b500390565b600060208284031215614fd857600080fd5b81516001600160e01b031981168114610cef57600080fd5b60006020828403121561500257600080fd5b815160ff81168114610cef57600080fd5b600081600019048311821515161561502d5761502d614e5b565b500290565b634e487b7160e01b600052601260045260246000fd5b60008261505757615057615032565b500490565b6000821982111561506f5761506f614e5b565b500190565b600181815b808511156150af57816000190482111561509557615095614e5b565b808516156150a257918102915b93841c9390800290615079565b509250929050565b6000826150c6575060016108a1565b816150d3575060006108a1565b81600181146150e957600281146150f35761510f565b60019150506108a1565b60ff84111561510457615104614e5b565b50506001821b6108a1565b5060208310610133831016604e8410600b8410161715615132575081810a6108a1565b61513c8383615074565b806000190482111561515057615150614e5b565b029392505050565b6000610cef60ff8416836150b7565b60006020828403121561517957600080fd5b8151610cef81614955565b60005b8381101561519f578181015183820152602001615187565b83811115612be35750506000910152565b600081518084526151c8816020860160208601615184565b601f01601f19169290920160200192915050565b6001600160a01b038616815260a060208201819052600090615200908301876149a5565b828103604084015261521281876148a6565b9050828103606084015261522681866148a6565b9050828103608084015261455381856151b0565b6000825161524c818460208701615184565b9190910192915050565b60008261526557615265615032565b500690565b60608152600061527d60608301866151b0565b828103602084015261528f81866151b0565b91505060018060a01b0383166040830152949350505050565b6000610cef83836150b7565b7f53696c6f2046696e616e636520426f72726f7761626c652000000000000000008152600082516152ec816018850160208701615184565b670811195c1bdcda5d60c21b6018939091019283015250602001919050565b607360f81b815260008251615327816001850160208701615184565b9190910160010192915050565b7f53696c6f2046696e616e63652050726f7465637465642000000000000000000081526000825161536c816017850160208701615184565b670811195c1bdcda5d60c21b6017939091019283015250601f01919050565b61073760f41b8152600082516153a8816002850160208701615184565b9190910160020192915050565b6c029b4b637902334b730b731b29609d1b8152600082516153dd81600d850160208701615184565b64081119589d60da1b600d939091019283015250601201919050565b601960fa1b815260008251615327816001850160208701615184565b60008351615427818460208801615184565b6301034b7160e51b9083019081528351615448816004840160208801615184565b642053696c6f60d81b60049290910191820152600901949350505050565b60008351615478818460208801615184565b602d60f81b9083019081528351615496816001840160208801615184565b01600101949350505050565b6000602082840312156154b457600080fd5b81516001600160401b03808211156154cb57600080fd5b818401915084601f8301126154df57600080fd5b8151818111156154f1576154f1614e12565b615504601f8201601f1916602001614ec1565b915080825285602082850101111561551b57600080fd5b61552c816020840160208601615184565b50949350505050565b60008351615547818460208801615184565b6001600160f81b0319939093169190920190815260010192915050565b602081526000610cef60208301846151b056fea2646970667358221220d18e16744c3be14b0ddaee210dd92b8a5125ef85955f3b68fbc4df16ba02522664736f6c634300080d0033a2646970667358221220824f7101ff81fb08d1aa649bff86787dd4a495dc3c1cda52c5d218d470c35f7d64736f6c634300080d0033000000000000000000000000d998c35b7900b344bbbe6555cc11576942cf309d000000000000000000000000857c44b994a9beedcf5fa4b7e9faf48556058f59
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100415760003560e01c8063573bbca5146100465780635a0c4de414610076578063bde127181461008b575b600080fd5b610059610054366004610246565b61009e565b6040516001600160a01b0390911681526020015b60405180910390f35b604051631683137960e21b815260200161006d565b600054610059906001600160a01b031681565b600080546001600160a01b031633146100ca576040516397691a2f60e01b815260040160405180910390fd5b337f000000000000000000000000857c44b994a9beedcf5fa4b7e9faf48556058f5985856040516100fa90610222565b6001600160a01b0394851681529284166020840152921660408201526001600160801b039091166060820152608001604051809103906000f080158015610145573d6000803e3d6000fd5b506040516001600160801b03851681529091506001600160a01b0380861691908316907f3d603ed158e689891fb302f8dcdd070ca09f651c8b61183dda2db71384cca1579060200160405180910390a39392505050565b60006001600160a01b0383161580159061021b575082826040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102069190610334565b60e083901b6001600160e01b03199081169116145b9392505050565b615c5f806200035f83390190565b634e487b7160e01b600052604160045260246000fd5b60008060006060848603121561025b57600080fd5b83356001600160a01b038116811461027257600080fd5b925060208401356001600160801b038116811461028e57600080fd5b9150604084013567ffffffffffffffff808211156102ab57600080fd5b818601915086601f8301126102bf57600080fd5b8135818111156102d1576102d1610230565b604051601f8201601f19908116603f011681019083821181831017156102f9576102f9610230565b8160405282815289602084870101111561031257600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561034657600080fd5b81516001600160e01b03198116811461021b57600080fdfe6101206040523480156200001257600080fd5b5060405162005c5f38038062005c5f833981016040819052620000359162000396565b8382828282826001600081905550600180819055506200006e836001600160a01b031663e99ed41d620001a560201b620012781760201c565b6200008c57604051639f45596360e01b815260040160405180910390fd5b806001600160801b0316600003620000b757604051634eb171af60e01b815260040160405180910390fd5b6000620000cf836200023160201b620012fe1760201c565b6001600160801b03831660c0526001600160a01b03808616608052841660a0529050620000fe81600a6200051c565b60e052505050506001600160a01b038681166101008190526040516303f9c36b60e01b8152918716600483015293506303f9c36b92506024019050602060405180830381865afa15801562000157573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200017d91906200052a565b6200019b57604051631252497b60e11b815260040160405180910390fd5b50505050620005e4565b60006001600160a01b0383161580159062000228575082826040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001ed573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000213919062000555565b60e083901b6001600160e01b03199081169116145b90505b92915050565b6040805160048152602481019091526020810180516001600160e01b0390811663313ce56760e01b17909152600091829182916200027391869190620002a816565b915091508162000287575060009392505050565b808060200190518101906200029d919062000581565b60ff16949350505050565b60006060620002c2846200037760201b6200136b1760201c565b620002e0576040516373d39f9d60e01b815260040160405180910390fd5b600080856001600160a01b031685604051620002fd9190620005a6565b600060405180830381855afa9150503d80600081146200033a576040519150601f19603f3d011682016040523d82523d6000602084013e6200033f565b606091505b509150915081620003685760006040518060200160405280600081525093509350505062000370565b600193509150505b9250929050565b3b151590565b6001600160a01b03811681146200039357600080fd5b50565b60008060008060808587031215620003ad57600080fd5b8451620003ba816200037d565b6020860151909450620003cd816200037d565b6040860151909350620003e0816200037d565b60608601519092506001600160801b0381168114620003fe57600080fd5b939692955090935050565b634e487b7160e01b600052601160045260246000fd5b600181815b808511156200046057816000190482111562000444576200044462000409565b808516156200045257918102915b93841c939080029062000424565b509250929050565b60008262000479575060016200022b565b8162000488575060006200022b565b8160018114620004a15760028114620004ac57620004cc565b60019150506200022b565b60ff841115620004c057620004c062000409565b50506001821b6200022b565b5060208310610133831016604e8410600b8410161715620004f1575081810a6200022b565b620004fd83836200041f565b806000190482111562000514576200051462000409565b029392505050565b600062000228838362000468565b6000602082840312156200053d57600080fd5b815180151581146200054e57600080fd5b9392505050565b6000602082840312156200056857600080fd5b81516001600160e01b0319811681146200054e57600080fd5b6000602082840312156200059457600080fd5b815160ff811681146200054e57600080fd5b6000825160005b81811015620005c95760208186018101518583015201620005ad565b81811115620005d9576000828501525b509190910192915050565b60805160a05160c05160e051610100516155ad620006b2600039600061177a01526000505060006104080152600081816101f901528181610b7301528181611d3701528181612642015281816126800152614160015260008181610304015281816104a2015281816105e401528181610ac101528181610df701528181610f0801528181611acd01528181611ba601528181611e21015281816120bd01528181612241015281816125b8015281816126aa015281816130f5015281816131a501526132a501526155ad6000f3fe608060405234801561001057600080fd5b50600436106101735760003560e01c8063a388991b116100de578063ead5d35911610097578063f87c3aa811610071578063f87c3aa814610392578063fb255703146103d0578063fbf178db146103f0578063ffa1ad741461040357600080fd5b8063ead5d35914610359578063f364181c1461036c578063f3d470c21461037f57600080fd5b8063a388991b146102d1578063a6e08aa1146102d9578063b8c876b1146102ec578063bde12718146102ff578063bf27304114610326578063dbc5b4811461034657600080fd5b806364654cf51161013057806364654cf51461024657806367e4ac2c1461025c5780639198e5151461027157806393a94ca314610292578063976ce495146102b4578063a1dfa423146102c757600080fd5b8063151541f01461017857806322867d781461019657806338b51ce1146101be5780633edd1128146101e15780634521c019146101f45780634b8a352914610233575b600080fd5b61018061044b565b60405161018d91906148e1565b60405180910390f35b6101a96101a436600461490c565b61057e565b6040805192835260208301919091520161018d565b6101d16101cc366004614938565b610599565b604051901515815260200161018d565b6101a96101ef366004614963565b61064c565b61021b7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161018d565b6101a961024136600461490c565b61067e565b61024e6106b4565b60405161018d929190614a25565b610264610834565b60405161018d9190614a86565b61028461027f366004614938565b610896565b60405190815260200161018d565b6102a56102a0366004614a99565b6108a7565b60405161018d93929190614bd6565b6101a96102c2366004614c0f565b610a45565b6102cf610a54565b005b6102cf610a93565b6101d16102e7366004614c50565b610c2b565b6102846102fa366004614938565b610cf6565b61021b7f000000000000000000000000000000000000000000000000000000000000000081565b610339610334366004614938565b610d81565b60405161018d9190614c89565b6101a9610354366004614c97565b610df2565b6101a9610367366004614963565b610ee8565b6101a961037a366004614ce8565b610f03565b6101d161038d366004614c50565b610fde565b6103a56103a0366004614938565b611124565b604080518251815260208084015190820152918101516001600160401b03169082015260600161018d565b6103e36103de366004614938565b6111a4565b60405161018d9190614d66565b6101a96103fe366004614dbf565b611254565b61042a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516fffffffffffffffffffffffffffffffff909116815260200161018d565b60606000610457610834565b905080516001600160401b0381111561047257610472614e12565b60405190808252806020026020018201604052801561049b578160200160208202803683370190505b50915060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105229190614e28565b905060005b82518110156105785761055383828151811061054557610545614e45565b602002602001015183611371565b84828151811061056557610565614e45565b6020908102919091010152600101610527565b50505090565b60008061058d84333386611418565b915091505b9250929050565b60006001600160a01b0382166105c257604051630b328ad960e31b815260040160405180910390fd5b6000806105cd6106b4565b915091506000806106406040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602001306001600160a01b03168152602001868152602001858152602001886001600160a01b031681525060016115e4565b10159695505050505050565b60008061065a336000611753565b61066485846117da565b61067185333387876117fb565b915091505b935093915050565b60008061068a84611d35565b156106a85760405163f3b4e6a160e01b815260040160405180910390fd5b61058d84333386611d67565b606080600280548060200260200160405190810160405280929190818152602001828054801561070d57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116106ef575b5050505050915081516001600160401b0381111561072d5761072d614e12565b60405190808252806020026020018201604052801561076657816020015b610753614855565b81526020019060019003908161074b5790505b50905060005b825181101561082f576003600084838151811061078b5761078b614e45565b6020908102919091018101516001600160a01b039081168352828201939093526040918201600020825160c0810184528154851681526001820154851692810192909252600281015490931691810191909152600382015460608201526004820154608082015260059091015460a0820152825183908390811061081157610811614e45565b6020026020010181905250808061082790614e71565b91505061076c565b509091565b6060600280548060200260200160405190810160405280929190818152602001828054801561088c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161086e575b5050505050905090565b60006108a182612095565b92915050565b60608060606108b4610834565b925085806001600160401b038111156108cf576108cf614e12565b60405190808252806020026020018201604052801561090257816020015b60608152602001906001900390816108ed5790505b509250806001600160401b0381111561091d5761091d614e12565b60405190808252806020026020018201604052801561095057816020015b606081526020019060019003908161093b5790505b50915060005b81811015610a395761098f89898381811061097357610973614e45565b90506020020160208101906109889190614938565b6000611753565b6109f7858a8a848181106109a5576109a5614e45565b90506020020160208101906109ba9190614938565b338a8a8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061237692505050565b858381518110610a0957610a09614e45565b60200260200101858481518110610a2257610a22614e45565b602090810291909101019190915252600101610956565b50509450945094915050565b60008061067185853386611418565b600260005403610a7f5760405162461bcd60e51b8152600401610a7690614e8a565b60405180910390fd5b6002600055610a8c6125b4565b6001600055565b600260005403610ab55760405162461bcd60e51b8152600401610a7690614e8a565b600260008190555060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663296041ea6040518163ffffffff1660e01b8152600401600060405180830381865afa158015610b1d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b459190810190614ef1565b905060005b8151811015610c1a576000828281518110610b6757610b67614e45565b602002602001015190507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614610c07576001600160a01b0381166000818152600460205260408082206002908101805460ff60401b19166802000000000000000017905590519092917f51efec4e8c6ee88d51e1ec000985e267b4296e493de77b4aa7a38315981390bf91a35b5080610c1281614e71565b915050610b4a565b50610c236125b4565b506001600055565b6001600160a01b038281166000908152600360205260408082206002015490516370a0823160e01b81528484166004820152919216906370a0823190602401602060405180830381865afa158015610c87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cab9190614f96565b158015610cef575060016001600160a01b0384166000908152600460205260409020600290810154600160401b900460ff1690811115610ced57610ced614d50565b145b9392505050565b6001600160a01b03811660008181526003602052604080822060049081015491516370a0823160e01b81523091810191909152919290916370a0823190602401602060405180830381865afa158015610d53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d779190614f96565b6108a19190614faf565b610d89614855565b506001600160a01b03908116600090815260036020818152604092839020835160c081018552815486168152600182015486169281019290925260028101549094169281019290925282015460608201526004820154608082015260059091015460a082015290565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e779190614e28565b6001600160a01b0316336001600160a01b031614610ea857604051639e41bdd760e01b815260040160405180910390fd5b610eb186611d35565b15610ecf5760405163f3b4e6a160e01b815260040160405180910390fd5b610edb86868686611d67565b9150915094509492505050565b600080610ef6336000611753565b61067185333387876127fe565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f64573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f889190614e28565b6001600160a01b0316336001600160a01b031614610fb957604051639e41bdd760e01b815260040160405180910390fd5b610fc38686611753565b610fd087878787876127fe565b915091509550959350505050565b6001600160a01b03828116600090815260036020526040808220805491516370a0823160e01b81528585166004820152929390929116906370a0823190602401602060405180830381865afa15801561103b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105f9190614f96565b1580156110d9575060018101546040516370a0823160e01b81526001600160a01b038581166004830152909116906370a0823190602401602060405180830381865afa1580156110b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d79190614f96565b155b801561111c575060016001600160a01b0385166000908152600460205260409020600290810154600160401b900460ff169081111561111a5761111a614d50565b145b949350505050565b6111516040518060600160405280600081526020016000815260200160006001600160401b031681525090565b506001600160a01b03166000818152600360208181526040808420815160608101835293810154845260050154838301529383526004905290829020600201546001600160401b03169181019190915290565b6111cc6040805160808101825260008082526020820181905291810182905290606082015290565b6001600160a01b0382166000908152600460209081526040918290208251608081018452815481526001820154928101929092526002808201546001600160401b03811694840194909452919290916060840191600160401b900460ff169081111561123a5761123a614d50565b600281111561124b5761124b614d50565b90525092915050565b6000806112613386611753565b61126b86846117da565b610edb86338787876117fb565b60006001600160a01b03831615801590610cef575082826040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e29190614fc6565b60e083901b6001600160e01b0319908116911614905092915050565b6040805160048152602481019091526020810180516001600160e01b031663313ce56760e01b1790526000908190819061133990859061295c565b915091508161134c575060009392505050565b808060200190518101906113609190614ff0565b60ff16949350505050565b3b151590565b60006002600054036113955760405162461bcd60e51b8152600401610a7690614e8a565b600260009081556001600160a01b0384168152600460205260409020805460018201546113c29190614faf565b915060006113cf85610cf6565b9050808311156113dd578092505b826000036113f05760009250505061140d565b81548301825561140a6001600160a01b0386168585612a0e565b50505b600160005592915050565b60008085816001600160a01b0382166000908152600460205260409020600290810154600160401b900460ff169081111561145557611455614d50565b03611473576040516367fa94e760e01b815260040160405180910390fd5b6002600054036114955760405162461bcd60e51b8152600401610a7690614e8a565b60026000556114a387612095565b506001600160a01b03871660009081526003602052604090206114c7818887612a71565b909450925060008390036114ee5760405163faf4da9160e01b815260040160405180910390fd5b866001600160a01b0316886001600160a01b03167f05f2eeda0e08e4b437f487c8d7d29b14537d15e3488170dc3de5dbdf8dac46848660405161153391815260200190565b60405180910390a36115506001600160a01b038916873087612bab565b838160050160008282546115649190614faf565b90915550506002810154604051632770a7eb60e21b81526001600160a01b0389811660048301526024820186905290911690639dc29fac90604401600060405180830381600087803b1580156115b957600080fd5b505af11580156115cd573d6000803e3d6000fd5b505050505060016000819055505094509492505050565b60008060006115f285612be9565b90506115fd81612d05565b600003611611576000809250925050610592565b600085600001516001600160a01b0316635ddf2be36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611655573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116799190614e28565b9050600061168c82886040015185612d4b565b9050600061169982612d05565b9050806000036116b3576000809550955050505050610592565b60006116bf848a612f66565b905060006116cc82612d05565b9050806000036116ea57600019600097509750505050505050610592565b60006117058b600001518c602001518d604001518d87612f84565b90508161171a670de0b6b3a764000086615013565b6117249190615048565b985081611739670de0b6b3a764000083615013565b6117439190615048565b9750505050505050509250929050565b6040516330bd295b60e01b81526001600160a01b03838116600483015282811660248301527f000000000000000000000000000000000000000000000000000000000000000016906330bd295b90604401600060405180830381600087803b1580156117be57600080fd5b505af11580156117d2573d6000803e3d6000fd5b505050505050565b6117e382611d35565b80156117ed575080155b156117f757600080fd5b5050565b6000806002600054036118205760405162461bcd60e51b8152600401610a7690614e8a565b60026000558661182f81612095565b5061183a8887610c2b565b6118575760405163206e46bd60e21b815260040160405180910390fd5b6001600160a01b03881660009081526003602052604081208694509085611882578160030154611888565b81600401545b9050851561199857611915818360010160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061190d9190614f96565b89919061301b565b9350611921878261505c565b60048381019190915560018301546040516340c10f1960e01b81526001600160a01b038b811693820193909352602481018790529116906340c10f1990604401600060405180830381600087803b15801561197b57600080fd5b505af115801561198f573d6000803e3d6000fd5b50505050611a68565b6119f1818360000160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118e9573d6000803e3d6000fd5b93506119fd878261505c565b600383015581546040516340c10f1960e01b81526001600160a01b038a8116600483015260248201879052909116906340c10f1990604401600060405180830381600087803b158015611a4f57600080fd5b505af1158015611a63573d6000803e3d6000fd5b505050505b611a7d6001600160a01b038b168a308a612bab565b6040805188815287151560208201526001600160a01b03808b1692908d16917fdd160bb401ec5b5e5ca443d41e8e7182f3fe72d70a04b9c0ba844483d212bcb5910160405180910390a3505060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316635ddf2be36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b4d9190614e28565b6001600160a01b0383166000908152600360208190526040822060048101549181015493945092611b7e919061505c565b604051630968252160e11b81523060048201526001600160a01b0386811660248301529192507f0000000000000000000000000000000000000000000000000000000000000000909116906312d04a4290604401602060405180830381865afa158015611bef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c139190614f96565b846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c759190614ff0565b611c8090600a615158565b6040516341976e0960e01b81526001600160a01b0387811660048301528491908716906341976e0990602401602060405180830381865afa158015611cc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ced9190614f96565b611cf79190615013565b611d019190615048565b1115611d205760405163221e43cf60e11b815260040160405180910390fd5b50506001600055509197909650945050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b600080600260005403611d8c5760405162461bcd60e51b8152600401610a7690614e8a565b6002600055611d9a86612095565b50611da58686610fde565b611dc2576040516301c08c7960e11b815260040160405180910390fd5b82611dcc87610cf6565b1015611deb57604051634323a55560e01b815260040160405180910390fd5b600060036000886001600160a01b03166001600160a01b03168152602001908152602001600020905060008160050154905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663072ea61c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea19190614f96565b905060008115611ecd57670de0b6b3a7640000611ebe8389615013565b611ec89190615048565b611ed0565b60005b9050611f60838560020160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f4f9190614f96565b611f59848b61505c565b9190613078565b879650945080611f70878561505c565b611f7a919061505c565b60058501556001600160a01b038a1660009081526004602052604081206001018054839290611faa90849061505c565b909155505060028401546040516340c10f1960e01b81526001600160a01b038b8116600483015260248201889052909116906340c10f1990604401600060405180830381600087803b158015611fff57600080fd5b505af1158015612013573d6000803e3d6000fd5b50505050886001600160a01b03168a6001600160a01b03167f312a5e5e1079f5dda4e95dbbd0b908b291fd5b992ef22073643ab691572c5b528960405161205c91815260200190565b60405180910390a36120786001600160a01b038b168989612a0e565b612081896130d3565b505060016000555091969095509350505050565b6040516344cf3e9360e01b81523060048201526001600160a01b0382811660248301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906344cf3e9390604401602060405180830381865afa158015612106573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061212a9190615167565b15612148576040516313d0ff5960e31b815260040160405180910390fd5b6001600160a01b0382166000908152600360209081526040808320600490925282206002810154919290916001600160401b0316908190036121ab5750600201805467ffffffffffffffff1916426001600160401b031617905550600092915050565b4281036121bd57506000949350505050565b60006121c88661317d565b60405162f704bf60e21b81526001600160a01b03888116600483015242602483015291909116906303dc12fc906044016020604051808303816000875af1158015612217573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061223b9190614f96565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166325ed3d446040518163ffffffff1660e01b8152600401602060405180830381865afa15801561229d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c19190614f96565b600586015460018601549192509060008080670de0b6b3a76400006122e68887615013565b6122f09190615048565b9a50670de0b6b3a7640000868c02049150818401925083831015612318576000199250831991505b50808a036123268b8661505c565b60058b015560038a015461233b90829061505c565b60039a909a01999099555060018701555050506002909201805467ffffffffffffffff1916426001600160401b031617905550919392505050565b60608060026001540361239c57604051634fe29f1760e01b815260040160405180910390fd5b60026001556123aa85610599565b1561240057600086516001600160401b038111156123ca576123ca614e12565b6040519080825280602002602001820160405280156123f3578160200160208202803683370190505b5092508291506125a59050565b61240b868686613212565b60405163e7b43da560e01b815291935091506001600160a01b0385169063e7b43da5906124449088908a90879087908a906004016151dc565b600060405180830381600087803b15801561245e57600080fd5b505af1158015612472573d6000803e3d6000fd5b5050505060005b865181101561257e5782818151811061249457612494614e45565b602002602001015160001415806124c557508181815181106124b8576124b8614e45565b6020026020010151600014155b1561256c57856001600160a01b03168782815181106124e6576124e6614e45565b60200260200101516001600160a01b03167ff3fa0eaee8f258c23b013654df25d1527f98a5c7ccd5e951dd77caca400ef97284848151811061252a5761252a614e45565b602002602001015186858151811061254457612544614e45565b6020026020010151604051612563929190918252602082015260400190565b60405180910390a35b8061257681614e71565b915050612479565b5061258885610599565b6125a55760405163e081c8f360e01b815260040160405180910390fd5b60018055909590945092505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663cc1fdf166040518163ffffffff1660e01b8152600401602060405180830381865afa158015612614573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126389190614e28565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600090815260036020526040902054919250166126a6576126a6817f0000000000000000000000000000000000000000000000000000000000000000600061346a565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663ee306a346040518163ffffffff1660e01b8152600401600060405180830381865afa158015612706573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261272e9190810190614ef1565b905060005b81518110156127f957600082828151811061275057612750614e45565b6020908102919091018101516001600160a01b0380821660009081526003909352604090922054909250166127905761278b8482600161346a565b6127e6565b6001600160a01b038116600081815260046020526040808220600201805460ff60401b1916600160401b17905551600192917f51efec4e8c6ee88d51e1ec000985e267b4296e493de77b4aa7a38315981390bf91a35b50806127f181614e71565b915050612733565b505050565b6000806002600054036128235760405162461bcd60e51b8152600401610a7690614e8a565b6002600090815587906001600160a01b0382166000908152600460205260409020600290810154600160401b900460ff169081111561286457612864614d50565b03612882576040516367fa94e760e01b815260040160405180910390fd5b61288b88612095565b5061289b888689898860006136d3565b909350915060008390036128c25760405163faf4da9160e01b815260040160405180910390fd5b6128cb87610599565b6128e85760405163e081c8f360e01b815260040160405180910390fd5b856001600160a01b0316876001600160a01b0316896001600160a01b03167f3b5f15635b488fe265654176726b3222080f3d6500a562f4664233b3ea2f028386886040516129429291909182521515602082015260400190565b60405180910390a450600160005590969095509350505050565b60006060833b61297f576040516373d39f9d60e01b815260040160405180910390fd5b600080856001600160a01b03168560405161299a919061523a565b600060405180830381855afa9150503d80600081146129d5576040519150601f19603f3d011682016040523d82523d6000602084013e6129da565b606091505b509150915081612a0157600060405180602001604052806000815250935093505050610592565b6001969095509350505050565b6040516001600160a01b0383166024820152604481018290526127f990849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613970565b60028301546040516370a0823160e01b81526001600160a01b03848116600483015260009283928392909116906370a0823190602401602060405180830381865afa158015612ac4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae89190614f96565b905060008660020160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612b41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b659190614f96565b60058801549091506000612b7a848385613a42565b9050808710612b8e57809550839450612b9f565b869550612b9c86838561301b565b94505b50505050935093915050565b6040516001600160a01b0380851660248301528316604482015260648101829052612be39085906323b872dd60e01b90608401612a3a565b50505050565b606081606001515182604001515114612c155760405163dba17af560e01b815260040160405180910390fd5b8160400151516001600160401b03811115612c3257612c32614e12565b604051908082528060200260200182016040528015612c5b578160200160208202803683370190505b50905060005b826040015151811015612cff576000612ca18460200151856000015186604001518581518110612c9357612c93614e45565b602002602001015142613a82565b9050612ccf84606001518381518110612cbc57612cbc614e45565b6020026020010151856080015183613b80565b838381518110612ce157612ce1614e45565b60209081029190910101525080612cf781614e71565b915050612c61565b50919050565b6000805b8251811015612cff57828181518110612d2457612d24614e45565b602002602001015182612d37919061505c565b915080612d4381614e71565b915050612d09565b60608151835114612d6f5760405163dba17af560e01b815260040160405180910390fd5b82516001600160401b03811115612d8857612d88614e12565b604051908082528060200260200182016040528015612db1578160200160208202803683370190505b50905060005b8351811015612f5e57828181518110612dd257612dd2614e45565b602002602001015160000315612f4c576000856001600160a01b03166341976e09868481518110612e0557612e05614e45565b60200260200101516040518263ffffffff1660e01b8152600401612e3891906001600160a01b0391909116815260200190565b602060405180830381865afa158015612e55573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e799190614f96565b90506000858381518110612e8f57612e8f614e45565b60200260200101516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ed4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ef89190614ff0565b9050612f2b828260ff16878681518110612f1457612f14614e45565b6020026020010151613c8c9092919063ffffffff16565b848481518110612f3d57612f3d614e45565b60200260200101818152505050505b80612f5681614e71565b915050612db7565b509392505050565b60606000612f7383613cad565b905061111c84846040015183612d4b565b60008151845114612fa85760405163dba17af560e01b815260040160405180910390fd5b60005b845181101561301157612ff38787878481518110612fcb57612fcb614e45565b602002602001015187878681518110612fe657612fe6614e45565b6020026020010151613eff565b612ffd908361505c565b91508061300981614e71565b915050612fab565b5095945050505050565b6000811580613028575082155b15613034575082610cef565b6000836130418487615013565b61304b9190615048565b90508015801561305a57508415155b1561111c57604051639811e0c760e01b815260040160405180910390fd5b6000811580613085575082155b15613091575082610cef565b600061309d8386615013565b905060006130ab8583615048565b90506130b78583615256565b156130ca576130c760018261505c565b90505b95945050505050565b6000806130de6106b4565b915091506000806131516040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602001306001600160a01b03168152602001868152602001858152602001876001600160a01b031681525060006115e4565b915091508082111561317657604051630655b2eb60e31b815260040160405180910390fd5b5050505050565b60405163122cfaaf60e21b81523060048201526001600160a01b0382811660248301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906348b3eabc90604401602060405180830381865afa1580156131ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a19190614e28565b82516060908190806001600160401b0381111561323157613231614e12565b60405190808252806020026020018201604052801561325a578160200160208202803683370190505b509250806001600160401b0381111561327557613275614e12565b60405190808252806020026020018201604052801561329e578160200160208202803683370190505b50915060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663eafecffa6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613301573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133259190614f96565b905060005b8281101561345f5761335488828151811061334757613347614e45565b6020026020010151612095565b506000600360008a848151811061336d5761336d614e45565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002090506133a48189600019612a71565b508583815181106133b7576133b7614e45565b60200260200101818152505060006133ef8a84815181106133da576133da614e45565b60200260200101516000198b8b6001896136d3565b509050600061341e8b858151811061340957613409614e45565b60200260200101516000198c8c60008a6136d3565b50905061342b818361505c565b88858151811061343d5761343d614e45565b602002602001018181525050505050808061345790614e71565b91505061332a565b505050935093915050565b60006134768383614024565b6001600160a01b03808516600090815260036020908152604091829020845191850151925163f8f38d1b60e01b8152949550939288169263f8f38d1b926134c29291899060040161526a565b6020604051808303816000875af11580156134e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135059190614e28565b81546001600160a01b0319166001600160a01b039182161782556040838101516060850151915163f8f38d1b60e01b81529288169263f8f38d1b9261354f9291899060040161526a565b6020604051808303816000875af115801561356e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135929190614e28565b6001820180546001600160a01b0319166001600160a01b03928316179055608083015160a0840151604051633b0eb45d60e21b81529288169263ec3ad174926135e1929091899060040161526a565b6020604051808303816000875af1158015613600573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136249190614e28565b600282810180546001600160a01b039384166001600160a01b0319918216179091558154600181810184557f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9091018054948916949092168417909155600083815260046020526040808220909301805460ff60401b1916600160401b17905591519092917f51efec4e8c6ee88d51e1ec000985e267b4296e493de77b4aa7a38315981390bf91a35050505050565b60008060008060006136e58b88614298565b9250925092506000198a036137d5576040516370a0823160e01b81526001600160a01b038a811660048301528316906370a0823190602401602060405180830381865afa15801561373a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061375e9190614f96565b93506137ce83836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137c69190614f96565b869190614303565b9450613849565b61384383836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613817573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061383b9190614f96565b8c9190613078565b93508994505b846000036138605760008094509450505050613965565b8483101561388157604051631648a98f60e31b815260040160405180910390fd5b848303925060006138938c8789614361565b9050808210156138b657604051634323a55560e01b815260040160405180910390fd5b6001600160a01b038c16600090815260036020526040902088156138e057600481018590556138e8565b600381018590555b604051632770a7eb60e21b81526001600160a01b038c8116600483015260248201889052851690639dc29fac90604401600060405180830381600087803b15801561393257600080fd5b505af1158015613946573d6000803e3d6000fd5b5061395f925050506001600160a01b038e168b84612a0e565b50505050505b965096945050505050565b60006139c5826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166143c19092919063ffffffff16565b8051909150156127f957808060200190518101906139e39190615167565b6127f95760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610a76565b6000811580613a4f575082155b15613a5c57506000610cef565b6000613a688486615013565b90506000613a768483615048565b90506130b78483615256565b60405163122cfaaf60e21b81526001600160a01b038581166004830152838116602483015260009182918616906348b3eabc90604401602060405180830381865afa158015613ad5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613af99190614e28565b60405163b1e0176560e01b81526001600160a01b0388811660048301528681166024830152604482018690529192509082169063b1e0176590606401602060405180830381865afa158015613b52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b769190614f96565b9695505050505050565b60408084015190516370a0823160e01b81526001600160a01b03848116600483015260009283929116906370a0823190602401602060405180830381865afa158015613bd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bf49190614f96565b905080600003613c08576000915050610cef565b6000613c188660a00151856143d0565b9050613b768187604001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c849190614f96565b849190613a42565b6000613c9982600a6152a8565b613ca38486615013565b61111c9190615048565b606081606001515182604001515114613cd95760405163dba17af560e01b815260040160405180910390fd5b8160400151516001600160401b03811115613cf657613cf6614e12565b604051908082528060200260200182016040528015613d1f578160200160208202803683370190505b50905060005b826040015151811015612cff57600083606001518281518110613d4a57613d4a614e45565b60209081029190910101515160808501516040516370a0823160e01b81526001600160a01b0391821660048201529116906370a0823190602401602060405180830381865afa158015613da1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dc59190614f96565b9050600084606001518381518110613ddf57613ddf614e45565b602090810291909101810151015160808601516040516370a0823160e01b81526001600160a01b0391821660048201529116906370a0823190602401602060405180830381865afa158015613e38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e5c9190614f96565b9050613e68818361505c565b600003613e76575050613eed565b6000613e9b8660200151876000015188604001518781518110612c9357612c93614e45565b9050613ecb86606001518581518110613eb657613eb6614e45565b60200260200101518484848a600001516143f9565b858581518110613edd57613edd614e45565b6020026020010181815250505050505b80613ef781614e71565b915050613d25565b60008080846001811115613f1557613f15614d50565b03613f94576040516330be9d2560e21b81526001600160a01b038781166004830152868116602483015288169063c2fa7494906044015b602060405180830381865afa158015613f69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f8d9190614f96565b9050613ffc565b6001846001811115613fa857613fa8614d50565b03613fe357604051630ca4db1160e21b81526001600160a01b03878116600483015286811660248301528816906332936c4490604401613f4c565b6040516353c3731560e01b815260040160405180910390fd5b670de0b6b3a764000061400f8285615013565b6140199190615048565b979650505050505050565b61405d6040518060c001604052806060815260200160608152602001606081526020016060815260200160608152602001606081525090565b60006140688461455f565b90506040518060c001604052808260405160200161408691906152b4565b6040516020818303038152906040528152602001826040516020016140ab919061530b565b6040516020818303038152906040528152602001826040516020016140d09190615334565b6040516020818303038152906040528152602001826040516020016140f5919061538b565b60405160208183030381529060405281526020018260405160200161411a91906153b5565b60405160208183030381529060405281526020018260405160200161413f91906153f9565b604051602081830303815290604052815250915082156142915760006141847f000000000000000000000000000000000000000000000000000000000000000061455f565b835160405191925061419a918390602001615415565b60408051601f1981840301815291815290845260208481015191516141c29291849101615466565b60405160208183030381529060405283602001819052508260400151816040516020016141f0929190615415565b604051602081830303815290604052836040018190525082606001518160405160200161421e929190615466565b604051602081830303815290604052836060018190525082608001518160405160200161424c929190615415565b60405160208183030381529060405283608001819052508260a001518160405160200161427a929190615466565b60408051601f1981840301815291905260a0840152505b5092915050565b6001600160a01b03821660009081526003602052604081208190819084156142da57600481015460018201549094506001600160a01b031692508391506142fb565b600381015481549094506001600160a01b031692506142f886610cf6565b91505b509250925092565b6000811580614310575082155b1561431d57506000610cef565b60008261432a8587615013565b6143349190615048565b90508015801561434357508415155b1561111c57604051630cb65c7760e21b815260040160405180910390fd5b600081600003614372575081610cef565b6001600160a01b0384166000908152600460205260408120600101546143999085856145fd565b6001600160a01b03871660009081526004602052604090206001015584039150509392505050565b606061111c848460008561462c565b6000670de0b6b3a76400006143e58385615013565b6143ef9190615048565b610cef908461505c565b60008085156144e8576144e36144758860600151856001600160a01b03166325ed3d446040518163ffffffff1660e01b8152600401602060405180830381865afa15801561444b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061446f9190614f96565b87614749565b88600001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156144b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144db9190614f96565b889190614303565b6144eb565b60005b9050600085156145445761453f886080015189602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156144b7573d6000803e3d6000fd5b614547565b60005b9050614553818361505c565b98975050505050505050565b6040805160048152602481019091526020810180516001600160e01b03166395d89b4160e01b179052606090600090819061459b90859061295c565b915091508115806145ab57508051155b156145d05750506040805180820190915260018152603f60f81b602082015292915050565b60208151036145e25761111c8161479d565b8080602001905181019061111c91906154a2565b5050919050565b670de0b6b3a764000081830204600081198511156146215750508219600019610676565b509392840192915050565b60608247101561468d5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610a76565b843b6146db5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610a76565b600080866001600160a01b031685876040516146f7919061523a565b60006040518083038185875af1925050503d8060008114614734576040519150601f19603f3d011682016040523d82523d6000602084013e614739565b606091505b509150915061401982828661481c565b60008061475e84670de0b6b3a7640000614faf565b9050670de0b6b3a764000081816147758689615013565b61477f9190615048565b6147899190615013565b6147939190615048565b6130ca908661505c565b805160609060005b818110156145f6578381815181106147bf576147bf614e45565b01602001516001600160f81b0319161561481457828482815181106147e6576147e6614e45565b602001015160f81c60f81b604051602001614802929190615535565b60405160208183030381529060405292505b6001016147a5565b6060831561482b575081610cef565b82511561483b5782518084602001fd5b8160405162461bcd60e51b8152600401610a769190615564565b6040518060c0016040528060006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b600081518084526020808501945080840160005b838110156148d6578151875295820195908201906001016148ba565b509495945050505050565b602081526000610cef60208301846148a6565b6001600160a01b038116811461490957600080fd5b50565b6000806040838503121561491f57600080fd5b823561492a816148f4565b946020939093013593505050565b60006020828403121561494a57600080fd5b8135610cef816148f4565b801515811461490957600080fd5b60008060006060848603121561497857600080fd5b8335614983816148f4565b925060208401359150604084013561499a81614955565b809150509250925092565b600081518084526020808501945080840160005b838110156148d65781516001600160a01b0316875295820195908201906001016149b9565b80516001600160a01b03908116835260208083015182169084015260408083015190911690830152606080820151908301526080808201519083015260a090810151910152565b604081526000614a3860408301856149a5565b82810360208481019190915284518083528582019282019060005b81811015614a7957614a668386516149de565b9383019360c09290920191600101614a53565b5090979650505050505050565b602081526000610cef60208301846149a5565b60008060008060408587031215614aaf57600080fd5b84356001600160401b0380821115614ac657600080fd5b818701915087601f830112614ada57600080fd5b813581811115614ae957600080fd5b8860208260051b8501011115614afe57600080fd5b602092830196509450908601359080821115614b1957600080fd5b818701915087601f830112614b2d57600080fd5b813581811115614b3c57600080fd5b886020828501011115614b4e57600080fd5b95989497505060200194505050565b6000815180845260208085019450848260051b86018286016000805b86811015614bc8578484038a52825180518086529087019087860190845b81811015614bb357835183529289019291890191600101614b97565b50509a87019a94505091850191600101614b79565b509198975050505050505050565b606081526000614be960608301866149a5565b8281036020840152614bfb8186614b5d565b90508281036040840152613b768185614b5d565b600080600060608486031215614c2457600080fd5b8335614c2f816148f4565b92506020840135614c3f816148f4565b929592945050506040919091013590565b60008060408385031215614c6357600080fd5b8235614c6e816148f4565b91506020830135614c7e816148f4565b809150509250929050565b60c081016108a182846149de565b60008060008060808587031215614cad57600080fd5b8435614cb8816148f4565b93506020850135614cc8816148f4565b92506040850135614cd8816148f4565b9396929550929360600135925050565b600080600080600060a08688031215614d0057600080fd5b8535614d0b816148f4565b94506020860135614d1b816148f4565b93506040860135614d2b816148f4565b9250606086013591506080860135614d4281614955565b809150509295509295909350565b634e487b7160e01b600052602160045260246000fd5b600060808201905082518252602083015160208301526001600160401b036040840151166040830152606083015160038110614db257634e487b7160e01b600052602160045260246000fd5b8060608401525092915050565b60008060008060808587031215614dd557600080fd5b8435614de0816148f4565b93506020850135614df0816148f4565b9250604085013591506060850135614e0781614955565b939692955090935050565b634e487b7160e01b600052604160045260246000fd5b600060208284031215614e3a57600080fd5b8151610cef816148f4565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201614e8357614e83614e5b565b5060010190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b604051601f8201601f191681016001600160401b0381118282101715614ee957614ee9614e12565b604052919050565b60006020808385031215614f0457600080fd5b82516001600160401b0380821115614f1b57600080fd5b818501915085601f830112614f2f57600080fd5b815181811115614f4157614f41614e12565b8060051b9150614f52848301614ec1565b8181529183018401918481019088841115614f6c57600080fd5b938501935b838510156145535784519250614f86836148f4565b8282529385019390850190614f71565b600060208284031215614fa857600080fd5b5051919050565b600082821015614fc157614fc1614e5b565b500390565b600060208284031215614fd857600080fd5b81516001600160e01b031981168114610cef57600080fd5b60006020828403121561500257600080fd5b815160ff81168114610cef57600080fd5b600081600019048311821515161561502d5761502d614e5b565b500290565b634e487b7160e01b600052601260045260246000fd5b60008261505757615057615032565b500490565b6000821982111561506f5761506f614e5b565b500190565b600181815b808511156150af57816000190482111561509557615095614e5b565b808516156150a257918102915b93841c9390800290615079565b509250929050565b6000826150c6575060016108a1565b816150d3575060006108a1565b81600181146150e957600281146150f35761510f565b60019150506108a1565b60ff84111561510457615104614e5b565b50506001821b6108a1565b5060208310610133831016604e8410600b8410161715615132575081810a6108a1565b61513c8383615074565b806000190482111561515057615150614e5b565b029392505050565b6000610cef60ff8416836150b7565b60006020828403121561517957600080fd5b8151610cef81614955565b60005b8381101561519f578181015183820152602001615187565b83811115612be35750506000910152565b600081518084526151c8816020860160208601615184565b601f01601f19169290920160200192915050565b6001600160a01b038616815260a060208201819052600090615200908301876149a5565b828103604084015261521281876148a6565b9050828103606084015261522681866148a6565b9050828103608084015261455381856151b0565b6000825161524c818460208701615184565b9190910192915050565b60008261526557615265615032565b500690565b60608152600061527d60608301866151b0565b828103602084015261528f81866151b0565b91505060018060a01b0383166040830152949350505050565b6000610cef83836150b7565b7f53696c6f2046696e616e636520426f72726f7761626c652000000000000000008152600082516152ec816018850160208701615184565b670811195c1bdcda5d60c21b6018939091019283015250602001919050565b607360f81b815260008251615327816001850160208701615184565b9190910160010192915050565b7f53696c6f2046696e616e63652050726f7465637465642000000000000000000081526000825161536c816017850160208701615184565b670811195c1bdcda5d60c21b6017939091019283015250601f01919050565b61073760f41b8152600082516153a8816002850160208701615184565b9190910160020192915050565b6c029b4b637902334b730b731b29609d1b8152600082516153dd81600d850160208701615184565b64081119589d60da1b600d939091019283015250601201919050565b601960fa1b815260008251615327816001850160208701615184565b60008351615427818460208801615184565b6301034b7160e51b9083019081528351615448816004840160208801615184565b642053696c6f60d81b60049290910191820152600901949350505050565b60008351615478818460208801615184565b602d60f81b9083019081528351615496816001840160208801615184565b01600101949350505050565b6000602082840312156154b457600080fd5b81516001600160401b03808211156154cb57600080fd5b818401915084601f8301126154df57600080fd5b8151818111156154f1576154f1614e12565b615504601f8201601f1916602001614ec1565b915080825285602082850101111561551b57600080fd5b61552c816020840160208601615184565b50949350505050565b60008351615547818460208801615184565b6001600160f81b0319939093169190920190815260010192915050565b602081526000610cef60208301846151b056fea2646970667358221220d18e16744c3be14b0ddaee210dd92b8a5125ef85955f3b68fbc4df16ba02522664736f6c634300080d0033a2646970667358221220824f7101ff81fb08d1aa649bff86787dd4a495dc3c1cda52c5d218d470c35f7d64736f6c634300080d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000d998c35b7900b344bbbe6555cc11576942cf309d000000000000000000000000857c44b994a9beedcf5fa4b7e9faf48556058f59
-----Decoded View---------------
Arg [0] : _repository (address): 0xd998C35B7900b344bbBe6555cc11576942Cf309d
Arg [1] : _stateChangesHandler (address): 0x857c44b994a9BeeDcF5FA4B7E9fAf48556058F59
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000d998c35b7900b344bbbe6555cc11576942cf309d
Arg [1] : 000000000000000000000000857c44b994a9beedcf5fa4b7e9faf48556058f59
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.