Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Latest 25 from a total of 98 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Update Price Per... | 10234848 | 34 hrs ago | IN | 0 ETH | 0.00021168 | ||||
| Purchase | 10144052 | 14 days ago | IN | 0.015 ETH | 0.00047252 | ||||
| Purchase | 9862709 | 56 days ago | IN | 0.015 ETH | 0.0009871 | ||||
| Purchase | 9706563 | 78 days ago | IN | 0.015 ETH | 0.0002879 | ||||
| Update Merkle Ro... | 9705921 | 78 days ago | IN | 0 ETH | 0.00007978 | ||||
| Set Project Invo... | 9705912 | 78 days ago | IN | 0 ETH | 0.00008278 | ||||
| Update Price Per... | 9705912 | 78 days ago | IN | 0 ETH | 0.00012889 | ||||
| Update Merkle Ro... | 8895028 | 194 days ago | IN | 0 ETH | 0.0000542 | ||||
| Update Merkle Ro... | 8894867 | 194 days ago | IN | 0 ETH | 0.00005415 | ||||
| Set Project Invo... | 8894864 | 194 days ago | IN | 0 ETH | 0.00008282 | ||||
| Update Merkle Ro... | 8886527 | 195 days ago | IN | 0 ETH | 0.00004992 | ||||
| Update Price Per... | 8886472 | 195 days ago | IN | 0 ETH | 0.00012576 | ||||
| Update Merkle Ro... | 8886469 | 195 days ago | IN | 0 ETH | 0.00007977 | ||||
| Purchase | 7804942 | 348 days ago | IN | 0.015 ETH | 0.00354518 | ||||
| Purchase To | 7750381 | 356 days ago | IN | 0.015 ETH | 0.00033737 | ||||
| Purchase | 7750375 | 356 days ago | IN | 0.015 ETH | 0.0003601 | ||||
| Purchase | 7750352 | 356 days ago | IN | 0.015 ETH | 0.00033224 | ||||
| Purchase | 7750344 | 356 days ago | IN | 0.015 ETH | 0.00034391 | ||||
| Purchase | 7749824 | 356 days ago | IN | 0.015 ETH | 0.00038514 | ||||
| Manually Limit P... | 7709835 | 362 days ago | IN | 0 ETH | 0.00009984 | ||||
| Set Project Invo... | 7709835 | 362 days ago | IN | 0 ETH | 0.00013169 | ||||
| Update Price Per... | 7709834 | 362 days ago | IN | 0 ETH | 0.00020693 | ||||
| Update Merkle Ro... | 7709831 | 362 days ago | IN | 0 ETH | 0.00014092 | ||||
| Update Merkle Ro... | 6859187 | 488 days ago | IN | 0 ETH | 0.00005413 | ||||
| Update Merkle Ro... | 6859034 | 488 days ago | IN | 0 ETH | 0.00004994 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
To
|
Amount
|
||
|---|---|---|---|---|---|---|---|
| Transfer | 10144052 | 14 days ago | 0.0135 ETH | ||||
| Transfer | 10144052 | 14 days ago | 0.0015 ETH | ||||
| Transfer | 9862709 | 56 days ago | 0.0135 ETH | ||||
| Transfer | 9862709 | 56 days ago | 0.0015 ETH | ||||
| Transfer | 9706563 | 78 days ago | 0.0135 ETH | ||||
| Transfer | 9706563 | 78 days ago | 0.0015 ETH | ||||
| Transfer | 7804942 | 348 days ago | 0.0135 ETH | ||||
| Transfer | 7804942 | 348 days ago | 0.0015 ETH | ||||
| Transfer | 7750381 | 356 days ago | 0.0135 ETH | ||||
| Transfer | 7750381 | 356 days ago | 0.0015 ETH | ||||
| Transfer | 7750375 | 356 days ago | 0.0135 ETH | ||||
| Transfer | 7750375 | 356 days ago | 0.0015 ETH | ||||
| Transfer | 7750352 | 356 days ago | 0.0135 ETH | ||||
| Transfer | 7750352 | 356 days ago | 0.0015 ETH | ||||
| Transfer | 7750344 | 356 days ago | 0.0135 ETH | ||||
| Transfer | 7750344 | 356 days ago | 0.0015 ETH | ||||
| Transfer | 7749824 | 356 days ago | 0.0135 ETH | ||||
| Transfer | 7749824 | 356 days ago | 0.0015 ETH | ||||
| Transfer | 6444605 | 555 days ago | 0.0009 ETH | ||||
| Transfer | 6444605 | 555 days ago | 0.0001 ETH | ||||
| Transfer | 5997650 | 624 days ago | 0.000009 ETH | ||||
| Transfer | 5997650 | 624 days ago | 0.000001 ETH | ||||
| Transfer | 5997643 | 624 days ago | 0.000009 ETH | ||||
| Transfer | 5997643 | 624 days ago | 0.000001 ETH | ||||
| Transfer | 5997617 | 624 days ago | 0.000009 ETH |
Loading...
Loading
Loading...
Loading
Loading...
Loading
Contract Name:
MinterSetPriceMerkleV5
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 25 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
// @dev fixed to specific solidity version for clarity and for more clear
// source code verification purposes.
pragma solidity 0.8.19;
import {IDelegationRegistry} from "../../interfaces/v0.8.x/IDelegationRegistry.sol";
import {ISharedMinterV0} from "../../interfaces/v0.8.x/ISharedMinterV0.sol";
import {IMinterFilterV1} from "../../interfaces/v0.8.x/IMinterFilterV1.sol";
import {ISharedMinterMerkleV0} from "../../interfaces/v0.8.x/ISharedMinterMerkleV0.sol";
import {AuthLib} from "../../libs/v0.8.x/AuthLib.sol";
import {MerkleLib} from "../../libs/v0.8.x/minter-libs/MerkleLib.sol";
import {SplitFundsLib} from "../../libs/v0.8.x/minter-libs/SplitFundsLib.sol";
import {MaxInvocationsLib} from "../../libs/v0.8.x/minter-libs/MaxInvocationsLib.sol";
import {SetPriceLib} from "../../libs/v0.8.x/minter-libs/SetPriceLib.sol";
import {ReentrancyGuard} from "@openzeppelin-4.5/contracts/security/ReentrancyGuard.sol";
/**
* @title Shared, filtered Minter contract that allows tokens to be minted with
* ETH for addresses in a Merkle allowlist.
* This is designed to be used with GenArt721CoreContractV3 flagship or
* engine contracts.
* @author Art Blocks Inc.
* @notice Privileged Roles and Ownership:
* This contract is designed to be managed, with limited powers.
* Privileged roles and abilities are controlled by the project's artist, which
* can be modified by the core contract's Admin ACL contract. Both of these
* roles hold extensive power and can modify minter details.
* Care must be taken to ensure that the admin ACL contract and artist
* addresses are secure behind a multi-sig or other access control mechanism.
* ----------------------------------------------------------------------------
* The following functions are restricted to a project's artist:
* - updateMerkleRoot
* - updatePricePerTokenInWei
* - setProjectInvocationsPerAddress
* - syncProjectMaxInvocationsToCore
* - manuallyLimitProjectMaxInvocations
* ----------------------------------------------------------------------------
* Additional admin and artist privileged roles may be described on other
* contracts that this minter integrates with.
* ----------------------------------------------------------------------------
* This contract allows vaults to configure token-level or wallet-level
* delegation of minting privileges. This allows a vault on an allowlist to
* delegate minting privileges to a wallet that is not on the allowlist,
* enabling the vault to remain air-gapped while still allowing minting. The
* delegation registry contract is responsible for managing these delegations,
* and is available at the address returned by the public immutable
* `delegationRegistryAddress`. At the time of writing, the delegation
* registry enables easy delegation configuring at https://delegate.cash/.
* Art Blocks does not guarentee the security of the delegation registry, and
* users should take care to ensure that the delegation registry is secure.
* Token-level delegations are configured by the vault owner, and contract-
* level delegations must be configured for the core token contract as returned
* by the public immutable variable `genArt721CoreAddress`.
* ----------------------------------------------------------------------------
* @notice Caution: While Engine projects must be registered on the Art Blocks
* Core Registry to assign this minter, this minter does not enforce that a
* project is registered when configured or queried. This is primarily for gas
* optimization purposes. It is, therefore, possible that fake projects may be
* configured on this minter, but they will not be able to mint tokens due to
* checks performed by this minter's Minter Filter.
*/
contract MinterSetPriceMerkleV5 is
ReentrancyGuard,
ISharedMinterV0,
ISharedMinterMerkleV0
{
/// Minter filter address this minter interacts with
address public immutable minterFilterAddress;
/// Minter filter this minter may interact with.
IMinterFilterV1 private immutable _minterFilter;
/// minterType for this minter
string public constant minterType = "MinterSetPriceMerkleV5";
/// minter version for this minter
string public constant minterVersion = "v5.0.0";
/// Delegation registry address
address public immutable delegationRegistryAddress;
/// Delegation registry address
IDelegationRegistry private immutable _delegationRegistryContract;
// MODIFIERS
// @dev contract uses modifier-like internal functions instead of modifiers
// to reduce contract bytecode size
// @dev contract uses AuthLib for some modifier-like functions
/**
* @notice Initializes contract to be a Filtered Minter for
* `minterFilter` minter filter.
* @param minterFilter Minter filter for which this will be a
* filtered minter.
* @param delegationRegistryAddress_ Delegation registry contract address.
*/
constructor(
address minterFilter,
address delegationRegistryAddress_
) ReentrancyGuard() {
minterFilterAddress = minterFilter;
_minterFilter = IMinterFilterV1(minterFilter);
delegationRegistryAddress = delegationRegistryAddress_;
_delegationRegistryContract = IDelegationRegistry(
delegationRegistryAddress_
);
emit MerkleLib.DelegationRegistryUpdated(delegationRegistryAddress_);
// broadcast default max invocations per address for this minter
emit MerkleLib.DefaultMaxInvocationsPerAddress(
MerkleLib.DEFAULT_MAX_INVOCATIONS_PER_ADDRESS
);
}
/**
* @notice Manually sets the local maximum invocations of project `projectId`
* with the provided `maxInvocations`, checking that `maxInvocations` is less
* than or equal to the value of project `project_id`'s maximum invocations that is
* set on the core contract.
* @dev Note that a `maxInvocations` of 0 can only be set if the current `invocations`
* value is also 0 and this would also set `maxHasBeenInvoked` to true, correctly short-circuiting
* this minter's purchase function, avoiding extra gas costs from the core contract's maxInvocations check.
* @param projectId Project ID to set the maximum invocations for.
* @param coreContract Core contract address for the given project.
* @param maxInvocations Maximum invocations to set for the project.
*/
function manuallyLimitProjectMaxInvocations(
uint256 projectId,
address coreContract,
uint24 maxInvocations
) external {
AuthLib.onlyArtist({
projectId: projectId,
coreContract: coreContract,
sender: msg.sender
});
MaxInvocationsLib.manuallyLimitProjectMaxInvocations({
projectId: projectId,
coreContract: coreContract,
maxInvocations: maxInvocations
});
}
/**
* @notice Updates this minter's price per token of project `projectId`
* to be '_pricePerTokenInWei`, in Wei.
* @dev Note that it is intentionally supported here that the configured
* price may be explicitly set to `0`.
* @param projectId Project ID to set the price per token for.
* @param coreContract Core contract address for the given project.
* @param pricePerTokenInWei Price per token to set for the project, in Wei.
*/
function updatePricePerTokenInWei(
uint256 projectId,
address coreContract,
uint248 pricePerTokenInWei
) external {
AuthLib.onlyArtist({
projectId: projectId,
coreContract: coreContract,
sender: msg.sender
});
SetPriceLib.updatePricePerToken({
projectId: projectId,
coreContract: coreContract,
pricePerToken: pricePerTokenInWei
});
// for convenience, sync local max invocations to the core contract if
// and only if max invocations have not already been synced.
// @dev do not sync if max invocations have already been synced, as
// local max invocations could have been manually set to be
// intentionally less than the core contract's max invocations.
// @dev if local maxInvocations and maxHasBeenInvoked are both
// initial values, we know they have not been populated on this minter
if (
MaxInvocationsLib.maxInvocationsIsUnconfigured({
projectId: projectId,
coreContract: coreContract
})
) {
MaxInvocationsLib.syncProjectMaxInvocationsToCore({
projectId: projectId,
coreContract: coreContract
});
}
}
/**
* @notice Update the Merkle root for project `projectId` on core contract `coreContract`.
* @param projectId Project ID to be updated.
* @param coreContract Core contract address for the given project.
* @param root root of Merkle tree defining addresses allowed to mint
* on project `projectId`.
*/
function updateMerkleRoot(
uint256 projectId,
address coreContract,
bytes32 root
) external {
AuthLib.onlyArtist({
projectId: projectId,
coreContract: coreContract,
sender: msg.sender
});
MerkleLib.updateMerkleRoot({
projectId: projectId,
coreContract: coreContract,
root: root
});
}
/**
* @notice Sets maximum allowed invocations per allowlisted address for
* project `project` to `limit`. If `limit` is set to 0, allowlisted
* addresses will be able to mint as many times as desired, until the
* project reaches its maximum invocations.
* Default is a value of 1 if never configured by artist.
* @param projectId Project ID to toggle the mint limit.
* @param coreContract Core contract address for the given project.
* @param maxInvocationsPerAddress Maximum allowed invocations per
* allowlisted address.
* @dev default value stated above must be updated if the value of
* CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE is changed.
*/
function setProjectInvocationsPerAddress(
uint256 projectId,
address coreContract,
uint24 maxInvocationsPerAddress
) external {
AuthLib.onlyArtist({
projectId: projectId,
coreContract: coreContract,
sender: msg.sender
});
MerkleLib.setProjectInvocationsPerAddress({
projectId: projectId,
coreContract: coreContract,
maxInvocationsPerAddress: maxInvocationsPerAddress
});
}
/**
* @notice Purchases a token from project `projectId`.
* @param projectId Project ID to mint a token on.
* @param coreContract Core contract address for the given project.
* @param proof Merkle proof for the given project.
* @return tokenId Token ID of minted token
*/
function purchase(
uint256 projectId,
address coreContract,
bytes32[] calldata proof
) external payable returns (uint256 tokenId) {
tokenId = purchaseTo({
to: msg.sender,
projectId: projectId,
coreContract: coreContract,
proof: proof,
vault: address(0)
});
return tokenId;
}
/**
* @notice Purchases a token from project `projectId` and sets
* the token's owner to `to`.
* @param to Address to be the new token's owner.
* @param projectId Project ID to mint a token on.
* @param coreContract Contract address of the core contract.
* @param proof Merkle proof for the given project.
* @return tokenId Token ID of minted token
*/
function purchaseTo(
address to,
uint256 projectId,
address coreContract,
bytes32[] calldata proof
) external payable returns (uint256 tokenId) {
return
purchaseTo({
to: to,
projectId: projectId,
coreContract: coreContract,
proof: proof,
vault: address(0)
});
}
// public getter functions
/**
* @notice Gets the maximum invocations project configuration.
* @param projectId The ID of the project whose data needs to be fetched.
* @param coreContract The address of the core contract.
* @return MaxInvocationsLib.MaxInvocationsProjectConfig instance with the
* configuration data.
*/
function maxInvocationsProjectConfig(
uint256 projectId,
address coreContract
)
external
view
returns (MaxInvocationsLib.MaxInvocationsProjectConfig memory)
{
return
MaxInvocationsLib.getMaxInvocationsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
}
/**
* @notice Gets the set price project configuration.
* @param projectId The ID of the project whose data needs to be fetched.
* @param coreContract The address of the core contract.
* @return SetPriceProjectConfig struct with the fixed price project
* configuration data.
*/
function setPriceProjectConfig(
uint256 projectId,
address coreContract
) external view returns (SetPriceLib.SetPriceProjectConfig memory) {
return
SetPriceLib.getSetPriceProjectConfig({
projectId: projectId,
coreContract: coreContract
});
}
/**
* @notice Retrieves the Merkle project configuration for a given contract and project.
* @dev This function fetches the Merkle project configuration from the
* merkleProjectConfigMapping using the provided core contract address and project ID.
* @param projectId The ID of the project.
* @param coreContract The address of the core contract.
* @return MerkleLib.MerkleProjectConfig The Merkle project configuration.
*/
function merkleProjectConfig(
uint256 projectId,
address coreContract
) external view returns (bool, uint24, bytes32) {
MerkleLib.MerkleProjectConfig storage merkleProjectConfig_ = MerkleLib
.getMerkleProjectConfig({
projectId: projectId,
coreContract: coreContract
});
return (
merkleProjectConfig_.useMaxInvocationsPerAddressOverride,
merkleProjectConfig_.maxInvocationsPerAddressOverride,
merkleProjectConfig_.merkleRoot
);
}
/**
* @notice Retrieves the mint invocation count for a specific project and purchaser.
* @dev This function retrieves the number of times a purchaser has minted
* in a specific project from the projectUserMintInvocationsMapping.
* @param projectId The ID of the project.
* @param coreContract The address of the core contract.
* @param purchaser The address of the purchaser.
* @return uint256 The number of times the purchaser has minted in the given project.
*/
function projectUserMintInvocations(
uint256 projectId,
address coreContract,
address purchaser
) external view returns (uint256) {
return
MerkleLib.projectUserMintInvocations({
projectId: projectId,
coreContract: coreContract,
purchaser: purchaser
});
}
/**
* @notice Checks if the specified `coreContract` is a valid engine contract.
* @dev This function retrieves the cached value of `isEngine` from
* the `isEngineCache` mapping. If the cached value is already set, it
* returns the cached value. Otherwise, it calls the `getV3CoreIsEngineView`
* function from the `SplitFundsLib` library to check if `coreContract`
* is a valid engine contract.
* @dev This function will revert if the provided `coreContract` is not
* a valid Engine or V3 Flagship contract.
* @param coreContract The address of the contract to check.
* @return bool indicating if `coreContract` is a valid engine contract.
*/
function isEngineView(address coreContract) external view returns (bool) {
SplitFundsLib.IsEngineCache storage isEngineCache = SplitFundsLib
.getIsEngineCacheConfig(coreContract);
if (isEngineCache.isCached) {
return isEngineCache.isEngine;
} else {
// @dev this calls the non-state-modifying variant of isEngine
return SplitFundsLib.getV3CoreIsEngineView(coreContract);
}
}
/**
* @notice projectId => has project reached its maximum number of
* invocations? Note that this returns a local cache of the core contract's
* state, and may be out of sync with the core contract. This is
* intentional, as it only enables gas optimization of mints after a
* project's maximum invocations has been reached. A false negative will
* only result in a gas cost increase, since the core contract will still
* enforce a maxInvocation check during minting. A false positive is not
* possible because the V3 core contract only allows maximum invocations
* to be reduced, not increased. Based on this rationale, we intentionally
* do not do input validation in this method as to whether or not the input
* @param projectId is an existing project ID.
* @param coreContract is an existing core contract address.
*/
function projectMaxHasBeenInvoked(
uint256 projectId,
address coreContract
) external view returns (bool) {
return
MaxInvocationsLib.getMaxHasBeenInvoked({
projectId: projectId,
coreContract: coreContract
});
}
/**
* @notice projectId => project's maximum number of invocations.
* Optionally synced with core contract value, for gas optimization.
* Note that this returns a local cache of the core contract's
* state, and may be out of sync with the core contract. This is
* intentional, as it only enables gas optimization of mints after a
* project's maximum invocations has been reached.
* @dev A number greater than the core contract's project max invocations
* will only result in a gas cost increase, since the core contract will
* still enforce a maxInvocation check during minting. A number less than
* the core contract's project max invocations is only possible when the
* project's max invocations have not been synced on this minter, since the
* V3 core contract only allows maximum invocations to be reduced, not
* increased. When this happens, the minter will enable minting, allowing
* the core contract to enforce the max invocations check. Based on this
* rationale, we intentionally do not do input validation in this method as
* to whether or not the input `projectId` is an existing project ID.
* @param projectId is an existing project ID.
* @param coreContract is an existing core contract address.
*/
function projectMaxInvocations(
uint256 projectId,
address coreContract
) external view returns (uint256) {
return
MaxInvocationsLib.getMaxInvocations({
projectId: projectId,
coreContract: coreContract
});
}
/**
* @notice Gets if price of token is configured, price of minting a
* token on project `projectId`, and currency symbol and address to be
* used as payment.
* @param projectId Project ID to get price information for
* @param coreContract Contract address of the core contract
* @return isConfigured true only if token price has been configured on
* this minter
* @return tokenPriceInWei current price of token on this minter - invalid
* if price has not yet been configured
* @return currencySymbol currency symbol for purchases of project on this
* minter. This minter always returns "ETH"
* @return currencyAddress currency address for purchases of project on
* this minter. This minter always returns null address, reserved for ether
*/
function getPriceInfo(
uint256 projectId,
address coreContract
)
external
view
returns (
bool isConfigured,
uint256 tokenPriceInWei,
string memory currencySymbol,
address currencyAddress
)
{
SetPriceLib.SetPriceProjectConfig
storage setPriceProjectConfig_ = SetPriceLib
.getSetPriceProjectConfig({
projectId: projectId,
coreContract: coreContract
});
isConfigured = setPriceProjectConfig_.priceIsConfigured;
tokenPriceInWei = setPriceProjectConfig_.pricePerToken;
currencySymbol = "ETH";
currencyAddress = address(0);
}
/**
* @notice Returns remaining invocations for a given address.
* If `projectLimitsMintInvocationsPerAddress` is false, individual
* addresses are only limited by the project's maximum invocations, and a
* dummy value of zero is returned for `mintInvocationsRemaining`.
* If `projectLimitsMintInvocationsPerAddress` is true, the quantity of
* remaining mint invocations for address `address` is returned as
* `mintInvocationsRemaining`.
* Note that mint invocations per address can be changed at any time by the
* artist of a project.
* Also note that all mint invocations are limited by a project's maximum
* invocations as defined on the core contract. This function may return
* a value greater than the project's remaining invocations.
* @param projectId Project ID to get remaining invocations for.
* @param coreContract Contract address of the core contract.
* @param address_ Wallet address to get remaining invocations for.
* @return projectLimitsMintInvocationsPerAddress true if project limits
* mint invocations per address, false if project does not limit mint
* invocations per address.
* @return mintInvocationsRemaining quantity of remaining mint invocations
* for wallet at `address`.
*/
function projectRemainingInvocationsForAddress(
uint256 projectId,
address coreContract,
address address_
)
external
view
returns (
bool projectLimitsMintInvocationsPerAddress,
uint256 mintInvocationsRemaining
)
{
return
MerkleLib.projectRemainingInvocationsForAddress({
projectId: projectId,
coreContract: coreContract,
address_: address_
});
}
/**
* @notice projectId => maximum invocations per allowlisted address. If a
* a value of 0 is returned, there is no limit on the number of mints per
* allowlisted address.
* Default behavior is limit 1 mint per address.
* This value can be changed at any time by the artist.
* @dev default value stated above must be updated if the value of
* CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE is changed.
* @param projectId Project ID to get maximum invocations per address for.
* @param coreContract Contract address of the core contract.
* @return Maximum number of invocations per address for project.
*/
function projectMaxInvocationsPerAddress(
uint256 projectId,
address coreContract
) external view returns (uint256) {
return
MerkleLib.projectMaxInvocationsPerAddress({
projectId: projectId,
coreContract: coreContract
});
}
/**
* @notice Processes a proof for an address.
* @param proof The proof to process.
* @param address_ The address to process the proof for.
* @return The resulting hash from processing the proof.
*/
function processProofForAddress(
bytes32[] calldata proof,
address address_
) external pure returns (bytes32) {
return
MerkleLib.processProofForAddress({
proof: proof,
address_: address_
});
}
/**
* @notice Returns hashed address (to be used as merkle tree leaf).
* Included as a public function to enable users to calculate their hashed
* address in Solidity when generating proofs off-chain.
* @param address_ address to be hashed
* @return bytes32 hashed address, via keccak256 (using encodePacked)
*/
function hashAddress(address address_) external pure returns (bytes32) {
return MerkleLib.hashAddress(address_);
}
/**
* @notice Syncs local maximum invocations of project `projectId` based on
* the value currently defined in the core contract.
* @param projectId Project ID to set the maximum invocations for.
* @param coreContract Core contract address for the given project.
* @dev this enables gas reduction after maxInvocations have been reached -
* core contracts shall still enforce a maxInvocation check during mint.
*/
function syncProjectMaxInvocationsToCore(
uint256 projectId,
address coreContract
) public {
AuthLib.onlyArtist({
projectId: projectId,
coreContract: coreContract,
sender: msg.sender
});
MaxInvocationsLib.syncProjectMaxInvocationsToCore({
projectId: projectId,
coreContract: coreContract
});
}
/**
* @notice Purchases a token from project `projectId` and sets
* the token's owner to `to`.
* @param to Address to be the new token's owner.
* @param projectId Project ID to mint a token on.
* @param coreContract Core contract address for the given project.
* @param proof Merkle proof for the given project.
* @param vault Vault being purchased on behalf of. Acceptable to be
* `address(0)` if no vault.
* @return tokenId Token ID of minted token
*/
function purchaseTo(
address to,
uint256 projectId,
address coreContract,
bytes32[] calldata proof,
address vault // acceptable to be `address(0)` if no vault
) public payable nonReentrant returns (uint256 tokenId) {
// CHECKS
// pre-mint MaxInvocationsLib checks
// Note that `maxHasBeenInvoked` is only checked here to reduce gas
// consumption after a project has been fully minted.
// `maxInvocationsProjectConfig.maxHasBeenInvoked` is locally cached to reduce
// gas consumption, but if not in sync with the core contract's value,
// the core contract also enforces its own max invocation check during
// minting.
MaxInvocationsLib.preMintChecks({
projectId: projectId,
coreContract: coreContract
});
// pre-mint checks for set price lib, and get price per token in wei
// @dev price per token is loaded into memory here for gas efficiency
uint256 pricePerTokenInWei = SetPriceLib.preMintChecksAndGetPrice({
projectId: projectId,
coreContract: coreContract
});
require(msg.value >= pricePerTokenInWei, "Min value to mint req.");
// NOTE: delegate-vault handling **begins here**.
// handle that the vault may be either the `msg.sender` in the case
// that there is not a true vault, or may be `vault` if one is
// provided explicitly (and it is valid).
address vault_ = msg.sender;
if (vault != address(0)) {
// If a vault is provided, it must be valid, otherwise throw rather
// than optimistically-minting with original `msg.sender`.
// Note, we do not check `checkDelegateForAll` as well, as it is known
// to be implicitly checked by calling `checkDelegateForContract`.
bool isValidDelegee = _delegationRegistryContract
.checkDelegateForContract({
delegate: msg.sender,
vault: vault,
contract_: coreContract
});
require(isValidDelegee, "Invalid delegate-vault pairing");
vault_ = vault;
}
// pre-mint MerkleLib checks
MerkleLib.preMintChecks({
projectId: projectId,
coreContract: coreContract,
proof: proof,
vault: vault_
});
// EFFECTS
// mint effects for MerkleLib
MerkleLib.mintEffects({
projectId: projectId,
coreContract: coreContract,
vault: vault_
});
tokenId = _minterFilter.mint_joo({
to: to,
projectId: projectId,
coreContract: coreContract,
sender: vault_
});
MaxInvocationsLib.validateMintEffectsInvocations({
tokenId: tokenId,
coreContract: coreContract
});
// INTERACTIONS
SplitFundsLib.splitFundsETHRefundSender({
projectId: projectId,
pricePerTokenInWei: pricePerTokenInWei,
coreContract: coreContract
});
return tokenId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.0;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The proofs can be generated using the JavaScript library
* https://github.com/miguelmota/merkletreejs[merkletreejs].
* Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
*
* See `test/utils/cryptography/MerkleProof.test.js` for some examples.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the merkle tree could be reinterpreted as a leaf value.
*/
library MerkleProof {
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*
* _Available since v4.7._
*/
function verifyCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*
* _Available since v4.4._
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*
* _Available since v4.7._
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be proved to be a part of a Merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* _Available since v4.7._
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* _Available since v4.7._
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and the sibling nodes in `proof`,
* consuming from one or the other at each step according to the instructions given by
* `proofFlags`.
*
* _Available since v4.7._
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the merkle tree.
uint256 leavesLen = leaves.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
return hashes[totalHashes - 1];
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}
*
* _Available since v4.7._
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the merkle tree.
uint256 leavesLen = leaves.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
return hashes[totalHashes - 1];
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. It the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`.
// We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
// This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
// Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
// good first aproximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1;
uint256 x = a;
if (x >> 128 > 0) {
x >>= 128;
result <<= 64;
}
if (x >> 64 > 0) {
x >>= 64;
result <<= 32;
}
if (x >> 32 > 0) {
x >>= 32;
result <<= 16;
}
if (x >> 16 > 0) {
x >>= 16;
result <<= 8;
}
if (x >> 8 > 0) {
x >>= 8;
result <<= 4;
}
if (x >> 4 > 0) {
x >>= 4;
result <<= 2;
}
if (x >> 2 > 0) {
result <<= 1;
}
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
uint256 result = sqrt(a);
if (rounding == Rounding.Up && result * result < a) {
result += 1;
}
return result;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/SafeCast.sol)
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toUint248(uint256 value) internal pure returns (uint248) {
require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toUint240(uint256 value) internal pure returns (uint240) {
require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toUint232(uint256 value) internal pure returns (uint232) {
require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.2._
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toUint216(uint256 value) internal pure returns (uint216) {
require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toUint208(uint256 value) internal pure returns (uint208) {
require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toUint200(uint256 value) internal pure returns (uint200) {
require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toUint192(uint256 value) internal pure returns (uint192) {
require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toUint184(uint256 value) internal pure returns (uint184) {
require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toUint176(uint256 value) internal pure returns (uint176) {
require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toUint168(uint256 value) internal pure returns (uint168) {
require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toUint160(uint256 value) internal pure returns (uint160) {
require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toUint152(uint256 value) internal pure returns (uint152) {
require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toUint144(uint256 value) internal pure returns (uint144) {
require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toUint136(uint256 value) internal pure returns (uint136) {
require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v2.5._
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toUint120(uint256 value) internal pure returns (uint120) {
require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toUint112(uint256 value) internal pure returns (uint112) {
require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toUint104(uint256 value) internal pure returns (uint104) {
require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.2._
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toUint88(uint256 value) internal pure returns (uint88) {
require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toUint80(uint256 value) internal pure returns (uint80) {
require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toUint72(uint256 value) internal pure returns (uint72) {
require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v2.5._
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toUint56(uint256 value) internal pure returns (uint56) {
require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toUint48(uint256 value) internal pure returns (uint48) {
require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toUint40(uint256 value) internal pure returns (uint40) {
require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v2.5._
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toUint24(uint256 value) internal pure returns (uint24) {
require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v2.5._
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v2.5._
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*
* _Available since v3.0._
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toInt248(int256 value) internal pure returns (int248) {
require(value >= type(int248).min && value <= type(int248).max, "SafeCast: value doesn't fit in 248 bits");
return int248(value);
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toInt240(int256 value) internal pure returns (int240) {
require(value >= type(int240).min && value <= type(int240).max, "SafeCast: value doesn't fit in 240 bits");
return int240(value);
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toInt232(int256 value) internal pure returns (int232) {
require(value >= type(int232).min && value <= type(int232).max, "SafeCast: value doesn't fit in 232 bits");
return int232(value);
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.7._
*/
function toInt224(int256 value) internal pure returns (int224) {
require(value >= type(int224).min && value <= type(int224).max, "SafeCast: value doesn't fit in 224 bits");
return int224(value);
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toInt216(int256 value) internal pure returns (int216) {
require(value >= type(int216).min && value <= type(int216).max, "SafeCast: value doesn't fit in 216 bits");
return int216(value);
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toInt208(int256 value) internal pure returns (int208) {
require(value >= type(int208).min && value <= type(int208).max, "SafeCast: value doesn't fit in 208 bits");
return int208(value);
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toInt200(int256 value) internal pure returns (int200) {
require(value >= type(int200).min && value <= type(int200).max, "SafeCast: value doesn't fit in 200 bits");
return int200(value);
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toInt192(int256 value) internal pure returns (int192) {
require(value >= type(int192).min && value <= type(int192).max, "SafeCast: value doesn't fit in 192 bits");
return int192(value);
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toInt184(int256 value) internal pure returns (int184) {
require(value >= type(int184).min && value <= type(int184).max, "SafeCast: value doesn't fit in 184 bits");
return int184(value);
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toInt176(int256 value) internal pure returns (int176) {
require(value >= type(int176).min && value <= type(int176).max, "SafeCast: value doesn't fit in 176 bits");
return int176(value);
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toInt168(int256 value) internal pure returns (int168) {
require(value >= type(int168).min && value <= type(int168).max, "SafeCast: value doesn't fit in 168 bits");
return int168(value);
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toInt160(int256 value) internal pure returns (int160) {
require(value >= type(int160).min && value <= type(int160).max, "SafeCast: value doesn't fit in 160 bits");
return int160(value);
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toInt152(int256 value) internal pure returns (int152) {
require(value >= type(int152).min && value <= type(int152).max, "SafeCast: value doesn't fit in 152 bits");
return int152(value);
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toInt144(int256 value) internal pure returns (int144) {
require(value >= type(int144).min && value <= type(int144).max, "SafeCast: value doesn't fit in 144 bits");
return int144(value);
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toInt136(int256 value) internal pure returns (int136) {
require(value >= type(int136).min && value <= type(int136).max, "SafeCast: value doesn't fit in 136 bits");
return int136(value);
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128) {
require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
return int128(value);
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toInt120(int256 value) internal pure returns (int120) {
require(value >= type(int120).min && value <= type(int120).max, "SafeCast: value doesn't fit in 120 bits");
return int120(value);
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toInt112(int256 value) internal pure returns (int112) {
require(value >= type(int112).min && value <= type(int112).max, "SafeCast: value doesn't fit in 112 bits");
return int112(value);
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toInt104(int256 value) internal pure returns (int104) {
require(value >= type(int104).min && value <= type(int104).max, "SafeCast: value doesn't fit in 104 bits");
return int104(value);
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.7._
*/
function toInt96(int256 value) internal pure returns (int96) {
require(value >= type(int96).min && value <= type(int96).max, "SafeCast: value doesn't fit in 96 bits");
return int96(value);
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toInt88(int256 value) internal pure returns (int88) {
require(value >= type(int88).min && value <= type(int88).max, "SafeCast: value doesn't fit in 88 bits");
return int88(value);
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toInt80(int256 value) internal pure returns (int80) {
require(value >= type(int80).min && value <= type(int80).max, "SafeCast: value doesn't fit in 80 bits");
return int80(value);
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toInt72(int256 value) internal pure returns (int72) {
require(value >= type(int72).min && value <= type(int72).max, "SafeCast: value doesn't fit in 72 bits");
return int72(value);
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64) {
require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
return int64(value);
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toInt56(int256 value) internal pure returns (int56) {
require(value >= type(int56).min && value <= type(int56).max, "SafeCast: value doesn't fit in 56 bits");
return int56(value);
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toInt48(int256 value) internal pure returns (int48) {
require(value >= type(int48).min && value <= type(int48).max, "SafeCast: value doesn't fit in 48 bits");
return int48(value);
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toInt40(int256 value) internal pure returns (int40) {
require(value >= type(int40).min && value <= type(int40).max, "SafeCast: value doesn't fit in 40 bits");
return int40(value);
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32) {
require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
return int32(value);
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toInt24(int256 value) internal pure returns (int24) {
require(value >= type(int24).min && value <= type(int24).max, "SafeCast: value doesn't fit in 24 bits");
return int24(value);
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16) {
require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
return int16(value);
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8) {
require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
return int8(value);
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*
* _Available since v3.0._
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
interface IAdminACLV0 {
/**
* @notice Token ID `_tokenId` minted to `_to`.
* @param previousSuperAdmin The previous superAdmin address.
* @param newSuperAdmin The new superAdmin address.
* @param genArt721CoreAddressesToUpdate Array of genArt721Core
* addresses to update to the new superAdmin, for indexing purposes only.
*/
event SuperAdminTransferred(
address indexed previousSuperAdmin,
address indexed newSuperAdmin,
address[] genArt721CoreAddressesToUpdate
);
/// Type of the Admin ACL contract, e.g. "AdminACLV0"
function AdminACLType() external view returns (string memory);
/// super admin address
function superAdmin() external view returns (address);
/**
* @notice Calls transferOwnership on other contract from this contract.
* This is useful for updating to a new AdminACL contract.
* @dev this function should be gated to only superAdmin-like addresses.
*/
function transferOwnershipOn(
address _contract,
address _newAdminACL
) external;
/**
* @notice Calls renounceOwnership on other contract from this contract.
* @dev this function should be gated to only superAdmin-like addresses.
*/
function renounceOwnershipOn(address _contract) external;
/**
* @notice Checks if sender `_sender` is allowed to call function with selector
* `_selector` on contract `_contract`.
*/
function allowed(
address _sender,
address _contract,
bytes4 _selector
) external returns (bool);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IEngineRegistryV0.sol";
interface ICoreRegistryV1 is IEngineRegistryV0 {
function registerContracts(
address[] calldata contractAddresses,
bytes32[] calldata coreVersions,
bytes32[] calldata coreTypes
) external;
function unregisterContracts(address[] calldata contractAddresses) external;
function getNumRegisteredContracts() external view returns (uint256);
function getRegisteredContractAt(
uint256 index
) external view returns (address);
function isRegisteredContract(
address contractAddress
) external view returns (bool isRegistered);
}// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.19;
/// @dev Source: https://github.com/0xfoobar/delegation-registry/blob/main/src/IDelegationRegistry.sol
/**
* @title An immutable registry contract to be deployed as a standalone primitive
* @dev See EIP-5639, new project launches can read previous cold wallet -> hot wallet delegations
* from here and integrate those permissions into their flow
*/
interface IDelegationRegistry {
/// @notice Delegation type
enum DelegationType {
NONE,
ALL,
CONTRACT,
TOKEN
}
/// @notice Info about a single delegation, used for onchain enumeration
struct DelegationInfo {
DelegationType type_;
address vault;
address delegate;
address contract_;
uint256 tokenId;
}
/// @notice Info about a single contract-level delegation
struct ContractDelegation {
address contract_;
address delegate;
}
/// @notice Info about a single token-level delegation
struct TokenDelegation {
address contract_;
uint256 tokenId;
address delegate;
}
/// @notice Emitted when a user delegates their entire wallet
event DelegateForAll(address vault, address delegate, bool value);
/// @notice Emitted when a user delegates a specific contract
event DelegateForContract(
address vault,
address delegate,
address contract_,
bool value
);
/// @notice Emitted when a user delegates a specific token
event DelegateForToken(
address vault,
address delegate,
address contract_,
uint256 tokenId,
bool value
);
/// @notice Emitted when a user revokes all delegations
event RevokeAllDelegates(address vault);
/// @notice Emitted when a user revoes all delegations for a given delegate
event RevokeDelegate(address vault, address delegate);
/**
* ----------- WRITE -----------
*/
/**
* @notice Allow the delegate to act on your behalf for all contracts
* @param delegate The hotwallet to act on your behalf
* @param value Whether to enable or disable delegation for this address, true for setting and false for revoking
*/
function delegateForAll(address delegate, bool value) external;
/**
* @notice Allow the delegate to act on your behalf for a specific contract
* @param delegate The hotwallet to act on your behalf
* @param contract_ The address for the contract you're delegating
* @param value Whether to enable or disable delegation for this address, true for setting and false for revoking
*/
function delegateForContract(
address delegate,
address contract_,
bool value
) external;
/**
* @notice Allow the delegate to act on your behalf for a specific token
* @param delegate The hotwallet to act on your behalf
* @param contract_ The address for the contract you're delegating
* @param tokenId The token id for the token you're delegating
* @param value Whether to enable or disable delegation for this address, true for setting and false for revoking
*/
function delegateForToken(
address delegate,
address contract_,
uint256 tokenId,
bool value
) external;
/**
* @notice Revoke all delegates
*/
function revokeAllDelegates() external;
/**
* @notice Revoke a specific delegate for all their permissions
* @param delegate The hotwallet to revoke
*/
function revokeDelegate(address delegate) external;
/**
* @notice Remove yourself as a delegate for a specific vault
* @param vault The vault which delegated to the msg.sender, and should be removed
*/
function revokeSelf(address vault) external;
/**
* ----------- READ -----------
*/
/**
* @notice Returns all active delegations a given delegate is able to claim on behalf of
* @param delegate The delegate that you would like to retrieve delegations for
* @return info Array of DelegationInfo structs
*/
function getDelegationsByDelegate(
address delegate
) external view returns (DelegationInfo[] memory);
/**
* @notice Returns an array of wallet-level delegates for a given vault
* @param vault The cold wallet who issued the delegation
* @return addresses Array of wallet-level delegates for a given vault
*/
function getDelegatesForAll(
address vault
) external view returns (address[] memory);
/**
* @notice Returns an array of contract-level delegates for a given vault and contract
* @param vault The cold wallet who issued the delegation
* @param contract_ The address for the contract you're delegating
* @return addresses Array of contract-level delegates for a given vault and contract
*/
function getDelegatesForContract(
address vault,
address contract_
) external view returns (address[] memory);
/**
* @notice Returns an array of contract-level delegates for a given vault's token
* @param vault The cold wallet who issued the delegation
* @param contract_ The address for the contract holding the token
* @param tokenId The token id for the token you're delegating
* @return addresses Array of contract-level delegates for a given vault's token
*/
function getDelegatesForToken(
address vault,
address contract_,
uint256 tokenId
) external view returns (address[] memory);
/**
* @notice Returns all contract-level delegations for a given vault
* @param vault The cold wallet who issued the delegations
* @return delegations Array of ContractDelegation structs
*/
function getContractLevelDelegations(
address vault
) external view returns (ContractDelegation[] memory delegations);
/**
* @notice Returns all token-level delegations for a given vault
* @param vault The cold wallet who issued the delegations
* @return delegations Array of TokenDelegation structs
*/
function getTokenLevelDelegations(
address vault
) external view returns (TokenDelegation[] memory delegations);
/**
* @notice Returns true if the address is delegated to act on the entire vault
* @param delegate The hotwallet to act on your behalf
* @param vault The cold wallet who issued the delegation
*/
function checkDelegateForAll(
address delegate,
address vault
) external view returns (bool);
/**
* @notice Returns true if the address is delegated to act on your behalf for a token contract or an entire vault
* @param delegate The hotwallet to act on your behalf
* @param contract_ The address for the contract you're delegating
* @param vault The cold wallet who issued the delegation
*/
function checkDelegateForContract(
address delegate,
address vault,
address contract_
) external view returns (bool);
/**
* @notice Returns true if the address is delegated to act on your behalf for a specific token, the token's contract or an entire vault
* @param delegate The hotwallet to act on your behalf
* @param contract_ The address for the contract you're delegating
* @param tokenId The token id for the token you're delegating
* @param vault The cold wallet who issued the delegation
*/
function checkDelegateForToken(
address delegate,
address vault,
address contract_,
uint256 tokenId
) external view returns (bool);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
interface IEngineRegistryV0 {
/// ADDRESS
/**
* @notice contract has been registered as a contract that is powered by the Art Blocks Engine.
*/
event ContractRegistered(
address indexed _contractAddress,
bytes32 _coreVersion,
bytes32 _coreType
);
/// ADDRESS
/**
* @notice contract has been unregistered as a contract that is powered by the Art Blocks Engine.
*/
event ContractUnregistered(address indexed _contractAddress);
/**
* @notice Emits a `ContractRegistered` event with the provided information.
* @dev this function should be gated to only deployer addresses.
*/
function registerContract(
address _contractAddress,
bytes32 _coreVersion,
bytes32 _coreType
) external;
/**
* @notice Emits a `ContractUnregistered` event with the provided information, validating that the provided
* address was indeed previously registered.
* @dev this function should be gated to only deployer addresses.
*/
function unregisterContract(address _contractAddress) external;
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
interface IFilteredMinterV0 {
/**
* @notice Price per token in wei updated for project `_projectId` to
* `_pricePerTokenInWei`.
*/
event PricePerTokenInWeiUpdated(
uint256 indexed _projectId,
uint256 indexed _pricePerTokenInWei
);
/**
* @notice Currency updated for project `_projectId` to symbol
* `_currencySymbol` and address `_currencyAddress`.
*/
event ProjectCurrencyInfoUpdated(
uint256 indexed _projectId,
address indexed _currencyAddress,
string _currencySymbol
);
/// togglePurchaseToDisabled updated
event PurchaseToDisabledUpdated(
uint256 indexed _projectId,
bool _purchaseToDisabled
);
// getter function of public variable
function minterType() external view returns (string memory);
function genArt721CoreAddress() external returns (address);
function minterFilterAddress() external returns (address);
// Triggers a purchase of a token from the desired project, to the
// TX-sending address.
function purchase(
uint256 _projectId
) external payable returns (uint256 tokenId);
// Triggers a purchase of a token from the desired project, to the specified
// receiving address.
function purchaseTo(
address _to,
uint256 _projectId
) external payable returns (uint256 tokenId);
// Toggles the ability for `purchaseTo` to be called directly with a
// specified receiving address that differs from the TX-sending address.
function togglePurchaseToDisabled(uint256 _projectId) external;
// Called to make the minter contract aware of the max invocations for a
// given project.
function setProjectMaxInvocations(uint256 _projectId) external;
// Gets if token price is configured, token price in wei, currency symbol,
// and currency address, assuming this is project's minter.
// Supersedes any defined core price.
function getPriceInfo(
uint256 _projectId
)
external
view
returns (
bool isConfigured,
uint256 tokenPriceInWei,
string memory currencySymbol,
address currencyAddress
);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
import "./IFilteredMinterV0.sol";
pragma solidity ^0.8.0;
/**
* @title This interface extends the IFilteredMinterV0 interface in order to
* add support for generic project minter configuration updates.
* @dev keys represent strings of finite length encoded in bytes32 to minimize
* gas.
* @author Art Blocks Inc.
*/
interface IFilteredMinterV1 is IFilteredMinterV0 {
/// ANY
/**
* @notice Generic project minter configuration event. Removes key `_key`
* for project `_projectId`.
*/
event ConfigKeyRemoved(uint256 indexed _projectId, bytes32 _key);
/// BOOL
/**
* @notice Generic project minter configuration event. Sets value of key
* `_key` to `_value` for project `_projectId`.
*/
event ConfigValueSet(uint256 indexed _projectId, bytes32 _key, bool _value);
/// UINT256
/**
* @notice Generic project minter configuration event. Sets value of key
* `_key` to `_value` for project `_projectId`.
*/
event ConfigValueSet(
uint256 indexed _projectId,
bytes32 _key,
uint256 _value
);
/**
* @notice Generic project minter configuration event. Adds value `_value`
* to the set of uint256 at key `_key` for project `_projectId`.
*/
event ConfigValueAddedToSet(
uint256 indexed _projectId,
bytes32 _key,
uint256 _value
);
/**
* @notice Generic project minter configuration event. Removes value
* `_value` to the set of uint256 at key `_key` for project `_projectId`.
*/
event ConfigValueRemovedFromSet(
uint256 indexed _projectId,
bytes32 _key,
uint256 _value
);
/// ADDRESS
/**
* @notice Generic project minter configuration event. Sets value of key
* `_key` to `_value` for project `_projectId`.
*/
event ConfigValueSet(
uint256 indexed _projectId,
bytes32 _key,
address _value
);
/**
* @notice Generic project minter configuration event. Adds value `_value`
* to the set of addresses at key `_key` for project `_projectId`.
*/
event ConfigValueAddedToSet(
uint256 indexed _projectId,
bytes32 _key,
address _value
);
/**
* @notice Generic project minter configuration event. Removes value
* `_value` to the set of addresses at key `_key` for project `_projectId`.
*/
event ConfigValueRemovedFromSet(
uint256 indexed _projectId,
bytes32 _key,
address _value
);
/// BYTES32
/**
* @notice Generic project minter configuration event. Sets value of key
* `_key` to `_value` for project `_projectId`.
*/
event ConfigValueSet(
uint256 indexed _projectId,
bytes32 _key,
bytes32 _value
);
/**
* @notice Generic project minter configuration event. Adds value `_value`
* to the set of bytes32 at key `_key` for project `_projectId`.
*/
event ConfigValueAddedToSet(
uint256 indexed _projectId,
bytes32 _key,
bytes32 _value
);
/**
* @notice Generic project minter configuration event. Removes value
* `_value` to the set of bytes32 at key `_key` for project `_projectId`.
*/
event ConfigValueRemovedFromSet(
uint256 indexed _projectId,
bytes32 _key,
bytes32 _value
);
/**
* @dev Strings not supported. Recommend conversion of (short) strings to
* bytes32 to remain gas-efficient.
*/
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
import "./IFilteredMinterV1.sol";
pragma solidity ^0.8.0;
/**
* @title This interface extends the IFilteredMinterV1 interface in order to
* add support for manually setting project max invocations.
* @author Art Blocks Inc.
*/
interface IFilteredMinterV2 is IFilteredMinterV1 {
/**
* @notice Local max invocations for project `_projectId`, tied to core contract `_coreContractAddress`,
* updated to `_maxInvocations`.
*/
event ProjectMaxInvocationsLimitUpdated(
uint256 indexed _projectId,
uint256 _maxInvocations
);
// Sets the local max invocations for a given project, checking that the provided max invocations is
// less than or equal to the global max invocations for the project set on the core contract.
// This does not impact the max invocations value defined on the core contract.
function manuallyLimitProjectMaxInvocations(
uint256 _projectId,
uint256 _maxInvocations
) external;
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IAdminACLV0.sol";
/// use the Royalty Registry's IManifold interface for token royalties
import "./IManifold.sol";
/**
* @title This interface is intended to house interface items that are common
* across all GenArt721CoreContractV3 flagship and derivative implementations.
* This interface extends the IManifold royalty interface in order to
* add support the Royalty Registry by default.
* @author Art Blocks Inc.
*/
interface IGenArt721CoreContractV3_Base is IManifold {
/**
* @notice Token ID `_tokenId` minted to `_to`.
*/
event Mint(address indexed _to, uint256 indexed _tokenId);
/**
* @notice currentMinter updated to `_currentMinter`.
* @dev Implemented starting with V3 core
*/
event MinterUpdated(address indexed _currentMinter);
/**
* @notice Platform updated on bytes32-encoded field `_field`.
*/
event PlatformUpdated(bytes32 indexed _field);
/**
* @notice Project ID `_projectId` updated on bytes32-encoded field
* `_update`.
*/
event ProjectUpdated(uint256 indexed _projectId, bytes32 indexed _update);
event ProposedArtistAddressesAndSplits(
uint256 indexed _projectId,
address _artistAddress,
address _additionalPayeePrimarySales,
uint256 _additionalPayeePrimarySalesPercentage,
address _additionalPayeeSecondarySales,
uint256 _additionalPayeeSecondarySalesPercentage
);
event AcceptedArtistAddressesAndSplits(uint256 indexed _projectId);
// version and type of the core contract
// coreVersion is a string of the form "0.x.y"
function coreVersion() external view returns (string memory);
// coreType is a string of the form "GenArt721CoreV3"
function coreType() external view returns (string memory);
// owner (pre-V3 was named admin) of contract
// this is expected to be an Admin ACL contract for V3
function owner() external view returns (address);
// Admin ACL contract for V3, will be at the address owner()
function adminACLContract() external returns (IAdminACLV0);
// backwards-compatible (pre-V3) admin - equal to owner()
function admin() external view returns (address);
/**
* Function determining if _sender is allowed to call function with
* selector _selector on contract `_contract`. Intended to be used with
* peripheral contracts such as minters, as well as internally by the
* core contract itself.
*/
function adminACLAllowed(
address _sender,
address _contract,
bytes4 _selector
) external returns (bool);
/// getter function of public variable
function startingProjectId() external view returns (uint256);
// getter function of public variable
function nextProjectId() external view returns (uint256);
// getter function of public mapping
function tokenIdToProjectId(
uint256 tokenId
) external view returns (uint256 projectId);
// @dev this is not available in V0
function isMintWhitelisted(address minter) external view returns (bool);
function projectIdToArtistAddress(
uint256 _projectId
) external view returns (address payable);
function projectIdToAdditionalPayeePrimarySales(
uint256 _projectId
) external view returns (address payable);
function projectIdToAdditionalPayeePrimarySalesPercentage(
uint256 _projectId
) external view returns (uint256);
function projectIdToSecondaryMarketRoyaltyPercentage(
uint256 _projectId
) external view returns (uint256);
function projectURIInfo(
uint256 _projectId
) external view returns (string memory projectBaseURI);
// @dev new function in V3
function projectStateData(
uint256 _projectId
)
external
view
returns (
uint256 invocations,
uint256 maxInvocations,
bool active,
bool paused,
uint256 completedTimestamp,
bool locked
);
function projectDetails(
uint256 _projectId
)
external
view
returns (
string memory projectName,
string memory artist,
string memory description,
string memory website,
string memory license
);
function projectScriptDetails(
uint256 _projectId
)
external
view
returns (
string memory scriptTypeAndVersion,
string memory aspectRatio,
uint256 scriptCount
);
function projectScriptByIndex(
uint256 _projectId,
uint256 _index
) external view returns (string memory);
function tokenIdToHash(uint256 _tokenId) external view returns (bytes32);
// function to set a token's hash (must be guarded)
function setTokenHash_8PT(uint256 _tokenId, bytes32 _hash) external;
// @dev gas-optimized signature in V3 for `mint`
function mint_Ecf(
address _to,
uint256 _projectId,
address _by
) external returns (uint256 tokenId);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IAdminACLV0.sol";
import "./IGenArt721CoreContractV3_Base.sol";
interface IGenArt721CoreContractV3_Engine is IGenArt721CoreContractV3_Base {
// @dev new function in V3
function getPrimaryRevenueSplits(
uint256 _projectId,
uint256 _price
)
external
view
returns (
uint256 renderProviderRevenue_,
address payable renderProviderAddress_,
uint256 platformProviderRevenue_,
address payable platformProviderAddress_,
uint256 artistRevenue_,
address payable artistAddress_,
uint256 additionalPayeePrimaryRevenue_,
address payable additionalPayeePrimaryAddress_
);
// @dev The render provider primary sales payment address
function renderProviderPrimarySalesAddress()
external
view
returns (address payable);
// @dev The platform provider primary sales payment address
function platformProviderPrimarySalesAddress()
external
view
returns (address payable);
// @dev Percentage of primary sales allocated to the render provider
function renderProviderPrimarySalesPercentage()
external
view
returns (uint256);
// @dev Percentage of primary sales allocated to the platform provider
function platformProviderPrimarySalesPercentage()
external
view
returns (uint256);
// @dev The render provider secondary sales royalties payment address
function renderProviderSecondarySalesAddress()
external
view
returns (address payable);
// @dev The platform provider secondary sales royalties payment address
function platformProviderSecondarySalesAddress()
external
view
returns (address payable);
// @dev Basis points of secondary sales allocated to the render provider
function renderProviderSecondarySalesBPS() external view returns (uint256);
// @dev Basis points of secondary sales allocated to the platform provider
function platformProviderSecondarySalesBPS()
external
view
returns (uint256);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IAdminACLV0.sol";
import "./IGenArt721CoreContractV3_Base.sol";
/**
* @title This interface extends IGenArt721CoreContractV3_Base with functions
* that are part of the Art Blocks Flagship core contract.
* @author Art Blocks Inc.
*/
// This interface extends IGenArt721CoreContractV3_Base with functions that are
// in part of the Art Blocks Flagship core contract.
interface IGenArt721CoreContractV3 is IGenArt721CoreContractV3_Base {
// @dev new function in V3
function getPrimaryRevenueSplits(
uint256 _projectId,
uint256 _price
)
external
view
returns (
uint256 artblocksRevenue_,
address payable artblocksAddress_,
uint256 artistRevenue_,
address payable artistAddress_,
uint256 additionalPayeePrimaryRevenue_,
address payable additionalPayeePrimaryAddress_
);
// @dev Art Blocks primary sales payment address
function artblocksPrimarySalesAddress()
external
view
returns (address payable);
/**
* @notice Backwards-compatible (pre-V3) function returning Art Blocks
* primary sales payment address (now called artblocksPrimarySalesAddress).
*/
function artblocksAddress() external view returns (address payable);
// @dev Percentage of primary sales allocated to Art Blocks
function artblocksPrimarySalesPercentage() external view returns (uint256);
/**
* @notice Backwards-compatible (pre-V3) function returning Art Blocks
* primary sales percentage (now called artblocksPrimarySalesPercentage).
*/
function artblocksPercentage() external view returns (uint256);
// @dev Art Blocks secondary sales royalties payment address
function artblocksSecondarySalesAddress()
external
view
returns (address payable);
// @dev Basis points of secondary sales allocated to Art Blocks
function artblocksSecondarySalesBPS() external view returns (uint256);
/**
* @notice Backwards-compatible (pre-V3) function that gets artist +
* artist's additional payee royalty data for token ID `_tokenId`.
* WARNING: Does not include Art Blocks portion of royalties.
*/
function getRoyaltyData(
uint256 _tokenId
)
external
view
returns (
address artistAddress,
address additionalPayee,
uint256 additionalPayeePercentage,
uint256 royaltyFeeByID
);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @dev Royalty Registry interface, used to support the Royalty Registry.
/// @dev Source: https://github.com/manifoldxyz/royalty-registry-solidity/blob/main/contracts/specs/IManifold.sol
/// @author: manifold.xyz
/**
* @dev Royalty interface for creator core classes
*/
interface IManifold {
/**
* @dev Get royalites of a token. Returns list of receivers and basisPoints
*
* bytes4(keccak256('getRoyalties(uint256)')) == 0xbb3bafd6
*
* => 0xbb3bafd6 = 0xbb3bafd6
*/
function getRoyalties(
uint256 tokenId
) external view returns (address payable[] memory, uint256[] memory);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
import "./IFilteredMinterV2.sol";
pragma solidity ^0.8.0;
/**
* @title This interface defines any events or functions required for a minter
* to conform to the MinterBase contract.
* @dev The MinterBase contract was not implemented from the beginning of the
* MinterSuite contract suite, therefore early versions of some minters may not
* conform to this interface.
* @author Art Blocks Inc.
*/
interface IMinterBaseV0 {
// Function that returns if a minter is configured to integrate with a V3 flagship or V3 engine contract.
// Returns true only if the minter is configured to integrate with an engine contract.
function isEngine() external returns (bool isEngine);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./ICoreRegistryV1.sol";
import "./IAdminACLV0.sol";
/**
* @title IMinterFilterV1
* @author Art Blocks Inc.
* @notice Interface for a new minter filter contract.
* This interface does not extend the previous version of the minter filter
* interface, as the previous version is not compatible with the new
* minter filter architecture.
* @dev This interface is for a minter filter that supports multiple core
* contracts, and allows for a minter to be set on a per-project basis.
*/
interface IMinterFilterV1 {
/**
* @notice Emitted when contract is deployed to notify indexing services
* of the new contract deployment.
*/
event Deployed();
/**
* @notice Globally approved minter `minter`.
*/
event MinterApprovedGlobally(address indexed minter, string minterType);
/**
* @notice Globally revoked minter `minter`.
* @dev contract owner may still approve this minter on a per-contract
* basis.
*/
event MinterRevokedGlobally(address indexed minter);
/**
* @notice Approved minter `minter` on core contract
* `coreContract`.
*/
event MinterApprovedForContract(
address indexed coreContract,
address indexed minter,
string minterType
);
/**
* @notice Revoked minter `minter` on core contract `coreContract`.
* @dev minter filter owner may still globally approve this minter for all
* contracts.
*/
event MinterRevokedForContract(
address indexed coreContract,
address indexed minter
);
/**
* @notice Minter at address `minter` set as minter for project
* `projectId` on core contract `coreContract`.
*/
event ProjectMinterRegistered(
uint256 indexed projectId,
address indexed coreContract,
address indexed minter,
string minterType
);
/**
* @notice Minter removed for project `projectId` on core contract
* `coreContract`.
*/
event ProjectMinterRemoved(
uint256 indexed projectId,
address indexed coreContract
);
/**
* @notice Admin ACL contract updated to `adminACLContract`.
*/
event AdminACLUpdated(address indexed adminACLContract);
/**
* @notice Core Registry contract updated to `coreRegistry`.
*/
event CoreRegistryUpdated(address indexed coreRegistry);
// struct used to return minter info
// @dev this is not used for storage of data
struct MinterWithType {
address minterAddress;
string minterType;
}
function setMinterForProject(
uint256 projectId,
address coreContract,
address minter
) external;
function removeMinterForProject(
uint256 projectId,
address coreContract
) external;
// @dev function name is optimized for gas
function mint_joo(
address to,
uint256 projectId,
address coreContract,
address sender
) external returns (uint256);
function updateCoreRegistry(address coreRegistry) external;
/**
* @notice Returns if `sender` is allowed to call function on `contract`
* with `selector` selector, according to the MinterFilter's Admin ACL.
*/
function adminACLAllowed(
address sender,
address contract_,
bytes4 selector
) external returns (bool);
function minterFilterType() external pure returns (string memory);
function getMinterForProject(
uint256 projectId,
address coreContract
) external view returns (address);
function projectHasMinter(
uint256 projectId,
address coreContract
) external view returns (bool);
/**
* @notice View that returns if a core contract is registered with the
* core registry, allowing this minter filter to service it.
* @param coreContract core contract address to be checked
*/
function isRegisteredCoreContract(
address coreContract
) external view returns (bool);
/// Address of current core registry contract
function coreRegistry() external view returns (ICoreRegistryV1);
/// The current admin ACL contract
function adminACLContract() external view returns (IAdminACLV0);
/// The quantity of projects on a core contract that have assigned minters
function getNumProjectsOnContractWithMinters(
address coreContract
) external view returns (uint256);
function getProjectAndMinterInfoOnContractAt(
address coreContract,
uint256 index
)
external
view
returns (
uint256 projectId,
address minterAddress,
string memory minterType
);
function getAllGloballyApprovedMinters()
external
view
returns (MinterWithType[] memory mintersWithTypes);
function getAllContractApprovedMinters(
address coreContract
) external view returns (MinterWithType[] memory mintersWithTypes);
/**
* Owner of contract.
* @dev This returns the address of the Admin ACL contract.
*/
function owner() external view returns (address);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
/**
* @title This interface adds support for including Merkle proofs when purchasing.
* @author Art Blocks Inc.
*/
interface ISharedMinterMerkleV0 {
// Triggers a purchase of a token from the desired project, to the
// TX-sending address. Requires Merkle proof.
function purchase(
uint256 projectId,
address coreContract,
bytes32[] calldata proof
) external payable returns (uint256 tokenId);
// Triggers a purchase of a token from the desired project, to the specified
// receiving address. Requires Merkle proof.
function purchaseTo(
address to,
uint256 projectId,
address coreContract,
bytes32[] calldata proof
) external payable returns (uint256 tokenId);
// Updates the Merkle root for the desired project.
function updateMerkleRoot(
uint256 projectId,
address coreContract,
bytes32 root
) external;
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
interface ISharedMinterV0 {
// Sets the local max invocations for a given project, checking that the provided max invocations is
// less than or equal to the global max invocations for the project set on the core contract.
// This does not impact the max invocations value defined on the core contract.
function manuallyLimitProjectMaxInvocations(
uint256 projectId,
address coreContract,
uint24 maxInvocations
) external;
// Called to make the minter contract aware of the max invocations for a
// given project.
function syncProjectMaxInvocationsToCore(
uint256 projectId,
address coreContract
) external;
// getter function of public variable
function minterType() external view returns (string memory);
function minterFilterAddress() external returns (address);
// Gets if token price is configured, token price in wei, currency symbol,
// and currency address, assuming this is project's minter.
// Supersedes any defined core price.
function getPriceInfo(
uint256 projectId,
address coreContract
)
external
view
returns (
bool isConfigured,
uint256 tokenPriceInWei,
string memory currencySymbol,
address currencyAddress
);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
/**
* @title Art Blocks Helpers Library
* @notice This library contains helper functions for common operations in the
* Art Blocks ecosystem of smart contracts.
* @author Art Blocks Inc.
*/
library ABHelpers {
uint256 constant ONE_MILLION = 1_000_000;
/**
* @notice Function to convert token id to project id.
* @param tokenId The id of the token.
*/
function tokenIdToProjectId(
uint256 tokenId
) internal pure returns (uint256) {
// int division properly rounds down
// @dev unchecked because will never divide by zero
unchecked {
return tokenId / ONE_MILLION;
}
}
/**
* @notice Function to convert token id to token number.
* @param tokenId The id of the token.
*/
function tokenIdToTokenNumber(
uint256 tokenId
) internal pure returns (uint256) {
// mod returns remainder, which is the token number
// @dev no way to disable mod zero check in solidity, so not unchecked
return tokenId % ONE_MILLION;
}
/**
* @notice Function to convert token id to token invocation.
* @dev token invocation is the token number plus one, because token #0 is
* invocation 1.
* @param tokenId The id of the token.
*/
function tokenIdToTokenInvocation(
uint256 tokenId
) internal pure returns (uint256) {
// mod returns remainder, which is the token number
// @dev no way to disable mod zero check in solidity, so not unchecked
return (tokenId % ONE_MILLION) + 1;
}
/**
* @notice Function to convert project id and token number to token id.
* @param projectId The id of the project.
* @param tokenNumber The token number.
*/
function tokenIdFromProjectIdAndTokenNumber(
uint256 projectId,
uint256 tokenNumber
) internal pure returns (uint256) {
// @dev intentionally not unchecked to ensure overflow detection, which
// would likley only occur in a malicious call
return (projectId * ONE_MILLION) + tokenNumber;
}
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {IGenArt721CoreContractV3_Base} from "../../interfaces/v0.8.x/IGenArt721CoreContractV3_Base.sol";
import {IMinterFilterV1} from "../../interfaces/v0.8.x/IMinterFilterV1.sol";
/**
* @title Art Blocks Authorization Minter Library
* @notice This library contains helper functions that may be used contracts to
* check authorization for performing operations in the Art Blocks V3 core
* contract ecosystem.
* @author Art Blocks Inc.
*/
library AuthLib {
/**
* @notice Function to restrict access to only AdminACL allowed calls, where
* AdminACL is the admin of an IMinterFilterV1.
* Reverts if not allowed.
* @param minterFilterAddress address of the minter filter to be checked,
* should implement IMinterFilterV1
* @param sender address of the caller
* @param contract_ address of the contract being called
* @param selector selector of the function being called
*/
function onlyMinterFilterAdminACL(
address minterFilterAddress,
address sender,
address contract_,
bytes4 selector
) internal {
require(
_minterFilterAdminACLAllowed({
minterFilterAddress: minterFilterAddress,
sender: sender,
contract_: contract_,
selector: selector
}),
"Only MinterFilter AdminACL"
);
}
/**
* @notice Function to restrict access to only AdminACL allowed calls, where
* AdminACL is the admin of a core contract at `coreContract`.
* Reverts if not allowed.
* @param coreContract address of the core contract to be checked
* @param sender address of the caller
* @param contract_ address of the contract being called
* @param selector selector of the function being called
*/
function onlyCoreAdminACL(
address coreContract,
address sender,
address contract_,
bytes4 selector
) internal {
require(
_coreAdminACLAllowed({
coreContract: coreContract,
sender: sender,
contract_: contract_,
selector: selector
}),
"Only Core AdminACL allowed"
);
}
/**
* @notice Throws if `sender` is any account other than the artist of the
* specified project `projectId` on core contract `coreContract`.
* @param projectId The ID of the project being checked.
* @param coreContract The address of the GenArt721CoreContractV3_Base
* contract.
* @param sender Wallet to check. Typically, the address of the caller.
* @dev `sender` must be the artist associated with `projectId` on `coreContract`.
*/
function onlyArtist(
uint256 projectId,
address coreContract,
address sender
) internal view {
require(
_senderIsArtist({
projectId: projectId,
coreContract: coreContract,
sender: sender
}),
"Only Artist"
);
}
/**
* @notice Function to restrict access to only the artist of a project, or AdminACL
* allowed calls, where AdminACL is the admin of a core contract at
* `coreContract`.
* @param projectId id of the project
* @param coreContract address of the core contract to be checked
* @param sender address of the caller
* @param contract_ address of the contract being called
* @param selector selector of the function being called
*/
function onlyCoreAdminACLOrArtist(
uint256 projectId,
address coreContract,
address sender,
address contract_,
bytes4 selector
) internal {
require(
_senderIsArtist({
projectId: projectId,
coreContract: coreContract,
sender: sender
}) ||
_coreAdminACLAllowed({
coreContract: coreContract,
sender: sender,
contract_: contract_,
selector: selector
}),
"Only Artist or Core Admin ACL"
);
}
// ------------------------------------------------------------------------
// Private functions used internally by this library
// ------------------------------------------------------------------------
/**
* @notice Private function that returns if minter filter contract's AdminACL
* allows `sender` to call function with selector `selector` on contract
* `contract`.
* @param minterFilterAddress address of the minter filter to be checked.
* Should implement IMinterFilterV1.
* @param sender address of the caller
* @param contract_ address of the contract being called
* @param selector selector of the function being called
*/
function _minterFilterAdminACLAllowed(
address minterFilterAddress,
address sender,
address contract_,
bytes4 selector
) private returns (bool) {
return
IMinterFilterV1(minterFilterAddress).adminACLAllowed({
sender: sender,
contract_: contract_,
selector: selector
});
}
/**
* @notice Private function that returns if core contract's AdminACL allows
* `sender` to call function with selector `selector` on contract
* `contract`.
* @param coreContract address of the core contract to be checked
* @param sender address of the caller
* @param contract_ address of the contract being called
* @param selector selector of the function being called
*/
function _coreAdminACLAllowed(
address coreContract,
address sender,
address contract_,
bytes4 selector
) private returns (bool) {
return
IGenArt721CoreContractV3_Base(coreContract).adminACLAllowed({
_sender: sender,
_contract: contract_,
_selector: selector
});
}
/**
* @notice Private function that returns if `sender` is the artist of `projectId`
* on `coreContract`.
* @param projectId project ID to check
* @param coreContract core contract to check
* @param sender wallet to check
*/
function _senderIsArtist(
uint256 projectId,
address coreContract,
address sender
) private view returns (bool senderIsArtist) {
return
sender ==
IGenArt721CoreContractV3_Base(coreContract)
.projectIdToArtistAddress(projectId);
}
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
/**
* @title Art Blocks Generic Events Library
* @notice This library is designed to define a set of generic events that all
* shared minter libraries may utilize to populate indexed extra minter details
* @dev Strings not supported. Recommend conversion of (short) strings to
* bytes32 to remain gas-efficient.
* @author Art Blocks Inc.
*/
library GenericMinterEventsLib {
/**
* @notice Generic project minter configuration event. Removes key `key`
* for project `projectId`.
* @param projectId Project ID to remove key for
* @param coreContract Core contract address that projectId is on
* @param key Key to remove
*/
event ConfigKeyRemoved(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key
);
/// BOOL
/**
* @notice Generic project minter configuration event. Sets value of key
* `key` to `value` for project `projectId`.
* @param projectId Project ID to set key for
* @param coreContract Core contract address that projectId is on
* @param key Key to set
* @param value Value to set key to
*/
event ConfigValueSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
bool value
);
/// UINT256
/**
* @notice Generic project minter configuration event. Sets value of key
* `key` to `value` for project `projectId`.
* @param projectId Project ID to set key for
* @param coreContract Core contract address that projectId is on
* @param key Key to set
* @param value Value to set key to
*/
event ConfigValueSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
uint256 value
);
/**
* @notice Generic project minter configuration event. Adds value `value`
* to the set of uint256 at key `key` for project `projectId`.
* @param projectId Project ID to set key for
* @param coreContract Core contract address that projectId is on
* @param key Key to modify
* @param value Value to add to the key's set
*/
event ConfigValueAddedToSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
uint256 value
);
/**
* @notice Generic project minter configuration event. Removes value
* `value` to the set of uint256 at key `key` for project `projectId`.
* @param projectId Project ID to set key for
* @param coreContract Core contract address that projectId is on
* @param key Key to modify
* @param value Value removed from the key's set
*/
event ConfigValueRemovedFromSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
uint256 value
);
/// ADDRESS
/**
* @notice Generic project minter configuration event. Sets value of key
* `key` to `value` for project `projectId`.
* @param projectId Project ID to set key for
* @param coreContract Core contract address that projectId is on
* @param key Key to set
* @param value Value to set key to
*/
event ConfigValueSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
address value
);
/**
* @notice Generic project minter configuration event. Adds value `value`
* to the set of addresses at key `key` for project `projectId`.
* @param projectId Project ID to set key for
* @param coreContract Core contract address that projectId is on
* @param key Key to modify
* @param value Value to add to the key's set
*/
event ConfigValueAddedToSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
address value
);
/**
* @notice Generic project minter configuration event. Removes value
* `value` to the set of addresses at key `key` for project `projectId`.
* @param projectId Project ID to set key for
* @param coreContract Core contract address that projectId is on
* @param key Key to modify
* @param value Value removed from the key's set
*/
event ConfigValueRemovedFromSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
address value
);
/// BYTES32
/**
* @notice Generic project minter configuration event. Sets value of key
* `key` to `value` for project `projectId`.
* @param projectId Project ID to set key for
* @param coreContract Core contract address that projectId is on
* @param key Key to set
* @param value Value to set key to
*/
event ConfigValueSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
bytes32 value
);
/**
* @notice Generic project minter configuration event. Adds value `value`
* to the set of bytes32 at key `key` for project `projectId`.
* @param projectId Project ID to set key for
* @param coreContract Core contract address that projectId is on
* @param key Key to modify
* @param value Value to add to the key's set
*/
event ConfigValueAddedToSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
bytes32 value
);
/**
* @notice Generic project minter configuration event. Removes value
* `value` to the set of bytes32 at key `key` for project `projectId`.
* @param projectId Project ID to set key for
* @param coreContract Core contract address that projectId is on
* @param key Key to modify
* @param value Value removed from the key's set
*/
event ConfigValueRemovedFromSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
bytes32 value
);
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {IGenArt721CoreContractV3_Base} from "../../../interfaces/v0.8.x/IGenArt721CoreContractV3_Base.sol";
import {ABHelpers} from "../ABHelpers.sol";
import {Math} from "@openzeppelin-4.7/contracts/utils/math/Math.sol";
import {SafeCast} from "@openzeppelin-4.7/contracts/utils/math/SafeCast.sol";
/**
* @title Art Blocks Max Invocations Library
* @notice This library manages the maximum invocation limits for Art Blocks
* projects. It provides functionality for synchronizing, manually limiting, and
* updating these limits, ensuring the integrity in relation to the core Art
* Blocks contract, and managing updates upon token minting.
* @dev Functions include `syncProjectMaxInvocationsToCore`,
* `manuallyLimitProjectMaxInvocations`, and `purchaseEffectsInvocations`.
* @author Art Blocks Inc.
*/
library MaxInvocationsLib {
using SafeCast for uint256;
/**
* @notice Local max invocations for project `projectId`, tied to core contract `coreContractAddress`,
* updated to `maxInvocations`.
* @param projectId The id of the project.
* @param coreContract The address of the core contract.
* @param maxInvocations The new max invocations limit.
*/
event ProjectMaxInvocationsLimitUpdated(
uint256 indexed projectId,
address indexed coreContract,
uint256 maxInvocations
);
// position of Max Invocations Lib storage, using a diamond storage pattern
// for this library
bytes32 constant MAX_INVOCATIONS_LIB_STORAGE_POSITION =
keccak256("maxinvocationslib.storage");
uint256 internal constant ONE_MILLION = 1_000_000;
/**
* @notice Data structure that holds max invocations project configuration.
*/
struct MaxInvocationsProjectConfig {
bool maxHasBeenInvoked;
uint24 maxInvocations;
}
// Diamond storage pattern is used in this library
struct MaxInvocationsLibStorage {
mapping(address coreContract => mapping(uint256 projectId => MaxInvocationsProjectConfig)) maxInvocationsProjectConfigs;
}
/**
* @notice Syncs project's max invocations to core contract value.
* @param projectId The id of the project.
* @param coreContract The address of the core contract.
*/
function syncProjectMaxInvocationsToCore(
uint256 projectId,
address coreContract
) internal {
(
uint256 coreInvocations,
uint256 coreMaxInvocations
) = coreContractInvocationData({
projectId: projectId,
coreContract: coreContract
});
// update storage with results
MaxInvocationsProjectConfig
storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
// @dev only bugged core would return > 1e6 invocations, but safe-cast
// for additional overflow safety
maxInvocationsProjectConfig.maxInvocations = coreMaxInvocations
.toUint24();
// We need to ensure maxHasBeenInvoked is correctly set after manually syncing the
// local maxInvocations value with the core contract's maxInvocations value.
maxInvocationsProjectConfig.maxHasBeenInvoked =
coreInvocations == coreMaxInvocations;
emit ProjectMaxInvocationsLimitUpdated({
projectId: projectId,
coreContract: coreContract,
maxInvocations: coreMaxInvocations
});
}
/**
* @notice Manually limits project's max invocations.
* @param projectId The id of the project.
* @param coreContract The address of the core contract.
* @param maxInvocations The new max invocations limit.
*/
function manuallyLimitProjectMaxInvocations(
uint256 projectId,
address coreContract,
uint24 maxInvocations
) internal {
// CHECKS
(
uint256 coreInvocations,
uint256 coreMaxInvocations
) = coreContractInvocationData({
projectId: projectId,
coreContract: coreContract
});
require(
maxInvocations <= coreMaxInvocations,
"Invalid max invocations"
);
require(maxInvocations >= coreInvocations, "Invalid max invocations");
// EFFECTS
MaxInvocationsProjectConfig
storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
// update storage with results
maxInvocationsProjectConfig.maxInvocations = uint24(maxInvocations);
// We need to ensure maxHasBeenInvoked is correctly set after manually setting the
// local maxInvocations value.
maxInvocationsProjectConfig.maxHasBeenInvoked =
coreInvocations == maxInvocations;
emit ProjectMaxInvocationsLimitUpdated({
projectId: projectId,
coreContract: coreContract,
maxInvocations: maxInvocations
});
}
/**
* @notice Validate effects on invocations after purchase. This ensures
* that the token invocation is less than or equal to the local max
* invocations, and also updates the local maxHasBeenInvoked value.
* @dev This function checks that the token invocation is less than or
* equal to the local max invocations, and also updates the local
* maxHasBeenInvoked value.
* @param tokenId The id of the token.
* @param coreContract The address of the core contract.
*/
function validateMintEffectsInvocations(
uint256 tokenId,
address coreContract
) internal {
uint256 projectId = ABHelpers.tokenIdToProjectId(tokenId);
MaxInvocationsProjectConfig
storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
// invocation is token number plus one, and will never overflow due to
// limit of 1e6 invocations per project. block scope for gas efficiency
// (i.e. avoid an unnecessary var initialization to 0).
unchecked {
uint256 tokenInvocation = ABHelpers.tokenIdToTokenInvocation(
tokenId
);
uint256 localMaxInvocations = maxInvocationsProjectConfig
.maxInvocations;
// handle the case where the token invocation == minter local max
// invocations occurred on a different minter, and we have a stale
// local maxHasBeenInvoked value returning a false negative.
// @dev this is a CHECK after EFFECTS, so security was considered
// in detail here.
require(
tokenInvocation <= localMaxInvocations,
"Max invocations reached"
);
// in typical case, update the local maxHasBeenInvoked value
// to true if the token invocation == minter local max invocations
// (enables gas efficient reverts after sellout)
if (tokenInvocation == localMaxInvocations) {
maxInvocationsProjectConfig.maxHasBeenInvoked = true;
}
}
}
/**
* @notice Checks that the max invocations have not been reached for a
* given project. This only checks the minter's local max invocations, and
* does not consider the core contract's max invocations.
* The function reverts if the max invocations have been reached.
* @param projectId The id of the project.
* @param coreContract The address of the core contract.
*/
function preMintChecks(
uint256 projectId,
address coreContract
) internal view {
// check that max invocations have not been reached
MaxInvocationsProjectConfig
storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
require(
!maxInvocationsProjectConfig.maxHasBeenInvoked,
"Max invocations reached"
);
}
/**
* @notice Helper function to check if max invocations has not been initialized.
* Returns true if not initialized, false if initialized.
* @param projectId The id of the project.
* @param coreContract The address of the core contract.
* @return bool
* @dev We know a project's max invocations have never been initialized if
* both max invocations and maxHasBeenInvoked are still initial values.
* This is because if maxInvocations were ever set to zero,
* maxHasBeenInvoked would be set to true.
*/
function maxInvocationsIsUnconfigured(
uint256 projectId,
address coreContract
) internal view returns (bool) {
MaxInvocationsProjectConfig
storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
return
maxInvocationsProjectConfig.maxInvocations == 0 &&
!maxInvocationsProjectConfig.maxHasBeenInvoked;
}
/**
* @notice Function returns if invocations remain available for a given project.
* This function calls the core contract to get the most up-to-date
* invocation data (which may be useful to avoid reverts during mint).
* This function considers core contract max invocations, and minter local
* max invocations, and returns a response based on the most limiting
* max invocations value.
* @param projectId The id of the project.
* @param coreContract The address of the core contract.
*/
function invocationsRemain(
uint256 projectId,
address coreContract
) internal view returns (bool) {
// get up-to-data invocation data from core contract
(
uint256 coreInvocations,
uint256 coreMaxInvocations
) = coreContractInvocationData({
projectId: projectId,
coreContract: coreContract
});
// load minter-local max invocations into memory
MaxInvocationsProjectConfig
storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
// invocations remain available if the core contract has not reached
// the most limiting max invocations, either on minter or core contract
uint256 limitingMaxInvocations = Math.min(
coreMaxInvocations,
maxInvocationsProjectConfig.maxInvocations // local max invocations
);
return coreInvocations < limitingMaxInvocations;
}
/**
* @notice Pulls core contract invocation data for a given project.
* @dev This function calls the core contract to get the invocation data
* @param projectId The id of the project.
* @param coreContract The address of the core contract.
* @return coreInvocations The number of invocations for the project.
* @return coreMaxInvocations The max invocations for the project, as
* defined on the core contract.
*/
function coreContractInvocationData(
uint256 projectId,
address coreContract
)
internal
view
returns (uint256 coreInvocations, uint256 coreMaxInvocations)
{
(
coreInvocations,
coreMaxInvocations,
,
,
,
) = IGenArt721CoreContractV3_Base(coreContract).projectStateData(
projectId
);
}
/**
* @notice Function returns the max invocations for a given project.
* @param projectId The id of the project.
* @param coreContract The address of the core contract.
* to be queried.
*/
function getMaxInvocations(
uint256 projectId,
address coreContract
) internal view returns (uint256) {
MaxInvocationsProjectConfig
storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
return maxInvocationsProjectConfig.maxInvocations;
}
/**
* @notice Function returns if max has been invoked for a given project.
* @param projectId The id of the project.
* @param coreContract The address of the core contract.
* to be queried.
*/
function getMaxHasBeenInvoked(
uint256 projectId,
address coreContract
) internal view returns (bool) {
MaxInvocationsProjectConfig
storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
return maxInvocationsProjectConfig.maxHasBeenInvoked;
}
/**
* @notice Function returns if a project has reached its max invocations.
* Function is labelled as "safe" because it checks the core contract's
* invocations and max invocations. If the local max invocations is greater
* than the core contract's max invocations, it will defer to the core
* contract's max invocations (since those are the limiting factor).
* @param projectId The id of the project.
* @param coreContract The address of the core contract.
*/
function projectMaxHasBeenInvokedSafe(
uint256 projectId,
address coreContract
) internal view returns (bool) {
// get max invocations from core contract
(
uint256 coreInvocations,
uint256 coreMaxInvocations
) = coreContractInvocationData({
projectId: projectId,
coreContract: coreContract
});
MaxInvocationsProjectConfig
storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
uint256 localMaxInvocations = maxInvocationsProjectConfig
.maxInvocations;
// value is locally defined, and could be out of date.
// only possible illogical state is if local max invocations is
// greater than core contract's max invocations, in which case
// we should use the core contract's max invocations
if (localMaxInvocations > coreMaxInvocations) {
// local max invocations is stale and illogical, defer to core
// contract's max invocations since it is the limiting factor
return (coreMaxInvocations == coreInvocations);
}
// local max invocations is limiting, so check core invocations against
// local max invocations
return (coreInvocations >= localMaxInvocations);
}
/**
* @notice Refreshes max invocations to account for core contract max
* invocations state, without imposing any additional restrictions on the
* minter's max invocations state.
* If minter max invocations have never been populated, this function will
* populate them to equal the core contract's max invocations state (which
* is the least restrictive state).
* If minter max invocations have been populated, this function will ensure
* the minter's max invocations are not greater than the core contract's
* max invocations (which would be stale and illogical), and update the
* minter's max invocations and maxHasBeenInvoked state to be consistent
* with the core contract's max invocations.
* If the minter max invocations have been populated and are not greater
* than the core contract's max invocations, this function will do nothing,
* since that is a valid state in which the minter has been configured to
* be more restrictive than the core contract.
* @dev assumes core contract's max invocations may only be reduced, which
* is the case for all V3 core contracts
* @param projectId The id of the project.
* @param coreContract The address of the core contract.
*/
function refreshMaxInvocations(
uint256 projectId,
address coreContract
) internal {
MaxInvocationsProjectConfig
storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
if (maxInvocationsIsUnconfigured(projectId, coreContract)) {
// populate the minter max invocation state to equal the values on
// the core contract (least restrictive state)
syncProjectMaxInvocationsToCore({
projectId: projectId,
coreContract: coreContract
});
} else {
// if local max invocations were already populated, validate the local state
(
uint256 coreInvocations,
uint256 coreMaxInvocations
) = coreContractInvocationData({
projectId: projectId,
coreContract: coreContract
});
uint256 localMaxInvocations = maxInvocationsProjectConfig
.maxInvocations;
if (localMaxInvocations > coreMaxInvocations) {
// if local max invocations are greater than core max invocations, make
// them equal since that is the least restrictive logical state
// @dev this is only possible if the core contract's max invocations
// have been reduced since the minter's max invocations were last
// updated
// set local max invocations to core contract's max invocations
maxInvocationsProjectConfig.maxInvocations = uint24(
coreMaxInvocations
);
// update the minter's `maxHasBeenInvoked` state
maxInvocationsProjectConfig
.maxHasBeenInvoked = (coreMaxInvocations ==
coreInvocations);
emit ProjectMaxInvocationsLimitUpdated({
projectId: projectId,
coreContract: coreContract,
maxInvocations: coreMaxInvocations
});
} else if (coreInvocations >= localMaxInvocations) {
// core invocations are greater than this minter's max
// invocations, indicating that minting must have occurred on
// another minter. update the minter's `maxHasBeenInvoked` to
// true to prevent any false negatives on
// `getMaxHasBeenInvoked'
maxInvocationsProjectConfig.maxHasBeenInvoked = true;
// @dev do not emit event, because we did not change the value
// of minter-local max invocations
}
}
}
/**
* @notice Loads the MaxInvocationsProjectConfig for a given project and core
* contract.
* @param projectId Project Id to get config for
* @param coreContract Core contract address to get config for
*/
function getMaxInvocationsProjectConfig(
uint256 projectId,
address coreContract
) internal view returns (MaxInvocationsProjectConfig storage) {
return s().maxInvocationsProjectConfigs[coreContract][projectId];
}
/**
* @notice Return the storage struct for reading and writing. This library
* uses a diamond storage pattern when managing storage.
* @return storageStruct The MaxInvocationsLibStorage struct.
*/
function s()
internal
pure
returns (MaxInvocationsLibStorage storage storageStruct)
{
bytes32 position = MAX_INVOCATIONS_LIB_STORAGE_POSITION;
assembly ("memory-safe") {
storageStruct.slot := position
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {GenericMinterEventsLib} from "./GenericMinterEventsLib.sol";
import {MerkleProof} from "@openzeppelin-4.7/contracts/utils/cryptography/MerkleProof.sol";
/**
* @title Art Blocks Merkle Library
* @notice This library is designed to manage and verify merkle based gating for Art Blocks projects.
* It provides functionalities such as updating the merkle root of project, verifying an address against a proof,
* and setting the maximum number of invocations per address for a project.
* @author Art Blocks Inc.
*/
library MerkleLib {
using MerkleProof for bytes32[];
/// Events specific to this library ///
/**
* @notice Notifies of the contract's default maximum mints allowed per
* user for a given project, on this minter. This value can be overridden
* by the artist of any project at any time.
* @param defaultMaxInvocationsPerAddress The default maximum mints allowed
*/
event DefaultMaxInvocationsPerAddress(
uint256 defaultMaxInvocationsPerAddress
);
/**
* @notice Notifies of the contracts' current delegation registry address.
* @param delegationRegistry The address of the delegation registry
*/
event DelegationRegistryUpdated(address delegationRegistry);
// position of Merkle Lib storage, using a diamond storage pattern for this
// library
bytes32 constant MERKLE_LIB_STORAGE_POSITION =
keccak256("merklelib.storage");
/// @notice Default maximum invocations per address
uint256 internal constant DEFAULT_MAX_INVOCATIONS_PER_ADDRESS = 1;
bytes32 internal constant CONFIG_MERKLE_ROOT = "merkleRoot";
bytes32 internal constant CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE =
"useMaxMintsPerAddrOverride"; // shortened to fit in 32 bytes
bytes32 internal constant CONFIG_MAX_INVOCATIONS_OVERRIDE =
"maxMintsPerAddrOverride"; // shortened to match format of previous key
struct MerkleProjectConfig {
// If true, the maxInvocationsPerAddressOverride will be used.
// If false, the default max invocations per address will be used.
bool useMaxInvocationsPerAddressOverride;
// Maximum invocations allowed per address.
// This will be used if useMaxInvocationsPerAddressOverride is true.
// A value of 0 means no limit.
// @dev Safe to use uint24 because maxInvocationsPerAddressOverride <= 1_000_000
// and 1_000_000 << max uint24
uint24 maxInvocationsPerAddressOverride;
// The root of the Merkle tree for this project.
bytes32 merkleRoot;
// The number of current invocations for this project from a given user address.
mapping(address user => uint256 mintInvocations) userMintInvocations;
}
// Diamond storage pattern is used in this library
struct MerkleLibStorage {
mapping(address coreContract => mapping(uint256 projectId => MerkleProjectConfig)) merkleProjectConfigs;
}
/**
* @notice Sets the maximum number of invocations per address for a project.
* @param projectId The ID of the project to set the maximum invocations for.
* @param coreContract The address of the core contract.
* @param maxInvocationsPerAddress The maximum number of invocations per address.
*/
function setProjectInvocationsPerAddress(
uint256 projectId,
address coreContract,
uint24 maxInvocationsPerAddress
) internal {
MerkleProjectConfig
storage merkleProjectConfig = getMerkleProjectConfig({
projectId: projectId,
coreContract: coreContract
});
merkleProjectConfig.useMaxInvocationsPerAddressOverride = true;
merkleProjectConfig
.maxInvocationsPerAddressOverride = maxInvocationsPerAddress;
emit GenericMinterEventsLib.ConfigValueSet({
projectId: projectId,
coreContract: coreContract,
key: CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE,
value: true
});
emit GenericMinterEventsLib.ConfigValueSet({
projectId: projectId,
coreContract: coreContract,
key: CONFIG_MAX_INVOCATIONS_OVERRIDE,
value: uint256(maxInvocationsPerAddress)
});
}
/**
* @notice Updates the Merkle root of a project.
* @param projectId The ID of the project to update.
* @param coreContract The address of the core contract.
* @param root The new Merkle root.
*/
function updateMerkleRoot(
uint256 projectId,
address coreContract,
bytes32 root
) internal {
require(root != bytes32(0), "Root must be provided");
MerkleProjectConfig
storage merkleProjectConfig = getMerkleProjectConfig({
projectId: projectId,
coreContract: coreContract
});
merkleProjectConfig.merkleRoot = root;
emit GenericMinterEventsLib.ConfigValueSet({
projectId: projectId,
coreContract: coreContract,
key: CONFIG_MERKLE_ROOT,
value: root
});
}
/**
* @notice Checks that a given proof is valid for the vault address, and
* also checks that the vault address has not exceeded the maximum number
* of invocations per address for the project.
* @dev Reverts if the proof is invalid or if the vault address has
* exceeded the maximum number of invocations per address for the project.
* @param projectId project id to check
* @param coreContract core contract address to check
* @param proof Merkle proof to check
* @param vault address to check proof against
*/
function preMintChecks(
uint256 projectId,
address coreContract,
bytes32[] calldata proof,
address vault
) internal view {
MerkleProjectConfig
storage merkleProjectConfig = getMerkleProjectConfig({
projectId: projectId,
coreContract: coreContract
});
// require valid Merkle proof
require(
_verifyAddress({
proofRoot: merkleProjectConfig.merkleRoot,
proof: proof,
address_: vault
}),
"Invalid Merkle proof"
);
// limit mints per address by project
uint256 maxProjectInvocationsPerAddress = projectMaxInvocationsPerAddress(
merkleProjectConfig
);
// note that mint limits index off of the `vault` (when applicable)
require(
merkleProjectConfig.userMintInvocations[vault] <
maxProjectInvocationsPerAddress ||
maxProjectInvocationsPerAddress == 0,
"Max invocations reached"
);
}
/**
* @notice Updates the number of invocations for the `vault` address on the
* given project.
* @param projectId Project Id to mint on
* @param coreContract Core contract address to mint on
* @param vault Address being used to mint (the allowlisted address)
*/
function mintEffects(
uint256 projectId,
address coreContract,
address vault
) internal {
MerkleProjectConfig
storage merkleProjectConfig = getMerkleProjectConfig({
projectId: projectId,
coreContract: coreContract
});
// increment mint invocations for vault address
unchecked {
// this will never overflow since user's invocations on a project
// are limited by the project's max invocations
merkleProjectConfig.userMintInvocations[vault]++;
}
}
/**
* @notice Hashes an address.
* @param address_ The address to hash.
* @return The hash of the address.
*/
function hashAddress(address address_) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(address_));
}
/**
* @notice Processes a proof for an address.
* @param proof The proof to process.
* @param address_ The address to process the proof for.
* @return The resulting hash from processing the proof.
*/
function processProofForAddress(
bytes32[] calldata proof,
address address_
) internal pure returns (bytes32) {
return proof.processProofCalldata(hashAddress(address_));
}
/**
* @notice Returns the maximum number of invocations per address for a project.
* @param projectConfig The merkle project config to check.
* @return The maximum number of invocations per address.
*/
function projectMaxInvocationsPerAddress(
MerkleProjectConfig storage projectConfig
) internal view returns (uint256) {
if (projectConfig.useMaxInvocationsPerAddressOverride) {
return uint256(projectConfig.maxInvocationsPerAddressOverride);
} else {
return DEFAULT_MAX_INVOCATIONS_PER_ADDRESS;
}
}
/**
* @notice Returns the maximum number of invocations per address for a project.
* @param projectId Project Id to get config for
* @param coreContract Core contract address to get config for
* @return The maximum number of invocations per address.
*/
function projectMaxInvocationsPerAddress(
uint256 projectId,
address coreContract
) internal view returns (uint256) {
MerkleProjectConfig storage projectConfig = getMerkleProjectConfig({
projectId: projectId,
coreContract: coreContract
});
return projectMaxInvocationsPerAddress(projectConfig);
}
/**
* @notice Returns the number of invocations for a given address on a given
* project.
* @param projectId Project Id to query
* @param coreContract Core contract address to query
* @param purchaser Address to query
*/
function projectUserMintInvocations(
uint256 projectId,
address coreContract,
address purchaser
) internal view returns (uint256) {
MerkleProjectConfig storage projectConfig = getMerkleProjectConfig({
projectId: projectId,
coreContract: coreContract
});
return projectConfig.userMintInvocations[purchaser];
}
/**
* @notice Returns remaining invocations for a given address.
* If `projectLimitsMintInvocationsPerAddress` is false, individual
* addresses are only limited by the project's maximum invocations, and a
* dummy value of zero is returned for `mintInvocationsRemaining`.
* If `projectLimitsMintInvocationsPerAddress` is true, the quantity of
* remaining mint invocations for address `address` is returned as
* `mintInvocationsRemaining`.
* Note that mint invocations per address can be changed at any time by the
* artist of a project.
* Also note that all mint invocations are limited by a project's maximum
* invocations as defined on the core contract. This function may return
* a value greater than the project's remaining invocations.
* @param projectId Project Id to get remaining invocations on
* @param coreContract Core contract address of project
* @param address_ Address to get remaining invocations for
* @return projectLimitsMintInvocationsPerAddress If true, the project
* limits mint invocations per address. If false, the project does not
* limit mint invocations per address.
* @return mintInvocationsRemaining The number of remaining mint invocations
* for address `address_`. If `projectLimitsMintInvocationsPerAddress` is
* false, this value is always dummy zero.
*/
function projectRemainingInvocationsForAddress(
uint256 projectId,
address coreContract,
address address_
)
internal
view
returns (
bool projectLimitsMintInvocationsPerAddress,
uint256 mintInvocationsRemaining
)
{
MerkleProjectConfig storage projectConfig = getMerkleProjectConfig({
projectId: projectId,
coreContract: coreContract
});
uint256 maxInvocationsPerAddress = projectMaxInvocationsPerAddress(
projectConfig
);
if (maxInvocationsPerAddress != 0) {
projectLimitsMintInvocationsPerAddress = true;
uint256 userMintInvocations = projectConfig.userMintInvocations[
address_
];
// if user has not reached max invocations per address, return
// remaining invocations
if (maxInvocationsPerAddress > userMintInvocations) {
unchecked {
// will never underflow due to the check above
mintInvocationsRemaining =
maxInvocationsPerAddress -
userMintInvocations;
}
}
// else user has reached their maximum invocations, so leave
// `mintInvocationsRemaining` at solidity initial value of zero
}
// else maxInvocationsPerAddress is zero, then the project does not
// limit mint invocations per address, so do nothing. Leave
// `projectLimitsMintInvocationsPerAddress` at solidity initial
// value of false. Also leave `mintInvocationsRemaining` at
// solidity initial value of zero, as indicated in this function's
// documentation.
}
/**
* Loads the MerkleProjectConfig for a given project and core contract.
* @param projectId Project Id to get config for
* @param coreContract Core contract address to get config for
*/
function getMerkleProjectConfig(
uint256 projectId,
address coreContract
) internal view returns (MerkleProjectConfig storage) {
return s().merkleProjectConfigs[coreContract][projectId];
}
/**
* @notice Return the storage struct for reading and writing. This library
* uses a diamond storage pattern when managing storage.
* @return storageStruct The MerkleLibStorage struct.
*/
function s()
internal
pure
returns (MerkleLibStorage storage storageStruct)
{
bytes32 position = MERKLE_LIB_STORAGE_POSITION;
assembly ("memory-safe") {
storageStruct.slot := position
}
}
/**
* @notice Verifies an address against a proof.
* @param proofRoot The root of the proof to verify agaisnst.
* @param proof The proof to verify.
* @param address_ The address to verify.
* @return True if the address is verified, false otherwise.
*/
function _verifyAddress(
bytes32 proofRoot,
bytes32[] calldata proof,
address address_
) private pure returns (bool) {
return
proof.verifyCalldata({
root: proofRoot,
leaf: hashAddress(address_)
});
}
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
/**
* @title Art Blocks Set Price Minter Library
* @notice This library is designed for the Art Blocks platform. It provides a
* struct and functions that falicitate the configuring of projects that use a
* fixed-price minting model.
* @author Art Blocks Inc.
*/
library SetPriceLib {
/**
* @notice Price per token updated for project `projectId` to
* `pricePerToken`.
* @param projectId Project Id price was updated for
* @param coreContract Core contract address price was updated for
* @param pricePerToken price per token, no decimals (e.g. in wei for ETH)
*/
event PricePerTokenUpdated(
uint256 indexed projectId,
address indexed coreContract,
uint256 indexed pricePerToken
);
/**
* @notice Price per token reset (unconfigured) for project `projectId`.
* @param projectId Project Id price was reset for
* @param coreContract Core contract address price was reset for
*/
event PricePerTokenReset(
uint256 indexed projectId,
address indexed coreContract
);
// position of Set Price Lib storage, using a diamond storage pattern
// for this library
bytes32 constant SET_PRICE_LIB_STORAGE_POSITION =
keccak256("setpricelib.storage");
// project-level variables
/**
* Struct used to store a project's currently configured price, and
* whether or not the price has been configured.
*/
struct SetPriceProjectConfig {
// @dev The price is stored with no accounting for decimals. e.g. in
// wei for ETH.
uint248 pricePerToken; // 0 if not configured
bool priceIsConfigured;
}
// Diamond storage pattern is used in this library
struct SetPriceLibStorage {
mapping(address coreContract => mapping(uint256 projectId => SetPriceProjectConfig)) setPriceProjectConfigs;
}
/**
* @notice Updates the minter's price per token to be `pricePerToken`.
* @dev Note that it is intentionally supported here that the configured
* price may be explicitly set to `0`.
* @param projectId Project Id to update price for
* @param coreContract Core contract address to update price for
* @param pricePerToken price per token, no decimals (e.g. in wei for ETH)
*/
function updatePricePerToken(
uint256 projectId,
address coreContract,
uint256 pricePerToken
) internal {
SetPriceProjectConfig
storage setPriceProjectConfig = getSetPriceProjectConfig({
projectId: projectId,
coreContract: coreContract
});
// update storage with new values
setPriceProjectConfig.pricePerToken = uint248(pricePerToken);
setPriceProjectConfig.priceIsConfigured = true;
emit PricePerTokenUpdated({
projectId: projectId,
coreContract: coreContract,
pricePerToken: pricePerToken
});
}
/**
* @notice Resets the minter's price per token to be unconfigured.
* @param projectId Project Id to reset price for
* @param coreContract Core contract address to reset the price for
*/
function resetPricePerToken(
uint256 projectId,
address coreContract
) internal {
// @dev all fields must be deleted, and none of them are a complex type
// @dev getSetPriceProjectConfig not used, as deletion of storage
// pointers is not supported
delete s().setPriceProjectConfigs[coreContract][projectId];
emit PricePerTokenReset({
projectId: projectId,
coreContract: coreContract
});
}
/**
* @notice Checks that the minter's price per token is configured, and
* returns the price per token.
* Reverts if the price is not configured.
* @param projectId Project Id to check and get price for
* @param coreContract Core contract address to check and get price for
* @return pricePerToken price per token, no decimals (e.g. in wei for ETH)
*/
function preMintChecksAndGetPrice(
uint256 projectId,
address coreContract
) internal view returns (uint256 pricePerToken) {
SetPriceProjectConfig
storage setPriceProjectConfig = getSetPriceProjectConfig({
projectId: projectId,
coreContract: coreContract
});
// require artist to have configured price of token on this minter
require(
setPriceProjectConfig.priceIsConfigured,
"Price not configured"
);
return setPriceProjectConfig.pricePerToken;
}
/**
* @notice Loads the SetPriceProjectConfig for a given project and core contract.
* @param projectId Project Id to get config for
* @param coreContract Core contract address to get config for
*/
function getSetPriceProjectConfig(
uint256 projectId,
address coreContract
) internal view returns (SetPriceProjectConfig storage) {
return s().setPriceProjectConfigs[coreContract][projectId];
}
/**
* @notice Return the storage struct for reading and writing. This library
* uses a diamond storage pattern when managing storage.
* @return storageStruct The SetPriceLibStorage struct.
*/
function s()
internal
pure
returns (SetPriceLibStorage storage storageStruct)
{
bytes32 position = SET_PRICE_LIB_STORAGE_POSITION;
assembly ("memory-safe") {
storageStruct.slot := position
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {IMinterBaseV0} from "../../../interfaces/v0.8.x/IMinterBaseV0.sol";
import {IGenArt721CoreContractV3_Base} from "../../../interfaces/v0.8.x/IGenArt721CoreContractV3_Base.sol";
import {IGenArt721CoreContractV3} from "../../../interfaces/v0.8.x/IGenArt721CoreContractV3.sol";
import {IGenArt721CoreContractV3_Engine} from "../../../interfaces/v0.8.x/IGenArt721CoreContractV3_Engine.sol";
import {IERC20} from "@openzeppelin-4.7/contracts/token/ERC20/IERC20.sol";
/**
* @title Art Blocks Split Funds Library
* @notice This library is designed for the Art Blocks platform. It splits
* Ether (ETH) and ERC20 token funds among stakeholders, such as sender
* (if refund is applicable), providers, artists, and artists' additional
* payees.
* @author Art Blocks Inc.
*/
library SplitFundsLib {
/**
* @notice Currency updated for project `projectId` to symbol
* `currencySymbol` and address `currencyAddress`.
* @param projectId Project ID currency was updated for
* @param coreContract Core contract address currency was updated for
* @param currencyAddress Currency address
* @param currencySymbol Currency symbol
*/
event ProjectCurrencyInfoUpdated(
uint256 indexed projectId,
address indexed coreContract,
address indexed currencyAddress,
string currencySymbol
);
// position of Split Funds Lib storage, using a diamond storage pattern
// for this library
bytes32 constant SPLIT_FUNDS_LIB_STORAGE_POSITION =
keccak256("splitfundslib.storage");
// contract-level variables
struct IsEngineCache {
bool isEngine;
bool isCached;
}
// project-level variables
struct SplitFundsProjectConfig {
address currencyAddress; // address(0) if ETH
string currencySymbol; // Assumed to be ETH if null
}
// Diamond storage pattern is used in this library
struct SplitFundsLibStorage {
mapping(address coreContract => mapping(uint256 projectId => SplitFundsProjectConfig)) splitFundsProjectConfigs;
mapping(address coreContract => IsEngineCache) isEngineCacheConfigs;
}
/**
* @notice splits ETH funds between sender (if refund), providers,
* artist, and artist's additional payee for a token purchased on
* project `projectId`.
* WARNING: This function uses msg.value and msg.sender to determine
* refund amounts, and therefore may not be applicable to all use cases
* (e.g. do not use with Dutch Auctions with on-chain settlement).
* @dev This function relies on msg.sender and msg.value, so it must be
* called directly from the contract that is receiving the payment.
* @dev possible DoS during splits is acknowledged, and mitigated by
* business practices, including end-to-end testing on mainnet, and
* admin-accepted artist payment addresses.
* @param projectId Project ID for which funds shall be split.
* @param pricePerTokenInWei Current price of token, in Wei.
* @param coreContract Address of the GenArt721CoreContract associated
* with the project.
*/
function splitFundsETHRefundSender(
uint256 projectId,
uint256 pricePerTokenInWei,
address coreContract
) internal {
if (msg.value > 0) {
// send refund to sender
uint256 refund = msg.value - pricePerTokenInWei;
if (refund > 0) {
(bool success_, ) = msg.sender.call{value: refund}("");
require(success_, "Refund failed");
}
// split revenues
splitRevenuesETHNoRefund({
projectId: projectId,
valueInWei: pricePerTokenInWei,
coreContract: coreContract
});
}
}
/**
* @notice Splits ETH revenues between providers, artist, and artist's
* additional payee for revenue generated by project `projectId`.
* This function does NOT refund msg.sender, and does NOT use msg.value
* when determining the value to be split.
* @dev possible DoS during splits is acknowledged, and mitigated by
* business practices, including end-to-end testing on mainnet, and
* admin-accepted artist payment addresses.
* @param projectId Project ID for which funds shall be split.
* @param valueInWei Value to be split, in Wei.
* @param coreContract Address of the GenArt721CoreContract
* associated with the project.
*/
function splitRevenuesETHNoRefund(
uint256 projectId,
uint256 valueInWei,
address coreContract
) internal {
if (valueInWei == 0) {
return; // return early
}
// split funds between platforms, artist, and artist's
// additional payee
bool isEngine_ = isEngine(coreContract);
uint256 renderProviderRevenue;
address payable renderProviderAddress;
uint256 platformProviderRevenue;
address payable platformProviderAddress;
uint256 artistRevenue;
address payable artistAddress;
uint256 additionalPayeePrimaryRevenue;
address payable additionalPayeePrimaryAddress;
if (isEngine_) {
// get engine splits
(
renderProviderRevenue,
renderProviderAddress,
platformProviderRevenue,
platformProviderAddress,
artistRevenue,
artistAddress,
additionalPayeePrimaryRevenue,
additionalPayeePrimaryAddress
) = IGenArt721CoreContractV3_Engine(coreContract)
.getPrimaryRevenueSplits({
_projectId: projectId,
_price: valueInWei
});
} else {
// get flagship splits
// @dev note that platformProviderAddress and
// platformProviderRevenue remain 0 for flagship
(
renderProviderRevenue, // artblocks revenue
renderProviderAddress, // artblocks address
artistRevenue,
artistAddress,
additionalPayeePrimaryRevenue,
additionalPayeePrimaryAddress
) = IGenArt721CoreContractV3(coreContract).getPrimaryRevenueSplits({
_projectId: projectId,
_price: valueInWei
});
}
// require total revenue split is 100%
// @dev note that platformProviderRevenue remains 0 for flagship
require(
renderProviderRevenue +
platformProviderRevenue +
artistRevenue +
additionalPayeePrimaryRevenue ==
valueInWei,
"Invalid revenue split totals"
);
// distribute revenues
// @dev note that platformProviderAddress and platformProviderRevenue
// remain 0 for flagship
_sendPaymentsETH({
platformProviderRevenue: platformProviderRevenue,
platformProviderAddress: platformProviderAddress,
renderProviderRevenue: renderProviderRevenue,
renderProviderAddress: renderProviderAddress,
artistRevenue: artistRevenue,
artistAddress: artistAddress,
additionalPayeePrimaryRevenue: additionalPayeePrimaryRevenue,
additionalPayeePrimaryAddress: additionalPayeePrimaryAddress
});
}
/**
* @notice Splits ERC20 funds between providers, artist, and artist's
* additional payee, for a token purchased on project `projectId`.
* The function performs checks to ensure that the ERC20 token is
* approved for transfer, and that a non-zero ERC20 token address is
* configured.
* @dev This function relies on msg.sender, so it must be
* called directly from the contract that is receiving the payment.
* @dev possible DoS during splits is acknowledged, and mitigated by
* business practices, including end-to-end testing on mainnet, and
* admin-accepted artist payment addresses.
* @param projectId Project ID for which funds shall be split.
* @param pricePerToken Current price of token, in base units. For example,
* if the ERC20 token has 6 decimals, an input value of `1_000_000` would
* represent a price of `1.000000` tokens.
* @param coreContract Core contract address.
*/
function splitFundsERC20(
uint256 projectId,
uint256 pricePerToken,
address coreContract
) internal {
if (pricePerToken == 0) {
return; // nothing to split, return early
}
IERC20 projectCurrency;
// block scope to avoid stack too deep error
{
SplitFundsProjectConfig
storage splitFundsProjectConfig = getSplitFundsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
address currencyAddress = splitFundsProjectConfig.currencyAddress;
require(
currencyAddress != address(0),
"ERC20: payment not configured"
);
// ERC20 token is used for payment
validateERC20Approvals({
msgSender: msg.sender,
currencyAddress: currencyAddress,
pricePerToken: pricePerToken
});
projectCurrency = IERC20(currencyAddress);
}
// split remaining funds between foundation, artist, and artist's
bool isEngine_ = isEngine(coreContract);
uint256 renderProviderRevenue;
address payable renderProviderAddress;
uint256 platformProviderRevenue;
address payable platformProviderAddress;
uint256 artistRevenue;
address payable artistAddress;
uint256 additionalPayeePrimaryRevenue;
address payable additionalPayeePrimaryAddress;
if (isEngine_) {
// get engine splits
(
renderProviderRevenue,
renderProviderAddress,
platformProviderRevenue,
platformProviderAddress,
artistRevenue,
artistAddress,
additionalPayeePrimaryRevenue,
additionalPayeePrimaryAddress
) = IGenArt721CoreContractV3_Engine(coreContract)
.getPrimaryRevenueSplits({
_projectId: projectId,
_price: pricePerToken
});
} else {
// get flagship splits
// @dev note that platformProviderAddress and
// platformProviderRevenue remain 0 for flagship
(
renderProviderRevenue, // artblocks revenue
renderProviderAddress, // artblocks address
artistRevenue,
artistAddress,
additionalPayeePrimaryRevenue,
additionalPayeePrimaryAddress
) = IGenArt721CoreContractV3(coreContract).getPrimaryRevenueSplits({
_projectId: projectId,
_price: pricePerToken
});
}
// require total revenue split is 100%
// @dev note that platformProviderRevenue remains 0 for flagship
require(
renderProviderRevenue +
platformProviderRevenue +
artistRevenue +
additionalPayeePrimaryRevenue ==
pricePerToken,
"Invalid revenue split totals"
);
// distribute revenues
// @dev note that platformProviderAddress and platformProviderRevenue
// remain 0 for flagship
_sendPaymentsERC20({
projectCurrency: projectCurrency,
platformProviderRevenue: platformProviderRevenue,
platformProviderAddress: platformProviderAddress,
renderProviderRevenue: renderProviderRevenue,
renderProviderAddress: renderProviderAddress,
artistRevenue: artistRevenue,
artistAddress: artistAddress,
additionalPayeePrimaryRevenue: additionalPayeePrimaryRevenue,
additionalPayeePrimaryAddress: additionalPayeePrimaryAddress
});
}
/**
* @notice Updates payment currency of the referenced
* SplitFundsProjectConfig to be `currencySymbol` at address
* `currencyAddress`.
* Only supports setting currency info of ERC20 tokens.
* Returns bool that is true if the price should be reset after this
* update. Price is recommended to be reset if the currency address was
* previously configured, but is now being updated to a different currency
* address. This is to protect accidental price reductions when changing
* currency if an artist is changing currencies in an unpaused state.
* @dev artist-defined currency symbol is used instead of any on-chain
* currency symbol.
* @param projectId Project ID to update.
* @param coreContract Core contract address.
* @param currencySymbol Currency symbol.
* @param currencyAddress Currency address.
* @return recommendPriceReset True if the price should be reset after this
* update.
*/
function updateProjectCurrencyInfoERC20(
uint256 projectId,
address coreContract,
string memory currencySymbol,
address currencyAddress
) internal returns (bool recommendPriceReset) {
// CHECKS
require(currencyAddress != address(0), "null address, only ERC20");
require(bytes(currencySymbol).length > 0, "only non-null symbol");
// EFFECTS
SplitFundsProjectConfig
storage splitFundsProjectConfig = getSplitFundsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
// recommend price reset if currency address was previously configured
recommendPriceReset = (splitFundsProjectConfig.currencyAddress !=
address(0));
splitFundsProjectConfig.currencySymbol = currencySymbol;
splitFundsProjectConfig.currencyAddress = currencyAddress;
emit ProjectCurrencyInfoUpdated({
projectId: projectId,
coreContract: coreContract,
currencyAddress: currencyAddress,
currencySymbol: currencySymbol
});
}
/**
* @notice Force sends `amount` (in wei) ETH to `to`, with a gas stipend
* equal to `minterRefundGasLimit`.
* If sending via the normal procedure fails, force sends the ETH by
* creating a temporary contract which uses `SELFDESTRUCT` to force send
* the ETH.
* Reverts if the current contract has insufficient balance.
* @param to The address to send ETH to.
* @param amount The amount of ETH to send.
* @param minterRefundGasLimit The gas limit to use when sending ETH, prior
* to fallback.
* @dev This function is adapted from the `forceSafeTransferETH` function
* in the `https://github.com/Vectorized/solady` repository, with
* modifications to not check if the current contract has sufficient
* balance. Therefore, the contract should be checked for sufficient
* balance before calling this function in the minter itself, if
* applicable.
*/
function forceSafeTransferETH(
address to,
uint256 amount,
uint256 minterRefundGasLimit
) internal {
// Manually inlined because the compiler doesn't inline functions with
// branches.
/// @solidity memory-safe-assembly
assembly {
// @dev intentionally do not check if this contract has sufficient
// balance, because that is not intended to be a valid state.
// Transfer the ETH and check if it succeeded or not.
if iszero(call(minterRefundGasLimit, to, amount, 0, 0, 0, 0)) {
// if the transfer failed, we create a temporary contract with
// initialization code that uses `SELFDESTRUCT` to force send
// the ETH.
// note: Compatible with `SENDALL`:
// https://eips.ethereum.org/EIPS/eip-4758
//---------------------------------------------------------------------------------------------------------------//
// Opcode | Opcode + Arguments | Description | Stack View //
//---------------------------------------------------------------------------------------------------------------//
// Contract creation code that uses `SELFDESTRUCT` to force send ETH to a specified address. //
// Creation code summary: 0x73<20-byte toAddress>0xff //
//---------------------------------------------------------------------------------------------------------------//
// 0x73 | 0x73_toAddress | PUSH20 toAddress | toAddress //
// 0xFF | 0xFF | SELFDESTRUCT | //
//---------------------------------------------------------------------------------------------------------------//
// Store the address in scratch space, starting at 0x00, which begins the 20-byte address at 32-20=12 in memory
// @dev use scratch space because we have enough space for simple creation code (less than 0x40 bytes)
mstore(0x00, to)
// store opcode PUSH20 immediately before the address, starting at 0x0b (11) in memory
mstore8(0x0b, 0x73)
// store opcode SELFDESTRUCT immediately after the address, starting at 0x20 (32) in memory
mstore8(0x20, 0xff)
// this will always succeed because the contract creation code is
// valid, and the address is valid because it is a 20-byte value
if iszero(create(amount, 0x0b, 0x16)) {
// @dev For better gas estimation.
if iszero(gt(gas(), 1000000)) {
revert(0, 0)
}
}
}
}
}
/**
* @notice Returns whether or not the provided address `coreContract`
* is an Art Blocks Engine core contract. Caches the result for future access.
* @param coreContract Address of the core contract to check.
*/
function isEngine(address coreContract) internal returns (bool) {
IsEngineCache storage isEngineCache = getIsEngineCacheConfig(
coreContract
);
// check cache, return early if cached
if (isEngineCache.isCached) {
return isEngineCache.isEngine;
}
// populate cache and return result
bool isEngine_ = getV3CoreIsEngineView(coreContract);
isEngineCache.isCached = true;
isEngineCache.isEngine = isEngine_;
return isEngine_;
}
/**
* @notice Returns whether a V3 core contract is an Art Blocks Engine
* contract or not. Return value of false indicates that the core is a
* flagship contract. This function does not update the cache state for the
* given V3 core contract.
* @dev this function reverts if a core contract does not return the
* expected number of return values from getPrimaryRevenueSplits() for
* either a flagship or engine core contract.
* @dev this function uses the length of the return data (in bytes) to
* determine whether the core is an engine or not.
* @param coreContract The address of the deployed core contract.
*/
function getV3CoreIsEngineView(
address coreContract
) internal view returns (bool) {
// call getPrimaryRevenueSplits() on core contract
bytes memory payload = abi.encodeWithSignature(
"getPrimaryRevenueSplits(uint256,uint256)",
0,
0
);
(bool success, bytes memory returnData) = coreContract.staticcall(
payload
);
require(success, "getPrimaryRevenueSplits() call failed");
// determine whether core is engine or not, based on return data length
uint256 returnDataLength = returnData.length;
if (returnDataLength == 6 * 32) {
// 6 32-byte words returned if flagship (not engine)
// @dev 6 32-byte words are expected because the non-engine core
// contracts return a payout address and uint256 payment value for
// the artist, and artist's additional payee, and Art Blocks.
// also note that per Solidity ABI encoding, the address return
// values are padded to 32 bytes.
return false;
} else if (returnDataLength == 8 * 32) {
// 8 32-byte words returned if engine
// @dev 8 32-byte words are expected because the engine core
// contracts return a payout address and uint256 payment value for
// the artist, artist's additional payee, render provider
// typically Art Blocks, and platform provider (partner).
// also note that per Solidity ABI encoding, the address return
// values are padded to 32 bytes.
return true;
}
// unexpected return value length
revert("Unexpected revenue split bytes");
}
/**
* @notice Gets the currency address and symbol for the referenced
* SplitFundsProjectConfig.
* Only supports ERC20 tokens - returns currencySymbol of `UNCONFIG` if
* `currencyAddress` is zero.
* @param projectId Project ID to get config for
* @param coreContract Core contract address to get config for
* @return currencyAddress
* @return currencySymbol
*/
function getCurrencyInfoERC20(
uint256 projectId,
address coreContract
)
internal
view
returns (address currencyAddress, string memory currencySymbol)
{
SplitFundsProjectConfig
storage splitFundsProjectConfig = getSplitFundsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
currencyAddress = splitFundsProjectConfig.currencyAddress;
// default to "UNCONFIG" if project currency address is initial value
currencySymbol = currencyAddress == address(0)
? "UNCONFIG"
: splitFundsProjectConfig.currencySymbol;
}
/**
* @notice Gets the balance of `currencyAddress` ERC20 tokens for `walletAddress`.
* @param currencyAddress ERC20 token address.
* @param walletAddress wallet address.
* @return balance
*/
function getERC20Balance(
address currencyAddress,
address walletAddress
) internal view returns (uint256) {
return IERC20(currencyAddress).balanceOf(walletAddress);
}
/**
* @notice Gets the allowance of `spenderAddress` to spend `walletAddress`'s
* `currencyAddress` ERC20 tokens.
* @param currencyAddress ERC20 token address.
* @param walletAddress wallet address.
* @param spenderAddress spender address.
* @return allowance
*/
function getERC20Allowance(
address currencyAddress,
address walletAddress,
address spenderAddress
) internal view returns (uint256 allowance) {
allowance = IERC20(currencyAddress).allowance({
owner: walletAddress,
spender: spenderAddress
});
return allowance;
}
/**
* @notice Function validates that `msgSender` has approved the contract to spend at least
* `pricePerToken` of `currencyAddress` ERC20 tokens, and that
* `msgSender` has a balance of at least `pricePerToken` of
* `currencyAddress` ERC20 tokens.
* Reverts if insufficient allowance or balance.
* @param msgSender Address of the message sender to validate.
* @param currencyAddress Address of the ERC20 token to validate.
* @param pricePerToken Price of token, in base units. For example,
* if the ERC20 token has 6 decimals, an input value of `1_000_000` would
* represent a price of `1.000000` tokens.
*/
function validateERC20Approvals(
address msgSender,
address currencyAddress,
uint256 pricePerToken
) private view {
require(
IERC20(currencyAddress).allowance({
owner: msgSender,
spender: address(this)
}) >= pricePerToken,
"Insufficient ERC20 allowance"
);
require(
IERC20(currencyAddress).balanceOf(msgSender) >= pricePerToken,
"Insufficient ERC20 balance"
);
}
/**
* @notice Sends ETH revenues between providers, artist, and artist's
* additional payee. Reverts if any payment fails.
* @dev This function pays priviliged addresses. DoS is acknowledged, and
* mitigated by business practices, including end-to-end testing on
* mainnet, and admin-accepted artist payment addresses.
* @param platformProviderRevenue Platform Provider revenue.
* @param platformProviderAddress Platform Provider address.
* @param renderProviderRevenue Render Provider revenue.
* @param renderProviderAddress Render Provider address.
* @param artistRevenue Artist revenue.
* @param artistAddress Artist address.
* @param additionalPayeePrimaryRevenue Additional Payee revenue.
* @param additionalPayeePrimaryAddress Additional Payee address.
*/
function _sendPaymentsETH(
uint256 platformProviderRevenue,
address payable platformProviderAddress,
uint256 renderProviderRevenue,
address payable renderProviderAddress,
uint256 artistRevenue,
address payable artistAddress,
uint256 additionalPayeePrimaryRevenue,
address payable additionalPayeePrimaryAddress
) private {
// Platform Provider payment (only possible if engine)
if (platformProviderRevenue > 0) {
(bool success, ) = platformProviderAddress.call{
value: platformProviderRevenue
}("");
require(success, "Platform Provider payment failed");
}
// Render Provider / Art Blocks payment
if (renderProviderRevenue > 0) {
(bool success, ) = renderProviderAddress.call{
value: renderProviderRevenue
}("");
require(success, "Render Provider payment failed");
}
// artist payment
if (artistRevenue > 0) {
(bool success, ) = artistAddress.call{value: artistRevenue}("");
require(success, "Artist payment failed");
}
// additional payee payment
if (additionalPayeePrimaryRevenue > 0) {
(bool success, ) = additionalPayeePrimaryAddress.call{
value: additionalPayeePrimaryRevenue
}("");
require(success, "Additional Payee payment failed");
}
}
/**
* @notice Sends ERC20 revenues between providers, artist, and artist's
* additional payee. Reverts if any payment fails. All revenue values
* should use base units. For example, if the ERC20 token has 6 decimals,
* an input value of `1_000_000` would represent an amount of `1.000000`
* tokens.
* @dev This function relies on msg.sender, so it must be called from
* the contract that is receiving the payment.
* @param projectCurrency IERC20 payment token.
* @param platformProviderRevenue Platform Provider revenue.
* @param platformProviderAddress Platform Provider address.
* @param renderProviderRevenue Render Provider revenue.
* @param renderProviderAddress Render Provider address.
* @param artistRevenue Artist revenue.
* @param artistAddress Artist address.
* @param additionalPayeePrimaryRevenue Additional Payee revenue.
* @param additionalPayeePrimaryAddress Additional Payee address.
*/
function _sendPaymentsERC20(
IERC20 projectCurrency,
uint256 platformProviderRevenue,
address payable platformProviderAddress,
uint256 renderProviderRevenue,
address payable renderProviderAddress,
uint256 artistRevenue,
address payable artistAddress,
uint256 additionalPayeePrimaryRevenue,
address payable additionalPayeePrimaryAddress
) private {
// Platform Provider payment (only possible if engine)
if (platformProviderRevenue > 0) {
require(
projectCurrency.transferFrom({
from: msg.sender,
to: platformProviderAddress,
amount: platformProviderRevenue
}),
"Platform Provider payment failed"
);
}
// Art Blocks payment
if (renderProviderRevenue > 0) {
require(
projectCurrency.transferFrom({
from: msg.sender,
to: renderProviderAddress,
amount: renderProviderRevenue
}),
"Render Provider payment failed"
);
}
// artist payment
if (artistRevenue > 0) {
require(
projectCurrency.transferFrom({
from: msg.sender,
to: artistAddress,
amount: artistRevenue
}),
"Artist payment failed"
);
}
// additional payee payment
if (additionalPayeePrimaryRevenue > 0) {
// @dev some ERC20 may not revert on transfer failure, so we
// check the return value
require(
projectCurrency.transferFrom({
from: msg.sender,
to: additionalPayeePrimaryAddress,
amount: additionalPayeePrimaryRevenue
}),
"Additional Payee payment failed"
);
}
}
/**
* @notice Loads the SplitFundsProjectConfig for a given project and core
* contract.
* @param projectId Project Id to get config for
* @param coreContract Core contract address to get config for
*/
function getSplitFundsProjectConfig(
uint256 projectId,
address coreContract
) internal view returns (SplitFundsProjectConfig storage) {
return s().splitFundsProjectConfigs[coreContract][projectId];
}
/**
* @notice Loads the IsEngineCache for a given core contract.
* @param coreContract Core contract address to get config for
*/
function getIsEngineCacheConfig(
address coreContract
) internal view returns (IsEngineCache storage) {
return s().isEngineCacheConfigs[coreContract];
}
/**
* @notice Return the storage struct for reading and writing. This library
* uses a diamond storage pattern when managing storage.
* @return storageStruct The SetPriceLibStorage struct.
*/
function s()
internal
pure
returns (SplitFundsLibStorage storage storageStruct)
{
bytes32 position = SPLIT_FUNDS_LIB_STORAGE_POSITION;
assembly ("memory-safe") {
storageStruct.slot := position
}
}
}{
"optimizer": {
"enabled": true,
"runs": 25
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"minterFilter","type":"address"},{"internalType":"address","name":"delegationRegistryAddress_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"delegationRegistryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"getPriceInfo","outputs":[{"internalType":"bool","name":"isConfigured","type":"bool"},{"internalType":"uint256","name":"tokenPriceInWei","type":"uint256"},{"internalType":"string","name":"currencySymbol","type":"string"},{"internalType":"address","name":"currencyAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"hashAddress","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"coreContract","type":"address"}],"name":"isEngineView","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint24","name":"maxInvocations","type":"uint24"}],"name":"manuallyLimitProjectMaxInvocations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"maxInvocationsProjectConfig","outputs":[{"components":[{"internalType":"bool","name":"maxHasBeenInvoked","type":"bool"},{"internalType":"uint24","name":"maxInvocations","type":"uint24"}],"internalType":"struct MaxInvocationsLib.MaxInvocationsProjectConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"merkleProjectConfig","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint24","name":"","type":"uint24"},{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minterFilterAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minterType","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minterVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"address","name":"address_","type":"address"}],"name":"processProofForAddress","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"projectMaxHasBeenInvoked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"projectMaxInvocations","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"projectMaxInvocationsPerAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"address","name":"address_","type":"address"}],"name":"projectRemainingInvocationsForAddress","outputs":[{"internalType":"bool","name":"projectLimitsMintInvocationsPerAddress","type":"bool"},{"internalType":"uint256","name":"mintInvocationsRemaining","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"address","name":"purchaser","type":"address"}],"name":"projectUserMintInvocations","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"purchase","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"purchaseTo","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"address","name":"vault","type":"address"}],"name":"purchaseTo","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"setPriceProjectConfig","outputs":[{"components":[{"internalType":"uint248","name":"pricePerToken","type":"uint248"},{"internalType":"bool","name":"priceIsConfigured","type":"bool"}],"internalType":"struct SetPriceLib.SetPriceProjectConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint24","name":"maxInvocationsPerAddress","type":"uint24"}],"name":"setProjectInvocationsPerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"}],"name":"syncProjectMaxInvocationsToCore","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"name":"updateMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"address","name":"coreContract","type":"address"},{"internalType":"uint248","name":"pricePerTokenInWei","type":"uint248"}],"name":"updatePricePerTokenInWei","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101006040523480156200001257600080fd5b50604051620023ab380380620023ab8339810160408190526200003591620000e5565b60016000556001600160a01b03828116608081905260a052811660c081905260e08190526040519081527f836360d1b094a7de3c3eab3d1185f3a5939467c23d4a12709dbdbf8c8d7e2f3b9060200160405180910390a1604051600181527fb004e34bc1718c504fde68324f2eba80b0329056f240eaaa6803c286c4b9e7ed9060200160405180910390a150506200011d565b80516001600160a01b0381168114620000e057600080fd5b919050565b60008060408385031215620000f957600080fd5b6200010483620000c8565b91506200011460208401620000c8565b90509250929050565b60805160a05160c05160e0516122546200015760003960006106da0152600061039d015260006107e70152600061049801526122546000f3fe60806040526004361061011b5760003560e01c806301000da7146101205780631467b1f7146101555780631ec2e523146101765780633aa5fe591461019657806340e5fa3b146101b65780634869f3df146101ed57806350a9e8421461020d5780635cf0a4111461022d5780636ece43dc146102405780637e947f24146102815780638b4eb61d146102a35780639a94488a146102c35780639b24c3c014610305578063b000e3341461034b578063b466dded1461036b578063bf5bf5f81461038b578063cce7c909146103d7578063d3ddabe6146103f7578063d9bffbce14610436578063d9eb16db14610456578063dd85582f14610486578063e9d1e8ac146104ba578063eb7eb95a146104fc578063ef2c8cc71461050f575b600080fd5b34801561012c57600080fd5b5061014061013b366004611ba3565b61052f565b60405190151581526020015b60405180910390f35b610168610163366004611c0b565b610567565b60405190815260200161014c565b34801561018257600080fd5b50610168610191366004611c7d565b610582565b3480156101a257600080fd5b506101686101b1366004611ba3565b610597565b3480156101c257600080fd5b506101d66101d1366004611cd3565b6105a8565b60408051921515835260208301919091520161014c565b3480156101f957600080fd5b50610168610208366004611d0a565b6105c2565b34801561021957600080fd5b50610168610228366004611cd3565b6105ce565b61016861023b366004611d3a565b6105db565b34801561024c57600080fd5b5061026061025b366004611d0a565b61087a565b60408051931515845262ffffff90921660208401529082015260600161014c565b34801561028d57600080fd5b506102a161029c366004611dbe565b6108ad565b005b3480156102af57600080fd5b506101686102be366004611d0a565b6108ea565b3480156102cf57600080fd5b506102e36102de366004611d0a565b6108f6565b6040805182511515815260209283015162ffffff16928101929092520161014c565b34801561031157600080fd5b50610325610320366004611d0a565b61093e565b6040805182516001600160f81b031681526020928301511515928101929092520161014c565b34801561035757600080fd5b506102a1610366366004611e01565b61098c565b34801561037757600080fd5b506102a1610386366004611e40565b6109a2565b34801561039757600080fd5b506103bf7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161014c565b3480156103e357600080fd5b506101406103f2366004611d0a565b6109b8565b34801561040357600080fd5b5061042960405180604001604052806006815260200165076352e302e360d41b81525081565b60405161014c9190611ec8565b34801561044257600080fd5b506102a1610451366004611d0a565b6109c4565b34801561046257600080fd5b50610476610471366004611d0a565b6109dd565b60405161014c9493929190611edb565b34801561049257600080fd5b506103bf7f000000000000000000000000000000000000000000000000000000000000000081565b3480156104c657600080fd5b50610429604051806040016040528060168152602001754d696e74657253657450726963654d65726b6c65563560501b81525081565b61016861050a366004611f15565b610a2f565b34801561051b57600080fd5b506102a161052a366004611e01565b610a49565b60008061053b83610a5f565b8054909150610100900460ff1615610557575460ff1692915050565b61056083610a98565b9392505050565b6000610578868686868660006105db565b9695505050505050565b600061058f848484610c0e565b949350505050565b60006105a282610c25565b92915050565b6000806105b6858585610c5f565b91509150935093915050565b60006105608383610cbc565b600061058f848484610cdc565b60006002600054036106345760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b60026000556106438686610d0f565b600061064f8787610d40565b90508034101561069a5760405162461bcd60e51b815260206004820152601660248201527526b4b7103b30b63ab2903a379036b4b73a103932b89760511b604482015260640161062b565b336001600160a01b0384161561079b5760405163090c9a2d60e41b81523360048201526001600160a01b03858116602483015288811660448301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906390c9a2d090606401602060405180830381865afa158015610723573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107479190611f80565b9050806107965760405162461bcd60e51b815260206004820152601e60248201527f496e76616c69642064656c65676174652d7661756c742070616972696e670000604482015260640161062b565b849150505b6107a88888888885610db0565b6107b3888883610e6a565b6040516117cd60e21b81526001600160a01b038a81166004830152602482018a9052888116604483015282811660648301527f00000000000000000000000000000000000000000000000000000000000000001690615f34906084016020604051808303816000875af115801561082e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108529190611f9b565b925061085e8388610e9e565b610869888389610f07565b505060016000559695505050505050565b60008060008061088a8686610fbc565b805460019091015460ff82169861010090920462ffffff16975095509350505050565b6108b8838333611007565b6108cc8383836001600160f81b031661104c565b6108d683836110ab565b156108e5576108e583836110db565b505050565b60006105608383611176565b6040805180820190915260008082526020820152610914838361118e565b60408051808201909152905460ff811615158252610100900462ffffff1660208201529392505050565b604080518082019091526000808252602082015261095c83836111b6565b6040805180820190915290546001600160f81b0381168252600160f81b900460ff16151560208201529392505050565b610997838333611007565b6108e58383836111dd565b6109ad838333611007565b6108e58383836112b3565b60006105608383611368565b6109cf828233611007565b6109d982826110db565b5050565b60008060606000806109ef87876111b6565b5460408051808201909152600381526208aa8960eb1b602082015260ff600160f81b830416996001600160f81b0390921698509650600095509350505050565b6000610a40338686868660006105db565b95945050505050565b610a54838333611007565b6108e5838383611381565b6001600160a01b031660009081527f69f22dd730b53ff235fa96b4306a31bef5dad48fe4bac28b1ceebbce14401b396020526040902090565b6040516000602482018190526044820181905290819060640160408051601f198184030181529181526020820180516001600160e01b0316638639415b60e01b1790525190915060009081906001600160a01b03861690610afa908590611fb4565b600060405180830381855afa9150503d8060008114610b35576040519150601f19603f3d011682016040523d82523d6000602084013e610b3a565b606091505b509150915081610b9a5760405162461bcd60e51b815260206004820152602560248201527f6765745072696d617279526576656e756553706c69747328292063616c6c2066604482015264185a5b195960da1b606482015260840161062b565b805160c0819003610bb15750600095945050505050565b8061010003610bc65750600195945050505050565b60405162461bcd60e51b815260206004820152601e60248201527f556e657870656374656420726576656e75652073706c69742062797465730000604482015260640161062b565b600061058f610c1c83610c25565b85908590611471565b6040516001600160601b0319606083901b166020820152600090603401604051602081830303815290604052805190602001209050919050565b6000806000610c6e8686610fbc565b90506000610c7b826114bd565b90508015610cb2576001600160a01b03851660009081526002830160205260409020546001945080821115610cb05780820393505b505b5050935093915050565b600080610cc9848461118e565b54610100900462ffffff16949350505050565b600080610ce98585610fbc565b6001600160a01b0384166000908152600290910160205260409020549150509392505050565b6000610d1b838361118e565b805490915060ff16156108e55760405162461bcd60e51b815260040161062b90611fd0565b600080610d4d84846111b6565b8054909150600160f81b900460ff16610d9f5760405162461bcd60e51b8152602060048201526014602482015273141c9a58d9481b9bdd0818dbdb999a59dd5c995960621b604482015260640161062b565b546001600160f81b03169392505050565b6000610dbc8686610fbc565b9050610dce81600101548585856114e6565b610e115760405162461bcd60e51b815260206004820152601460248201527324b73b30b634b21026b2b935b63290383937b7b360611b604482015260640161062b565b6000610e1c826114bd565b6001600160a01b0384166000908152600284016020526040902054909150811180610e45575080155b610e615760405162461bcd60e51b815260040161062b90611fd0565b50505050505050565b6000610e768484610fbc565b6001600160a01b03909216600090815260029092016020525060409020805460010190555050565b620f424082046000610eb0828461118e565b90506000610ebd856114fe565b8254909150610100900462ffffff1680821115610eec5760405162461bcd60e51b815260040161062b90611fd0565b808203610eff57825460ff191660011783555b505050505050565b34156108e5576000610f198334612017565b90508015610fab57604051600090339083908381818185875af1925050503d8060008114610f63576040519150601f19603f3d011682016040523d82523d6000602084013e610f68565b606091505b5050905080610fa95760405162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b604482015260640161062b565b505b610fb6848484611518565b50505050565b60007f3ed708d712dfa866262466688638d160ef9516c1b2c87fe2388dc87cbf5bbf895b6001600160a01b039290921660009081526020928352604080822094825293909252502090565b6110128383836116d9565b6108e55760405162461bcd60e51b815260206004820152600b60248201526a13db9b1e48105c9d1a5cdd60aa1b604482015260640161062b565b600061105884846111b6565b600160f81b6001600160f81b03841617815560405190915082906001600160a01b0385169086907f98e99eae7ab1c56b5b882e049df69b522affbaa1a93afd3be411447a1606027890600090a450505050565b6000806110b8848461118e565b8054909150610100900462ffffff1615801561058f57505460ff16159392505050565b6000806110e88484611762565b9150915060006110f8858561118e565b9050611103826117e0565b815460ff1962ffffff92909216610100029190911663ffffffff19909116178383141781556040516001600160a01b0385169086907f1c3e74a6c6fefbaeee166b031cd2016349c6c863d5188b67c9bbd0b0dcb2e237906111679086815260200190565b60405180910390a35050505050565b6000806111838484610fbc565b905061058f816114bd565b60007f73e3af67f35f30fb72bbaaf6cc07b987364ed643c93ba201702e213464e877ee610fe0565b60007efc986db700c71340c673de619c46ee721167fc4ea17804bb535c4c196aa7d7610fe0565b6000806111ea8585611762565b91509150808362ffffff1611156112135760405162461bcd60e51b815260040161062b9061202a565b818362ffffff1610156112385760405162461bcd60e51b815260040161062b9061202a565b6000611244868661118e565b805462ffffff861685811463ffffffff19909216610100820260ff1916179190911782556040519081529091506001600160a01b0386169087907f1c3e74a6c6fefbaeee166b031cd2016349c6c863d5188b67c9bbd0b0dcb2e2379060200160405180910390a3505050505050565b806112f85760405162461bcd60e51b8152602060048201526015602482015274149bdbdd081b5d5cdd081899481c1c9bdd9a591959605a1b604482015260640161062b565b60006113048484610fbc565b6001810183905560408051691b595c9adb19549bdbdd60b21b8152602081018590529192506001600160a01b0385169186917fbd2962ddf019622ba935ab411adde29d756d1db6d628d89b2a174cdf6538b8d591015b60405180910390a350505050565b600080611375848461118e565b5460ff16949350505050565b600061138d8484610fbc565b8054600163ffffffff1990911661010062ffffff861602178117825560408051797573654d61784d696e7473506572416464724f7665727269646560301b815260208101929092529192506001600160a01b0385169186917fbb5f9d4f49f83650956b76c40de0f082a06430bf0222e1b6b3f90a7a0f845c4d910160405180910390a360408051766d61784d696e7473506572416464724f7665727269646560481b815262ffffff841660208201526001600160a01b0385169186917f1809fd1f08e6b0df08fd848757688435a0ab8520a51dcca23eba5a3c7f7280d6910161135a565b600081815b848110156114b4576114a0828787848181106114945761149461205b565b90506020020135611848565b9150806114ac81612071565b915050611476565b50949350505050565b805460009060ff16156114d9575054610100900462ffffff1690565b506001919050565b919050565b6000610a40856114f584610c25565b86918691611877565b600061150d620f42408361208a565b6105a29060016120ac565b8160000361152557505050565b60006115308261188f565b905060008060008060008060008088156115ce57604051638639415b60e01b8152600481018d9052602481018c90526001600160a01b038b1690638639415b9060440161010060405180830381865afa158015611591573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b591906120bf565b969e50949c50929a50909850965094509250905061164e565b604051638639415b60e01b8152600481018d9052602481018c90526001600160a01b038b1690638639415b9060440160c060405180830381865afa15801561161a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061163e9190612147565b949c50929a509096509450925090505b8a828561165b898c6120ac565b61166591906120ac565b61166f91906120ac565b146116bb5760405162461bcd60e51b815260206004820152601c60248201527b496e76616c696420726576656e75652073706c697420746f74616c7360201b604482015260640161062b565b6116cb86868a8a888888886118dd565b505050505050505050505050565b60405163a47d29cb60e01b8152600481018490526000906001600160a01b0384169063a47d29cb90602401602060405180830381865afa158015611721573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061174591906121a2565b6001600160a01b0316826001600160a01b03161490509392505050565b604051630ea5613f60e01b81526004810183905260009081906001600160a01b03841690630ea5613f9060240160c060405180830381865afa1580156117ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117d091906121bf565b5093989297509195505050505050565b600062ffffff8211156118445760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203260448201526534206269747360d01b606482015260840161062b565b5090565b6000818310611864576000828152602084905260409020610560565b6000838152602083905260409020610560565b600082611885868685611471565b1495945050505050565b60008061189b83610a5f565b8054909150610100900460ff16156118b7575460ff1692915050565b60006118c284610a98565b825481151561ffff1990911617610100179092555092915050565b8715611988576000876001600160a01b03168960405160006040518083038185875af1925050503d8060008114611930576040519150601f19603f3d011682016040523d82523d6000602084013e611935565b606091505b50509050806119865760405162461bcd60e51b815260206004820181905260248201527f506c6174666f726d2050726f7669646572207061796d656e74206661696c6564604482015260640161062b565b505b8515611a33576000856001600160a01b03168760405160006040518083038185875af1925050503d80600081146119db576040519150601f19603f3d011682016040523d82523d6000602084013e6119e0565b606091505b5050905080611a315760405162461bcd60e51b815260206004820152601e60248201527f52656e6465722050726f7669646572207061796d656e74206661696c65640000604482015260640161062b565b505b8315611ad6576000836001600160a01b03168560405160006040518083038185875af1925050503d8060008114611a86576040519150601f19603f3d011682016040523d82523d6000602084013e611a8b565b606091505b5050905080611ad45760405162461bcd60e51b8152602060048201526015602482015274105c9d1a5cdd081c185e5b595b9d0819985a5b1959605a1b604482015260640161062b565b505b8115611b81576000816001600160a01b03168360405160006040518083038185875af1925050503d8060008114611b29576040519150601f19603f3d011682016040523d82523d6000602084013e611b2e565b606091505b5050905080611b7f5760405162461bcd60e51b815260206004820152601f60248201527f4164646974696f6e616c205061796565207061796d656e74206661696c656400604482015260640161062b565b505b5050505050505050565b6001600160a01b0381168114611ba057600080fd5b50565b600060208284031215611bb557600080fd5b813561056081611b8b565b60008083601f840112611bd257600080fd5b5081356001600160401b03811115611be957600080fd5b6020830191508360208260051b8501011115611c0457600080fd5b9250929050565b600080600080600060808688031215611c2357600080fd5b8535611c2e81611b8b565b9450602086013593506040860135611c4581611b8b565b925060608601356001600160401b03811115611c6057600080fd5b611c6c88828901611bc0565b969995985093965092949392505050565b600080600060408486031215611c9257600080fd5b83356001600160401b03811115611ca857600080fd5b611cb486828701611bc0565b9094509250506020840135611cc881611b8b565b809150509250925092565b600080600060608486031215611ce857600080fd5b833592506020840135611cfa81611b8b565b91506040840135611cc881611b8b565b60008060408385031215611d1d57600080fd5b823591506020830135611d2f81611b8b565b809150509250929050565b60008060008060008060a08789031215611d5357600080fd5b8635611d5e81611b8b565b9550602087013594506040870135611d7581611b8b565b935060608701356001600160401b03811115611d9057600080fd5b611d9c89828a01611bc0565b9094509250506080870135611db081611b8b565b809150509295509295509295565b600080600060608486031215611dd357600080fd5b833592506020840135611de581611b8b565b915060408401356001600160f81b0381168114611cc857600080fd5b600080600060608486031215611e1657600080fd5b833592506020840135611e2881611b8b565b9150604084013562ffffff81168114611cc857600080fd5b600080600060608486031215611e5557600080fd5b833592506020840135611e6781611b8b565b929592945050506040919091013590565b60005b83811015611e93578181015183820152602001611e7b565b50506000910152565b60008151808452611eb4816020860160208601611e78565b601f01601f19169290920160200192915050565b6020815260006105606020830184611e9c565b8415158152836020820152608060408201526000611efc6080830185611e9c565b905060018060a01b038316606083015295945050505050565b60008060008060608587031215611f2b57600080fd5b843593506020850135611f3d81611b8b565b925060408501356001600160401b03811115611f5857600080fd5b611f6487828801611bc0565b95989497509550505050565b805180151581146114e157600080fd5b600060208284031215611f9257600080fd5b61056082611f70565b600060208284031215611fad57600080fd5b5051919050565b60008251611fc6818460208701611e78565b9190910192915050565b60208082526017908201527613585e081a5b9d9bd8d85d1a5bdb9cc81c995858da1959604a1b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b818103818111156105a2576105a2612001565b602080825260179082015276496e76616c6964206d617820696e766f636174696f6e7360481b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60006001820161208357612083612001565b5060010190565b6000826120a757634e487b7160e01b600052601260045260246000fd5b500690565b808201808211156105a2576105a2612001565b600080600080600080600080610100898b0312156120dc57600080fd5b8851975060208901516120ee81611b8b565b60408a015160608b0151919850965061210681611b8b565b60808a015160a08b0151919650945061211e81611b8b565b60c08a015160e08b0151919450925061213681611b8b565b809150509295985092959890939650565b60008060008060008060c0878903121561216057600080fd5b86519550602087015161217281611b8b565b60408801516060890151919650945061218a81611b8b565b608088015160a08901519194509250611db081611b8b565b6000602082840312156121b457600080fd5b815161056081611b8b565b60008060008060008060c087890312156121d857600080fd5b86519550602087015194506121ef60408801611f70565b93506121fd60608801611f70565b92506080870151915061221260a08801611f70565b9050929550929550929556fea264697066735822122069ffec58ab468753e2b5e10f298cec4690dca5eff3b72cce18e1506c685e62d264736f6c6343000813003300000000000000000000000029e9f09244497503f304fa549d50efc751d818d200000000000000000000000000000000000076a84fef008cdabe6409d2fe638b
Deployed Bytecode
0x60806040526004361061011b5760003560e01c806301000da7146101205780631467b1f7146101555780631ec2e523146101765780633aa5fe591461019657806340e5fa3b146101b65780634869f3df146101ed57806350a9e8421461020d5780635cf0a4111461022d5780636ece43dc146102405780637e947f24146102815780638b4eb61d146102a35780639a94488a146102c35780639b24c3c014610305578063b000e3341461034b578063b466dded1461036b578063bf5bf5f81461038b578063cce7c909146103d7578063d3ddabe6146103f7578063d9bffbce14610436578063d9eb16db14610456578063dd85582f14610486578063e9d1e8ac146104ba578063eb7eb95a146104fc578063ef2c8cc71461050f575b600080fd5b34801561012c57600080fd5b5061014061013b366004611ba3565b61052f565b60405190151581526020015b60405180910390f35b610168610163366004611c0b565b610567565b60405190815260200161014c565b34801561018257600080fd5b50610168610191366004611c7d565b610582565b3480156101a257600080fd5b506101686101b1366004611ba3565b610597565b3480156101c257600080fd5b506101d66101d1366004611cd3565b6105a8565b60408051921515835260208301919091520161014c565b3480156101f957600080fd5b50610168610208366004611d0a565b6105c2565b34801561021957600080fd5b50610168610228366004611cd3565b6105ce565b61016861023b366004611d3a565b6105db565b34801561024c57600080fd5b5061026061025b366004611d0a565b61087a565b60408051931515845262ffffff90921660208401529082015260600161014c565b34801561028d57600080fd5b506102a161029c366004611dbe565b6108ad565b005b3480156102af57600080fd5b506101686102be366004611d0a565b6108ea565b3480156102cf57600080fd5b506102e36102de366004611d0a565b6108f6565b6040805182511515815260209283015162ffffff16928101929092520161014c565b34801561031157600080fd5b50610325610320366004611d0a565b61093e565b6040805182516001600160f81b031681526020928301511515928101929092520161014c565b34801561035757600080fd5b506102a1610366366004611e01565b61098c565b34801561037757600080fd5b506102a1610386366004611e40565b6109a2565b34801561039757600080fd5b506103bf7f00000000000000000000000000000000000076a84fef008cdabe6409d2fe638b81565b6040516001600160a01b03909116815260200161014c565b3480156103e357600080fd5b506101406103f2366004611d0a565b6109b8565b34801561040357600080fd5b5061042960405180604001604052806006815260200165076352e302e360d41b81525081565b60405161014c9190611ec8565b34801561044257600080fd5b506102a1610451366004611d0a565b6109c4565b34801561046257600080fd5b50610476610471366004611d0a565b6109dd565b60405161014c9493929190611edb565b34801561049257600080fd5b506103bf7f00000000000000000000000029e9f09244497503f304fa549d50efc751d818d281565b3480156104c657600080fd5b50610429604051806040016040528060168152602001754d696e74657253657450726963654d65726b6c65563560501b81525081565b61016861050a366004611f15565b610a2f565b34801561051b57600080fd5b506102a161052a366004611e01565b610a49565b60008061053b83610a5f565b8054909150610100900460ff1615610557575460ff1692915050565b61056083610a98565b9392505050565b6000610578868686868660006105db565b9695505050505050565b600061058f848484610c0e565b949350505050565b60006105a282610c25565b92915050565b6000806105b6858585610c5f565b91509150935093915050565b60006105608383610cbc565b600061058f848484610cdc565b60006002600054036106345760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b60026000556106438686610d0f565b600061064f8787610d40565b90508034101561069a5760405162461bcd60e51b815260206004820152601660248201527526b4b7103b30b63ab2903a379036b4b73a103932b89760511b604482015260640161062b565b336001600160a01b0384161561079b5760405163090c9a2d60e41b81523360048201526001600160a01b03858116602483015288811660448301526000917f00000000000000000000000000000000000076a84fef008cdabe6409d2fe638b909116906390c9a2d090606401602060405180830381865afa158015610723573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107479190611f80565b9050806107965760405162461bcd60e51b815260206004820152601e60248201527f496e76616c69642064656c65676174652d7661756c742070616972696e670000604482015260640161062b565b849150505b6107a88888888885610db0565b6107b3888883610e6a565b6040516117cd60e21b81526001600160a01b038a81166004830152602482018a9052888116604483015282811660648301527f00000000000000000000000029e9f09244497503f304fa549d50efc751d818d21690615f34906084016020604051808303816000875af115801561082e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108529190611f9b565b925061085e8388610e9e565b610869888389610f07565b505060016000559695505050505050565b60008060008061088a8686610fbc565b805460019091015460ff82169861010090920462ffffff16975095509350505050565b6108b8838333611007565b6108cc8383836001600160f81b031661104c565b6108d683836110ab565b156108e5576108e583836110db565b505050565b60006105608383611176565b6040805180820190915260008082526020820152610914838361118e565b60408051808201909152905460ff811615158252610100900462ffffff1660208201529392505050565b604080518082019091526000808252602082015261095c83836111b6565b6040805180820190915290546001600160f81b0381168252600160f81b900460ff16151560208201529392505050565b610997838333611007565b6108e58383836111dd565b6109ad838333611007565b6108e58383836112b3565b60006105608383611368565b6109cf828233611007565b6109d982826110db565b5050565b60008060606000806109ef87876111b6565b5460408051808201909152600381526208aa8960eb1b602082015260ff600160f81b830416996001600160f81b0390921698509650600095509350505050565b6000610a40338686868660006105db565b95945050505050565b610a54838333611007565b6108e5838383611381565b6001600160a01b031660009081527f69f22dd730b53ff235fa96b4306a31bef5dad48fe4bac28b1ceebbce14401b396020526040902090565b6040516000602482018190526044820181905290819060640160408051601f198184030181529181526020820180516001600160e01b0316638639415b60e01b1790525190915060009081906001600160a01b03861690610afa908590611fb4565b600060405180830381855afa9150503d8060008114610b35576040519150601f19603f3d011682016040523d82523d6000602084013e610b3a565b606091505b509150915081610b9a5760405162461bcd60e51b815260206004820152602560248201527f6765745072696d617279526576656e756553706c69747328292063616c6c2066604482015264185a5b195960da1b606482015260840161062b565b805160c0819003610bb15750600095945050505050565b8061010003610bc65750600195945050505050565b60405162461bcd60e51b815260206004820152601e60248201527f556e657870656374656420726576656e75652073706c69742062797465730000604482015260640161062b565b600061058f610c1c83610c25565b85908590611471565b6040516001600160601b0319606083901b166020820152600090603401604051602081830303815290604052805190602001209050919050565b6000806000610c6e8686610fbc565b90506000610c7b826114bd565b90508015610cb2576001600160a01b03851660009081526002830160205260409020546001945080821115610cb05780820393505b505b5050935093915050565b600080610cc9848461118e565b54610100900462ffffff16949350505050565b600080610ce98585610fbc565b6001600160a01b0384166000908152600290910160205260409020549150509392505050565b6000610d1b838361118e565b805490915060ff16156108e55760405162461bcd60e51b815260040161062b90611fd0565b600080610d4d84846111b6565b8054909150600160f81b900460ff16610d9f5760405162461bcd60e51b8152602060048201526014602482015273141c9a58d9481b9bdd0818dbdb999a59dd5c995960621b604482015260640161062b565b546001600160f81b03169392505050565b6000610dbc8686610fbc565b9050610dce81600101548585856114e6565b610e115760405162461bcd60e51b815260206004820152601460248201527324b73b30b634b21026b2b935b63290383937b7b360611b604482015260640161062b565b6000610e1c826114bd565b6001600160a01b0384166000908152600284016020526040902054909150811180610e45575080155b610e615760405162461bcd60e51b815260040161062b90611fd0565b50505050505050565b6000610e768484610fbc565b6001600160a01b03909216600090815260029092016020525060409020805460010190555050565b620f424082046000610eb0828461118e565b90506000610ebd856114fe565b8254909150610100900462ffffff1680821115610eec5760405162461bcd60e51b815260040161062b90611fd0565b808203610eff57825460ff191660011783555b505050505050565b34156108e5576000610f198334612017565b90508015610fab57604051600090339083908381818185875af1925050503d8060008114610f63576040519150601f19603f3d011682016040523d82523d6000602084013e610f68565b606091505b5050905080610fa95760405162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b604482015260640161062b565b505b610fb6848484611518565b50505050565b60007f3ed708d712dfa866262466688638d160ef9516c1b2c87fe2388dc87cbf5bbf895b6001600160a01b039290921660009081526020928352604080822094825293909252502090565b6110128383836116d9565b6108e55760405162461bcd60e51b815260206004820152600b60248201526a13db9b1e48105c9d1a5cdd60aa1b604482015260640161062b565b600061105884846111b6565b600160f81b6001600160f81b03841617815560405190915082906001600160a01b0385169086907f98e99eae7ab1c56b5b882e049df69b522affbaa1a93afd3be411447a1606027890600090a450505050565b6000806110b8848461118e565b8054909150610100900462ffffff1615801561058f57505460ff16159392505050565b6000806110e88484611762565b9150915060006110f8858561118e565b9050611103826117e0565b815460ff1962ffffff92909216610100029190911663ffffffff19909116178383141781556040516001600160a01b0385169086907f1c3e74a6c6fefbaeee166b031cd2016349c6c863d5188b67c9bbd0b0dcb2e237906111679086815260200190565b60405180910390a35050505050565b6000806111838484610fbc565b905061058f816114bd565b60007f73e3af67f35f30fb72bbaaf6cc07b987364ed643c93ba201702e213464e877ee610fe0565b60007efc986db700c71340c673de619c46ee721167fc4ea17804bb535c4c196aa7d7610fe0565b6000806111ea8585611762565b91509150808362ffffff1611156112135760405162461bcd60e51b815260040161062b9061202a565b818362ffffff1610156112385760405162461bcd60e51b815260040161062b9061202a565b6000611244868661118e565b805462ffffff861685811463ffffffff19909216610100820260ff1916179190911782556040519081529091506001600160a01b0386169087907f1c3e74a6c6fefbaeee166b031cd2016349c6c863d5188b67c9bbd0b0dcb2e2379060200160405180910390a3505050505050565b806112f85760405162461bcd60e51b8152602060048201526015602482015274149bdbdd081b5d5cdd081899481c1c9bdd9a591959605a1b604482015260640161062b565b60006113048484610fbc565b6001810183905560408051691b595c9adb19549bdbdd60b21b8152602081018590529192506001600160a01b0385169186917fbd2962ddf019622ba935ab411adde29d756d1db6d628d89b2a174cdf6538b8d591015b60405180910390a350505050565b600080611375848461118e565b5460ff16949350505050565b600061138d8484610fbc565b8054600163ffffffff1990911661010062ffffff861602178117825560408051797573654d61784d696e7473506572416464724f7665727269646560301b815260208101929092529192506001600160a01b0385169186917fbb5f9d4f49f83650956b76c40de0f082a06430bf0222e1b6b3f90a7a0f845c4d910160405180910390a360408051766d61784d696e7473506572416464724f7665727269646560481b815262ffffff841660208201526001600160a01b0385169186917f1809fd1f08e6b0df08fd848757688435a0ab8520a51dcca23eba5a3c7f7280d6910161135a565b600081815b848110156114b4576114a0828787848181106114945761149461205b565b90506020020135611848565b9150806114ac81612071565b915050611476565b50949350505050565b805460009060ff16156114d9575054610100900462ffffff1690565b506001919050565b919050565b6000610a40856114f584610c25565b86918691611877565b600061150d620f42408361208a565b6105a29060016120ac565b8160000361152557505050565b60006115308261188f565b905060008060008060008060008088156115ce57604051638639415b60e01b8152600481018d9052602481018c90526001600160a01b038b1690638639415b9060440161010060405180830381865afa158015611591573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115b591906120bf565b969e50949c50929a50909850965094509250905061164e565b604051638639415b60e01b8152600481018d9052602481018c90526001600160a01b038b1690638639415b9060440160c060405180830381865afa15801561161a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061163e9190612147565b949c50929a509096509450925090505b8a828561165b898c6120ac565b61166591906120ac565b61166f91906120ac565b146116bb5760405162461bcd60e51b815260206004820152601c60248201527b496e76616c696420726576656e75652073706c697420746f74616c7360201b604482015260640161062b565b6116cb86868a8a888888886118dd565b505050505050505050505050565b60405163a47d29cb60e01b8152600481018490526000906001600160a01b0384169063a47d29cb90602401602060405180830381865afa158015611721573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061174591906121a2565b6001600160a01b0316826001600160a01b03161490509392505050565b604051630ea5613f60e01b81526004810183905260009081906001600160a01b03841690630ea5613f9060240160c060405180830381865afa1580156117ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117d091906121bf565b5093989297509195505050505050565b600062ffffff8211156118445760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203260448201526534206269747360d01b606482015260840161062b565b5090565b6000818310611864576000828152602084905260409020610560565b6000838152602083905260409020610560565b600082611885868685611471565b1495945050505050565b60008061189b83610a5f565b8054909150610100900460ff16156118b7575460ff1692915050565b60006118c284610a98565b825481151561ffff1990911617610100179092555092915050565b8715611988576000876001600160a01b03168960405160006040518083038185875af1925050503d8060008114611930576040519150601f19603f3d011682016040523d82523d6000602084013e611935565b606091505b50509050806119865760405162461bcd60e51b815260206004820181905260248201527f506c6174666f726d2050726f7669646572207061796d656e74206661696c6564604482015260640161062b565b505b8515611a33576000856001600160a01b03168760405160006040518083038185875af1925050503d80600081146119db576040519150601f19603f3d011682016040523d82523d6000602084013e6119e0565b606091505b5050905080611a315760405162461bcd60e51b815260206004820152601e60248201527f52656e6465722050726f7669646572207061796d656e74206661696c65640000604482015260640161062b565b505b8315611ad6576000836001600160a01b03168560405160006040518083038185875af1925050503d8060008114611a86576040519150601f19603f3d011682016040523d82523d6000602084013e611a8b565b606091505b5050905080611ad45760405162461bcd60e51b8152602060048201526015602482015274105c9d1a5cdd081c185e5b595b9d0819985a5b1959605a1b604482015260640161062b565b505b8115611b81576000816001600160a01b03168360405160006040518083038185875af1925050503d8060008114611b29576040519150601f19603f3d011682016040523d82523d6000602084013e611b2e565b606091505b5050905080611b7f5760405162461bcd60e51b815260206004820152601f60248201527f4164646974696f6e616c205061796565207061796d656e74206661696c656400604482015260640161062b565b505b5050505050505050565b6001600160a01b0381168114611ba057600080fd5b50565b600060208284031215611bb557600080fd5b813561056081611b8b565b60008083601f840112611bd257600080fd5b5081356001600160401b03811115611be957600080fd5b6020830191508360208260051b8501011115611c0457600080fd5b9250929050565b600080600080600060808688031215611c2357600080fd5b8535611c2e81611b8b565b9450602086013593506040860135611c4581611b8b565b925060608601356001600160401b03811115611c6057600080fd5b611c6c88828901611bc0565b969995985093965092949392505050565b600080600060408486031215611c9257600080fd5b83356001600160401b03811115611ca857600080fd5b611cb486828701611bc0565b9094509250506020840135611cc881611b8b565b809150509250925092565b600080600060608486031215611ce857600080fd5b833592506020840135611cfa81611b8b565b91506040840135611cc881611b8b565b60008060408385031215611d1d57600080fd5b823591506020830135611d2f81611b8b565b809150509250929050565b60008060008060008060a08789031215611d5357600080fd5b8635611d5e81611b8b565b9550602087013594506040870135611d7581611b8b565b935060608701356001600160401b03811115611d9057600080fd5b611d9c89828a01611bc0565b9094509250506080870135611db081611b8b565b809150509295509295509295565b600080600060608486031215611dd357600080fd5b833592506020840135611de581611b8b565b915060408401356001600160f81b0381168114611cc857600080fd5b600080600060608486031215611e1657600080fd5b833592506020840135611e2881611b8b565b9150604084013562ffffff81168114611cc857600080fd5b600080600060608486031215611e5557600080fd5b833592506020840135611e6781611b8b565b929592945050506040919091013590565b60005b83811015611e93578181015183820152602001611e7b565b50506000910152565b60008151808452611eb4816020860160208601611e78565b601f01601f19169290920160200192915050565b6020815260006105606020830184611e9c565b8415158152836020820152608060408201526000611efc6080830185611e9c565b905060018060a01b038316606083015295945050505050565b60008060008060608587031215611f2b57600080fd5b843593506020850135611f3d81611b8b565b925060408501356001600160401b03811115611f5857600080fd5b611f6487828801611bc0565b95989497509550505050565b805180151581146114e157600080fd5b600060208284031215611f9257600080fd5b61056082611f70565b600060208284031215611fad57600080fd5b5051919050565b60008251611fc6818460208701611e78565b9190910192915050565b60208082526017908201527613585e081a5b9d9bd8d85d1a5bdb9cc81c995858da1959604a1b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b818103818111156105a2576105a2612001565b602080825260179082015276496e76616c6964206d617820696e766f636174696f6e7360481b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60006001820161208357612083612001565b5060010190565b6000826120a757634e487b7160e01b600052601260045260246000fd5b500690565b808201808211156105a2576105a2612001565b600080600080600080600080610100898b0312156120dc57600080fd5b8851975060208901516120ee81611b8b565b60408a015160608b0151919850965061210681611b8b565b60808a015160a08b0151919650945061211e81611b8b565b60c08a015160e08b0151919450925061213681611b8b565b809150509295985092959890939650565b60008060008060008060c0878903121561216057600080fd5b86519550602087015161217281611b8b565b60408801516060890151919650945061218a81611b8b565b608088015160a08901519194509250611db081611b8b565b6000602082840312156121b457600080fd5b815161056081611b8b565b60008060008060008060c087890312156121d857600080fd5b86519550602087015194506121ef60408801611f70565b93506121fd60608801611f70565b92506080870151915061221260a08801611f70565b9050929550929550929556fea264697066735822122069ffec58ab468753e2b5e10f298cec4690dca5eff3b72cce18e1506c685e62d264736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000029e9f09244497503f304fa549d50efc751d818d200000000000000000000000000000000000076a84fef008cdabe6409d2fe638b
-----Decoded View---------------
Arg [0] : minterFilter (address): 0x29e9f09244497503f304FA549d50eFC751D818d2
Arg [1] : delegationRegistryAddress_ (address): 0x00000000000076A84feF008CDAbe6409d2FE638B
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000029e9f09244497503f304fa549d50efc751d818d2
Arg [1] : 00000000000000000000000000000000000076a84fef008cdabe6409d2fe638b
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.