Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60806040 | 22489684 | 266 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
TwoCapPurchaseAmountFacet
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 1000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {IPurchaseAmountFacet} from "../../../../interfaces/skeletonInterfaces/purchasePhaseInterfaces/IPurchaseAmountFacet.sol";
import {TwoCapPurchaseAmountFacetStorage} from "./TwoCapPurchaseAmountFacetStorage.sol";
import {AccessControlFacetStorage} from "../../../../genericFacets/accessControlFacet/AccessControlFacetStorage.sol";
import {DelegateCallee} from "../../../../helpers/DelegateCallee.sol";
import {ERC2771RecipientStorage} from "../../../../helpers/erc2771Facet/ERC2771RecipientStorage.sol";
import {ERC2771RecipientHelperLib} from "../../../../helpers/erc2771Facet/ERC2771RecipientHelperLib.sol";
/**
* @notice TwoCapPurchaseAmountFacet
*/
contract TwoCapPurchaseAmountFacet is IPurchaseAmountFacet, DelegateCallee {
using TwoCapPurchaseAmountFacetStorage for TwoCapPurchaseAmountFacetStorage.Layout;
using AccessControlFacetStorage for AccessControlFacetStorage.Layout;
using ERC2771RecipientStorage for ERC2771RecipientStorage.Layout;
using ERC2771RecipientHelperLib for ERC2771RecipientStorage.Layout;
/// @notice Thrown when trying to initialize from non admin account.
error UnauthorizedInitialization(address account);
event PurchaseAmountInitialized(uint256 maxHardCap);
event PurchaseAmountConfigured(
uint256 indexed campaignId,
uint256 softCap,
uint256 hardCap,
uint256 softCapPerAccount,
uint256 hardCapPerAccount
);
/// @inheritdoc IPurchaseAmountFacet
function initPurchaseAmountFacet(bytes calldata initPurchaseAmountData) external onlyExternalDelegateCall {
address account = ERC2771RecipientStorage.layout()._msgSender();
if (!AccessControlFacetStorage.layout().hasRole(AccessControlFacetStorage.ADMIN_ROLE, account)) {
revert UnauthorizedInitialization(account);
}
uint256 maxHardCap_ = TwoCapPurchaseAmountFacetStorage.layout().initPurchaseAmountFacet(initPurchaseAmountData);
emit PurchaseAmountInitialized(maxHardCap_);
}
/// @inheritdoc IPurchaseAmountFacet
function setAndCheckPurchaseAmounts(bytes calldata postFractionsAmountData) external onlyInternalDelegateCall {
(
uint256 campaignId_,
uint256 softCap_,
uint256 hardCap_,
uint256 softCapPerAccount_,
uint256 hardCapPerAccount_
) = TwoCapPurchaseAmountFacetStorage.layout().setAndCheckPurchaseAmounts(postFractionsAmountData);
emit PurchaseAmountConfigured(campaignId_, softCap_, hardCap_, softCapPerAccount_, hardCapPerAccount_);
}
/// @inheritdoc IPurchaseAmountFacet
function checkPurchaseAmounts(uint256 campaignId, uint256 amountOfFractions, address account) external onlyInternalDelegateCall {
TwoCapPurchaseAmountFacetStorage.layout().checkPurchaseAmounts(campaignId, amountOfFractions, account);
}
/// @inheritdoc IPurchaseAmountFacet
function isTotalAmountValid(uint256 campaignId) external view returns (bool) {
return TwoCapPurchaseAmountFacetStorage.layout().isTotalAmountValid(campaignId);
}
function getTotalFractionsPurchased(uint256 campaignId) external view returns (uint256) {
return TwoCapPurchaseAmountFacetStorage.layout().getTotalFractionsPurchased(campaignId);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @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 is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @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._positions[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 cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 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 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[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._positions[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;
assembly ("memory-safe") {
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;
assembly ("memory-safe") {
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;
assembly ("memory-safe") {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
/**
* @title GeneralStorage
* @author Evergonlabs
* @notice Contains all the storage that is always used by tmi tokenizer in all use case scenarios
*/
library GeneralStorage {
/// @dev Percentages are represented with a base of 1e6 (100% == 1e6), supporting 4 decimal places of precision (1 represents 0.0001%)
uint256 internal constant BASE_100_PERCENT = 100_0000;
/** ================================================== STORAGE =================================================
* @dev Unique identifier for the storage slot where the Layout struct is stored. Derived from the ERC7201 formula.
* STORAGE_SLOT: 0xf0437e7029522d8b42dba4d03c0c209e5760e46ee8176da4d252fecccc0a2f00
*/
bytes32 internal constant STORAGE_SLOT =
keccak256(abi.encode(uint256(keccak256("Evergonlabs.Tmi-Tokenizer.storage.GeneralStorage")) - 1)) & ~bytes32(uint256(0xff));
struct IdInfo {
uint256 fractionsCreated; // The amount of existing fractions
uint256 nftId; // The unique ID generated by the wrapper
address creator; // The account that created the fractions
address fractionsContract; // The address of the fraction token contract
// isMinting should only be used when wrapped assets are nftized contracts (real world) that represent pools etc.
bool isMinting; // If it is true, means that fractions are minted during purchase, if it is false, means that fractions are already minted
uint256 totalFractionsPurchased;
uint256 totalPacketsGathered;
}
struct Layout {
address wrapper; // The address of the wrapper proxy used by this platform (for all campaigns)
uint256 currentId; // The current campaign id(counter keeping track of how many campaigns were created)
mapping(uint256 campaignId => IdInfo) infoForId; // campaignId => IdInfo struct
}
/**
* @dev Retrieves a reference to the Layout struct stored at a specified storage slot
*/
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly ("memory-safe") {
l.slot := slot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {ERC2771RecipientStorage} from "../../helpers/erc2771Facet/ERC2771RecipientStorage.sol";
import {ERC2771RecipientHelperLib} from "../../helpers/erc2771Facet/ERC2771RecipientHelperLib.sol";
/**
* @title AccessControlFacetStorage
* @author Evergonlabs
* @notice Library that contains storage and functionality associated with AccessControlFacet.
*/
library AccessControlFacetStorage {
using EnumerableSet for EnumerableSet.Bytes32Set;
using EnumerableSet for EnumerableSet.AddressSet;
using ERC2771RecipientStorage for ERC2771RecipientStorage.Layout;
using ERC2771RecipientHelperLib for ERC2771RecipientStorage.Layout;
/* =================================================== ERRORS ================================================== */
/// @notice Thrown when attempting to re-initialize.
error AlreadyInitialized();
/// @notice Thrown when attempting to handle (grant or revoke) a default role (either `OPEN_ROLE` or `ADMIN_ROLE`).
error RestrictedDefaultRole(bytes32 role);
/// @notice Thrown when an account attempts to handle (grant or revoke) a role it is not permitted to manage.
error UnauthorizedRoleHandling(address account, bytes32 role);
/// @notice Thrown when an account attempts an action without the required role.
error AccountMissingRole(address account, bytes32 role);
/// @notice Thrown when attempting to assign the zero address as the admin.
error InvalidZeroAddressForAdmin();
/// @notice Thrown when a campaignId of 0 is provided, which is invalid for campaign-scoped role checks.
error InvalidCampaignIdZero();
/// @notice Thrown when no accounts provided for role granting.
error ZeroAccountsToGrant();
/// @notice Thrown when no accounts provided for role revocation.
error ZeroAccountsToRevoke();
/// @notice Thrown when an attempt is made to handle `ADMIN_ROLE`.
error CannotHandleAdminRole();
/** ================================================== STORAGE =================================================
* @dev Unique identifier for the storage slot where the Layout struct is stored. Derived from the ERC7201 formula.
* STORAGE_SLOT: 0xf38f18dbf28c638dc6bd2b4f1f6d9a444471db909c2346c79451042d2b750100
*/
bytes32 internal constant STORAGE_SLOT =
keccak256(abi.encode(uint256(keccak256("Evergonlabs.Tmi-Tokenizer.storage.AccessControlFacetStorage")) - 1)) &
~bytes32(uint256(0xff));
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant OPEN_ROLE = keccak256("OPEN_ROLE"); // Every user has this role
struct Layout {
bool isInitialized;
// The roles granted to `account`
mapping(address account => EnumerableSet.Bytes32Set roles) userRoles;
// The accounts with `role`
mapping(bytes32 role => EnumerableSet.AddressSet accounts) usersWithRole;
// The roles granted to account for ID
mapping(uint256 campaignId => mapping(address account => EnumerableSet.Bytes32Set roles)) userRolesForId;
// // The accounts with `role` for ID
mapping(uint256 campaignId => mapping(bytes32 role => EnumerableSet.AddressSet accounts)) usersWithRoleForId;
// The roles that are handled by account
mapping(address account => EnumerableSet.Bytes32Set) userHandledRoles;
}
/* ================================================= MODIFIERS ================================================= */
/**
* @notice Restricts function execution to designated role handlers for a specified role.
*
* @dev Checks that the specified role is not `OPEN_ROLE` or `ADMIN_ROLE`, and verifies whether
* the caller is authorized to handle the specified role.
* If the caller is neither a designated role handler nor the Admin, the function reverts.
*
* @param role The identifier of the role for which the caller must be a designated handler.
*/
modifier onlyRoleHandler(bytes32 role) {
address account = ERC2771RecipientStorage.layout()._msgSender();
if (role == OPEN_ROLE || role == ADMIN_ROLE) revert RestrictedDefaultRole(role);
if (!(layout().userHandledRoles[account].contains(role) || layout().userRoles[account].contains(ADMIN_ROLE))) {
revert UnauthorizedRoleHandling(account, role);
}
_;
}
/**
* @notice Rrestricts function execution to accounts with the specified role.
*
* @dev Checks whether the caller has the specified role, else it reverts.
* If the specified role is the `OPEN_ROLE` then function proceeds with the execution.
*
* @param role The identifier of the role that the caller must hold to proceed.
*/
modifier onlyRole(bytes32 role) {
address account = ERC2771RecipientStorage.layout()._msgSender();
if (!(role == OPEN_ROLE || layout().userRoles[account].contains(role))) {
revert AccountMissingRole(account, role);
}
_;
}
/* =================================================== LAYOUT ================================================== */
/**
* @dev Retrieves a reference to the Layout struct stored at a specified storage slot
*/
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly ("memory-safe") {
l.slot := slot
}
}
/* ================================================= INITIALIZER =============================================== */
/**
* @notice Initializes the `AccessControlFacetStorage` by setting the Admin of the tmi-fractions platform.
* @dev `admin` cannot be address(0).
* @param l A reference to the Layout struct in storage.
* @param account The address to be the designated Admin of the platform, responsible for managing permissions.
*/
function initAccessControlFacet(Layout storage l, address account) internal {
if (l.isInitialized) revert AlreadyInitialized();
if (account == address(0)) revert InvalidZeroAddressForAdmin();
l.isInitialized = true;
l.userRoles[account].add(ADMIN_ROLE);
l.usersWithRole[ADMIN_ROLE].add(account);
}
/* =================================================== QUERIES ================================================= */
/**
* @notice Checks whether a specific account holds a specified role within the tmi-fractions platform.
* @dev This function verifies role-based permissions within a platform-scoped context.
* @param l A reference to the Layout struct in storage.
* @param role The role identifier to check.
* @param account The address to verify for the specified role.
* @return `true` if `account` holds `role`; `false` otherwise.
*/
function hasRole(Layout storage l, bytes32 role, address account) internal view returns (bool) {
return (role == OPEN_ROLE || l.userRoles[account].contains(role));
}
/**
* @notice Checks whether a specific account holds a specified role within a given campaign.
* @dev This function verifies role-based permissions within a campaign-scoped context.
* @param l A reference to the Layout struct in storage.
* @param campaignId The unique identifier of the targeted campaign.
* @param role The role identifier to check.
* @param account The address to verify for the specified role in the given campaign.
* @return `true` if `account` holds `role` within `campaignId`; `false` otherwise.
*/
function hasRoleForId(Layout storage l, uint256 campaignId, bytes32 role, address account) internal view returns (bool) {
return (role == OPEN_ROLE || l.userRolesForId[campaignId][account].contains(role));
}
/**
* @notice Returns all the roles that a specified account holds within a platform-scoped context.
* @param l A reference to the Layout struct in storage.
* @param account The address to query for platform-scoped roles.
* @return An array of all the platform-scoped roles that `account` holds.
*/
function getRolesOf(Layout storage l, address account) internal view returns (bytes32[] memory) {
return l.userRoles[account].values();
}
/**
* @notice Returns all the accounts that hold the specified role within a platform-scoped context.
* @param l A reference to the Layout struct in storage.
* @param role The role to check for.
* @return An array of all the accounts that hold the specified role.
*/
function getAccountsWithRole(Layout storage l, bytes32 role) internal view returns (address[] memory) {
return l.usersWithRole[role].values();
}
/**
* @notice Returns all the roles that a specified account holds within a campaign-scoped context.
* @param l A reference to the Layout struct in storage.
* @param campaignId The unique identifier of the targeted campaign.
* @param account The address to query for the targeted campaign-scoped roles.
* @return An array of all the roles held by `account` within `campaignId`.
*/
function getRolesOfAccountForId(Layout storage l, uint256 campaignId, address account) internal view returns (bytes32[] memory) {
return l.userRolesForId[campaignId][account].values();
}
/**
* @notice Returns all the accounts that hold a specified role within a given campaign.
* @param l A reference to the Layout struct in storage.
* @param campaignId The unique identifier of the targeted campaign.
* @param role The role to check for.
* @return An array of all the accounts that hold the specified role within `campaignId`.
*/
function getAccountsWithRoleForId(Layout storage l, uint256 campaignId, bytes32 role) internal view returns (address[] memory) {
return l.usersWithRoleForId[campaignId][role].values();
}
/**
* @notice Returns all the roles that a specified account is a role handler for.
* @dev A role handler manages its role both in a platform-scoped and campaign-scoped context.
* @param l A reference to the Layout struct in storage.
* @param account The account to check.
* @return An array of all the roles that `account` is a role handler for.
*/
function getHandledRolesOf(Layout storage l, address account) internal view returns (bytes32[] memory) {
return l.userHandledRoles[account].values();
}
/* =================================================== GRANTING ================================================ */
/**
* @notice Grants `role` to `account` within a platform-scoped context.
* @dev Only callable by the Admin or a handler of `role`.
* @param l A reference to the Layout struct in storage.
* @param role The identifier of the role to grant.
* @param account The address to which `role` is granted.
*/
function grantRole(Layout storage l, bytes32 role, address account) internal onlyRoleHandler(role) {
l.userRoles[account].add(role);
l.usersWithRole[role].add(account);
}
/**
* @notice Grants `role` to `account` within a campaign-scoped context.
* @dev Only callable by the Admin or a handler of `role`.
* @param l A reference to the Layout struct in storage.
* @param campaignId The unique identifier of the targeted campaign.
* @param role The identifier of the role to grant.
* @param account The address to which `role` is granted.
*/
function grantRoleForId(Layout storage l, uint256 campaignId, bytes32 role, address account) internal onlyRoleHandler(role) {
l.userRolesForId[campaignId][account].add(role);
l.usersWithRoleForId[campaignId][role].add(account);
}
/**
* @notice Grants `role` to multiple `accounts` within a platform-scoped context.
* @dev Only callable by the Admin or a handler of `role`.
* @param l A reference to the Layout struct in storage.
* @param role The identifier of the role to grant.
* @param accounts An array of addresses to which `role` is granted.
*/
function grantRoleMultiple(Layout storage l, bytes32 role, address[] calldata accounts) internal onlyRoleHandler(role) {
uint256 length = accounts.length;
if (length == 0) revert ZeroAccountsToGrant();
EnumerableSet.AddressSet storage usersWithRole = l.usersWithRole[role];
for (uint256 i = 0; i < length; ) {
l.userRoles[accounts[i]].add(role);
usersWithRole.add(accounts[i]);
unchecked {
i += 1;
}
}
}
/**
* @notice Grants `role` to multiple `accounts` within a campaign-scoped context.
* @dev Only callable by the Admin or a handler of `role`.
* @param l A reference to the Layout struct in storage.
* @param campaignId The unique identifier of the targeted campaign.
* @param role The identifier of the role to grant.
* @param accounts An array of addresses to which `role` is granted.
*/
function grantRoleMultipleForId(
Layout storage l,
uint256 campaignId,
bytes32 role,
address[] calldata accounts
) internal onlyRoleHandler(role) {
if (campaignId == 0) revert InvalidCampaignIdZero();
uint256 length = accounts.length;
if (length == 0) revert ZeroAccountsToGrant();
mapping(address => EnumerableSet.Bytes32Set) storage userRolesForId = l.userRolesForId[campaignId];
EnumerableSet.AddressSet storage usersWithRoleForId = l.usersWithRoleForId[campaignId][role];
for (uint256 i = 0; i < length; ) {
userRolesForId[accounts[i]].add(role);
usersWithRoleForId.add(accounts[i]);
unchecked {
i += 1;
}
}
}
/* =================================================== REVOKING ================================================ */
/**
* @notice Revokes `role` from `account` within a platform-scoped context.
* @dev Only callable by the Admin or a handler of `role`.
* @param l A reference to the Layout struct in storage.
* @param role The identifier of the role to revoke.
* @param account The address from which `role` is revoked.
*/
function revokeRole(Layout storage l, bytes32 role, address account) internal onlyRoleHandler(role) {
l.userRoles[account].remove(role);
l.usersWithRole[role].remove(account);
}
/**
* @notice Revokes `role` from `account` within a campaign-scoped context.
* @dev Only callable by the Admin or a handler of `role`.
* @param l A reference to the Layout struct in storage.
* @param campaignId The unique identifier of the targeted campaign.
* @param role The identifier of the role to revoke.
* @param account The address from which `role` is revoked.
*/
function revokeRoleForId(Layout storage l, uint256 campaignId, bytes32 role, address account) internal onlyRoleHandler(role) {
if (campaignId == 0) revert InvalidCampaignIdZero();
l.userRolesForId[campaignId][account].remove(role);
l.usersWithRoleForId[campaignId][role].remove(account);
}
/**
* @notice Revokes `role` from multiple `accounts` within a platform-scoped context.
* @dev Only callable by the Admin or a handler of `role`.
* @param l A reference to the Layout struct in storage.
* @param role The identifier of the role to revoke.
* @param accounts An array of addresses from which `role` is revoked.
*/
function revokeRoleMultiple(Layout storage l, bytes32 role, address[] calldata accounts) internal onlyRoleHandler(role) {
uint256 length = accounts.length;
if (length == 0) revert ZeroAccountsToRevoke();
EnumerableSet.AddressSet storage usersWithRole = l.usersWithRole[role];
for (uint256 i = 0; i < length; ) {
l.userRoles[accounts[i]].remove(role);
usersWithRole.remove(accounts[i]);
unchecked {
i += 1;
}
}
}
/**
* @notice Revokes `role` from multiple `accounts` within a campaign-scoped context.
* @dev Only callable by the Admin or a handler of `role`.
* @param l A reference to the Layout struct in storage.
* @param campaignId The unique identifier of the targeted campaign.
* @param role The identifier of the role to revoke.
* @param accounts An array of addresses from which `role` is revoked.
*/
function revokeRoleMultipleForId(
Layout storage l,
uint256 campaignId,
bytes32 role,
address[] calldata accounts
) internal onlyRoleHandler(role) {
if (campaignId == 0) revert InvalidCampaignIdZero();
uint256 length = accounts.length;
if (length == 0) revert ZeroAccountsToRevoke();
mapping(address => EnumerableSet.Bytes32Set) storage userRolesForId = l.userRolesForId[campaignId];
EnumerableSet.AddressSet storage usersWithRoleForId = l.usersWithRoleForId[campaignId][role];
for (uint256 i = 0; i < length; ) {
userRolesForId[accounts[i]].remove(role);
usersWithRoleForId.remove(accounts[i]);
unchecked {
i += 1;
}
}
}
/* ================================================ ROLE HANDLING ============================================== */
/**
* @notice Delegates the handling of `role` to the specified account, allowing `account` to manage
* the granting and revocation of this role.
*
* @dev Only callable by the Admin.
*
* Designated role handlers can manage the associated roles both at the platform level (platform-scoped context)
* and within individual campaigns (campaign-scoped context).
*
* @param l A reference to the Layout struct in storage.
* @param role The identifier of the role to delegate handling for.
* @param account The address to be designated as a role handler for `role`.
*/
function addRoleHandler(Layout storage l, bytes32 role, address account) internal onlyRole(ADMIN_ROLE) {
if (role == ADMIN_ROLE) revert CannotHandleAdminRole();
l.userHandledRoles[account].add(role);
}
/**
* @notice Revokes `role` handling privileges from the specified account, preventing `account` from managing
* the granting and revocation of this role.
* @dev Only callable by the Admin.
* @param l A reference to the Layout struct in storage.
* @param role The identifier of the role to revoke handling for.
* @param account The address to be removed as a role handler for `role`.
*/
function removeRoleHandler(Layout storage l, bytes32 role, address account) internal onlyRole(ADMIN_ROLE) {
l.userHandledRoles[account].remove(role);
}
/* ================================================ CHANGE ADMIN =============================================== */
/**
* @notice Replaces the current Admin with a new Admin.
* @dev Only callable by the (current) Admin.
* In this version, only one Admin is supported per platform.
* @param l A reference to the Layout struct in storage.
* @param account The address of the new Admin.
*/
function changeAdmin(Layout storage l, address account) internal onlyRole(ADMIN_ROLE) {
address caller = ERC2771RecipientStorage.layout()._msgSender();
if (account == address(0)) revert InvalidZeroAddressForAdmin();
l.userRoles[caller].remove(ADMIN_ROLE);
l.usersWithRole[ADMIN_ROLE].remove(caller);
l.userRoles[account].add(ADMIN_ROLE);
l.usersWithRole[ADMIN_ROLE].add(account);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
/**
* @title DelegateCallee
* @author Evergonlabs
* @notice This abstract contract provides modifiers to enforce specific call contexts
* for functions of facets in a diamond architecture (see EIP-2535). It ensures proper function execution
* by restricting access based on the caller's address.
*
* @dev Functions of facets must always be called via delegate calls from the diamond proxy. If there is no delegate call,
* the function is not in the context of the diamond, therby eliminating the risk of unintended behavior
* and storage access.
*
* When using `delegatecall`, the `msgSender` within the context of the called contract is the address of the original
* caller that invoked the function, not the address of the contract executing the delegate call.
* This distinction allows us to differentiate between "internal" and "external" delegate calls in the context of the
* diamond architecture.
*/
abstract contract DelegateCallee {
/* =================================================== ERRORS ================================================== */
/// @notice Thrown when a function should not be called by an address other than the diamond proxy itself.
error OnlyInternalDelegateCall(address invalidCaller, bytes4 functionSignature);
/// @notice Thrown when a function should not be called by the diamond proxy itself.
error OnlyExternalDelegateCall(bytes4 functionSignature);
/* ================================================= MODIFIERS ================================================= */
/**
* @notice Restricts access to functions that should only be invoked via "internal" delegate calls
* from the diamond contract. This ensures that certain operations are executed in the correct
* context and prevents unauthorized access or unexpected behavior.
*
* @dev This modifier verifies that the caller is the contract itself (i.e., the diamond proxy).
* If the function is called from outside the diamond, the transaction will revert.
*/
modifier onlyInternalDelegateCall() {
if (msg.sender != address(this)) revert OnlyInternalDelegateCall(msg.sender, msg.sig);
_;
}
/**
* @notice Restricts access to functions that should only be invoked via "external" delegate calls
* (calls originating from an admin or other external caller targeting the diamond proxy).
* This modifier is mainly used to ensure that initialization operations cannot be executed directly
* by the diamond contract itself.
*
* @dev This modifier checks that the caller is not the contract itself (i.e., the diamond proxy).
* If the function is called by the diamond, the transaction will revert.
*/
modifier onlyExternalDelegateCall() {
if (msg.sender == address(this)) revert OnlyExternalDelegateCall(msg.sig);
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import "./ERC2771RecipientStorage.sol";
/**
* @title ERC2771RecipientHelperLib
* @author Evergonlabs
* @notice Implements EIP-2771 standard, as defined in https://eips.ethereum.org/EIPS/eip-2771
* @notice This library can be included in a Diamond Facet to provide it with `_msgSender()` and `_msgData()`
*/
library ERC2771RecipientHelperLib {
using ERC2771RecipientStorage for ERC2771RecipientStorage.Layout;
/**
* @notice Returns the sender of this call.
* If the call came through the trusted forwarder, return the original sender,
* otherwise, return `msg.sender`.
* @dev Should be used in the contract anywhere instead of `msg.sender`.
* @return ret The address of the real sender of this call.
*/
function _msgSender(ERC2771RecipientStorage.Layout storage layout) internal view returns (address ret) {
if (msg.data.length >= 20 && _isTrustedForwarder(layout, msg.sender)) {
// At this point we know that the sender is a trusted forwarder,
// so we trust that the last bytes of msg.data are the verified sender address.
// extract sender address from the end of msg.data
assembly ("memory-safe") {
ret := shr(96, calldataload(sub(calldatasize(), 20)))
}
} else {
ret = msg.sender;
}
}
/**
* @notice Returns the msg.data of this call.
* If the call came through the trusted forwarder, then the real sender was appended as the last 20 bytes
* of the msg.data - so this method will strip those 20 bytes off.
* Otherwise (if the call was made directly and not through the forwarder), return `msg.data`.
* @dev Should be used in the contract instead of msg.data, where this difference matters.
* @return ret The msg.data of this call.
*/
function _msgData(ERC2771RecipientStorage.Layout storage layout) internal view returns (bytes calldata ret) {
if (msg.data.length >= 20 && _isTrustedForwarder(layout, msg.sender)) {
unchecked {
return msg.data[0:msg.data.length - 20];
}
} else {
return msg.data;
}
}
/**
* @notice Queries whether a given address corresponds to the trusted forwarder that is being used.
* @param forwarder the address to inquire.
* @return bool true if the given address is the address of the trusted forwarder, false if not.
*/
function _isTrustedForwarder(ERC2771RecipientStorage.Layout storage layout, address forwarder) internal view returns (bool) {
return forwarder == layout.trustedForwarder;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
/**
* @title ERC2771RecipientStorage
* @author Evergonlabs
* @notice Provides storage for the trustedForwarder, required to impelement EIP-2771 in a Diamond architecture
*/
library ERC2771RecipientStorage {
/**
* @dev Unique identifier for the storage slot where the Layout struct is stored. Derived from the ERC7201 formula.
* STORAGE_SLOT: 0xe392033769e11fdec1c6ff271abad0dad25c63e828b624968d649ada17197800
*/
bytes32 internal constant STORAGE_SLOT =
keccak256(abi.encode(uint256(keccak256("Evergonlabs.Tmi-Tokenizer.storage.ERC2771RecipientStorage")) - 1)) &
~bytes32(uint256(0xff));
struct Layout {
address trustedForwarder;
}
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly ("memory-safe") {
l.slot := slot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
/**
* @notice IPurchaseAmountFacet
*/
interface IPurchaseAmountFacet {
function initPurchaseAmountFacet(bytes calldata initPurchaseAmountData) external;
/// @dev Also sets `hardCapPerUser`
function setAndCheckPurchaseAmounts(bytes calldata postFractionAmountsData) external;
function checkPurchaseAmounts(uint256 campaignId, uint256 amountOfFractions, address account) external;
function isTotalAmountValid(uint256 campaignId) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {GeneralStorage} from "../../../../generalStorage/GeneralStorage.sol";
/**
* @notice TwoCapPurchaseAmountFacetStorage
*/
library TwoCapPurchaseAmountFacetStorage {
using GeneralStorage for GeneralStorage.Layout;
/* =================================================== ERRORS ================================================== */
/// @notice Thrown when attempting to re-initialize.
error AlreadyInitialized();
/// @notice Thrown at initialization when attempting to pass max hardcap as 0.
error InvalidZeroMaxHardcap();
/// @notice Thrown when attempting to pass invalid input amounts for softcap and hardcap.
error InvalidInputAmounts(uint256 campaignId, uint256 softCap, uint256 hardCap);
/// @notice Thrown when attempting to pass hardcap that exceeds max hardcap.
error HardcapExceedsMaxHardcap(uint256 campaignId, uint256 hardCap, uint256 maxHardCap);
/// @notice Thrown when attempting to pass hardcap per account that exceeds hardcap.
error HardcapPerAccountExceedsHardcap(uint256 campaignId, uint256 hardCapPerAccount, uint256 hardCap);
/// notice Thrown when attempting to pass softcap per account that exceeds hardcap per account.
error SoftcapPerAccountExceedsHardcapPerAccount(uint256 campaignId, uint256 softCapPerAccount, uint256 hardCapPerAccount);
/// @notice Thrown when attempting to purchase 0 amount of fractions.
error InvalidZeroAmountOfFractionsToPurchase(uint256 campaignId, address account);
/// @notice Thrown when attempting to purchase more fractions than available.
error InvalidOverHardcapPurchase(uint256 campaignId, address account);
/// @notice Thrown when attempting to purchase more fractions than account hardcap allows.
error InvalidOverAccountHardcapPurchase(uint256 campaignId, uint256 hardCapPerAccount, address account);
/// @notice Thrown when attempting to purchase less fractions than account softcap allows.
error InvalidUnderAccountSoftcapPurchase(uint256 campaignId, uint256 softCapPerAccount, address account);
/** ================================================== STORAGE =================================================
* @dev Unique identifier for the storage slot where the Layout struct is stored. Derived from the ERC7201 formula.
* STORAGE_SLOT: 0x80289af729f64dfcd230ded31d365fe80f029d0b25b8ca23db30fd92fc014800
*/
bytes32 internal constant STORAGE_SLOT =
keccak256(abi.encode(uint256(keccak256("Evergonlabs.Tmi-Tokenizer.storage.TwoCapPurchaseAmountFacetStorage")) - 1)) &
~bytes32(uint256(0xff));
struct Layout {
uint256 maxHardCap;
mapping(uint256 campaignId => uint256 softCap) softCapForId;
mapping(uint256 campaignId => uint256 hardCap) hardCapForId;
mapping(uint256 campaignId => uint256 hardCapPerAccount) hardCapPerAccountForId;
mapping(uint256 campaignId => uint256 softCapPerAccount) softCapPerAccountForId;
mapping(uint256 campaignId => mapping(address account => uint256 amount)) fractionsPurchasedByAccountForId;
}
/**
* @dev Retrieves a reference to the Layout struct stored at a specified storage slot
*/
function layout() internal pure returns (Layout storage l) {
bytes32 slot = STORAGE_SLOT;
assembly ("memory-safe") {
l.slot := slot
}
}
function initPurchaseAmountFacet(Layout storage l, bytes calldata initPurchaseAmountData) internal returns (uint256) {
if (l.maxHardCap != 0) revert AlreadyInitialized();
uint256 maxHardCap_ = abi.decode(initPurchaseAmountData, (uint256));
if (maxHardCap_ == 0) revert InvalidZeroMaxHardcap();
l.maxHardCap = maxHardCap_;
return maxHardCap_;
}
/**
* @notice Sets the amount-based fraction caps (both campaign-wide and per-account) for a campaign's purchase phase.
*
* @dev To disable a campaign's:
* - `softCap`: Set its value to `1` (1 fraction purchased guarantees a successful campaign).
* - `hardCap` : Set its value to `type(uint256).max`.
* - `softCapPerAccount`: Set its value to `1`.
* - `hardCapPerAccount`: Set its value to `hardCap`.
*
* @param l A reference to the Layout struct in storage.
* @param postFractionsAmountData ABI-encoded data containing the following (uint256 values):
* - `softCap`: The minimum amount of fractions that must be collectively purchased for the campaign to succeed.
* - `hardCap`: The maximum amount of fractions that can be collectively purchased in the campaign.
* - `softCapPerAccount`: The minumum amount of fractions that any account can purchase in the campaign (minimum participation)
* - `hardCapPerAccount`: The maximum amount of fractions that any account can purchase in the campaign (maximum participation).
*
* @return campaignId The ID of the targeted campaign.
* @return softCap The `softCap` set.
* @return hardCap The `hardCap` set.
* @return softCapPerAccount The `softCapPerAccount` set.
* @return hardCapPerAccount The `hardCapPerAccount` set.
*/
function setAndCheckPurchaseAmounts(
Layout storage l,
bytes calldata postFractionsAmountData
) internal returns (uint256, uint256, uint256, uint256, uint256) {
(uint256 softCap, uint256 hardCap, uint256 softCapPerAccount, uint256 hardCapPerAccount) = abi.decode(
postFractionsAmountData,
(uint256, uint256, uint256, uint256)
);
GeneralStorage.Layout storage gs = GeneralStorage.layout();
uint256 currentId = gs.currentId;
if (hardCap == 0 || softCap > hardCap || softCap > gs.infoForId[currentId].fractionsCreated)
revert InvalidInputAmounts(currentId, softCap, hardCap);
if (hardCap > l.maxHardCap) revert HardcapExceedsMaxHardcap(currentId, hardCap, l.maxHardCap);
if (hardCapPerAccount > hardCap) revert HardcapPerAccountExceedsHardcap(currentId, hardCapPerAccount, hardCap);
if (softCapPerAccount > hardCapPerAccount)
revert SoftcapPerAccountExceedsHardcapPerAccount(currentId, softCapPerAccount, hardCapPerAccount);
l.softCapForId[currentId] = softCap;
l.hardCapForId[currentId] = hardCap;
l.softCapPerAccountForId[currentId] = softCapPerAccount;
l.hardCapPerAccountForId[currentId] = hardCapPerAccount;
return (currentId, softCap, hardCap, softCapPerAccount, hardCapPerAccount);
}
function checkPurchaseAmounts(Layout storage l, uint256 campaignId, uint256 amountOfFractions, address account) internal {
if (amountOfFractions == 0) revert InvalidZeroAmountOfFractionsToPurchase(campaignId, account);
if (amountOfFractions < l.softCapPerAccountForId[campaignId])
revert InvalidUnderAccountSoftcapPurchase(campaignId, l.softCapPerAccountForId[campaignId], account);
GeneralStorage.IdInfo storage infoForId = GeneralStorage.layout().infoForId[campaignId];
infoForId.totalFractionsPurchased += amountOfFractions;
if (infoForId.totalFractionsPurchased > l.hardCapForId[campaignId]) revert InvalidOverHardcapPurchase(campaignId, account);
mapping(address => uint256) storage fractionsPurchasedByAccount = l.fractionsPurchasedByAccountForId[campaignId];
uint256 fractionsPurchased = fractionsPurchasedByAccount[account] + amountOfFractions;
if (fractionsPurchased > l.hardCapPerAccountForId[campaignId])
revert InvalidOverAccountHardcapPurchase(campaignId, l.hardCapPerAccountForId[campaignId], account);
fractionsPurchasedByAccount[account] = fractionsPurchased;
}
function isTotalAmountValid(Layout storage l, uint256 campaignId) internal view returns (bool) {
return (GeneralStorage.layout().infoForId[campaignId].totalFractionsPurchased >= l.softCapForId[campaignId]);
}
function getTotalFractionsPurchased(Layout storage, uint256 campaignId) internal view returns (uint256) {
return GeneralStorage.layout().infoForId[campaignId].totalFractionsPurchased;
}
}{
"optimizer": {
"enabled": true,
"runs": 1000,
"details": {}
},
"evmVersion": "cancun",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"uint256","name":"campaignId","type":"uint256"},{"internalType":"uint256","name":"hardCap","type":"uint256"},{"internalType":"uint256","name":"maxHardCap","type":"uint256"}],"name":"HardcapExceedsMaxHardcap","type":"error"},{"inputs":[{"internalType":"uint256","name":"campaignId","type":"uint256"},{"internalType":"uint256","name":"hardCapPerAccount","type":"uint256"},{"internalType":"uint256","name":"hardCap","type":"uint256"}],"name":"HardcapPerAccountExceedsHardcap","type":"error"},{"inputs":[{"internalType":"uint256","name":"campaignId","type":"uint256"},{"internalType":"uint256","name":"softCap","type":"uint256"},{"internalType":"uint256","name":"hardCap","type":"uint256"}],"name":"InvalidInputAmounts","type":"error"},{"inputs":[{"internalType":"uint256","name":"campaignId","type":"uint256"},{"internalType":"uint256","name":"hardCapPerAccount","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"InvalidOverAccountHardcapPurchase","type":"error"},{"inputs":[{"internalType":"uint256","name":"campaignId","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"InvalidOverHardcapPurchase","type":"error"},{"inputs":[{"internalType":"uint256","name":"campaignId","type":"uint256"},{"internalType":"uint256","name":"softCapPerAccount","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"InvalidUnderAccountSoftcapPurchase","type":"error"},{"inputs":[{"internalType":"uint256","name":"campaignId","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"InvalidZeroAmountOfFractionsToPurchase","type":"error"},{"inputs":[],"name":"InvalidZeroMaxHardcap","type":"error"},{"inputs":[{"internalType":"bytes4","name":"functionSignature","type":"bytes4"}],"name":"OnlyExternalDelegateCall","type":"error"},{"inputs":[{"internalType":"address","name":"invalidCaller","type":"address"},{"internalType":"bytes4","name":"functionSignature","type":"bytes4"}],"name":"OnlyInternalDelegateCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"campaignId","type":"uint256"},{"internalType":"uint256","name":"softCapPerAccount","type":"uint256"},{"internalType":"uint256","name":"hardCapPerAccount","type":"uint256"}],"name":"SoftcapPerAccountExceedsHardcapPerAccount","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"UnauthorizedInitialization","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"campaignId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"softCap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"hardCap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"softCapPerAccount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"hardCapPerAccount","type":"uint256"}],"name":"PurchaseAmountConfigured","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxHardCap","type":"uint256"}],"name":"PurchaseAmountInitialized","type":"event"},{"inputs":[{"internalType":"uint256","name":"campaignId","type":"uint256"},{"internalType":"uint256","name":"amountOfFractions","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"checkPurchaseAmounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"campaignId","type":"uint256"}],"name":"getTotalFractionsPurchased","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"initPurchaseAmountData","type":"bytes"}],"name":"initPurchaseAmountFacet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"campaignId","type":"uint256"}],"name":"isTotalAmountValid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"postFractionsAmountData","type":"bytes"}],"name":"setAndCheckPurchaseAmounts","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6080604052348015600e575f5ffd5b50610b8a8061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610064575f3560e01c8063b35f74421161004d578063b35f744214610090578063ce7748a7146100b8578063e1d64837146100d9575f5ffd5b8063541c8a841461006857806369517cd01461007d575b5f5ffd5b61007b610076366004610a0c565b6100ec565b005b61007b61008b366004610a4d565b610157565b6100a361009e366004610abb565b61021b565b60405190151581526020015b60405180910390f35b6100cb6100c6366004610abb565b610234565b6040519081526020016100af565b61007b6100e7366004610a4d565b610247565b33301461013c576040516373db113d60e01b81523360048201527fffffffff000000000000000000000000000000000000000000000000000000005f351660248201526044015b60405180910390fd5b61015283838361014a610383565b9291906103e4565b505050565b3330146101a2576040516373db113d60e01b81523360048201527fffffffff000000000000000000000000000000000000000000000000000000005f35166024820152604401610133565b5f5f5f5f5f6101bb87876101b4610383565b9190610604565b6040805185815260208101859052908101839052606081018290529499509297509095509350915085907f5afe193ce94c047509f07250a78b7c78813148e16d07f7cd1443c44125db57a79060800160405180910390a250505050505050565b5f61022e82610228610383565b90610804565b92915050565b5f61022e82610241610383565b9061083a565b3033036102a5576040517f87d02f0e0000000000000000000000000000000000000000000000000000000081527fffffffff000000000000000000000000000000000000000000000000000000005f35166004820152602401610133565b5f6102b66102b161085b565b61088b565b90506102ec7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775826102e56108c0565b91906108f0565b61032d576040517fa447f38b0000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610133565b5f610342848461033b610383565b919061094d565b90507f54af87098826c4982c1f08333366bf07c0a1799867a49f1e4404e6d39773c3458160405161037591815260200190565b60405180910390a150505050565b5f8060ff196103b360017f63c5ad0c2800f6539341fd5d517dc47dd5350c2da018fc7047f79f3992641d36610aff565b6040516020016103c591815260200190565b60408051601f1981840301815291905280516020909101201692915050565b815f0361042f576040517f24d084af000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0382166024820152604401610133565b5f8381526004850160205260409020548210156104a3575f83815260048581016020526040918290205491517f9fe1e14e00000000000000000000000000000000000000000000000000000000815290810185905260248101919091526001600160a01b0382166044820152606401610133565b5f6104ac6109dc565b6002015f8581526020019081526020015f20905082816004015f8282546104d39190610b12565b90915550505f84815260028601602052604090205460048201541115610537576040517f78a3cdf4000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b0383166024820152604401610133565b5f84815260058601602090815260408083206001600160a01b03861684529182905282205490919061056a908690610b12565b5f8781526003890160205260409020549091508111156105e1575f868152600388016020526040908190205490517f2f10c4f00000000000000000000000000000000000000000000000000000000081526004810188905260248101919091526001600160a01b0385166044820152606401610133565b6001600160a01b039093165f908152602091909152604090209190915550505050565b5f80808080808080806106198a8c018c610b25565b93509350935093505f61062a6109dc565b600181015490915084158061063e57508486115b8061065757505f81815260028301602052604090205486115b1561069f576040517fdabba4da000000000000000000000000000000000000000000000000000000008152600481018290526024810187905260448101869052606401610133565b8d548511156106ee578d546040517f70762e6800000000000000000000000000000000000000000000000000000000815260048101839052602481018790526044810191909152606401610133565b84831115610739576040517fde4bdcde000000000000000000000000000000000000000000000000000000008152600481018290526024810184905260448101869052606401610133565b82841115610784576040517f5bb39c40000000000000000000000000000000000000000000000000000000008152600481018290526024810185905260448101849052606401610133565b858e6001015f8381526020019081526020015f2081905550848e6002015f8381526020019081526020015f2081905550838e6004015f8381526020019081526020015f2081905550828e6003015f8381526020019081526020015f208190555080868686869a509a509a509a509a50505050505050939792965093509350565b5f81815260018301602052604081205461081c6109dc565b5f938452600201602052604090922060040154919091101592915050565b5f6108436109dc565b5f928352600201602052506040902060040154919050565b5f8060ff196103b360017fd01e7296b19e02e5aa08631cc06bf3618b23d16cd2190e524730d1a2c29fcac9610aff565b5f601436108015906108a6575081546001600160a01b031633145b156108b957505036601319013560601c90565b5033919050565b5f8060ff196103b360017f19521ffda0517558553ffbd4cede0bd8d007b30cfe0aee9dd94bb478f6120c8a610aff565b5f7fefa06053e2ca99a43c97c4a4f3d8a394ee3323a8ff237e625fba09fe30ceb0a483148061094557506001600160a01b0382165f908152600185810160209081526040808420878552909201905290205415155b949350505050565b82545f9015610988576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61099583850185610abb565b9050805f036109d0576040517f4ddf2e0b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80855590509392505050565b5f8060ff196103b360017f12d0c1de9024181affb334dcf16a967ea2f865722d211cefd3c4b2093cde3423610aff565b5f5f5f60608486031215610a1e575f5ffd5b833592506020840135915060408401356001600160a01b0381168114610a42575f5ffd5b809150509250925092565b5f5f60208385031215610a5e575f5ffd5b823567ffffffffffffffff811115610a74575f5ffd5b8301601f81018513610a84575f5ffd5b803567ffffffffffffffff811115610a9a575f5ffd5b856020828401011115610aab575f5ffd5b6020919091019590945092505050565b5f60208284031215610acb575f5ffd5b5035919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8181038181111561022e5761022e610ad2565b8082018082111561022e5761022e610ad2565b5f5f5f5f60808587031215610b38575f5ffd5b505082359460208401359450604084013593606001359250905056fea26469706673582212203ee76c31cd11cf853809d90aeafb62b5f9927965bf78b48712f2e2fb3712db8364736f6c634300081b0033
Deployed Bytecode
0x608060405234801561000f575f5ffd5b5060043610610064575f3560e01c8063b35f74421161004d578063b35f744214610090578063ce7748a7146100b8578063e1d64837146100d9575f5ffd5b8063541c8a841461006857806369517cd01461007d575b5f5ffd5b61007b610076366004610a0c565b6100ec565b005b61007b61008b366004610a4d565b610157565b6100a361009e366004610abb565b61021b565b60405190151581526020015b60405180910390f35b6100cb6100c6366004610abb565b610234565b6040519081526020016100af565b61007b6100e7366004610a4d565b610247565b33301461013c576040516373db113d60e01b81523360048201527fffffffff000000000000000000000000000000000000000000000000000000005f351660248201526044015b60405180910390fd5b61015283838361014a610383565b9291906103e4565b505050565b3330146101a2576040516373db113d60e01b81523360048201527fffffffff000000000000000000000000000000000000000000000000000000005f35166024820152604401610133565b5f5f5f5f5f6101bb87876101b4610383565b9190610604565b6040805185815260208101859052908101839052606081018290529499509297509095509350915085907f5afe193ce94c047509f07250a78b7c78813148e16d07f7cd1443c44125db57a79060800160405180910390a250505050505050565b5f61022e82610228610383565b90610804565b92915050565b5f61022e82610241610383565b9061083a565b3033036102a5576040517f87d02f0e0000000000000000000000000000000000000000000000000000000081527fffffffff000000000000000000000000000000000000000000000000000000005f35166004820152602401610133565b5f6102b66102b161085b565b61088b565b90506102ec7fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c21775826102e56108c0565b91906108f0565b61032d576040517fa447f38b0000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610133565b5f610342848461033b610383565b919061094d565b90507f54af87098826c4982c1f08333366bf07c0a1799867a49f1e4404e6d39773c3458160405161037591815260200190565b60405180910390a150505050565b5f8060ff196103b360017f63c5ad0c2800f6539341fd5d517dc47dd5350c2da018fc7047f79f3992641d36610aff565b6040516020016103c591815260200190565b60408051601f1981840301815291905280516020909101201692915050565b815f0361042f576040517f24d084af000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b0382166024820152604401610133565b5f8381526004850160205260409020548210156104a3575f83815260048581016020526040918290205491517f9fe1e14e00000000000000000000000000000000000000000000000000000000815290810185905260248101919091526001600160a01b0382166044820152606401610133565b5f6104ac6109dc565b6002015f8581526020019081526020015f20905082816004015f8282546104d39190610b12565b90915550505f84815260028601602052604090205460048201541115610537576040517f78a3cdf4000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b0383166024820152604401610133565b5f84815260058601602090815260408083206001600160a01b03861684529182905282205490919061056a908690610b12565b5f8781526003890160205260409020549091508111156105e1575f868152600388016020526040908190205490517f2f10c4f00000000000000000000000000000000000000000000000000000000081526004810188905260248101919091526001600160a01b0385166044820152606401610133565b6001600160a01b039093165f908152602091909152604090209190915550505050565b5f80808080808080806106198a8c018c610b25565b93509350935093505f61062a6109dc565b600181015490915084158061063e57508486115b8061065757505f81815260028301602052604090205486115b1561069f576040517fdabba4da000000000000000000000000000000000000000000000000000000008152600481018290526024810187905260448101869052606401610133565b8d548511156106ee578d546040517f70762e6800000000000000000000000000000000000000000000000000000000815260048101839052602481018790526044810191909152606401610133565b84831115610739576040517fde4bdcde000000000000000000000000000000000000000000000000000000008152600481018290526024810184905260448101869052606401610133565b82841115610784576040517f5bb39c40000000000000000000000000000000000000000000000000000000008152600481018290526024810185905260448101849052606401610133565b858e6001015f8381526020019081526020015f2081905550848e6002015f8381526020019081526020015f2081905550838e6004015f8381526020019081526020015f2081905550828e6003015f8381526020019081526020015f208190555080868686869a509a509a509a509a50505050505050939792965093509350565b5f81815260018301602052604081205461081c6109dc565b5f938452600201602052604090922060040154919091101592915050565b5f6108436109dc565b5f928352600201602052506040902060040154919050565b5f8060ff196103b360017fd01e7296b19e02e5aa08631cc06bf3618b23d16cd2190e524730d1a2c29fcac9610aff565b5f601436108015906108a6575081546001600160a01b031633145b156108b957505036601319013560601c90565b5033919050565b5f8060ff196103b360017f19521ffda0517558553ffbd4cede0bd8d007b30cfe0aee9dd94bb478f6120c8a610aff565b5f7fefa06053e2ca99a43c97c4a4f3d8a394ee3323a8ff237e625fba09fe30ceb0a483148061094557506001600160a01b0382165f908152600185810160209081526040808420878552909201905290205415155b949350505050565b82545f9015610988576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61099583850185610abb565b9050805f036109d0576040517f4ddf2e0b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80855590509392505050565b5f8060ff196103b360017f12d0c1de9024181affb334dcf16a967ea2f865722d211cefd3c4b2093cde3423610aff565b5f5f5f60608486031215610a1e575f5ffd5b833592506020840135915060408401356001600160a01b0381168114610a42575f5ffd5b809150509250925092565b5f5f60208385031215610a5e575f5ffd5b823567ffffffffffffffff811115610a74575f5ffd5b8301601f81018513610a84575f5ffd5b803567ffffffffffffffff811115610a9a575f5ffd5b856020828401011115610aab575f5ffd5b6020919091019590945092505050565b5f60208284031215610acb575f5ffd5b5035919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b8181038181111561022e5761022e610ad2565b8082018082111561022e5761022e610ad2565b5f5f5f5f60808587031215610b38575f5ffd5b505082359460208401359450604084013593606001359250905056fea26469706673582212203ee76c31cd11cf853809d90aeafb62b5f9927965bf78b48712f2e2fb3712db8364736f6c634300081b0033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.