Address Contract Verified
Address
0x723EBF276f95E992480D009bC132c9820A39B58F
Balance
0 ETH
Nonce
1
Code Size
8767 bytes
Creator
0x00df4E8d...84Ef at tx 0x07411d40...10b860
Indexed Transactions
0
Contract Bytecode
8767 bytes
0x6080604052600436106101315760003560e01c806301000da7146101365780631467b1f71461016b5780631ec2e5231461018c5780633aa5fe59146101ac57806340e5fa3b146101cc5780634869f3df1461020357806350a9e84214610223578063542afda3146102435780635cf0a411146102585780636ece43dc1461026b5780637e947f24146102ac5780638b4eb61d146102ce5780639a94488a146102ee5780639b24c3c014610330578063a14f10ca14610376578063b000e33414610396578063b466dded146103b6578063bf5bf5f8146103d6578063cce7c90914610422578063d3ddabe614610442578063d9bffbce14610481578063d9eb16db146104a1578063dd85582f146104d1578063e9d1e8ac14610505578063eb7eb95a14610547578063ef2c8cc71461055a575b600080fd5b34801561014257600080fd5b50610156610151366004611b88565b61057a565b60405190151581526020015b60405180910390f35b61017e610179366004611bf0565b6105b2565b604051908152602001610162565b34801561019857600080fd5b5061017e6101a7366004611c62565b6105cd565b3480156101b857600080fd5b5061017e6101c7366004611b88565b6105e2565b3480156101d857600080fd5b506101ec6101e7366004611cb8565b6105f3565b604080519215158352602083019190915201610162565b34801561020f57600080fd5b5061017e61021e366004611cef565b61060d565b34801561022f57600080fd5b5061017e61023e366004611cb8565b610619565b34801561024f57600080fd5b5061017e610626565b61017e610266366004611d1f565b610635565b34801561027757600080fd5b5061028b610286366004611cef565b6108d4565b60408051931515845262ffffff909216602084015290820152606001610162565b3480156102b857600080fd5b506102cc6102c7366004611da3565b610907565b005b3480156102da57600080fd5b5061017e6102e9366004611cef565b61099c565b3480156102fa57600080fd5b5061030e610309366004611cef565b6109a8565b6040805182511515815260209283015162ffffff169281019290925201610162565b34801561033c57600080fd5b5061035061034b366004611cef565b6109f0565b6040805182516001600160f81b0316815260209283015115159281019290925201610162565b34801561038257600080fd5b506102cc610391366004611de6565b610a3e565b3480156103a257600080fd5b506102cc6103b1366004611dff565b610a7d565b3480156103c257600080fd5b506102cc6103d1366004611e3e565b610a93565b3480156103e257600080fd5b5061040a7f00000000000000000000000000000000000076a84fef008cdabe6409d2fe638b81565b6040516001600160a01b039091168152602001610162565b34801561042e57600080fd5b5061015661043d366004611cef565b610aa9565b34801561044e57600080fd5b5061047460405180604001604052806006815260200165076302e302e360d41b81525081565b6040516101629190611ec6565b34801561048d57600080fd5b506102cc61049c366004611cef565b610ab5565b3480156104ad57600080fd5b506104c16104bc366004611cef565b610ace565b6040516101629493929190611ed9565b3480156104dd57600080fd5b5061040a7f000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b81565b34801561051157600080fd5b506104746040518060400160405280601681526020017504d696e7465724d696e50726963654d65726b6c6556360541b81525081565b61017e610555366004611f13565b610b20565b34801561056657600080fd5b506102cc610575366004611dff565b610b3a565b60008061058683610b50565b8054909150610100900460ff16156105a2575460ff1692915050565b6105ab83610b89565b9392505050565b60006105c386868686866000610635565b9695505050505050565b60006105da848484610cff565b949350505050565b60006105ed82610d16565b92915050565b600080610601858585610d50565b91509150935093915050565b60006105ab8383610dad565b60006105da848484610dcd565b6000610630610e00565b905090565b600060026000540361068e5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b600260005561069d8686610e10565b60006106a98787610e41565b9050803410156106f45760405162461bcd60e51b815260206004820152601660248201527526b4b7103b30b63ab2903a379036b4b73a103932b89760511b6044820152606401610685565b336001600160a01b038416156107f55760405163090c9a2d60e41b81523360048201526001600160a01b03858116602483015288811660448301526000917f00000000000000000000000000000000000076a84fef008cdabe6409d2fe638b909116906390c9a2d090606401602060405180830381865afa15801561077d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a19190611f7e565b9050806107f05760405162461bcd60e51b815260206004820152601e60248201527f496e76616c69642064656c65676174652d7661756c742070616972696e6700006044820152606401610685565b849150505b6108028888888885610eb1565b61080d888883610f6b565b6040516117cd60e21b81526001600160a01b038a81166004830152602482018a9052888116604483015282811660648301527f000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b1690615f34906084016020604051808303816000875af1158015610888573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108ac9190611f99565b92506108b88388610f9f565b6108c388838961100c565b505060016000559695505050505050565b6000806000806108e486866110c1565b805460019091015460ff82169861010090920462ffffff16975095509350505050565b61091283833361110c565b61091a610e00565b816001600160f81b0316101561096a5760405162461bcd60e51b81526020600482015260156024820152744f6e6c7920677465206d696e206d696e742066656560581b6044820152606401610685565b61097e8383836001600160f81b0316611151565b61098883836111b0565b156109975761099783836111e0565b505050565b60006105ab838361127b565b60408051808201909152600080825260208201526109c68383611293565b60408051808201909152905460ff811615158252610100900462ffffff1660208201529392505050565b6040805180820190915260008082526020820152610a0e83836112bb565b6040805180820190915290546001600160f81b0381168252600160f81b900460ff16151560208201529392505050565b610a717f000000000000000000000000a2ccfe293bc2cdd78d8166a82d1e18cd2148122b33306350a7886560e11b6112e2565b610a7a81611337565b50565b610a8883833361110c565b61099783838361137e565b610a9e83833361110c565b610997838383611454565b60006105ab8383611509565b610ac082823361110c565b610aca82826111e0565b5050565b6000806060600080610ae087876112bb565b5460408051808201909152600381526208aa8960eb1b602082015260ff600160f81b830416996001600160f81b0390921698509650600095509350505050565b6000610b3133868686866000610635565b95945050505050565b610b4583833361110c565b610997838383611522565b6001600160a01b031660009081527f69f22dd730b53ff235fa96b4306a31bef5dad48fe4bac28b1ceebbce14401b396020526040902090565b6040516000602482018190526044820181905290819060640160408051601f198184030181529181526020820180516001600160e01b0316638639415b60e01b1790525190915060009081906001600160a01b03861690610beb908590611fb2565b600060405180830381855afa9150503d8060008114610c26576040519150601f19603f3d011682016040523d82523d6000602084013e610c2b565b606091505b509150915081610c8b5760405162461bcd60e51b815260206004820152602560248201527f6765745072696d617279526576656e756553706c69747328292063616c6c2066604482015264185a5b195960da1b6064820152608401610685565b805160c0819003610ca25750600095945050505050565b8061010003610cb75750600195945050505050565b60405162461bcd60e51b815260206004820152601e60248201527f556e657870656374656420726576656e75652073706c697420627974657300006044820152606401610685565b60006105da610d0d83610d16565b85908590611612565b6040516001600160601b0319606083901b166020820152600090603401604051602081830303815290604052805190602001209050919050565b6000806000610d5f86866110c1565b90506000610d6c8261165e565b90508015610da3576001600160a01b03851660009081526002830160205260409020546001945080821115610da15780820393505b505b5050935093915050565b600080610dba8484611293565b54610100900462ffffff16949350505050565b600080610dda85856110c1565b6001600160a01b0384166000908152600290910160205260409020549150509392505050565b6000610e0a611687565b54919050565b6000610e1c8383611293565b805490915060ff16156109975760405162461bcd60e51b815260040161068590611fce565b600080610e4e84846112bb565b8054909150600160f81b900460ff16610ea05760405162461bcd60e51b8152602060048201526014602482015273141c9a58d9481b9bdd0818dbdb999a59dd5c995960621b6044820152606401610685565b546001600160f81b03169392505050565b6000610ebd86866110c1565b9050610ecf81600101548585856116ab565b610f125760405162461bcd60e51b815260206004820152601460248201527324b73b30b634b21026b2b935b63290383937b7b360611b6044820152606401610685565b6000610f1d8261165e565b6001600160a01b0384166000908152600284016020526040902054909150811180610f46575080155b610f625760405162461bcd60e51b815260040161068590611fce565b50505050505050565b6000610f7784846110c1565b6001600160a01b03909216600090815260029092016020525060409020805460010190555050565b6000610faa836116c3565b90506000610fb88284611293565b8054909150620f4240850660010190610100900462ffffff1680821115610ff15760405162461bcd60e51b815260040161068590611fce565b80820361100457825460ff191660011783555b505050505050565b341561099757600061101e8334612015565b905080156110b057604051600090339083908381818185875af1925050503d8060008114611068576040519150601f19603f3d011682016040523d82523d6000602084013e61106d565b606091505b50509050806110ae5760405162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b6044820152606401610685565b505b6110bb8484846116d2565b50505050565b60007f3ed708d712dfa866262466688638d160ef9516c1b2c87fe2388dc87cbf5bbf895b6001600160a01b039290921660009081526020928352604080822094825293909252502090565b6111178383836118e7565b6109975760405162461bcd60e51b815260206004820152600b60248201526a13db9b1e48105c9d1a5cdd60aa1b6044820152606401610685565b600061115d84846112bb565b600160f81b6001600160f81b03841617815560405190915082906001600160a01b0385169086907f98e99eae7ab1c56b5b882e049df69b522affbaa1a93afd3be411447a1606027890600090a450505050565b6000806111bd8484611293565b8054909150610100900462ffffff161580156105da57505460ff16159392505050565b6000806111ed8484611970565b9150915060006111fd8585611293565b9050611208826119ee565b815460ff1962ffffff92909216610100029190911663ffffffff19909116178383141781556040516001600160a01b0385169086907f1c3e74a6c6fefbaeee166b031cd2016349c6c863d5188b67c9bbd0b0dcb2e2379061126c9086815260200190565b60405180910390a35050505050565b60008061128884846110c1565b90506105da8161165e565b60007f73e3af67f35f30fb72bbaaf6cc07b987364ed643c93ba201702e213464e877ee6110e5565b60007efc986db700c71340c673de619c46ee721167fc4ea17804bb535c4c196aa7d76110e5565b6112ee84848484611a56565b6110bb5760405162461bcd60e51b815260206004820152601a60248201527913db9b1e48135a5b9d195c919a5b1d195c8810591b5a5b9050d360321b6044820152606401610685565b6000611341611687565b8281556040518381529091507fe3595da100053125f59ccb164c0aadf2b9ad08a2ec6de1e706e0c7ca160416779060200160405180910390a15050565b60008061138b8585611970565b91509150808362ffffff1611156113b45760405162461bcd60e51b815260040161068590612028565b818362ffffff1610156113d95760405162461bcd60e51b815260040161068590612028565b60006113e58686611293565b805462ffffff861685811463ffffffff19909216610100820260ff1916179190911782556040519081529091506001600160a01b0386169087907f1c3e74a6c6fefbaeee166b031cd2016349c6c863d5188b67c9bbd0b0dcb2e2379060200160405180910390a3505050505050565b806114995760405162461bcd60e51b8152602060048201526015602482015274149bdbdd081b5d5cdd081899481c1c9bdd9a591959605a1b6044820152606401610685565b60006114a584846110c1565b6001810183905560408051691b595c9adb19549bdbdd60b21b8152602081018590529192506001600160a01b0385169186917fbd2962ddf019622ba935ab411adde29d756d1db6d628d89b2a174cdf6538b8d591015b60405180910390a350505050565b6000806115168484611293565b5460ff16949350505050565b600061152e84846110c1565b8054600163ffffffff1990911661010062ffffff861602178117825560408051797573654d61784d696e7473506572416464724f7665727269646560301b815260208101929092529192506001600160a01b0385169186917fbb5f9d4f49f83650956b76c40de0f082a06430bf0222e1b6b3f90a7a0f845c4d910160405180910390a360408051766d61784d696e7473506572416464724f7665727269646560481b815262ffffff841660208201526001600160a01b0385169186917f1809fd1f08e6b0df08fd848757688435a0ab8520a51dcca23eba5a3c7f7280d691016114fb565b600081815b84811015611655576116418287878481811061163557611635612059565b90506020020135611ade565b91508061164d8161206f565b915050611617565b50949350505050565b805460009060ff161561167a575054610100900462ffffff1690565b506001919050565b919050565b7f6aab9ffc5cd1e167fd16093ec2dc9fad33c2d2de62ce5b9a1df3152d598e2f9390565b6000610b31856116ba84610d16565b86918691611b0d565b60006105ed620f424083612088565b816000036116df57505050565b60006116ea82611b25565b90506000811561177557604051638639415b60e01b815260048101869052602481018590526001600160a01b03841690638639415b9060440161010060405180830381865afa158015611741573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176591906120aa565b509496506117ee95505050505050565b604051638639415b60e01b815260048101869052602481018590526001600160a01b03841690638639415b9060440160c060405180830381865afa1580156117c1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e59190612132565b50929450505050505b6001600160a01b0381166118445760405162461bcd60e51b815260206004820152601f60248201527f52656e6465722050726f76696465722061646472657373206e6f7420736574006044820152606401610685565b6000816001600160a01b03168560405160006040518083038185875af1925050503d8060008114611891576040519150601f19603f3d011682016040523d82523d6000602084013e611896565b606091505b50509050806110045760405162461bcd60e51b815260206004820152601e60248201527f52656e6465722050726f7669646572207061796d656e74206661696c656400006044820152606401610685565b60405163a47d29cb60e01b8152600481018490526000906001600160a01b0384169063a47d29cb90602401602060405180830381865afa15801561192f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611953919061218d565b6001600160a01b0316826001600160a01b03161490509392505050565b604051630ea5613f60e01b81526004810183905260009081906001600160a01b03841690630ea5613f9060240160c060405180830381865afa1580156119ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119de91906121aa565b5093989297509195505050505050565b600062ffffff821115611a525760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203260448201526534206269747360d01b6064820152608401610685565b5090565b60405163230448b160e01b81526001600160a01b03848116600483015283811660248301526001600160e01b0319831660448301526000919086169063230448b1906064016020604051808303816000875af1158015611aba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b319190611f7e565b6000818310611afa5760008281526020849052604090206105ab565b60008381526020839052604090206105ab565b600082611b1b868685611612565b1495945050505050565b600080611b3183610b50565b8054909150610100900460ff1615611b4d575460ff1692915050565b6000611b5884610b89565b825481151561ffff1990911617610100179092555092915050565b6001600160a01b0381168114610a7a57600080fd5b600060208284031215611b9a57600080fd5b81356105ab81611b73565b60008083601f840112611bb757600080fd5b5081356001600160401b03811115611bce57600080fd5b6020830191508360208260051b8501011115611be957600080fd5b9250929050565b600080600080600060808688031215611c0857600080fd5b8535611c1381611b73565b9450602086013593506040860135611c2a81611b73565b925060608601356001600160401b03811115611c4557600080fd5b611c5188828901611ba5565b969995985093965092949392505050565b600080600060408486031215611c7757600080fd5b83356001600160401b03811115611c8d57600080fd5b611c9986828701611ba5565b9094509250506020840135611cad81611b73565b809150509250925092565b600080600060608486031215611ccd57600080fd5b833592506020840135611cdf81611b73565b91506040840135611cad81611b73565b60008060408385031215611d0257600080fd5b823591506020830135611d1481611b73565b809150509250929050565b60008060008060008060a08789031215611d3857600080fd5b8635611d4381611b73565b9550602087013594506040870135611d5a81611b73565b935060608701356001600160401b03811115611d7557600080fd5b611d8189828a01611ba5565b9094509250506080870135611d9581611b73565b809150509295509295509295565b600080600060608486031215611db857600080fd5b833592506020840135611dca81611b73565b915060408401356001600160f81b0381168114611cad57600080fd5b600060208284031215611df857600080fd5b5035919050565b600080600060608486031215611e1457600080fd5b833592506020840135611e2681611b73565b9150604084013562ffffff81168114611cad57600080fd5b600080600060608486031215611e5357600080fd5b833592506020840135611e6581611b73565b929592945050506040919091013590565b60005b83811015611e91578181015183820152602001611e79565b50506000910152565b60008151808452611eb2816020860160208601611e76565b601f01601f19169290920160200192915050565b6020815260006105ab6020830184611e9a565b8415158152836020820152608060408201526000611efa6080830185611e9a565b905060018060a01b038316606083015295945050505050565b60008060008060608587031215611f2957600080fd5b843593506020850135611f3b81611b73565b925060408501356001600160401b03811115611f5657600080fd5b611f6287828801611ba5565b95989497509550505050565b8051801515811461168257600080fd5b600060208284031215611f9057600080fd5b6105ab82611f6e565b600060208284031215611fab57600080fd5b5051919050565b60008251611fc4818460208701611e76565b9190910192915050565b60208082526017908201527613585e081a5b9d9bd8d85d1a5bdb9cc81c995858da1959604a1b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b818103818111156105ed576105ed611fff565b602080825260179082015276496e76616c6964206d617820696e766f636174696f6e7360481b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60006001820161208157612081611fff565b5060010190565b6000826120a557634e487b7160e01b600052601260045260246000fd5b500490565b600080600080600080600080610100898b0312156120c757600080fd5b8851975060208901516120d981611b73565b60408a015160608b015191985096506120f181611b73565b60808a015160a08b0151919650945061210981611b73565b60c08a015160e08b0151919450925061212181611b73565b809150509295985092959890939650565b60008060008060008060c0878903121561214b57600080fd5b86519550602087015161215d81611b73565b60408801516060890151919650945061217581611b73565b608088015160a08901519194509250611d9581611b73565b60006020828403121561219f57600080fd5b81516105ab81611b73565b60008060008060008060c087890312156121c357600080fd5b86519550602087015194506121da60408801611f6e565b93506121e860608801611f6e565b9250608087015191506121fd60a08801611f6e565b9050929550929550929556fea26469706673582212202b31b533f6b5cfeefdfa0e6f3d0d6896a7c757159450391e69ab87936252211564736f6c63430008130033
Verified Source Code Full Match
Compiler: v0.8.19+commit.7dd6d404
EVM: paris
Optimization: Yes (25 runs)
AuthLib.sol 196 lines
// 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);
}
}
ABHelpers.sol 69 lines
// 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 no way to disable division by zero check in solidity v0.8.24, so not 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) {
unchecked {
// mod returns remainder, which is the token number
// @dev no way to disable mod zero check in solidity, unchecked to optimize gas for addition
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;
}
}
IAdminACLV0.sol 51 lines
// 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);
}
IMinterBaseV0.sol 20 lines
// 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);
}
Math.sol 226 lines
// 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;
}
}
ICoreRegistryV1.sol 25 lines
// 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);
}
IMinterFilterV1.sol 184 lines
// 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);
}
ISharedMinterV0.sol 49 lines
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {ISharedMinterRequired} from "./ISharedMinterRequired.sol";
/**
* @title ISharedMinterV0
* @notice This interface extends the minimum required interface for a shared
* minter contract to add additional functionality that is generally available
* for all shared minter contracts on the shared minter filter.
* @dev Custom, one-off minter contracts that are not globally approved may
* choose to not implement this interface, but should still implement the
* ISharedMinterRequired interface.
*/
interface ISharedMinterV0 is ISharedMinterRequired {
// 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;
// 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
);
}
MerkleLib.sol 388 lines
// 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_)
});
}
}
ISplitProviderV0.sol 49 lines
// SPDX-License-Identifier: LGPL-3.0-only
// Creatd By: Art Blocks Inc.
pragma solidity ^0.8.0;
import {ISplitFactoryV2} from "./integration-refs/splits-0x-v2/ISplitFactoryV2.sol";
interface ISplitProviderV0 {
/**
* @notice SplitInputs struct defines the inputs for requested splitters.
* It is defined in a way easily communicated from the Art Blocks GenArt721V3 contract,
* to allow for easy integration and minimal additional bytecode in the GenArt721V3 contract.
*/
struct SplitInputs {
address platformProviderSecondarySalesAddress;
uint16 platformProviderSecondarySalesBPS;
address renderProviderSecondarySalesAddress;
uint16 renderProviderSecondarySalesBPS;
uint8 artistTotalRoyaltyPercentage;
address artist;
address additionalPayee;
uint8 additionalPayeePercentage;
}
/**
* @notice Emitted when a new splitter contract is created.
* @param splitter address of the splitter contract
*/
event SplitterCreated(address indexed splitter);
/**
* @notice Gets or creates an immutable splitter contract at a deterministic address.
* Splits in the splitter contract are determined by the input split parameters,
* so we can safely create the splitter contract at a deterministic address (or use
* the existing splitter contract if it already exists at that address).
* @dev Uses the 0xSplits v2 implementation to create a splitter contract
* @param splitInputs The split input parameters.
* @return splitter The newly created splitter contract address.
*/
function getOrCreateSplitter(
SplitInputs calldata splitInputs
) external returns (address);
/**
* @notice Indicates the type of the contract, e.g. `SplitProviderV0`.
* @return type_ The type of the contract.
*/
function type_() external pure returns (bytes32);
}
IEngineRegistryV0.sol 38 lines
// 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;
}
IFilteredMinterV0.sol 74 lines
// 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
);
}
IFilteredMinterV1.sol 127 lines
// 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.
*/
}
IFilteredMinterV2.sol 30 lines
// 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;
}
MinPriceLib.sol 64 lines
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
/**
* @title Art Blocks Min Price Library
* @notice This library is designed for the Art Blocks platform. It includes
* events to assist with minters that implement a minimum mint fee.
* @author Art Blocks Inc.
*/
library MinPriceLib {
/**
* @notice Min mint fee, in wei, was updated to be the
* provided value.
* @param minMintFee Min mint fee, in wei
*/
event MinMintFeeUpdated(uint256 minMintFee);
// position of MinPrice Lib storage, using a diamond storage pattern
// for this library
bytes32 constant MIN_PRICE_LIB_STORAGE_POSITION =
keccak256("minpricelib.storage");
// Diamond storage pattern is used in this library
struct MinPriceLibStorage {
uint256 minMintFee;
}
/**
* @notice Update the minimum mint fee for the minter, and emit
* an event.
* @param newMinMintFee New minimum mint fee, in wei
*/
function updateMinMintFee(uint256 newMinMintFee) internal {
MinPriceLibStorage storage minPriceLibStorage_ = s();
minPriceLibStorage_.minMintFee = newMinMintFee;
emit MinMintFeeUpdated(newMinMintFee);
}
/**
* @notice Loads the min mint fee from storage.
*/
function getMinMintFee() internal view returns (uint256 minMintFee) {
return s().minMintFee;
}
/**
* @notice Return the storage struct for reading and writing. This library
* uses a diamond storage pattern when managing storage.
* @return storageStruct The MinPriceLibStorage struct.
*/
function s()
internal
pure
returns (MinPriceLibStorage storage storageStruct)
{
bytes32 position = MIN_PRICE_LIB_STORAGE_POSITION;
assembly ("memory-safe") {
storageStruct.slot := position
}
}
}
SetPriceLib.sol 162 lines
// 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
}
}
}
IERC20.sol 82 lines
// 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);
}
SafeCast.sol 1135 lines
// 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);
}
}
IDelegationRegistry.sol 221 lines
// 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);
}
SplitFundsLib.sol 854 lines
// 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 pays ETH funds to sender (if refund), with all of token price
* being sent to the render provider 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 sendAllToRenderProviderETHRefundSender(
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");
}
// send remaining to render provider
sendAllToRenderProviderETHNoRefund({
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 Sends all revenue generated by project `projectId` to render
* provider.
* 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 payment addresses.
* @param projectId Project ID for which funds shall be sent.
* @param valueInWei Value to be sent, in Wei.
* @param coreContract Address of the GenArt721CoreContract
* associated with the project.
*/
function sendAllToRenderProviderETHNoRefund(
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);
address payable renderProviderAddress;
if (isEngine_) {
// get engine splits
(
,
renderProviderAddress,
,
,
,
,
,
) = IGenArt721CoreContractV3_Engine(coreContract)
.getPrimaryRevenueSplits({
_projectId: projectId,
_price: valueInWei
});
} else {
// get flagship splits
// @dev note that platformProviderAddress and
// platformProviderRevenue remain 0 for flagship
(
,
// artblocks revenue
renderProviderAddress, // artblocks address
,
,
,
) = IGenArt721CoreContractV3(coreContract).getPrimaryRevenueSplits({
_projectId: projectId,
_price: valueInWei
});
}
require(
renderProviderAddress != address(0),
"Render Provider address not set"
);
// distribute revenue
// Render Provider / Art Blocks payment
// @dev previous conditional ensures valueInWei is non-zero
(bool success, ) = renderProviderAddress.call{value: valueInWei}("");
require(success, "Render Provider payment failed");
}
/**
* @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 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: 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 currency address for the referenced SplitFundsProjectConfig.
* @return currencySymbol currency symbol for the referenced SplitFundsProjectConfig.
*/
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 Balance of ERC-20
*/
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 Allowance of ERC-20
*/
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
}
}
}
ISharedMinterMerkleV0.sol 34 lines
// 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;
}
ISharedMinterRequired.sol 26 lines
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
/**
* @title ISharedMinterRequired
* @notice This interface contains the minimum required interface for a shared
* minter contract. All custom, one-off minter contracts should implement this
* interface.
*/
interface ISharedMinterRequired {
/**
* @notice Returns the minter's type, used by the minter filter for metadata
* purposes.
* @return The minter type.
*/
function minterType() external view returns (string memory);
/**
* @notice Returns the minter's associated shared minter filter address.
* @dev used by subgraph indexing service for entity relation purposes.
* @return The minter filter address.
*/
function minterFilterAddress() external returns (address);
}
ISharedMinterMinPriceV0.sol 22 lines
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
/**
* @title This interface adds support for minimum price minting.
* @author Art Blocks Inc.
*/
interface ISharedMinterMinPriceV0 {
/**
* @notice Update the default minimum mint fee for the minter.
* @param newDefaultMinMintFee New default minimum mint fee, in wei
*/
function updateDefaultMinMintFee(uint256 newDefaultMinMintFee) external;
/**
* @notice Get the default mint fee for the minter.
* @return defaultMinMintFee Default minimum mint fee, in wei
*/
function defaultMinMintFee() external view returns (uint256);
}
MaxInvocationsLib.sol 489 lines
// 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.
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.
* @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 invocationsRemainOnCore(
uint256 projectId,
address coreContract
) internal view returns (bool) {
return
getInvocationsAvailable({
projectId: projectId,
coreContract: coreContract
}) != 0;
}
/**
* @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) {
return
getInvocationsAvailable({
projectId: projectId,
coreContract: coreContract
}) == 0;
}
/**
* @notice Function returns the number of invocations available for a given
* project. Function checks the core contract's invocations and minter's max
* invocations, ensuring that the most limiting value is used, even if the
* local minter max invocations is stale.
* @param projectId The id of the project.
* @param coreContract The address of the core contract.
* @return Number of invocations available for the project.
*/
function getInvocationsAvailable(
uint256 projectId,
address coreContract
) internal view returns (uint256) {
// get max invocations from core contract
(
uint256 coreInvocations,
uint256 coreMaxInvocations
) = coreContractInvocationData({
projectId: projectId,
coreContract: coreContract
});
MaxInvocationsProjectConfig
storage maxInvocationsProjectConfig = getMaxInvocationsProjectConfig({
projectId: projectId,
coreContract: coreContract
});
uint256 limitingMaxInvocations = Math.min(
coreMaxInvocations,
maxInvocationsProjectConfig.maxInvocations // local max invocations
);
// if core invocations are greater than the limiting max invocations,
// return 0 since no invocations remain
if (coreInvocations >= limitingMaxInvocations) {
return 0;
}
// otherwise, return the number of invocations remaining
// @dev will not undeflow due to previous check
return limitingMaxInvocations - coreInvocations;
}
/**
* @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
}
}
}
ReentrancyGuard.sol 63 lines
// 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;
}
}
IGenArt721CoreContractV3.sol 79 lines
// 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
);
}
MinterMinPriceMerkleV0.sol 765 lines
// 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 {ISharedMinterMinPriceV0} from "../../interfaces/v0.8.x/ISharedMinterMinPriceV0.sol";
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 {MinPriceLib} from "../../libs/v0.8.x/minter-libs/MinPriceLib.sol";
import {ReentrancyGuard} from "@openzeppelin-4.5/contracts/security/ReentrancyGuard.sol";
/**
* @title Shared, filtered Minter contract that allows tokens to be minted with
* a minimum fee in 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 the minter filter's admin ACL:
* - updateMinMintFee
* ----------------------------------------------------------------------------
* 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 MinterMinPriceMerkleV0 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 = "MinterMinPriceMerkleV0";
/// minter version for this minter
string public constant minterVersion = "v0.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.
* @param minMintFee_ Min mint fee for this minter, applied to all
* and-on projects that use this minter.
*/
constructor(
address minterFilter,
address delegationRegistryAddress_,
uint256 minMintFee_
) ReentrancyGuard() {
minterFilterAddress = minterFilter;
_minterFilter = IMinterFilterV1(minterFilter);
delegationRegistryAddress = delegationRegistryAddress_;
_delegationRegistryContract = IDelegationRegistry(
delegationRegistryAddress_
);
MinPriceLib.updateMinMintFee({newMinMintFee: minMintFee_});
emit MerkleLib.DelegationRegistryUpdated(delegationRegistryAddress_);
// broadcast default max invocations per address for this minter
emit MerkleLib.DefaultMaxInvocationsPerAddress(
MerkleLib.DEFAULT_MAX_INVOCATIONS_PER_ADDRESS
);
}
/**
* @notice Updates the min mint fee for this minter.
* This function is restricted to the minter filter's admin.
* This function is intended to be used to update the min mint fee
* for this minter, which is applied to all and-on projects that use this
* minter. This function does not update the price per token for existing
* projects that have already been initialized.
* @param newMinMintFee New min mint fee for this minter.
*/
function updateMinMintFee(uint256 newMinMintFee) external {
// CHECKS
AuthLib.onlyMinterFilterAdminACL({
minterFilterAddress: minterFilterAddress,
sender: msg.sender,
contract_: address(this),
selector: this.updateMinMintFee.selector
});
// EFFECTS
MinPriceLib.updateMinMintFee({newMinMintFee: newMinMintFee});
// @dev this update applies to an-on projects to call `updatePricePerTokenInWei`
// on this minter. There is no need to sync this value to existing projects.
}
/**
* @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.
* Reverts if price per token is less than the min mint fee.
* @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.
* Must be greater than or equal to the min mint fee.
*/
function updatePricePerTokenInWei(
uint256 projectId,
address coreContract,
uint248 pricePerTokenInWei
) external {
AuthLib.onlyArtist({
projectId: projectId,
coreContract: coreContract,
sender: msg.sender
});
// require configured price gte min mint fee
// @dev intentionally support price > min mint fee, to handle cases
// such as opt-in premium off-chain services
require(
pricePerTokenInWei >= MinPriceLib.getMinMintFee(),
"Only gte min mint fee"
);
// price per token is the input pricePerTokenInWei
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 Returns the min mint fee for this minter, applied to all
* and-on projects.
* @return minMintFee Min mint fee, in wei
*/
function minMintFee() external view returns (uint256) {
return MinPriceLib.getMinMintFee();
}
/**
* @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.sendAllToRenderProviderETHRefundSender({
projectId: projectId,
pricePerTokenInWei: pricePerTokenInWei,
coreContract: coreContract
});
return tokenId;
}
}
GenericMinterEventsLib.sol 171 lines
// 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. Removed key `key`
* for project `projectId`.
* @param projectId Project ID key was removed for
* @param coreContract Core contract address that projectId is on
* @param key Key removed
*/
event ConfigKeyRemoved(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key
);
/// BOOL
/**
* @notice Generic project minter configuration event. Value of key
* `key` was set to `value` for project `projectId`.
* @param projectId Project ID key was set for
* @param coreContract Core contract address that projectId is on
* @param key Key set
* @param value Value key was set to
*/
event ConfigValueSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
bool value
);
/// UINT256
/**
* @notice Generic project minter configuration event. Value of key
* `key` was set to `value` for project `projectId`.
* @param projectId Project ID key was set for
* @param coreContract Core contract address that projectId is on
* @param key Key set
* @param value Value key was set to
*/
event ConfigValueSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
uint256 value
);
/**
* @notice Generic project minter configuration event. Added value `value`
* to the set of uint256 at key `key` for project `projectId`.
* @param projectId Project ID key was set for
* @param coreContract Core contract address that projectId is on
* @param key Key modified
* @param value Value added to the key's set
*/
event ConfigValueAddedToSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
uint256 value
);
/**
* @notice Generic project minter configuration event. Removed value
* `value` to the set of uint256 at key `key` for project `projectId`.
* @param projectId Project ID key was set for
* @param coreContract Core contract address that projectId is on
* @param key Key modified
* @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. Value of key
* `key` was set to `value` for project `projectId`.
* @param projectId Project ID key was set for
* @param coreContract Core contract address that projectId is on
* @param key Key set
* @param value Value key was set to
*/
event ConfigValueSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
address value
);
/**
* @notice Generic project minter configuration event. Added value `value`
* to the set of addresses at key `key` for project `projectId`.
* @param projectId Project ID key was set for
* @param coreContract Core contract address that projectId is on
* @param key Key modified
* @param value Value added to the key's set
*/
event ConfigValueAddedToSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
address value
);
/**
* @notice Generic project minter configuration event. Removed value
* `value` to the set of addresses at key `key` for project `projectId`.
* @param projectId Project ID key was set for
* @param coreContract Core contract address that projectId is on
* @param key Key modified
* @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. Value of key
* `key` was set to `value` for project `projectId`.
* @param projectId Project ID key was set for
* @param coreContract Core contract address that projectId is on
* @param key Key set
* @param value Value key was set to
*/
event ConfigValueSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
bytes32 value
);
/**
* @notice Generic project minter configuration event. Added value `value`
* to the set of bytes32 at key `key` for project `projectId`.
* @param projectId Project ID key was set for
* @param coreContract Core contract address that projectId is on
* @param key Key modified
* @param value Value added to the key's set
*/
event ConfigValueAddedToSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
bytes32 value
);
/**
* @notice Generic project minter configuration event. Removed value
* `value` to the set of bytes32 at key `key` for project `projectId`.
* @param projectId Project ID key was set for
* @param coreContract Core contract address that projectId is on
* @param key Key modified
* @param value Value removed from the key's set
*/
event ConfigValueRemovedFromSet(
uint256 indexed projectId,
address indexed coreContract,
bytes32 key,
bytes32 value
);
}
IGenArt721CoreContractV3_Base.sol 283 lines
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IAdminACLV0.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 {
// This interface emits generic events that contain fields that indicate
// which parameter has been updated. This is sufficient for application
// state management, while also simplifying the contract and indexing code.
// This was done as an alternative to having custom events that emit what
// field-values have changed for each event, given that changed values can
// be introspected by indexers due to the design of this smart contract
// exposing these state changes via publicly viewable fields.
/**
* @notice Event emitted when the Art Blocks Curation Registry contract is updated.
* @dev only utilized by subset of V3 core contracts (Art Blocks Curated contracts)
* @param artblocksCurationRegistryAddress Address of Art Blocks Curation Registry contract.
*/
event ArtBlocksCurationRegistryContractUpdated(
address indexed artblocksCurationRegistryAddress
);
/**
* @notice Project's royalty splitter was updated to `_splitter`.
* @dev New event in v3.2
* @param projectId The project ID.
* @param royaltySplitter The new splitter address to receive royalties.
*/
event ProjectRoyaltySplitterUpdated(
uint256 indexed projectId,
address indexed royaltySplitter
);
// The following fields are used to indicate which contract-level parameter
// has been updated in the `PlatformUpdated` event:
// @dev only append to the end of this enum in the case of future updates
enum PlatformUpdatedFields {
FIELD_NEXT_PROJECT_ID, // 0
FIELD_NEW_PROJECTS_FORBIDDEN, // 1
FIELD_DEFAULT_BASE_URI, // 2
FIELD_RANDOMIZER_ADDRESS, // 3
FIELD_NEXT_CORE_CONTRACT, // 4
FIELD_ARTBLOCKS_DEPENDENCY_REGISTRY_ADDRESS, // 5
FIELD_ARTBLOCKS_ON_CHAIN_GENERATOR_ADDRESS, // 6
FIELD_PROVIDER_SALES_ADDRESSES, // 7
FIELD_PROVIDER_PRIMARY_SALES_PERCENTAGES, // 8
FIELD_PROVIDER_SECONDARY_SALES_BPS, // 9
FIELD_SPLIT_PROVIDER, // 10
FIELD_BYTECODE_STORAGE_READER // 11
}
// The following fields are used to indicate which project-level parameter
// has been updated in the `ProjectUpdated` event:
// @dev only append to the end of this enum in the case of future updates
enum ProjectUpdatedFields {
FIELD_PROJECT_COMPLETED, // 0
FIELD_PROJECT_ACTIVE, // 1
FIELD_PROJECT_ARTIST_ADDRESS, // 2
FIELD_PROJECT_PAUSED, // 3
FIELD_PROJECT_CREATED, // 4
FIELD_PROJECT_NAME, // 5
FIELD_PROJECT_ARTIST_NAME, // 6
FIELD_PROJECT_SECONDARY_MARKET_ROYALTY_PERCENTAGE, // 7
FIELD_PROJECT_DESCRIPTION, // 8
FIELD_PROJECT_WEBSITE, // 9
FIELD_PROJECT_LICENSE, // 10
FIELD_PROJECT_MAX_INVOCATIONS, // 11
FIELD_PROJECT_SCRIPT, // 12
FIELD_PROJECT_SCRIPT_TYPE, // 13
FIELD_PROJECT_ASPECT_RATIO, // 14
FIELD_PROJECT_BASE_URI, // 15
FIELD_PROJECT_PROVIDER_SECONDARY_FINANCIALS // 16
}
/**
* @notice Error codes for the GenArt721 contract. Used by the GenArt721Error
* custom error.
* @dev only append to the end of this enum in the case of future updates
*/
enum ErrorCodes {
OnlyNonZeroAddress, // 0
OnlyNonEmptyString, // 1
OnlyNonEmptyBytes, // 2
TokenDoesNotExist, // 3
ProjectDoesNotExist, // 4
OnlyUnlockedProjects, // 5
OnlyAdminACL, // 6
OnlyArtist, // 7
OnlyArtistOrAdminACL, // 8
OnlyAdminACLOrRenouncedArtist, // 9
OnlyMinterContract, // 10
MaxInvocationsReached, // 11
ProjectMustExistAndBeActive, // 12
PurchasesPaused, // 13
OnlyRandomizer, // 14
TokenHashAlreadySet, // 15
NoZeroHashSeed, // 16
OverMaxSumOfPercentages, // 17
IndexOutOfBounds, // 18
OverMaxSumOfBPS, // 19
MaxOf100Percent, // 20
PrimaryPayeeIsZeroAddress, // 21
SecondaryPayeeIsZeroAddress, // 22
MustMatchArtistProposal, // 23
NewProjectsForbidden, // 24
NewProjectsAlreadyForbidden, // 25
OnlyArtistOrAdminIfLocked, // 26
OverMaxSecondaryRoyaltyPercentage, // 27
OnlyMaxInvocationsDecrease, // 28
OnlyGteInvocations, // 29
ScriptIdOutOfRange, // 30
NoScriptsToRemove, // 31
ScriptTypeAndVersionFormat, // 32
AspectRatioTooLong, // 33
AspectRatioNoNumbers, // 34
AspectRatioImproperFormat, // 35
OnlyNullPlatformProvider, // 36
ContractInitialized // 37
}
/**
* @notice Emits an error code `_errorCode` in the GenArt721Error event.
* @dev Emitting error codes instead of error strings saves significant
* contract bytecode size, allowing for more contract functionality within
* the 24KB contract size limit.
* @param _errorCode The error code to emit. See ErrorCodes enum.
*/
error GenArt721Error(ErrorCodes _errorCode);
/**
* @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 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);
}
MerkleProof.sol 212 lines
// 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)
}
}
}
IGenArt721CoreContractV3_Engine.sol 150 lines
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IAdminACLV0.sol";
import "./IGenArt721CoreContractV3_Base.sol";
import "./ISplitProviderV0.sol";
/**
* @notice Struct representing Engine contract configuration.
* @param tokenName Name of token.
* @param tokenSymbol Token symbol.
* @param renderProviderAddress address to send render provider revenue to
* @param randomizerContract Randomizer contract.
* @param splitProviderAddress Address to use as royalty splitter provider for the contract.
* @param minterFilterAddress Address of the Minter Filter to set as the Minter
* on the contract.
* @param startingProjectId The initial next project ID.
* @param autoApproveArtistSplitProposals Whether or not to always
* auto-approve proposed artist split updates.
* @param nullPlatformProvider Enforce always setting zero platform provider fees and addresses.
* @param allowArtistProjectActivation Allow artist to activate their own projects.
* @dev _startingProjectId should be set to a value much, much less than
* max(uint248), but an explicit input type of `uint248` is used as it is
* safer to cast up to `uint256` than it is to cast down for the purposes
* of setting `_nextProjectId`.
*/
struct EngineConfiguration {
string tokenName;
string tokenSymbol;
address renderProviderAddress;
address platformProviderAddress;
address newSuperAdminAddress;
address randomizerContract;
address splitProviderAddress;
address minterFilterAddress;
uint248 startingProjectId;
bool autoApproveArtistSplitProposals;
bool nullPlatformProvider;
bool allowArtistProjectActivation;
}
interface IGenArt721CoreContractV3_Engine is IGenArt721CoreContractV3_Base {
// @dev new function in V3.2
/**
* @notice Initializes the contract with the provided `engineConfiguration`.
* This function should be called atomically, immediately after deployment.
* Only callable once. Validation on `engineConfiguration` is performed by caller.
* @param engineConfiguration EngineConfiguration to configure the contract with.
* @param adminACLContract_ Address of admin access control contract, to be
* set as contract owner.
* @param defaultBaseURIHost Base URI prefix to initialize default base URI with.
* @param bytecodeStorageReaderContract_ Address of the bytecode storage reader contract.
*/
function initialize(
EngineConfiguration calldata engineConfiguration,
address adminACLContract_,
string memory defaultBaseURIHost,
address bytecodeStorageReaderContract_
) external;
// @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);
/** @notice The default render provider payment address for all secondary sales royalty
* revenues, for all new projects. Individual project payment info is defined
* in each project's ProjectFinance struct.
* @return The default render provider payment address for secondary sales royalties.
*/
function defaultRenderProviderSecondarySalesAddress()
external
view
returns (address payable);
/** @notice The default platform provider payment address for all secondary sales royalty
* revenues, for all new projects. Individual project payment info is defined
* in each project's ProjectFinance struct.
* @return The default platform provider payment address for secondary sales royalties.
*/
function defaultPlatformProviderSecondarySalesAddress()
external
view
returns (address payable);
/** @notice The default render provider payment basis points for all secondary sales royalty
* revenues, for all new projects. Individual project payment info is defined
* in each project's ProjectFinance struct.
* @return The default render provider payment basis points for secondary sales royalties.
*/
function defaultRenderProviderSecondarySalesBPS()
external
view
returns (uint256);
/** @notice The default platform provider payment basis points for all secondary sales royalty
* revenues, for all new projects. Individual project payment info is defined
* in each project's ProjectFinance struct.
* @return The default platform provider payment basis points for secondary sales royalties.
*/
function defaultPlatformProviderSecondarySalesBPS()
external
view
returns (uint256);
/**
* @notice The address of the current split provider being used by the contract.
* @return The address of the current split provider.
*/
function splitProvider() external view returns (ISplitProviderV0);
}
ISplitFactoryV2.sol 62 lines
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc. to support the 0xSplits V2 integration
// Sourced from:
// - https://github.com/0xSplits/splits-contracts-monorepo/blob/main/packages/splits-v2/src/libraries/SplitV2.sol
// - https://github.com/0xSplits/splits-contracts-monorepo/blob/main/packages/splits-v2/src/splitters/SplitFactoryV2.sol
pragma solidity ^0.8.0;
interface ISplitFactoryV2 {
/* -------------------------------------------------------------------------- */
/* STRUCTS */
/* -------------------------------------------------------------------------- */
/**
* @notice Split struct
* @dev This struct is used to store the split information.
* @dev There are no hard caps on the number of recipients/totalAllocation/allocation unit. Thus the chain and its
* gas limits will dictate these hard caps. Please double check if the split you are creating can be distributed on
* the chain.
* @param recipients The recipients of the split.
* @param allocations The allocations of the split.
* @param totalAllocation The total allocation of the split.
* @param distributionIncentive The incentive for distribution. Limits max incentive to 6.5%.
*/
struct Split {
address[] recipients;
uint256[] allocations;
uint256 totalAllocation;
uint16 distributionIncentive;
}
/* -------------------------------------------------------------------------- */
/* FUNCTIONS */
/* -------------------------------------------------------------------------- */
/**
* @notice Create a new split with params and owner.
* @param _splitParams Params to create split with.
* @param _owner Owner of created split.
* @param _creator Creator of created split.
* @param _salt Salt for create2.
* @return split Address of the created split.
*/
function createSplitDeterministic(
Split calldata _splitParams,
address _owner,
address _creator,
bytes32 _salt
) external returns (address split);
/**
* @notice Predict the address of a new split and check if it is deployed.
* @param _splitParams Params to create split with.
* @param _owner Owner of created split.
* @param _salt Salt for create2.
*/
function isDeployed(
Split calldata _splitParams,
address _owner,
bytes32 _salt
) external view returns (address split, bool exists);
}
Read Contract
delegationRegistryAddress 0xbf5bf5f8 → address
getPriceInfo 0xd9eb16db → bool, uint256, string, address
hashAddress 0x3aa5fe59 → bytes32
isEngineView 0x01000da7 → bool
maxInvocationsProjectConfig 0x9a94488a → tuple
merkleProjectConfig 0x6ece43dc → bool, uint24, bytes32
minMintFee 0x542afda3 → uint256
minterFilterAddress 0xdd85582f → address
minterType 0xe9d1e8ac → string
minterVersion 0xd3ddabe6 → string
processProofForAddress 0x1ec2e523 → bytes32
projectMaxHasBeenInvoked 0xcce7c909 → bool
projectMaxInvocations 0x4869f3df → uint256
projectMaxInvocationsPerAddress 0x8b4eb61d → uint256
projectRemainingInvocationsForAddress 0x40e5fa3b → bool, uint256
projectUserMintInvocations 0x50a9e842 → uint256
setPriceProjectConfig 0x9b24c3c0 → tuple
Write Contract 9 functions
These functions modify contract state and require a wallet transaction to execute.
manuallyLimitProjectMaxInvocations 0xb000e334
uint256 projectId
address coreContract
uint24 maxInvocations
purchase 0xeb7eb95a
uint256 projectId
address coreContract
bytes32[] proof
returns: uint256
purchaseTo 0x1467b1f7
address to
uint256 projectId
address coreContract
bytes32[] proof
returns: uint256
purchaseTo 0x5cf0a411
address to
uint256 projectId
address coreContract
bytes32[] proof
address vault
returns: uint256
setProjectInvocationsPerAddress 0xef2c8cc7
uint256 projectId
address coreContract
uint24 maxInvocationsPerAddress
syncProjectMaxInvocationsToCore 0xd9bffbce
uint256 projectId
address coreContract
updateMerkleRoot 0xb466dded
uint256 projectId
address coreContract
bytes32 root
updateMinMintFee 0xa14f10ca
uint256 newMinMintFee
updatePricePerTokenInWei 0x7e947f24
uint256 projectId
address coreContract
uint248 pricePerTokenInWei
Recent Transactions
No transactions found for this address