diff --git a/outputz.txt b/outputz.txt new file mode 100644 index 0000000..40a0494 --- /dev/null +++ b/outputz.txt @@ -0,0 +1,1520 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.15; + + + + + + + + + +interface IRewarder { + function onReward( + uint relicId, + uint rewardAmount, + address to, + uint amount, + uint oldLevel, + uint newLevel + ) external; + + function onDeposit( + uint relicId, + uint depositAmount, + uint oldAmount, + uint oldLevel, + uint newLevel + ) external; + + function onWithdraw( + uint relicId, + uint withdrawalAmount, + uint oldAmount, + uint oldLevel, + uint newLevel + ) external; + + function onSplit( + uint fromId, + uint newId, + uint amount, + uint fromAmount, + uint level + ) external; + + function onShift( + uint fromId, + uint toId, + uint amount, + uint oldFromAmount, + uint oldToAmount, + uint fromLevel, + uint oldToLevel, + uint newToLevel + ) external; + + function onMerge( + uint fromId, + uint toId, + uint fromAmount, + uint toAmount, + uint fromLevel, + uint oldToLevel, + uint newToLevel + ) external; + + function pendingTokens( + uint relicId, + uint rewardAmount + ) external view returns (address[] memory, uint[] memory); +} + + +interface IRollingRewarder is IRewarder { + + function fund() external; + + function setRewardsPool(address _rewardsPool) external; +} + + + + + + + +/// @title Child rewarder contract to be deployed and called by a ParentRewarder, rather than directly by the Reliquary. +abstract contract ChildRewarder is IRewarder { + /// @notice Address of ParentRewarder which deployed this contract + address public immutable parent; + + modifier onlyParent() { + require( + msg.sender == address(parent), + "Only parent can call this function." + ); + _; + } + + /** + * @dev Contructor called on deployment of this contract. + */ + constructor() { + parent = msg.sender; + } +} + + + + + + + +abstract contract SingleAssetRewarder is IRewarder { + address public rewardToken; + address public immutable reliquary; + + /// @dev Limits function calls to address of Reliquary contract `reliquary` + modifier onlyReliquary() { + require(msg.sender == reliquary, "Only Reliquary can call this function."); + _; + } + + /** + * @dev Contructor called on deployment of this contract. + * @param _rewardToken Address of token rewards are distributed in. + * @param _reliquary Address of Reliquary this rewarder will read state from. + */ + constructor(address _rewardToken, address _reliquary) { + rewardToken = _rewardToken; + reliquary = _reliquary; + } + + /** + * @notice Called by Reliquary harvest or withdrawAndHarvest function. + * @param rewardAmount Amount of reward token owed for this position from the Reliquary. + * @param to Address to send rewards to. + */ + function onReward( + uint relicId, + uint rewardAmount, + address to, + uint oldAmount, + uint oldLevel, + uint newLevel + ) external virtual override {} + + /// @notice Called by Reliquary _deposit function. + function onDeposit( + uint relicId, + uint depositAmount, + uint oldAmount, + uint oldLevel, + uint newLevel + ) external virtual override {} + + /// @notice Called by Reliquary withdraw or withdrawAndHarvest function. + function onWithdraw( + uint relicId, + uint withdrawalAmount, + uint oldAmount, + uint oldLevel, + uint newLevel + ) external virtual override {} + + /** + * @notice Returns the amount of pending tokens for a position from this rewarder. + * Interface supports multiple tokens. + * @param rewardAmount Amount of reward token owed for this position from the Reliquary. + */ + function pendingTokens(uint relicId, uint rewardAmount) + external + view + virtual + override + returns (address[] memory rewardTokens, uint[] memory rewardAmounts) + { + rewardTokens = new address[](1); + rewardTokens[0] = rewardToken; + + rewardAmounts = new uint[](1); + rewardAmounts[0] = pendingToken(relicId, rewardAmount); + } + + /// @notice Returns the amount of pending rewardToken for a position from this rewarder. + /// @param rewardAmount Amount of reward token owed for this position from the Reliquary. + function pendingToken(uint relicId, uint rewardAmount) public view virtual returns (uint pending) {} +} + + + + + +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol) + + + + +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol) + + + + +// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) + + + +/** + * @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); +} + + +/** + * @dev Required interface of an ERC721 compliant contract. + */ +interface IERC721 is IERC165 { + /** + * @dev Emitted when `tokenId` token is transferred from `from` to `to`. + */ + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. + */ + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. + */ + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + /** + * @dev Returns the number of tokens in ``owner``'s account. + */ + function balanceOf(address owner) external view returns (uint256 balance); + + /** + * @dev Returns the owner of the `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function ownerOf(uint256 tokenId) external view returns (address owner); + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes calldata data + ) external; + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC721 protocol to prevent tokens from being forever locked. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /** + * @dev Transfers `tokenId` token from `from` to `to`. + * + * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 + * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must + * understand this adds an external call which potentially creates a reentrancy vulnerability. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 tokenId + ) external; + + /** + * @dev Gives permission to `to` to transfer `tokenId` token to another account. + * The approval is cleared when the token is transferred. + * + * Only a single account can be approved at a time, so approving the zero address clears previous approvals. + * + * Requirements: + * + * - The caller must own the token or be an approved operator. + * - `tokenId` must exist. + * + * Emits an {Approval} event. + */ + function approve(address to, uint256 tokenId) external; + + /** + * @dev Approve or remove `operator` as an operator for the caller. + * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. + * + * Requirements: + * + * - The `operator` cannot be the caller. + * + * Emits an {ApprovalForAll} event. + */ + function setApprovalForAll(address operator, bool _approved) external; + + /** + * @dev Returns the account approved for `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function getApproved(uint256 tokenId) external view returns (address operator); + + /** + * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. + * + * See {setApprovalForAll} + */ + function isApprovedForAll(address owner, address operator) external view returns (bool); +} + + +/** + * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Enumerable is IERC721 { + /** + * @dev Returns the total amount of tokens stored by the contract. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns a token ID owned by `owner` at a given `index` of its token list. + * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. + */ + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); + + /** + * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. + * Use along with {totalSupply} to enumerate all tokens. + */ + function tokenByIndex(uint256 index) external view returns (uint256); +} + + + + + +interface IGauge { + function notifyRewardAmount(address token, uint amount) external; + function getReward(address account, address[] memory tokens) external; + function getReward(address account) external; + function getReward() external; + function claimFees() external returns (uint claimed0, uint claimed1); + function left(address token) external view returns (uint); + function rewardRate(address _pair) external view returns (uint); + function balanceOf(address _account) external view returns (uint); + function isForPair() external view returns (bool); + function totalSupply() external view returns (uint); + function earned(address token, address account) external view returns (uint); + function deposit(uint256 amount) external; + function withdraw(uint256 amount) external; +} + + +/** + * @notice Info for each Reliquary position. + * `amount` LP token amount the position owner has provided. + * `rewardDebt` Amount of reward token accumalated before the position's entry or last harvest. + * `rewardCredit` Amount of reward token owed to the user on next harvest. + * `entry` Used to determine the maturity of the position. + * `poolId` ID of the pool to which this position belongs. + * `level` Index of this position's level within the pool's array of levels. + */ +struct PositionInfo { + uint amount; + uint rewardDebt; + uint rewardCredit; + uint entry; // position owner's relative entry into the pool. + uint poolId; // ensures that a single Relic is only used for one pool. + uint level; +} + +/** + * @notice Info of each Reliquary pool. + * `accRewardPerShare` Accumulated reward tokens per share of pool (1 / 1e12). + * `lastRewardTime` Last timestamp the accumulated reward was updated. + * `allocPoint` Pool's individual allocation - ratio of the total allocation. + * `name` Name of pool to be displayed in NFT image. + * `allowPartialWithdrawals` Whether users can withdraw less than their entire position. + * A value of false will also disable shift and split functionality. + */ +struct PoolInfo { + uint accRewardPerShare; + uint lastRewardTime; + uint allocPoint; + string name; + bool allowPartialWithdrawals; + GaugeInfo gaugeInfo; // Gauge info (does this pool have a gauge and where is it) +} + +/** + * @notice Info for gauges + * `isGauge` Indicates if the pool is a gauge. + * `gauge` Corresponding gauge contract. + */ +struct GaugeInfo { + bool isGauge; + IGauge gauge; +} + +/** + * @notice Info for each level in a pool that determines how maturity is rewarded. + * `requiredMaturities` The minimum maturity (in seconds) required to reach each Level. + * `multipliers` Multiplier for each level applied to amount of incentivized token when calculating rewards in the pool. + * This is applied to both the numerator and denominator in the calculation such that the size of a user's position + * is effectively considered to be the actual number of tokens times the multiplier for their level. + * Also note that these multipliers do not affect the overall emission rate. + * `balance` Total (actual) number of tokens deposited in positions at each level. + */ +struct LevelInfo { + uint[] requiredMaturities; + uint[] multipliers; + uint[] balance; +} + +/** + * @notice Object representing pending rewards and related data for a position. + * `relicId` The NFT ID of the given position. + * `poolId` ID of the pool to which this position belongs. + * `pendingReward` pending reward amount for a given position. + */ +struct PendingReward { + uint relicId; + uint poolId; + uint pendingReward; +} + +interface IReliquary is IERC721Enumerable { + function setEmissionCurve(address _emissionCurve) external; + function addPool( + uint allocPoint, + address _poolToken, + address _rewarder, + uint[] calldata requiredMaturity, + uint[] calldata levelMultipliers, + string memory name, + address _nftDescriptor, + bool allowPartialWithdrawals + ) external; + function modifyPool( + uint pid, + uint allocPoint, + address _rewarder, + string calldata name, + address _nftDescriptor, + bool overwriteRewarder + ) external; + function massUpdatePools(uint[] calldata pids) external; + function updatePool(uint pid) external; + function deposit(uint amount, uint relicId) external; + function withdraw(uint amount, uint relicId) external; + function harvest(uint relicId, address harvestTo) external; + function withdrawAndHarvest(uint amount, uint relicId, address harvestTo) external; + function emergencyWithdraw(uint relicId) external; + function updatePosition(uint relicId) external; + function getPositionForId(uint) external view returns (PositionInfo memory); + function getPoolInfo(uint) external view returns (PoolInfo memory); + function getLevelInfo(uint) external view returns (LevelInfo memory); + function isApprovedOrOwner(address, uint) external view returns (bool); + function createRelicAndDeposit(address to, uint pid, uint amount) external returns (uint id); + function split(uint relicId, uint amount, address to) external returns (uint newId); + function shift(uint fromId, uint toId, uint amount) external; + function merge(uint fromId, uint toId) external; + function burn(uint tokenId) external; + function pendingReward(uint relicId) external view returns (uint pending); + function levelOnUpdate(uint relicId) external view returns (uint level); + function poolLength() external view returns (uint); + + function rewardToken() external view returns (address); + function nftDescriptor(uint) external view returns (address); + function emissionCurve() external view returns (address); + function poolToken(uint) external view returns (address); + function rewarder(uint) external view returns (address); + function totalAllocPoint() external view returns (uint); + function updatePoolWithGaugeDeposit(uint256 pid) external; +} + + + + + +interface IEmissionCurve { + function getRate(uint lastRewardTime) external view returns (uint rate); +} + + +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) + + + + +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) + + + +/** + * @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 amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); +} + + +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) + + + +/** + * @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. + */ +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]. + */ + 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); +} + + +// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) + + + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + * + * [IMPORTANT] + * ==== + * You shouldn't rely on `isContract` to protect against flash loan attacks! + * + * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets + * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract + * constructor. + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize/address.code.length, which returns 0 + // for contracts in construction, since the code is only stored at the end + // of the constructor execution. + + return account.code.length > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + (bool success, ) = recipient.call{value: amount}(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain `call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + (bool success, bytes memory returndata) = target.call{value: value}(data); + return verifyCallResultFromTarget(target, success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + (bool success, bytes memory returndata) = target.staticcall(data); + return verifyCallResultFromTarget(target, success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + (bool success, bytes memory returndata) = target.delegatecall(data); + return verifyCallResultFromTarget(target, success, returndata, errorMessage); + } + + /** + * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling + * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. + * + * _Available since v4.8._ + */ + function verifyCallResultFromTarget( + address target, + bool success, + bytes memory returndata, + string memory errorMessage + ) internal view returns (bytes memory) { + if (success) { + if (returndata.length == 0) { + // only check isContract if the call was successful and the return data is empty + // otherwise we already know that it was a contract + require(isContract(target), "Address: call to non-contract"); + } + return returndata; + } else { + _revert(returndata, errorMessage); + } + } + + /** + * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the + * revert reason or using the provided one. + * + * _Available since v4.3._ + */ + function verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) internal pure returns (bytes memory) { + if (success) { + return returndata; + } else { + _revert(returndata, errorMessage); + } + } + + function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage); + } + } +} + + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using Address for address; + + function safeTransfer( + IERC20 token, + address to, + uint256 value + ) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom( + IERC20 token, + address from, + address to, + uint256 value + ) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + } + + /** + * @dev Deprecated. This function has issues similar to the ones found in + * {IERC20-approve}, and its usage is discouraged. + * + * Whenever possible, use {safeIncreaseAllowance} and + * {safeDecreaseAllowance} instead. + */ + function safeApprove( + IERC20 token, + address spender, + uint256 value + ) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + require( + (value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + function safeIncreaseAllowance( + IERC20 token, + address spender, + uint256 value + ) internal { + uint256 newAllowance = token.allowance(address(this), spender) + value; + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + function safeDecreaseAllowance( + IERC20 token, + address spender, + uint256 value + ) internal { + unchecked { + uint256 oldAllowance = token.allowance(address(this), spender); + require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); + uint256 newAllowance = oldAllowance - value; + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + } + + function safePermit( + IERC20Permit token, + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) internal { + uint256 nonceBefore = token.nonces(owner); + token.permit(owner, spender, value, deadline, v, r, s); + uint256 nonceAfter = token.nonces(owner); + require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that + // the target address contains contract code and also asserts for success in the low-level call. + + bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); + if (returndata.length > 0) { + // Return data is optional + require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); + } + } +} + + +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) + + + + + +/** + * @dev Interface for the optional metadata functions from the ERC20 standard. + * + * _Available since v4.1._ + */ +interface IERC20Metadata is IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} + + +// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) + + + + +// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) + + + +/** + * @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; + } +} + + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * By default, the owner account will be the one that deploys the contract. This + * can later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +abstract contract Ownable is Context { + address private _owner; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the deployer as the initial owner. + */ + constructor() { + _transferOwnership(_msgSender()); + } + + /** + * @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 { + require(owner() == _msgSender(), "Ownable: caller is not the owner"); + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions anymore. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby removing any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + _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 { + require(newOwner != address(0), "Ownable: new owner is the zero address"); + _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); + } +} + + +/// @title Rewarder that can be funded with a set token, distributing it over a period of time. +contract RollingRewarder is IRollingRewarder, SingleAssetRewarder, ChildRewarder, Ownable { + using SafeERC20 for IERC20; + + uint256 public immutable ACC_REWARD_PRECISION = 1e18; + uint256 public immutable REWARD_PER_SECOND_PRECISION = 10_000; + + uint256 public immutable poolId; + address public rewardsPool; + + uint256 public lastDistributionTime; + uint256 public distributionPeriod; + uint256 public lastIssuanceTimestamp; + uint256 public totalIssued; + + uint256 public _rewardPerSecond; + uint256 public accRewardPerShare; + uint[] private multipliers; + + mapping(uint256 => uint256) public rewardDebt; + mapping(uint256 => uint256) public rewardCredit; + + event LogOnReward(uint relicId, uint rewardAmount, address to); + event UpdateDistributionPeriod(uint256 newDistributionPeriod); + + /** + * @dev Contructor called on deployment of this contract. + * @param _rewardToken Address of token rewards are distributed in. + * @param _reliquary Address of Reliquary this rewarder will read state from. + */ + constructor( + address _rewardToken, + address _reliquary, + uint256 _poolId + ) SingleAssetRewarder(_rewardToken, _reliquary) { + poolId = _poolId; + + multipliers = IReliquary(_reliquary).getLevelInfo(_poolId).multipliers; + + _updateDistributionPeriod(7 days); + } + + /** + * @notice Called by Reliquary harvest or withdrawAndHarvest function. + * @param to Address to send rewards to. + */ + function onReward( + uint relicId, + uint, // rewardAmount + address to, + uint amount, + uint oldLevel, + uint newLevel + ) external virtual override(IRewarder, SingleAssetRewarder) onlyParent { + uint256 oldAmountMultiplied = amount * multipliers[oldLevel]; + uint256 newAmountMultiplied = amount * multipliers[newLevel]; + + _issueTokens(_poolBalance()); + + uint256 pending = ((oldAmountMultiplied * accRewardPerShare) / + ACC_REWARD_PRECISION) - rewardDebt[relicId]; + pending += rewardCredit[relicId]; + + rewardCredit[relicId] = 0; + + rewardDebt[relicId] = ((newAmountMultiplied * accRewardPerShare) / + ACC_REWARD_PRECISION); + if (pending > 0) { + IERC20(rewardToken).safeTransfer(to, pending); + emit LogOnReward(relicId, pending, to); + } + } + + function onDeposit( + uint relicId, + uint depositAmount, + uint oldAmount, + uint oldLevel, + uint newLevel + ) external virtual override(IRewarder, SingleAssetRewarder) onlyParent { + uint256 oldAmountMultiplied = oldAmount * multipliers[oldLevel]; + uint256 newAmountMultiplied = (oldAmount + depositAmount) * + multipliers[newLevel]; + + _issueTokens(_poolBalance()); + + rewardCredit[relicId] += + ((oldAmountMultiplied * accRewardPerShare) / ACC_REWARD_PRECISION) - + rewardDebt[relicId]; + rewardDebt[relicId] = ((newAmountMultiplied * accRewardPerShare) / + ACC_REWARD_PRECISION); + } + + function onWithdraw( + uint relicId, + uint withdrawalAmount, + uint oldAmount, + uint oldLevel, + uint newLevel + ) external virtual override(IRewarder, SingleAssetRewarder) onlyParent { + uint256 oldAmountMultiplied = oldAmount * multipliers[oldLevel]; + uint256 newAmountMultiplied = (oldAmount - withdrawalAmount) * + multipliers[newLevel]; + + _issueTokens(_poolBalance()); + + rewardCredit[relicId] += + (oldAmountMultiplied * accRewardPerShare) / + ACC_REWARD_PRECISION - + rewardDebt[relicId]; + rewardDebt[relicId] = ((newAmountMultiplied * accRewardPerShare) / + ACC_REWARD_PRECISION); + } + + function onSplit( + uint fromId, + uint newId, + uint amount, + uint fromAmount, + uint level + ) external virtual onlyParent { + _issueTokens(_poolBalance()); + uint256 _multiplier = multipliers[level]; + rewardCredit[fromId] += + ((fromAmount * _multiplier * accRewardPerShare) / + ACC_REWARD_PRECISION) - + rewardDebt[fromId]; + rewardDebt[fromId] = (((fromAmount - amount) * + _multiplier * + accRewardPerShare) / ACC_REWARD_PRECISION); + rewardDebt[newId] = ((amount * _multiplier * accRewardPerShare) / + ACC_REWARD_PRECISION); + } + + function onShift( + uint fromId, + uint toId, + uint amount, + uint oldFromAmount, + uint oldToAmount, + uint fromLevel, + uint oldToLevel, + uint newToLevel + ) external virtual onlyParent { + uint256 _multiplierFrom = multipliers[fromLevel]; + + _issueTokens(_poolBalance()); + + rewardCredit[fromId] += + ((oldFromAmount * _multiplierFrom * accRewardPerShare) / + ACC_REWARD_PRECISION) - + rewardDebt[fromId]; + rewardDebt[fromId] = (((oldFromAmount - amount) * + _multiplierFrom * + accRewardPerShare) / ACC_REWARD_PRECISION); + rewardCredit[toId] += + ((oldToAmount * multipliers[oldToLevel] * accRewardPerShare) / + ACC_REWARD_PRECISION) - + rewardDebt[toId]; + rewardDebt[toId] = (((oldToAmount + amount) * + multipliers[newToLevel] * + accRewardPerShare) / ACC_REWARD_PRECISION); + } + + function onMerge( + uint fromId, + uint toId, + uint fromAmount, + uint toAmount, + uint fromLevel, + uint oldToLevel, + uint newToLevel + ) external virtual onlyParent { + uint fromAmountMultiplied = fromAmount * multipliers[fromLevel]; + uint oldToAmountMultiplied = toAmount * multipliers[oldToLevel]; + uint newToAmountMultiplied = (toAmount + fromAmount) * + multipliers[newToLevel]; + + _issueTokens(_poolBalance()); + + uint pendingTo = (accRewardPerShare * + (fromAmountMultiplied + oldToAmountMultiplied)) / + ACC_REWARD_PRECISION + + rewardCredit[fromId] - + rewardDebt[fromId] - + rewardDebt[toId]; + if (pendingTo != 0) { + rewardCredit[toId] += pendingTo; + } + + rewardCredit[fromId] = 0; + + rewardDebt[toId] = + (newToAmountMultiplied * accRewardPerShare) / + ACC_REWARD_PRECISION; + } + + /// @notice Returns the amount of pending rewardToken for a position from this rewarder. + function pendingToken( + uint relicId, + uint // rewardAmount + ) + public + view + override(SingleAssetRewarder) + returns (uint amount) + { + uint256 poolBalance = _poolBalance(); + uint256 _lastIssuanceTimestamp = lastIssuanceTimestamp; //last time token was distributed + uint256 _lastDistributionTime = lastDistributionTime; //timestamp of the final distribution of tokens + uint256 _totalIssued = totalIssued; //how many tokens to issue + uint256 newAccReward; + if (_lastIssuanceTimestamp < _lastDistributionTime) { + uint256 endTimestamp = block.timestamp > _lastDistributionTime + ? _lastDistributionTime + : block.timestamp; + uint256 timePassed = endTimestamp - _lastIssuanceTimestamp; + uint256 issuance = getRewardAmount(timePassed); + if (poolBalance != 0) { + newAccReward = accRewardPerShare + + (issuance * ACC_REWARD_PRECISION) / + poolBalance; + + _totalIssued = _totalIssued + issuance; + } + } + + PositionInfo memory position = IReliquary(reliquary).getPositionForId(relicId); + + uint256 amountMultiplied = position.amount * multipliers[position.level]; + + uint256 pending = ((amountMultiplied * newAccReward) / + ACC_REWARD_PRECISION) - rewardDebt[relicId]; + pending += rewardCredit[relicId]; + + amount = pending; + } + + function _updateDistributionPeriod( + uint256 _newDistributionPeriod + ) internal { + distributionPeriod = _newDistributionPeriod; + emit UpdateDistributionPeriod(_newDistributionPeriod); + } + + function updateDistributionPeriod( + uint256 _newDistributionPeriod + ) external onlyOwner { + _updateDistributionPeriod(_newDistributionPeriod); + } + + function rewardPerSecond() public view returns (uint256) { + return _rewardPerSecond; + } + + function getRewardAmount(uint seconds_) public view returns (uint256) { + return ((_rewardPerSecond * seconds_) / REWARD_PER_SECOND_PRECISION); + } + + function _fund(uint256 _amount) internal { + require(_amount != 0, "cannot fund 0"); + + uint256 _lastIssuanceTimestamp = lastIssuanceTimestamp; //last time token was distributed + uint256 _lastDistributionTime = lastDistributionTime; //timestamp of the final distribution of tokens + uint256 amount = _amount; //amount of tokens to add to the distribution + if (_lastIssuanceTimestamp < _lastDistributionTime) { + uint256 timeLeft = _lastDistributionTime - _lastIssuanceTimestamp; //time left until final distribution + uint256 notIssued = getRewardAmount(timeLeft); //how many tokens are left to issue + amount += notIssued; // add to the funding amount that hasnt been issued + } + + uint256 _distributionPeriod = distributionPeriod; //how many days will we distribute these assets over + _rewardPerSecond = + (amount * REWARD_PER_SECOND_PRECISION) / + _distributionPeriod; //how many tokens per second will be distributed + lastDistributionTime = block.timestamp + _distributionPeriod; //when will the new final distribution be + lastIssuanceTimestamp = block.timestamp; //when was the last time tokens were distributed -- now + + IERC20(rewardToken).safeTransferFrom( + rewardsPool, + address(this), + _amount + ); //transfer the tokens to the contract + } + + /// @notice Issues tokens. + /// @param poolBalance Amount of tokens in the pool. This must be passed because the pool balance may have changed. + /// @return issuance Amount of tokens issued. + function _issueTokens( + uint256 poolBalance + ) internal returns (uint256 issuance) { + uint256 _lastIssuanceTimestamp = lastIssuanceTimestamp; //last time token was distributed + uint256 _lastDistributionTime = lastDistributionTime; //timestamp of the final distribution of tokens + uint256 _totalIssued = totalIssued; //how many tokens to issue + if (_lastIssuanceTimestamp < _lastDistributionTime) { + uint256 endTimestamp = block.timestamp > _lastDistributionTime + ? _lastDistributionTime + : block.timestamp; + uint256 timePassed = endTimestamp - _lastIssuanceTimestamp; + issuance = getRewardAmount(timePassed); + if (poolBalance != 0) { + accRewardPerShare += + (issuance * ACC_REWARD_PRECISION) / + poolBalance; + + _totalIssued = _totalIssued + issuance; + totalIssued = _totalIssued; + } + } + + lastIssuanceTimestamp = block.timestamp; + } + + function _poolBalance() internal view returns (uint256 total) { + LevelInfo memory levelInfo = IReliquary(reliquary).getLevelInfo(poolId); + uint length = levelInfo.balance.length; + for (uint i; i < length; ) { + total += levelInfo.balance[i] * levelInfo.multipliers[i]; + unchecked { + ++i; + } + } + } + + function fund() external { + require(msg.sender == rewardsPool, "only rewards pool can fund"); + _fund(IERC20(rewardToken).balanceOf(rewardsPool)); + } + + function setRewardsPool(address _rewardsPool) external { + require(msg.sender == parent, "only parent can set rewards pool"); + rewardsPool = _rewardsPool; + } +} diff --git a/scripts/After_Add_Pool.s.sol b/scripts/After_Add_Pool.s.sol new file mode 100644 index 0000000..5935eb0 --- /dev/null +++ b/scripts/After_Add_Pool.s.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import {Reliquary} from "contracts/Reliquary.sol"; +import {OwnableCurve} from "contracts/emission_curves/OwnableCurve.sol"; +import {DepositHelperERC4626} from "contracts/helpers/DepositHelperERC4626.sol"; +import {NFTDescriptor, NFTDescriptorPair} from "contracts/nft_descriptors/NFTDescriptorPair.sol"; +import {NFTDescriptorSingle4626} from "contracts/nft_descriptors/NFTDescriptorSingle4626.sol"; +import {ParentRewarderRolling, RollingRewarder} from "contracts/rewarders/ParentRewarder-Rolling.sol"; +import {RewardsPool} from "contracts/rewarders/RewardsPool.sol"; + +contract Deploy is Script { + using stdJson for string; + + struct Pool { + uint allocPoint; + bool allowPartialWithdrawals; + uint[] levelMultipliers; + string name; + address poolToken; + uint[] requiredMaturities; + int rewarderIndex; + string tokenType; + } + + struct ParentRewarder { + uint poolId; + } + + struct Rewarder { + uint parentIndex; + address rewardToken; + } + + bytes32 constant OPERATOR = keccak256("OPERATOR"); + bytes32 constant CHILD_SETTER = keccak256("CHILD_SETTER"); + bytes32 constant REWARD_SETTER = keccak256("REWARD_SETTER"); + + string config; + address multisig; + address reliquary; + RewardsPool rewardsPool; + address[] rewarderAddresses; + ParentRewarderRolling[] parentRewarders; + address nftDescriptorNormal; + + function run() external { + config = vm.readFile("scripts/deploy_conf.json"); + multisig = config.readAddress(".multisig"); + + Pool[] memory pools = abi.decode(config.parseRaw(".pools"), (Pool[])); + reliquary = 0xF512283347C174399Cc3E11492ead8b49BD2712e; + vm.startBroadcast(); + + _deployRewarders(); + + if (multisig != address(0)) { + _renounceRoles(); + } + + + vm.stopBroadcast(); + } + + function _deployRewarders() internal { + address rewardToken = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c; + ParentRewarderRolling parent = ParentRewarderRolling(0xc1Df4fC2B3d672A7152C7cF0C63604dfC192B0f9); // TODO: replace with actual parent contract + address rewarderAddress = parent.createChild(rewardToken, tx.origin); + RollingRewarder(rewarderAddress).updateDistributionPeriod(14 days); + rewardsPool = new RewardsPool(rewardToken, rewarderAddress); + parent.setChildsRewardPool(rewarderAddress, address(rewardsPool)); + rewarderAddresses.push(rewarderAddress); + + } + + + function _renounceRoles() internal { + bytes32 defaultAdminRole = bytes32(0x0000000000000000000000000000000000000000000000000000000000000000); + rewardsPool.transferOwnership(multisig); + ParentRewarderRolling parent = ParentRewarderRolling(0xc1Df4fC2B3d672A7152C7cF0C63604dfC192B0f9); // TODO: replace with actual parent contract + parent.grantRole(defaultAdminRole, multisig); + parent.grantRole(CHILD_SETTER, multisig); + parent.grantRole(REWARD_SETTER, multisig); + parent.renounceRole(defaultAdminRole, tx.origin); + parent.renounceRole(CHILD_SETTER, tx.origin); + parent.renounceRole(REWARD_SETTER, tx.origin); + } +} + + // reliquary.addPool( + // 66, + // 0x42c95788F791a2be3584446854c8d9BB01BE88A9, + // address(rewarder), + // [0], + // [100], + // "HBR Staking", + // nftDescriptor, + // true + // ); diff --git a/scripts/Deploy.s.sol b/scripts/Deploy.s.sol deleted file mode 100644 index 9e328af..0000000 --- a/scripts/Deploy.s.sol +++ /dev/null @@ -1,168 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; -import {Reliquary} from "contracts/Reliquary.sol"; -import {OwnableCurve} from "contracts/emission_curves/OwnableCurve.sol"; -import {DepositHelperERC4626} from "contracts/helpers/DepositHelperERC4626.sol"; -import {NFTDescriptor, NFTDescriptorPair} from "contracts/nft_descriptors/NFTDescriptorPair.sol"; -import {NFTDescriptorSingle4626} from "contracts/nft_descriptors/NFTDescriptorSingle4626.sol"; -import {ParentRewarderRolling} from "contracts/rewarders/ParentRewarder-Rolling.sol"; - -contract Deploy is Script { - using stdJson for string; - - struct Pool { - uint allocPoint; - bool allowPartialWithdrawals; - uint[] levelMultipliers; - string name; - address poolToken; - uint[] requiredMaturities; - int rewarderIndex; - string tokenType; - } - - struct ParentRewarder { - uint poolId; - } - - struct Rewarder { - uint parentIndex; - address rewardToken; - } - - bytes32 constant OPERATOR = keccak256("OPERATOR"); - bytes32 constant EMISSION_CURVE = keccak256("EMISSION_CURVE"); - bytes32 constant CHILD_SETTER = keccak256("CHILD_SETTER"); - bytes32 constant REWARD_SETTER = keccak256("REWARD_SETTER"); - - string config; - address multisig; - Reliquary reliquary; - OwnableCurve emissionCurve; - address[] rewarderAddresses; - ParentRewarderRolling[] parentRewarders; - address nftDescriptorNormal; - address nftDescriptor4626; - address nftDescriptorPair; - address depositHelper4626; - - function run() external { - config = vm.readFile("scripts/deploy_conf.json"); - string memory name = config.readString(".name"); - string memory symbol = config.readString(".symbol"); - multisig = config.readAddress(".multisig"); - address rewardToken = config.readAddress(".rewardToken"); - address thenaToken = config.readAddress(".thenaToken"); - address voter = config.readAddress(".voter"); - address thenaReceiver = config.readAddress(".thenaReceiver"); - uint emissionRate = config.readUint(".emissionRate"); - Pool[] memory pools = abi.decode(config.parseRaw(".pools"), (Pool[])); - - vm.startBroadcast(); - - emissionCurve = new OwnableCurve(emissionRate); - - reliquary = new Reliquary(rewardToken, address(emissionCurve), thenaToken, voter, thenaReceiver, name, symbol); - - - reliquary.grantRole(OPERATOR, tx.origin); - for (uint i = 0; i < pools.length; ++i) { - Pool memory pool = pools[i]; - - address nftDescriptor = _deployHelpers(pool.tokenType); - - reliquary.addPool( - pool.allocPoint, - pool.poolToken, - address(0), - pool.requiredMaturities, - pool.levelMultipliers, - pool.name, - nftDescriptor, - pool.allowPartialWithdrawals - ); - } - - _deployRewarders(); - - if (multisig != address(0)) { - _renounceRoles(); - } - - vm.stopBroadcast(); - } - - function _deployRewarders() internal { - ParentRewarder[] memory parents = abi.decode(config.parseRaw(".parentRewarders"), (ParentRewarder[])); - for (uint i; i < parents.length; ++i) { - ParentRewarder memory parent = parents[i]; - - ParentRewarderRolling newParent = new ParentRewarderRolling( - address(reliquary), parent.poolId - ); - - newParent.grantRole(CHILD_SETTER, tx.origin); - newParent.grantRole(REWARD_SETTER, tx.origin); - parentRewarders.push(newParent); - rewarderAddresses.push(address(newParent)); - - Pool[] memory pools = abi.decode(config.parseRaw(".pools"), (Pool[])); - - reliquary.modifyPool(0, 100, address(newParent), pools[0].name, address(0), true); - } - - Rewarder[] memory rewarders = abi.decode(config.parseRaw(".childRewarders"), (Rewarder[])); - for (uint i; i < rewarders.length; ++i) { - Rewarder memory rewarder = rewarders[i]; - - ParentRewarderRolling parent = parentRewarders[rewarder.parentIndex]; - address rewarderAddress = parent.createChild(rewarder.rewardToken, tx.origin); - rewarderAddresses.push(rewarderAddress); - } - } - - function _deployHelpers(string memory poolTokenType) internal returns (address nftDescriptor) { - bytes32 typeHash = keccak256(bytes(poolTokenType)); - if (typeHash == keccak256("normal")) { - if (nftDescriptorNormal == address(0)) { - nftDescriptorNormal = address(new NFTDescriptor(address(reliquary))); - } - nftDescriptor = nftDescriptorNormal; - } else if (typeHash == keccak256("4626")) { - if (nftDescriptor4626 == address(0)) { - nftDescriptor4626 = address(new NFTDescriptorSingle4626(address(reliquary))); - } - nftDescriptor = nftDescriptor4626; - if (depositHelper4626 == address(0)) { - depositHelper4626 = address(new DepositHelperERC4626(reliquary, config.readAddress(".weth"))); - } - } else if (typeHash == keccak256("pair")) { - if (nftDescriptorPair == address(0)) { - nftDescriptorPair = address(new NFTDescriptorPair(address(reliquary))); - } - nftDescriptor = nftDescriptorPair; - } else { - revert(string.concat("invalid token type ", poolTokenType)); - } - } - - function _renounceRoles() internal { - bytes32 defaultAdminRole = reliquary.DEFAULT_ADMIN_ROLE(); - reliquary.grantRole(defaultAdminRole, multisig); - reliquary.grantRole(OPERATOR, multisig); - reliquary.grantRole(EMISSION_CURVE, multisig); - reliquary.renounceRole(OPERATOR, tx.origin); - reliquary.renounceRole(defaultAdminRole, tx.origin); - emissionCurve.transferOwnership(multisig); - for (uint i; i < parentRewarders.length; ++i) { - parentRewarders[i].grantRole(defaultAdminRole, multisig); - parentRewarders[i].grantRole(CHILD_SETTER, multisig); - parentRewarders[i].grantRole(REWARD_SETTER, multisig); - parentRewarders[i].renounceRole(defaultAdminRole, tx.origin); - parentRewarders[i].renounceRole(CHILD_SETTER, tx.origin); - parentRewarders[i].renounceRole(REWARD_SETTER, tx.origin); - } - } -} diff --git a/scripts/Deploy_Simple.s.sol b/scripts/Deploy_Simple.s.sol new file mode 100644 index 0000000..32a5aa4 --- /dev/null +++ b/scripts/Deploy_Simple.s.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import {Reliquary} from "contracts/Reliquary.sol"; +import {OwnableCurve} from "contracts/emission_curves/OwnableCurve.sol"; +import {DepositHelperERC4626} from "contracts/helpers/DepositHelperERC4626.sol"; +import {NFTDescriptor, NFTDescriptorPair} from "contracts/nft_descriptors/NFTDescriptorPair.sol"; +import {NFTDescriptorSingle4626} from "contracts/nft_descriptors/NFTDescriptorSingle4626.sol"; +import {ParentRewarderRolling, RollingRewarder} from "contracts/rewarders/ParentRewarder-Rolling.sol"; +import {RewardsPool} from "contracts/rewarders/RewardsPool.sol"; + +contract Deploy is Script { + using stdJson for string; + + struct Pool { + uint allocPoint; + bool allowPartialWithdrawals; + uint[] levelMultipliers; + string name; + address poolToken; + uint[] requiredMaturities; + int rewarderIndex; + string tokenType; + } + + struct ParentRewarder { + uint poolId; + } + + struct Rewarder { + uint parentIndex; + address rewardToken; + } + + bytes32 constant OPERATOR = keccak256("OPERATOR"); + bytes32 constant CHILD_SETTER = keccak256("CHILD_SETTER"); + bytes32 constant REWARD_SETTER = keccak256("REWARD_SETTER"); + + string config; + address multisig; + address reliquary; + RewardsPool rewardsPool; + address[] rewarderAddresses; + ParentRewarderRolling[] parentRewarders; + address nftDescriptorNormal; + + function run() external { + config = vm.readFile("scripts/deploy_conf.json"); + multisig = config.readAddress(".multisig"); + + Pool[] memory pools = abi.decode(config.parseRaw(".pools"), (Pool[])); + reliquary = 0xF512283347C174399Cc3E11492ead8b49BD2712e; + vm.startBroadcast(); + for (uint i = 0; i < pools.length; ++i) { + Pool memory pool = pools[i]; + address nftDescriptor = _deployHelpers(pool.tokenType); + } + + _deployRewarders(); + + vm.stopBroadcast(); + } + + function _deployRewarders() internal { + ParentRewarder[] memory parents = abi.decode(config.parseRaw(".parentRewarders"), (ParentRewarder[])); + for (uint i; i < parents.length; ++i) { + ParentRewarder memory parent = parents[i]; + + ParentRewarderRolling newParent = new ParentRewarderRolling( + reliquary, parent.poolId + ); + + newParent.grantRole(CHILD_SETTER, tx.origin); + newParent.grantRole(REWARD_SETTER, tx.origin); + parentRewarders.push(newParent); + rewarderAddresses.push(address(newParent)); + } + } + + function _deployHelpers(string memory poolTokenType) internal returns (address nftDescriptor) { + bytes32 typeHash = keccak256(bytes(poolTokenType)); + if (typeHash == keccak256("normal")) { + if (nftDescriptorNormal == address(0)) { + nftDescriptorNormal = address(new NFTDescriptor(address(reliquary))); + } + nftDescriptor = nftDescriptorNormal; + } else { + revert(string.concat("invalid token type ", poolTokenType)); + } + } +} + + // reliquary.addPool( + // 66, + // 0x42c95788F791a2be3584446854c8d9BB01BE88A9, + // address(rewarder), + // [0], + // [100], + // "HBR Staking", + // nftDescriptor, + // true + // );