// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.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 AccessControl is Context, IAccessControl, ERC165 {
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(IAccessControl).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 ",
Strings.toHexString(account),
" is missing role ",
Strings.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());
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* 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);
}
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
import {EnumerableSet} from
"@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IERC20Metadata} from
"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {IArrakisMetaVaultFactory} from
"./interfaces/IArrakisMetaVaultFactory.sol";
import {ICreationCode} from "./interfaces/ICreationCode.sol";
import {IManager} from "./interfaces/IManager.sol";
import {IArrakisMetaVault} from "./interfaces/IArrakisMetaVault.sol";
import {IModuleRegistry} from "./interfaces/IModuleRegistry.sol";
import {TimeLock} from "./TimeLock.sol";
import {PrivateVaultNFT} from "./PrivateVaultNFT.sol";
import {Create3} from "@create3/contracts/Create3.sol";
import {Ownable} from "@solady/contracts/auth/Ownable.sol";
/// @dev this contract will use create3 to deploy vaults.
contract ArrakisMetaVaultFactory is
IArrakisMetaVaultFactory,
Pausable,
Ownable
{
using EnumerableSet for EnumerableSet.AddressSet;
// #region immutable properties.
address public immutable moduleRegistryPublic;
address public immutable moduleRegistryPrivate;
address public immutable creationCodePublicVault;
address public immutable creationCodePrivateVault;
PrivateVaultNFT public immutable nft;
// #endregion immutable properties.
address public manager;
// #region internal properties.
EnumerableSet.AddressSet internal _publicVaults;
EnumerableSet.AddressSet internal _privateVaults;
EnumerableSet.AddressSet internal _deployers;
// #endregion internal properties.
constructor(
address owner_,
address manager_,
address moduleRegistryPublic_,
address moduleRegistryPrivate_,
address creationCodePublicVault_,
address creationCodePrivateVault_
) {
if (
owner_ == address(0) || manager_ == address(0)
|| moduleRegistryPublic_ == address(0)
|| moduleRegistryPrivate_ == address(0)
|| creationCodePublicVault_ == address(0)
|| creationCodePrivateVault_ == address(0)
) revert AddressZero();
_initializeOwner(owner_);
manager = manager_;
moduleRegistryPublic = moduleRegistryPublic_;
moduleRegistryPrivate = moduleRegistryPrivate_;
creationCodePublicVault = creationCodePublicVault_;
creationCodePrivateVault = creationCodePrivateVault_;
nft = new PrivateVaultNFT();
}
// #region pausable functions.
/// @notice function used to pause the factory.
/// @dev only callable by owner.
function pause() external onlyOwner {
_pause();
}
/// @notice function used to unpause the factory.
/// @dev only callable by owner.
function unpause() external onlyOwner {
_unpause();
}
// #endregion pausable functions.
// #region set manager.
/// @notice function used to set a new manager.
/// @param newManager_ address that will managed newly created vault.
/// @dev only callable by owner.
function setManager(address newManager_) external onlyOwner {
address oldManager = manager;
if (newManager_ == address(0)) revert AddressZero();
if (newManager_ == oldManager) revert SameManager();
manager = newManager_;
emit LogSetManager(oldManager, newManager_);
}
// #endregion set manager.
/// @notice function used to deploy ERC20 token wrapped Arrakis
/// Meta Vault.
/// @param salt_ bytes32 used to get a deterministic all chains address.
/// @param token0_ address of the first token of the token pair.
/// @param token1_ address of the second token of the token pair.
/// @param owner_ address of the owner of the vault.
/// @param beacon_ address of the beacon that will be used to create the default module.
/// @param moduleCreationPayload_ payload for initializing the module.
/// @param initManagementPayload_ data for initialize management.
/// @return vault address of the newly created Token Meta Vault.
function deployPublicVault(
bytes32 salt_,
address token0_,
address token1_,
address owner_,
address beacon_,
bytes calldata moduleCreationPayload_,
bytes calldata initManagementPayload_
) external whenNotPaused returns (address vault) {
// #region check only deployer can create public vault.
if (!_deployers.contains(msg.sender)) revert NotADeployer();
// #endregion check only deployer can create public vault.
// #region create timeLock.
address timeLock;
{
address[] memory proposers = new address[](1);
address[] memory executors = new address[](1);
proposers[0] = owner_;
executors[0] = owner_;
// NOTE let's create3 timelock or remove create3 for public vault.
timeLock = address(
new TimeLock(2 days, proposers, executors, owner_)
);
}
// #endregion create timeLock.
{
// #region get the creation code for TokenMetaVault.
bytes memory creationCode = abi.encodePacked(
ICreationCode(creationCodePublicVault).getCreationCode(
),
_getPublicVaultConstructorPayload(
timeLock, token0_, token1_
)
);
bytes32 salt = keccak256(abi.encode(msg.sender, salt_));
// #endregion get the creation code for TokenMetaVault.
vault = Create3.create3(salt, creationCode);
}
_publicVaults.add(vault);
// #region create a module.
address module;
{
bytes memory moduleCreationPayload = abi.encodePacked(
moduleCreationPayload_,
bytes32(uint256(uint160(vault)))
);
module = IModuleRegistry(moduleRegistryPublic)
.createModule(vault, beacon_, moduleCreationPayload);
}
// #endregion create a module.
IArrakisMetaVault(vault).initialize(module);
_initManagement(vault, initManagementPayload_);
emit LogPublicVaultCreation(
msg.sender,
salt_,
token0_,
token1_,
owner_,
module,
vault,
timeLock
);
}
/// @notice function used to deploy owned Arrakis
/// Meta Vault.
/// @param salt_ bytes32 needed to compute vault address deterministic way.
/// @param token0_ address of the first token of the token pair.
/// @param token1_ address of the second token of the token pair.
/// @param owner_ address of the owner of the vault.
/// @param beacon_ address of the beacon that will be used to create the default module.
/// @param moduleCreationPayload_ payload for initializing the module.
/// @param initManagementPayload_ data for initialize management.
/// @return vault address of the newly created private Meta Vault.
function deployPrivateVault(
bytes32 salt_,
address token0_,
address token1_,
address owner_,
address beacon_,
bytes calldata moduleCreationPayload_,
bytes calldata initManagementPayload_
) external whenNotPaused returns (address vault) {
// #region compute salt = salt + msg.sender.
bytes32 salt = keccak256(abi.encode(msg.sender, salt_));
// #endregion compute salt = salt + msg.sender.
// #region get the creation code for TokenMetaVault.
bytes memory creationCode = abi.encodePacked(
ICreationCode(creationCodePrivateVault).getCreationCode(),
abi.encode(
moduleRegistryPrivate,
manager,
token0_,
token1_,
address(nft)
)
);
// #endregion get the creation code for TokenMetaVault.
vault = Create3.create3(salt, creationCode);
nft.mint(owner_, uint256(uint160(vault)));
_privateVaults.add(vault);
// #region create a module.
address module;
{
bytes memory moduleCreationPayload = abi.encodePacked(
moduleCreationPayload_,
bytes32(uint256(uint160(vault)))
);
module = IModuleRegistry(moduleRegistryPrivate)
.createModule(vault, beacon_, moduleCreationPayload);
}
IArrakisMetaVault(vault).initialize(module);
// #endregion create a module.
_initManagement(vault, initManagementPayload_);
emit LogPrivateVaultCreation(
msg.sender, salt_, token0_, token1_, owner_, module, vault
);
}
/// @notice function used to grant the role to deploy to a list of addresses.
/// @param deployers_ list of addresses that owner want to grant permission to deploy.
function whitelistDeployer(address[] calldata deployers_)
external
onlyOwner
{
uint256 length = deployers_.length;
for (uint256 i; i < length; i++) {
address deployer = deployers_[i];
if (deployer == address(0)) revert AddressZero();
if (_deployers.contains(deployer)) {
revert AlreadyWhitelistedDeployer(deployer);
}
_deployers.add(deployer);
}
emit LogWhitelistDeployers(deployers_);
}
/// @notice function used to grant the role to deploy to a list of addresses.
/// @param deployers_ list of addresses that owner want to revoke permission to deploy.
function blacklistDeployer(address[] calldata deployers_)
external
onlyOwner
{
uint256 length = deployers_.length;
for (uint256 i; i < length; i++) {
address deployer = deployers_[i];
if (!_deployers.contains(deployer)) {
revert NotAlreadyADeployer(deployer);
}
_deployers.remove(deployer);
}
emit LogBlacklistDeployers(deployers_);
}
// #region view/pure function.
/// @notice get Arrakis Modular standard token name for two corresponding tokens.
/// @param token0_ address of the first token.
/// @param token1_ address of the second token.
/// @return name name of the arrakis modular token vault.
function getTokenName(
address token0_,
address token1_
) public view returns (string memory) {
string memory symbol0 = IERC20Metadata(token0_).symbol();
string memory symbol1 = IERC20Metadata(token1_).symbol();
return _append("Arrakis Public LP ", symbol0, "/", symbol1);
}
/// @notice get a list of public vaults created by this factory
/// @param startIndex_ start index
/// @param endIndex_ end index
/// @return vaults list of all created vaults.
function publicVaults(
uint256 startIndex_,
uint256 endIndex_
) external view returns (address[] memory) {
if (startIndex_ >= endIndex_) {
revert StartIndexLtEndIndex(startIndex_, endIndex_);
}
uint256 vaultsLength = numOfPublicVaults();
if (endIndex_ > vaultsLength) {
revert EndIndexGtNbOfVaults(endIndex_, vaultsLength);
}
address[] memory vs = new address[](endIndex_ - startIndex_);
for (uint256 i = startIndex_; i < endIndex_; i++) {
vs[i - startIndex_] = _publicVaults.at(i);
}
return vs;
}
/// @notice numOfPublicVaults counts the total number of public vaults in existence
/// @return result total number of vaults deployed
function numOfPublicVaults()
public
view
returns (uint256 result)
{
return _publicVaults.length();
}
/// @notice isPublicVault check if the inputed vault is a public vault.
/// @param vault_ address of the address to check.
/// @return isPublicVault true if the inputed vault is public or otherwise false.
function isPublicVault(address vault_)
external
view
returns (bool)
{
return _publicVaults.contains(vault_);
}
/// @notice get a list of private vaults created by this factory
/// @param startIndex_ start index
/// @param endIndex_ end index
/// @return vaults list of all created vaults.
function privateVaults(
uint256 startIndex_,
uint256 endIndex_
) external view returns (address[] memory) {
if (startIndex_ >= endIndex_) {
revert StartIndexLtEndIndex(startIndex_, endIndex_);
}
uint256 vaultsLength = numOfPrivateVaults();
if (endIndex_ > vaultsLength) {
revert EndIndexGtNbOfVaults(endIndex_, vaultsLength);
}
address[] memory vs = new address[](endIndex_ - startIndex_);
for (uint256 i = startIndex_; i < endIndex_; i++) {
vs[i - startIndex_] = _privateVaults.at(i);
}
return vs;
}
/// @notice numOfPrivateVaults counts the total number of private vaults in existence
/// @return result total number of vaults deployed
function numOfPrivateVaults()
public
view
returns (uint256 result)
{
return _privateVaults.length();
}
/// @notice isPrivateVault check if the inputed vault is a private vault.
/// @param vault_ address of the address to check.
/// @return isPublicVault true if the inputed vault is private or otherwise false.
function isPrivateVault(address vault_)
external
view
returns (bool)
{
return _privateVaults.contains(vault_);
}
/// @notice function used to get a list of address that can deploy public vault.
function deployers() external view returns (address[] memory) {
return _deployers.values();
}
// #endregion view/pure functions.
// #region internal functions.
function _initManagement(
address vault_,
bytes memory data_
) internal {
/// @dev to anticipate futur changes in the manager's initManagement function
/// manager should implement getInitManagementSelector function, so factory can get the
/// the right selector of the function.
bytes4 selector =
IManager(manager).getInitManagementSelector();
/// @dev for initializing management we need to know the vault address,
/// so manager should follow this pattern where vault address is the first parameter of the function.
bytes memory data = data_.length == 0
? abi.encodeWithSelector(selector, vault_)
: abi.encodePacked(
abi.encodeWithSelector(selector, vault_), data_
);
(bool success,) = manager.call(data);
if (!success) revert CallFailed();
if (!IManager(manager).isManaged(vault_)) {
revert VaultNotManaged();
}
}
function _getPublicVaultConstructorPayload(
address timeLock_,
address token0_,
address token1_
) internal view returns (bytes memory) {
string memory name = "Arrakis Public LP";
string memory symbol = "ARRAKIS";
try this.getTokenName(token0_, token1_) returns (
string memory result
) {
name = result;
} catch {} // solhint-disable-line no-empty-blocks
return abi.encode(
timeLock_,
name,
symbol,
moduleRegistryPublic,
manager,
token0_,
token1_
);
}
function _append(
string memory a_,
string memory b_,
string memory c_,
string memory d_
) internal pure returns (string memory) {
return string(abi.encodePacked(a_, b_, c_, d_));
}
// #endregion internal functions.
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library to encode strings in Base64.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol)
/// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - <brecht@loopring.org>.
library Base64 {
/// @dev Encodes `data` using the base64 encoding described in RFC 4648.
/// See: https://datatracker.ietf.org/doc/html/rfc4648
/// @param fileSafe Whether to replace '+' with '-' and '/' with '_'.
/// @param noPadding Whether to strip away the padding.
function encode(bytes memory data, bool fileSafe, bool noPadding)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let dataLength := mload(data)
if dataLength {
// Multiply by 4/3 rounded up.
// The `shl(2, ...)` is equivalent to multiplying by 4.
let encodedLength := shl(2, div(add(dataLength, 2), 3))
// Set `result` to point to the start of the free memory.
result := mload(0x40)
// Store the table into the scratch space.
// Offsetted by -1 byte so that the `mload` will load the character.
// We will rewrite the free memory pointer at `0x40` later with
// the allocated size.
// The magic constant 0x0670 will turn "-_" into "+/".
mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef")
mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670)))
// Skip the first slot, which stores the length.
let ptr := add(result, 0x20)
let end := add(ptr, encodedLength)
// Run over the input, 3 bytes at a time.
for {} 1 {} {
data := add(data, 3) // Advance 3 bytes.
let input := mload(data)
// Write 4 bytes. Optimized for fewer stack operations.
mstore8(0, mload(and(shr(18, input), 0x3F)))
mstore8(1, mload(and(shr(12, input), 0x3F)))
mstore8(2, mload(and(shr(6, input), 0x3F)))
mstore8(3, mload(and(input, 0x3F)))
mstore(ptr, mload(0x00))
ptr := add(ptr, 4) // Advance 4 bytes.
if iszero(lt(ptr, end)) { break }
}
mstore(0x40, add(end, 0x20)) // Allocate the memory.
// Equivalent to `o = [0, 2, 1][dataLength % 3]`.
let o := div(2, mod(dataLength, 3))
// Offset `ptr` and pad with '='. We can simply write over the end.
mstore(sub(ptr, o), shl(240, 0x3d3d))
// Set `o` to zero if there is padding.
o := mul(iszero(iszero(noPadding)), o)
mstore(sub(ptr, o), 0) // Zeroize the slot after the string.
mstore(result, sub(encodedLength, o)) // Store the length.
}
}
}
/// @dev Encodes `data` using the base64 encoding described in RFC 4648.
/// Equivalent to `encode(data, false, false)`.
function encode(bytes memory data) internal pure returns (string memory result) {
result = encode(data, false, false);
}
/// @dev Encodes `data` using the base64 encoding described in RFC 4648.
/// Equivalent to `encode(data, fileSafe, false)`.
function encode(bytes memory data, bool fileSafe)
internal
pure
returns (string memory result)
{
result = encode(data, fileSafe, false);
}
/// @dev Decodes base64 encoded `data`.
///
/// Supports:
/// - RFC 4648 (both standard and file-safe mode).
/// - RFC 3501 (63: ',').
///
/// Does not support:
/// - Line breaks.
///
/// Note: For performance reasons,
/// this function will NOT revert on invalid `data` inputs.
/// Outputs for invalid inputs will simply be undefined behaviour.
/// It is the user's responsibility to ensure that the `data`
/// is a valid base64 encoded string.
function decode(string memory data) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
let dataLength := mload(data)
if dataLength {
let decodedLength := mul(shr(2, dataLength), 3)
for {} 1 {} {
// If padded.
if iszero(and(dataLength, 3)) {
let t := xor(mload(add(data, dataLength)), 0x3d3d)
// forgefmt: disable-next-item
decodedLength := sub(
decodedLength,
add(iszero(byte(30, t)), iszero(byte(31, t)))
)
break
}
// If non-padded.
decodedLength := add(decodedLength, sub(and(dataLength, 3), 1))
break
}
result := mload(0x40)
// Write the length of the bytes.
mstore(result, decodedLength)
// Skip the first slot, which stores the length.
let ptr := add(result, 0x20)
let end := add(ptr, decodedLength)
// Load the table into the scratch space.
// Constants are optimized for smaller bytecode with zero gas overhead.
// `m` also doubles as the mask of the upper 6 bits.
let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc
mstore(0x5b, m)
mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064)
mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4)
for {} 1 {} {
// Read 4 bytes.
data := add(data, 4)
let input := mload(data)
// Write 3 bytes.
// forgefmt: disable-next-item
mstore(ptr, or(
and(m, mload(byte(28, input))),
shr(6, or(
and(m, mload(byte(29, input))),
shr(6, or(
and(m, mload(byte(30, input))),
shr(6, mload(byte(31, input)))
))
))
))
ptr := add(ptr, 3)
if iszero(lt(ptr, end)) { break }
}
mstore(0x40, add(end, 0x20)) // Allocate the memory.
mstore(end, 0) // Zeroize the slot after the bytes.
mstore(0x60, 0) // Restore the zero slot.
}
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
/**
@title A library for deploying contracts EIP-3171 style.
@author Agustin Aguilar <aa@horizon.io>
*/
library Create3 {
error ErrorCreatingProxy();
error ErrorCreatingContract();
error TargetAlreadyExists();
/**
@notice The bytecode for a contract that proxies the creation of another contract
@dev If this code is deployed using CREATE2 it can be used to decouple `creationCode` from the child contract address
0x67363d3d37363d34f03d5260086018f3:
0x00 0x67 0x67XXXXXXXXXXXXXXXX PUSH8 bytecode 0x363d3d37363d34f0
0x01 0x3d 0x3d RETURNDATASIZE 0 0x363d3d37363d34f0
0x02 0x52 0x52 MSTORE
0x03 0x60 0x6008 PUSH1 08 8
0x04 0x60 0x6018 PUSH1 18 24 8
0x05 0xf3 0xf3 RETURN
0x363d3d37363d34f0:
0x00 0x36 0x36 CALLDATASIZE cds
0x01 0x3d 0x3d RETURNDATASIZE 0 cds
0x02 0x3d 0x3d RETURNDATASIZE 0 0 cds
0x03 0x37 0x37 CALLDATACOPY
0x04 0x36 0x36 CALLDATASIZE cds
0x05 0x3d 0x3d RETURNDATASIZE 0 cds
0x06 0x34 0x34 CALLVALUE val 0 cds
0x07 0xf0 0xf0 CREATE addr
*/
bytes internal constant PROXY_CHILD_BYTECODE = hex"67_36_3d_3d_37_36_3d_34_f0_3d_52_60_08_60_18_f3";
// KECCAK256_PROXY_CHILD_BYTECODE = keccak256(PROXY_CHILD_BYTECODE);
bytes32 internal constant KECCAK256_PROXY_CHILD_BYTECODE = 0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;
/**
@notice Returns the size of the code on a given address
@param _addr Address that may or may not contain code
@return size of the code on the given `_addr`
*/
function codeSize(address _addr) internal view returns (uint256 size) {
assembly { size := extcodesize(_addr) }
}
/**
@notice Creates a new contract with given `_creationCode` and `_salt`
@param _salt Salt of the contract creation, resulting address will be derivated from this value only
@param _creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address
@return addr of the deployed contract, reverts on error
*/
function create3(bytes32 _salt, bytes memory _creationCode) internal returns (address addr) {
return create3(_salt, _creationCode, 0);
}
/**
@notice Creates a new contract with given `_creationCode` and `_salt`
@param _salt Salt of the contract creation, resulting address will be derivated from this value only
@param _creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address
@param _value In WEI of ETH to be forwarded to child contract
@return addr of the deployed contract, reverts on error
*/
function create3(bytes32 _salt, bytes memory _creationCode, uint256 _value) internal returns (address addr) {
// Creation code
bytes memory creationCode = PROXY_CHILD_BYTECODE;
// Get target final address
addr = addressOf(_salt);
if (codeSize(addr) != 0) revert TargetAlreadyExists();
// Create CREATE2 proxy
address proxy; assembly { proxy := create2(0, add(creationCode, 32), mload(creationCode), _salt)}
if (proxy == address(0)) revert ErrorCreatingProxy();
// Call proxy with final init code
(bool success,) = proxy.call{ value: _value }(_creationCode);
if (!success || codeSize(addr) == 0) revert ErrorCreatingContract();
}
/**
@notice Computes the resulting address of a contract deployed using address(this) and the given `_salt`
@param _salt Salt of the contract creation, resulting address will be derivated from this value only
@return addr of the deployed contract, reverts on error
@dev The address creation formula is: keccak256(rlp([keccak256(0xff ++ address(this) ++ _salt ++ keccak256(childBytecode))[12:], 0x01]))
*/
function addressOf(bytes32 _salt) internal view returns (address) {
address proxy = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex'ff',
address(this),
_salt,
KECCAK256_PROXY_CHILD_BYTECODE
)
)
)
)
);
return address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex"d6_94",
proxy,
hex"01"
)
)
)
)
);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.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 ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.0;
import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
using Address for address;
using Strings for uint256;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Mapping from token ID to owner address
mapping(uint256 => address) private _owners;
// Mapping owner address to token count
mapping(address => uint256) private _balances;
// Mapping from token ID to approved address
mapping(uint256 => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: address zero is not a valid owner");
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
address owner = _ownerOf(tokenId);
require(owner != address(0), "ERC721: invalid token ID");
return owner;
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
_requireMinted(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(
_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not token owner or approved for all"
);
_approve(to, tokenId);
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual override returns (address) {
_requireMinted(tokenId);
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual override {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_safeTransfer(from, to, tokenId, data);
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
*/
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _owners[tokenId];
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
* and stop existing when they are burned (`_burn`).
*/
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _ownerOf(tokenId) != address(0);
}
/**
* @dev Returns whether `spender` is allowed to manage `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
}
/**
* @dev Safely mints `tokenId` and transfers it to `to`.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
require(
_checkOnERC721Received(address(0), to, tokenId, data),
"ERC721: transfer to non ERC721Receiver implementer"
);
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId, 1);
// Check that tokenId was not minted by `_beforeTokenTransfer` hook
require(!_exists(tokenId), "ERC721: token already minted");
unchecked {
// Will not overflow unless all 2**256 token ids are minted to the same owner.
// Given that tokens are minted one by one, it is impossible in practice that
// this ever happens. Might change if we allow batch minting.
// The ERC fails to describe this case.
_balances[to] += 1;
}
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
_afterTokenTransfer(address(0), to, tokenId, 1);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
* This is an internal function that does not check if the sender is authorized to operate on the token.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721.ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId, 1);
// Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
owner = ERC721.ownerOf(tokenId);
// Clear approvals
delete _tokenApprovals[tokenId];
unchecked {
// Cannot overflow, as that would require more tokens to be burned/transferred
// out than the owner initially received through minting and transferring in.
_balances[owner] -= 1;
}
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
_afterTokenTransfer(owner, address(0), tokenId, 1);
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal virtual {
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId, 1);
// Check that tokenId was not transferred by `_beforeTokenTransfer` hook
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
// Clear approvals from the previous owner
delete _tokenApprovals[tokenId];
unchecked {
// `_balances[from]` cannot overflow for the same reason as described in `_burn`:
// `from`'s balance is the number of token held, which is at least one before the current
// transfer.
// `_balances[to]` could overflow in the conditions described in `_mint`. That would require
// all 2**256 token ids to be minted, which in practice is impossible.
_balances[from] -= 1;
_balances[to] += 1;
}
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
_afterTokenTransfer(from, to, tokenId, 1);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits an {Approval} event.
*/
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
require(owner != operator, "ERC721: approve to caller");
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Reverts if the `tokenId` has not been minted yet.
*/
function _requireMinted(uint256 tokenId) internal view virtual {
require(_exists(tokenId), "ERC721: invalid token ID");
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
* The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory data
) private returns (bool) {
if (to.isContract()) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
return retval == IERC721Receiver.onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC721: transfer to non ERC721Receiver implementer");
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
/**
* @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
* used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
* - When `from` is zero, the tokens will be minted for `to`.
* - When `to` is zero, ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
* - `batchSize` is non-zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
/**
* @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
* used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
* - When `from` is zero, the tokens were minted for `to`.
* - When `to` is zero, ``from``'s tokens were burned.
* - `from` and `to` are never both zero.
* - `batchSize` is non-zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
/**
* @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
*
* WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
* being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
* that `ownerOf(tokenId)` is `a`.
*/
// solhint-disable-next-line func-name-mixedcase
function __unsafe_increaseBalance(address account, uint256 amount) internal {
_balances[account] += amount;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {IERC20Metadata} from
"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IArrakisMetaVault} from "./IArrakisMetaVault.sol";
import {IOracleWrapper} from "./IOracleWrapper.sol";
/// @title Liquidity providing module interface.
/// @author Arrakis Finance
/// @notice Module interfaces, modules are implementing differents strategies that an
/// arrakis module can use.
interface IArrakisLPModule {
// #region errors.
/// @dev triggered when an address that should not
/// be zero is equal to address zero.
error AddressZero();
/// @dev triggered when the caller is different than
/// the metaVault that own this module.
error OnlyMetaVault(address caller, address metaVault);
/// @dev triggered when the caller is different than
/// the manager defined by the metaVault.
error OnlyManager(address caller, address manager);
/// @dev triggered if proportion of minting or burning is
/// zero.
error ProportionZero();
/// @dev triggered if during withdraw more than 100% of the
/// position.
error ProportionGtBASE();
/// @dev triggered when manager want to set his more
/// earned by the position than 100% of fees earned.
error NewFeesGtPIPS(uint256 newFees);
/// @dev triggered when manager is setting the same fees
/// that already active.
error SameManagerFee();
/// @dev triggered when inits values are zeros.
error InitsAreZeros();
/// @dev triggered when pause/unpaused function is
/// called by someone else than guardian.
error OnlyGuardian();
// #endregion errors.
// #region events.
/// @notice Event describing a withdrawal of participation by an user inside this module.
/// @dev withdraw action can be indexed by receiver.
/// @param receiver address that will receive the tokens withdrawn.
/// @param proportion percentage of the current position that user want to withdraw.
/// @param amount0 amount of token0 send to "receiver" due to withdraw action.
/// @param amount1 amount of token1 send to "receiver" due to withdraw action.
event LogWithdraw(
address indexed receiver,
uint256 proportion,
uint256 amount0,
uint256 amount1
);
/// @notice Event describing a manager fee withdrawal.
/// @param manager address of the manager that will fees earned due to his fund management.
/// @param amount0 amount of token0 that manager has earned and will be transfered.
/// @param amount1 amount of token1 that manager has earned and will be transfered.
event LogWithdrawManagerBalance(
address manager, uint256 amount0, uint256 amount1
);
/// @notice Event describing manager set his fees.
/// @param oldFee fees share that have been taken by manager.
/// @param newFee fees share that have been taken by manager.
event LogSetManagerFeePIPS(uint256 oldFee, uint256 newFee);
// #endregion events.
/// @notice function used to pause the module.
/// @dev only callable by guardian
function pause() external;
/// @notice function used to unpause the module.
/// @dev only callable by guardian
function unpause() external;
/// @notice function used to initialize the module
/// when a module switch happen
/// @param data_ bytes that contain information to initialize
/// the position.
function initializePosition(bytes calldata data_) external;
/// @notice function used by metaVault to withdraw tokens from the strategy.
/// @param receiver_ address that will receive tokens.
/// @param proportion_ the proportion of the total position that need to be withdrawn.
/// @return amount0 amount of token0 withdrawn.
/// @return amount1 amount of token1 withdrawn.
function withdraw(
address receiver_,
uint256 proportion_
) external returns (uint256 amount0, uint256 amount1);
/// @notice function used by metaVault or manager to get manager fees.
/// @return amount0 amount of token0 sent to manager.
/// @return amount1 amount of token1 sent to manager.
function withdrawManagerBalance()
external
returns (uint256 amount0, uint256 amount1);
/// @notice function used to set manager fees.
/// @param newFeePIPS_ new fee that will be applied.
function setManagerFeePIPS(uint256 newFeePIPS_) external;
// #region view functions.
/// @notice function used to get metaVault as IArrakisMetaVault.
/// @return metaVault that implement IArrakisMetaVault.
function metaVault() external view returns (IArrakisMetaVault);
/// @notice function used to get the address that can pause the module.
/// @return guardian address of the pauser.
function guardian() external view returns (address);
/// @notice function used to get manager token0 balance.
/// @dev amount of fees in token0 that manager have not taken yet.
/// @return managerBalance0 amount of token0 that manager earned.
function managerBalance0() external view returns (uint256);
/// @notice function used to get manager token1 balance.
/// @dev amount of fees in token1 that manager have not taken yet.
/// @return managerBalance1 amount of token1 that manager earned.
function managerBalance1() external view returns (uint256);
/// @notice function used to get manager fees.
/// @return managerFeePIPS amount of token1 that manager earned.
function managerFeePIPS() external view returns (uint256);
/// @notice function used to get token0 as IERC20Metadata.
/// @return token0 as IERC20Metadata.
function token0() external view returns (IERC20Metadata);
/// @notice function used to get token0 as IERC20Metadata.
/// @return token1 as IERC20Metadata.
function token1() external view returns (IERC20Metadata);
/// @notice function used to get the initial amounts needed to open a position.
/// @return init0 the amount of token0 needed to open a position.
/// @return init1 the amount of token1 needed to open a position.
function getInits()
external
view
returns (uint256 init0, uint256 init1);
/// @notice function used to get the amount of token0 and token1 sitting
/// on the position.
/// @return amount0 the amount of token0 sitting on the position.
/// @return amount1 the amount of token1 sitting on the position.
function totalUnderlying()
external
view
returns (uint256 amount0, uint256 amount1);
/// @notice function used to get the amounts of token0 and token1 sitting
/// on the position for a specific price.
/// @param priceX96_ price at which we want to simulate our tokens composition
/// @return amount0 the amount of token0 sitting on the position for priceX96.
/// @return amount1 the amount of token1 sitting on the position for priceX96.
function totalUnderlyingAtPrice(uint160 priceX96_)
external
view
returns (uint256 amount0, uint256 amount1);
/// @notice function used to validate if module state is not manipulated
/// before rebalance.
/// @param oracle_ oracle that will used to check internal state.
/// @param maxDeviation_ maximum deviation allowed.
function validateRebalance(
IOracleWrapper oracle_,
uint24 maxDeviation_
) external view;
// #endregion view function.
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {IArrakisLPModule} from "./IArrakisLPModule.sol";
/// @title IArrakisMetaVault
/// @notice IArrakisMetaVault is a vault that is able to invest dynamically deposited
/// tokens into protocols through his module.
interface IArrakisMetaVault {
// #region errors.
/// @dev triggered when an address that should not
/// be zero is equal to address zero.
error AddressZero(string property);
/// @dev triggered when the caller is different than
/// the manager.
error OnlyManager(address caller, address manager);
/// @dev triggered when a low level call failed during
/// execution.
error CallFailed();
/// @dev triggered when manager try to set the active
/// module as active.
error SameModule();
/// @dev triggered when owner of the vault try to set the
/// manager with the current manager.
error SameManager();
/// @dev triggered when all tokens withdrawal has been done
/// during a switch of module.
error ModuleNotEmpty(uint256 amount0, uint256 amount1);
/// @dev triggered when owner try to whitelist a module
/// that has been already whitelisted.
error AlreadyWhitelisted(address module);
/// @dev triggered when owner try to blacklist a module
/// that has not been whitelisted.
error NotWhitelistedModule(address module);
/// @dev triggered when owner try to blacklist the active module.
error ActiveModule();
/// @dev triggered during vault creation if token0 address is greater than
/// token1 address.
error Token0GtToken1();
/// @dev triggered during vault creation if token0 address is equal to
/// token1 address.
error Token0EqToken1();
/// @dev triggered when whitelisting action is occuring and module's beacon
/// is not whitelisted on module registry.
error NotWhitelistedBeacon();
/// @dev triggered when guardian of the whitelisting module is different than
/// the guardian of the registry.
error NotSameGuardian();
/// @dev triggered when a function logic is not implemented.
error NotImplemented();
/// @dev triggered when two arrays suppposed to have the same length, have different length.
error ArrayNotSameLength();
/// @dev triggered when function is called by someone else than the owner.
error OnlyOwner();
/// @dev triggered when setModule action try to remove funds.
error WithdrawNotAllowed();
/// @dev triggered when setModule function end without
/// initiliazePosition call.
error PositionNotInitialized();
/// @dev triggered when the first external call of setModule function
/// isn't InitializePosition function.
error NotPositionInitializationCall();
// #endregion errors.
// #region events.
/// @notice Event describing a manager fee withdrawal.
/// @param amount0 amount of token0 that manager has earned and will be transfered.
/// @param amount1 amount of token1 that manager has earned and will be transfered.
event LogWithdrawManagerBalance(uint256 amount0, uint256 amount1);
/// @notice Event describing owner setting the manager.
/// @param manager address of manager that will manage the portfolio.
event LogSetManager(address manager);
/// @notice Event describing manager setting the module.
/// @param module address of the new active module.
/// @param payloads data payloads for initializing positions on the new module.
event LogSetModule(address module, bytes[] payloads);
/// @notice Event describing default module that the vault will be initialized with.
/// @param module address of the default module.
event LogSetFirstModule(address module);
/// @notice Event describing list of modules that has been whitelisted by owner.
/// @param modules list of addresses corresponding to new modules now available
/// to be activated by manager.
event LogWhiteListedModules(address[] modules);
/// @notice Event describing whitelisted of the first module during vault creation.
/// @param module default activation.
event LogWhitelistedModule(address module);
/// @notice Event describing blacklisting action of modules by owner.
/// @param modules list of addresses corresponding to old modules that has been
/// blacklisted.
event LogBlackListedModules(address[] modules);
// #endregion events.
/// @notice function used to initialize default module.
/// @param module_ address of the default module.
function initialize(address module_) external;
/// @notice function used to set module
/// @param module_ address of the new module
/// @param payloads_ datas to initialize/rebalance on the new module
function setModule(
address module_,
bytes[] calldata payloads_
) external;
/// @notice function used to whitelist modules that can used by manager.
/// @param beacons_ array of beacons addresses to use for modules creation.
/// @param data_ array of payload to use for modules creation.
function whitelistModules(
address[] calldata beacons_,
bytes[] calldata data_
) external;
/// @notice function used to blacklist modules that can used by manager.
/// @param modules_ array of module addresses to be blacklisted.
function blacklistModules(address[] calldata modules_) external;
// #region view functions.
/// @notice function used to get the list of modules whitelisted.
/// @return modules whitelisted modules addresses.
function whitelistedModules()
external
view
returns (address[] memory modules);
/// @notice function used to get the amount of token0 and token1 sitting
/// on the position.
/// @return amount0 the amount of token0 sitting on the position.
/// @return amount1 the amount of token1 sitting on the position.
function totalUnderlying()
external
view
returns (uint256 amount0, uint256 amount1);
/// @notice function used to get the amounts of token0 and token1 sitting
/// on the position for a specific price.
/// @param priceX96 price at which we want to simulate our tokens composition
/// @return amount0 the amount of token0 sitting on the position for priceX96.
/// @return amount1 the amount of token1 sitting on the position for priceX96.
function totalUnderlyingAtPrice(uint160 priceX96)
external
view
returns (uint256 amount0, uint256 amount1);
/// @notice function used to get the initial amounts needed to open a position.
/// @return init0 the amount of token0 needed to open a position.
/// @return init1 the amount of token1 needed to open a position.
function getInits()
external
view
returns (uint256 init0, uint256 init1);
/// @notice function used to get the address of token0.
function token0() external view returns (address);
/// @notice function used to get the address of token1.
function token1() external view returns (address);
/// @notice function used to get manager address.
function manager() external view returns (address);
/// @notice function used to get module used to
/// open/close/manager a position.
function module() external view returns (IArrakisLPModule);
/// @notice function used to get module registry.
/// @return registry address of module registry.
function moduleRegistry()
external
view
returns (address registry);
// #endregion view functions.
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface IArrakisMetaVaultFactory {
// #region errors.
error AddressZero();
/// @dev triggered when querying vaults on factory
/// and start index is lower than end index.
error StartIndexLtEndIndex(uint256 startIndex, uint256 endIndex);
/// @dev triggered when querying vaults on factory
/// and end index of the query is bigger the biggest index of the vaults array.
error EndIndexGtNbOfVaults(
uint256 endIndex, uint256 numberOfVaults
);
/// @dev triggered when owner want to whitelist a deployer that has been already
/// whitelisted.
error AlreadyWhitelistedDeployer(address deployer);
/// @dev triggered when owner want to blackist a deployer that is not a current
/// deployer.
error NotAlreadyADeployer(address deployer);
/// @dev triggered when public vault deploy function is
/// called by an address that is not a deployer.
error NotADeployer();
/// @dev triggered when init management low level failed.
error CallFailed();
/// @dev triggered when init management happened and still the vault is
/// not under management by manager.
error VaultNotManaged();
/// @dev triggered when owner is setting a new manager, and the new manager
/// address match with the old manager address.
error SameManager();
// #endregion errors.
// #region events.
/// @notice event emitted when public vault is created by a deployer.
/// @param creator address that is creating the public vault, a deployer.
/// @param salt salt used for create3.
/// @param token0 first token of the token pair.
/// @param token1 second token of the token pair.
/// @param owner address of the owner.
/// @param module default module that will be used by the meta vault.
/// @param publicVault address of the deployed meta vault.
/// @param timeLock timeLock that will owned the meta vault.
event LogPublicVaultCreation(
address indexed creator,
bytes32 salt,
address token0,
address token1,
address owner,
address module,
address publicVault,
address timeLock
);
/// @notice event emitted when private vault is created.
/// @param creator address that is deploying the vault.
/// @param salt salt used for create3.
/// @param token0 address of the first token of the pair.
/// @param token1 address of the second token of the pair.
/// @param owner address that will owned the private vault.
/// @param module address of the default module.
/// @param privateVault address of the deployed meta vault.
event LogPrivateVaultCreation(
address indexed creator,
bytes32 salt,
address token0,
address token1,
address owner,
address module,
address privateVault
);
/// @notice event emitted when whitelisting an array of public vault
/// deployers.
/// @param deployers list of deployers added to the whitelist.
event LogWhitelistDeployers(address[] deployers);
/// @notice event emitted when blacklisting an array of public vault
/// deployers.
/// @param deployers list of deployers removed from the whitelist.
event LogBlacklistDeployers(address[] deployers);
/// @notice event emitted when owner set a new manager.
/// @param oldManager address of the previous manager.
/// @param newManager address of the new manager.
event LogSetManager(address oldManager, address newManager);
// #endregion events.
// #region state changing functions.
/// @notice function used to pause the factory.
/// @dev only callable by owner.
function pause() external;
/// @notice function used to unpause the factory.
/// @dev only callable by owner.
function unpause() external;
/// @notice function used to set a new manager.
/// @param newManager_ address that will managed newly created vault.
/// @dev only callable by owner.
function setManager(address newManager_) external;
/// @notice function used to deploy ERC20 token wrapped Arrakis
/// Meta Vault.
/// @param salt_ bytes32 used to get a deterministic all chains address.
/// @param token0_ address of the first token of the token pair.
/// @param token1_ address of the second token of the token pair.
/// @param owner_ address of the owner of the vault.
/// @param beacon_ address of the beacon that will be used to create the default module.
/// @param moduleCreationPayload_ payload for initializing the module.
/// @param initManagementPayload_ data for initialize management.
/// @return vault address of the newly created Token Meta Vault.
function deployPublicVault(
bytes32 salt_,
address token0_,
address token1_,
address owner_,
address beacon_,
bytes calldata moduleCreationPayload_,
bytes calldata initManagementPayload_
) external returns (address vault);
/// @notice function used to deploy owned Arrakis
/// Meta Vault.
/// @param salt_ bytes32 needed to compute vault address deterministic way.
/// @param token0_ address of the first token of the token pair.
/// @param token1_ address of the second token of the token pair.
/// @param owner_ address of the owner of the vault.
/// @param beacon_ address of the beacon that will be used to create the default module.
/// @param moduleCreationPayload_ payload for initializing the module.
/// @param initManagementPayload_ data for initialize management.
/// @return vault address of the newly created private Meta Vault.
function deployPrivateVault(
bytes32 salt_,
address token0_,
address token1_,
address owner_,
address beacon_,
bytes calldata moduleCreationPayload_,
bytes calldata initManagementPayload_
) external returns (address vault);
/// @notice function used to grant the role to deploy to a list of addresses.
/// @param deployers_ list of addresses that owner want to grant permission to deploy.
function whitelistDeployer(address[] calldata deployers_)
external;
/// @notice function used to grant the role to deploy to a list of addresses.
/// @param deployers_ list of addresses that owner want to revoke permission to deploy.
function blacklistDeployer(address[] calldata deployers_)
external;
// #endregion state changing functions.
// #region view/pure functions.
/// @notice get Arrakis Modular standard token name for two corresponding tokens.
/// @param token0_ address of the first token.
/// @param token1_ address of the second token.
/// @return name name of the arrakis modular token vault.
function getTokenName(
address token0_,
address token1_
) external view returns (string memory);
/// @notice get a list of public vaults created by this factory
/// @param startIndex_ start index
/// @param endIndex_ end index
/// @return vaults list of all created vaults.
function publicVaults(
uint256 startIndex_,
uint256 endIndex_
) external view returns (address[] memory);
/// @notice numOfPublicVaults counts the total number of token vaults in existence
/// @return result total number of vaults deployed
function numOfPublicVaults()
external
view
returns (uint256 result);
/// @notice isPublicVault check if the inputed vault is a public vault.
/// @param vault_ address of the address to check.
/// @return isPublicVault true if the inputed vault is public or otherwise false.
function isPublicVault(address vault_)
external
view
returns (bool);
/// @notice get a list of private vaults created by this factory
/// @param startIndex_ start index
/// @param endIndex_ end index
/// @return vaults list of all created vaults.
function privateVaults(
uint256 startIndex_,
uint256 endIndex_
) external view returns (address[] memory);
/// @notice numOfPrivateVaults counts the total number of private vaults in existence
/// @return result total number of vaults deployed
function numOfPrivateVaults()
external
view
returns (uint256 result);
/// @notice isPrivateVault check if the inputed vault is a private vault.
/// @param vault_ address of the address to check.
/// @return isPublicVault true if the inputed vault is private or otherwise false.
function isPrivateVault(address vault_)
external
view
returns (bool);
/// @notice function used to get the manager of newly deployed vault.
/// @return manager address that will manager vault that will be
/// created.
function manager() external view returns (address);
/// @notice function used to get a list of address that can deploy public vault.
function deployers() external view returns (address[] memory);
/// @notice function used to get public module registry.
function moduleRegistryPublic() external view returns (address);
/// @notice function used to get private module registry.
function moduleRegistryPrivate()
external
view
returns (address);
// #endregion view/pure functions.
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface ICreationCode {
function getCreationCode() external pure returns (bytes memory);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev _Available since v3.1._
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface IManager {
// #region external view functions.
/// @notice function used to know the selector of initManagement functions.
/// @param selector bytes4 defining the init management selector.
function getInitManagementSelector()
external
pure
returns (bytes4 selector);
/// @notice function used to know if a vault is under management by this manager.
/// @param vault_ address of the meta vault the caller want to check.
/// @return isManaged boolean which is true if the vault is under management, false otherwise.
function isManaged(address vault_)
external
view
returns (bool isManaged);
// #endregion external view functions.
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/// @title IModuleRegistry
/// @author Arrakis Team.
/// @notice interface of module registry that contains all whitelisted modules.
interface IModuleRegistry {
// #region errors.
error AddressZero();
error AlreadyWhitelistedBeacon(address beacon);
error NotAlreadyWhitelistedBeacon(address beacon);
error NotWhitelistedBeacon();
error NotBeacon();
error ModuleNotLinkedToMetaVault();
error NotSameGuardian();
error NotSameAdmin();
// #endregion errors.
// #region events.
/// @notice Log whitelist action of beacons.
/// @param beacons list of beacons whitelisted.
event LogWhitelistBeacons(address[] beacons);
/// @notice Log blacklist action of beacons.
/// @param beacons list of beacons blacklisted.
event LogBlacklistBeacons(address[] beacons);
// #endregion events.
// #region view functions.
/// @notice function to get the whitelisted list of IBeacon
/// that have module as implementation.
/// @return beacons list of upgradeable beacon.
function beacons()
external
view
returns (address[] memory beacons);
/// @notice function to know if the beacons enumerableSet contain
/// beacon_
/// @param beacon_ beacon address to check
/// @param isContained is true if beacon_ is whitelisted.
function beaconsContains(address beacon_)
external
view
returns (bool isContained);
/// @notice function used to get the guardian address of arrakis protocol.
/// @return guardian address of the pauser.
function guardian() external view returns (address);
/// @notice function used to get the admin address that can
/// upgrade beacon implementation.
/// @dev admin address should be a timelock contract.
/// @return admin address that can upgrade beacon implementation.
function admin() external view returns (address);
// #endregion view functions.
// #region state modifying functions.
/// @dev function used to initialize module registry.
/// @param factory_ address of ArrakisMetaVaultFactory,
/// who is the only one who can call the init management function.
function initialize(address factory_) external;
/// @notice function used to whitelist IBeacon that contain
/// implementation of valid module.
/// @param beacons_ list of beacon to whitelist.
function whitelistBeacons(address[] calldata beacons_) external;
/// @notice function used to blacklist IBeacon that contain
/// implementation of unvalid (from now) module.
/// @param beacons_ list of beacon to blacklist.
function blacklistBeacons(address[] calldata beacons_) external;
/// @notice function used to create module instance that can be
/// whitelisted as module inside a vault.
/// @param beacon_ which whitelisted beacon's implementation we want to
/// create an instance of.
/// @param payload_ payload to create the module.
function createModule(
address vault_,
address beacon_,
bytes calldata payload_
) external returns (address module);
// #endregion state modifying functions.
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface IOracleWrapper {
// #region errors.
error AddressZero();
error DecimalsToken0Zero();
error DecimalsToken1Zero();
// #endregion errors.
/// @notice function used to get price0.
/// @return price0 price of token0/token1.
function getPrice0() external view returns (uint256 price0);
/// @notice function used to get price1.
/// @return price1 price of token1/token0.
function getPrice1() external view returns (uint256 price1);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface IPrivateVaultNFT {
/// @notice function used to mint nft (representing a vault) and send it.
/// @param to_ address where to send the NFT.
/// @param tokenId_ id of the NFT to mint.
function mint(address to_, uint256 tokenId_) external;
// #region view functions.
/// @dev for doing meta data calls of tokens.
function getMetaDatas(
address token0_,
address token1_
)
external
view
returns (
uint8 decimals0,
uint8 decimals1,
string memory symbol0,
string memory symbol1
);
// #endregion view functions.
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface IRenderController {
// #region errors.
error InvalidRenderer();
error AddressZero();
// #endregion errors.
event LogSetRenderer(address newRenderer);
// #region functions.
/// @notice function used to set the renderer contract adress
/// @dev only the owner can do it.
/// @param renderer_ address of the contract that will
/// render the tokenUri for the svg of the nft.
function setRenderer(address renderer_) external;
// #endregion functions.
// #region view functions.
/// @dev for knowning if renderer is a NFTSVG contract.
function isNFTSVG(address renderer_)
external
view
returns (bool);
/// @notice NFTSVG contract that will generate the tokenURI.
function renderer() external view returns (address);
// #endregion view functions.
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
interface ITimeLock {
// #region errors.
error NotImplemented();
// #endregion errors.
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/Address.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) || (!Address.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;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
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);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "@solady/contracts/utils/Base64.sol";
import {NFTSVGUtils} from "./NFTSVGUtils.sol";
/// @notice Parameters for generating the URI
struct SVGParams {
address vault;
uint256 amount0;
uint256 amount1;
uint8 decimals0;
uint8 decimals1;
string symbol0;
string symbol1;
}
/// @dev Interface for the NFTSVG contract
interface INFTSVG {
/// @notice Checks if the contract is compliant with the NFTSVG interface
function isNFTSVG() external pure returns (bool);
/// @notice Generates a URI for a given vault
/// @param params_ Parameters for generating the URI
function generateVaultURI(SVGParams memory params_)
external
pure
returns (string memory);
/// @notice Generates a fallback URI for a given vault
/// @param params_ Parameters for generating the URI
function generateFallbackURI(SVGParams memory params_)
external
pure
returns (string memory);
}
contract NFTSVG is INFTSVG {
/// @notice Checks if the contract is compliant with the NFTSVG interface
function isNFTSVG() external pure returns (bool) {
return true;
}
/// @notice Generates a URI for a given vault
/// @param params_ Parameters for generating the URI
function generateVaultURI(SVGParams memory params_)
public
pure
returns (string memory)
{
string memory name = _generateName(params_);
string memory description = _generateDescription(params_);
string memory image =
Base64.encode(bytes(_generateSVGImage(params_.vault)));
return string(
abi.encodePacked(
"data:application/json;base64,",
Base64.encode(
bytes(
abi.encodePacked(
'{"name":"',
name,
'", "description":"',
description,
'", "image": "',
"data:image/svg+xml;base64,",
image,
'"}'
)
)
)
)
);
}
/// @notice Generates a fallback URI for a given vault
/// @param params_ Parameters for generating the URI
function generateFallbackURI(SVGParams memory params_)
public
pure
returns (string memory)
{
string memory description = _generateDescription(params_);
string memory image =
Base64.encode(bytes(_generateSVGImage(params_.vault)));
return string(
abi.encodePacked(
"data:application/json;base64,",
Base64.encode(
bytes(
abi.encodePacked(
'{"name":" Arrakis Private Vault',
'", "description":"',
description,
'", "image": "',
"data:image/svg+xml;base64,",
image,
'"}'
)
)
)
)
);
}
/// @notice Generates the name of the URI for a given vault
/// @param params_ Parameters for generating the URI
function _generateName(SVGParams memory params_)
internal
pure
returns (string memory)
{
(string memory s1, string memory s2) =
NFTSVGUtils.addressToString(params_.vault);
return string(
abi.encodePacked(
"Arrakis ",
params_.symbol0,
"/",
params_.symbol1,
": ",
s1,
s2
)
);
}
/// @notice Generates the description of the URI for a given vault
/// @param params_ Parameters for generating the URI
function _generateDescription(SVGParams memory params_)
internal
pure
returns (string memory)
{
(string memory s1, string memory s2) =
NFTSVGUtils.addressToString(params_.vault);
return string(
abi.encodePacked(
unicode"⚠️ DO NOT TRANSFER TO UNTRUSTED PARTIES.",
"\\n\\nThis NFT gives ownership of an Arrakis Modular Private Vault (",
s1,
s2,
") with an inventory of ",
NFTSVGUtils.uintToFloatString(
params_.amount0, params_.decimals0
),
" ",
params_.symbol0,
" and ",
NFTSVGUtils.uintToFloatString(
params_.amount1, params_.decimals1
),
" ",
params_.symbol1,
"."
)
);
}
/// @notice Generates the SVG image of the URI for a given vault
/// @param vault_ The vault address represented by the NFT
function _generateSVGImage(address vault_)
internal
pure
returns (string memory svg)
{
return string(
abi.encodePacked(
'<svg width="290" height="500" viewBox="0 0 290 500" fill="none" xmlns="http://www.w3.org/2000/svg"><defs>',
_generateSVGDefs(),
"</defs>",
_generateSVGFrame(),
_generateSVGFront(),
_generateSVGBack(vault_),
"</svg>"
)
);
}
// #region auxiliary functions for generating the SVG image
function _generateSVGDefs()
internal
pure
returns (string memory)
{
return string(
abi.encodePacked(
'<linearGradient id="rect-gradient" gradientUnits="objectBoundingBox" x1="0" y1="0" x2=".75" y2="1.5">',
'<stop offset="0"><animate attributeName="stop-color" values="#050302;#050302;#7D440E;#7D440E;#DC701D;#F0B567;#F0B567;#7D440E;#7D440E;#050302;#050302;#050302;#050302;" dur="30s" repeatCount="indefinite"></animate></stop>',
'<stop offset=".33"><animate attributeName="stop-color" values="#050302;#050302;#7D440E;#F0B567;#F0B567;#EC9117;#DC701D;#FA7C40;#7D440E;#7D440E;#050302;#050302;#050302;" dur="30s" repeatCount="indefinite"></animate></stop>',
'<stop offset=".67"><animate attributeName="stop-color" values="#050302;#050302;#050302;#7D440E;#DC701D;#F0B567;#F0B567;#EC9117;#DC701D;#7D440E;#E89857;#050302;#050302;" dur="30s" repeatCount="indefinite"></animate></stop>',
'<stop offset="1"><animate attributeName="stop-color" values="#050302;#050302;#050302;#050302;#7D440E;#DC701D;#DC701D;#FA7C40;#FA7C40;#DC701D;#E89857;#050302;#050302;" dur="30s" repeatCount="indefinite"></animate></stop>',
'<animateTransform attributeName="gradientTransform" type="translate" from="-.8 -.8" to=".8 .8" dur="30s" repeatCount="indefinite" /></linearGradient>',
'<linearGradient id="tail-gradient" x1="183.99" y1="59.2903" x2="171.409" y2="178.815" gradientUnits="userSpaceOnUse"><stop stop-color="white"/><stop offset="1" stop-color="white" stop-opacity="0"/></linearGradient>',
_generateSVGMasks()
)
);
}
function _generateSVGMasks()
internal
pure
returns (string memory)
{
return string(
abi.encodePacked(
'<mask id="waves-mask"><rect x="0" y="0" width="100%" height="100%" fill="white" />',
'<g style="scale(3)"><linearGradient id="waves2" x1="0" x2="1" y1="0" y2="0"><stop stop-color="#000000" offset="0"></stop><stop stop-color="#000000" offset="1"></stop></linearGradient>',
'<path d="" fill="black" opacity="0.33"><animate attributeName="d" dur="8s" repeatCount="indefinite" keyTimes="0;0.333;0.667;1" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" begin="0s" values="M0 0L0 279.72Q72.50 95.90 145 80.31T290 -2.87L290 0Z;M0 0L0 178.34Q72.50 66.44 145 50.16T290 -49.50L290 0Z;M0 0L0 244.09Q72.50 178.37 145 151.37T290 -124.25L290 0Z;M0 0L0 279.72Q72.50 95.90 145 80.31T290 -2.87L290 0Z"></animate></path>'
'<path d="" fill="black" opacity="0.33"><animate attributeName="d" dur="8s" repeatCount="indefinite" keyTimes="0;0.333;0.667;1" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" begin="-4.166666666666667s" values="M0 0L0 258.07Q72.50 130.78 145 98.80T290 -39.43L290 0Z;M0 0L0 242.23Q72.50 39.39 145 16.94T290 -28.59L290 0Z;M0 0L0 224.02Q72.50 53.87 145 31.25T290 -65.65L290 0Z;M0 0L0 258.07Q72.50 130.78 145 98.80T290 -39.43L290 0Z"></animate></path>',
"</g></mask>",
'<mask id="inner_rect_mask" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="10.5" y="10.5" width="269" height="479"><rect x="10.5" y="10.5" width="269" height="479" rx="23.5" fill="#D9D9D9"/></mask>'
)
);
}
function _generateSVGFrame()
internal
pure
returns (string memory)
{
return string(
abi.encodePacked(
'<rect width="290" height="500" rx="24" fill="black"/><rect width="290" height="500" rx="24" fill="url(#rect-gradient)"/>',
'<g id="waves" mask="url(#inner_rect_mask)"><g mask="url(#waves-mask)"><g style="transform:translate(145.0px,250.0px) scale(-1,1) translate(-145.0px,-250.0px)"><linearGradient id="waves1" x1="0" x2="1" y1="0" y2="0"><stop stop-color="#ffffff" offset="0"></stop><stop stop-color="#ffffff" offset="1"></stop></linearGradient>',
'<path d="" fill="white" opacity="0.033"><animate attributeName="d" dur="15s" repeatCount="indefinite" keyTimes="0;0.333;0.667;1" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" begin="0s" values="M0 0L0 225.85Q72.50 535.81 145 487.99T290 618.39L290 0Z;M0 0L0 62.93Q72.50 363.50 145 349.64T290 259.60L290 0Z;M0 0L0 215.10Q72.50 239.83 145 212.50T290 525.33L290 0Z;M0 0L0 225.85Q72.50 535.81 145 487.99T290 618.39L290 0Z"></animate></path>',
'<path d="" fill="white" opacity="0.033"><animate attributeName="d" dur="15s" repeatCount="indefinite" keyTimes="0;0.333;0.667;1" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" begin="-3.7037037037037037s" values="M0 0L0 -139.57Q72.50 522.50 145 485.95T290 463.92L290 0Z;M0 0L0 206.11Q72.50 251.85 145 229.63T290 357.17L290 0Z;M0 0L0 -112.70Q72.50 427.35 145 404.61T290 683.65L290 0Z;M0 0L0 -139.57Q72.50 522.50 145 485.95T290 463.92L290 0Z"></animate></path>',
'<path d="" fill="white" opacity="0.033"><animate attributeName="d" dur="15s" repeatCount="indefinite" keyTimes="0;0.333;0.667;1" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" begin="-7.407407407407407s" values="M0 0L0 120.25Q72.50 269.55 145 248.41T290 380.02L290 0Z;M0 0L0 -35.24Q72.50 502.96 145 476.67T290 570.38L290 0Z;M0 0L0 -1.04Q72.50 515.48 145 487.93T290 370.53L290 0Z;M0 0L0 120.25Q72.50 269.55 145 248.41T290 380.02L290 0Z"></animate></path>',
'</g></g></g><rect x="10.5" y="10.5" width="269" height="479" rx="23.5" stroke="white" stroke-opacity="0.33" stroke-width="2"/>',
NFTSVGUtils.generateSVGLogo(),
_generateSVGDunes()
)
);
}
function _generateSVGDunes()
internal
pure
returns (string memory)
{
return string(
abi.encodePacked(
'<g id="dunes" transform="translate(0 33)"><g mask="url(#inner_rect_mask)">',
'<path d="M0.5 355.5C178.5 330.5 153.023 310.248 108 316C76.5 320.024 86.3739 305.5 106 294.5C135.856 277.767 137.872 272.876 130.5 267.5M130.5 267.5C73.5966 287.906 40.9646 300.008 0.5 305.5M130.5 267.5C169.496 271.232 185.689 274.308 210.5 280.5C249.139 288.743 267.721 291.842 290 292" stroke="white" stroke-opacity="0.33"/>',
'<path d="M0.5 262.5C0.5 262.5 48.5 255 102 253C155.5 251 183 241.5 189.5 235.227M189.5 235.227C222.621 246.569 191.696 261.647 163.5 271M189.5 235.227C198.96 233.427 225.5 242.827 244.5 246.329C273.228 251.623 280.179 251.674 291 251.263" stroke="white" stroke-opacity="0.33"/>',
"</g></g>"
)
);
}
function _generateSVGFront()
internal
pure
returns (string memory)
{
return string(
abi.encodePacked(
'<g id="front" fill="white">',
'<animate attributeName="opacity" values="1;1;1;1;1;1;1;1;0;0;0;0;0;1" dur="30s" repeatCount="indefinite"/>',
'<path d="M171.691 152.953L134.814 127.595L114.918 136.159C114.776 136.241 114.61 136.274 114.447 136.253C114.284 136.231 114.133 136.157 114.017 136.041C113.901 135.924 113.826 135.773 113.805 135.61C113.783 135.447 113.816 135.282 113.899 135.139L122.189 115.243L113.899 95.3515C113.814 95.2091 113.78 95.0429 113.8 94.8788C113.821 94.7146 113.895 94.562 114.012 94.4448C114.129 94.3276 114.281 94.2525 114.445 94.2313C114.609 94.2101 114.775 94.244 114.918 94.3276L134.814 102.631L154.706 94.3276C154.849 94.246 155.015 94.2137 155.178 94.2356C155.341 94.2575 155.492 94.3324 155.609 94.4488C155.725 94.5651 155.8 94.7165 155.822 94.8796C155.844 95.0427 155.811 95.2085 155.73 95.3515L147.439 115.243L172.524 152.146C172.631 152.257 172.69 152.405 172.687 152.559C172.685 152.713 172.621 152.859 172.511 152.966C172.401 153.073 172.252 153.132 172.098 153.129C171.944 153.127 171.798 153.064 171.691 152.953Z"/>',
'<g id="sand-worm">',
'<path opacity="0.6" d="M142.289 53.0082C140.09 52.9128 136.103 53.6677 133.283 54.2794C133.064 54.3278 132.866 54.445 132.718 54.6138C132.57 54.7826 132.479 54.9942 132.46 55.2178C132.441 55.4415 132.493 55.6655 132.61 55.8572C132.727 56.0489 132.902 56.1983 133.109 56.2837L135.578 57.2989C135.578 57.2989 139.023 55.3423 142.289 53.0082Z"/>',
'<path opacity="0.6" d="M135.257 65.8977C137.673 65.9238 140.866 65.8977 142.727 65.7546C140.155 64.453 136.389 62.7654 136.389 62.7654L134.653 63.9801C134.47 64.1087 134.332 64.2923 134.26 64.5044C134.188 64.7165 134.186 64.9461 134.253 65.1598C134.32 65.3734 134.454 65.5602 134.634 65.6929C134.814 65.8256 135.033 65.8973 135.257 65.8977Z"/>',
'<path d="M211.14 112.992C207.492 82.4446 182.151 57.377 151.977 53.7805C147.539 53.2512 143.569 53.0646 142.463 53.0082C142.038 52.9841 141.619 53.1067 141.274 53.3553C139.296 54.761 137.152 56.236 135.061 57.6547C134.675 57.9172 134.365 58.276 134.16 58.6956C133.956 59.1152 133.865 59.5809 133.896 60.0466C133.927 60.5123 134.079 60.9618 134.338 61.3504C134.596 61.739 134.952 62.0534 135.369 62.2621C137.773 63.4595 140.293 64.7177 142.003 65.5767C142.309 65.7282 142.651 65.7941 142.992 65.7676C143.46 65.7286 148.91 65.8804 150.571 66.0713C167.925 68.0236 184.168 78.1192 193.565 92.8916C212.355 121.959 202.511 160.216 174.433 178.012C173.973 178.303 173.487 178.598 172.979 178.88C171.292 179.851 172.628 182.428 174.381 181.569C174.958 181.287 175.522 180.992 176.068 180.702C199.908 167.339 214.984 140.636 211.14 112.992Z" fill="url(#tail-gradient)"/>',
'<animateTransform attributeName="transform" attributeType="XML" type="rotate" from="360 140 120" to="0 140 120" dur="25s" repeatCount="indefinite"/>',
"</g></g>"
)
);
}
function _generateSVGBack(address vault_)
internal
pure
returns (string memory)
{
(string memory s1, string memory s2) =
NFTSVGUtils.addressToString(vault_);
return string(
abi.encodePacked(
'<g id="back" attributeName="opacity" values="0;" fill="white" font-family="system-ui" font-weight="bold" text-anchor="middle">',
'<animate attributeName="opacity" values="0;0;0;0;0;0;0;0;0;1;1;1;0;0" dur="30s" repeatCount="indefinite"/>',
'<text font-size="18" x="145" y="75">This NFT gives ownership</text><text font-size="18" x="145" y="100">rights of a private vault</text>',
'<g opacity="0.67"><text font-size="13" font-weight="normal" x="145" y="127.5">Be careful when transferring</text>',
'<text font-size="13" font-weight="normal" x="145" y="172.5">Vault ID</text>',
'</g><text x="145" y="200">',
s1,
'</text><text x="145" y="222.5">',
s2,
"</text></g>"
)
);
}
// #endregion auxiliary functions for generating the SVG image
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
library NFTSVGUtils {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
/// @notice Generates the logo for the NFT.
function generateSVGLogo() public pure returns (string memory) {
return string(
abi.encodePacked(
'<g id="text-logo" fill="white">',
'<path d="M79.9663 439.232L57.0249 463.54L57.217 463.934H82.2822V458.758H70.5587L85.4821 442.936V463.934H91.9779V444.152C91.9779 437.754 84.3214 434.62 79.9692 439.232H79.9663Z"/>',
'<path d="M225.085 463.928H205.16V458.119H226.554L208.558 449.852L208.307 449.691C205.646 448.006 204.307 445.656 205.18 442.303C206.027 439.045 208.659 437.464 211.729 437.464H231.163V443.281H211.145L227.732 451.178C230.903 452.751 232.598 455.316 231.723 458.804C230.903 462.099 228.327 463.92 225.099 463.92L225.091 463.928H225.085Z"/>',
'<path d="M169.585 437.473H163.073V463.929H169.585V437.473Z"/><path d="M193.81 437.464H184.47L172.802 447.1C171.817 447.997 171.213 449.28 171.148 450.631C171.082 452.029 171.594 453.406 172.551 454.415L184.413 463.94H193.911L178.338 450.867L193.81 437.464Z"/>',
'<path d="M202.296 437.473H195.783V463.937H202.296V437.473Z"/><path d="M101.976 463.937H95.4628V447.31C95.4628 441.878 99.7896 437.473 105.125 437.473H114.453V443.281H105.266C103.45 443.281 101.976 444.782 101.976 446.631V463.937Z"/>',
'<path d="M121.878 463.937H115.366V447.31C115.366 441.878 119.692 437.473 125.027 437.473H134.356V443.281H125.169C123.353 443.281 121.878 444.782 121.878 446.631V463.937Z"/>',
'<path d="M147.449 439.232L124.508 463.54L124.7 463.934H149.765V458.758H138.042L152.964 442.936V463.934H159.46V444.152C159.46 437.754 151.804 434.62 147.452 439.232H147.449Z"/>',
'</g>'
)
);
}
/// @notice Converts an address to 2 string slices.
/// @param addr_ address to convert to string.
function addressToString(address addr_)
public
pure
returns (string memory, string memory)
{
uint256 value = uint256(uint160(addr_));
bytes memory s1 = new bytes(22);
bytes memory s2 = new bytes(20);
s1[0] = "0";
s1[1] = "x";
for (uint256 i = 19; i > 0; i--) {
s2[i] = HEX_DIGITS[value & 0xf];
value >>= 4;
}
s2[0] = HEX_DIGITS[value & 0xf];
value >>= 4;
for (uint256 i = 21; i > 1; i--) {
s1[i] = HEX_DIGITS[value & 0xf];
value >>= 4;
}
return (string(s1), string(s2));
}
/// @notice Converts uints to float strings with 4 decimal places.
/// @param value_ uint to convert to string.
/// @param decimals_ number of decimal places of the input value.
function uintToFloatString(
uint256 value_,
uint8 decimals_
) public pure returns (string memory) {
if (decimals_ < 5) {
return _uintToString(value_);
}
uint256 scaleFactor = 10 ** decimals_;
uint256 fraction =
(value_ % scaleFactor) / 10 ** (decimals_ - 4);
string memory fractionStr;
if (fraction == 0) {
fractionStr = "0000";
} else if (fraction < 10) {
fractionStr = string(
abi.encodePacked("000", _uintToString(fraction))
);
} else if (fraction < 100) {
fractionStr = string(
abi.encodePacked("00", _uintToString(fraction))
);
} else if (fraction < 1000) {
fractionStr =
string(abi.encodePacked("0", _uintToString(fraction)));
} else {
fractionStr = _uintToString(fraction);
}
return string(
abi.encodePacked(
_uintToString(value_ / scaleFactor), ".", fractionStr
)
);
}
/// @notice Code borrowed form:
/// https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol
///
/// @notice Converts uints to strings.
/// @param value_ uint to convert to string.
function _uintToString(uint256 value_)
internal
pure
returns (string memory str)
{
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but we allocate 160 bytes
// to keep the free memory pointer word aligned. We'll need 1 word for the length, 1 word for the
// trailing zeros padding, and 3 other words for a max of 78 digits. In total: 5 * 32 = 160 bytes.
let newFreeMemoryPointer := add(mload(0x40), 160)
// Update the free memory pointer to avoid overriding our string.
mstore(0x40, newFreeMemoryPointer)
// Assign str to the end of the zone of newly allocated memory.
str := sub(newFreeMemoryPointer, 32)
// Clean the last word of memory it may not be overwritten.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// prettier-ignore
for { let temp := value_ } 1 {} {
// Move the pointer 1 byte to the left.
str := sub(str, 1)
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing temp until zero.
temp := div(temp, 10)
// prettier-ignore
if iszero(temp) { break }
}
// Compute and cache the final total length of the string.
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 32)
// Store the string's length at the start of memory allocated for our string.
mstore(str, length)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @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();
/// @dev Cannot double-initialize.
error AlreadyInitialized();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_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.
bytes32 internal constant _OWNER_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;
/// 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 Override to return true to make `_initializeOwner` prevent double-initialization.
function _guardInitializeOwner() internal pure virtual returns (bool guard) {}
/// @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 {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
if sload(ownerSlot) {
mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
revert(0x1c, 0x04)
}
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
} else {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(_OWNER_SLOT, 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 {
if (_guardInitializeOwner()) {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// 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, or(newOwner, shl(255, iszero(newOwner))))
}
} else {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := _OWNER_SLOT
// 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(_OWNER_SLOT))) {
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(_OWNER_SLOT)
}
}
/// @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();
_;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
import {INFTSVG, SVGParams} from "src/utils/NFTSVG.sol";
import {IPrivateVaultNFT} from "./interfaces/IPrivateVaultNFT.sol";
import {IArrakisMetaVault} from "./interfaces/IArrakisMetaVault.sol";
import {IRenderController} from "./interfaces/IRenderController.sol";
import {IERC20Metadata} from
"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {RenderController} from "./RenderController.sol";
import {Ownable} from "@solady/contracts/auth/Ownable.sol";
contract PrivateVaultNFT is Ownable, ERC721, IPrivateVaultNFT {
address public immutable renderController;
constructor() ERC721("Arrakis Private LP NFT", "ARRAKIS") {
_initializeOwner(msg.sender);
renderController = address(new RenderController());
}
/// @notice function used to mint nft (representing a vault) and send it.
/// @param to_ address where to send the NFT.
/// @param tokenId_ id of the NFT to mint.
function mint(address to_, uint256 tokenId_) external onlyOwner {
_safeMint(to_, tokenId_);
}
function tokenURI(uint256 tokenId_)
public
view
override
returns (string memory)
{
IArrakisMetaVault vault =
IArrakisMetaVault(address(uint160(tokenId_)));
(uint256 amount0, uint256 amount1) = vault.totalUnderlying();
address _renderer =
IRenderController(renderController).renderer();
try this.getMetaDatas(vault.token0(), vault.token1())
returns (
uint8 decimals0,
uint8 decimals1,
string memory symbol0,
string memory symbol1
) {
return INFTSVG(_renderer).generateVaultURI(
SVGParams({
vault: address(vault),
amount0: amount0,
amount1: amount1,
decimals0: decimals0,
decimals1: decimals1,
symbol0: symbol0,
symbol1: symbol1
})
);
} catch {
return INFTSVG(_renderer).generateFallbackURI(
SVGParams({
vault: address(vault),
amount0: amount0,
amount1: amount1,
decimals0: 4,
decimals1: 4,
symbol0: "TKN0",
symbol1: "TKN1"
})
);
}
}
function getMetaDatas(
address token0_,
address token1_
)
public
view
returns (
uint8 decimals0,
uint8 decimals1,
string memory symbol0,
string memory symbol1
)
{
decimals0 = IERC20Metadata(token0_).decimals();
decimals1 = IERC20Metadata(token1_).decimals();
symbol0 = IERC20Metadata(token0_).symbol();
symbol1 = IERC20Metadata(token1_).symbol();
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
import {INFTSVG} from "src/utils/NFTSVG.sol";
import {IRenderController} from "./interfaces/IRenderController.sol";
import {Initializable} from
"@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {Ownable} from "@solady/contracts/auth/Ownable.sol";
contract RenderController is
Ownable,
IRenderController,
Initializable
{
address public renderer;
function initialize(address owner_) external initializer {
if (owner_ == address(0)) {
revert AddressZero();
}
_initializeOwner(owner_);
}
/// @notice function used to set the renderer contract
/// @dev only the svgController can do it.
/// @param renderer_ address of the contract that will
/// render the tokenUri for the svg of the nft.
function setRenderer(address renderer_) external onlyOwner {
bool _isNftSvg;
try this.isNFTSVG(renderer_) returns (bool isNs) {
_isNftSvg = isNs;
} catch {
_isNftSvg = false;
}
if (!_isNftSvg) revert InvalidRenderer();
renderer = renderer_;
emit LogSetRenderer(renderer_);
}
function isNFTSVG(address renderer_) public view returns (bool) {
return INFTSVG(renderer_).isNFTSVG();
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @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);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
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 = Math.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(SignedMath.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, Math.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));
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {ITimeLock} from "./interfaces/ITimeLock.sol";
import {TimelockController} from
"@openzeppelin/contracts/governance/TimelockController.sol";
contract TimeLock is TimelockController, ITimeLock {
constructor(
uint256 minDelay,
address[] memory proposers,
address[] memory executors,
address admin
) TimelockController(minDelay, proposers, executors, admin) {}
/// @dev override updateDelay function of TimelockController to not allow
/// update of delay.
function updateDelay(uint256) external pure override {
revert NotImplemented();
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (governance/TimelockController.sol)
pragma solidity ^0.8.0;
import "../access/AccessControl.sol";
import "../token/ERC721/IERC721Receiver.sol";
import "../token/ERC1155/IERC1155Receiver.sol";
/**
* @dev Contract module which acts as a timelocked controller. When set as the
* owner of an `Ownable` smart contract, it enforces a timelock on all
* `onlyOwner` maintenance operations. This gives time for users of the
* controlled contract to exit before a potentially dangerous maintenance
* operation is applied.
*
* By default, this contract is self administered, meaning administration tasks
* have to go through the timelock process. The proposer (resp executor) role
* is in charge of proposing (resp executing) operations. A common use case is
* to position this {TimelockController} as the owner of a smart contract, with
* a multisig or a DAO as the sole proposer.
*
* _Available since v3.3._
*/
contract TimelockController is AccessControl, IERC721Receiver, IERC1155Receiver {
bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE");
bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
bytes32 public constant CANCELLER_ROLE = keccak256("CANCELLER_ROLE");
uint256 internal constant _DONE_TIMESTAMP = uint256(1);
mapping(bytes32 => uint256) private _timestamps;
uint256 private _minDelay;
/**
* @dev Emitted when a call is scheduled as part of operation `id`.
*/
event CallScheduled(
bytes32 indexed id,
uint256 indexed index,
address target,
uint256 value,
bytes data,
bytes32 predecessor,
uint256 delay
);
/**
* @dev Emitted when a call is performed as part of operation `id`.
*/
event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data);
/**
* @dev Emitted when new proposal is scheduled with non-zero salt.
*/
event CallSalt(bytes32 indexed id, bytes32 salt);
/**
* @dev Emitted when operation `id` is cancelled.
*/
event Cancelled(bytes32 indexed id);
/**
* @dev Emitted when the minimum delay for future operations is modified.
*/
event MinDelayChange(uint256 oldDuration, uint256 newDuration);
/**
* @dev Initializes the contract with the following parameters:
*
* - `minDelay`: initial minimum delay for operations
* - `proposers`: accounts to be granted proposer and canceller roles
* - `executors`: accounts to be granted executor role
* - `admin`: optional account to be granted admin role; disable with zero address
*
* IMPORTANT: The optional admin can aid with initial configuration of roles after deployment
* without being subject to delay, but this role should be subsequently renounced in favor of
* administration through timelocked proposals. Previous versions of this contract would assign
* this admin to the deployer automatically and should be renounced as well.
*/
constructor(uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) {
_setRoleAdmin(TIMELOCK_ADMIN_ROLE, TIMELOCK_ADMIN_ROLE);
_setRoleAdmin(PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE);
_setRoleAdmin(EXECUTOR_ROLE, TIMELOCK_ADMIN_ROLE);
_setRoleAdmin(CANCELLER_ROLE, TIMELOCK_ADMIN_ROLE);
// self administration
_setupRole(TIMELOCK_ADMIN_ROLE, address(this));
// optional admin
if (admin != address(0)) {
_setupRole(TIMELOCK_ADMIN_ROLE, admin);
}
// register proposers and cancellers
for (uint256 i = 0; i < proposers.length; ++i) {
_setupRole(PROPOSER_ROLE, proposers[i]);
_setupRole(CANCELLER_ROLE, proposers[i]);
}
// register executors
for (uint256 i = 0; i < executors.length; ++i) {
_setupRole(EXECUTOR_ROLE, executors[i]);
}
_minDelay = minDelay;
emit MinDelayChange(0, minDelay);
}
/**
* @dev Modifier to make a function callable only by a certain role. In
* addition to checking the sender's role, `address(0)` 's role is also
* considered. Granting a role to `address(0)` is equivalent to enabling
* this role for everyone.
*/
modifier onlyRoleOrOpenRole(bytes32 role) {
if (!hasRole(role, address(0))) {
_checkRole(role, _msgSender());
}
_;
}
/**
* @dev Contract might receive/hold ETH as part of the maintenance process.
*/
receive() external payable {}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, AccessControl) returns (bool) {
return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns whether an id correspond to a registered operation. This
* includes both Pending, Ready and Done operations.
*/
function isOperation(bytes32 id) public view virtual returns (bool) {
return getTimestamp(id) > 0;
}
/**
* @dev Returns whether an operation is pending or not. Note that a "pending" operation may also be "ready".
*/
function isOperationPending(bytes32 id) public view virtual returns (bool) {
return getTimestamp(id) > _DONE_TIMESTAMP;
}
/**
* @dev Returns whether an operation is ready for execution. Note that a "ready" operation is also "pending".
*/
function isOperationReady(bytes32 id) public view virtual returns (bool) {
uint256 timestamp = getTimestamp(id);
return timestamp > _DONE_TIMESTAMP && timestamp <= block.timestamp;
}
/**
* @dev Returns whether an operation is done or not.
*/
function isOperationDone(bytes32 id) public view virtual returns (bool) {
return getTimestamp(id) == _DONE_TIMESTAMP;
}
/**
* @dev Returns the timestamp at which an operation becomes ready (0 for
* unset operations, 1 for done operations).
*/
function getTimestamp(bytes32 id) public view virtual returns (uint256) {
return _timestamps[id];
}
/**
* @dev Returns the minimum delay for an operation to become valid.
*
* This value can be changed by executing an operation that calls `updateDelay`.
*/
function getMinDelay() public view virtual returns (uint256) {
return _minDelay;
}
/**
* @dev Returns the identifier of an operation containing a single
* transaction.
*/
function hashOperation(
address target,
uint256 value,
bytes calldata data,
bytes32 predecessor,
bytes32 salt
) public pure virtual returns (bytes32) {
return keccak256(abi.encode(target, value, data, predecessor, salt));
}
/**
* @dev Returns the identifier of an operation containing a batch of
* transactions.
*/
function hashOperationBatch(
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata payloads,
bytes32 predecessor,
bytes32 salt
) public pure virtual returns (bytes32) {
return keccak256(abi.encode(targets, values, payloads, predecessor, salt));
}
/**
* @dev Schedule an operation containing a single transaction.
*
* Emits {CallSalt} if salt is nonzero, and {CallScheduled}.
*
* Requirements:
*
* - the caller must have the 'proposer' role.
*/
function schedule(
address target,
uint256 value,
bytes calldata data,
bytes32 predecessor,
bytes32 salt,
uint256 delay
) public virtual onlyRole(PROPOSER_ROLE) {
bytes32 id = hashOperation(target, value, data, predecessor, salt);
_schedule(id, delay);
emit CallScheduled(id, 0, target, value, data, predecessor, delay);
if (salt != bytes32(0)) {
emit CallSalt(id, salt);
}
}
/**
* @dev Schedule an operation containing a batch of transactions.
*
* Emits {CallSalt} if salt is nonzero, and one {CallScheduled} event per transaction in the batch.
*
* Requirements:
*
* - the caller must have the 'proposer' role.
*/
function scheduleBatch(
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata payloads,
bytes32 predecessor,
bytes32 salt,
uint256 delay
) public virtual onlyRole(PROPOSER_ROLE) {
require(targets.length == values.length, "TimelockController: length mismatch");
require(targets.length == payloads.length, "TimelockController: length mismatch");
bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt);
_schedule(id, delay);
for (uint256 i = 0; i < targets.length; ++i) {
emit CallScheduled(id, i, targets[i], values[i], payloads[i], predecessor, delay);
}
if (salt != bytes32(0)) {
emit CallSalt(id, salt);
}
}
/**
* @dev Schedule an operation that is to become valid after a given delay.
*/
function _schedule(bytes32 id, uint256 delay) private {
require(!isOperation(id), "TimelockController: operation already scheduled");
require(delay >= getMinDelay(), "TimelockController: insufficient delay");
_timestamps[id] = block.timestamp + delay;
}
/**
* @dev Cancel an operation.
*
* Requirements:
*
* - the caller must have the 'canceller' role.
*/
function cancel(bytes32 id) public virtual onlyRole(CANCELLER_ROLE) {
require(isOperationPending(id), "TimelockController: operation cannot be cancelled");
delete _timestamps[id];
emit Cancelled(id);
}
/**
* @dev Execute an (ready) operation containing a single transaction.
*
* Emits a {CallExecuted} event.
*
* Requirements:
*
* - the caller must have the 'executor' role.
*/
// This function can reenter, but it doesn't pose a risk because _afterCall checks that the proposal is pending,
// thus any modifications to the operation during reentrancy should be caught.
// slither-disable-next-line reentrancy-eth
function execute(
address target,
uint256 value,
bytes calldata payload,
bytes32 predecessor,
bytes32 salt
) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) {
bytes32 id = hashOperation(target, value, payload, predecessor, salt);
_beforeCall(id, predecessor);
_execute(target, value, payload);
emit CallExecuted(id, 0, target, value, payload);
_afterCall(id);
}
/**
* @dev Execute an (ready) operation containing a batch of transactions.
*
* Emits one {CallExecuted} event per transaction in the batch.
*
* Requirements:
*
* - the caller must have the 'executor' role.
*/
// This function can reenter, but it doesn't pose a risk because _afterCall checks that the proposal is pending,
// thus any modifications to the operation during reentrancy should be caught.
// slither-disable-next-line reentrancy-eth
function executeBatch(
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata payloads,
bytes32 predecessor,
bytes32 salt
) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) {
require(targets.length == values.length, "TimelockController: length mismatch");
require(targets.length == payloads.length, "TimelockController: length mismatch");
bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt);
_beforeCall(id, predecessor);
for (uint256 i = 0; i < targets.length; ++i) {
address target = targets[i];
uint256 value = values[i];
bytes calldata payload = payloads[i];
_execute(target, value, payload);
emit CallExecuted(id, i, target, value, payload);
}
_afterCall(id);
}
/**
* @dev Execute an operation's call.
*/
function _execute(address target, uint256 value, bytes calldata data) internal virtual {
(bool success, ) = target.call{value: value}(data);
require(success, "TimelockController: underlying transaction reverted");
}
/**
* @dev Checks before execution of an operation's calls.
*/
function _beforeCall(bytes32 id, bytes32 predecessor) private view {
require(isOperationReady(id), "TimelockController: operation is not ready");
require(predecessor == bytes32(0) || isOperationDone(predecessor), "TimelockController: missing dependency");
}
/**
* @dev Checks after execution of an operation's calls.
*/
function _afterCall(bytes32 id) private {
require(isOperationReady(id), "TimelockController: operation is not ready");
_timestamps[id] = _DONE_TIMESTAMP;
}
/**
* @dev Changes the minimum timelock duration for future operations.
*
* Emits a {MinDelayChange} event.
*
* Requirements:
*
* - the caller must be the timelock itself. This can only be achieved by scheduling and later executing
* an operation where the timelock is the target and the data is the ABI-encoded call to this function.
*/
function updateDelay(uint256 newDelay) external virtual {
require(msg.sender == address(this), "TimelockController: caller must be timelock");
emit MinDelayChange(_minDelay, newDelay);
_minDelay = newDelay;
}
/**
* @dev See {IERC721Receiver-onERC721Received}.
*/
function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
return this.onERC721Received.selector;
}
/**
* @dev See {IERC1155Receiver-onERC1155Received}.
*/
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155Received.selector;
}
/**
* @dev See {IERC1155Receiver-onERC1155BatchReceived}.
*/
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}
{
"compilationTarget": {
"src/ArrakisMetaVaultFactory.sol": "ArrakisMetaVaultFactory"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 10000
},
"remappings": [
":@create3/contracts/=lib/create3/contracts/",
":@ensdomains/=lib/v4-periphery/lib/v4-core/node_modules/@ensdomains/",
":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":@solady/contracts/=lib/solady/src/",
":@uniswap/v3-core/=lib/valantis-hot/lib/v3-core/",
":@uniswap/v3-periphery/=lib/valantis-hot/lib/v3-periphery/",
":@uniswap/v4-core/=lib/v4-periphery/lib/v4-core/",
":@v3-lib-0.8/contracts/=lib/v3-lib-0.8/contracts/",
":@valantis-core/contracts/=lib/valantis-hot/lib/valantis-core/src/",
":@valantis-hot/contracts-test/=lib/valantis-hot/test/",
":@valantis-hot/contracts/=lib/valantis-hot/src/",
":automate/=lib/automate/contracts/",
":create3/=lib/create3/contracts/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
":forge-gas-snapshot/=lib/v4-periphery/lib/forge-gas-snapshot/src/",
":forge-std/=lib/forge-std/src/",
":hardhat/=lib/v4-periphery/lib/v4-core/node_modules/hardhat/",
":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/",
":solady/=lib/solady/",
":solmate/=lib/v4-periphery/lib/solmate/src/",
":v3-core/=lib/valantis-hot/lib/v3-core/contracts/",
":v3-lib-0.8/=lib/v3-lib-0.8/contracts/",
":v3-periphery/=lib/valantis-hot/lib/v3-periphery/contracts/",
":v4-core/=lib/v4-periphery/lib/v4-core/src/",
":v4-periphery/=lib/v4-periphery/contracts/",
":valantis-core/=lib/valantis-hot/lib/valantis-core/",
":valantis-hot/=lib/valantis-hot/"
]
}
[{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"manager_","type":"address"},{"internalType":"address","name":"moduleRegistryPublic_","type":"address"},{"internalType":"address","name":"moduleRegistryPrivate_","type":"address"},{"internalType":"address","name":"creationCodePublicVault_","type":"address"},{"internalType":"address","name":"creationCodePrivateVault_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressZero","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"deployer","type":"address"}],"name":"AlreadyWhitelistedDeployer","type":"error"},{"inputs":[],"name":"CallFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"endIndex","type":"uint256"},{"internalType":"uint256","name":"numberOfVaults","type":"uint256"}],"name":"EndIndexGtNbOfVaults","type":"error"},{"inputs":[],"name":"ErrorCreatingContract","type":"error"},{"inputs":[],"name":"ErrorCreatingProxy","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotADeployer","type":"error"},{"inputs":[{"internalType":"address","name":"deployer","type":"address"}],"name":"NotAlreadyADeployer","type":"error"},{"inputs":[],"name":"SameManager","type":"error"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"endIndex","type":"uint256"}],"name":"StartIndexLtEndIndex","type":"error"},{"inputs":[],"name":"TargetAlreadyExists","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"VaultNotManaged","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"deployers","type":"address[]"}],"name":"LogBlacklistDeployers","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"},{"indexed":false,"internalType":"address","name":"token0","type":"address"},{"indexed":false,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"module","type":"address"},{"indexed":false,"internalType":"address","name":"privateVault","type":"address"}],"name":"LogPrivateVaultCreation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"},{"indexed":false,"internalType":"address","name":"token0","type":"address"},{"indexed":false,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"module","type":"address"},{"indexed":false,"internalType":"address","name":"publicVault","type":"address"},{"indexed":false,"internalType":"address","name":"timeLock","type":"address"}],"name":"LogPublicVaultCreation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldManager","type":"address"},{"indexed":false,"internalType":"address","name":"newManager","type":"address"}],"name":"LogSetManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"deployers","type":"address[]"}],"name":"LogWhitelistDeployers","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"address[]","name":"deployers_","type":"address[]"}],"name":"blacklistDeployer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"creationCodePrivateVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"creationCodePublicVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"salt_","type":"bytes32"},{"internalType":"address","name":"token0_","type":"address"},{"internalType":"address","name":"token1_","type":"address"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"beacon_","type":"address"},{"internalType":"bytes","name":"moduleCreationPayload_","type":"bytes"},{"internalType":"bytes","name":"initManagementPayload_","type":"bytes"}],"name":"deployPrivateVault","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"salt_","type":"bytes32"},{"internalType":"address","name":"token0_","type":"address"},{"internalType":"address","name":"token1_","type":"address"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"beacon_","type":"address"},{"internalType":"bytes","name":"moduleCreationPayload_","type":"bytes"},{"internalType":"bytes","name":"initManagementPayload_","type":"bytes"}],"name":"deployPublicVault","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deployers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token0_","type":"address"},{"internalType":"address","name":"token1_","type":"address"}],"name":"getTokenName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault_","type":"address"}],"name":"isPrivateVault","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault_","type":"address"}],"name":"isPublicVault","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"moduleRegistryPrivate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"moduleRegistryPublic","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nft","outputs":[{"internalType":"contract PrivateVaultNFT","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numOfPrivateVaults","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numOfPublicVaults","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex_","type":"uint256"},{"internalType":"uint256","name":"endIndex_","type":"uint256"}],"name":"privateVaults","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex_","type":"uint256"},{"internalType":"uint256","name":"endIndex_","type":"uint256"}],"name":"publicVaults","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newManager_","type":"address"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"deployers_","type":"address[]"}],"name":"whitelistDeployer","outputs":[],"stateMutability":"nonpayable","type":"function"}]