// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {MintOrder, MintParameters, MintToken, BurnToken, PaymentToken} from "../lib/MinterStructs.sol";
/**
* @title ILayerrMinter
* @author 0xth0mas (Layerr)
* @notice ILayerrMinter interface defines functions required in the LayerrMinter to be callable by token contracts
*/
interface ILayerrMinter {
/// @dev Event emitted when a mint order is fulfilled
event MintOrderFulfilled(
bytes32 indexed mintParametersDigest,
address indexed minter,
uint256 indexed quantity
);
/// @dev Event emitted when a token contract updates an allowed signer for EIP712 signatures
event ContractAllowedSignerUpdate(
address indexed _contract,
address indexed _signer,
bool indexed _allowed
);
/// @dev Event emitted when a token contract updates an allowed oracle signer for offchain authorization of a wallet to use a signature
event ContractOracleUpdated(
address indexed _contract,
address indexed _oracle,
bool indexed _allowed
);
/// @dev Event emitted when a signer updates their nonce with LayerrMinter. Updating a nonce invalidates all previously signed EIP712 signatures.
event SignerNonceIncremented(
address indexed _signer,
uint256 indexed _nonce
);
/// @dev Event emitted when a specific signature's validity is updated with the LayerrMinter contract.
event SignatureValidityUpdated(
address indexed _contract,
bool indexed invalid,
bytes32 mintParametersDigests
);
/// @dev Thrown when the amount of native tokens supplied in msg.value is insufficient for the mint order
error InsufficientPayment();
/// @dev Thrown when a payment fails to be forwarded to the intended recipient
error PaymentFailed();
/// @dev Thrown when a MintParameters payment token uses a token type value other than native or ERC20
error InvalidPaymentTokenType();
/// @dev Thrown when a MintParameters burn token uses a token type value other than ERC20, ERC721 or ERC1155
error InvalidBurnTokenType();
/// @dev Thrown when a MintParameters mint token uses a token type value other than ERC20, ERC721 or ERC1155
error InvalidMintTokenType();
/// @dev Thrown when a MintParameters burn token uses a burn type value other than contract burn or send to dead
error InvalidBurnType();
/// @dev Thrown when a MintParameters burn token requires a specific burn token id and the tokenId supplied does not match
error InvalidBurnTokenId();
/// @dev Thrown when a MintParameters burn token requires a specific ERC721 token and the burn amount is greater than 1
error CannotBurnMultipleERC721WithSameId();
/// @dev Thrown when attempting to mint with MintParameters that have a start time greater than the current block time
error MintHasNotStarted();
/// @dev Thrown when attempting to mint with MintParameters that have an end time less than the current block time
error MintHasEnded();
/// @dev Thrown when a MintParameters has a merkleroot set but the supplied merkle proof is invalid
error InvalidMerkleProof();
/// @dev Thrown when a MintOrder will cause a token's minted supply to exceed the defined maximum supply in MintParameters
error MintExceedsMaxSupply();
/// @dev Thrown when a MintOrder will cause a minter's minted amount to exceed the defined max per wallet in MintParameters
error MintExceedsMaxPerWallet();
/// @dev Thrown when a MintParameters mint token has a specific ERC721 token and the mint amount is greater than 1
error CannotMintMultipleERC721WithSameId();
/// @dev Thrown when the recovered signer for the MintParameters is not an allowed signer for the mint token
error NotAllowedSigner();
/// @dev Thrown when the recovered signer's nonce does not match the current nonce in LayerrMinter
error SignerNonceInvalid();
/// @dev Thrown when a signature has been marked as invalid for a mint token contract
error SignatureInvalid();
/// @dev Thrown when MintParameters requires an oracle signature and the recovered signer is not an allowed oracle for the contract
error InvalidOracleSignature();
/// @dev Thrown when MintParameters has a max signature use set and the MintOrder will exceed the maximum uses
error ExceedsMaxSignatureUsage();
/// @dev Thrown when attempting to increment nonce on behalf of another account and the signature is invalid
error InvalidSignatureToIncrementNonce();
/**
* @notice This function is called by token contracts to update allowed signers for minting
* @param _signer address of the EIP712 signer
* @param _allowed if the `_signer` is allowed to sign for minting
*/
function setContractAllowedSigner(address _signer, bool _allowed) external;
/**
* @notice This function is called by token contracts to update allowed oracles for offchain authorizations
* @param _oracle address of the oracle
* @param _allowed if the `_oracle` is allowed to sign offchain authorizations
*/
function setContractAllowedOracle(address _oracle, bool _allowed) external;
/**
* @notice This function is called by token contracts to update validity of signatures for the LayerrMinter contract
* @dev `invalid` should be true to invalidate signatures, the default state of `invalid` being false means
* a signature is valid for a contract assuming all other conditions are met
* @param mintParametersDigests an array of message digests for MintParameters to update validity of
* @param invalid if the supplied digests will be marked as valid or invalid
*/
function setSignatureValidity(
bytes32[] calldata mintParametersDigests,
bool invalid
) external;
/**
* @notice Increments the nonce for a signer to invalidate all previous signed MintParameters
*/
function incrementSignerNonce() external;
/**
* @notice Increments the nonce on behalf of another account by validating a signature from that account
* @dev The signature is an eth personal sign message of the current signer nonce plus the chain id
* ex. current nonce 0 on chain 5 would be a signature of \x19Ethereum Signed Message:\n15
* current nonce 50 on chain 1 would be a signature of \x19Ethereum Signed Message:\n251
* @param signer account to increment nonce for
* @param signature signature proof that the request is coming from the account
*/
function incrementNonceFor(address signer, bytes calldata signature) external;
/**
* @notice Validates and processes a single MintOrder, tokens are minted to msg.sender
* @param mintOrder struct containing the details of the mint order
*/
function mint(
MintOrder calldata mintOrder
) external payable;
/**
* @notice Validates and processes an array of MintOrders, tokens are minted to msg.sender
* @param mintOrders array of structs containing the details of the mint orders
*/
function mintBatch(
MintOrder[] calldata mintOrders
) external payable;
/**
* @notice Validates and processes a single MintOrder, tokens are minted to `mintToWallet`
* @param mintToWallet the address tokens will be minted to
* @param mintOrder struct containing the details of the mint order
* @param paymentContext Contextual information related to the payment process
* (Note: This parameter is required for integration with
* the payment processor and does not impact the behavior
* of the function)
*/
function mintTo(
address mintToWallet,
MintOrder calldata mintOrder,
uint256 paymentContext
) external payable;
/**
* @notice Validates and processes an array of MintOrders, tokens are minted to `mintToWallet`
* @param mintToWallet the address tokens will be minted to
* @param mintOrders array of structs containing the details of the mint orders
* @param paymentContext Contextual information related to the payment process
* (Note: This parameter is required for integration with
* the payment processor and does not impact the behavior
* of the function)
*/
function mintBatchTo(
address mintToWallet,
MintOrder[] calldata mintOrders,
uint256 paymentContext
) external payable;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
import {StringValue} from "./lib/StorageTypes.sol";
import {AddressValue} from "./lib/StorageTypes.sol";
import {ILayerrMinter} from "./interfaces/ILayerrMinter.sol";
import {LAYERROWNABLE_OWNER_SLOT, LAYERRTOKEN_NAME_SLOT, LAYERRTOKEN_SYMBOL_SLOT, LAYERRTOKEN_RENDERER_SLOT} from "./common/LayerrStorage.sol";
/**
* @title LayerrProxy
* @author 0xth0mas (Layerr)
* @notice A proxy contract that serves as an interface for interacting with
* Layerr tokens. At deployment it sets token properties and contract
* ownership, initializes signers and mint extensions, and configures
* royalties.
*/
contract LayerrProxy {
/// @dev the implementation address for the proxy contract
address immutable proxy;
/// @dev this is included as a hint for block explorers
bytes32 private constant PROXY_IMPLEMENTATION_REFERENCE = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// @dev Thrown when a required initialization call fails
error DeploymentFailed();
/**
* @notice Initializes the proxy contract
* @param _owner initial owner of the token contract
* @param _proxy implementation address for the proxy contract
* @param _name token contract name
* @param _symbol token contract symbol
* @param royaltyPct default royalty percentage in BPS
* @param royaltyReceiver default royalty receiver
* @param operatorFilterRegistry address of the operator filter registry to subscribe to
* @param _extension minting extension to use with the token contract
* @param _renderer renderer to use with the token contract
* @param _signers array of allowed signers for the mint extension
*/
constructor(
address _owner,
address _proxy,
string memory _name,
string memory _symbol,
uint96 royaltyPct,
address royaltyReceiver,
address operatorFilterRegistry,
address _extension,
address _renderer,
address[] memory _signers
) {
proxy = _proxy;
StringValue storage name;
StringValue storage symbol;
AddressValue storage renderer;
AddressValue storage owner;
AddressValue storage explorerProxy;
/// @solidity memory-safe-assembly
assembly {
name.slot := LAYERRTOKEN_NAME_SLOT
symbol.slot := LAYERRTOKEN_SYMBOL_SLOT
renderer.slot := LAYERRTOKEN_RENDERER_SLOT
owner.slot := LAYERROWNABLE_OWNER_SLOT
explorerProxy.slot := PROXY_IMPLEMENTATION_REFERENCE
}
name.value = _name;
symbol.value = _symbol;
renderer.value = _renderer;
owner.value = msg.sender;
explorerProxy.value = _proxy;
uint256 signersLength = _signers.length;
for(uint256 signerIndex;signerIndex < signersLength;) {
ILayerrMinter(_extension).setContractAllowedSigner(_signers[signerIndex], true);
unchecked {
++signerIndex;
}
}
(bool success, ) = _proxy.delegatecall(abi.encodeWithSignature("setRoyalty(uint96,address)", royaltyPct, royaltyReceiver));
if(!success) revert DeploymentFailed();
(success, ) = _proxy.delegatecall(abi.encodeWithSignature("setOperatorFilter(address)", operatorFilterRegistry));
//this item may fail if deploying a contract that does not use an operator filter
(success, ) = _proxy.delegatecall(abi.encodeWithSignature("setMintExtension(address,bool)", _extension, true));
if(!success) revert DeploymentFailed();
(success, ) = _proxy.delegatecall(abi.encodeWithSignature("initialize()"));
if(!success) revert DeploymentFailed();
owner.value = _owner;
}
fallback() external payable {
address _proxy = proxy;
assembly {
calldatacopy(0x0, 0x0, calldatasize())
let result := delegatecall(gas(), _proxy, 0x0, calldatasize(), 0x0, 0)
returndatacopy(0x0, 0x0, returndatasize())
switch result case 0 {revert(0, returndatasize())} default {return (0, returndatasize())}
}
}
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
/// @dev Storage slot for current owner calculated from keccak256('Layerr.LayerrOwnable.owner')
bytes32 constant LAYERROWNABLE_OWNER_SLOT = 0xedc628ad38a73ae7d50600532f1bf21da1bfb1390b4f8174f361aca54d4c6b66;
/// @dev Storage slot for pending ownership transfer calculated from keccak256('Layerr.LayerrOwnable.newOwner')
bytes32 constant LAYERROWNABLE_NEW_OWNER_SLOT = 0x15c115ab76de082272ae65126522082d4aad634b6478097549f84086af3b84bc;
/// @dev Storage slot for token name calculated from keccak256('Layerr.LayerrToken.name')
bytes32 constant LAYERRTOKEN_NAME_SLOT = 0x7f84c61ed30727f282b62cab23f49ac7f4d263f04a4948416b7b9ba7f34a20dc;
/// @dev Storage slot for token symbol calculated from keccak256('Layerr.LayerrToken.symbol')
bytes32 constant LAYERRTOKEN_SYMBOL_SLOT = 0xdc0f2363b26c589c72caecd2357dae5fee235863060295a057e8d69d61a96d8a;
/// @dev Storage slot for URI renderer calculated from keccak256('Layerr.LayerrToken.renderer')
bytes32 constant LAYERRTOKEN_RENDERER_SLOT = 0x395b7021d979c3dbed0f5d530785632316942232113ba3dbe325dc167550e320;
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
/**
* @dev EIP712 Domain for signature verification
*/
struct EIP712Domain {
string name;
string version;
uint256 chainId;
address verifyingContract;
}
/**
* @dev MintOrders contain MintParameters as defined by a token creator
* along with proofs required to validate the MintParameters and
* parameters specific to the mint being performed.
*
* `mintParameters` are the parameters signed by the token creator
* `quantity` is a multiplier for mintTokens, burnTokens and paymentTokens
* defined in mintParameters
* `mintParametersSignature` is the signature from the token creator
* `oracleSignature` is a signature of the hash of the mintParameters digest
* and msg.sender. The recovered signer must be an allowed oracle for
* the token contract if oracleSignatureRequired is true for mintParameters.
* `merkleProof` is the proof that is checked if merkleRoot is not bytes(0) in
* mintParameters
* `suppliedBurnTokenIds` is an array of tokenIds to be used when processing
* burnTokens. There must be one item in the array for each ERC1155 burnToken
* regardless of `quantity` and `quantity` items in the array for each ERC721
* burnToken.
* `referrer` is the address that will receive a portion of a paymentToken if
* not address(0) and paymentToken's referralBPS is greater than 0
* `vaultWallet` is used for allowlist mints if the msg.sender address it not on
* the allowlist but their delegate.cash vault wallet is.
*
*/
struct MintOrder {
MintParameters mintParameters;
uint256 quantity;
bytes mintParametersSignature;
bytes oracleSignature;
bytes32[] merkleProof;
uint256[] suppliedBurnTokenIds;
address referrer;
address vaultWallet;
}
/**
* @dev MintParameters define the tokens to be minted and conditions that must be met
* for the mint to be successfully processed.
*
* `mintTokens` is an array of tokens that will be minted
* `burnTokens` is an array of tokens required to be burned
* `paymentTokens` is an array of tokens required as payment
* `startTime` is the UTC timestamp of when the mint will start
* `endTime` is the UTC timestamp of when the mint will end
* `signatureMaxUses` limits the number of mints that can be performed with the
* specific mintParameters/signature
* `merkleRoot` is the root of the merkletree for allowlist minting
* `nonce` is the signer nonce that can be incremented on the LayerrMinter
* contract to invalidate all previous signatures
* `oracleSignatureRequired` if true requires a secondary signature to process the mint
*/
struct MintParameters {
MintToken[] mintTokens;
BurnToken[] burnTokens;
PaymentToken[] paymentTokens;
uint256 startTime;
uint256 endTime;
uint256 signatureMaxUses;
bytes32 merkleRoot;
uint256 nonce;
bool oracleSignatureRequired;
}
/**
* @dev Defines the token that will be minted
*
* `contractAddress` address of contract to mint tokens from
* `specificTokenId` used for ERC721 -
* if true, mint is non-sequential ERC721
* if false, mint is sequential ERC721A
* `tokenType` is the type of token being minted defined in TokenTypes.sol
* `tokenId` the tokenId to mint if specificTokenId is true
* `mintAmount` is the quantity to be minted
* `maxSupply` is checked against the total minted amount at time of mint
* minting reverts if `mintAmount` * `quantity` will cause total minted to
* exceed `maxSupply`
* `maxMintPerWallet` is checked against the number minted for the wallet
* minting reverts if `mintAmount` * `quantity` will cause wallet minted to
* exceed `maxMintPerWallet`
*/
struct MintToken {
address contractAddress;
bool specificTokenId;
uint256 tokenType;
uint256 tokenId;
uint256 mintAmount;
uint256 maxSupply;
uint256 maxMintPerWallet;
}
/**
* @dev Defines the token that will be burned
*
* `contractAddress` address of contract to burn tokens from
* `specificTokenId` specifies if the user has the option of choosing any token
* from the contract or if they must burn a specific token
* `tokenType` is the type of token being burned, defined in TokenTypes.sol
* `burnType` is the type of burn to perform, burn function call or transfer to
* dead address, defined in BurnType.sol
* `tokenId` the tokenId to burn if specificTokenId is true
* `burnAmount` is the quantity to be burned
*/
struct BurnToken {
address contractAddress;
bool specificTokenId;
uint256 tokenType;
uint256 burnType;
uint256 tokenId;
uint256 burnAmount;
}
/**
* @dev Defines the token that will be used for payment
*
* `contractAddress` address of contract to for payment if ERC20
* if tokenType is native token then this should be set to 0x000...000
* to save calldata gas units
* `tokenType` is the type of token being used for payment, defined in TokenTypes.sol
* `payTo` the address that will receive the payment
* `paymentAmount` the amount for the payment in base units for the token
* ex. a native payment on Ethereum for 1 ETH would be specified in wei
* which would be 1**18 wei
* `referralBPS` is the percentage of the payment in BPS that will be sent to the
* `referrer` on the MintOrder if `referralBPS` is greater than 0 and `referrer`
* is not address(0)
*/
struct PaymentToken {
address contractAddress;
uint256 tokenType;
address payTo;
uint256 paymentAmount;
uint256 referralBPS;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
/// @dev Simple struct to store a string value in a custom storage slot
struct StringValue {
string value;
}
/// @dev Simple struct to store an address value in a custom storage slot
struct AddressValue {
address value;
}
{
"compilationTarget": {
"contracts/LayerrProxy.sol": "LayerrProxy"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 33333
},
"remappings": [],
"viaIR": true
}
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_proxy","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint96","name":"royaltyPct","type":"uint96"},{"internalType":"address","name":"royaltyReceiver","type":"address"},{"internalType":"address","name":"operatorFilterRegistry","type":"address"},{"internalType":"address","name":"_extension","type":"address"},{"internalType":"address","name":"_renderer","type":"address"},{"internalType":"address[]","name":"_signers","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DeploymentFailed","type":"error"},{"stateMutability":"payable","type":"fallback"}]