Created
March 26, 2024 13:48
-
-
Save codenamejason/ff59cef318394e4ce847c3bf176f336a to your computer and use it in GitHub Desktop.
Allo flattenned
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: AGPL-3.0-only | |
pragma solidity =0.8.19 ^0.8.0 ^0.8.1 ^0.8.19 ^0.8.2 ^0.8.4; | |
// contracts/core/libraries/Errors.sol | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⢿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⡟⠘⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣾⠻⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⡿⠀⠀⠸⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⢀⣠⣴⣴⣶⣶⣶⣦⣦⣀⡀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⡿⠃⠀⠙⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠁⠀⠀⠀⢻⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠘⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⠃⠀⠀⠀⠀⠈⢿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⣰⣿⣿⣿⡿⠋⠁⠀⠀⠈⠘⠹⣿⣿⣿⣿⣆⠀⠀⠀ | |
// ⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠈⢿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⠏⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡀⠀⠀ | |
// ⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣟⠀⡀⢀⠀⡀⢀⠀⡀⢈⢿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⡇⠀⠀ | |
// ⠀⠀⣠⣿⣿⣿⣿⣿⣿⡿⠋⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⡿⢿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣷⡀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠸⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠂⠀⠀ | |
// ⠀⠀⠙⠛⠿⠻⠻⠛⠉⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣧⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⢻⣿⣿⣿⣷⣀⢀⠀⠀⠀⡀⣰⣾⣿⣿⣿⠏⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣧⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠹⢿⣿⣿⣿⣿⣾⣾⣷⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ | |
// allo.gitcoin.co | |
/// @title Errors | |
/// @author @thelostone-mc <aditya@gitcoin.co>, @KurtMerbeth <kurt@gitcoin.co>, @codenamejason <jason@gitcoin.co> | |
/// @notice Library containing all custom errors the protocol may revert with. | |
contract Errors { | |
/// ====================== | |
/// ====== Generic ======= | |
/// ====================== | |
/// @notice Thrown as a general error when input / data is invalid | |
error INVALID(); | |
/// @notice Thrown when mismatch in decoding data | |
error MISMATCH(); | |
/// @notice Thrown when not enough funds are available | |
error NOT_ENOUGH_FUNDS(); | |
/// @notice Thrown when user is not authorized | |
error UNAUTHORIZED(); | |
/// @notice Thrown when address is the zero address | |
error ZERO_ADDRESS(); | |
/// @notice Thrown when the function is not implemented | |
error NOT_IMPLEMENTED(); | |
/// @notice Thrown when the value is non-zero | |
error NON_ZERO_VALUE(); | |
/// ====================== | |
/// ====== Registry ====== | |
/// ====================== | |
/// @dev Thrown when the nonce passed has been used or not available | |
error NONCE_NOT_AVAILABLE(); | |
/// @dev Thrown when the 'msg.sender' is not the pending owner on ownership transfer | |
error NOT_PENDING_OWNER(); | |
/// @dev Thrown if the anchor creation fails | |
error ANCHOR_ERROR(); | |
/// ====================== | |
/// ======== Allo ======== | |
/// ====================== | |
/// @notice Thrown when the strategy is not approved | |
error NOT_APPROVED_STRATEGY(); | |
/// @notice Thrown when the strategy is approved and should be cloned | |
error IS_APPROVED_STRATEGY(); | |
/// @notice Thrown when the fee is below 1e18 which is the fee percentage denominator | |
error INVALID_FEE(); | |
/// ====================== | |
/// ===== IStrategy ====== | |
/// ====================== | |
/// @notice Thrown when data is already intialized | |
error ALREADY_INITIALIZED(); | |
/// @notice Thrown when data is yet to be initialized | |
error NOT_INITIALIZED(); | |
/// @notice Thrown when an invalid address is used | |
error INVALID_ADDRESS(); | |
/// @notice Thrown when a pool is inactive | |
error POOL_INACTIVE(); | |
/// @notice Thrown when a pool is already active | |
error POOL_ACTIVE(); | |
/// @notice Thrown when two arrays length are not equal | |
error ARRAY_MISMATCH(); | |
/// @notice Thrown when the registration is invalid. | |
error INVALID_REGISTRATION(); | |
/// @notice Thrown when the metadata is invalid. | |
error INVALID_METADATA(); | |
/// @notice Thrown when the recipient is not accepted. | |
error RECIPIENT_NOT_ACCEPTED(); | |
/// @notice Thrown when recipient is already accepted. | |
error RECIPIENT_ALREADY_ACCEPTED(); | |
/// @notice Thrown when registration is not active. | |
error REGISTRATION_NOT_ACTIVE(); | |
/// @notice Thrown when registration is active. | |
error REGISTRATION_ACTIVE(); | |
/// @notice Thrown when there is an error in recipient. | |
error RECIPIENT_ERROR(address recipientId); | |
/// @notice Thrown when the allocation is not active. | |
error ALLOCATION_NOT_ACTIVE(); | |
/// @notice Thrown when the allocation is not ended. | |
error ALLOCATION_NOT_ENDED(); | |
/// @notice Thrown when the allocation is active. | |
error ALLOCATION_ACTIVE(); | |
} | |
// contracts/core/libraries/Metadata.sol | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⢿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⡟⠘⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣾⠻⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⡿⠀⠀⠸⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⢀⣠⣴⣴⣶⣶⣶⣦⣦⣀⡀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⡿⠃⠀⠙⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠁⠀⠀⠀⢻⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠘⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⠃⠀⠀⠀⠀⠈⢿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⣰⣿⣿⣿⡿⠋⠁⠀⠀⠈⠘⠹⣿⣿⣿⣿⣆⠀⠀⠀ | |
// ⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠈⢿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⠏⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡀⠀⠀ | |
// ⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣟⠀⡀⢀⠀⡀⢀⠀⡀⢈⢿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⡇⠀⠀ | |
// ⠀⠀⣠⣿⣿⣿⣿⣿⣿⡿⠋⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⡿⢿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣷⡀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠸⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠂⠀⠀ | |
// ⠀⠀⠙⠛⠿⠻⠻⠛⠉⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣧⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⢻⣿⣿⣿⣷⣀⢀⠀⠀⠀⡀⣰⣾⣿⣿⣿⠏⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣧⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠹⢿⣿⣿⣿⣿⣾⣾⣷⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ | |
// allo.gitcoin.co | |
/// @title Metadata | |
/// @author @thelostone-mc <aditya@gitcoin.co>, @0xKurt <kurt@gitcoin.co>, @codenamejason <jason@gitcoin.co>, @0xZakk <zakk@gitcoin.co>, @nfrgosselin <nate@gitcoin.co> | |
/// @notice Metadata is used to define the metadata for the protocol that is used throughout the system. | |
struct Metadata { | |
/// @notice Protocol ID corresponding to a specific protocol (currently using IPFS = 1) | |
uint256 protocol; | |
/// @notice Pointer (hash) to fetch metadata for the specified protocol | |
string pointer; | |
} | |
// contracts/core/libraries/Native.sol | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⢿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⡟⠘⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣾⠻⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⡿⠀⠀⠸⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⢀⣠⣴⣴⣶⣶⣶⣦⣦⣀⡀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⡿⠃⠀⠙⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠁⠀⠀⠀⢻⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠘⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⠃⠀⠀⠀⠀⠈⢿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⣰⣿⣿⣿⡿⠋⠁⠀⠀⠈⠘⠹⣿⣿⣿⣿⣆⠀⠀⠀ | |
// ⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠈⢿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⠏⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡀⠀⠀ | |
// ⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣟⠀⡀⢀⠀⡀⢀⠀⡀⢈⢿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⡇⠀⠀ | |
// ⠀⠀⣠⣿⣿⣿⣿⣿⣿⡿⠋⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⡿⢿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣷⡀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠸⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠂⠀⠀ | |
// ⠀⠀⠙⠛⠿⠻⠻⠛⠉⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣧⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⢻⣿⣿⣿⣷⣀⢀⠀⠀⠀⡀⣰⣾⣿⣿⣿⠏⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣧⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠹⢿⣿⣿⣿⣿⣾⣾⣷⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ | |
// allo.gitcoin.co | |
/// @title Native token information | |
/// @author @thelostone-mc <aditya@gitcoin.co>, @0xKurt <kurt@gitcoin.co>, @codenamejason <jason@gitcoin.co>, @0xZakk <zakk@gitcoin.co>, @nfrgosselin <nate@gitcoin.co> | |
/// @notice This is used to define the address of the native token for the protocol | |
contract Native { | |
/// @notice Address of the native token | |
address public constant NATIVE = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlUpgradeable.sol | |
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) | |
/** | |
* @dev External interface of AccessControl declared to support ERC165 detection. | |
*/ | |
interface IAccessControlUpgradeable { | |
/** | |
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` | |
* | |
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite | |
* {RoleAdminChanged} not being emitted signaling this. | |
* | |
* _Available since v3.1._ | |
*/ | |
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); | |
/** | |
* @dev Emitted when `account` is granted `role`. | |
* | |
* `sender` is the account that originated the contract call, an admin role | |
* bearer except when using {AccessControl-_setupRole}. | |
*/ | |
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); | |
/** | |
* @dev Emitted when `account` is revoked `role`. | |
* | |
* `sender` is the account that originated the contract call: | |
* - if using `revokeRole`, it is the admin role bearer | |
* - if using `renounceRole`, it is the role bearer (i.e. `account`) | |
*/ | |
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); | |
/** | |
* @dev Returns `true` if `account` has been granted `role`. | |
*/ | |
function hasRole(bytes32 role, address account) external view returns (bool); | |
/** | |
* @dev Returns the admin role that controls `role`. See {grantRole} and | |
* {revokeRole}. | |
* | |
* To change a role's admin, use {AccessControl-_setRoleAdmin}. | |
*/ | |
function getRoleAdmin(bytes32 role) external view returns (bytes32); | |
/** | |
* @dev Grants `role` to `account`. | |
* | |
* If `account` had not been already granted `role`, emits a {RoleGranted} | |
* event. | |
* | |
* Requirements: | |
* | |
* - the caller must have ``role``'s admin role. | |
*/ | |
function grantRole(bytes32 role, address account) external; | |
/** | |
* @dev Revokes `role` from `account`. | |
* | |
* If `account` had been granted `role`, emits a {RoleRevoked} event. | |
* | |
* Requirements: | |
* | |
* - the caller must have ``role``'s admin role. | |
*/ | |
function revokeRole(bytes32 role, address account) external; | |
/** | |
* @dev Revokes `role` from the calling account. | |
* | |
* Roles are often managed via {grantRole} and {revokeRole}: this function's | |
* purpose is to provide a mechanism for accounts to lose their privileges | |
* if they are compromised (such as when a trusted device is misplaced). | |
* | |
* If the calling account had been granted `role`, emits a {RoleRevoked} | |
* event. | |
* | |
* Requirements: | |
* | |
* - the caller must be `account`. | |
*/ | |
function renounceRole(bytes32 role, address account) external; | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/proxy/ClonesUpgradeable.sol | |
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol) | |
/** | |
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for | |
* deploying minimal proxy contracts, also known as "clones". | |
* | |
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies | |
* > a minimal bytecode implementation that delegates all calls to a known, fixed address. | |
* | |
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` | |
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the | |
* deterministic method. | |
* | |
* _Available since v3.4._ | |
*/ | |
library ClonesUpgradeable { | |
/** | |
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. | |
* | |
* This function uses the create opcode, which should never revert. | |
*/ | |
function clone(address implementation) internal returns (address instance) { | |
/// @solidity memory-safe-assembly | |
assembly { | |
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes | |
// of the `implementation` address with the bytecode before the address. | |
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) | |
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address. | |
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) | |
instance := create(0, 0x09, 0x37) | |
} | |
require(instance != address(0), "ERC1167: create failed"); | |
} | |
/** | |
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. | |
* | |
* This function uses the create2 opcode and a `salt` to deterministically deploy | |
* the clone. Using the same `implementation` and `salt` multiple time will revert, since | |
* the clones cannot be deployed twice at the same address. | |
*/ | |
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { | |
/// @solidity memory-safe-assembly | |
assembly { | |
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes | |
// of the `implementation` address with the bytecode before the address. | |
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) | |
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address. | |
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) | |
instance := create2(0, 0x09, 0x37, salt) | |
} | |
require(instance != address(0), "ERC1167: create2 failed"); | |
} | |
/** | |
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. | |
*/ | |
function predictDeterministicAddress( | |
address implementation, | |
bytes32 salt, | |
address deployer | |
) internal pure returns (address predicted) { | |
/// @solidity memory-safe-assembly | |
assembly { | |
let ptr := mload(0x40) | |
mstore(add(ptr, 0x38), deployer) | |
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) | |
mstore(add(ptr, 0x14), implementation) | |
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) | |
mstore(add(ptr, 0x58), salt) | |
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) | |
predicted := keccak256(add(ptr, 0x43), 0x55) | |
} | |
} | |
/** | |
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. | |
*/ | |
function predictDeterministicAddress( | |
address implementation, | |
bytes32 salt | |
) internal view returns (address predicted) { | |
return predictDeterministicAddress(implementation, salt, address(this)); | |
} | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol | |
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) | |
/** | |
* @dev Interface of the ERC20 standard as defined in the EIP. | |
*/ | |
interface IERC20Upgradeable { | |
/** | |
* @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); | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/utils/AddressUpgradeable.sol | |
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) | |
/** | |
* @dev Collection of functions related to the address type | |
*/ | |
library AddressUpgradeable { | |
/** | |
* @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 | |
* | |
* Furthermore, `isContract` will also return true if the target contract within | |
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`, | |
* which only has an effect at the end of a transaction. | |
* ==== | |
* | |
* [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://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.0/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); | |
} | |
} | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/IERC165Upgradeable.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 IERC165Upgradeable { | |
/** | |
* @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); | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/utils/math/MathUpgradeable.sol | |
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) | |
/** | |
* @dev Standard math utilities missing in the Solidity language. | |
*/ | |
library MathUpgradeable { | |
enum Rounding { | |
Down, // Toward negative infinity | |
Up, // Toward infinity | |
Zero // Toward zero | |
} | |
/** | |
* @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 up instead | |
* of rounding down. | |
*/ | |
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { | |
// (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; // Least significant 256 bits of the product | |
uint256 prod1; // Most significant 256 bits of the product | |
assembly { | |
let mm := mulmod(x, y, not(0)) | |
prod0 := mul(x, y) | |
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. | |
require(denominator > prod1, "Math: mulDiv overflow"); | |
/////////////////////////////////////////////// | |
// 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. | |
// Does not overflow because the denominator cannot be zero at this stage in the function. | |
uint256 twos = denominator & (~denominator + 1); | |
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 (rounding == Rounding.Up && 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 down. | |
* | |
* 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0); | |
} | |
} | |
/** | |
* @dev Return the log in base 2, rounded down, of a positive value. | |
* 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 + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); | |
} | |
} | |
/** | |
* @dev Return the log in base 10, rounded down, of a positive value. | |
* 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 + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); | |
} | |
} | |
/** | |
* @dev Return the log in base 256, rounded down, of a positive value. | |
* 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 + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); | |
} | |
} | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/utils/math/SignedMathUpgradeable.sol | |
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) | |
/** | |
* @dev Standard signed math utilities missing in the Solidity language. | |
*/ | |
library SignedMathUpgradeable { | |
/** | |
* @dev Returns the largest of two signed numbers. | |
*/ | |
function max(int256 a, int256 b) internal pure returns (int256) { | |
return a > b ? a : b; | |
} | |
/** | |
* @dev Returns the smallest of two signed numbers. | |
*/ | |
function min(int256 a, int256 b) internal pure returns (int256) { | |
return a < b ? a : b; | |
} | |
/** | |
* @dev Returns the average of two signed numbers without overflow. | |
* The result is rounded towards zero. | |
*/ | |
function average(int256 a, int256 b) internal pure returns (int256) { | |
// Formula from the book "Hacker's Delight" | |
int256 x = (a & b) + ((a ^ b) >> 1); | |
return x + (int256(uint256(x) >> 255) & (a ^ b)); | |
} | |
/** | |
* @dev Returns the absolute unsigned value of a signed value. | |
*/ | |
function abs(int256 n) internal pure returns (uint256) { | |
unchecked { | |
// must be unchecked in order to support `n = type(int256).min` | |
return uint256(n >= 0 ? n : -n); | |
} | |
} | |
} | |
// lib/solady/src/auth/Ownable.sol | |
/// @notice Simple single owner authorization mixin. | |
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) | |
/// | |
/// @dev Note: | |
/// This implementation does NOT auto-initialize the owner to `msg.sender`. | |
/// You MUST call the `_initializeOwner` in the constructor / initializer. | |
/// | |
/// While the ownable portion follows | |
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility, | |
/// the nomenclature for the 2-step ownership handover may be unique to this codebase. | |
abstract contract Ownable { | |
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | |
/* CUSTOM ERRORS */ | |
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | |
/// @dev The caller is not authorized to call the function. | |
error Unauthorized(); | |
/// @dev The `newOwner` cannot be the zero address. | |
error NewOwnerIsZeroAddress(); | |
/// @dev The `pendingOwner` does not have a valid handover request. | |
error NoHandoverRequest(); | |
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | |
/* EVENTS */ | |
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | |
/// @dev The ownership is transferred from `oldOwner` to `newOwner`. | |
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be | |
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173), | |
/// despite it not being as lightweight as a single argument event. | |
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); | |
/// @dev An ownership handover to `pendingOwner` has been requested. | |
event OwnershipHandoverRequested(address indexed pendingOwner); | |
/// @dev The ownership handover to `pendingOwner` has been canceled. | |
event OwnershipHandoverCanceled(address indexed pendingOwner); | |
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. | |
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = | |
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; | |
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. | |
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = | |
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; | |
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. | |
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE = | |
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92; | |
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | |
/* STORAGE */ | |
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | |
/// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`. | |
/// It is intentionally chosen to be a high value | |
/// to avoid collision with lower slots. | |
/// The choice of manual storage layout is to enable compatibility | |
/// with both regular and upgradeable contracts. | |
uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8; | |
/// The ownership handover slot of `newOwner` is given by: | |
/// ``` | |
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED)) | |
/// let handoverSlot := keccak256(0x00, 0x20) | |
/// ``` | |
/// It stores the expiry timestamp of the two-step ownership handover. | |
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; | |
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | |
/* INTERNAL FUNCTIONS */ | |
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | |
/// @dev Initializes the owner directly without authorization guard. | |
/// This function must be called upon initialization, | |
/// regardless of whether the contract is upgradeable or not. | |
/// This is to enable generalization to both regular and upgradeable contracts, | |
/// and to save gas in case the initial owner is not the caller. | |
/// For performance reasons, this function will not check if there | |
/// is an existing owner. | |
function _initializeOwner(address newOwner) internal virtual { | |
/// @solidity memory-safe-assembly | |
assembly { | |
// Clean the upper 96 bits. | |
newOwner := shr(96, shl(96, newOwner)) | |
// Store the new value. | |
sstore(not(_OWNER_SLOT_NOT), newOwner) | |
// Emit the {OwnershipTransferred} event. | |
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) | |
} | |
} | |
/// @dev Sets the owner directly without authorization guard. | |
function _setOwner(address newOwner) internal virtual { | |
/// @solidity memory-safe-assembly | |
assembly { | |
let ownerSlot := not(_OWNER_SLOT_NOT) | |
// Clean the upper 96 bits. | |
newOwner := shr(96, shl(96, newOwner)) | |
// Emit the {OwnershipTransferred} event. | |
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) | |
// Store the new value. | |
sstore(ownerSlot, newOwner) | |
} | |
} | |
/// @dev Throws if the sender is not the owner. | |
function _checkOwner() internal view virtual { | |
/// @solidity memory-safe-assembly | |
assembly { | |
// If the caller is not the stored owner, revert. | |
if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) { | |
mstore(0x00, 0x82b42900) // `Unauthorized()`. | |
revert(0x1c, 0x04) | |
} | |
} | |
} | |
/// @dev Returns how long a two-step ownership handover is valid for in seconds. | |
/// Override to return a different value if needed. | |
/// Made internal to conserve bytecode. Wrap it in a public function if needed. | |
function _ownershipHandoverValidFor() internal view virtual returns (uint64) { | |
return 48 * 3600; | |
} | |
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | |
/* PUBLIC UPDATE FUNCTIONS */ | |
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | |
/// @dev Allows the owner to transfer the ownership to `newOwner`. | |
function transferOwnership(address newOwner) public payable virtual onlyOwner { | |
/// @solidity memory-safe-assembly | |
assembly { | |
if iszero(shl(96, newOwner)) { | |
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`. | |
revert(0x1c, 0x04) | |
} | |
} | |
_setOwner(newOwner); | |
} | |
/// @dev Allows the owner to renounce their ownership. | |
function renounceOwnership() public payable virtual onlyOwner { | |
_setOwner(address(0)); | |
} | |
/// @dev Request a two-step ownership handover to the caller. | |
/// The request will automatically expire in 48 hours (172800 seconds) by default. | |
function requestOwnershipHandover() public payable virtual { | |
unchecked { | |
uint256 expires = block.timestamp + _ownershipHandoverValidFor(); | |
/// @solidity memory-safe-assembly | |
assembly { | |
// Compute and set the handover slot to `expires`. | |
mstore(0x0c, _HANDOVER_SLOT_SEED) | |
mstore(0x00, caller()) | |
sstore(keccak256(0x0c, 0x20), expires) | |
// Emit the {OwnershipHandoverRequested} event. | |
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller()) | |
} | |
} | |
} | |
/// @dev Cancels the two-step ownership handover to the caller, if any. | |
function cancelOwnershipHandover() public payable virtual { | |
/// @solidity memory-safe-assembly | |
assembly { | |
// Compute and set the handover slot to 0. | |
mstore(0x0c, _HANDOVER_SLOT_SEED) | |
mstore(0x00, caller()) | |
sstore(keccak256(0x0c, 0x20), 0) | |
// Emit the {OwnershipHandoverCanceled} event. | |
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller()) | |
} | |
} | |
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`. | |
/// Reverts if there is no existing ownership handover requested by `pendingOwner`. | |
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { | |
/// @solidity memory-safe-assembly | |
assembly { | |
// Compute and set the handover slot to 0. | |
mstore(0x0c, _HANDOVER_SLOT_SEED) | |
mstore(0x00, pendingOwner) | |
let handoverSlot := keccak256(0x0c, 0x20) | |
// If the handover does not exist, or has expired. | |
if gt(timestamp(), sload(handoverSlot)) { | |
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`. | |
revert(0x1c, 0x04) | |
} | |
// Set the handover slot to 0. | |
sstore(handoverSlot, 0) | |
} | |
_setOwner(pendingOwner); | |
} | |
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | |
/* PUBLIC READ FUNCTIONS */ | |
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | |
/// @dev Returns the owner of the contract. | |
function owner() public view virtual returns (address result) { | |
/// @solidity memory-safe-assembly | |
assembly { | |
result := sload(not(_OWNER_SLOT_NOT)) | |
} | |
} | |
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. | |
function ownershipHandoverExpiresAt(address pendingOwner) | |
public | |
view | |
virtual | |
returns (uint256 result) | |
{ | |
/// @solidity memory-safe-assembly | |
assembly { | |
// Compute the handover slot. | |
mstore(0x0c, _HANDOVER_SLOT_SEED) | |
mstore(0x00, pendingOwner) | |
// Load the handover slot. | |
result := sload(keccak256(0x0c, 0x20)) | |
} | |
} | |
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | |
/* MODIFIERS */ | |
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | |
/// @dev Marks a function as only callable by the owner. | |
modifier onlyOwner() virtual { | |
_checkOwner(); | |
_; | |
} | |
} | |
// lib/solady/src/utils/SafeTransferLib.sol | |
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. | |
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) | |
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) | |
/// | |
/// @dev Note: | |
/// - For ETH transfers, please use `forceSafeTransferETH` for gas griefing protection. | |
/// - For ERC20s, this implementation won't check that a token has code, | |
/// responsibility is delegated to the caller. | |
library SafeTransferLib { | |
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | |
/* CUSTOM ERRORS */ | |
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | |
/// @dev The ETH transfer has failed. | |
error ETHTransferFailed(); | |
/// @dev The ERC20 `transferFrom` has failed. | |
error TransferFromFailed(); | |
/// @dev The ERC20 `transfer` has failed. | |
error TransferFailed(); | |
/// @dev The ERC20 `approve` has failed. | |
error ApproveFailed(); | |
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | |
/* CONSTANTS */ | |
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | |
/// @dev Suggested gas stipend for contract receiving ETH | |
/// that disallows any storage writes. | |
uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300; | |
/// @dev Suggested gas stipend for contract receiving ETH to perform a few | |
/// storage reads and writes, but low enough to prevent griefing. | |
/// Multiply by a small constant (e.g. 2), if needed. | |
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000; | |
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | |
/* ETH OPERATIONS */ | |
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | |
/// @dev Sends `amount` (in wei) ETH to `to`. | |
/// Reverts upon failure. | |
/// | |
/// Note: This implementation does NOT protect against gas griefing. | |
/// Please use `forceSafeTransferETH` for gas griefing protection. | |
function safeTransferETH(address to, uint256 amount) internal { | |
/// @solidity memory-safe-assembly | |
assembly { | |
// Transfer the ETH and check if it succeeded or not. | |
if iszero(call(gas(), to, amount, 0x00, 0x00, 0x00, 0x00)) { | |
// Store the function selector of `ETHTransferFailed()`. | |
mstore(0x00, 0xb12d13eb) | |
// Revert with (offset, size). | |
revert(0x1c, 0x04) | |
} | |
} | |
} | |
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`. | |
/// The `gasStipend` can be set to a low enough value to prevent | |
/// storage writes or gas griefing. | |
/// | |
/// If sending via the normal procedure fails, force sends the ETH by | |
/// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH. | |
/// | |
/// Reverts if the current contract has insufficient balance. | |
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal { | |
/// @solidity memory-safe-assembly | |
assembly { | |
// If insufficient balance, revert. | |
if lt(selfbalance(), amount) { | |
// Store the function selector of `ETHTransferFailed()`. | |
mstore(0x00, 0xb12d13eb) | |
// Revert with (offset, size). | |
revert(0x1c, 0x04) | |
} | |
// Transfer the ETH and check if it succeeded or not. | |
if iszero(call(gasStipend, to, amount, 0x00, 0x00, 0x00, 0x00)) { | |
mstore(0x00, to) // Store the address in scratch space. | |
mstore8(0x0b, 0x73) // Opcode `PUSH20`. | |
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. | |
// We can directly use `SELFDESTRUCT` in the contract creation. | |
// Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758 | |
if iszero(create(amount, 0x0b, 0x16)) { | |
// To coerce gas estimation to provide enough gas for the `create` above. | |
if iszero(gt(gas(), 1000000)) { revert(0x00, 0x00) } | |
} | |
} | |
} | |
} | |
/// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend | |
/// equal to `GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default | |
/// for 99% of cases and can be overridden with the three-argument version of this | |
/// function if necessary. | |
/// | |
/// If sending via the normal procedure fails, force sends the ETH by | |
/// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH. | |
/// | |
/// Reverts if the current contract has insufficient balance. | |
function forceSafeTransferETH(address to, uint256 amount) internal { | |
// Manually inlined because the compiler doesn't inline functions with branches. | |
/// @solidity memory-safe-assembly | |
assembly { | |
// If insufficient balance, revert. | |
if lt(selfbalance(), amount) { | |
// Store the function selector of `ETHTransferFailed()`. | |
mstore(0x00, 0xb12d13eb) | |
// Revert with (offset, size). | |
revert(0x1c, 0x04) | |
} | |
// Transfer the ETH and check if it succeeded or not. | |
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, 0x00, 0x00, 0x00, 0x00)) { | |
mstore(0x00, to) // Store the address in scratch space. | |
mstore8(0x0b, 0x73) // Opcode `PUSH20`. | |
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. | |
// We can directly use `SELFDESTRUCT` in the contract creation. | |
// Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758 | |
if iszero(create(amount, 0x0b, 0x16)) { | |
// To coerce gas estimation to provide enough gas for the `create` above. | |
if iszero(gt(gas(), 1000000)) { revert(0x00, 0x00) } | |
} | |
} | |
} | |
} | |
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`. | |
/// The `gasStipend` can be set to a low enough value to prevent | |
/// storage writes or gas griefing. | |
/// | |
/// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend. | |
/// | |
/// Note: Does NOT revert upon failure. | |
/// Returns whether the transfer of ETH is successful instead. | |
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend) | |
internal | |
returns (bool success) | |
{ | |
/// @solidity memory-safe-assembly | |
assembly { | |
// Transfer the ETH and check if it succeeded or not. | |
success := call(gasStipend, to, amount, 0x00, 0x00, 0x00, 0x00) | |
} | |
} | |
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | |
/* ERC20 OPERATIONS */ | |
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | |
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`. | |
/// Reverts upon failure. | |
/// | |
/// The `from` account must have at least `amount` approved for | |
/// the current contract to manage. | |
function safeTransferFrom(address token, address from, address to, uint256 amount) internal { | |
/// @solidity memory-safe-assembly | |
assembly { | |
let m := mload(0x40) // Cache the free memory pointer. | |
mstore(0x60, amount) // Store the `amount` argument. | |
mstore(0x40, to) // Store the `to` argument. | |
mstore(0x2c, shl(96, from)) // Store the `from` argument. | |
// Store the function selector of `transferFrom(address,address,uint256)`. | |
mstore(0x0c, 0x23b872dd000000000000000000000000) | |
if iszero( | |
and( // The arguments of `and` are evaluated from right to left. | |
// Set success to whether the call reverted, if not we check it either | |
// returned exactly 1 (can't just be non-zero data), or had no return data. | |
or(eq(mload(0x00), 1), iszero(returndatasize())), | |
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) | |
) | |
) { | |
// Store the function selector of `TransferFromFailed()`. | |
mstore(0x00, 0x7939f424) | |
// Revert with (offset, size). | |
revert(0x1c, 0x04) | |
} | |
mstore(0x60, 0) // Restore the zero slot to zero. | |
mstore(0x40, m) // Restore the free memory pointer. | |
} | |
} | |
/// @dev Sends all of ERC20 `token` from `from` to `to`. | |
/// Reverts upon failure. | |
/// | |
/// The `from` account must have their entire balance approved for | |
/// the current contract to manage. | |
function safeTransferAllFrom(address token, address from, address to) | |
internal | |
returns (uint256 amount) | |
{ | |
/// @solidity memory-safe-assembly | |
assembly { | |
let m := mload(0x40) // Cache the free memory pointer. | |
mstore(0x40, to) // Store the `to` argument. | |
mstore(0x2c, shl(96, from)) // Store the `from` argument. | |
// Store the function selector of `balanceOf(address)`. | |
mstore(0x0c, 0x70a08231000000000000000000000000) | |
if iszero( | |
and( // The arguments of `and` are evaluated from right to left. | |
gt(returndatasize(), 0x1f), // At least 32 bytes returned. | |
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20) | |
) | |
) { | |
// Store the function selector of `TransferFromFailed()`. | |
mstore(0x00, 0x7939f424) | |
// Revert with (offset, size). | |
revert(0x1c, 0x04) | |
} | |
// Store the function selector of `transferFrom(address,address,uint256)`. | |
mstore(0x00, 0x23b872dd) | |
// The `amount` is already at 0x60. Load it for the function's return value. | |
amount := mload(0x60) | |
if iszero( | |
and( // The arguments of `and` are evaluated from right to left. | |
// Set success to whether the call reverted, if not we check it either | |
// returned exactly 1 (can't just be non-zero data), or had no return data. | |
or(eq(mload(0x00), 1), iszero(returndatasize())), | |
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) | |
) | |
) { | |
// Store the function selector of `TransferFromFailed()`. | |
mstore(0x00, 0x7939f424) | |
// Revert with (offset, size). | |
revert(0x1c, 0x04) | |
} | |
mstore(0x60, 0) // Restore the zero slot to zero. | |
mstore(0x40, m) // Restore the free memory pointer. | |
} | |
} | |
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. | |
/// Reverts upon failure. | |
function safeTransfer(address token, address to, uint256 amount) internal { | |
/// @solidity memory-safe-assembly | |
assembly { | |
mstore(0x14, to) // Store the `to` argument. | |
mstore(0x34, amount) // Store the `amount` argument. | |
// Store the function selector of `transfer(address,uint256)`. | |
mstore(0x00, 0xa9059cbb000000000000000000000000) | |
if iszero( | |
and( // The arguments of `and` are evaluated from right to left. | |
// Set success to whether the call reverted, if not we check it either | |
// returned exactly 1 (can't just be non-zero data), or had no return data. | |
or(eq(mload(0x00), 1), iszero(returndatasize())), | |
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) | |
) | |
) { | |
// Store the function selector of `TransferFailed()`. | |
mstore(0x00, 0x90b8ec18) | |
// Revert with (offset, size). | |
revert(0x1c, 0x04) | |
} | |
// Restore the part of the free memory pointer that was overwritten. | |
mstore(0x34, 0) | |
} | |
} | |
/// @dev Sends all of ERC20 `token` from the current contract to `to`. | |
/// Reverts upon failure. | |
function safeTransferAll(address token, address to) internal returns (uint256 amount) { | |
/// @solidity memory-safe-assembly | |
assembly { | |
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. | |
mstore(0x20, address()) // Store the address of the current contract. | |
if iszero( | |
and( // The arguments of `and` are evaluated from right to left. | |
gt(returndatasize(), 0x1f), // At least 32 bytes returned. | |
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20) | |
) | |
) { | |
// Store the function selector of `TransferFailed()`. | |
mstore(0x00, 0x90b8ec18) | |
// Revert with (offset, size). | |
revert(0x1c, 0x04) | |
} | |
mstore(0x14, to) // Store the `to` argument. | |
// The `amount` is already at 0x34. Load it for the function's return value. | |
amount := mload(0x34) | |
// Store the function selector of `transfer(address,uint256)`. | |
mstore(0x00, 0xa9059cbb000000000000000000000000) | |
if iszero( | |
and( // The arguments of `and` are evaluated from right to left. | |
// Set success to whether the call reverted, if not we check it either | |
// returned exactly 1 (can't just be non-zero data), or had no return data. | |
or(eq(mload(0x00), 1), iszero(returndatasize())), | |
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) | |
) | |
) { | |
// Store the function selector of `TransferFailed()`. | |
mstore(0x00, 0x90b8ec18) | |
// Revert with (offset, size). | |
revert(0x1c, 0x04) | |
} | |
// Restore the part of the free memory pointer that was overwritten. | |
mstore(0x34, 0) | |
} | |
} | |
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. | |
/// Reverts upon failure. | |
function safeApprove(address token, address to, uint256 amount) internal { | |
/// @solidity memory-safe-assembly | |
assembly { | |
mstore(0x14, to) // Store the `to` argument. | |
mstore(0x34, amount) // Store the `amount` argument. | |
// Store the function selector of `approve(address,uint256)`. | |
mstore(0x00, 0x095ea7b3000000000000000000000000) | |
if iszero( | |
and( // The arguments of `and` are evaluated from right to left. | |
// Set success to whether the call reverted, if not we check it either | |
// returned exactly 1 (can't just be non-zero data), or had no return data. | |
or(eq(mload(0x00), 1), iszero(returndatasize())), | |
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) | |
) | |
) { | |
// Store the function selector of `ApproveFailed()`. | |
mstore(0x00, 0x3e3f8f73) | |
// Revert with (offset, size). | |
revert(0x1c, 0x04) | |
} | |
// Restore the part of the free memory pointer that was overwritten. | |
mstore(0x34, 0) | |
} | |
} | |
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. | |
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero, | |
/// then retries the approval again (some tokens, e.g. USDT, requires this). | |
/// Reverts upon failure. | |
function safeApproveWithRetry(address token, address to, uint256 amount) internal { | |
/// @solidity memory-safe-assembly | |
assembly { | |
mstore(0x14, to) // Store the `to` argument. | |
mstore(0x34, amount) // Store the `amount` argument. | |
// Store the function selector of `approve(address,uint256)`. | |
mstore(0x00, 0x095ea7b3000000000000000000000000) | |
if iszero( | |
and( // The arguments of `and` are evaluated from right to left. | |
// Set success to whether the call reverted, if not we check it either | |
// returned exactly 1 (can't just be non-zero data), or had no return data. | |
or(eq(mload(0x00), 1), iszero(returndatasize())), | |
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) | |
) | |
) { | |
mstore(0x34, 0) // Store 0 for the `amount`. | |
mstore(0x00, 0x095ea7b3000000000000000000000000) // Store the function selector. | |
// We can ignore the result of this call. Just need to check the next call. | |
pop(call(gas(), token, 0, 0x10, 0x44, 0x00, 0x00)) | |
mstore(0x34, amount) // Store back the original `amount`. | |
if iszero( | |
and( | |
or(eq(mload(0x00), 1), iszero(returndatasize())), | |
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) | |
) | |
) { | |
// Store the function selector of `ApproveFailed()`. | |
mstore(0x00, 0x3e3f8f73) | |
// Revert with (offset, size). | |
revert(0x1c, 0x04) | |
} | |
} | |
// Restore the part of the free memory pointer that was overwritten. | |
mstore(0x34, 0) | |
} | |
} | |
/// @dev Returns the amount of ERC20 `token` owned by `account`. | |
/// Returns zero if the `token` does not exist. | |
function balanceOf(address token, address account) internal view returns (uint256 amount) { | |
/// @solidity memory-safe-assembly | |
assembly { | |
mstore(0x14, account) // Store the `account` argument. | |
// Store the function selector of `balanceOf(address)`. | |
mstore(0x00, 0x70a08231000000000000000000000000) | |
amount := | |
mul( | |
mload(0x20), | |
and( // The arguments of `and` are evaluated from right to left. | |
gt(returndatasize(), 0x1f), // At least 32 bytes returned. | |
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20) | |
) | |
) | |
} | |
} | |
} | |
// contracts/core/interfaces/IRegistry.sol | |
// Internal Libraries | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⢿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⡟⠘⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣾⠻⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⡿⠀⠀⠸⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⢀⣠⣴⣴⣶⣶⣶⣦⣦⣀⡀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⡿⠃⠀⠙⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠁⠀⠀⠀⢻⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠘⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⠃⠀⠀⠀⠀⠈⢿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⣰⣿⣿⣿⡿⠋⠁⠀⠀⠈⠘⠹⣿⣿⣿⣿⣆⠀⠀⠀ | |
// ⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠈⢿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⠏⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡀⠀⠀ | |
// ⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣟⠀⡀⢀⠀⡀⢀⠀⡀⢈⢿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⡇⠀⠀ | |
// ⠀⠀⣠⣿⣿⣿⣿⣿⣿⡿⠋⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⡿⢿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣷⡀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠸⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠂⠀⠀ | |
// ⠀⠀⠙⠛⠿⠻⠻⠛⠉⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣧⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⢻⣿⣿⣿⣷⣀⢀⠀⠀⠀⡀⣰⣾⣿⣿⣿⠏⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣧⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠹⢿⣿⣿⣿⣿⣾⣾⣷⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ | |
// allo.gitcoin.co | |
/// @title IRegistry Interface | |
/// @author @thelostone-mc <aditya@gitcoin.co>, @0xKurt <kurt@gitcoin.co>, @codenamejason <jason@gitcoin.co>, @0xZakk <zakk@gitcoin.co>, @nfrgosselin <nate@gitcoin.co> | |
/// @notice Interface for the Registry contract and exposes all functions needed to use the Registry | |
/// within the Allo protocol. | |
/// @dev The Registry Interface is used to interact with the Allo protocol and create profiles | |
/// that can be used to interact with the Allo protocol. The Registry is the main contract | |
/// that all other contracts interact with to get the 'Profile' information needed to | |
/// interact with the Allo protocol. The Registry is also used to create new profiles | |
/// and update existing profiles. The Registry is also used to add and remove members | |
/// from a profile. The Registry will not always be used in a strategy and will depend on | |
/// the strategy being used. | |
interface IRegistry { | |
/// ====================== | |
/// ======= Structs ====== | |
/// ====================== | |
/// @dev The Profile struct that all profiles are based from | |
struct Profile { | |
bytes32 id; | |
uint256 nonce; | |
string name; | |
Metadata metadata; | |
address owner; | |
address anchor; | |
} | |
/// ====================== | |
/// ======= Events ======= | |
/// ====================== | |
/// @dev Emitted when a profile is created. This will return your anchor address. | |
event ProfileCreated( | |
bytes32 indexed profileId, uint256 nonce, string name, Metadata metadata, address owner, address anchor | |
); | |
/// @dev Emitted when a profile name is updated. This will update the anchor when the name is updated and return it. | |
event ProfileNameUpdated(bytes32 indexed profileId, string name, address anchor); | |
/// @dev Emitted when a profile's metadata is updated. | |
event ProfileMetadataUpdated(bytes32 indexed profileId, Metadata metadata); | |
/// @dev Emitted when a profile owner is updated. | |
event ProfileOwnerUpdated(bytes32 indexed profileId, address owner); | |
/// @dev Emitted when a profile pending owner is updated. | |
event ProfilePendingOwnerUpdated(bytes32 indexed profileId, address pendingOwner); | |
/// ========================= | |
/// ==== View Functions ===== | |
/// ========================= | |
/// @dev Returns the 'Profile' for a '_profileId' passed | |
/// @param _profileId The 'profileId' to return the 'Profile' for | |
/// @return profile The 'Profile' for the '_profileId' passed | |
function getProfileById(bytes32 _profileId) external view returns (Profile memory profile); | |
/// @dev Returns the 'Profile' for an '_anchor' passed | |
/// @param _anchor The 'anchor' to return the 'Profile' for | |
/// @return profile The 'Profile' for the '_anchor' passed | |
function getProfileByAnchor(address _anchor) external view returns (Profile memory profile); | |
/// @dev Returns a boolean if the '_account' is a member or owner of the '_profileId' passed in | |
/// @param _profileId The 'profileId' to check if the '_account' is a member or owner of | |
/// @param _account The 'account' to check if they are a member or owner of the '_profileId' passed in | |
/// @return isOwnerOrMemberOfProfile A boolean if the '_account' is a member or owner of the '_profileId' passed in | |
function isOwnerOrMemberOfProfile(bytes32 _profileId, address _account) | |
external | |
view | |
returns (bool isOwnerOrMemberOfProfile); | |
/// @dev Returns a boolean if the '_account' is an owner of the '_profileId' passed in | |
/// @param _profileId The 'profileId' to check if the '_account' is an owner of | |
/// @param _owner The 'owner' to check if they are an owner of the '_profileId' passed in | |
/// @return isOwnerOfProfile A boolean if the '_account' is an owner of the '_profileId' passed in | |
function isOwnerOfProfile(bytes32 _profileId, address _owner) external view returns (bool isOwnerOfProfile); | |
/// @dev Returns a boolean if the '_account' is a member of the '_profileId' passed in | |
/// @param _profileId The 'profileId' to check if the '_account' is a member of | |
/// @param _member The 'member' to check if they are a member of the '_profileId' passed in | |
/// @return isMemberOfProfile A boolean if the '_account' is a member of the '_profileId' passed in | |
function isMemberOfProfile(bytes32 _profileId, address _member) external view returns (bool isMemberOfProfile); | |
/// ==================================== | |
/// ==== External/Public Functions ===== | |
/// ==================================== | |
/// @dev Creates a new 'Profile' and returns the 'profileId' of the new profile | |
/// | |
/// Note: The 'name' and 'nonce' are used to generate the 'anchor' address | |
/// | |
/// Requirements: None, anyone can create a new profile | |
/// | |
/// @param _nonce The nonce to use to generate the 'anchor' address | |
/// @param _name The name to use to generate the 'anchor' address | |
/// @param _metadata The 'Metadata' to use to generate the 'anchor' address | |
/// @param _owner The 'owner' to use to generate the 'anchor' address | |
/// @param _members The 'members' to use to generate the 'anchor' address | |
/// @return profileId The 'profileId' of the new profile | |
function createProfile( | |
uint256 _nonce, | |
string memory _name, | |
Metadata memory _metadata, | |
address _owner, | |
address[] memory _members | |
) external returns (bytes32 profileId); | |
/// @dev Updates the 'name' of the '_profileId' passed in and returns the new 'anchor' address | |
/// | |
/// Requirements: Only the 'Profile' owner can update the name | |
/// | |
/// Note: The 'name' and 'nonce' are used to generate the 'anchor' address and this will update the 'anchor' | |
/// so please use caution. You can always recreate your 'anchor' address by updating the name back | |
/// to the original name used to create the profile. | |
/// | |
/// @param _profileId The 'profileId' to update the name for | |
/// @param _name The new 'name' value | |
/// @return anchor The new 'anchor' address | |
function updateProfileName(bytes32 _profileId, string memory _name) external returns (address anchor); | |
/// @dev Updates the 'Metadata' of the '_profileId' passed in | |
/// | |
/// Requirements: Only the 'Profile' owner can update the metadata | |
/// | |
/// @param _profileId The 'profileId' to update the metadata for | |
/// @param _metadata The new 'Metadata' value | |
function updateProfileMetadata(bytes32 _profileId, Metadata memory _metadata) external; | |
/// @dev Updates the pending 'owner' of the '_profileId' passed in | |
/// | |
/// Requirements: Only the 'Profile' owner can update the pending owner | |
/// | |
/// @param _profileId The 'profileId' to update the pending owner for | |
/// @param _pendingOwner The new pending 'owner' value | |
function updateProfilePendingOwner(bytes32 _profileId, address _pendingOwner) external; | |
/// @dev Accepts the pending 'owner' of the '_profileId' passed in | |
/// | |
/// Requirements: Only the pending owner can accept the ownership | |
/// | |
/// @param _profileId The 'profileId' to accept the ownership for | |
function acceptProfileOwnership(bytes32 _profileId) external; | |
/// @dev Adds members to the '_profileId' passed in | |
/// | |
/// Requirements: Only the 'Profile' owner can add members | |
/// | |
/// @param _profileId The 'profileId' to add members to | |
/// @param _members The members to add to the '_profileId' passed in | |
function addMembers(bytes32 _profileId, address[] memory _members) external; | |
/// @dev Removes members from the '_profileId' passed in | |
/// | |
/// Requirements: Only the 'Profile' owner can remove members | |
/// | |
/// @param _profileId The 'profileId' to remove members from | |
/// @param _members The members to remove from the '_profileId' passed in | |
function removeMembers(bytes32 _profileId, address[] memory _members) external; | |
/// @dev Recovers funds from the contract | |
/// | |
/// Requirements: Must be the Allo owner | |
/// | |
/// @param _token The token you want to use to recover funds | |
/// @param _recipient The recipient of the recovered funds | |
function recoverFunds(address _token, address _recipient) external; | |
} | |
// contracts/core/libraries/Clone.sol | |
// External Libraries | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⢿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⡟⠘⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣾⠻⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⡿⠀⠀⠸⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⢀⣠⣴⣴⣶⣶⣶⣦⣦⣀⡀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⡿⠃⠀⠙⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠁⠀⠀⠀⢻⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠘⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⠃⠀⠀⠀⠀⠈⢿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⣰⣿⣿⣿⡿⠋⠁⠀⠀⠈⠘⠹⣿⣿⣿⣿⣆⠀⠀⠀ | |
// ⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠈⢿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⠏⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡀⠀⠀ | |
// ⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣟⠀⡀⢀⠀⡀⢀⠀⡀⢈⢿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⡇⠀⠀ | |
// ⠀⠀⣠⣿⣿⣿⣿⣿⣿⡿⠋⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⡿⢿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣷⡀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠸⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠂⠀⠀ | |
// ⠀⠀⠙⠛⠿⠻⠻⠛⠉⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣧⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⢻⣿⣿⣿⣷⣀⢀⠀⠀⠀⡀⣰⣾⣿⣿⣿⠏⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣧⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠹⢿⣿⣿⣿⣿⣾⣾⣷⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ | |
// allo.gitcoin.co | |
/// @title Clone library | |
/// @author @thelostone-mc <aditya@gitcoin.co>, @0xKurt <kurt@gitcoin.co>, @codenamejason <jason@gitcoin.co>, @0xZakk <zakk@gitcoin.co>, @nfrgosselin <nate@gitcoin.co> | |
/// @notice A helper library to create deterministic clones of the strategy contracts when a pool is created | |
/// @dev Handles the creation of clones for the strategy contracts and returns the address of the clone | |
library Clone { | |
/// @dev Create a clone of the contract | |
/// @param _contract The address of the contract to clone | |
/// @param _nonce The nonce to use for the clone | |
function createClone(address _contract, uint256 _nonce) internal returns (address) { | |
bytes32 salt = keccak256(abi.encodePacked(msg.sender, _nonce)); | |
// Return the address of the contract | |
return ClonesUpgradeable.cloneDeterministic(_contract, salt); | |
} | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol | |
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol) | |
/** | |
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed | |
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an | |
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer | |
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. | |
* | |
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be | |
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in | |
* case an upgrade adds a module that needs to be initialized. | |
* | |
* For example: | |
* | |
* [.hljs-theme-light.nopadding] | |
* ```solidity | |
* contract MyToken is ERC20Upgradeable { | |
* function initialize() initializer public { | |
* __ERC20_init("MyToken", "MTK"); | |
* } | |
* } | |
* | |
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { | |
* function initializeV2() reinitializer(2) public { | |
* __ERC20Permit_init("MyToken"); | |
* } | |
* } | |
* ``` | |
* | |
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as | |
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. | |
* | |
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure | |
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. | |
* | |
* [CAUTION] | |
* ==== | |
* Avoid leaving a contract uninitialized. | |
* | |
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation | |
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke | |
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: | |
* | |
* [.hljs-theme-light.nopadding] | |
* ``` | |
* /// @custom:oz-upgrades-unsafe-allow constructor | |
* constructor() { | |
* _disableInitializers(); | |
* } | |
* ``` | |
* ==== | |
*/ | |
abstract contract Initializable { | |
/** | |
* @dev Indicates that the contract has been initialized. | |
* @custom:oz-retyped-from bool | |
*/ | |
uint8 private _initialized; | |
/** | |
* @dev Indicates that the contract is in the process of being initialized. | |
*/ | |
bool private _initializing; | |
/** | |
* @dev Triggered when the contract has been initialized or reinitialized. | |
*/ | |
event Initialized(uint8 version); | |
/** | |
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, | |
* `onlyInitializing` functions can be used to initialize parent contracts. | |
* | |
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a | |
* constructor. | |
* | |
* Emits an {Initialized} event. | |
*/ | |
modifier initializer() { | |
bool isTopLevelCall = !_initializing; | |
require( | |
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), | |
"Initializable: contract is already initialized" | |
); | |
_initialized = 1; | |
if (isTopLevelCall) { | |
_initializing = true; | |
} | |
_; | |
if (isTopLevelCall) { | |
_initializing = false; | |
emit Initialized(1); | |
} | |
} | |
/** | |
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the | |
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be | |
* used to initialize parent contracts. | |
* | |
* A reinitializer may be used after the original initialization step. This is essential to configure modules that | |
* are added through upgrades and that require initialization. | |
* | |
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` | |
* cannot be nested. If one is invoked in the context of another, execution will revert. | |
* | |
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in | |
* a contract, executing them in the right order is up to the developer or operator. | |
* | |
* WARNING: setting the version to 255 will prevent any future reinitialization. | |
* | |
* Emits an {Initialized} event. | |
*/ | |
modifier reinitializer(uint8 version) { | |
require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); | |
_initialized = version; | |
_initializing = true; | |
_; | |
_initializing = false; | |
emit Initialized(version); | |
} | |
/** | |
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the | |
* {initializer} and {reinitializer} modifiers, directly or indirectly. | |
*/ | |
modifier onlyInitializing() { | |
require(_initializing, "Initializable: contract is not initializing"); | |
_; | |
} | |
/** | |
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. | |
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized | |
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called | |
* through proxies. | |
* | |
* Emits an {Initialized} event the first time it is successfully executed. | |
*/ | |
function _disableInitializers() internal virtual { | |
require(!_initializing, "Initializable: contract is initializing"); | |
if (_initialized != type(uint8).max) { | |
_initialized = type(uint8).max; | |
emit Initialized(type(uint8).max); | |
} | |
} | |
/** | |
* @dev Returns the highest version that has been initialized. See {reinitializer}. | |
*/ | |
function _getInitializedVersion() internal view returns (uint8) { | |
return _initialized; | |
} | |
/** | |
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. | |
*/ | |
function _isInitializing() internal view returns (bool) { | |
return _initializing; | |
} | |
} | |
// contracts/core/libraries/Transfer.sol | |
// External Libraries | |
// Internal Libraries | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⢿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⡟⠘⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣾⠻⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⡿⠀⠀⠸⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⢀⣠⣴⣴⣶⣶⣶⣦⣦⣀⡀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⡿⠃⠀⠙⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠁⠀⠀⠀⢻⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠘⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⠃⠀⠀⠀⠀⠈⢿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⣰⣿⣿⣿⡿⠋⠁⠀⠀⠈⠘⠹⣿⣿⣿⣿⣆⠀⠀⠀ | |
// ⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠈⢿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⠏⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡀⠀⠀ | |
// ⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣟⠀⡀⢀⠀⡀⢀⠀⡀⢈⢿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⡇⠀⠀ | |
// ⠀⠀⣠⣿⣿⣿⣿⣿⣿⡿⠋⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⡿⢿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣷⡀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠸⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠂⠀⠀ | |
// ⠀⠀⠙⠛⠿⠻⠻⠛⠉⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣧⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⢻⣿⣿⣿⣷⣀⢀⠀⠀⠀⡀⣰⣾⣿⣿⣿⠏⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣧⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠹⢿⣿⣿⣿⣿⣾⣾⣷⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ | |
// allo.gitcoin.co | |
/// @title Transfer contract | |
/// @author @thelostone-mc <aditya@gitcoin.co>, @0xKurt <kurt@gitcoin.co>, @codenamejason <jason@gitcoin.co>, @0xZakk <zakk@gitcoin.co>, @nfrgosselin <nate@gitcoin.co> | |
/// @notice A helper contract to transfer tokens within Allo protocol | |
/// @dev Handles the transfer of tokens to an address | |
contract Transfer is Native { | |
/// @notice Thrown when the amount of tokens sent does not match the amount of tokens expected | |
error AMOUNT_MISMATCH(); | |
/// @notice This holds the details for a transfer | |
struct TransferData { | |
address from; | |
address to; | |
uint256 amount; | |
} | |
/// @notice Transfer an amount of a token to an array of addresses | |
/// @param _token The address of the token | |
/// @param _transferData TransferData[] | |
/// @return Whether the transfer was successful or not | |
function _transferAmountsFrom(address _token, TransferData[] memory _transferData) | |
internal | |
virtual | |
returns (bool) | |
{ | |
uint256 msgValue = msg.value; | |
for (uint256 i; i < _transferData.length;) { | |
TransferData memory transferData = _transferData[i]; | |
if (_token == NATIVE) { | |
msgValue -= transferData.amount; | |
SafeTransferLib.safeTransferETH(transferData.to, transferData.amount); | |
} else { | |
SafeTransferLib.safeTransferFrom(_token, transferData.from, transferData.to, transferData.amount); | |
} | |
unchecked { | |
i++; | |
} | |
} | |
if (msgValue != 0) revert AMOUNT_MISMATCH(); | |
return true; | |
} | |
/// @notice Transfer an amount of a token to an address | |
/// @param _token The address of the token | |
/// @param _transferData Individual TransferData | |
/// @return Whether the transfer was successful or not | |
function _transferAmountFrom(address _token, TransferData memory _transferData) internal virtual returns (bool) { | |
uint256 amount = _transferData.amount; | |
if (_token == NATIVE) { | |
// Native Token | |
if (msg.value < amount) revert AMOUNT_MISMATCH(); | |
SafeTransferLib.safeTransferETH(_transferData.to, amount); | |
} else { | |
SafeTransferLib.safeTransferFrom(_token, _transferData.from, _transferData.to, amount); | |
} | |
return true; | |
} | |
/// @notice Transfer an amount of a token to an address | |
/// @param _token The token to transfer | |
/// @param _to The address to transfer to | |
/// @param _amount The amount to transfer | |
function _transferAmount(address _token, address _to, uint256 _amount) internal virtual { | |
if (_token == NATIVE) { | |
SafeTransferLib.safeTransferETH(_to, _amount); | |
} else { | |
SafeTransferLib.safeTransfer(_token, _to, _amount); | |
} | |
} | |
/// @notice Get the balance of a token for an account | |
/// @param _token The token to get the balance of | |
/// @param _account The account to get the balance for | |
/// @return The balance of the token for the account | |
function _getBalance(address _token, address _account) internal view returns (uint256) { | |
if (_token == NATIVE) { | |
return payable(_account).balance; | |
} else { | |
return SafeTransferLib.balanceOf(_token, _account); | |
} | |
} | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/security/ReentrancyGuardUpgradeable.sol | |
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol) | |
/** | |
* @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 ReentrancyGuardUpgradeable is Initializable { | |
// 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; | |
function __ReentrancyGuard_init() internal onlyInitializing { | |
__ReentrancyGuard_init_unchained(); | |
} | |
function __ReentrancyGuard_init_unchained() internal onlyInitializing { | |
_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 | |
require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); | |
// 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; | |
} | |
/** | |
* @dev This empty reserved space is put in place to allow future versions to add new | |
* variables without shifting down storage in the inheritance chain. | |
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps | |
*/ | |
uint256[49] private __gap; | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.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 ContextUpgradeable is Initializable { | |
function __Context_init() internal onlyInitializing { | |
} | |
function __Context_init_unchained() internal onlyInitializing { | |
} | |
function _msgSender() internal view virtual returns (address) { | |
return msg.sender; | |
} | |
function _msgData() internal view virtual returns (bytes calldata) { | |
return msg.data; | |
} | |
/** | |
* @dev This empty reserved space is put in place to allow future versions to add new | |
* variables without shifting down storage in the inheritance chain. | |
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps | |
*/ | |
uint256[50] private __gap; | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/utils/StringsUpgradeable.sol | |
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) | |
/** | |
* @dev String operations. | |
*/ | |
library StringsUpgradeable { | |
bytes16 private constant _SYMBOLS = "0123456789abcdef"; | |
uint8 private constant _ADDRESS_LENGTH = 20; | |
/** | |
* @dev Converts a `uint256` to its ASCII `string` decimal representation. | |
*/ | |
function toString(uint256 value) internal pure returns (string memory) { | |
unchecked { | |
uint256 length = MathUpgradeable.log10(value) + 1; | |
string memory buffer = new string(length); | |
uint256 ptr; | |
/// @solidity memory-safe-assembly | |
assembly { | |
ptr := add(buffer, add(32, length)) | |
} | |
while (true) { | |
ptr--; | |
/// @solidity memory-safe-assembly | |
assembly { | |
mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) | |
} | |
value /= 10; | |
if (value == 0) break; | |
} | |
return buffer; | |
} | |
} | |
/** | |
* @dev Converts a `int256` to its ASCII `string` decimal representation. | |
*/ | |
function toString(int256 value) internal pure returns (string memory) { | |
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value)))); | |
} | |
/** | |
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. | |
*/ | |
function toHexString(uint256 value) internal pure returns (string memory) { | |
unchecked { | |
return toHexString(value, MathUpgradeable.log256(value) + 1); | |
} | |
} | |
/** | |
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. | |
*/ | |
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { | |
bytes memory buffer = new bytes(2 * length + 2); | |
buffer[0] = "0"; | |
buffer[1] = "x"; | |
for (uint256 i = 2 * length + 1; i > 1; --i) { | |
buffer[i] = _SYMBOLS[value & 0xf]; | |
value >>= 4; | |
} | |
require(value == 0, "Strings: hex length insufficient"); | |
return string(buffer); | |
} | |
/** | |
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. | |
*/ | |
function toHexString(address addr) internal pure returns (string memory) { | |
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); | |
} | |
/** | |
* @dev Returns true if the two strings are equal. | |
*/ | |
function equal(string memory a, string memory b) internal pure returns (bool) { | |
return keccak256(bytes(a)) == keccak256(bytes(b)); | |
} | |
} | |
// contracts/core/interfaces/IAllo.sol | |
// Interfaces | |
// Internal Libraries | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⢿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⡟⠘⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣾⠻⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⡿⠀⠀⠸⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⢀⣠⣴⣴⣶⣶⣶⣦⣦⣀⡀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⡿⠃⠀⠙⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠁⠀⠀⠀⢻⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠘⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⠃⠀⠀⠀⠀⠈⢿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⣰⣿⣿⣿⡿⠋⠁⠀⠀⠈⠘⠹⣿⣿⣿⣿⣆⠀⠀⠀ | |
// ⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠈⢿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⠏⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡀⠀⠀ | |
// ⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣟⠀⡀⢀⠀⡀⢀⠀⡀⢈⢿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⡇⠀⠀ | |
// ⠀⠀⣠⣿⣿⣿⣿⣿⣿⡿⠋⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⡿⢿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣷⡀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠸⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠂⠀⠀ | |
// ⠀⠀⠙⠛⠿⠻⠻⠛⠉⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣧⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⢻⣿⣿⣿⣷⣀⢀⠀⠀⠀⡀⣰⣾⣿⣿⣿⠏⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣧⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠹⢿⣿⣿⣿⣿⣾⣾⣷⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ | |
// allo.gitcoin.co | |
/// @title Allo Interface | |
/// @author @thelostone-mc <aditya@gitcoin.co>, @0xKurt <kurt@gitcoin.co>, @codenamejason <jason@gitcoin.co>, @0xZakk <zakk@gitcoin.co>, @nfrgosselin <nate@gitcoin.co> | |
/// @notice Interface for the Allo contract. It exposes all functions needed to use the Allo protocol. | |
interface IAllo { | |
/// ====================== | |
/// ======= Structs ====== | |
/// ====================== | |
/// @notice the Pool struct that all strategy pools are based from | |
struct Pool { | |
bytes32 profileId; | |
IStrategy strategy; | |
address token; | |
Metadata metadata; | |
bytes32 managerRole; | |
bytes32 adminRole; | |
} | |
/// ====================== | |
/// ======= Events ======= | |
/// ====================== | |
/// @notice Event emitted when a new pool is created | |
/// @param poolId ID of the pool created | |
/// @param profileId ID of the profile the pool is associated with | |
/// @param strategy Address of the strategy contract | |
/// @param token Address of the token pool was funded with when created | |
/// @param amount Amount pool was funded with when created | |
/// @param metadata Pool metadata | |
event PoolCreated( | |
uint256 indexed poolId, | |
bytes32 indexed profileId, | |
IStrategy strategy, | |
address token, | |
uint256 amount, | |
Metadata metadata | |
); | |
/// @notice Emitted when a pools metadata is updated | |
/// @param poolId ID of the pool updated | |
/// @param metadata Pool metadata that was updated | |
event PoolMetadataUpdated(uint256 indexed poolId, Metadata metadata); | |
/// @notice Emitted when a pool is funded | |
/// @param poolId ID of the pool funded | |
/// @param amount Amount funded to the pool | |
/// @param fee Amount of the fee paid to the treasury | |
event PoolFunded(uint256 indexed poolId, uint256 amount, uint256 fee); | |
/// @notice Emitted when the base fee is paid | |
/// @param poolId ID of the pool the base fee was paid for | |
/// @param amount Amount of the base fee paid | |
event BaseFeePaid(uint256 indexed poolId, uint256 amount); | |
/// @notice Emitted when the treasury address is updated | |
/// @param treasury Address of the new treasury | |
event TreasuryUpdated(address treasury); | |
/// @notice Emitted when the percent fee is updated | |
/// @param percentFee New percentage for the fee | |
event PercentFeeUpdated(uint256 percentFee); | |
/// @notice Emitted when the base fee is updated | |
/// @param baseFee New base fee amount | |
event BaseFeeUpdated(uint256 baseFee); | |
/// @notice Emitted when the registry address is updated | |
/// @param registry Address of the new registry | |
event RegistryUpdated(address registry); | |
/// @notice Emitted when a strategy is approved and added to the cloneable strategies | |
/// @param strategy Address of the strategy approved | |
event StrategyApproved(address strategy); | |
/// @notice Emitted when a strategy is removed from the cloneable strategies | |
/// @param strategy Address of the strategy removed | |
event StrategyRemoved(address strategy); | |
/// ==================================== | |
/// ==== External/Public Functions ===== | |
/// ==================================== | |
/// @notice Initialize the Allo contract | |
/// @param _owner Address of the owner | |
/// @param _registry Address of the registry contract | |
/// @param _treasury Address of the treasury | |
/// @param _percentFee Percentage for the fee | |
/// @param _baseFee Base fee amount | |
function initialize( | |
address _owner, | |
address _registry, | |
address payable _treasury, | |
uint256 _percentFee, | |
uint256 _baseFee | |
) external; | |
/// @notice Creates a new pool (with a custom strategy) | |
/// @dev 'msg.sender' must be a member or owner of a profile to create a pool with or without a custom strategy, The encoded data | |
/// will be specific to a given strategy requirements, reference the strategy implementation of 'initialize()'. The strategy | |
/// address passed must not be a cloneable strategy. The strategy address passed must not be the zero address. 'msg.sender' must | |
/// be a member or owner of the profile id passed as '_profileId'. | |
/// @param _profileId The 'profileId' of the registry profile, used to check if 'msg.sender' is a member or owner of the profile | |
/// @param _strategy The address of the deployed custom strategy | |
/// @param _initStrategyData The data to initialize the strategy | |
/// @param _token The address of the token you want to use in your pool | |
/// @param _amount The amount of the token you want to deposit into the pool on initialization | |
/// @param _metadata The 'Metadata' of the pool, this uses our 'Meatdata.sol' struct (consistent throughout the protocol) | |
/// @param _managers The managers of the pool, and can be added/removed later by the pool admin | |
/// @return poolId The ID of the pool | |
function createPoolWithCustomStrategy( | |
bytes32 _profileId, | |
address _strategy, | |
bytes memory _initStrategyData, | |
address _token, | |
uint256 _amount, | |
Metadata memory _metadata, | |
address[] memory _managers | |
) external payable returns (uint256 poolId); | |
/// @notice Creates a new pool (by cloning a cloneable strategies). | |
/// @dev 'msg.sender' must be owner or member of the profile id passed as '_profileId'. | |
/// @param _profileId The ID of the registry profile, used to check if 'msg.sender' is a member or owner of the profile | |
/// @param _strategy The address of the strategy contract the pool will use. | |
/// @param _initStrategyData The data to initialize the strategy | |
/// @param _token The address of the token | |
/// @param _amount The amount of the token | |
/// @param _metadata The metadata of the pool | |
/// @param _managers The managers of the pool | |
/// @custom:initstrategydata The encoded data will be specific to a given strategy requirements, | |
/// reference the strategy implementation of 'initialize()' | |
function createPool( | |
bytes32 _profileId, | |
address _strategy, | |
bytes memory _initStrategyData, | |
address _token, | |
uint256 _amount, | |
Metadata memory _metadata, | |
address[] memory _managers | |
) external payable returns (uint256 poolId); | |
/// @notice Updates a pools metadata. | |
/// @dev 'msg.sender' must be a pool admin. | |
/// @param _poolId The ID of the pool to update | |
/// @param _metadata The new metadata to set | |
function updatePoolMetadata(uint256 _poolId, Metadata memory _metadata) external; | |
/// @notice Update the registry address. | |
/// @dev 'msg.sender' must be the Allo contract owner. | |
/// @param _registry The new registry address | |
function updateRegistry(address _registry) external; | |
/// @notice Updates the treasury address. | |
/// @dev 'msg.sender' must be the Allo contract owner. | |
/// @param _treasury The new treasury address | |
function updateTreasury(address payable _treasury) external; | |
/// @notice Updates the percentage for the fee. | |
/// @dev 'msg.sender' must be the Allo contract owner. | |
/// @param _percentFee The new percentage for the fee | |
function updatePercentFee(uint256 _percentFee) external; | |
/// @notice Updates the base fee. | |
/// @dev 'msg.sender' must be the Allo contract owner. | |
/// @param _baseFee The new base fee | |
function updateBaseFee(uint256 _baseFee) external; | |
/// @notice Adds a strategy to the cloneable strategies. | |
/// @dev 'msg.sender' must be the Allo contract owner. | |
/// @param _strategy The address of the strategy to add | |
function addToCloneableStrategies(address _strategy) external; | |
/// @notice Removes a strategy from the cloneable strategies. | |
/// @dev 'msg.sender' must be the Allo contract owner. | |
/// @param _strategy The address of the strategy to remove | |
function removeFromCloneableStrategies(address _strategy) external; | |
/// @notice Adds a pool manager to the pool. | |
/// @dev 'msg.sender' must be a pool admin. | |
/// @param _poolId The ID of the pool to add the manager to | |
/// @param _manager The address of the manager to add | |
function addPoolManager(uint256 _poolId, address _manager) external; | |
/// @notice Removes a pool manager from the pool. | |
/// @dev 'msg.sender' must be a pool admin. | |
/// @param _poolId The ID of the pool to remove the manager from | |
/// @param _manager The address of the manager to remove | |
function removePoolManager(uint256 _poolId, address _manager) external; | |
/// @notice Recovers funds from a pool. | |
/// @dev 'msg.sender' must be a pool admin. | |
/// @param _token The token to recover | |
/// @param _recipient The address to send the recovered funds to | |
function recoverFunds(address _token, address _recipient) external; | |
/// @notice Registers a recipient and emits {Registered} event if successful and may be handled differently by each strategy. | |
/// @param _poolId The ID of the pool to register the recipient for | |
function registerRecipient(uint256 _poolId, bytes memory _data) external payable returns (address); | |
/// @notice Registers a batch of recipients. | |
/// @param _poolIds The pool ID's to register the recipients for | |
/// @param _data The data to pass to the strategy and may be handled differently by each strategy | |
function batchRegisterRecipient(uint256[] memory _poolIds, bytes[] memory _data) | |
external | |
returns (address[] memory); | |
/// @notice Funds a pool. | |
/// @dev 'msg.value' must be greater than 0 if the token is the native token | |
/// or '_amount' must be greater than 0 if the token is not the native token. | |
/// @param _poolId The ID of the pool to fund | |
/// @param _amount The amount to fund the pool with | |
function fundPool(uint256 _poolId, uint256 _amount) external payable; | |
/// @notice Allocates funds to a recipient. | |
/// @dev Each strategy will handle the allocation of funds differently. | |
/// @param _poolId The ID of the pool to allocate funds from | |
/// @param _data The data to pass to the strategy and may be handled differently by each strategy. | |
function allocate(uint256 _poolId, bytes memory _data) external payable; | |
/// @notice Allocates funds to multiple recipients. | |
/// @dev Each strategy will handle the allocation of funds differently | |
function batchAllocate(uint256[] calldata _poolIds, bytes[] memory _datas) external; | |
/// @notice Distributes funds to recipients and emits {Distributed} event if successful | |
/// @dev Each strategy will handle the distribution of funds differently | |
/// @param _poolId The ID of the pool to distribute from | |
/// @param _recipientIds The recipient ids to distribute to | |
/// @param _data The data to pass to the strategy and may be handled differently by each strategy | |
function distribute(uint256 _poolId, address[] memory _recipientIds, bytes memory _data) external; | |
/// ========================= | |
/// ==== View Functions ===== | |
/// ========================= | |
/// @notice Checks if an address is a pool admin. | |
/// @param _poolId The ID of the pool to check | |
/// @param _address The address to check | |
/// @return 'true' if the '_address' is a pool admin, otherwise 'false' | |
function isPoolAdmin(uint256 _poolId, address _address) external view returns (bool); | |
/// @notice Checks if an address is a pool manager. | |
/// @param _poolId The ID of the pool to check | |
/// @param _address The address to check | |
/// @return 'true' if the '_address' is a pool manager, otherwise 'false' | |
function isPoolManager(uint256 _poolId, address _address) external view returns (bool); | |
/// @notice Checks if a strategy is cloneable (is in the cloneableStrategies mapping). | |
/// @param _strategy The address of the strategy to check | |
/// @return 'true' if the '_strategy' is cloneable, otherwise 'false' | |
function isCloneableStrategy(address _strategy) external view returns (bool); | |
/// @notice Returns the address of the strategy for a given 'poolId' | |
/// @param _poolId The ID of the pool to check | |
/// @return strategy The address of the strategy for the ID of the pool passed in | |
function getStrategy(uint256 _poolId) external view returns (address); | |
/// @notice Returns the current percent fee | |
/// @return percentFee The current percentage for the fee | |
function getPercentFee() external view returns (uint256); | |
/// @notice Returns the current base fee | |
/// @return baseFee The current base fee | |
function getBaseFee() external view returns (uint256); | |
/// @notice Returns the current treasury address | |
/// @return treasury The current treasury address | |
function getTreasury() external view returns (address payable); | |
/// @notice Returns the current registry address | |
/// @return registry The current registry address | |
function getRegistry() external view returns (IRegistry); | |
/// @notice Returns the 'Pool' struct for a given 'poolId' | |
/// @param _poolId The ID of the pool to check | |
/// @return pool The 'Pool' struct for the ID of the pool passed in | |
function getPool(uint256 _poolId) external view returns (Pool memory); | |
/// @notice Returns the current fee denominator | |
/// @dev 1e18 represents 100% | |
/// @return feeDenominator The current fee denominator | |
function getFeeDenominator() external view returns (uint256); | |
} | |
// contracts/core/interfaces/IStrategy.sol | |
// Interfaces | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⢿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⡟⠘⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣾⠻⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⡿⠀⠀⠸⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⢀⣠⣴⣴⣶⣶⣶⣦⣦⣀⡀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⡿⠃⠀⠙⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠁⠀⠀⠀⢻⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠘⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⠃⠀⠀⠀⠀⠈⢿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⣰⣿⣿⣿⡿⠋⠁⠀⠀⠈⠘⠹⣿⣿⣿⣿⣆⠀⠀⠀ | |
// ⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠈⢿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⠏⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡀⠀⠀ | |
// ⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣟⠀⡀⢀⠀⡀⢀⠀⡀⢈⢿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⡇⠀⠀ | |
// ⠀⠀⣠⣿⣿⣿⣿⣿⣿⡿⠋⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⡿⢿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣷⡀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠸⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠂⠀⠀ | |
// ⠀⠀⠙⠛⠿⠻⠻⠛⠉⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣧⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⢻⣿⣿⣿⣷⣀⢀⠀⠀⠀⡀⣰⣾⣿⣿⣿⠏⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣧⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠹⢿⣿⣿⣿⣿⣾⣾⣷⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ | |
// allo.gitcoin.co | |
/// @title IStrategy Interface | |
/// @author @thelostone-mc <aditya@gitcoin.co>, @0xKurt <kurt@gitcoin.co>, @codenamejason <jason@gitcoin.co>, @0xZakk <zakk@gitcoin.co>, @nfrgosselin <nate@gitcoin.co> @0xZakk <zakk@gitcoin.co>, @nfrgosselin <nate@gitcoin.co> | |
/// @notice BaseStrategy is the base contract that all strategies should inherit from and uses this interface. | |
interface IStrategy { | |
/// ====================== | |
/// ======= Storage ====== | |
/// ====================== | |
/// @notice The Status enum that all recipients are based from | |
enum Status { | |
None, | |
Pending, | |
Accepted, | |
Rejected, | |
Appealed, | |
InReview, | |
Canceled | |
} | |
/// @notice Payout summary struct to hold the payout data | |
struct PayoutSummary { | |
address recipientAddress; | |
uint256 amount; | |
} | |
/// ====================== | |
/// ======= Events ======= | |
/// ====================== | |
/// @notice Emitted when strategy is initialized. | |
/// @param poolId The ID of the pool | |
/// @param data The data passed to the 'initialize' function | |
event Initialized(uint256 poolId, bytes data); | |
/// @notice Emitted when a recipient is registered. | |
/// @param recipientId The ID of the recipient | |
/// @param data The data passed to the 'registerRecipient' function | |
/// @param sender The sender | |
event Registered(address indexed recipientId, bytes data, address sender); | |
/// @notice Emitted when a recipient is allocated to. | |
/// @param recipientId The ID of the recipient | |
/// @param amount The amount allocated | |
/// @param token The token allocated | |
event Allocated(address indexed recipientId, uint256 amount, address token, address sender); | |
/// @notice Emitted when tokens are distributed. | |
/// @param recipientId The ID of the recipient | |
/// @param recipientAddress The recipient | |
/// @param amount The amount distributed | |
/// @param sender The sender | |
event Distributed(address indexed recipientId, address recipientAddress, uint256 amount, address sender); | |
/// @notice Emitted when pool is set to active status. | |
/// @param active The status of the pool | |
event PoolActive(bool active); | |
/// ====================== | |
/// ======= Views ======== | |
/// ====================== | |
/// @notice Getter for the address of the Allo contract. | |
/// @return The 'Allo' contract | |
function getAllo() external view returns (IAllo); | |
/// @notice Getter for the 'poolId' for this strategy. | |
/// @return The ID of the pool | |
function getPoolId() external view returns (uint256); | |
/// @notice Getter for the 'id' of the strategy. | |
/// @return The ID of the strategy | |
function getStrategyId() external view returns (bytes32); | |
/// @notice Checks whether a allocator is valid or not, will usually be true for all strategies | |
/// and will depend on the strategy implementation. | |
/// @param _allocator The allocator to check | |
/// @return Whether the allocator is valid or not | |
function isValidAllocator(address _allocator) external view returns (bool); | |
/// @notice whether pool is active. | |
/// @return Whether the pool is active or not | |
function isPoolActive() external returns (bool); | |
/// @notice Checks the amount of tokens in the pool. | |
/// @return The balance of the pool | |
function getPoolAmount() external view returns (uint256); | |
/// @notice Increases the balance of the pool. | |
/// @param _amount The amount to increase the pool by | |
function increasePoolAmount(uint256 _amount) external; | |
/// @notice Checks the status of a recipient probably tracked in a mapping, but will depend on the implementation | |
/// for example, the OpenSelfRegistration only maps users to bool, and then assumes Accepted for those | |
/// since there is no need for Pending or Rejected. | |
/// @param _recipientId The ID of the recipient | |
/// @return The status of the recipient | |
function getRecipientStatus(address _recipientId) external view returns (Status); | |
/// @notice Checks the amount allocated to a recipient for distribution. | |
/// @dev Input the values you would send to distribute(), get the amounts each recipient in the array would receive. | |
/// The encoded '_data' will be determined by the strategy, and will be used to determine the payout. | |
/// @param _recipientIds The IDs of the recipients | |
/// @param _data The encoded data | |
function getPayouts(address[] memory _recipientIds, bytes[] memory _data) | |
external | |
view | |
returns (PayoutSummary[] memory); | |
/// ====================== | |
/// ===== Functions ====== | |
/// ====================== | |
/// @notice | |
/// @dev The default BaseStrategy version will not use the data if a strategy wants to use it, they will overwrite it, | |
/// use it, and then call super.initialize(). | |
/// @param _poolId The ID of the pool | |
/// @param _data The encoded data | |
function initialize(uint256 _poolId, bytes memory _data) external; | |
/// @notice This will register a recipient, set their status (and any other strategy specific values), and | |
/// return the ID of the recipient. | |
/// @dev Able to change status all the way up to 'Accepted', or to 'Pending' and if there are more steps, additional | |
/// functions should be added to allow the owner to check this. The owner could also check attestations directly | |
/// and then accept for instance. The '_data' will be determined by the strategy implementation. | |
/// @param _data The data to use to register the recipient | |
/// @param _sender The address of the sender | |
/// @return The ID of the recipient | |
function registerRecipient(bytes memory _data, address _sender) external payable returns (address); | |
/// @notice This will allocate to a recipient. | |
/// @dev The encoded '_data' will be determined by the strategy implementation. | |
/// @param _data The data to use to allocate to the recipient | |
/// @param _sender The address of the sender | |
function allocate(bytes memory _data, address _sender) external payable; | |
/// @notice This will distribute funds (tokens) to recipients. | |
/// @dev most strategies will track a TOTAL amount per recipient, and a PAID amount, and pay the difference | |
/// this contract will need to track the amount paid already, so that it doesn't double pay. | |
function distribute(address[] memory _recipientIds, bytes memory _data, address _sender) external; | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol | |
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) | |
/** | |
* @dev Implementation of the {IERC165} interface. | |
* | |
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check | |
* for the additional interface id that will be supported. For example: | |
* | |
* ```solidity | |
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { | |
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); | |
* } | |
* ``` | |
* | |
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. | |
*/ | |
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable { | |
function __ERC165_init() internal onlyInitializing { | |
} | |
function __ERC165_init_unchained() internal onlyInitializing { | |
} | |
/** | |
* @dev See {IERC165-supportsInterface}. | |
*/ | |
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { | |
return interfaceId == type(IERC165Upgradeable).interfaceId; | |
} | |
/** | |
* @dev This empty reserved space is put in place to allow future versions to add new | |
* variables without shifting down storage in the inheritance chain. | |
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps | |
*/ | |
uint256[50] private __gap; | |
} | |
// lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol | |
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol) | |
/** | |
* @dev Contract module that allows children to implement role-based access | |
* control mechanisms. This is a lightweight version that doesn't allow enumerating role | |
* members except through off-chain means by accessing the contract event logs. Some | |
* applications may benefit from on-chain enumerability, for those cases see | |
* {AccessControlEnumerable}. | |
* | |
* Roles are referred to by their `bytes32` identifier. These should be exposed | |
* in the external API and be unique. The best way to achieve this is by | |
* using `public constant` hash digests: | |
* | |
* ```solidity | |
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); | |
* ``` | |
* | |
* Roles can be used to represent a set of permissions. To restrict access to a | |
* function call, use {hasRole}: | |
* | |
* ```solidity | |
* function foo() public { | |
* require(hasRole(MY_ROLE, msg.sender)); | |
* ... | |
* } | |
* ``` | |
* | |
* Roles can be granted and revoked dynamically via the {grantRole} and | |
* {revokeRole} functions. Each role has an associated admin role, and only | |
* accounts that have a role's admin role can call {grantRole} and {revokeRole}. | |
* | |
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means | |
* that only accounts with this role will be able to grant or revoke other | |
* roles. More complex role relationships can be created by using | |
* {_setRoleAdmin}. | |
* | |
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to | |
* grant and revoke this role. Extra precautions should be taken to secure | |
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} | |
* to enforce additional security measures for this role. | |
*/ | |
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable { | |
function __AccessControl_init() internal onlyInitializing { | |
} | |
function __AccessControl_init_unchained() internal onlyInitializing { | |
} | |
struct RoleData { | |
mapping(address => bool) members; | |
bytes32 adminRole; | |
} | |
mapping(bytes32 => RoleData) private _roles; | |
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; | |
/** | |
* @dev Modifier that checks that an account has a specific role. Reverts | |
* with a standardized message including the required role. | |
* | |
* The format of the revert reason is given by the following regular expression: | |
* | |
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ | |
* | |
* _Available since v4.1._ | |
*/ | |
modifier onlyRole(bytes32 role) { | |
_checkRole(role); | |
_; | |
} | |
/** | |
* @dev See {IERC165-supportsInterface}. | |
*/ | |
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { | |
return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId); | |
} | |
/** | |
* @dev Returns `true` if `account` has been granted `role`. | |
*/ | |
function hasRole(bytes32 role, address account) public view virtual override returns (bool) { | |
return _roles[role].members[account]; | |
} | |
/** | |
* @dev Revert with a standard message if `_msgSender()` is missing `role`. | |
* Overriding this function changes the behavior of the {onlyRole} modifier. | |
* | |
* Format of the revert message is described in {_checkRole}. | |
* | |
* _Available since v4.6._ | |
*/ | |
function _checkRole(bytes32 role) internal view virtual { | |
_checkRole(role, _msgSender()); | |
} | |
/** | |
* @dev Revert with a standard message if `account` is missing `role`. | |
* | |
* The format of the revert reason is given by the following regular expression: | |
* | |
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ | |
*/ | |
function _checkRole(bytes32 role, address account) internal view virtual { | |
if (!hasRole(role, account)) { | |
revert( | |
string( | |
abi.encodePacked( | |
"AccessControl: account ", | |
StringsUpgradeable.toHexString(account), | |
" is missing role ", | |
StringsUpgradeable.toHexString(uint256(role), 32) | |
) | |
) | |
); | |
} | |
} | |
/** | |
* @dev Returns the admin role that controls `role`. See {grantRole} and | |
* {revokeRole}. | |
* | |
* To change a role's admin, use {_setRoleAdmin}. | |
*/ | |
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { | |
return _roles[role].adminRole; | |
} | |
/** | |
* @dev Grants `role` to `account`. | |
* | |
* If `account` had not been already granted `role`, emits a {RoleGranted} | |
* event. | |
* | |
* Requirements: | |
* | |
* - the caller must have ``role``'s admin role. | |
* | |
* May emit a {RoleGranted} event. | |
*/ | |
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { | |
_grantRole(role, account); | |
} | |
/** | |
* @dev Revokes `role` from `account`. | |
* | |
* If `account` had been granted `role`, emits a {RoleRevoked} event. | |
* | |
* Requirements: | |
* | |
* - the caller must have ``role``'s admin role. | |
* | |
* May emit a {RoleRevoked} event. | |
*/ | |
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { | |
_revokeRole(role, account); | |
} | |
/** | |
* @dev Revokes `role` from the calling account. | |
* | |
* Roles are often managed via {grantRole} and {revokeRole}: this function's | |
* purpose is to provide a mechanism for accounts to lose their privileges | |
* if they are compromised (such as when a trusted device is misplaced). | |
* | |
* If the calling account had been revoked `role`, emits a {RoleRevoked} | |
* event. | |
* | |
* Requirements: | |
* | |
* - the caller must be `account`. | |
* | |
* May emit a {RoleRevoked} event. | |
*/ | |
function renounceRole(bytes32 role, address account) public virtual override { | |
require(account == _msgSender(), "AccessControl: can only renounce roles for self"); | |
_revokeRole(role, account); | |
} | |
/** | |
* @dev Grants `role` to `account`. | |
* | |
* If `account` had not been already granted `role`, emits a {RoleGranted} | |
* event. Note that unlike {grantRole}, this function doesn't perform any | |
* checks on the calling account. | |
* | |
* May emit a {RoleGranted} event. | |
* | |
* [WARNING] | |
* ==== | |
* This function should only be called from the constructor when setting | |
* up the initial roles for the system. | |
* | |
* Using this function in any other way is effectively circumventing the admin | |
* system imposed by {AccessControl}. | |
* ==== | |
* | |
* NOTE: This function is deprecated in favor of {_grantRole}. | |
*/ | |
function _setupRole(bytes32 role, address account) internal virtual { | |
_grantRole(role, account); | |
} | |
/** | |
* @dev Sets `adminRole` as ``role``'s admin role. | |
* | |
* Emits a {RoleAdminChanged} event. | |
*/ | |
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { | |
bytes32 previousAdminRole = getRoleAdmin(role); | |
_roles[role].adminRole = adminRole; | |
emit RoleAdminChanged(role, previousAdminRole, adminRole); | |
} | |
/** | |
* @dev Grants `role` to `account`. | |
* | |
* Internal function without access restriction. | |
* | |
* May emit a {RoleGranted} event. | |
*/ | |
function _grantRole(bytes32 role, address account) internal virtual { | |
if (!hasRole(role, account)) { | |
_roles[role].members[account] = true; | |
emit RoleGranted(role, account, _msgSender()); | |
} | |
} | |
/** | |
* @dev Revokes `role` from `account`. | |
* | |
* Internal function without access restriction. | |
* | |
* May emit a {RoleRevoked} event. | |
*/ | |
function _revokeRole(bytes32 role, address account) internal virtual { | |
if (hasRole(role, account)) { | |
_roles[role].members[account] = false; | |
emit RoleRevoked(role, account, _msgSender()); | |
} | |
} | |
/** | |
* @dev This empty reserved space is put in place to allow future versions to add new | |
* variables without shifting down storage in the inheritance chain. | |
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps | |
*/ | |
uint256[49] private __gap; | |
} | |
// contracts/core/Allo.sol | |
// External Libraries | |
// Interfaces | |
// Internal Libraries | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⢿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⡟⠘⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣾⠻⣿⣿⣿⣿⣿⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⡿⠀⠀⠸⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠀⠀⢀⣠⣴⣴⣶⣶⣶⣦⣦⣀⡀⠀⠀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⣴⣿⣿⣿⣿⣿⣿⡿⠃⠀⠙⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠁⠀⠀⠀⢻⣿⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡀⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠘⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⣿⠃⠀⠀⠀⠀⠈⢿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⣰⣿⣿⣿⡿⠋⠁⠀⠀⠈⠘⠹⣿⣿⣿⣿⣆⠀⠀⠀ | |
// ⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣿⣿⡿⠀⠀⠀⠀⠀⠀⠈⢿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⠏⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⡀⠀⠀ | |
// ⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣟⠀⡀⢀⠀⡀⢀⠀⡀⢈⢿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⡇⠀⠀ | |
// ⠀⠀⣠⣿⣿⣿⣿⣿⣿⡿⠋⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⡿⢿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣷⡀⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠸⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠂⠀⠀ | |
// ⠀⠀⠙⠛⠿⠻⠻⠛⠉⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣧⠀⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⢻⣿⣿⣿⣷⣀⢀⠀⠀⠀⡀⣰⣾⣿⣿⣿⠏⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣧⠀⠀⢸⣿⣿⣿⣗⠀⠀⠀⢸⣿⣿⣿⡯⠀⠀⠀⠀⠹⢿⣿⣿⣿⣿⣾⣾⣷⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀ | |
// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠙⠋⠛⠙⠋⠛⠙⠋⠛⠙⠋⠃⠀⠀⠀⠀⠀⠀⠀⠀⠠⠿⠻⠟⠿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⠟⠿⠟⠿⠆⠀⠸⠿⠿⠟⠯⠀⠀⠀⠸⠿⠿⠿⠏⠀⠀⠀⠀⠀⠈⠉⠻⠻⡿⣿⢿⡿⡿⠿⠛⠁⠀⠀⠀⠀⠀⠀ | |
// allo.gitcoin.co | |
/// @title Allo | |
/// @author @thelostone-mc <aditya@gitcoin.co>, @0xKurt <kurt@gitcoin.co>, @codenamejason <jason@gitcoin.co>, @0xZakk <zakk@gitcoin.co>, @nfrgosselin <nate@gitcoin.co> | |
/// @notice This contract is used to create & manage pools as well as manage the protocol. | |
/// @dev The contract must be initialized with the 'initialize()' function. | |
contract Allo is | |
IAllo, | |
Native, | |
Transfer, | |
Initializable, | |
Ownable, | |
AccessControlUpgradeable, | |
ReentrancyGuardUpgradeable, | |
Errors | |
{ | |
// ========================== | |
// === Storage Variables ==== | |
// ========================== | |
/// @notice Percentage that is used to calculate the fee Allo takes from each pool when funded | |
/// and is deducted when a pool is funded. So if you want to fund a round with 1000 DAI and the fee | |
/// percentage is 1e17 (10%), then 100 DAI will be deducted from the 1000 DAI and the pool will be | |
/// funded with 900 DAI. The fee is then sent to the treasury address. | |
/// @dev How the percentage is represented in our contracts: 1e18 = 100%, 1e17 = 10%, 1e16 = 1%, 1e15 = 0.1% | |
uint256 private percentFee; | |
/// @notice Fee Allo charges for all pools on creation | |
/// @dev This is different from the 'percentFee' in that this is a flat fee and not a percentage. So if you want to create a pool | |
/// with a base fee of 100 DAI, then you would pass 100 DAI to the 'createPool()' function and the pool would be created | |
/// with 100 DAI less than the amount you passed to the function. The base fee is sent to the treasury address. | |
uint256 internal baseFee; | |
/// @notice Incremental index to track the pools created | |
uint256 private _poolIndex; | |
/// @notice Allo treasury | |
address payable private treasury; | |
/// @notice Registry contract | |
IRegistry private registry; | |
/// @notice Maps the `msg.sender` to a `nonce` to prevent duplicates | |
/// @dev 'msg.sender' -> 'nonce' for cloning strategies | |
mapping(address => uint256) private _nonces; | |
/// @notice Maps the pool ID to the pool details | |
/// @dev 'Pool.id' -> 'Pool' | |
mapping(uint256 => Pool) private pools; | |
/// @notice Returns a bool for whether a strategy is cloneable or not using the strategy address as the key | |
/// @dev Strategy.address -> bool | |
mapping(address => bool) private cloneableStrategies; | |
// ==================================== | |
// =========== Initializer ============= | |
// ==================================== | |
/// @notice Initializes the contract after an upgrade | |
/// @dev During upgrade -> a higher version should be passed to reinitializer | |
/// @param _owner The owner of allo | |
/// @param _registry The address of the registry | |
/// @param _treasury The address of the treasury | |
/// @param _percentFee The percentage fee | |
/// @param _baseFee The base fee | |
function initialize( | |
address _owner, | |
address _registry, | |
address payable _treasury, | |
uint256 _percentFee, | |
uint256 _baseFee | |
) external reinitializer(1) { | |
// Initialize the owner using Solady ownable library | |
_initializeOwner(_owner); | |
// Set the address of the registry | |
_updateRegistry(_registry); | |
// Set the address of the treasury | |
_updateTreasury(_treasury); | |
// Set the fee percentage | |
_updatePercentFee(_percentFee); | |
// Set the base fee | |
_updateBaseFee(_baseFee); | |
} | |
// ==================================== | |
// =========== Modifier =============== | |
// ==================================== | |
// Both modifiers below are using OpenZeppelin's AccessControl.sol with custom roles under the hood | |
/// @notice Reverts UNAUTHORIZED() if the caller is not a pool manager | |
/// @param _poolId The pool id | |
modifier onlyPoolManager(uint256 _poolId) { | |
_checkOnlyPoolManager(_poolId); | |
_; | |
} | |
/// @notice Reverts UNAUTHORIZED() if the caller is not a pool admin | |
/// @param _poolId The pool id | |
modifier onlyPoolAdmin(uint256 _poolId) { | |
_checkOnlyPoolAdmin(_poolId); | |
_; | |
} | |
// ==================================== | |
// ==== External/Public Functions ===== | |
// ==================================== | |
/// @notice Creates a new pool (with a custom strategy) | |
/// @dev 'msg.sender' must be a member or owner of a profile to create a pool with or without a custom strategy, The encoded data | |
/// will be specific to a given strategy requirements, reference the strategy implementation of 'initialize()'. The strategy | |
/// address passed must not be a cloneable strategy. The strategy address passed must not be the zero address. 'msg.sender' must | |
/// be a member or owner of the profile id passed as '_profileId'. | |
/// @param _profileId The 'profileId' of the registry profile, used to check if 'msg.sender' is a member or owner of the profile | |
/// @param _strategy The address of the deployed custom strategy | |
/// @param _initStrategyData The data to initialize the strategy | |
/// @param _token The address of the token you want to use in your pool | |
/// @param _amount The amount of the token you want to deposit into the pool on initialization | |
/// @param _metadata The 'Metadata' of the pool, this uses our 'Meatdata.sol' struct (consistent throughout the protocol) | |
/// @param _managers The managers of the pool, and can be added/removed later by the pool admin | |
/// @return poolId The ID of the pool | |
function createPoolWithCustomStrategy( | |
bytes32 _profileId, | |
address _strategy, | |
bytes memory _initStrategyData, | |
address _token, | |
uint256 _amount, | |
Metadata memory _metadata, | |
address[] memory _managers | |
) external payable returns (uint256 poolId) { | |
// Revert if the strategy address passed is the zero address with 'ZERO_ADDRESS()' | |
if (_strategy == address(0)) revert ZERO_ADDRESS(); | |
// Revert if we already have this strategy in our cloneable mapping with 'IS_APPROVED_STRATEGY()' (only non-cloneable strategies can be used) | |
if (_isCloneableStrategy(_strategy)) revert IS_APPROVED_STRATEGY(); | |
// Call the internal '_createPool()' function and return the pool ID | |
return _createPool(_profileId, IStrategy(_strategy), _initStrategyData, _token, _amount, _metadata, _managers); | |
} | |
/// @notice Creates a new pool (by cloning a cloneable strategies). | |
/// @dev 'msg.sender' must be owner or member of the profile id passed as '_profileId'. | |
/// @param _profileId The ID of the registry profile, used to check if 'msg.sender' is a member or owner of the profile | |
/// @param _strategy The address of the strategy contract the pool will use. | |
/// @param _initStrategyData The data to initialize the strategy | |
/// @param _token The address of the token | |
/// @param _amount The amount of the token | |
/// @param _metadata The metadata of the pool | |
/// @param _managers The managers of the pool | |
/// @custom:initstrategydata The encoded data will be specific to a given strategy requirements, | |
/// reference the strategy implementation of 'initialize()' | |
function createPool( | |
bytes32 _profileId, | |
address _strategy, | |
bytes memory _initStrategyData, | |
address _token, | |
uint256 _amount, | |
Metadata memory _metadata, | |
address[] memory _managers | |
) external payable nonReentrant returns (uint256 poolId) { | |
if (!_isCloneableStrategy(_strategy)) { | |
revert NOT_APPROVED_STRATEGY(); | |
} | |
// Returns the created pool ID | |
return _createPool( | |
_profileId, | |
IStrategy(Clone.createClone(_strategy, _nonces[msg.sender]++)), | |
_initStrategyData, | |
_token, | |
_amount, | |
_metadata, | |
_managers | |
); | |
} | |
/// @notice Update pool metadata | |
/// @dev 'msg.sender' must be a pool manager. Emits 'PoolMetadataUpdated()' event. | |
/// @param _poolId ID of the pool | |
/// @param _metadata The new metadata of the pool | |
function updatePoolMetadata(uint256 _poolId, Metadata memory _metadata) external onlyPoolManager(_poolId) { | |
Pool storage pool = pools[_poolId]; | |
pool.metadata = _metadata; | |
emit PoolMetadataUpdated(_poolId, _metadata); | |
} | |
/// @notice Updates the registry address. | |
/// @dev Use this to update the registry address. 'msg.sender' must be Allo owner. | |
/// @param _registry The new registry address | |
function updateRegistry(address _registry) external onlyOwner { | |
_updateRegistry(_registry); | |
} | |
/// @notice Updates the treasury address. | |
/// @dev Use this to update the treasury address. 'msg.sender' must be Allo owner. | |
/// @param _treasury The new treasury address | |
function updateTreasury(address payable _treasury) external onlyOwner { | |
_updateTreasury(_treasury); | |
} | |
/// @notice Updates the fee percentage. | |
/// @dev Use this to update the fee percentage. 'msg.sender' must be Allo owner. | |
/// @param _percentFee The new fee | |
function updatePercentFee(uint256 _percentFee) external onlyOwner { | |
_updatePercentFee(_percentFee); | |
} | |
/// @notice Updates the base fee. | |
/// @dev Use this to update the base fee. 'msg.sender' must be Allo owner. | |
/// @param _baseFee The new base fee | |
function updateBaseFee(uint256 _baseFee) external onlyOwner { | |
_updateBaseFee(_baseFee); | |
} | |
/// @notice Add a strategy to the allowlist. | |
/// @dev Emits the 'StrategyApproved()' event. 'msg.sender' must be Allo owner. | |
/// @param _strategy The address of the strategy | |
function addToCloneableStrategies(address _strategy) external onlyOwner { | |
if (_strategy == address(0)) revert ZERO_ADDRESS(); | |
cloneableStrategies[_strategy] = true; | |
emit StrategyApproved(_strategy); | |
} | |
/// @notice Remove a strategy from the allowlist | |
/// @dev Emits 'StrategyRemoved()' event. 'msg.sender must be Allo owner. | |
/// @param _strategy The address of the strategy | |
function removeFromCloneableStrategies(address _strategy) external onlyOwner { | |
// Set the strategy to false in the cloneableStrategies mapping | |
cloneableStrategies[_strategy] = false; | |
// Emit the StrategyRemoved event | |
emit StrategyRemoved(_strategy); | |
} | |
/// @notice Add a pool manager | |
/// @dev Emits 'RoleGranted()' event. 'msg.sender' must be a pool admin. | |
/// @param _poolId ID of the pool | |
/// @param _manager The address to add | |
function addPoolManager(uint256 _poolId, address _manager) external onlyPoolAdmin(_poolId) { | |
// Reverts if the address is the zero address with 'ZERO_ADDRESS()' | |
if (_manager == address(0)) revert ZERO_ADDRESS(); | |
// Grants the pool manager role to the '_manager' address | |
_grantRole(pools[_poolId].managerRole, _manager); | |
} | |
/// @notice Remove a pool manager | |
/// @dev Emits 'RoleRevoked()' event. 'msg.sender' must be a pool admin. | |
/// @param _poolId ID of the pool | |
/// @param _manager The address to remove | |
function removePoolManager(uint256 _poolId, address _manager) external onlyPoolAdmin(_poolId) { | |
_revokeRole(pools[_poolId].managerRole, _manager); | |
} | |
/// @notice Transfer the funds recovered to the recipient | |
/// @dev 'msg.sender' must be Allo owner | |
/// @param _token The token to transfer | |
/// @param _recipient The recipient | |
function recoverFunds(address _token, address _recipient) external onlyOwner { | |
// Get the amount of the token to transfer, which is always the entire balance of the contract address | |
uint256 amount = _token == NATIVE ? address(this).balance : IERC20Upgradeable(_token).balanceOf(address(this)); | |
// Transfer the amount to the recipient (pool owner) | |
_transferAmount(_token, _recipient, amount); | |
} | |
// ==================================== | |
// ======= Strategy Functions ========= | |
// ==================================== | |
/// @notice Passes _data through to the strategy for that pool. | |
/// @dev The encoded data will be specific to a given strategy requirements, reference the strategy | |
/// implementation of registerRecipient(). | |
/// @param _poolId ID of the pool | |
/// @param _data Encoded data unique to a strategy that registerRecipient() requires | |
/// @return recipientId The recipient ID that has been registered | |
function registerRecipient(uint256 _poolId, bytes memory _data) external payable nonReentrant returns (address) { | |
// Return the recipientId (address) from the strategy | |
return pools[_poolId].strategy.registerRecipient{value: msg.value}(_data, msg.sender); | |
} | |
/// @notice Register multiple recipients to multiple pools. | |
/// @dev Returns the 'recipientIds' from the strategy that have been registered from calling this function. | |
/// Encoded data unique to a strategy that registerRecipient() requires. Encoded '_data' length must match | |
/// '_poolIds' length or this will revert with MISMATCH(). Other requirements will be determined by the strategy. | |
/// @param _poolIds ID's of the pools | |
/// @param _data An array of encoded data unique to a strategy that registerRecipient() requires. | |
/// @return recipientIds The recipient IDs that have been registered | |
function batchRegisterRecipient(uint256[] memory _poolIds, bytes[] memory _data) | |
external | |
nonReentrant | |
returns (address[] memory recipientIds) | |
{ | |
uint256 poolIdLength = _poolIds.length; | |
recipientIds = new address[](poolIdLength); | |
if (poolIdLength != _data.length) revert MISMATCH(); | |
// Loop through the '_poolIds' & '_data' and call the 'strategy.registerRecipient()' function | |
for (uint256 i; i < poolIdLength;) { | |
recipientIds[i] = pools[_poolIds[i]].strategy.registerRecipient(_data[i], msg.sender); | |
unchecked { | |
++i; | |
} | |
} | |
// Return the recipientIds that have been registered | |
return recipientIds; | |
} | |
/// @notice Fund a pool. | |
/// @dev Anyone can fund a pool and call this function. | |
/// @param _poolId ID of the pool | |
/// @param _amount The amount to be deposited into the pool | |
function fundPool(uint256 _poolId, uint256 _amount) external payable nonReentrant { | |
// if amount is 0, revert with 'NOT_ENOUGH_FUNDS()' error | |
if (_amount == 0) revert NOT_ENOUGH_FUNDS(); | |
Pool memory pool = pools[_poolId]; | |
if (pool.token == NATIVE && _amount != msg.value) revert NOT_ENOUGH_FUNDS(); | |
// Call the internal fundPool() function | |
_fundPool(_amount, _poolId, pool.strategy); | |
} | |
/// @notice Allocate to a recipient or multiple recipients. | |
/// @dev The encoded data will be specific to a given strategy requirements, reference the strategy | |
/// implementation of allocate(). | |
/// @param _poolId ID of the pool | |
/// @param _data Encoded data unique to the strategy for that pool | |
function allocate(uint256 _poolId, bytes memory _data) external payable nonReentrant { | |
_allocate(_poolId, _data); | |
} | |
/// @notice Allocate to multiple pools | |
/// @dev The encoded data will be specific to a given strategy requirements, reference the strategy | |
/// implementation of allocate(). Please note that this is not a 'payable' function, so if you | |
/// want to send funds to the strategy, you must send the funds using 'fundPool()'. | |
/// @param _poolIds IDs of the pools | |
/// @param _datas encoded data unique to the strategy for that pool | |
function batchAllocate(uint256[] calldata _poolIds, bytes[] memory _datas) external nonReentrant { | |
uint256 numPools = _poolIds.length; | |
// Reverts if the length of _poolIds does not match the length of _datas with 'MISMATCH()' error | |
if (numPools != _datas.length) revert MISMATCH(); | |
// Loop through the _poolIds & _datas and call the internal _allocate() function | |
for (uint256 i; i < numPools;) { | |
_allocate(_poolIds[i], _datas[i]); | |
unchecked { | |
++i; | |
} | |
} | |
} | |
/// @notice Distribute to a recipient or multiple recipients. | |
/// @dev The encoded data will be specific to a given strategy requirements, reference the strategy | |
/// implementation of 'strategy.distribute()'. | |
/// @param _poolId ID of the pool | |
/// @param _recipientIds Ids of the recipients of the distribution | |
/// @param _data Encoded data unique to the strategy | |
function distribute(uint256 _poolId, address[] memory _recipientIds, bytes memory _data) external nonReentrant { | |
pools[_poolId].strategy.distribute(_recipientIds, _data, msg.sender); | |
} | |
/// ==================================== | |
/// ======= Internal Functions ========= | |
/// ==================================== | |
/// @notice Internal function to check is caller is pool manager | |
/// @param _poolId The pool id | |
function _checkOnlyPoolManager(uint256 _poolId) internal view { | |
if (!_isPoolManager(_poolId, msg.sender)) revert UNAUTHORIZED(); | |
} | |
/// @notice Internal function to check is caller is pool admin | |
/// @param _poolId The pool id | |
function _checkOnlyPoolAdmin(uint256 _poolId) internal view { | |
if (!_isPoolAdmin(_poolId, msg.sender)) revert UNAUTHORIZED(); | |
} | |
/// @notice Creates a new pool. | |
/// @dev This is an internal function that is called by the 'createPool()' & 'createPoolWithCustomStrategy()' functions | |
/// It is used to create a new pool and is called by both functions. The 'msg.sender' must be a member or owner of | |
/// a profile to create a pool. | |
/// @param _profileId The ID of the profile of for pool creator in the registry | |
/// @param _strategy The address of strategy | |
/// @param _initStrategyData The data to initialize the strategy | |
/// @param _token The address of the token that the pool is denominated in | |
/// @param _amount The amount of the token to be deposited into the pool | |
/// @param _metadata The 'Metadata' of the pool | |
/// @param _managers The managers of the pool | |
/// @return poolId The ID of the pool | |
function _createPool( | |
bytes32 _profileId, | |
IStrategy _strategy, | |
bytes memory _initStrategyData, | |
address _token, | |
uint256 _amount, | |
Metadata memory _metadata, | |
address[] memory _managers | |
) internal returns (uint256 poolId) { | |
if (!registry.isOwnerOrMemberOfProfile(_profileId, msg.sender)) revert UNAUTHORIZED(); | |
poolId = ++_poolIndex; | |
// Generate the manager & admin roles for the pool (this is the way we do this throughout the protocol for consistency) | |
bytes32 POOL_MANAGER_ROLE = bytes32(poolId); | |
bytes32 POOL_ADMIN_ROLE = keccak256(abi.encodePacked(poolId, "admin")); | |
// Create the Pool instance | |
Pool memory pool = Pool({ | |
profileId: _profileId, | |
strategy: _strategy, | |
metadata: _metadata, | |
token: _token, | |
managerRole: POOL_MANAGER_ROLE, | |
adminRole: POOL_ADMIN_ROLE | |
}); | |
// Add the pool to the mapping of created pools | |
pools[poolId] = pool; | |
// Grant admin roles to the pool creator | |
_grantRole(POOL_ADMIN_ROLE, msg.sender); | |
// Set admin role for POOL_MANAGER_ROLE | |
_setRoleAdmin(POOL_MANAGER_ROLE, POOL_ADMIN_ROLE); | |
// initialize strategies | |
// Initialization is expected to revert when invoked more than once with 'ALREADY_INITIALIZED()' error | |
_strategy.initialize(poolId, _initStrategyData); | |
if (_strategy.getPoolId() != poolId || address(_strategy.getAllo()) != address(this)) revert MISMATCH(); | |
// grant pool managers roles | |
uint256 managersLength = _managers.length; | |
for (uint256 i; i < managersLength;) { | |
address manager = _managers[i]; | |
if (manager == address(0)) revert ZERO_ADDRESS(); | |
_grantRole(POOL_MANAGER_ROLE, manager); | |
unchecked { | |
++i; | |
} | |
} | |
if (baseFee > 0) { | |
// To prevent paying the baseFee from the Allo contract's balance | |
// If _token is NATIVE, then baseFee + _amount should be > than msg.value. | |
// If _token is not NATIVE, then baseFee should be > than msg.value. | |
if ((_token == NATIVE && (baseFee + _amount != msg.value)) || (_token != NATIVE && baseFee != msg.value)) { | |
revert NOT_ENOUGH_FUNDS(); | |
} | |
_transferAmount(NATIVE, treasury, baseFee); | |
emit BaseFeePaid(poolId, baseFee); | |
} | |
if (_amount > 0) { | |
_fundPool(_amount, poolId, _strategy); | |
} | |
emit PoolCreated(poolId, _profileId, _strategy, _token, _amount, _metadata); | |
} | |
/// @notice Allocate to recipient(s). | |
/// @dev Passes '_data' & 'msg.sender' through to the strategy for that pool. | |
/// This is an internal function that is called by the 'allocate()' & 'batchAllocate()' functions. | |
/// @param _poolId ID of the pool | |
/// @param _data Encoded data unique to the strategy for that pool | |
function _allocate(uint256 _poolId, bytes memory _data) internal { | |
pools[_poolId].strategy.allocate{value: msg.value}(_data, msg.sender); | |
} | |
/// @notice Fund a pool. | |
/// @dev Deducts the fee and transfers the amount to the distribution strategy. | |
/// Emits a 'PoolFunded' event. | |
/// @param _amount The amount to transfer | |
/// @param _poolId The 'poolId' for the pool you are funding | |
/// @param _strategy The address of the strategy | |
function _fundPool(uint256 _amount, uint256 _poolId, IStrategy _strategy) internal { | |
uint256 feeAmount; | |
uint256 amountAfterFee = _amount; | |
Pool storage pool = pools[_poolId]; | |
address _token = pool.token; | |
if (percentFee > 0) { | |
feeAmount = (_amount * percentFee) / getFeeDenominator(); | |
amountAfterFee -= feeAmount; | |
if (feeAmount + amountAfterFee != _amount) revert INVALID(); | |
if (_token == NATIVE) { | |
_transferAmountFrom(_token, TransferData({from: msg.sender, to: treasury, amount: feeAmount})); | |
} else { | |
uint256 balanceBeforeFee = _getBalance(_token, treasury); | |
_transferAmountFrom(_token, TransferData({from: msg.sender, to: treasury, amount: feeAmount})); | |
uint256 balanceAfterFee = _getBalance(_token, treasury); | |
// Track actual fee paid to account for fee on ERC20 token transfers | |
feeAmount = balanceAfterFee - balanceBeforeFee; | |
} | |
} | |
if (_token == NATIVE) { | |
_transferAmountFrom( | |
_token, TransferData({from: msg.sender, to: address(_strategy), amount: amountAfterFee}) | |
); | |
} else { | |
uint256 balanceBeforeFundingPool = _getBalance(_token, address(_strategy)); | |
_transferAmountFrom( | |
_token, TransferData({from: msg.sender, to: address(_strategy), amount: amountAfterFee}) | |
); | |
uint256 balanceAfterFundingPool = _getBalance(_token, address(_strategy)); | |
// Track actual fee paid to account for fee on ERC20 token transfers | |
amountAfterFee = balanceAfterFundingPool - balanceBeforeFundingPool; | |
} | |
_strategy.increasePoolAmount(amountAfterFee); | |
emit PoolFunded(_poolId, amountAfterFee, feeAmount); | |
} | |
/// @notice Checks if the strategy is an approved cloneable strategy. | |
/// @dev Internal function used by createPoolwithCustomStrategy and createPool to | |
/// determine if a strategy is in the cloneable strategy allow list. | |
/// @param _strategy The address of the strategy | |
/// @return This will return 'true' if the strategy is cloneable, otherwise 'false' | |
function _isCloneableStrategy(address _strategy) internal view returns (bool) { | |
return cloneableStrategies[_strategy]; | |
} | |
/// @notice Checks if the address is a pool admin | |
/// @dev Internal function used to determine if an address is a pool admin | |
/// @param _poolId The ID of the pool | |
/// @param _address The address to check | |
/// @return This will return 'true' if the address is a pool admin, otherwise 'false' | |
function _isPoolAdmin(uint256 _poolId, address _address) internal view returns (bool) { | |
return hasRole(pools[_poolId].adminRole, _address); | |
} | |
/// @notice Checks if the address is a pool manager | |
/// @dev Internal function used to determine if an address is a pool manager | |
/// @param _poolId The ID of the pool | |
/// @param _address The address to check | |
/// @return This will return 'true' if the address is a pool manager, otherwise 'false' | |
function _isPoolManager(uint256 _poolId, address _address) internal view returns (bool) { | |
return hasRole(pools[_poolId].managerRole, _address) || _isPoolAdmin(_poolId, _address); | |
} | |
/// @notice Updates the registry address | |
/// @dev Internal function used to update the registry address. | |
/// Emits a RegistryUpdated event. | |
/// @param _registry The new registry address | |
function _updateRegistry(address _registry) internal { | |
if (_registry == address(0)) revert ZERO_ADDRESS(); | |
registry = IRegistry(_registry); | |
emit RegistryUpdated(_registry); | |
} | |
/// @notice Updates the treasury address | |
/// @dev Internal function used to update the treasury address. | |
/// Emits a TreasuryUpdated event. | |
/// @param _treasury The new treasury address | |
function _updateTreasury(address payable _treasury) internal { | |
if (_treasury == address(0)) revert ZERO_ADDRESS(); | |
treasury = _treasury; | |
emit TreasuryUpdated(treasury); | |
} | |
/// @notice Updates the fee percentage | |
/// @dev Internal function used to update the percentage fee. | |
/// Emits a PercentFeeUpdated event. | |
/// @param _percentFee The new fee | |
function _updatePercentFee(uint256 _percentFee) internal { | |
if (_percentFee > 1e18) revert INVALID_FEE(); | |
percentFee = _percentFee; | |
emit PercentFeeUpdated(percentFee); | |
} | |
/// @notice Updates the base fee | |
/// @dev Internal function used to update the base fee. | |
/// Emits a BaseFeeUpdated event. | |
/// @param _baseFee The new base fee | |
function _updateBaseFee(uint256 _baseFee) internal { | |
baseFee = _baseFee; | |
emit BaseFeeUpdated(baseFee); | |
} | |
// ========================= | |
// ==== View Functions ===== | |
// ========================= | |
/// @notice Getter for the fee denominator | |
/// @return FEE_DENOMINATOR The fee denominator is (1e18) which represents 100% | |
function getFeeDenominator() public pure returns (uint256 FEE_DENOMINATOR) { | |
return 1e18; | |
} | |
/// @notice Checks if the address is a pool admin. | |
/// @param _poolId The ID of the pool | |
/// @param _address The address to check | |
/// @return 'true' if the address is a pool admin, otherwise 'false' | |
function isPoolAdmin(uint256 _poolId, address _address) external view returns (bool) { | |
return _isPoolAdmin(_poolId, _address); | |
} | |
/// @notice Checks if the address is a pool manager | |
/// @param _poolId The ID of the pool | |
/// @param _address The address to check | |
/// @return 'true' if the address is a pool manager, otherwise 'false' | |
function isPoolManager(uint256 _poolId, address _address) external view returns (bool) { | |
return _isPoolManager(_poolId, _address); | |
} | |
/// @notice Getter for the strategy. | |
/// @param _poolId The ID of the pool | |
/// @return The address of the strategy | |
function getStrategy(uint256 _poolId) external view returns (address) { | |
return address(pools[_poolId].strategy); | |
} | |
/// @notice Getter for fee percentage. | |
/// @return The fee percentage (1e18 = 100%) | |
function getPercentFee() external view returns (uint256) { | |
return percentFee; | |
} | |
/// @notice Getter for base fee. | |
/// @return The base fee | |
function getBaseFee() external view returns (uint256) { | |
return baseFee; | |
} | |
/// @notice Getter for treasury address. | |
/// @return The treasury address | |
function getTreasury() external view returns (address payable) { | |
return treasury; | |
} | |
/// @notice Getter for registry. | |
/// @return The registry address | |
function getRegistry() external view returns (IRegistry) { | |
return registry; | |
} | |
/// @notice Getter for if strategy is cloneable. | |
/// @param _strategy The address of the strategy | |
/// @return 'true' if the strategy is cloneable, otherwise 'false' | |
function isCloneableStrategy(address _strategy) external view returns (bool) { | |
return _isCloneableStrategy(_strategy); | |
} | |
/// @notice Getter for the 'Pool'. | |
/// @param _poolId The ID of the pool | |
/// @return The 'Pool' struct | |
function getPool(uint256 _poolId) external view returns (Pool memory) { | |
return pools[_poolId]; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment