Address Contract Verified
Address
0x962EaeDCe9487C63de122d0d776DB71a3D47f7e5
Balance
0 ETH
Nonce
1
Code Size
2297 bytes
Creator
0xf0cF211A...FD70 at tx 0x09b7685c...b9bc0f
Indexed Transactions
0 (1 on-chain, 0.6% indexed)
Contract Bytecode
2297 bytes
0x608060405234801561001057600080fd5b506004361061007d5760003560e01c80637eb0bdc91161005b5780637eb0bdc9146100e8578063829311d2146100fd5780639528164814610110578063dbdc814b1461013057600080fd5b806301ffc9a714610082578063146a2911146100aa5780637b103999146100bd575b600080fd5b610095610090366004610784565b610143565b60405190151581526020015b60405180910390f35b6100956100b83660046107c3565b61017a565b6000546100d0906001600160a01b031681565b6040516001600160a01b0390911681526020016100a1565b6100fb6100f63660046107c3565b610199565b005b6100fb61010b3660046107f3565b61025e565b61012361011e3660046107f3565b61033a565b6040516100a1919061080c565b6100fb61013e3660046107c3565b610354565b60006001600160e01b0319821663a6bd120960e01b148061017457506001600160e01b031982166301ffc9a760e01b145b92915050565b60008281526033602052604081206101929083610411565b9392505050565b816101a381610433565b60008381526033602052604090206101bb9083610411565b6101fe5760405162461bcd60e51b815260206004820152600f60248201526e10591b5a5b881b9bdd08199bdd5b99608a1b60448201526064015b60405180910390fd5b60008381526033602052604090206102169083610497565b50604051600081526001600160a01b0383169084907f87fd40bb6b885abfecf2754b655e5305fe55362fe513c29f96296ec7068f6c26906020015b60405180910390a3505050565b8061026881610433565b600082815260336020526040812061027f906104ac565b905060005b8151811015610334576102cb8282815181106102a2576102a2610859565b60200260200101516033600087815260200190815260200160002061049790919063ffffffff16565b508181815181106102de576102de610859565b60200260200101516001600160a01b0316847f87fd40bb6b885abfecf2754b655e5305fe55362fe513c29f96296ec7068f6c266000604051610324911515815260200190565b60405180910390a3600101610284565b50505050565b6000818152603360205260409020606090610174906104ac565b8161035e81610433565b60008381526033602052604090206103769083610411565b156103ba5760405162461bcd60e51b815260206004820152601460248201527341646d696e20616c72656164792065786973747360601b60448201526064016101f5565b60008381526033602052604090206103d290836104b9565b50604051600181526001600160a01b0383169084907f87fd40bb6b885abfecf2754b655e5305fe55362fe513c29f96296ec7068f6c2690602001610251565b6001600160a01b03811660009081526001830160205260408120541515610192565b61043c816104ce565b6104945760405162461bcd60e51b8152602060048201526024808201527f4e6f7420746865206f776e6572206f6620746865204f7267616e697a6174696f6044820152631b93919560e21b60648201526084016101f5565b50565b6000610192836001600160a01b0384166104da565b60606000610192836105cd565b6000610192836001600160a01b038416610629565b60006101748233610678565b600081815260018301602052604081205480156105c35760006104fe60018361086f565b85549091506000906105129060019061086f565b905080821461057757600086600001828154811061053257610532610859565b906000526020600020015490508087600001848154811061055557610555610859565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061058857610588610890565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610174565b6000915050610174565b60608160000180548060200260200160405190810160405280929190818152602001828054801561061d57602002820191906000526020600020905b815481526020019060010190808311610609575b50505050509050919050565b600081815260018301602052604081205461067057508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610174565b506000610174565b6000816001600160a01b031661068d8461069e565b6001600160a01b0316149392505050565b60008060009054906101000a90046001600160a01b03166001600160a01b031663cfd8fe0c6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106f2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061071691906108a6565b6001600160a01b0316636352211e836040518263ffffffff1660e01b815260040161074391815260200190565b602060405180830381865afa158015610760573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061017491906108a6565b60006020828403121561079657600080fd5b81356001600160e01b03198116811461019257600080fd5b6001600160a01b038116811461049457600080fd5b600080604083850312156107d657600080fd5b8235915060208301356107e8816107ae565b809150509250929050565b60006020828403121561080557600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b8181101561084d5783516001600160a01b031683529284019291840191600101610828565b50909695505050505050565b634e487b7160e01b600052603260045260246000fd5b8181038181111561017457634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b6000602082840312156108b857600080fd5b8151610192816107ae56fea2646970667358221220af663e4d6a6eaea63b9c267a385d9d9c87b3fcee8301a4b58f9ba8abf40a566b64736f6c63430008180033
Verified Source Code Full Match
Compiler: v0.8.24+commit.e11b9ed9
EVM: paris
Optimization: Yes (200 runs)
IERC721.sol 135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 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 ERC-721 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 ERC-721
* 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 address zero.
*
* 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);
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* 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[ERC 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);
}
EnumerableSet.sol 375 lines
// 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;
}
}
RegistryEnabled.sol 119 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IContractRegistry} from "../registry/IContractRegistry.sol";
import {IOrganizationAdmin} from "../admin/IOrganizationAdmin.sol";
/**
* @title RegistryEnabled
* @notice A base contract for contracts that use the central registry to interact with other contracts within the ProductMint system.
*/
abstract contract RegistryEnabled is Context {
/**
* @notice The registry contract
*/
IContractRegistry public registry;
uint256[50] private __gap;
constructor(address _registry) {
registry = IContractRegistry(_registry);
}
// Registry
modifier onlyRegistry(address expectedContract) {
_checkRegistry(expectedContract);
_;
}
function _checkRegistry(address expectedContract) internal view {
require(_msgSender() == expectedContract, "Caller not authorized");
}
// Org Admin
modifier onlyOrgAdmin(uint256 organizationId) {
_checkOrgAdmin(organizationId);
_;
}
function _isOrgAdmin(uint256 organizationId) internal view returns (bool) {
return _isOrgAdminAddress(organizationId, _msgSender());
}
function _isOrgAdminAddress(
uint256 organizationId,
address orgAdmin
) internal view returns (bool) {
return
_isOrgOwnerAddress(organizationId, orgAdmin) ||
IOrganizationAdmin(registry.orgAdmin()).isAdmin(
organizationId,
orgAdmin
);
}
function _checkOrgAdmin(uint256 organizationId) internal view {
require(
_isOrgAdmin(organizationId),
"Not an admin of the organization"
);
}
// Product Pass NFT
modifier onlyPassOwner(uint256 _productPassId) {
_checkPassOwner(_productPassId);
_;
}
function _checkPassOwner(uint256 _productPassId) internal view {
require(
_isPassOwner(_productPassId),
"Not the owner of the ProductPassNFT"
);
}
function _isPassOwner(uint256 _productPassId) internal view returns (bool) {
return _passOwner(_productPassId) == _msgSender();
}
function _passOwner(
uint256 _productPassId
) internal view returns (address) {
return IERC721(registry.productPassNFT()).ownerOf(_productPassId);
}
// Organization NFT
modifier onlyOrgOwner(uint256 organizationId) {
_checkOrgOwner(organizationId);
_;
}
function _checkOrgOwner(uint256 organizationId) internal view {
require(
_isOrgOwner(organizationId),
"Not the owner of the OrganizationNFT"
);
}
function _isOrgOwner(uint256 organizationId) internal view returns (bool) {
return _isOrgOwnerAddress(organizationId, _msgSender());
}
function _isOrgOwnerAddress(
uint256 organizationId,
address orgOwner
) internal view returns (bool) {
return _orgOwner(organizationId) == orgOwner;
}
function _orgOwner(uint256 organizationId) internal view returns (address) {
return IERC721(registry.organizationNFT()).ownerOf(organizationId);
}
}
IOrganizationAdmin.sol 63 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IOrganizationAdmin {
/**
* Organization Admins
*/
/**
* @notice Emitted when an admin is added or removed from an organization.
* @param orgId The ID of the organization.
* @param admin The address of the admin.
* @param status The status of the admin. True if added, false if removed.
*/
event OrgAdminUpdate(
uint256 indexed orgId,
address indexed admin,
bool status
);
/**
* @notice Adds an admin to the organization.
* Admins are great for delegating the responsibilities of the organization.
* @dev Only the owner of the organization can add admins.
* @param organizationId The ID of the organization.
* @param admin The address of the admin to add.
*/
function addAdmin(uint256 organizationId, address admin) external;
/**
* @notice Removes an admin from the organization.
* @param organizationId The ID of the organization.
* @param admin The address of the admin to remove.
*/
function removeAdmin(uint256 organizationId, address admin) external;
/**
* @notice Removes all admins from the organization.
* @param organizationId The ID of the organization.
*/
function removeAllAdmins(uint256 organizationId) external;
/**
* @notice Returns all admins for an organization.
* @param organizationId The ID of the organization.
* @return admins The addresses of the admins for the organization.
*/
function getAdmins(
uint256 organizationId
) external view returns (address[] memory);
/**
* @notice Checks if an address is an admin for an organization.
* @param organizationId The ID of the organization.
* @param admin The address of the admin to check.
* @return status True if the address is an admin, false otherwise.
*/
function isAdmin(
uint256 organizationId,
address admin
) external view returns (bool);
}
OrganizationAdmin.sol 105 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {
EnumerableSet
} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {RegistryEnabled} from "../abstract/RegistryEnabled.sol";
import {IOrganizationAdmin} from "./IOrganizationAdmin.sol";
/*
____ _ _ __ __ _ _
| _ \ _ __ ___ __| |_ _ ___| |_| \/ (_)_ __ | |_
| |_) | '__/ _ \ / _` | | | |/ __| __| |\/| | | '_ \| __|
| __/| | | (_) | (_| | |_| | (__| |_| | | | | | | | |_
|_| |_| \___/ \__,_|\__,_|\___|\__|_| |_|_|_| |_|\__|
NFT based payment system to mint products onchain with one-time payments and
recurring permissionless subscriptions.
https://productmint.io
*/
/**
* @title OrganizationAdmin
* @notice A contract that allows an organization to manage its admins.
*
* Delegate admins to avoid having to use the owner of the organization to interact with the ProductMint system.
*/
contract OrganizationAdmin is RegistryEnabled, IOrganizationAdmin {
using EnumerableSet for EnumerableSet.AddressSet;
// Organization ID => Addresses that can record usages for the organization.
mapping(uint256 => EnumerableSet.AddressSet) private admins;
constructor(address _contractRegistry) RegistryEnabled(_contractRegistry) {}
/**
* Organization Admins
*/
function addAdmin(
uint256 organizationId,
address admin
) external onlyOrgOwner(organizationId) {
require(
!admins[organizationId].contains(admin),
"Admin already exists"
);
admins[organizationId].add(admin);
emit OrgAdminUpdate(organizationId, admin, true);
}
function removeAdmin(
uint256 organizationId,
address admin
) external onlyOrgOwner(organizationId) {
require(admins[organizationId].contains(admin), "Admin not found");
admins[organizationId].remove(admin);
emit OrgAdminUpdate(organizationId, admin, false);
}
function removeAllAdmins(
uint256 organizationId
) external onlyOrgOwner(organizationId) {
address[] memory adminsToRemove = admins[organizationId].values();
for (uint256 i = 0; i < adminsToRemove.length; i++) {
admins[organizationId].remove(adminsToRemove[i]);
emit OrgAdminUpdate(organizationId, adminsToRemove[i], false);
}
}
function getAdmins(
uint256 organizationId
) external view returns (address[] memory) {
return admins[organizationId].values();
}
function isAdmin(
uint256 organizationId,
address admin
) external view returns (bool) {
return admins[organizationId].contains(admin);
}
/**
* ERC165
*/
function supportsInterface(
bytes4 interfaceId
) external pure returns (bool) {
return
interfaceId == type(IOrganizationAdmin).interfaceId ||
interfaceId == type(IERC165).interfaceId;
}
}
IContractRegistry.sol 196 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IContractRegistry {
/**
* @notice Emitted when a contract is updated.
* @param contractName The name of the contract.
* @param newAddress The new address of the contract.
*/
event ContractUpdated(
string indexed contractName,
address indexed newAddress
);
/**
* Manager
*/
/**
* @notice Purchase manager responsible for facilitating product purchases and managing subscriptions.
* @return The address of the purchase manager.
*/
function purchaseManager() external view returns (address);
/**
* Admin
*/
/**
* @notice Organization admin that can delegate other addresses to help manage the organization and make calls on behalf of the organization.
* @return The address of the organization admin.
*/
function orgAdmin() external view returns (address);
/**
* NFTs
*/
/**
* @notice Product Pass NFT that represents a user's membership to an organization and allows them to purchase products.
* @return The address of the product pass NFT.
*/
function productPassNFT() external view returns (address);
/**
* @notice Organization NFT that represents a product owner and allows for the creation of products that can be purchased by users via Product Passes.
* @return The address of the organization NFT.
*/
function organizationNFT() external view returns (address);
/**
* Registry
*/
/**
* @notice Product registry that allows for the creation of products that can be purchased by users via Product Passes.
* @return The address of the product registry.
*/
function productRegistry() external view returns (address);
/**
* @notice Pricing registry that allows for the creation of pricing models that can be linked to products for purchase.
* @return The address of the pricing registry.
*/
function pricingRegistry() external view returns (address);
/**
* @notice Purchase registry records all purchases made by users via Product Passes.
* @return The address of the purchase registry.
*/
function purchaseRegistry() external view returns (address);
/**
* @notice Coupon registry that allows for the creation of coupons that can be applied to purchases and subscriptions.
* @return The address of the coupon registry.
*/
function couponRegistry() external view returns (address);
/**
* @notice Permanent discount registry that allows for the creation of permanent discounts that are minted onto product passes.
* Discounts minted onto product passes are permanent and used in all future purchases including any subscription renewals.
* @return The address of the discount registry.
*/
function discountRegistry() external view returns (address);
/**
* Calculator
*/
/**
* @notice Pricing calculator that allows performs all the pricing calculations for different pricing models created in the pricing registry.
* @return The address of the pricing calculator.
*/
function pricingCalculator() external view returns (address);
/**
* Oracles
*/
/**
* @notice Responsible for knowing whether the product pass NFT can be transferred from one user to another.
* Products created in the registry must be set to transferable to be able to be transferred.
* @return The address of the product transfer oracle.
*/
function productTransferOracle() external view returns (address);
/**
* @notice Responsible for knowing whether the subscription can be transferred from one user to another.
* Subs must be in a paused state to be transferred.
* Organizations must enable this feature for subscriptions to be paused.
* @return The address of the subscription transfer oracle.
*/
function subscriptionTransferOracle() external view returns (address);
/**
* Escrow
*/
/**
* @notice Escrow that that holds all the subscriptions that have been created for product passes
* @return The address of the subscription escrow.
*/
function subscriptionEscrow() external view returns (address);
/**
* @notice Escrow that holds all the payments that have been made for purchases that are withdrawable by the organization.
* @return The address of the payment escrow.
*/
function paymentEscrow() external view returns (address);
/**
* Usage Recorder
*/
/**
* @notice Usage recorder where usage meters can be created for pricing models set to usage based billing.
* Once a meter has been created and is active, organizations can begin recording usage for their products.
* @return The address of the usage recorder.
*/
function usageRecorder() external view returns (address);
/**
* Locks
*/
/**
* @notice Lock for the product pass NFT.
* This lock prevents the product pass NFT from being changed once it has been set.
* @return The address of the product pass NFT.
*/
function PASS_LOCK() external view returns (bytes32);
/**
* @notice Lock for the organization NFT.
* This lock prevents the organization NFT from being changed once it has been set.
* @return The address of the organization NFT.
*/
function ORG_LOCK() external view returns (bytes32);
/**
* Batch Setup
*/
struct BatchSetupContracts {
// Manager
address purchaseManager;
// Admin
address orgAdmin;
// NFTs
address productPassNFT;
address organizationNFT;
// Registry
address productRegistry;
address pricingRegistry;
address purchaseRegistry;
address couponRegistry;
address discountRegistry;
// Calculator
address pricingCalculator;
// Oracles
address productTransferOracle;
address subscriptionTransferOracle;
// Escrow
address subscriptionEscrow;
address paymentEscrow;
// Usage recorder
address usageRecorder;
}
/**
* @notice Batch set all the contracts in the registry.
* @param _contracts The contracts to be set.
*/
function batchSetContracts(BatchSetupContracts memory _contracts) external;
}
Read Contract
getAdmins 0x95281648 → address[]
isAdmin 0x146a2911 → bool
registry 0x7b103999 → address
supportsInterface 0x01ffc9a7 → bool
Write Contract 3 functions
These functions modify contract state and require a wallet transaction to execute.
addAdmin 0xdbdc814b
uint256 organizationId
address admin
removeAdmin 0x7eb0bdc9
uint256 organizationId
address admin
removeAllAdmins 0x829311d2
uint256 organizationId
Recent Transactions
This address has 1 on-chain transactions, but only 0.6% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →