Latest 6 from a total of 6 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Withdraw Manager... | 48639462 | 12 days ago | IN | 0 MON | 0.02121204 | ||||
| Transfer Ownersh... | 35330864 | 74 days ago | IN | 0 MON | 0.0053511 | ||||
| Transfer Ownersh... | 35329894 | 74 days ago | IN | 0 MON | 0.0053511 | ||||
| Transfer Ownersh... | 35324737 | 74 days ago | IN | 0 MON | 0.0039254 | ||||
| Transfer Ownersh... | 35323731 | 74 days ago | IN | 0 MON | 0.0039254 | ||||
| Transfer Ownersh... | 35322763 | 74 days ago | IN | 0 MON | 0.0066742 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
FeeManager
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import {IRecoverable} from "../interfaces/IRecoverable.sol";
import {IFeeManager, FeeStructure} from "../interfaces/IFeeManager.sol";
import {IAccountableStrategy} from "../interfaces/IAccountableStrategy.sol";
import {
Unauthorized,
ZeroAddress,
InvalidManagerSplit,
InvalidPerformanceFee,
InvalidEstablishmentFee
} from "../constants/Errors.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Multicall} from "@openzeppelin/contracts/utils/Multicall.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
/// @title FeeManager
/// @notice A contract that manages fees for strategies
contract FeeManager is IFeeManager, Ownable2Step, ReentrancyGuard, Multicall {
using Math for uint256;
using SafeERC20 for IERC20;
/// @notice Whether the strategy has a prepayment fee
mapping(address strategy => bool hasPrepaymentFee) public _hasPrepaymentFee;
/// @notice The fee structures for each strategy
mapping(address strategy => FeeStructure feeStructure) private _feeStructures;
/// @notice The pending fee structure for each strategy
mapping(address strategy => FeeStructure pendingFeeStructure) private _pendingFeeStructures;
/// @dev performance fee in basis points (10%)
uint256 private constant _performanceFee = 1e5;
/// @dev establishment fee annualized in bassis points (1%)
uint256 private constant _establishmentFee = 1e4;
/// @dev manager split in basis points (50%)
uint256 private constant _managerSplit = 5e5;
/// @dev protocol split in basis points (50%)
uint256 private constant _protocolSplit = 5e5;
/// @dev prepayment fee in basis points (2%)
uint256 private constant _prepaymentFee = 2e4;
/// @dev max performance fee in basis points (50%)
uint256 public constant MAX_PERFORMANCE_FEE = 5e5;
/// @dev max establishment fee in basis points (10%)
uint256 public constant MAX_ESTABLISHMENT_FEE = 1e5;
/// @dev basis points (100%)
uint256 public constant BASIS_POINTS = 1e6;
/// @notice The treasury address
address public treasury;
/// @notice The prepayment fees grouped by strategy
mapping(address strategy => uint256 prepaymentFee) public prepaymentFees;
/// @notice The protocol fees grouped by asset
mapping(address asset => uint256 protocolFee) public protocolFees;
/// @notice The manager fees grouped by (manager, asset, strategy)
mapping(address manager => mapping(address asset => mapping(address strategy => uint256))) public managerFees;
modifier onlyTreasury() {
if (msg.sender != treasury) revert Unauthorized();
_;
}
modifier onlyManager(address strategy) {
if (msg.sender != IAccountableStrategy(strategy).investmentManager()) revert Unauthorized();
_;
}
constructor(address treasury_, address owner_) Ownable(owner_) {
treasury = treasury_;
}
/// @inheritdoc IFeeManager
function collect(address asset, uint256 performanceFee_, uint256 establishmentFee_) public nonReentrant {
_collectFeeSplit(asset, msg.sender, performanceFee_);
protocolFees[asset] += establishmentFee_;
IERC20(asset).safeTransferFrom(msg.sender, address(this), establishmentFee_ + performanceFee_);
emit Collected(asset, msg.sender, performanceFee_, establishmentFee_);
}
/// @inheritdoc IFeeManager
function collectEstablishmentFee(address asset, uint256 amount) public nonReentrant {
protocolFees[asset] += amount;
IERC20(asset).safeTransferFrom(msg.sender, address(this), amount);
emit Collected(asset, msg.sender, 0, amount);
}
/// @inheritdoc IFeeManager
function collectPerformanceFee(address asset, uint256 amount) public nonReentrant {
_collectFeeSplit(asset, msg.sender, amount);
IERC20(asset).safeTransferFrom(msg.sender, address(this), amount);
emit Collected(asset, msg.sender, amount, 0);
}
/// @dev Collects fees and splits between manager and protocol
function _collectFeeSplit(address asset, address strategy, uint256 amount) private {
address manager = IAccountableStrategy(msg.sender).investmentManager();
if (manager == address(0)) revert ZeroAddress();
uint256 managerFee;
uint256 protocolFee;
if (managerSplit(strategy) == 0) {
managerFee = 0;
protocolFee = amount;
} else {
managerFee = _split(amount, managerSplit(strategy));
protocolFee = amount - managerFee;
}
protocolFees[asset] += protocolFee;
managerFees[manager][asset][strategy] += managerFee;
}
/// @inheritdoc IFeeManager
function withdrawProtocolFee(address asset) public nonReentrant onlyTreasury {
uint256 amount = protocolFees[asset];
if (amount > 0) {
protocolFees[asset] = 0;
IERC20(asset).safeTransfer(treasury, amount);
emit Withdraw(asset, address(0), treasury, amount);
}
}
/// @inheritdoc IFeeManager
function withdrawManagerFee(address receiver, address asset, address strategy) public nonReentrant {
uint256 amount = managerFees[msg.sender][asset][strategy];
if (amount > 0) {
managerFees[msg.sender][asset][strategy] = 0;
IERC20(asset).safeTransfer(receiver, amount);
emit Withdraw(asset, strategy, receiver, amount);
}
}
/// @inheritdoc IFeeManager
function setFeeStructure(address strategy, FeeStructure memory fees) public onlyOwner {
_requireValidFeeStructure(fees);
_feeStructures[strategy] = fees;
_feeStructures[strategy].overrideDefaults = true;
IAccountableStrategy(strategy).onFeeStructureChange();
emit FeeStructureSet(strategy, fees);
}
/// @inheritdoc IFeeManager
function setPendingFeeStructure(address strategy, FeeStructure memory fees) public onlyManager(strategy) {
_requireValidFeeStructure(fees);
_pendingFeeStructures[strategy] = fees;
emit PendingFeeStructureSet(strategy, fees);
}
function _requireValidFeeStructure(FeeStructure memory fees) private pure {
if (fees.performanceFee > MAX_PERFORMANCE_FEE) revert InvalidPerformanceFee();
if (fees.establishmentFee > MAX_ESTABLISHMENT_FEE) revert InvalidEstablishmentFee();
}
/// @inheritdoc IFeeManager
function approvePendingFeeStructure(address strategy) public onlyOwner {
_feeStructures[strategy] = _pendingFeeStructures[strategy];
_feeStructures[strategy].overrideDefaults = true;
delete _pendingFeeStructures[strategy];
IAccountableStrategy(strategy).onFeeStructureChange();
emit FeeStructureSet(strategy, _feeStructures[strategy]);
}
/// @inheritdoc IFeeManager
function setPerformanceFee(address strategy, uint256 fee) public onlyManager(strategy) {
if (fee > MAX_PERFORMANCE_FEE) revert InvalidPerformanceFee();
FeeStructure memory feeStructure_ = _feeStructures[strategy];
if (feeStructure_.overrideDefaults) {
feeStructure_.performanceFee = fee;
} else {
feeStructure_.performanceFee = fee;
feeStructure_.establishmentFee = _establishmentFee;
feeStructure_.managerSplit = _managerSplit;
feeStructure_.overrideDefaults = true;
}
_feeStructures[strategy] = feeStructure_;
IAccountableStrategy(strategy).onFeeStructureChange();
emit PerformanceFeeSet(strategy, fee);
}
/// @inheritdoc IFeeManager
function setPrepaymentFee(address strategy, uint256 fee) public onlyManager(strategy) {
prepaymentFees[strategy] = fee;
_hasPrepaymentFee[strategy] = true;
IAccountableStrategy(strategy).onFeeStructureChange();
emit PrepaymentFeeSet(strategy, fee);
}
/// @inheritdoc IFeeManager
function setTreasury(address treasury_) public onlyOwner {
if (treasury_ == address(0)) revert ZeroAddress();
address oldTreasury = treasury;
treasury = treasury_;
emit TreasurySet(oldTreasury, treasury_);
}
/// @inheritdoc IFeeManager
function establishmentFee(address strategy) public view returns (uint256) {
if (_feeStructures[strategy].overrideDefaults) {
return _feeStructures[strategy].establishmentFee;
}
return _establishmentFee;
}
/// @inheritdoc IFeeManager
function performanceFee(address strategy) public view returns (uint256) {
if (_feeStructures[strategy].overrideDefaults) {
return _feeStructures[strategy].performanceFee;
}
return _performanceFee;
}
/// @inheritdoc IFeeManager
function managerSplit(address strategy) public view returns (uint256) {
if (_feeStructures[strategy].overrideDefaults) {
return _feeStructures[strategy].managerSplit;
}
return _managerSplit;
}
/// @inheritdoc IFeeManager
function protocolSplit(address strategy) public view returns (uint256) {
return BASIS_POINTS - managerSplit(strategy);
}
/// @inheritdoc IFeeManager
function prepaymentFee(address strategy) public view returns (uint256) {
if (_hasPrepaymentFee[strategy]) {
return prepaymentFees[strategy];
}
return _prepaymentFee;
}
/// @inheritdoc IFeeManager
function feeStructure(address strategy) public view returns (FeeStructure memory) {
return _feeStructures[strategy];
}
/// @inheritdoc IFeeManager
function pendingFeeStructure(address strategy) public view returns (FeeStructure memory) {
return _pendingFeeStructures[strategy];
}
/// @inheritdoc IFeeManager
function managerTotalFees(address manager, address asset, address[] calldata strategies)
public
view
returns (uint256 totalFees)
{
for (uint256 i = 0; i < strategies.length;) {
totalFees += managerFees[manager][asset][strategies[i]];
unchecked {
++i;
}
}
}
/// @inheritdoc IRecoverable
function recoverTokens(address token, address receiver, uint256 amount) external onlyOwner {
IERC20(token).safeTransfer(receiver, amount);
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
return interfaceId == type(IFeeManager).interfaceId || interfaceId == type(IERC165).interfaceId;
}
/// @dev Splits an amount by a split percentage
/// @param amount_ The amount to split
/// @param split_ The split percentage
/// @return The split amount
function _split(uint256 amount_, uint256 split_) private pure returns (uint256) {
if (split_ == 0) {
return 0;
}
return amount_.mulDiv(split_, BASIS_POINTS, Math.Rounding.Ceil);
}
/// @dev Prevents renouncement of ownership
function renounceOwnership() public virtual override onlyOwner {
revert Unauthorized();
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
/// @notice Interface for recoverable tokens
interface IRecoverable {
/// @notice Recovers tokens from the contract
/// @param token The address of the token to recover
/// @param to The address to send the recovered tokens to
/// @param amount The amount of tokens to recover
function recoverTokens(address token, address to, uint256 amount) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import {IRecoverable} from "./IRecoverable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/// @notice The fee structure for a strategy
struct FeeStructure {
/// @notice The establishment fee
uint256 establishmentFee;
/// @notice The performance fee
uint256 performanceFee;
/// @notice The manager split
uint256 managerSplit;
/// @notice Whether to override the default fees
bool overrideDefaults;
}
interface IFeeManager is IRecoverable, IERC165 {
/// @notice An event that is emitted when fees are collected
event Collected(address indexed asset, address indexed strategy, uint256 performanceFee, uint256 establishmentFee);
/// @notice An event that is emitted when protocol fees are withdrawn
event Withdraw(address indexed asset, address indexed strategy, address indexed receiver, uint256 amount);
/// @notice An event that is emitted when the fee structure is set
event FeeStructureSet(address indexed strategy, FeeStructure feeStructure);
/// @notice An event that is emitted when the pending fee structure is set
event PendingFeeStructureSet(address indexed strategy, FeeStructure feeStructure);
/// @notice An event that is emitted when the performance fee is set
event PerformanceFeeSet(address indexed strategy, uint256 performanceFee);
/// @notice An event that is emitted when the prepayment fee is set
event PrepaymentFeeSet(address indexed strategy, uint256 prepaymentFee);
/// @notice An event that is emitted when the treasury is set
event TreasurySet(address indexed oldTreasury, address indexed newTreasury);
/// @notice Collects strategy fees
/// @param asset The address of the asset
/// @param performanceFee The performance fee
/// @param establishmentFee The establishment fee
function collect(address asset, uint256 performanceFee, uint256 establishmentFee) external;
/// @notice Collects establishment fees
/// @param asset The address of the asset
/// @param amount The amount of establishment fees
function collectEstablishmentFee(address asset, uint256 amount) external;
/// @notice Collects performance fees
/// @param asset The address of the asset
/// @param amount The amount of performance fees
function collectPerformanceFee(address asset, uint256 amount) external;
/// @notice Approves the pending fee structure
/// @param strategy The address of the strategy
function approvePendingFeeStructure(address strategy) external;
/// @notice Withdraws protocol fees
/// @param asset The address of the asset
function withdrawProtocolFee(address asset) external;
/// @notice Withdraws manager fees
/// @param receiver The address of the receiver
/// @param asset The address of the asset
/// @param strategy The address of the strategy
function withdrawManagerFee(address receiver, address asset, address strategy) external;
/// @notice Sets the fee structure
/// @param strategy The address of the strategy
/// @param feeStructure The fee structure
function setFeeStructure(address strategy, FeeStructure memory feeStructure) external;
/// @notice Sets the pending fee structure
/// @param strategy The address of the strategy
/// @param feeStructure The fee structure
function setPendingFeeStructure(address strategy, FeeStructure memory feeStructure) external;
/// @notice Sets the performance fee
/// @param strategy The address of the strategy
/// @param performanceFee The performance fee
function setPerformanceFee(address strategy, uint256 performanceFee) external;
/// @notice Sets the prepayment fee
/// @param strategy The address of the strategy
/// @param prepaymentFee The prepayment fee
function setPrepaymentFee(address strategy, uint256 prepaymentFee) external;
/// @notice Sets the treasury
/// @param treasury The address of the treasury
function setTreasury(address treasury) external;
/// @notice Returns the establishment fee
/// @param strategy The address of the strategy
/// @return The establishment fee
function establishmentFee(address strategy) external view returns (uint256);
/// @notice Returns the performance fee
/// @param strategy The address of the strategy
/// @return The performance fee
function performanceFee(address strategy) external view returns (uint256);
/// @notice Returns the manager split
/// @param strategy The address of the strategy
/// @return The manager split
function managerSplit(address strategy) external view returns (uint256);
/// @notice Returns the protocol split
/// @param strategy The address of the strategy
/// @return The protocol split
function protocolSplit(address strategy) external view returns (uint256);
/// @notice Returns the prepayment fee
/// @param strategy The address of the strategy
/// @return The prepayment fee
function prepaymentFee(address strategy) external view returns (uint256);
/// @notice Returns the fee structure
/// @param strategy The address of the strategy
/// @return The fees percentages for the strategy
function feeStructure(address strategy) external view returns (FeeStructure memory);
/// @notice Returns the pending fee structure
/// @param strategy The address of the strategy
/// @return The pending fee structure
function pendingFeeStructure(address strategy) external view returns (FeeStructure memory);
/// @notice Returns the treasury
/// @return The treasury
function treasury() external view returns (address);
/// @notice Returns the manager fees
/// @param manager The address of the manager
/// @param asset The address of the asset
/// @param strategy The address of the strategy
/// @return The manager fees of a given strategy
function managerFees(address manager, address asset, address strategy) external view returns (uint256);
/// @notice Returns the total manager fees
/// @param manager The address of the manager
/// @param asset The address of the asset
/// @param strategies The addresses of the strategies
/// @return The total manager fees of a given asset and strategies
function managerTotalFees(address manager, address asset, address[] calldata strategies)
external
view
returns (uint256);
/// @notice Returns the protocol fees
/// @param asset The address of the asset
/// @return The protocol fees
function protocolFees(address asset) external view returns (uint256);
/// @notice The basis points for the fee manager
/// @return The basis points
function BASIS_POINTS() external pure returns (uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/// @notice Struct containing loan terms and state
struct Loan {
/// @notice Minimum deposit amount allowed
uint256 minDeposit;
/// @notice Minimum redeem amount allowed
uint256 minRedeem;
/// @notice Maximum loan amount that can be drawn
/// The full threshold can be met after deposit period if the loan is not locked
uint256 maxCapacity;
/// @notice Minimum loan amount for the loan to be accepted
/// The threshold needs to be met during deposit period or loan can be rejected
uint256 minCapacity;
/// @notice Liquidity ratio that has to be maintained in the vault at all times
/// Expressed in basis points and cannot exceed 1e6, defaults to 0
uint256 reserveThreshold;
/// @notice Principal amount still to be repaid
uint256 outstandingPrincipal;
/// @notice Interest amount still to be paid
uint256 outstandingInterest;
/// @notice all funds accepted and available for borrowing
uint256 drawableFunds;
/// @notice Annual interest rate in basis points
uint256 interestRate;
/// @notice Late interest penalty in basis points
uint256 lateInterestPenalty;
/// @notice Total claimable interest available for users
uint256 claimableInterest;
/// @notice Time interval between interest payments
uint256 interestInterval;
/// @notice Time of loan start
uint256 startTime;
/// @notice Time of terms set
uint256 termsSetTime;
/// @notice Time of terms update
uint256 termsUpdateTime;
/// @notice Loan duration in seconds
uint256 duration;
/// @notice Deposit period in seconds
uint256 depositPeriod;
/// @notice Grace period after deposit period for borrower to accept loan
uint256 acceptGracePeriod;
/// @notice Withdrawal window period in seconds
/// If set it allows redemption requests
uint256 withdrawalPeriod;
/// @notice Late interest payment grace period in seconds
/// This is the time period before a loan is considered delinquent
uint256 lateInterestGracePeriod;
}
/// @notice Struct containing loan terms
struct LoanTerms {
/// @notice Minimum deposit amount allowed
uint256 minDeposit;
/// @notice Minimum redeem amount allowed
uint256 minRedeem;
/// @notice Maximum loan capacity
uint256 maxCapacity;
/// @notice Minimum loan capacity
uint256 minCapacity;
/// @notice Annual interest rate in basis points
uint256 interestRate;
/// @notice Late interest penalty in basis points
uint256 lateInterestPenalty;
/// @notice Late interest grace period in seconds
uint256 lateInterestGracePeriod;
/// @notice Time interval between interest payments
uint256 interestInterval;
/// @notice Duration of the loan in seconds
uint256 duration;
/// @notice Deposit period in seconds
uint256 depositPeriod;
/// @notice Grace period after deposit period for borrower to accept loan
uint256 acceptGracePeriod;
/// @notice Withdrawal window period in seconds
uint256 withdrawalPeriod;
}
/// @notice Struct containing DVN proof data
struct DVNProof {
/// @notice Merkle root of the DVN merkle tree
bytes32 root;
/// @notice Signature of the DVN payload
bytes32 signature;
/// @notice Message hash that was signed
bytes32 messageHash;
}
/// @notice Enum representing different loan states
enum LoanState {
/// @notice Loan terms are not set
Initialized,
/// @notice Loan terms are set
TermsSet,
/// @notice Loan is accepted by borrower and locked
OngoingLocked,
/// @notice Loan is accepted by borrower and dynamic deposits are allowed
OngoingDynamic,
/// @notice Loan is rejected
Rejected,
/// @notice Loan is repaid
Repaid,
/// @notice Loan is in default
InDefault,
/// @notice Loan is in default claims
InDefaultClaims
}
/// @notice Interface for managing strategy vault hooks
interface IStrategyVaultHooks {
/// @notice Hook for handling deposit requests
/// @param share The address of the share token
/// @param assets The amount of assets requested
/// @param controller The address of the controller
/// @param owner The address of the owner of the assets
/// @return canFulfill Whether the deposit can be fulfilled instantly
/// @return price The price of the share
function onRequestDeposit(address share, uint256 assets, address controller, address owner)
external
returns (bool canFulfill, uint256 price);
/// @notice Hook for handling redeem requests
/// @param share The address of the share token
/// @param shares The amount of shares requested
/// @param controller The address of the controller
/// @param owner The address of the owner of the shares
/// @return canFulfill Whether the redeem can be fulfilled instantly
/// @return price The price of the share
function onRequestRedeem(address share, uint256 shares, address controller, address owner)
external
returns (bool canFulfill, uint256 price);
/// @notice Hook for handling cancel deposit requests
/// @param share The address of the share token
/// @param controller The address of the controller
/// @return canCancel Whether the deposit can be cancelled instantly
function onCancelDepositRequest(address share, address controller) external returns (bool canCancel);
/// @notice Hook for handling cancel redeem requests
/// @param share The address of the share token
/// @param controller The address of the controller
/// @return canCancel Whether the redeem can be cancelled instantly
function onCancelRedeemRequest(address share, address controller) external returns (bool canCancel);
/// @notice Hook for handling deposit requests
/// @param share The address of the share token
/// @param assets The amount of assets to deposit
/// @param receiver The address of the receiver
/// @param controller The address of the controller
function onDeposit(address share, uint256 assets, address receiver, address controller)
external
returns (uint256 price);
/// @notice Hook for handling mint requests
/// @param share The address of the share token
/// @param shares The amount of shares to mint
/// @param receiver The address of the receiver
/// @param controller The address of the controller
function onMint(address share, uint256 shares, address receiver, address controller)
external
returns (uint256 price);
/// @notice Hook for handling redeem requests
/// @param share The address of the share token
/// @param shares The amount of shares to redeem
/// @param receiver The address of the receiver
function onRedeem(address share, uint256 shares, address receiver, address controller) external;
/// @notice Hook for handling withdraw requests
/// @param share The address of the share token
/// @param assets The amount of assets to withdraw
/// @param receiver The address of the receiver
/// @param controller The address of the controller
function onWithdraw(address share, uint256 assets, address receiver, address controller) external;
/// @notice Hook for handling vault transfers
/// @param from The address of the sender
/// @param to The address of the receiver
/// @param amount The amount of shares to transfer
function onTransfer(address share, address from, address to, uint256 amount) external;
/// @notice Operator authorization
/// @param operator The address of the operator
/// @dev Reverts for unauthorized operator. Operator should not be an input value of another call
function authOperator(address operator) external;
/// @notice Centralized share price
/// @param share The address of the share token
function sharePrice(address share) external view returns (uint256 price);
/// @notice Centralized accrued assets
/// @param share The address of the share token
/// @return assets The accrued assets to be distributed
function accruedAssets(address share) external view returns (uint256 assets);
/// @notice Centralized max deposit capacity
/// @param share The address of the share token
/// @param controller The address of the controller
/// @return maxAssets The maximum amount of assets that can be deposited
function maxDeposit(address share, address controller) external view returns (uint256 maxAssets);
/// @notice Centralized max redeem capacity
/// @param share The address of the share token
/// @param controller The address of the controller
/// @return maxShares The maximum amount of shares that can be redeemed
function maxRedeem(address share, address controller) external view returns (uint256 maxShares);
}
/// @notice Interface for managing fee manager hooks
interface IFeeManagerHooks {
/// @notice Hook for handling fee structure changes
function onFeeStructureChange() external;
}
interface IUpdateLateStatus {
/// @notice Updates the late status of the loan
function updateLateStatus() external;
}
/// @notice Interface for managing base strategy components
interface IAccountableStrategy is IStrategyVaultHooks, IFeeManagerHooks, IERC165 {
/// @notice Event emitted when the borrower is changed
event BorrowerChanged(address indexed oldBorrower, address indexed newBorrower);
/// @notice Event emitted when the pending borrower is set
event PendingBorrowerSet(address indexed pendingBorrower);
/// @notice Event emitted when the safety module is set
event SafetyModuleSet(address indexed safetyModule);
/// @notice Event emitted when the investment manager is set
event InvestmentManagerSet(address indexed investmentManager);
/// @notice Event emitted when the proof signer is set
event ProofSignerSet(address indexed proofSigner);
/// @notice Event emitted when the DVN proof is published
event DVNProofPublished(bytes32 indexed root, bytes32 indexed signature, bytes32 indexed messageHash);
/// @notice Event emitted when the default is rejected
event LoanDefaultRejected();
/// @notice Event emitted when the default is accepted
event LoanDefaulted(uint256 principal, uint256 collateral);
/// @notice Event emitted when the penalties enabled flag is set
event PenaltiesEnabledSet(bool enabled);
/// @notice Event emitted when the security admin enabled flag is set
event SecurityAdminEnabledSet(bool enabled);
/// @notice Event emitted when the operations admin enabled flag is set
event OperationsAdminEnabledSet(bool enabled);
/// @notice Event emitted when the reserve threshold is set
event ReserveThresholdSet(uint256 threshold);
/// @notice Event emitted when the rewards distributor is set
event RewardsDistributorSet(address indexed rewardsDistributor);
/// @notice Event emitted when the price oracle is set
event PriceOracleSet(address indexed priceOracle);
/// @notice Set a new pending borrower
/// @param newBorrower Address of the new pending borrower
function setPendingBorrower(address newBorrower) external;
/// @notice Accept the borrower role as pending borrower
function acceptBorrowerRole() external;
/// @notice Set a new safety module contract
/// @dev It can act as a factory in a deploy-and-set manner
/// @param safetyModule Address of the new safety module
function setSafetyModule(address safetyModule) external;
/// @notice Set a new rewards distributor contract
/// @dev It can act as a factory in a deploy-and-set manner
/// @param rewards Address of the new rewards distributor
function setRewardsDistributor(address rewards) external;
/// @notice Set a new price oracle contract
/// @param priceOracle Address of the new price oracle
function setPriceOracle(address priceOracle) external;
/// @notice Set a new investment manager contract
/// @param investmentManager Address of the new investment manager
function setInvestmentManager(address investmentManager) external;
/// @notice Set the auth signer address
/// @dev This is function relays data to the vault's access module
/// It sets the kyc verification signer when permission level is `KYC`
/// @param authSigner Address of the new auth signer
function setAuthSigner(address authSigner) external;
/// @notice Set the lenders addresses and permissions
/// @dev This is function relays data to the vault's access module
/// It whitelists lenders when permission level is `Whitelist`
/// @param lenders Addresses of the lenders to be whitelisted
/// @param allowed Whether the lenders are allowed to perform actions
function setLenders(address[] calldata lenders, bool[] calldata allowed) external;
/// @notice Set the proof signer address
/// @param proofSigner Address of the new proof signer
function setProofSigner(address proofSigner) external;
/// @notice Publish a new DVN proof
/// @param proof Latest DVN proof data
function publishDVNProof(DVNProof memory proof) external;
/// @notice Accept the default of the loan
function acceptDefault() external;
/// @notice Reject the default of the loan
function rejectDefault() external;
/// @notice Set the penalties enabled flag
/// @param enabled Whether penalties are enabled
function setPenaltiesEnabled(bool enabled) external;
/// @notice Set the security admin enabled flag
/// @param enabled Whether security admin is enabled
function setSecurityAdminEnabled(bool enabled) external;
/// @notice Set the operations admin enabled flag
/// @param enabled Whether operations admin is enabled
function setOperationsAdminEnabled(bool enabled) external;
/// @notice Set the shares transferable flag
/// @param transferable Whether shares are transferable
function setSharesTransferable(bool transferable) external;
/// @notice Set the min reserve threshold required in the vault
/// @param threshold The threshold expressed in basis points
function setReserveThreshold(uint256 threshold) external;
/// @notice Get the global registry contract address
/// @return The global registry address
function globals() external view returns (address);
/// @notice Get the current borrower address
/// @return The borrower address
function borrower() external view returns (address);
/// @notice Get the pending borrower address
/// @return The pending borrower address
function pendingBorrower() external view returns (address);
/// @notice Get the investment manager contract address
/// @return The investment manager address
function investmentManager() external view returns (address);
/// @notice Get the fee manager contract address
/// @return The fee manager address
function feeManager() external view returns (address);
/// @notice Get the safety module contract address
/// @return The safety module address
function safetyModule() external view returns (address);
/// @notice Get the rewards distributor contract address
/// @return The rewards distributor address
function rewards() external view returns (address);
/// @notice Get the price oracle contract address
/// @return The price oracle address
function priceOracle() external view returns (address);
/// @notice Get the proof signer address
/// @return The proof signer address
/// @dev Returns the pub key of the node signing proofs
function proofSigner() external view returns (address);
/// @notice Get the vault contract address
/// @return The vault address
function vault() external view returns (address);
/// @notice Get the current loan state
/// @return The loan struct
function loan() external view returns (Loan memory);
/// @notice Get the DVN proof
/// @return The latest DVN proof data
function dvnProof() external view returns (DVNProof memory);
/// @notice Get the loan state
/// @return The loan state enum value
function loanState() external view returns (LoanState);
/// @notice Get the penalties enabled flag
/// @return The penalties enabled flag
function penaltiesEnabled() external view returns (bool);
/// @notice Get the security admin enabled flag
/// @return The security admin enabled flag
function securityAdminEnabled() external view returns (bool);
/// @notice Get the operations admin enabled flag
/// @return The operations admin enabled flag
function operationsAdminEnabled() external view returns (bool);
/// @notice Get the precision for the strategy
/// @return The precision
function PRECISION() external view returns (uint256);
/// @notice Get the basis points for the strategy
/// @return The basis points
function BASIS_POINTS() external view returns (uint256);
/// @notice Get the version of the strategy
/// @dev Increment when upgrading the strategy
function version() external view returns (uint256);
}
/// @notice Interface for managing loan lifecycle and payments
interface IAccountableLoan is IAccountableStrategy {
/// @notice Event emitted when the loan is initialized
/// @param minCapacity The minimum loan capacity
/// @param maxCapacity The maximum loan capacity
/// @param interestRate The interest rate of the loan
/// @param lateInterestPenalty The late interest penalty
/// @param interestInterval The interest interval
/// @param duration The duration of the loan
event LoanTermsSet(
uint256 minCapacity,
uint256 maxCapacity,
uint256 interestRate,
uint256 lateInterestPenalty,
uint256 interestInterval,
uint256 duration
);
/// @notice Event emitted when the default is initiated
event LoanDefaultInitiated();
/// @notice Event emitted when a default is covered by safety collateral
/// @param safetyModule The address of the safety module
/// @param provider The address of the provider
/// @param collateral The amount of collateral covered
event DefaultCovered(address indexed safetyModule, address indexed provider, uint256 collateral);
/// @notice Event emitted when funds are borrowed
/// @param borrower The address of the borrower
/// @param assets The amount of funds borrowed
event Borrowed(address indexed borrower, uint256 assets);
/// @notice Event emitted when the loan is repaid
/// @param assets The amount of funds repaid
event LoanRepaid(uint256 assets);
/// @notice Event emitted when the loan is partially repaid
/// @param amount The amount of funds repaid to close loan
/// @param remainingInterest The remaining interest amount
event LoanPrepaid(uint256 amount, uint256 remainingInterest);
/// @notice Event emitted when interest is paid
/// @param borrower The address of the borrower
/// @param totalAmount The total amount paid
/// @param interestPenalty The interest penalty amount paid
/// @param performanceFee The performance fee amount paid
/// @param establishmentFee The establishment fee amount paid
/// @param paymentInterval The interval of the payment
event InterestPaid(
address indexed borrower,
uint256 totalAmount,
uint256 interestPenalty,
uint256 performanceFee,
uint256 establishmentFee,
uint256 paymentInterval
);
/// @notice Initialize a new loan with the given terms
/// @param terms The loan terms to initialize with
function setTerms(LoanTerms memory terms) external;
/// @notice Update an existing loan with new terms
/// @param terms The new loan terms to update to
function updateTerms(LoanTerms memory terms) external;
/// @notice Initialize loan default state
function defaultLoan() external;
/// @notice Cover the default of the loan
/// @param assets The amount of assets to cover
function coverDefault(uint256 assets) external;
/// @notice Borrow funds from the loan
/// @param assets The amount of funds to borrow
function borrow(uint256 assets) external;
/// @notice Repay the loan amount
/// @param assets The amount of funds to repay
function repay(uint256 assets) external;
/// @notice Make a partial loan repayment
function prepay() external;
/// @notice Make an interest payment
/// @param assets The interest amount to pay
function pay(uint256 assets) external;
/// @notice Get the period of time the loan has been delinquent
/// @return The period of time the loan has been delinquent
function timeDelinquent() external view returns (uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.20; // ========================================================================== // // Authorization Errors // // ========================================================================== // /// @notice Error thrown when unauthorized error Unauthorized(); /// @notice Thrown when a signature verification fails due to invalid signer error InvalidSigner(); /// @notice Thrown when an operation is attempted with invalid operator permissions error InvalidOperator(); /// @notice Error thrown when invalid pending borrower is provided error InvalidPendingBorrower(); /// @notice Thrown when KYC status is not verified error KYCNotVerified(); /// @notice Thrown when a block expiration is invalid (e.g. in the past) error InvalidBlockExpiration(); /// @notice Thrown when a signature verification fails error InvalidSignature(); /// @notice Thrown when a message data length is invalid error InvalidMsgDataLength(); /// @notice Thrown when an accounts array is empty error EmptyAccountsArray(); // ========================================================================== // // Vault operations errors // // ========================================================================== // /// @notice Error thrown when trying to cancel a deposit request failed error CancelDepositRequestFailed(); /// @notice Error thrown when trying to cancel a redeem request failed error CancelRedeemRequestFailed(); /// @notice Error thrown when deposit amount exceeds max deposit error ExceedsMaxDeposit(); /// @notice Error thrown when assets exceed redemption limits error ExceedsRedeemLimit(); /// @notice Error thrown when redeem amount exceeds max redeem error ExceedsMaxRedeem(); /// @notice Error thrown when deposit/withdraw amount is insufficient error InsufficientAmount(); /// @notice Error thrown when redeem share amount is insufficient error InsufficientShares(); /// @notice Error thrown when no redeem request exists error NoRedeemRequest(); /// @notice Error thrown when no pending redeem request exists error NoPendingRedeemRequest(); /// @notice Error thrown when no cancel redeem request exists error NoCancelRedeemRequest(); /// @notice Thrown when attempting to transfer shares that are not transferable error SharesNotTransferable(); /// @notice Error thrown when no queue request exists error NoQueueRequest(); /// @notice Error thrown when redeem amount exceeds liquidity error InsufficientLiquidity(); /// @notice Error thrown when no queue requests exist error NoQueueRequests(); /// @notice Error thrown when asset is not whitelisted error AssetNotWhitelisted(); // ========================================================================== // // Loan operations errors // // ========================================================================== // /// @notice Error thrown when loan terms are already set error LoanTermsAlreadySet(); /// @notice Error thrown when loan terms are not set error LoanTermsNotSet(); /// @notice Error thrown when loan is ongoing error LoanOngoing(); /// @notice Error thrown when loan is not ongoing error LoanNotOngoing(); /// @notice Error thrown when loan is not in default error LoanNotInDefault(); /// @notice Error thrown when loan terms are not met error LoanTermsNotMet(); /// @notice Error thrown when loan has not matured error LoanNotMatured(); /// @notice Error thrown when repayment terms are not met error LoanCannotBeRepaid(); /// @notice Error thrown when interest is already claimed error InterestAlreadyClaimed(); /// @notice Error thrown when there are outstanding interest payments error OutstandingInterestPayments(); /// @notice Thrown when an operation is attempted with invalid controller permissions error InvalidController(); /// @notice Thrown when an operation is attempted with invalid vault manager permissions error InvalidVaultManager(); /// @notice Thrown when an operation is attempted with invalid interval duration pair error InvalidIntervalDurationPair(); /// @notice Thrown when an operation is attempted with invalid interval length error InvalidIntervalLength(); /// @notice Thrown when an operation is attempted with invalid withdrawal period error InvalidWithdrawalPeriod(); /// @notice Error thrown when capacity is set to a lower value than acceptable error CapacityTooLow(); /// @notice Error thrown when accept grace period is longer than acceptable for the loan error AcceptGracePeriodTooLong(); /// @notice Error thrown when a threshold exceeds the max bound error ThresholdTooHigh(); /// @notice Error thrown when attempting to pay when no payment is due error NoPaymentDue(); // ========================================================================== // // General errors // // ========================================================================== // /// @notice Thrown when an index is out of bounds error OutOfBounds(); /// @notice Thrown when an invalid range is provided error InvalidRange(); /// @notice Thrown when an operation is requested with zero amount error ZeroAmount(); /// @notice Thrown when an input address is address(0) error ZeroAddress(); /// @notice Thrown when deployment of a loan proxy contract fails /// @param reason The reason for the failed deployment error FailedDeployment(string reason); /// @notice Thrown when a deposit is not allowed error DepositNotAllowed(); /// @notice Thrown when a redeem is not allowed error RedeemNotAllowed(); /// @notice Thrown when input arrays lengths don't match error ArrayLengthMismatch(); /// @notice Error thrown when an operation is not required/allowed for the current permission level error PermissionLevelMismatch(); /// @notice Error thrown when an operation is not supported by the strategy error NotSupportedByStrategy(); // ========================================================================== // // Fee Manager errors // // ========================================================================== // /// @notice Error thrown when manager split is invalid error InvalidManagerSplit(); /// @notice Error thrown when performance fee is invalid error InvalidPerformanceFee(); /// @notice Error thrown when establishment fee is invalid error InvalidEstablishmentFee(); // ========================================================================== // // Rewards errors // // ========================================================================== // /// @notice Error thrown when a root is already set error RootAlreadySet(); /// @notice Error thrown when no root is set error RootNotSet(); /// @notice Error thrown when a root is already pending error RootAlreadyPending(); /// @notice Error thrown when no pending root is set error NoPendingRoot(); /// @notice Error thrown when timelock is not expired error TimelockNotExpired(); /// @notice Error thrown when a proof is invalid error InvalidProof(); /// @notice Error thrown when the amount to claim is not enough error NotEnoughClaimableAmount(); /// @notice Error thrown when an invalid rewards type is provided error InvalidRewardsType();
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Multicall.sol)
pragma solidity ^0.8.20;
import {Address} from "./Address.sol";
import {Context} from "./Context.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* Consider any assumption about calldata validation performed by the sender may be violated if it's not especially
* careful about sending transactions invoking {multicall}. For example, a relay address that filters function
* selectors won't filter calls nested within a {multicall} operation.
*
* NOTE: Since 5.0.1 and 4.9.4, this contract identifies non-canonical contexts (i.e. `msg.sender` is not {_msgSender}).
* If a non-canonical context is identified, the following self `delegatecall` appends the last bytes of `msg.data`
* to the subcall. This makes it safe to use with {ERC2771Context}. Contexts that don't affect the resolution of
* {_msgSender} are not propagated to subcalls.
*/
abstract contract Multicall is Context {
/**
* @dev Receives and executes a batch of function calls on this contract.
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
bytes memory context = msg.sender == _msgSender()
? new bytes(0)
: msg.data[msg.data.length - _contextSuffixLength():];
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), bytes.concat(data[i], context));
}
return results;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../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;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @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);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @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).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// 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 cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @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;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
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() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @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 or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* 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.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @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`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// 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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}{
"remappings": [
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"murky/=lib/murky/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"treasury_","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidEstablishmentFee","type":"error"},{"inputs":[],"name":"InvalidPerformanceFee","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"performanceFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"establishmentFee","type":"uint256"}],"name":"Collected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"components":[{"internalType":"uint256","name":"establishmentFee","type":"uint256"},{"internalType":"uint256","name":"performanceFee","type":"uint256"},{"internalType":"uint256","name":"managerSplit","type":"uint256"},{"internalType":"bool","name":"overrideDefaults","type":"bool"}],"indexed":false,"internalType":"struct FeeStructure","name":"feeStructure","type":"tuple"}],"name":"FeeStructureSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"components":[{"internalType":"uint256","name":"establishmentFee","type":"uint256"},{"internalType":"uint256","name":"performanceFee","type":"uint256"},{"internalType":"uint256","name":"managerSplit","type":"uint256"},{"internalType":"bool","name":"overrideDefaults","type":"bool"}],"indexed":false,"internalType":"struct FeeStructure","name":"feeStructure","type":"tuple"}],"name":"PendingFeeStructureSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"performanceFee","type":"uint256"}],"name":"PerformanceFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"prepaymentFee","type":"uint256"}],"name":"PrepaymentFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldTreasury","type":"address"},{"indexed":true,"internalType":"address","name":"newTreasury","type":"address"}],"name":"TreasurySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"BASIS_POINTS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_ESTABLISHMENT_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PERFORMANCE_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"_hasPrepaymentFee","outputs":[{"internalType":"bool","name":"hasPrepaymentFee","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"approvePendingFeeStructure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"performanceFee_","type":"uint256"},{"internalType":"uint256","name":"establishmentFee_","type":"uint256"}],"name":"collect","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"collectEstablishmentFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"collectPerformanceFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"establishmentFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"feeStructure","outputs":[{"components":[{"internalType":"uint256","name":"establishmentFee","type":"uint256"},{"internalType":"uint256","name":"performanceFee","type":"uint256"},{"internalType":"uint256","name":"managerSplit","type":"uint256"},{"internalType":"bool","name":"overrideDefaults","type":"bool"}],"internalType":"struct FeeStructure","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"manager","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"strategy","type":"address"}],"name":"managerFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"managerSplit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"manager","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address[]","name":"strategies","type":"address[]"}],"name":"managerTotalFees","outputs":[{"internalType":"uint256","name":"totalFees","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"pendingFeeStructure","outputs":[{"components":[{"internalType":"uint256","name":"establishmentFee","type":"uint256"},{"internalType":"uint256","name":"performanceFee","type":"uint256"},{"internalType":"uint256","name":"managerSplit","type":"uint256"},{"internalType":"bool","name":"overrideDefaults","type":"bool"}],"internalType":"struct FeeStructure","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"performanceFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"prepaymentFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"prepaymentFees","outputs":[{"internalType":"uint256","name":"prepaymentFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"protocolFees","outputs":[{"internalType":"uint256","name":"protocolFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"}],"name":"protocolSplit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"components":[{"internalType":"uint256","name":"establishmentFee","type":"uint256"},{"internalType":"uint256","name":"performanceFee","type":"uint256"},{"internalType":"uint256","name":"managerSplit","type":"uint256"},{"internalType":"bool","name":"overrideDefaults","type":"bool"}],"internalType":"struct FeeStructure","name":"fees","type":"tuple"}],"name":"setFeeStructure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"components":[{"internalType":"uint256","name":"establishmentFee","type":"uint256"},{"internalType":"uint256","name":"performanceFee","type":"uint256"},{"internalType":"uint256","name":"managerSplit","type":"uint256"},{"internalType":"bool","name":"overrideDefaults","type":"bool"}],"internalType":"struct FeeStructure","name":"fees","type":"tuple"}],"name":"setPendingFeeStructure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setPerformanceFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"strategy","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setPrepaymentFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"treasury_","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"strategy","type":"address"}],"name":"withdrawManagerFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"withdrawProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6080346100f857601f611bf638819003918201601f19168301916001600160401b038311848410176100fc5780849260409485528339810103126100f85761004681610110565b906001600160a01b039061005c90602001610110565b169081156100e557600180546001600160a01b03199081169091555f80549182168417815560405193916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a36001600255600680546001600160a01b0319166001600160a01b0392909216919091179055611ad190816101258239f35b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036100f85756fe6080806040526004361015610012575f80fd5b5f905f3560e01c9081628b9b921461127e5750806301ffc9a71461122757806324a71d3e146111155780633abe49de146110f157806355d52466146110d25780635a5a3857146110935780635f3e849f146110515780635f72b8c11461102d57806361d027b314611004578063621776fa14610ede5780636244699414610e70578063668fb6dc14610d9e578063715018a614610d77578063782b353414610c3957806379ba509714610bb957806380e17b0b14610af6578063852409781461098857806385f9d8301461090857806389c957e9146108cf5780638da5cb5b146108a8578063a06ab9721461087c578063a4aec28e146107ec578063ac9650d814610626578063acc10f1114610581578063bdca916514610563578063bff3fcb214610545578063c8be469c14610455578063cf5ef09414610408578063d3645623146103b7578063dcf844a71461037e578063e1f1c4a714610360578063e30c397814610337578063f0f44260146102b3578063f2fde38b146102465763fa7a17ba1461019e575f80fd5b3461024357602036600319011261024357604061023f916101bd611431565b6101c56116b9565b506001600160a01b031681526005602052206040519060ff906003906101ea84611473565b8054845260018101546020850152600281015460408501520154161515606082015260405191829182919091606080608083019480518452602081015160208501526040810151604085015201511515910152565b0390f35b80fd5b503461024357602036600319011261024357610260611431565b610268611707565b600180546001600160a01b0319166001600160a01b0392831690811790915582549091167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b5034610243576020366003190112610243576102cd611431565b6102d5611707565b6001600160a01b0316801561032857600680546001600160a01b0319811683179091556001600160a01b03167f21eb548722a564f6e09f039f7aa858ae94c911910f3823b37af2250eeca4f4038380a380f35b63d92e233d60e01b8252600482fd5b50346102435780600319360112610243576001546040516001600160a01b039091168152602090f35b50346102435780600319360112610243576020604051620f42408152f35b5034610243576020366003190112610243576020906040906001600160a01b036103a6611431565b168152600883522054604051908152f35b5034610243576020366003190112610243576103d96103d4611431565b6115b2565b620f42400390620f424082116103f457602082604051908152f35b634e487b7160e01b81526011600452602490fd5b503461024357602036600319011261024357604061023f91610428611431565b6104306116b9565b506001600160a01b031681526004602052206040519060ff906003906101ea84611473565b50346102435760603660031901126102435761046f611431565b610477611447565b61047f61145d565b9161048861176f565b3384526009602090815260408086206001600160a01b038581165f908152918452828220908716825290925290205491826104c7575b84600160025580f35b3385526009602090815260408087206001600160a01b039384165f81815291845282822094881682529383522086905590917f3115d1449a7b732c986cba18244e897a450f61e1bb8d589cd2e69e6c8924f9f7919061052785828661172d565b6040519485526001600160a01b03908116951693a45f8080806104be565b50346102435780600319360112610243576020604051620186a08152f35b503461024357806003193601126102435760206040516207a1208152f35b50346102435760603660031901126102435761059b611431565b60243590604435906105ab61176f565b6105b683338361178d565b60018060a01b0316908184526008602052604084206105d6828254611621565b90556105ed6105e58483611621565b303385611890565b60405192835260208301527fdcdcaef5de7ddddb7ef392b97e6b32c1c43fd5f5ed468fd987baaa3c5252f5f560403393a3600160025580f35b50346102435760203660031901126102435760043567ffffffffffffffff81116107e857610658903690600401611527565b90602060405161066882826114a3565b84815281810191601f19810136843761068085611679565b9361068e60405195866114a3565b858552601f1961069d87611679565b01875b8181106107d957505036819003601e190190875b87811015610761578060051b8201358381121561075d5782019081359167ffffffffffffffff83116107595785018a8336038213610243578061073d92896107296001978b8e6040519483869484860198893784019083820190898252519283915e010185815203601f1981018352826114a3565b5190305af46107366118d4565b90306119ba565b610747828a611691565b526107528189611691565b50016106b4565b8a80fd5b8980fd5b88848860405191808301818452825180915260408401918060408360051b870101940192865b8388106107945786860387f35b9091929394838080600193603f198b8203018752818a518051918291828552018484015e86838284010152601f80199101160101970193019701969093929193610787565b606087820185015283016106a0565b5080fd5b503461024357604036600319011261024357610806611431565b6024359061081261176f565b6001600160a01b03168083526008602052604083208054610834908490611621565b905561084282303384611890565b6040519183835260208301527fdcdcaef5de7ddddb7ef392b97e6b32c1c43fd5f5ed468fd987baaa3c5252f5f560403393a3600160025580f35b50346102435760203660031901126102435760206108a061089b611431565b611642565b604051908152f35b5034610243578060031936011261024357546040516001600160a01b039091168152602090f35b5034610243576020366003190112610243576020906040906001600160a01b036108f7611431565b168152600783522054604051908152f35b503461024357604036600319011261024357610922611431565b6024359061092e61176f565b61093982338361178d565b6001600160a01b031661094e82303384611890565b6040519182528260208301527fdcdcaef5de7ddddb7ef392b97e6b32c1c43fd5f5ed468fd987baaa3c5252f5f560403393a3600160025580f35b5034610243576020366003190112610243576109a2611431565b6109aa611707565b6001600160a01b031680825260056020908152604080842083855260049092528320818103610ab8575b5050808252600460205260036040832001600160ff198254161790558082526005602052816003604082208281558260018201558260028201550155803b156107e857604051633593b99960e11b81528290818160048183875af18015610aad57610a98575b5081905260046020527fbf01b2316019e2ee671136646a19eac78b4c315c1efe3ea439fcb90f3403bccc60806040842060ff60036040519280548452600181015460208501526002810154604085015201541615156060820152a280f35b81610aa2916114a3565b6107e857815f610a3a565b6040513d84823e3d90fd5b600360ff8184610aef95548555600181015460018601556002810154600286015501541691019060ff801983541691151516179055565b5f806109d4565b503461024357606036600319011261024357610b10611431565b610b18611447565b9160443567ffffffffffffffff81116107e857610b3a84913690600401611527565b9293909184916001600160a01b039182169116825b85871015610bae57818452600960205260408420835f5260205260405f208760051b86013560018060a01b0381168103610baa5791610ba291600193848060a01b03165f5260205260405f205490611621565b960195610b4f565b8580fd5b602090604051908152f35b5034610243578060031936011261024357600154336001600160a01b03821603610c26576001600160a01b0319908116600155815433918116821783556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b63118cdaa760e01b825233600452602482fd5b503461024357604036600319011261024357610c53611431565b6040516304b31bb160e41b81526001600160a01b039091169060243590602081600481865afa908115610d6c578491610d3d575b506001600160a01b03163303610d2f578183526007602052806040842055818352600360205260408320600160ff19825416179055813b15610d2b57604051633593b99960e11b81528390818160048183885af18015610aad57610d16575b505060207f4e96bd64bffdce6e304c896a5a4378c91accc1cb7603d0caca264163ba7d7f0b91604051908152a280f35b81610d20916114a3565b610d2b57825f610ce6565b8280fd5b6282b42960e81b8352600483fd5b610d5f915060203d602011610d65575b610d5781836114a3565b810190611558565b5f610c87565b503d610d4d565b6040513d86823e3d90fd5b5034610243578060031936011261024357600490610d93611707565b6282b42960e81b8152fd5b503461024357602036600319011261024357610db8611431565b610dc061176f565b6006546001600160a01b03163303610e62576001600160a01b03168082526008602052604082205490829082610dfa575b50600160025580f35b808252600860205260408220829055600654610e219084906001600160a01b03168361172d565b6006546040519384526001600160a01b0316927f3115d1449a7b732c986cba18244e897a450f61e1bb8d589cd2e69e6c8924f9f790602090a45f8181610df1565b6282b42960e81b8252600482fd5b503461024357606036600319011261024357610e8a611431565b906040610e95611447565b91610e9e61145d565b9360018060a01b031681526009602052209060018060a01b03165f5260205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b503461024357610eed366114c5565b90610ef6611707565b610eff826116dd565b60018060a01b0316908183526004602052610f4b6040842082518155602083015160018201556040830151600282015560036060840151151591019060ff801983541691151516179055565b818352600460205260408320600301805460ff19166001179055813b15610d2b57604051633593b99960e11b81528390818160048183885af18015610aad57610fef575b5050610fe97fbf01b2316019e2ee671136646a19eac78b4c315c1efe3ea439fcb90f3403bccc9160405191829182919091606080608083019480518452602081015160208501526040810151604085015201511515910152565b0390a280f35b81610ff9916114a3565b610d2b57825f610f8f565b50346102435780600319360112610243576006546040516001600160a01b039091168152602090f35b50346102435760203660031901126102435760206108a061104c611431565b6115ed565b50346102435760603660031901126102435761109061106e611431565b611076611447565b61107e611707565b604435916001600160a01b031661172d565b80f35b50346102435760203660031901126102435760209060ff906040906001600160a01b036110be611431565b168152600384522054166040519015158152f35b50346102435760203660031901126102435760206108a06103d4611431565b50346102435760203660031901126102435760206108a0611110611431565b611577565b503461024357611124366114c5565b6040516304b31bb160e41b81526001600160a01b0390921691602081600481865afa908115610d6c578491611208575b506001600160a01b03163303610d2f57610fe9816111927f857ba4306a88d9ff795171bf47361c59df7f9adee5ededf3b76f7e34e9207d94936116dd565b83855260056020526111d56040862082518155602083015160018201556040830151600282015560036060840151151591019060ff801983541691151516179055565b60405191829182919091606080608083019480518452602081015160208501526040810151604085015201511515910152565b611221915060203d602011610d6557610d5781836114a3565b5f611154565b50346102435760203660031901126102435760043563ffffffff60e01b81168091036107e85760209063a02803bf60e01b811490811561126d575b506040519015158152f35b6301ffc9a760e01b14905082611262565b9050346113da5760403660031901126113da57611299611431565b6304b31bb160e41b82526001600160a01b03169060243590602081600481865afa9081156113cf575f91611412575b506001600160a01b03163303611404576207a12081116113f557815f52600460205261136260405f2060036040519161130083611473565b805483526002810154604084019081529082015460ff1615801560608501908152919060208501906113de578681525b875f52600460205260405f209451855551600185015551600284015551151591019060ff801983541691151516179055565b813b156113da57604051633593b99960e11b81525f8160048183875af180156113cf576113b9575b5060207fbc590c47ab5b0f493568e4bbad2ca8bb15683285a437d974e8157d7d812716d091604051908152a280f35b6113c69193505f906114a3565b5f91602061138a565b6040513d5f823e3d90fd5b5f80fd5b86815261271085526207a120825260018352611330565b630f14508d60e41b5f5260045ffd5b6282b42960e81b5f5260045ffd5b61142b915060203d602011610d6557610d5781836114a3565b5f6112c8565b600435906001600160a01b03821682036113da57565b602435906001600160a01b03821682036113da57565b604435906001600160a01b03821682036113da57565b6080810190811067ffffffffffffffff82111761148f57604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff82111761148f57604052565b9060a06003198301126113da576004356001600160a01b03811681036113da576080909260231901126113da576040516114fe81611473565b60243581526044356020820152606435604082015260843580151581036113da57606082015290565b9181601f840112156113da5782359167ffffffffffffffff83116113da576020808501948460051b0101116113da57565b908160209103126113da57516001600160a01b03811681036113da5790565b6001600160a01b03165f8181526004602052604090206003015460ff166115a05750620186a090565b5f526004602052600160405f20015490565b6001600160a01b03165f8181526004602052604090206003015460ff166115db57506207a12090565b5f526004602052600260405f20015490565b6001600160a01b03165f8181526003602052604090205460ff166116125750614e2090565b5f52600760205260405f205490565b9190820180921161162e57565b634e487b7160e01b5f52601160045260245ffd5b6001600160a01b03165f8181526004602052604090206003015460ff1661166a575061271090565b5f52600460205260405f205490565b67ffffffffffffffff811161148f5760051b60200190565b80518210156116a55760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b604051906116c682611473565b5f6060838281528260208201528260408201520152565b6207a1206020820151116113f557620186a09051116116f857565b630989d05560e31b5f5260045ffd5b5f546001600160a01b0316330361171a57565b63118cdaa760e01b5f523360045260245ffd5b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448083019390935291815261176d916117686064836114a3565b611913565b565b600280541461177e5760028055565b633ee5aeb560e01b5f5260045ffd5b6040516304b31bb160e41b8152602081600481335afa9081156113cf575f91611871575b506001600160a01b03168015611862576117ca836115b2565b611830575f935b60018060a01b0383165f5260086020526117f060405f20918254611621565b90555f52600960205260405f209060018060a01b03165f5260205260405f209060018060a01b03165f5260205261182c60405f20918254611621565b9055565b61184261183c846115b2565b85611987565b93848103908111156117d157634e487b7160e01b5f52601160045260245ffd5b63d92e233d60e01b5f5260045ffd5b61188a915060203d602011610d6557610d5781836114a3565b5f6117b1565b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815261176d916117686084836114a3565b3d1561190e573d9067ffffffffffffffff821161148f5760405191611903601f8201601f1916602001846114a3565b82523d5f602084013e565b606090565b5f8061193b9260018060a01b03169360208151910182865af16119346118d4565b90836119ba565b8051908115159182611963575b50506119515750565b635274afe760e01b5f5260045260245ffd5b81925090602091810103126113da57602001518015908115036113da575f80611948565b9080156119b4578061199d620f42409284611a18565b92096119a65790565b6001810180911161162e5790565b50505f90565b906119de57508051156119cf57805190602001fd5b630a12f52160e11b5f5260045ffd5b81511580611a0f575b6119ef575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156119e7565b9190915f838202915f1985820991838084109303928084039314611a8d5782620f42401115611a7e57507fde8f6cefed634549b62c77574f722e1ac57e23f24d8fd5cb790fb65668c261399394620f4240910990828211900360fa1b910360061c170290565b63227bc15360e01b8152600490fd5b505050620f4240919250049056fea26469706673582212202bf05220bde64db62d01fc895c9d3ff282a5686364d585a3c9b1228b4ad5aea864736f6c634300081b00330000000000000000000000004b07aaa370189e5603df56c84f59c5a59181bfb1000000000000000000000000bc83ff5d65a7454adbaffd6ddc5695d978e1d50d
Deployed Bytecode
0x6080806040526004361015610012575f80fd5b5f905f3560e01c9081628b9b921461127e5750806301ffc9a71461122757806324a71d3e146111155780633abe49de146110f157806355d52466146110d25780635a5a3857146110935780635f3e849f146110515780635f72b8c11461102d57806361d027b314611004578063621776fa14610ede5780636244699414610e70578063668fb6dc14610d9e578063715018a614610d77578063782b353414610c3957806379ba509714610bb957806380e17b0b14610af6578063852409781461098857806385f9d8301461090857806389c957e9146108cf5780638da5cb5b146108a8578063a06ab9721461087c578063a4aec28e146107ec578063ac9650d814610626578063acc10f1114610581578063bdca916514610563578063bff3fcb214610545578063c8be469c14610455578063cf5ef09414610408578063d3645623146103b7578063dcf844a71461037e578063e1f1c4a714610360578063e30c397814610337578063f0f44260146102b3578063f2fde38b146102465763fa7a17ba1461019e575f80fd5b3461024357602036600319011261024357604061023f916101bd611431565b6101c56116b9565b506001600160a01b031681526005602052206040519060ff906003906101ea84611473565b8054845260018101546020850152600281015460408501520154161515606082015260405191829182919091606080608083019480518452602081015160208501526040810151604085015201511515910152565b0390f35b80fd5b503461024357602036600319011261024357610260611431565b610268611707565b600180546001600160a01b0319166001600160a01b0392831690811790915582549091167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b5034610243576020366003190112610243576102cd611431565b6102d5611707565b6001600160a01b0316801561032857600680546001600160a01b0319811683179091556001600160a01b03167f21eb548722a564f6e09f039f7aa858ae94c911910f3823b37af2250eeca4f4038380a380f35b63d92e233d60e01b8252600482fd5b50346102435780600319360112610243576001546040516001600160a01b039091168152602090f35b50346102435780600319360112610243576020604051620f42408152f35b5034610243576020366003190112610243576020906040906001600160a01b036103a6611431565b168152600883522054604051908152f35b5034610243576020366003190112610243576103d96103d4611431565b6115b2565b620f42400390620f424082116103f457602082604051908152f35b634e487b7160e01b81526011600452602490fd5b503461024357602036600319011261024357604061023f91610428611431565b6104306116b9565b506001600160a01b031681526004602052206040519060ff906003906101ea84611473565b50346102435760603660031901126102435761046f611431565b610477611447565b61047f61145d565b9161048861176f565b3384526009602090815260408086206001600160a01b038581165f908152918452828220908716825290925290205491826104c7575b84600160025580f35b3385526009602090815260408087206001600160a01b039384165f81815291845282822094881682529383522086905590917f3115d1449a7b732c986cba18244e897a450f61e1bb8d589cd2e69e6c8924f9f7919061052785828661172d565b6040519485526001600160a01b03908116951693a45f8080806104be565b50346102435780600319360112610243576020604051620186a08152f35b503461024357806003193601126102435760206040516207a1208152f35b50346102435760603660031901126102435761059b611431565b60243590604435906105ab61176f565b6105b683338361178d565b60018060a01b0316908184526008602052604084206105d6828254611621565b90556105ed6105e58483611621565b303385611890565b60405192835260208301527fdcdcaef5de7ddddb7ef392b97e6b32c1c43fd5f5ed468fd987baaa3c5252f5f560403393a3600160025580f35b50346102435760203660031901126102435760043567ffffffffffffffff81116107e857610658903690600401611527565b90602060405161066882826114a3565b84815281810191601f19810136843761068085611679565b9361068e60405195866114a3565b858552601f1961069d87611679565b01875b8181106107d957505036819003601e190190875b87811015610761578060051b8201358381121561075d5782019081359167ffffffffffffffff83116107595785018a8336038213610243578061073d92896107296001978b8e6040519483869484860198893784019083820190898252519283915e010185815203601f1981018352826114a3565b5190305af46107366118d4565b90306119ba565b610747828a611691565b526107528189611691565b50016106b4565b8a80fd5b8980fd5b88848860405191808301818452825180915260408401918060408360051b870101940192865b8388106107945786860387f35b9091929394838080600193603f198b8203018752818a518051918291828552018484015e86838284010152601f80199101160101970193019701969093929193610787565b606087820185015283016106a0565b5080fd5b503461024357604036600319011261024357610806611431565b6024359061081261176f565b6001600160a01b03168083526008602052604083208054610834908490611621565b905561084282303384611890565b6040519183835260208301527fdcdcaef5de7ddddb7ef392b97e6b32c1c43fd5f5ed468fd987baaa3c5252f5f560403393a3600160025580f35b50346102435760203660031901126102435760206108a061089b611431565b611642565b604051908152f35b5034610243578060031936011261024357546040516001600160a01b039091168152602090f35b5034610243576020366003190112610243576020906040906001600160a01b036108f7611431565b168152600783522054604051908152f35b503461024357604036600319011261024357610922611431565b6024359061092e61176f565b61093982338361178d565b6001600160a01b031661094e82303384611890565b6040519182528260208301527fdcdcaef5de7ddddb7ef392b97e6b32c1c43fd5f5ed468fd987baaa3c5252f5f560403393a3600160025580f35b5034610243576020366003190112610243576109a2611431565b6109aa611707565b6001600160a01b031680825260056020908152604080842083855260049092528320818103610ab8575b5050808252600460205260036040832001600160ff198254161790558082526005602052816003604082208281558260018201558260028201550155803b156107e857604051633593b99960e11b81528290818160048183875af18015610aad57610a98575b5081905260046020527fbf01b2316019e2ee671136646a19eac78b4c315c1efe3ea439fcb90f3403bccc60806040842060ff60036040519280548452600181015460208501526002810154604085015201541615156060820152a280f35b81610aa2916114a3565b6107e857815f610a3a565b6040513d84823e3d90fd5b600360ff8184610aef95548555600181015460018601556002810154600286015501541691019060ff801983541691151516179055565b5f806109d4565b503461024357606036600319011261024357610b10611431565b610b18611447565b9160443567ffffffffffffffff81116107e857610b3a84913690600401611527565b9293909184916001600160a01b039182169116825b85871015610bae57818452600960205260408420835f5260205260405f208760051b86013560018060a01b0381168103610baa5791610ba291600193848060a01b03165f5260205260405f205490611621565b960195610b4f565b8580fd5b602090604051908152f35b5034610243578060031936011261024357600154336001600160a01b03821603610c26576001600160a01b0319908116600155815433918116821783556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b63118cdaa760e01b825233600452602482fd5b503461024357604036600319011261024357610c53611431565b6040516304b31bb160e41b81526001600160a01b039091169060243590602081600481865afa908115610d6c578491610d3d575b506001600160a01b03163303610d2f578183526007602052806040842055818352600360205260408320600160ff19825416179055813b15610d2b57604051633593b99960e11b81528390818160048183885af18015610aad57610d16575b505060207f4e96bd64bffdce6e304c896a5a4378c91accc1cb7603d0caca264163ba7d7f0b91604051908152a280f35b81610d20916114a3565b610d2b57825f610ce6565b8280fd5b6282b42960e81b8352600483fd5b610d5f915060203d602011610d65575b610d5781836114a3565b810190611558565b5f610c87565b503d610d4d565b6040513d86823e3d90fd5b5034610243578060031936011261024357600490610d93611707565b6282b42960e81b8152fd5b503461024357602036600319011261024357610db8611431565b610dc061176f565b6006546001600160a01b03163303610e62576001600160a01b03168082526008602052604082205490829082610dfa575b50600160025580f35b808252600860205260408220829055600654610e219084906001600160a01b03168361172d565b6006546040519384526001600160a01b0316927f3115d1449a7b732c986cba18244e897a450f61e1bb8d589cd2e69e6c8924f9f790602090a45f8181610df1565b6282b42960e81b8252600482fd5b503461024357606036600319011261024357610e8a611431565b906040610e95611447565b91610e9e61145d565b9360018060a01b031681526009602052209060018060a01b03165f5260205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b503461024357610eed366114c5565b90610ef6611707565b610eff826116dd565b60018060a01b0316908183526004602052610f4b6040842082518155602083015160018201556040830151600282015560036060840151151591019060ff801983541691151516179055565b818352600460205260408320600301805460ff19166001179055813b15610d2b57604051633593b99960e11b81528390818160048183885af18015610aad57610fef575b5050610fe97fbf01b2316019e2ee671136646a19eac78b4c315c1efe3ea439fcb90f3403bccc9160405191829182919091606080608083019480518452602081015160208501526040810151604085015201511515910152565b0390a280f35b81610ff9916114a3565b610d2b57825f610f8f565b50346102435780600319360112610243576006546040516001600160a01b039091168152602090f35b50346102435760203660031901126102435760206108a061104c611431565b6115ed565b50346102435760603660031901126102435761109061106e611431565b611076611447565b61107e611707565b604435916001600160a01b031661172d565b80f35b50346102435760203660031901126102435760209060ff906040906001600160a01b036110be611431565b168152600384522054166040519015158152f35b50346102435760203660031901126102435760206108a06103d4611431565b50346102435760203660031901126102435760206108a0611110611431565b611577565b503461024357611124366114c5565b6040516304b31bb160e41b81526001600160a01b0390921691602081600481865afa908115610d6c578491611208575b506001600160a01b03163303610d2f57610fe9816111927f857ba4306a88d9ff795171bf47361c59df7f9adee5ededf3b76f7e34e9207d94936116dd565b83855260056020526111d56040862082518155602083015160018201556040830151600282015560036060840151151591019060ff801983541691151516179055565b60405191829182919091606080608083019480518452602081015160208501526040810151604085015201511515910152565b611221915060203d602011610d6557610d5781836114a3565b5f611154565b50346102435760203660031901126102435760043563ffffffff60e01b81168091036107e85760209063a02803bf60e01b811490811561126d575b506040519015158152f35b6301ffc9a760e01b14905082611262565b9050346113da5760403660031901126113da57611299611431565b6304b31bb160e41b82526001600160a01b03169060243590602081600481865afa9081156113cf575f91611412575b506001600160a01b03163303611404576207a12081116113f557815f52600460205261136260405f2060036040519161130083611473565b805483526002810154604084019081529082015460ff1615801560608501908152919060208501906113de578681525b875f52600460205260405f209451855551600185015551600284015551151591019060ff801983541691151516179055565b813b156113da57604051633593b99960e11b81525f8160048183875af180156113cf576113b9575b5060207fbc590c47ab5b0f493568e4bbad2ca8bb15683285a437d974e8157d7d812716d091604051908152a280f35b6113c69193505f906114a3565b5f91602061138a565b6040513d5f823e3d90fd5b5f80fd5b86815261271085526207a120825260018352611330565b630f14508d60e41b5f5260045ffd5b6282b42960e81b5f5260045ffd5b61142b915060203d602011610d6557610d5781836114a3565b5f6112c8565b600435906001600160a01b03821682036113da57565b602435906001600160a01b03821682036113da57565b604435906001600160a01b03821682036113da57565b6080810190811067ffffffffffffffff82111761148f57604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff82111761148f57604052565b9060a06003198301126113da576004356001600160a01b03811681036113da576080909260231901126113da576040516114fe81611473565b60243581526044356020820152606435604082015260843580151581036113da57606082015290565b9181601f840112156113da5782359167ffffffffffffffff83116113da576020808501948460051b0101116113da57565b908160209103126113da57516001600160a01b03811681036113da5790565b6001600160a01b03165f8181526004602052604090206003015460ff166115a05750620186a090565b5f526004602052600160405f20015490565b6001600160a01b03165f8181526004602052604090206003015460ff166115db57506207a12090565b5f526004602052600260405f20015490565b6001600160a01b03165f8181526003602052604090205460ff166116125750614e2090565b5f52600760205260405f205490565b9190820180921161162e57565b634e487b7160e01b5f52601160045260245ffd5b6001600160a01b03165f8181526004602052604090206003015460ff1661166a575061271090565b5f52600460205260405f205490565b67ffffffffffffffff811161148f5760051b60200190565b80518210156116a55760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b604051906116c682611473565b5f6060838281528260208201528260408201520152565b6207a1206020820151116113f557620186a09051116116f857565b630989d05560e31b5f5260045ffd5b5f546001600160a01b0316330361171a57565b63118cdaa760e01b5f523360045260245ffd5b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448083019390935291815261176d916117686064836114a3565b611913565b565b600280541461177e5760028055565b633ee5aeb560e01b5f5260045ffd5b6040516304b31bb160e41b8152602081600481335afa9081156113cf575f91611871575b506001600160a01b03168015611862576117ca836115b2565b611830575f935b60018060a01b0383165f5260086020526117f060405f20918254611621565b90555f52600960205260405f209060018060a01b03165f5260205260405f209060018060a01b03165f5260205261182c60405f20918254611621565b9055565b61184261183c846115b2565b85611987565b93848103908111156117d157634e487b7160e01b5f52601160045260245ffd5b63d92e233d60e01b5f5260045ffd5b61188a915060203d602011610d6557610d5781836114a3565b5f6117b1565b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815261176d916117686084836114a3565b3d1561190e573d9067ffffffffffffffff821161148f5760405191611903601f8201601f1916602001846114a3565b82523d5f602084013e565b606090565b5f8061193b9260018060a01b03169360208151910182865af16119346118d4565b90836119ba565b8051908115159182611963575b50506119515750565b635274afe760e01b5f5260045260245ffd5b81925090602091810103126113da57602001518015908115036113da575f80611948565b9080156119b4578061199d620f42409284611a18565b92096119a65790565b6001810180911161162e5790565b50505f90565b906119de57508051156119cf57805190602001fd5b630a12f52160e11b5f5260045ffd5b81511580611a0f575b6119ef575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156119e7565b9190915f838202915f1985820991838084109303928084039314611a8d5782620f42401115611a7e57507fde8f6cefed634549b62c77574f722e1ac57e23f24d8fd5cb790fb65668c261399394620f4240910990828211900360fa1b910360061c170290565b63227bc15360e01b8152600490fd5b505050620f4240919250049056fea26469706673582212202bf05220bde64db62d01fc895c9d3ff282a5686364d585a3c9b1228b4ad5aea864736f6c634300081b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000004b07aaa370189e5603df56c84f59c5a59181bfb1000000000000000000000000bc83ff5d65a7454adbaffd6ddc5695d978e1d50d
-----Decoded View---------------
Arg [0] : treasury_ (address): 0x4B07AaA370189E5603DF56C84f59c5A59181BFB1
Arg [1] : owner_ (address): 0xBC83FF5d65a7454adBAFfD6DDc5695D978e1d50d
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000004b07aaa370189e5603df56c84f59c5a59181bfb1
Arg [1] : 000000000000000000000000bc83ff5d65a7454adbaffd6ddc5695d978e1d50d
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$1,140.45
Net Worth in MON
Token Allocations
USDC
100.00%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| MONAD | 100.00% | $0.999708 | 1,140.7832 | $1,140.45 |
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.