Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x660C56a3f42D9527324af8A51CBe78E8f2Db17aE
Balance 0.000000000 ETH
Nonce 1
Code Size 17583 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

17583 bytes
0x6080604052600436101561001257600080fd5b60003560e01c806301ffc9a71461018257806311a003271461017d578063150b7a021461017857806317a288df146101735780631efeff841461016e5780632b1708b0146101695780632ede65df1461016457806332689eb81461015f5780633596188d1461015a5780635333a0a4146101555780635e9e78d014610150578063715018a61461014b57806374268ff2146101465780637fea43b814610141578063854cff2f1461013c5780638a72ea6a146101375780638b4c042a146101325780638da5cb5b1461012d578063a85c38ef14610128578063c402b96514610123578063e74b981b1461011e578063f23a6e61146101195763f2fde38b0361018757610cce565b610caf565b610bab565b610b94565b610b21565b6109b0565b610997565b610966565b61083c565b6107f9565b6106f5565b6106dd565b6106b9565b61069d565b610621565b61060d565b6105cd565b6105b9565b610570565b610534565b610318565b610235565b6101dd565b600080fd5b7fffffffff0000000000000000000000000000000000000000000000000000000081165b0361018757565b905035906101c48261018c565b565b90602082820312610187576101da916101b7565b90565b346101875761020a6101f86101f33660046101c6565b613146565b60405191829182901515815260200190565b0390f35b806101b0565b905035906101c48261020e565b90602082820312610187576101da91610214565b346101875761024d610248366004610221565b6111b8565b604051005b73ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff81166101b0565b905035906101c48261026b565b909182601f830112156101875781359167ffffffffffffffff831161018757602001926001830284011161018757565b90608082820312610187576102d98183610287565b926102e78260208501610287565b926102f58360408301610214565b92606082013567ffffffffffffffff8111610187576103149201610294565b9091565b346101875761020a61033761032e3660046102c4565b93929092612825565b604051918291827fffffffff00000000000000000000000000000000000000000000000000000000909116815260200190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810190811067ffffffffffffffff8211176103d957604052565b61036a565b906101c46103eb60405190565b9283610399565b67ffffffffffffffff81116103d95760208091020190565b9092919261041f61041a826103f2565b6103de565b938185526020808601920283019281841161018757915b8383106104435750505050565b602080916104518486610214565b815201920191610436565b9080601f83011215610187578160206101da9335910161040a565b9092919261048761041a826103f2565b938185526020808601920283019281841161018757915b8383106104ab5750505050565b602080916104b98486610287565b81520192019161049e565b9080601f83011215610187578160206101da93359101610477565b91606083830312610187576104f48284610214565b92602081013567ffffffffffffffff8111610187578361051591830161045c565b92604082013567ffffffffffffffff8111610187576101da92016104c4565b346101875761024d6105473660046104df565b91611634565b9190604083820312610187576101da906105678185610214565b93602001610214565b346101875761024d61058336600461054d565b906113f6565b9091606082840312610187576101da6105a28484610214565b936105b08160208601610214565b93604001610214565b61024d6105c7366004610589565b91611baf565b346101875761024d6105e0366004610589565b91610d2f565b9091606082840312610187576101da6105ff8484610214565b936105b08160208601610287565b61024d61061b3660046105e6565b91611fa5565b346101875761024d610634366004610589565b916130df565b906080828203126101875761064f8183610214565b9261065d8260208501610214565b92604081013567ffffffffffffffff8111610187578361067e91830161045c565b92606082013567ffffffffffffffff8111610187576101da92016104c4565b346101875761024d6106b036600461063a565b92919091611dc6565b346101875761024d6106cc36600461054d565b906112a7565b600091031261018757565b34610187576106ed3660046106d2565b61024d613250565b346101875761024d610708366004610221565b611abf565b9091606082840312610187576101da6105a28484610287565b0190565b9061074a610743610739845190565b8084529260200190565b9260200190565b9060005b81811061075b5750505090565b90919261078e610787600192865173ffffffffffffffffffffffffffffffffffffffff16815260200190565b9460200190565b92910161074e565b906107a5610743610739845190565b9060005b8181106107b65750505090565b9091926107cc6107876001928651815260200190565b9291016107a9565b60408082526101da9391926107eb9184019061072a565b916020818403910152610796565b346101875761081261080c36600461070d565b916138e4565b9061020a61081f60405190565b928392836107d4565b90602082820312610187576101da91610287565b346101875761024d61084f366004610828565b61308d565b6101da6101da6101da9290565b9061086b90610854565b600052602052604060002090565b6101da9081565b6101da9054610879565b61089590600b610861565b61089e81610880565b916108ab60018301610880565b916108b860028201610880565b916101da60046108df600385015473ffffffffffffffffffffffffffffffffffffffff1690565b93015473ffffffffffffffffffffffffffffffffffffffff1690565b909594926101c49461092d61094a9261092660809661091f60a088019c6000890152565b6020870152565b6040850152565b73ffffffffffffffffffffffffffffffffffffffff166060830152565b019073ffffffffffffffffffffffffffffffffffffffff169052565b346101875761020a61098161097c366004610221565b61088a565b9161098e95939560405190565b958695866108fb565b346101875761024d6109aa366004610589565b91611d04565b34610187576109c03660046106d2565b61020a6109e260005473ffffffffffffffffffffffffffffffffffffffff1690565b6040519182918273ffffffffffffffffffffffffffffffffffffffff909116815260200190565b90610a18610743610739845190565b9060005b818110610a295750505090565b909192610a55610787600192865173ffffffffffffffffffffffffffffffffffffffff16815260200190565b929101610a1c565b805182526101da9160e0610aff6101008301610a7e60208601516020860152565b610a8d60408601516040860152565b60608581015173ffffffffffffffffffffffffffffffffffffffff169085015260808581015173ffffffffffffffffffffffffffffffffffffffff169085015260a08581015173ffffffffffffffffffffffffffffffffffffffff169085015260c085015184820360c0860152610796565b9201519060e0818403910152610a09565b60208082526101da92910190610a5d565b346101875761020a610b3c610b37366004610221565b61312f565b60405191829182610b10565b919060a08382031261018757610b5e8184610287565b92610b6c8260208301610214565b926101da610b7d8460408501610214565b93610b8b8160608601610287565b93608001610214565b61024d610ba2366004610b48565b939290926119e7565b346101875761024d610bbe366004610828565b613053565b67ffffffffffffffff81116103d957602090601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b90826000939282370152565b90929192610c1a61041a82610bc3565b93818552602085019082840111610187576101c492610bfe565b9080601f83011215610187578160206101da93359101610c0a565b91909160a08184031261018757610c668382610287565b92610c748160208401610287565b92610c828260408501610214565b92610c908360608301610214565b92608082013567ffffffffffffffff8111610187576101da9201610c34565b346101875761020a610337610cc5366004610c4f565b9392909261227e565b346101875761024d610ce1366004610828565b6132b0565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff905b9181191691161790565b90610d246101da610d2b92610854565b8254610ce6565b9055565b610d496101c493610d42600193826113f6565b600a610861565b01610d14565b90610d6a610d5e610739845490565b92600052602060002090565b9060005b818110610d7b5750505090565b909192610d9f610d98600192610d9087610880565b815260200190565b9460010190565b929101610d6e565b906101da91610d4f565b906101c4610dcb92610dc260405190565b93848092610da7565b0383610399565b90610de1610d5e610739845490565b9060005b818110610df25750505090565b909192610e3a610d98600192610e1c875473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff16815260200190565b929101610de5565b906101da91610dd2565b906101c4610dcb92610e5d60405190565b93848092610e42565b6101da6101006103de565b906101c4610f9e6007610e82610e66565b94610e93610e8f82610880565b8752565b610ea9610ea260018301610880565b6020880152565b610ebf610eb860028301610880565b6040880152565b610f00610ee3600383015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff166060880152565b610f41610f24600483015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff166080880152565b610f82610f65600583015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1660a0880152565b610f98610f9160068301610db1565b60c0880152565b01610e4c565b60e0840152565b6101da90610e71565b1561018757565b91906008610d0a910291610fe87fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff841b90565b921b90565b9190610ffe6101da610d2b93610854565b908354610fb5565b6101c491600091610fed565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b919061104c565b9290565b9180830292818404149015171561105f57565b611012565b81811061106f575050565b8061107d6000600193611006565b01611064565b909182811061109157505050565b6101c4929061109f90610d5e565b9081019101611064565b906801000000000000000081116103d957816110c66101c4935490565b90828155611083565b60006101c4916110a9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b9060000361111a576101c4906110cf565b6110da565b600060076101c49261113383808301611006565b6111408360018301611006565b61114d8360028301611006565b82600382015582600482015582600582015561116c8360068301611109565b01611109565b9060000361111a576101c49061111f565b6102526101da6101da9273ffffffffffffffffffffffffffffffffffffffff1690565b6101da90611183565b6101da906111a6565b611267600a6111cf6111ca8483610861565b610fa5565b611229600061122486339561121f61120161025260a089015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff89165b14610fae565b610861565b611172565b606081015173ffffffffffffffffffffffffffffffffffffffff169161124e306111af565b611261604061125b855190565b94015190565b936129ee565b6112a27f91635156bba825be2f9a200b140420e4bee0d251f7d43bf956e63f74960030e09161129560405190565b9182918290815260200190565b0390a1565b906113019060016112bc6101da85600a610861565b610d49336112196112e7610252600586015473ffffffffffffffffffffffffffffffffffffffff1690565b9173ffffffffffffffffffffffffffffffffffffffff1690565b6112a27fb8b459bc0688c37baf5f735d17f1711684bc14ab7db116f88bc18bf409b9309a9161129560405190565b9190820391821161105f57565b61134f6113496101da9290565b60e01b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b73ffffffffffffffffffffffffffffffffffffffff9182168152911660208201526101da9260a08201926113b391906113ac90610926565b6060830152565b60808183039101526000815260200190565b906113d261041a83610bc3565b918252565b3d156113f1576113e63d6113c5565b903d6000602084013e565b606090565b8091611503600a916114fe6000806114116101da8888610861565b60046114e6339261145b61143f610252600584015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff8616611219565b6114d7600282019861147e611478826114738d610880565b61132f565b8b610d14565b600383015473ffffffffffffffffffffffffffffffffffffffff169563f242432a906114bd6114b78a6114b0306111af565b9701610880565b9261133c565b946114c760405190565b9788966020880190815201611374565b60208201810382520382610399565b82602082019151925af16114f86113d7565b50610fae565b610880565b9060009161151761151384610854565b9190565b03611529576112679261122491610861565b5050506112a27fb8b459bc0688c37baf5f735d17f1711684bc14ab7db116f88bc18bf409b9309a9161129560405190565b81519167ffffffffffffffff83116103d957611585610d5e60019261157f86866110a9565b60200190565b92049160005b8381106115985750505050565b60019060206115a86101da865190565b940193818401550161158b565b906101c49161155a565b81519167ffffffffffffffff83116103d9576115e4610d5e60019261157f86866110a9565b92049160005b8381106115f75750505050565b600190602061161d6101da865173ffffffffffffffffffffffffffffffffffffffff1690565b94019381840155016115ea565b906101c4916115bf565b91600761168c846113019461121f339161167a600a936112196112e7610252600561165f8a8a610861565b015473ffffffffffffffffffffffffffffffffffffffff1690565b60066116868585610861565b016115b5565b0161162a565b8015156101b0565b905051906101c482611692565b90602082820312610187576101da9161169a565b939291906000806116e160045473ffffffffffffffffffffffffffffffffffffffff1690565b600461172b6116f363f6a3d24e61133c565b6114d78b61170060405190565b948593602085019081520173ffffffffffffffffffffffffffffffffffffffff909116815260200190565b602081019051915afa506117556117406113d7565b602061174a825190565b8183010191016116a7565b600090611763825b91151590565b14611773575b506101c4946118e5565b808061179460055473ffffffffffffffffffffffffffffffffffffffff1690565b60046117b36117a6633af32abf61133c565b6114d78c61170060405190565b602081019051915afa506117d461175d6117ce6117406113d7565b92151590565b146117df5738611769565b6040517f82b42900000000000000000000000000000000000000000000000000000000008152600490fd5b0390fd5b6101da60a06103de565b9073ffffffffffffffffffffffffffffffffffffffff90610d0a565b906118446101da610d2b926111af565b8254611818565b6101c491906118b89060809060049061186c81611866875190565b90610d14565b61187e60018201611866602088015190565b61189060028201611866604088015190565b6118be600382016118b8606088015173ffffffffffffffffffffffffffffffffffffffff1690565b90611834565b0192015173ffffffffffffffffffffffffffffffffffffffff1690565b906101c49161184b565b6119a094936119656119829261194861199b96611941611903600290565b9961190d8b613356565b61193a60009961191c8b610854565b85036119ce5761192c8334611219565b61193461180e565b9a8b0152565b6020890152565b6040870152565b73ffffffffffffffffffffffffffffffffffffffff166060850152565b73ffffffffffffffffffffffffffffffffffffffff166080830152565b611996600b61199085613341565b90610861565b6118db565b613341565b6112a27f682fd9923da5632e7c7702dabcfa626195d5f444833bc25f94e418e258e791869161129560405190565b6119e2346112196115136101da8988611041565b61192c565b906101c4949392916116bb565b906101c4611a546004611a0561180e565b94611a12610e8f82610880565b611a21610ea260018301610880565b611a30610eb860028301610880565b61165f610ee3600383015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff166080840152565b6101da906119f4565b600090600490611a8a8382611006565b611a978360018301611006565b611aa48360028301611006565b8260038201550155565b9060000361111a576101c490611a7a565b611b5b600b611b56611ad9611ad48584610861565b611a71565b611b3c60408201611ae8815190565b9087600092611af961151385610854565b03611b89576020850151611b379250965b61121f6080339701966112196112e76102528a5173ffffffffffffffffffffffffffffffffffffffff1690565b611aae565b5173ffffffffffffffffffffffffffffffffffffffff1690565b612fe8565b6112a27f88686b85d6f2c3ab9a04e4f15a22fcfa025ffd97226dcf0a67cdf682def556769161129560405190565b611ba9611b3792611ba3611b9e602089015190565b915190565b90611041565b96611b0a565b9190611c3791611c32611bc66101da86600b610861565b91611bf2336112196112e7610252600488015473ffffffffffffffffffffffffffffffffffffffff1690565b60016002840193611c0285610880565b611c0f6115136000610854565b03611c6557610d49346112196115136101da611c2c878701610880565b8861132f565b610d14565b6112a27f7eff4a127d11f41398f54211d23467816d498336249c9103adb206d679f0e4829161129560405190565b611c99346112196115136101da611c7c8b89611041565b611c93611c8a898901610880565b611ba38d610880565b9061132f565b610d49565b15611ca557565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f68777900000000000000000000000000000000000000000000000000000000006044820152606490fd5b91611c3791611d176101da85600b610861565b913391611d5e611d41610252600487015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff851614611c9e565b611d686000610854565b8103611d8b5750806001611d84611b5693611473838801610880565b9401610d14565b9290600182611c99611da183611b569601610880565b96611dc0611db66002850199611ba38b610880565b611c938388611041565b97610d14565b91929092600a611dd96111ca8583610861565b90600b611de9611ad48883610861565b96611e0b60a085015173ffffffffffffffffffffffffffffffffffffffff1690565b3393908890611e19866112e7565b1480611f8a575b80611f36575b611e2f90610fae565b611e3891610861565b6000611e4391611172565b611e4c91610861565b6000611e5791611aae565b815195611e65602082015190565b60609093015160009073ffffffffffffffffffffffffffffffffffffffff1660809092015173ffffffffffffffffffffffffffffffffffffffff1693611ea9610e66565b98611eb490838b0152565b6020890152611ec290610854565b604088015273ffffffffffffffffffffffffffffffffffffffff16606087015273ffffffffffffffffffffffffffffffffffffffff16608086015273ffffffffffffffffffffffffffffffffffffffff1660a085015260c084015260e0830152611f2c6007610880565b906101c492612b64565b50611e2f8a611f826112e76102526060611f66818d015173ffffffffffffffffffffffffffffffffffffffff1690565b94015173ffffffffffffffffffffffffffffffffffffffff1690565b149050611e26565b5089611f9f6115136101da600061125b8b5190565b14611e20565b90916101da92600a90611fbb6111ca8584610861565b91843491611fca602086015190565b90611ff5611fef60009a8b94611fdf86610854565b8b0361211f57611ba36001610854565b85611219565b61200e8761147360026120088786610861565b01610880565b61201a61151384610854565b036120fb5761202c9261122491610861565b8583015193612052606085015173ffffffffffffffffffffffffffffffffffffffff1690565b90612074608086015173ffffffffffffffffffffffffffffffffffffffff1690565b9260c08601519560e0015196612088610e66565b998a01526020890152604088015273ffffffffffffffffffffffffffffffffffffffff16606087015273ffffffffffffffffffffffffffffffffffffffff16608086015273ffffffffffffffffffffffffffffffffffffffff1660a085015260c084015260e0830152611f2c6006610880565b6002915061211a9261210c91610861565b016118668661147383610880565b61202c565b8a90611041565b905051906101c48261020e565b9092919261214361041a826103f2565b938185526020808601920283019281841161018757915b8383106121675750505050565b602080916121758486612126565b81520192019161215a565b9080601f830112156101875781516101da92602001612133565b905051906101c48261026b565b909291926121b761041a826103f2565b938185526020808601920283019281841161018757915b8383106121db5750505050565b602080916121e9848661219a565b8152019201916121ce565b9080601f830112156101875781516101da926020016121a7565b90608082820312610187576122238183612126565b926122318260208501612126565b92604081015167ffffffffffffffff81116101875783612252918301612180565b92606082015167ffffffffffffffff8111610187576101da92016121f4565b9190820180921161105f57565b9390929061229b906020612290825190565b81830101910161220e565b949295916000949194976122ae89610854565b8203612436575050600b906122c6611ad48884610861565b87896122e9606084015173ffffffffffffffffffffffffffffffffffffffff1690565b3395906122f5876112e7565b148061241f575b61230590610fae565b6123188561147360026120088786610861565b61232461151384610854565b036123fb5761233692611b3791610861565b81612342602083015190565b9061234c91611041565b60809091015173ffffffffffffffffffffffffffffffffffffffff1694612371610e66565b998a01526020890152604088015273ffffffffffffffffffffffffffffffffffffffff16606087015273ffffffffffffffffffffffffffffffffffffffff16608086015273ffffffffffffffffffffffffffffffffffffffff1660a085015260c084015260e08301526123e46007610880565b906123ee92612b64565b6101da63f23a6e6161133c565b6002915061241a9261240c91610861565b016118668461147383610880565b612336565b5061230561242d8386015190565b891490506122fc565b90949596976124488195949395610854565b890361245e57506124599750612816565b6123ee565b6124ee975060019596506124d891949250610d49936124816101da8b600a610861565b956124c36112e76124a960058a015473ffffffffffffffffffffffffffffffffffffffff1690565b9273ffffffffffffffffffffffffffffffffffffffff1690565b149182612540575b82612524575b5050610fae565b61186660028401916124e983610880565b612271565b61251c7fb8b459bc0688c37baf5f735d17f1711684bc14ab7db116f88bc18bf409b9309a9161129560405190565b0390a16123ee565b612538919250611048611513918801610880565b1438806124d1565b9150612563600387015473ffffffffffffffffffffffffffffffffffffffff1690565b61256c336112e7565b14916124cb565b9594939291903360008061259c60045473ffffffffffffffffffffffffffffffffffffffff1690565b60046125bb6125ae63f6a3d24e61133c565b6114d78761170060405190565b602081019051915afa506125d06117406113d7565b600091906125dd8361175d565b146125ee575b50506101c49661273d565b818091600461263061261560055473ffffffffffffffffffffffffffffffffffffffff1690565b926114d7612626633af32abf61133c565b9161170060405190565b602081019051915afa5061264b61175d6117ce6117406113d7565b146117df5738806125e3565b6101c4919061272d9060e09060079061267281611866875190565b61268460018201611866602088015190565b61269660028201611866604088015190565b6126be600382016118b8606088015173ffffffffffffffffffffffffffffffffffffffff1690565b6126e6600482016118b8608088015173ffffffffffffffffffffffffffffffffffffffff1690565b61270e600582016118b860a088015173ffffffffffffffffffffffffffffffffffffffff1690565b6127266006820161272060c088015190565b906115b5565b0192015190565b9061162a565b906101c491612657565b9194929593600161274d81613356565b61275690613341565b963391612761610e66565b9788526020880152604087015273ffffffffffffffffffffffffffffffffffffffff16606086015273ffffffffffffffffffffffffffffffffffffffff16608085015273ffffffffffffffffffffffffffffffffffffffff1660a084015260c083015260e08201526127d482600a610861565b906127de91612733565b7f7e82078c35b6665b9d320ebeaa6c266960fad5b802c5558cf7df60c4769af95b9061280960405190565b90815280602081016112a2565b906101c4969594939291612573565b9261283d919294612834600090565b5081019061063a565b94919260009491949061284f82610854565b810361287757509061286461286a9792610854565b92612816565b6101da63150b7a0261133c565b959493509150600b61288c611ad48783610861565b92806128af606086015173ffffffffffffffffffffffffffffffffffffffff1690565b33939089906128bd866112e7565b148061299c575b6128cd90610fae565b6128d691610861565b906128e091611aae565b602084015160809094015173ffffffffffffffffffffffffffffffffffffffff169361290a610e66565b9861291590838b0152565b602089015261292390610854565b604088015273ffffffffffffffffffffffffffffffffffffffff16606087015273ffffffffffffffffffffffffffffffffffffffff16608086015273ffffffffffffffffffffffffffffffffffffffff1660a085015260c084015260e083015261298d6007610880565b9061299792612b64565b61286a565b506128cd8b6129b1611513611048878c015190565b1490506128c4565b73ffffffffffffffffffffffffffffffffffffffff9182168152911660208201526060810192916101c49160400152565b0152565b906101c4949293600491612a00600090565b506000958695612a0f87610854565b8103612a4557506114e6926114d791612a2b6342842e0e61133c565b93612a3560405190565b96879560208701908152016129b9565b6114e6936114d7926114bd63f242432a61133c565b73ffffffffffffffffffffffffffffffffffffffff90911681526040810192916101c49160200152565b6101da612710610854565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8115612ac8570490565b612a8f565b801561105f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90612b30825190565b811015612b41576020809102010190565b612af8565b9081526060810193926101c49290916040916129ea905b6020830152565b90916020820191612b73835190565b908192612b7e606090565b8060019182936060860194612ba7865173ffffffffffffffffffffffffffffffffffffffff1690565b3f612bb86115136101da6009610880565b14612f4e575b612f19575b8251968791600098612bd76115138b610854565b1115612ecb575050612be96008610880565b612bf561151389610854565b11612e81575b83805b612df5575b505050505b60e0830190612c1682515190565b9081612c2461151388610854565b11612d40575b505050611b9e612cc8612ccd612cc8612ce894612c636080880199611b568b5173ffffffffffffffffffffffffffffffffffffffff1690565b611b3c612c84825173ffffffffffffffffffffffffffffffffffffffff1690565b612c8d306111af565b98612caf60a082015173ffffffffffffffffffffffffffffffffffffffff1690565b9a81019a6040612cbd8d5190565b92019a6112618c5190565b955190565b955173ffffffffffffffffffffffffffffffffffffffff1690565b94612d3b612d28612d22612d1c7f09c7d1ab159dbdc16054d51213cf8ab76b507ff896aca64379b18e54ce7959ef966111af565b96610854565b966111af565b96612d3260405190565b93849384612b46565b0390a4565b9081805b612d4f575b50612c2a565b15612de0575b612d5f8691612acd565b91828960c08801612d78612d74848351612b27565b5190565b612d846115138c610854565b11612d95575b505050919082612d44565b611b3c612dd0612dc9612dbb869e95611ba3611b9e612d74612dd89a611b569951612b27565b612dc3612a84565b90612abe565b809461132f565b9b8851612b27565b388981612d8a565b612de986610854565b8111612d555780612d49565b15612e6c575b612e058791612acd565b9081612e14612d748286612b27565b612e206115138b610854565b11612e2d575b5084612bfe565b98612e67612e438792611c93612d748e89612b27565b9a611b56612e61612d74612e5a611b3c858c612b27565b9389612b27565b916111af565b612e26565b612e7587610854565b8111612dfb5780612c03565b612ec6612ea6612e9f612dbb612e958d5190565b611ba36008610880565b809a61132f565b98611b5660035473ffffffffffffffffffffffffffffffffffffffff1690565b612bfb565b612dbb919950612f149450612ef49350612eed9250612ee88b5190565b611041565b809761132f565b95611b5660035473ffffffffffffffffffffffffffffffffffffffff1690565b612c08565b915050612f47612f3d845173ffffffffffffffffffffffffffffffffffffffff1690565b8551895191613463565b9091612bc3565b506000807f000000000000000000000000167a296135c0067903d4c099806405f6c316442e6004612fcd8a6114d78b612fb3612fad88612fa7637abab711945173ffffffffffffffffffffffffffffffffffffffff1690565b95015190565b9161133c565b92612fbd60405190565b9586946020860190815201612a5a565b82602082019151925af150612fe36117406113d7565b612bbe565b60006101c492613000612ffb83946111af565b6111af565b9061300a60405190565b90818003925af16114f86113d7565b6101c49061304861303f60005473ffffffffffffffffffffffffffffffffffffffff1690565b611219336112e7565b6101c4906003611834565b6101c490613019565b6101c49061308261303f60005473ffffffffffffffffffffffffffffffffffffffff1690565b6101c4906005611834565b6101c49061305c565b906101c492916130be61303f60005473ffffffffffffffffffffffffffffffffffffffff1690565b6101c492916130d16130d8926006610d14565b6007610d14565b6008610d14565b906101c49291613096565b6130f2610e66565b906000825260208080808080808089016000815201600081520160008152016000815201600081520160608152016060905250565b6101da6130ea565b6111ca6101da9161313e613127565b50600a610861565b6131536301ffc9a761133c565b7fffffffff000000000000000000000000000000000000000000000000000000008216149081156131c5575b8115613189575090565b90506131c161319b63150b7a0261133c565b917fffffffff000000000000000000000000000000000000000000000000000000001690565b1490565b90506131d463f23a6e6161133c565b7fffffffff000000000000000000000000000000000000000000000000000000008216149061317f565b61322061303f60005473ffffffffffffffffffffffffffffffffffffffff1690565b6101c461323e565b6102526101da6101da9290565b6101da90613228565b6101c461324b6000613235565b6132e5565b6101c46131fe565b6101c49061327e61303f60005473ffffffffffffffffffffffffffffffffffffffff1690565b6101c49061324b6132926102526000613235565b73ffffffffffffffffffffffffffffffffffffffff83161415610fae565b6101c490613258565b73ffffffffffffffffffffffffffffffffffffffff90911681526040810192916101c49160209061094a565b60005473ffffffffffffffffffffffffffffffffffffffff169061330a816000611834565b7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0916112a261333860405190565b928392836132b9565b60006101da9161334f600090565b5001610880565b6101c49061186660006133696001610854565b92019161072683610880565b9092919261338561041a826103f2565b938185526020808601920283019281841161018757915b8383106133a95750505050565b602080916133b7848661219a565b81520192019161339c565b9080601f830112156101875781516101da92602001613375565b91909160408184031261018757805167ffffffffffffffff811161018757836134069183016133c2565b92602082015167ffffffffffffffff8111610187576101da9201612180565b73ffffffffffffffffffffffffffffffffffffffff90911681526060810193926101c49290916040916129ea90612b5d565b6040513d6000823e3d90fd5b6000919392613470606090565b506134a98361347e306111af565b637fea43b86134b4613492620f4240610854565b9561349c60405190565b9a8b988997889560e01b90565b855260048501613425565b0393f191826000918294613527575b50611513576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f526f79616c7479206c6f6f6b7570206661696c656400000000000000000000006044820152606490fd5b909350613545913d8091833e61353d8183610399565b8101906133dc565b92386134c3565b90602082820312610187576101da9161219a565b9081526040810192916101c49160200152565b9190604083820312610187576101da9061358d818561219a565b93602001612126565b6135a36101da6101da9290565b63ffffffff1690565b61ffff81166101b0565b905051906101c4826135ac565b919091604081840312610187576135f86135dd60406103de565b9360006135ea828561219a565b9086015260208093016135b6565b90830152565b9092919261360e61041a826103f2565b93818552604060208601920283019281841161018757915b8383106136335750505050565b602060409161364284866135c3565b815201920191613626565b9080601f830112156101875781516101da926020016135fe565b9060208282031261018757815167ffffffffffffffff8111610187576101da920161364d565b906113d261041a836103f2565b369037565b906101c46136b56136af8461368d565b936103f2565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0016020840161369a565b6101da6101da6101da9261ffff1690565b156136f857565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f496e76616c69642073706c6974000000000000000000000000000000000000006044820152606490fd5b90602082820312610187576101da91612126565b6bffffffffffffffffffffffff81166101b0565b905051906101c48261376b565b919091604081840312610187576135f86137a660406103de565b9360006137b3828561219a565b90860152602080930161377f565b909291926137d161041a826103f2565b93818552604060208601920283019281841161018757915b8383106137f65750505050565b6020604091613805848661378c565b8152019201916137e9565b9080601f830112156101875781516101da926020016137c1565b9060208282031261018757815167ffffffffffffffff8111610187576101da9201613810565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461105f5760010190565b6101da6101da6101da926bffffffffffffffffffffffff1690565b9060208282031261018757815167ffffffffffffffff8111610187576101da92016133c2565b9060208282031261018757815167ffffffffffffffff8111610187576101da9201612180565b6060939284929190613935602061391d612ffb7f000000000000000000000000ad2184fb5dbcfc05d8f056542fb25b04fa32a95d6111af565b63de5488af9061392c60405190565b93849260e01b90565b825273ffffffffffffffffffffffffffffffffffffffff8616600483015260249082905afa8015613da25761397291600091614406575b506111af565b9061397c826111af565b9163d6a1919a61399561398e60405190565b9160e01b90565b8152600081806139a9868a60048401613560565b038183885af18060009283926143e8575b506140db575050632a55205a6139d261398e60405190565b8152604081806139e6868a60048401613560565b0381875afa8060009283926143b5575b5061412457505063bb3bafd6613a0e61398e60405190565b815260048101869052600081602481875afa806000928392614106575b506140db575050613a506102527341a322b28d0ff354040e2cbc676f0320d8c8850d90565b73ffffffffffffffffffffffffffffffffffffffff85161480156140a8575b613f51575b63cad96cca613a8561398e60405190565b815260048101869052600081602481875afa60009181613f2e575b50613dc2575063b9c4d9fb613ab460405190565b613abe8260e01b90565b815260048101879052600081602481885afa60009181613da7575b50613ce9575b505063d5a06d4c613af261398e60405190565b815260048101869052600081602481875afa806000928392613ccb575b50613c5557505063f6622074613b2761398e60405190565b815260008180613b3b898960048401612a5a565b0381875afa806000928392613cad575b50613c55575050639ca7dc7a613b6361398e60405190565b815260008180613b77898960048401612a5a565b0381875afa806000928392613c8f575b50613c5557505060009392916134a99150613bb763fbda03ab91613baa60405190565b9788968795869560e01b90565b03915afa90816000918293613c38575b50613bd25750509190565b80519194509150613be76115136101da865190565b03613bf0579190565b613bfc611b9e84925190565b9061180a613c0960405190565b9283927fab8b67c600000000000000000000000000000000000000000000000000000000845260048401613560565b909250613c4e913d8091833e61353d8183610399565b9138613bc7565b965097505092505050613c66845190565b613c746115136101da855190565b03613c8357906101da91614424565b50613bfc611b9e845190565b9092613ca592503d8091833e61353d8183610399565b909138613b87565b9092613cc392503d8091833e61353d8183610399565b909138613b4b565b9092613ce192503d8091833e61353d8183610399565b909138613b0f565b50613cf661398e60405190565b815260048101869052600081602481875afa908115613da257600091613d81575b50630ebd4c7f613d2961398e60405190565b815260048101879052600081602481885afa60009181613d5e575b5015600003613adf57965097505092505050613c66845190565b613d7a91923d8091833e613d728183610399565b8101906138be565b9038613d44565b613d9c913d8091833e613d948183610399565b810190613898565b38613d17565b613457565b613dbb91923d8091833e613d948183610399565b9038613ad9565b9750509350505050613dda613dd5845190565b61369f565b90613de6613dd5855190565b936000926000613df581610854565b945b613e026101da855190565b861015613ed657613eca613ed091613eb7613eaa613e9f613e99613e946020613e808e613e7a8d8f838f613e5990613e3d83613e5f95612b27565b51015173ffffffffffffffffffffffffffffffffffffffff1690565b92612b27565b9073ffffffffffffffffffffffffffffffffffffffff169052565b8d612b27565b5101516bffffffffffffffffffffffff1690565b61387d565b8a611041565b612dc3612710610854565b613eb48a8d612b27565b52565b613ec4612d74898c612b27565b90612271565b95613850565b94613df7565b9391945050613ee3915090565b8111613eee57509190565b61180a90613efb60405190565b9182917f3728b83d0000000000000000000000000000000000000000000000000000000083526004830190815260200190565b613f4a91923d8091833e613f428183610399565b81019061382a565b9038613aa0565b613f6f612ffb7317b0c8564e53f22364a6c8de6f7ca5ce9bea4e5d81565b63b85ed7e490613f88613f8160405190565b9260e01b90565b825260208280613f9c8a8a60048401612a5a565b0381845afa60009281614078575b50613fb7575b5050613a74565b6020613fcc9163860110f59061392c60405190565b82528180613fdf898d8d60048501613425565b03915afa60009181614048575b5015600003613fb05796509750505050505061151361400b6001610854565b91613eb461404261402461401e8661369f565b9561369f565b9661403d600091613e5f61403784610854565b89612b27565b610854565b86612b27565b61406a91925060203d8111614071575b6140628183610399565b810190613757565b9038613fec565b503d614058565b61409a91935060203d81116140a1575b6140928183610399565b81019061354c565b9138613faa565b503d614088565b5073b932a70a57673d89f4acffbe830e8ed7f75fb9e073ffffffffffffffffffffffffffffffffffffffff851614613a6f565b95509750509150506101da92506141016140f3865190565b6112196115136101da865190565b614424565b909261411c92503d8091833e61353d8183610399565b909138613a2b565b959396509750925083116143a85760009384918291883b61415161414785613596565b9163ffffffff1690565b116142c5575b614188945061417a63fd90e89761416d60405190565b9687948593849360e01b90565b835260048301526024820190565b03915afa8492816142a1575b506141da57505061151390613eb46140426141af6001610854565b9461403d6141ce6141c86141c28961369f565b9861369f565b996111af565b613e5f61403784610854565b92909394506141e7845190565b936141f18561369f565b906141fb8661369f565b96855b88885b8810156142855761427688614201936124e961427d94613eb461426e613e9f8e612ee88f8f8f91614260602092613e5f8e613e596142428261426999612b27565b519586015173ffffffffffffffffffffffffffffffffffffffff1690565b015161ffff1690565b6136e0565b948592612b27565b9760010190565b9690506141fe565b509350955050925061429a6115136115139390565b11156136f1565b6142be9193503d8087833e6142b68183610399565b810190613667565b9138614194565b916142e9906142da612ffb8b979496976111af565b63d78d610b9061392c60405190565b825260049082905afa87918161438c575b5015871461430c575092918591614157565b9597505050505061431e613dd5835190565b9361432a613dd5845190565b9461433482610854565b9361433d815190565b95855b88885b8810156142855761427688614343936124e961438494613eb461426e613e9f8e612ee88f8f8f91614260602092613e5f8e613e596142428261426999612b27565b969050614340565b6143a19192503d808a833e6142b68183610399565b90386142fa565b61180a83613efb60405190565b9092506143d9915060403d81116143e1575b6143d18183610399565b810190613573565b9091386139f6565b503d6143c7565b90926143fe92503d8091833e61353d8183610399565b9091386139ba565b61441e915060203d81116140a1576140928183610399565b3861396c565b9190614431613dd5825190565b9260009161443f6000610854565b925b61444c6101da835190565b8410156144995761448d61449391614480614476613e9f614470612d748a89612b27565b88611041565b613eb4888b612b27565b613ec4612d74878a612b27565b93613850565b92614441565b93949392506144a59050565b8111613eee57509056

Verified Source Code Partial Match

Compiler: v0.8.20+commit.a1b79de6 EVM: paris
Ownable.sol 73 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable {
    address private _owner;

    event OwnershipTransferred(address previousOwner, address newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(msg.sender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == msg.sender);
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() external onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0));
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
Counters.sol 53 lines
/*----------------------------------------------------------*|
|*          ███    ██ ██ ███    ██ ███████  █████           *|
|*          ████   ██ ██ ████   ██ ██      ██   ██          *|
|*          ██ ██  ██ ██ ██ ██  ██ █████   ███████          *|
|*          ██  ██ ██ ██ ██  ██ ██ ██      ██   ██          *|
|*          ██   ████ ██ ██   ████ ██      ██   ██          *|
|*----------------------------------------------------------*/

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

/**
 *
 * @title Counters                                           *
 *                                                           *
 * @dev Stripped down version of OpenZeppelin Contracts       *
 * v4.4.1 (utils/Counters.sol), identical to                 *
 * CountersUpgradeable.sol being a library. Provides         *
 * counters that can only be incremented.                    *
 * Used to track the total supply of ERC721 ids.             *
 * @dev Include with `using Counters for Counters.Counter;`  *
 *                                                           *
 * @custom:security-contact [email protected]                    *
 *
 */
/**
 * @title Counters
 * @dev Stripped down version of OpenZeppelin Contracts v4.4.1
 * (utils/Counters.sol), identical to
 * CountersUpgradeable.sol being a library. Provides counters that can only be
 * incremented. Used to track the total
 * supply of ERC721 ids.
 * @dev Include with `using Counters for Counters.Counter;`
 */
library Counters {
    struct Counter {
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    /// @dev if implementing ERC721A there could be an overflow risk by removing
    /// overflow protection with `unchecked`,
    /// unless we limit the amount of tokens that can be minted, or require that
    /// totalsupply be less than 2^256 - 1
    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }
}
NinfaMarketplace.sol 1010 lines
/*----------------------------------------------------------*|
|*          ███    ██ ██ ███    ██ ███████  █████           *|
|*          ████   ██ ██ ████   ██ ██      ██   ██          *|
|*          ██ ██  ██ ██ ██ ██  ██ █████   ███████          *|
|*          ██  ██ ██ ██ ██  ██ ██ ██      ██   ██          *|
|*          ██   ████ ██ ██   ████ ██      ██   ██          *|
|*----------------------------------------------------------*/

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import "./utils/Counters.sol";
import "./utils/RoyaltyEngineV1.sol";
import "./access/Ownable.sol";

/**
 *
 * @title NinfaMarketplace                                   *
 *                                                           *
 * @notice On-chain NFT marketplace                          *
 *                                                           *
 * @custom:security-contact [email protected]                    *
 *
 */

contract NinfaMarketplace is Ownable, RoyaltyEngineV1 {
    /*----------------------------------------------------------*|
    |*  # VARIABLES                                             *|
    |*----------------------------------------------------------*/

    using Counters for Counters.Counter;
    /// @notice Orders counter
    Counters.Counter private _orderCount;
    /// @notice Offers counter
    Counters.Counter private _offerCount;
    /// @notice address of admin multisig contract for receiving fees generated
    /// by the marketplace
    address private _feeRecipient;
    /// @notice factory contract for deploying self-sovereign collections.
    address private _factory;
    /// @notice Ninfa's whitelist contract, used for checking if a collection is whitelisted
    address private _whitelist;
    /// @notice Ninfa's external registry, mapping Ninfa's ERC721 sovreign
    /// collection's tokenIds to a boolean value
    /// indicating if the token has been sold on any of Ninfa's contracts, such
    /// as this marketplace or an auction
    /// contract.
    address private immutable _PRIMARY_MARKET_REGISTRY;
    ///@notice constant 10,000 BPS = 100% shares sale price
    uint256 private constant _BPS_DENOMINATOR = 10_000;
    /// @notice Ninfa Marketplace fee percentage on primary sales from orders,
    /// expressed in basis points
    uint256 private _primaryOrdersFee;
    /// @notice Ninfa Marketplace fee percentage on primary sales from offers,
    /// expressed in basis points
    uint256 private _primaryOffersFee;
    /// @notice Ninfa Marketplace fee percentage on all secondary sales,
    /// expressed in basis points
    uint256 private _secondaryMarketFee;
    /// @notice codehash of Ninfa's ERC721 sovreign collection
    bytes32 private _ERC721SovreignV1CodeHash;
    /// @notice `_orderCount` counter to `_Order` struct mapping
    mapping(uint256 => _Order) private _orders;
    /// @notice `_offerCount` counter to `_Offer` struct mapping
    mapping(uint256 => _Offer) public offers;

    /*----------------------------------------------------------*|
    |*  # STRUCTS                                               *|
    |*----------------------------------------------------------*/

    /**
     * @dev the `Order` struct is used both for storing order information, as
     * well as trade information when passed as a
     * function parameter to the private `_trade` function
     * @param tokenId the NFT id, for now we only allow trading NINFA NFT's so
     * no erc721 address is needed
     * @param unitPrice ERC-1155 unit price in ETH, or total price if ERC-721
     * since there is only 1 unit of each token.
     * @dev when the `Order` struct is passed as a function parameter to
     * `_trade`, `unitPrice` always refers to the
     * total price of ERC-1155 tokens, i.e. token value * unit price
     * @param collection address of the ERC721 or ERC1155 contract. Doesn't
     * require any access control besides the
     * collection being whitelisted, i.e. msg.sender may be any address.
     * @param erc1155Value the NFT amount, _amount == 0 for ERC721 and _amount >
     * 0 for ERC1155
     * @param commissionBps commission amounts, expressed in basis points 0 -
     * 10000
     * @param commissionReceivers receivers of commission on sales (primary AND
     * secondary)
     * @param collection address of the collection being sold
     * @param from always refers to the seller, who either creater an order or
     * is accepting an offer.
     * @param operator address of an authorized operator, such as a gallery
     * managing an artist; the operator is allowed
     * to change or cancel an order they created on the Marketplace.
     * @dev operator address usually also corresponds to the commission receiver
     * in order to receive sale commissions,
     * unless `commissionReceiver` is set to yet another address, e.g. a payment
     * splitter.
     */
    struct _Order {
        uint256 tokenId;
        uint256 unitPrice;
        uint256 erc1155Value;
        address collection;
        address from;
        address operator;
        uint256[] commissionBps;
        address[] commissionReceivers;
    }

    struct _Offer {
        uint256 tokenId;
        uint256 unitPrice;
        uint256 erc1155Value;
        address collection;
        address from; // buyer
    }

    /*----------------------------------------------------------*|
    |*  # EVENTS                                                *|
    |*----------------------------------------------------------*/

    event OrderCreated(uint256 orderId);

    event OrderUpdated(uint256 orderId);

    event OrderDeleted(uint256 orderId);

    event OfferCreated(uint256 offerId);

    event OfferUpdated(uint256 offerId);

    event OfferDeleted(uint256 offerId);

    // we have order/offer id and all the related data stored in db.
    event Trade( // seller
        address indexed collection,
        uint256 indexed tokenId,
        address indexed from,
        uint256 id,
        uint256 price,
        uint256 erc1155Value
    );

    /*----------------------------------------------------------*|
    |*  # MODIFIERS                                             *|
    |*----------------------------------------------------------*/

    /**
     * @notice ERC721 and ERC1155 collections must be whitelisted.
     * @dev if the collection has not been whitelisted, check if it is one of
     * Ninfa's factory clones (Ninfa's
     * self-sovereign collections)
     * @dev by checking the whitelist first with nested `if`s avoids having to
     * make an external call unnecessarily
     */
    modifier isWhitelisted(address _collection) {
        bytes memory authorized;
            (, authorized) = _factory.staticcall(
                abi.encodeWithSelector(0xf6a3d24e, _collection)
            );
        if (abi.decode(authorized, (bool)) == false) {
            (, authorized) = _whitelist.staticcall(
                abi.encodeWithSelector(0x3af32abf, _collection)
            );
            if (abi.decode(authorized, (bool)) == false) revert Unauthorized();
        }
        _;
    }

    /*----------------------------------------------------------*|
    |*  # ORDERS                                                *|
    |*----------------------------------------------------------*/

    /**
     * @notice create a new order on the marketplace by transfering an NFT to
     * it.
     * @dev Will create a new order by sending tokens with data bytes containing
     * function parameters
     *
     * Require:
     *
     * - can only be called by an NFT smart contract transfering an NFT to the
     * marketplace
     * - collection must be whitelisted
     *
     */
    function _createOrder(
        address _operator, //  either the previous owner or operator, i.e.
            // whichever address called safeTransferFrom on
            // the ERC1155 contract
        address _from, // previous owner, i.e. seller
        uint256 _id,
        uint256 _value,
        uint256 _unitPrice,
        uint256[] memory _commissionBps,
        address[] memory _commissionReceivers
    )
        private
        isWhitelisted(msg.sender)
    {
        // `_orderCount` starts at 1
        _orderCount.increment();
        uint256 _orderId = _orderCount.current();

        // create order with new `_orderId` in orders mapping
        _orders[_orderId] = _Order(
            _id,
            _unitPrice,
            _value,
            msg.sender, // collection
            _from,
            _operator,
            _commissionBps,
            _commissionReceivers
        );
        emit OrderCreated(_orderId);
    }

    /// @dev only for 1155 for updating the price and LOWER the amount
    function updateOrder(uint256 _erc1155RedeemAmount, uint256 _unitPrice, uint256 _orderId) external {
        lowerOrderErc1155Amount(_orderId, _erc1155RedeemAmount);

        _orders[_orderId].unitPrice = _unitPrice;
    }

    /**
     * @notice cancels order and transfers NFT back to owner
     * @param _orderId the Id of the order
     * @dev delete `_orders[_orderId]` from storage BEFORE making external calls
     * for transfering the NFT back to the
     * seller (check-effects pattern)
     *
     *
     * SHOULD:
     *
     * This function does not check whether the order exists or not
     *
     */
    function deleteOrder(uint256 _orderId) external {
        _Order memory order = _orders[_orderId];
        require(msg.sender == order.operator);

        delete _orders[_orderId];

        _transferNFT(order.collection, address(this), msg.sender, order.tokenId, order.erc1155Value);

        emit OrderDeleted(_orderId);
    }

    /**
     * @param _unitPrice will override the old price, note that it may be 0
     * although this could only mean that a mistake
     * was made
     * @dev this function doesn't enforce a positive value for `_unitPrice` in
     * order to save a little gas for the users.
     * @dev if `_unitPrice` is set to 0, the order will be deleted from the
     * database (not from the smart contract), see
     * {NinfaMarketplace-OrderUpdated} modifier
     */
    function updateOrderPrice(uint256 _orderId, uint256 _unitPrice) external {
        _Order storage order = _orders[_orderId];
        require(msg.sender == order.operator);

        order.unitPrice = _unitPrice;

        emit OrderUpdated(_orderId);
    }

    /**
     * @notice function to lower an order's amount of the ERC-1155 tokenId on
     * sale, doesn't apply to ERC-721 because it
     * is non-fungible
     * @param _erc1155RedeemAmount is the (negative) difference of tokens to be
     * withdrawn by the seller. Should be
     * different from 0, although not strictly required.
     */
    function lowerOrderErc1155Amount(uint256 _orderId, uint256 _erc1155RedeemAmount) public {
        _Order storage order = _orders[_orderId];
        require(msg.sender == order.operator);

        /// @dev warning, make changes to storage BEFORE making external calls
        /// for transfering the NFT back to the
        /// seller (check-effects-interactions pattern)
        order.erc1155Value -= _erc1155RedeemAmount;

        (bool success,) = order.collection.call(
            abi.encodeWithSelector(
                0xf242432a, // bytes4(keccak256('safeTransferFrom(address,address,uint256,uint256,bytes)'))
                address(this),
                msg.sender,
                order.tokenId,
                _erc1155RedeemAmount,
                ""
            )
        );
        require(success);

        if (order.erc1155Value == 0) {
            delete _orders[_orderId]; // it is not possible to delete using
                // storage pointers
                // https://docs.soliditylang.org/en/develop/types.html#data-location
            emit OrderDeleted(_orderId);
        } else {
            emit OrderUpdated(_orderId);
        }
    }

    function setOrderCommission(
        uint256 _orderId, 
        uint256[] memory _commissionBps, 
        address[] memory _commissionReceivers) external {
        // should we check if the total commission is less than 10000?
        // should revert in trade function anyway
        require(msg.sender == _orders[_orderId].operator);
        _orders[_orderId].commissionBps = _commissionBps;
        _orders[_orderId].commissionReceivers = _commissionReceivers;
        emit OrderUpdated(_orderId);
    }

    /*----------------------------------------------------------*|
    |*  # OFFERS                                                *|
    |*----------------------------------------------------------*/

    /**
     * @dev offers can be made independently of whether the token is on sale or
     * not, the msg.value is used to determine
     * the offer amount, so no function parameter is needed for that
     * @dev there is no require to check that an offer or offer doesn't already
     * exist and if so, that the offer amount
     * is not greater than the order itself, this was omitted in order to save
     * gas; the frontend should check this in
     * order to prevent mistakes from the user
     * @param _collection address of the erc721 implementation contract or proxy
     * contract
     * @param _tokenId the token Id to make an offer to
     * @param _amount the NFT amount, _amount == 0 for ERC721 and _amount > 0
     * for ERC1155
     * @param _from needed in order to integrate Wert payment solution, because
     * in every txn Wert is the `msg.sender`.
     *      using _msgSender does not represent a security risk, on the other
     * hand, it is possible for the buyer to use
     * this parameter simply in order to transfer the NFT to an address other
     * than their own, this can be useful for
     * external contract buying NFTs.
     */
    function createOffer(
        address _collection,
        uint256 _tokenId,
        uint256 _amount,
        address _from,
        uint256 _unitPrice
    )
        external
        payable
        isWhitelisted(_collection)
    {
        _offerCount.increment(); // start count at 1

        if (_amount == 0) require(msg.value == _unitPrice);
        else require(msg.value == _unitPrice * _amount);

        offers[_offerCount.current()] = _Offer(
            _tokenId, // uint256 tokenId;
            _unitPrice, // uint256 unitPrice;
            _amount, // uint256 amount;
            _collection, // address collection;
            _from // address from;
        );

        emit OfferCreated(_offerCount.current());
    }

    /**
     * @dev cancels offer and refunds ETH back to bidder. When an order gets
     * filled, the offer isn't marked as
     * cancelled, in order to allow users to claim back their money.
     * @param _offerId the Id of the offer.
     */
    function deleteOffer(uint256 _offerId) external {
        // in memory copy needed so that it is possible to delete the struct
        // inside the storage offers mapping, while
        // keeping check effects interact pattern intact
        _Offer memory offer = offers[_offerId];

        uint256 refund;

        if (offer.erc1155Value == 0) {
            refund = offer.unitPrice;
        } else {
            refund = offer.unitPrice * offer.erc1155Value;
        }
        require(msg.sender == offer.from);
        // mark offer as cancelled forever, updating offer price before external
        // call, Checks Effects Interactions
        // pattern
        delete offers[_offerId];
        // transfer the offer amount back to bidder
        _sendValue(offer.from, refund);

        emit OfferDeleted(_offerId);
    }

    /**
     * @dev this is one of two functions called by a buyer in order to modify
     * their offer, there are two functions,
     * `raiseOffer()` and `lowerOffer()`, because they expect different
     * parameters depending on whether the offer is
     * being raised or lowerd.
     *      A `msg.value` is required, this function will add the amount sent to
     * the old offer amount. The frontend
     * needs to calculate the difference between the old and new offer.
     *      E.g. A buyer calls createOffer() and pays 0.1 ETH. The same buyer
     * later wants to raise the offer to 0.3 ETH,
     * therefore they now need to send 0.2 ETH, because 0.1 was was sent before.
     * @dev anyone can call this function, i.e. requiring that caller is offer
     * creator is not needed
     * @param _offerId the id of the offer
     * call this function only if the new total price is greater than the old
     * total price
     */
    function raiseOfferPrice(uint256 _offerId, uint256 _erc1155Value, uint256 _unitPrice) external payable {
        _Offer storage _offer = offers[_offerId];

        require(msg.sender == _offer.from);

        if (_offer.erc1155Value == 0) {
            require(msg.value == _unitPrice - _offer.unitPrice);
        } else {
            require(msg.value == (_unitPrice * _erc1155Value) - (_offer.unitPrice * _offer.erc1155Value));
        }

        _offer.unitPrice = _unitPrice; // transfer extra amount needed on top of
            // older offer
        _offer.erc1155Value = _erc1155Value;

        emit OfferUpdated(_offerId);
    }

    /**
     * @dev this is one of two functions called by a buyer in order to modify
     * their offer, there are two functions,
     * `raiseOffer()` and `lowerOffer()`, because they expect different
     * parameters depending on whether the offer is
     * being raised or lowerd.
     *      In contrast with `raiseOffer()`, instead of `msg.value` this
     * function expects a uint parameter representing
     * the new (lower) offer; the buyer will get refunded the difference.
     *      E.g. A buyer calls createOffer() and pays 0.3 ETH. The same buyer
     * later wants to lower the offer to 0.1 ETH,
     * therefore they will get refunded 0.2 ETH. I.e. The amount expected by the
     * `_newAmount` paramer is 0.1 ETH (1^17).
     * @param _offerId the id of the offer
     */
    function lowerOfferPrice(uint256 _offerId, uint256 _erc1155Amount, uint256 _unitPrice) external {
        _Offer storage _offer = offers[_offerId];

        require(msg.sender == _offer.from, "hwy");

        uint256 refund;

        if (_erc1155Amount == 0) {
            refund = _offer.unitPrice - _unitPrice;
        } else {
            refund = (_offer.unitPrice * _offer.erc1155Value) - (_unitPrice * _erc1155Amount);
            _offer.erc1155Value = _erc1155Amount;
        }

        _offer.unitPrice = _unitPrice; // needed to store result before offer
            // price is updated
        // transfer the difference between old and new lower offer to the user
        _sendValue(msg.sender, refund);

        emit OfferUpdated(_offerId);
    }

    function acceptListedTokenOffer(
        uint256 _orderId,
        uint256 _offerId,
        uint256[] memory _commissionBps,
        address[] memory _commissionReceivers
    )
        external
    {
        _Order memory order = _orders[_orderId];
        _Offer memory offer = offers[_offerId];

        require(order.operator == msg.sender && order.tokenId == offer.tokenId && order.collection == offer.collection);

        delete _orders[_orderId];
        delete offers[_offerId];

        _trade(
            _Order(
                order.tokenId, // uint256 tokenId
                offer.unitPrice, // offer price, not order price
                0,
                order.collection, // address collection
                msg.sender, // seller
                offer.from, // buyer / nft recipient
                _commissionBps, // uint256 commissionBps
                _commissionReceivers
            ),
            _orderId,
            _primaryOffersFee
        );
    }

    /*----------------------------------------------------------*|
    |*  # TRADING                                               *|
    |*----------------------------------------------------------*/

    /**
     * @notice the collector calls this function to buy an NFT at the ask price,
     * only if an order exists
     * @notice if someone has an open offer but calls fillOrder, the offer will
     * remain open, meaning they will need to
     * call cancelOffer() to get a refund. This is unlikely, as users will
     * likely be aware of this and use the refund in
     * order to pay for part of the order.
     * @param _id avoids having to store a mapping to order id like the
     * deprecated `mapping(address => mapping(uint256
     * => uint256)) private _tokenToOrderId` which would have not worked for
     * erc1155 as each token has a supply.
     * _orderId does not constitute a vulnerability as it is user provided,
     * since A) a regular user will go through the
     * frontend which gets orderId from events
     * @param _buyer needed in order to integrate Wert payment solution, because
     * in every txn Wert is the msg.sender,
     * although using msg.sender would cost less gas.
     * using _msgSender does not represent a security risk, on the other hand,
     * it is possible for the buyer to use this
     * parameter simply in order to transfer the NFT to an address other than
     * their own, this can be useful for external
     * contract buying NFTs.
     *  + @param _erc1155Value market order amount (total or partial fill).
     * `_erc1155Value == 0` corresponds to one erc721 tokenId, `_erc1155Value >
     * 0` for erc1155 tokenIds
     *
     * MUST:
     *
     * - `msg.value` must be equal to `_orders[_orderId]der.unitPrice *
     * buyAmount`
     * - `_orders[_orderId].sellAmount >= buyAmount`
     *
     */
    function fillOrder(uint256 _id, address _buyer, uint256 _erc1155Value) external payable {
        _Order memory order = _orders[_id];

        require(msg.value == order.unitPrice * (_erc1155Value == 0 ? 1 : _erc1155Value));

        // subtracting user-suplied `_erc1155Value` from order amount,
        // transaction will revert if underflow, implicitly
        // requiring `_orders[_id]._erc1155Value >= _erc1155Value`
        if (_orders[_id].erc1155Value - _erc1155Value == 0) delete _orders[_id];
        else _orders[_id].erc1155Value -= _erc1155Value;

        _trade(
            _Order(
                order.tokenId, // uint256 tokenId
                msg.value, // price
                _erc1155Value,
                order.collection, // address collection
                order.from, // seller or from
                _buyer, // address buyer used for nft transfer
                order.commissionBps, // uint256 commissionBps
                order.commissionReceivers // address commissionReceiver
            ),
            _id,
            _primaryOrdersFee // uint256 primaryFee
        );
    }

    /**
     *
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been
     * updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param _operator The address which initiated the transfer (i.e.
     * msg.sender)
     * @param _from     The address which previously owned the token
     * @param _tokenId  The ID of the token being transferred
     * @param _value    The amount of tokens being transferred
     * @param _data     Additional data with no specified format
     * @param _data     `uint256 id` corresponding to an order to be updated or
     * an offer to be accepted, if the `id`
     * parameter is 0 create a new order.
     * @param _data     `uint256 unitPrice` is only required for updating an
     * order, i.e. if `unitPrice == 0` `id` an
     * offer id, if `unitPrice > 0` `id` is an order id
     * @return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * if transfer is allowed
     */
    function onERC1155Received(
        address _operator, //  either the previous owner or operator, whoever
            // called safeTransferFrom on the ERC1155
            // contract
        address _from, // previous owner
        uint256 _tokenId,
        uint256 _value,
        bytes memory _data
    )
        external
        returns (bytes4)
    {
        (uint256 id, uint256 unitPrice, uint256[] memory commissionBps, address[] memory commissionReceivers) =
            abi.decode(_data, (uint256, uint256, uint256[], address[]));

        if (unitPrice == 0) {
            /*----------------------------------------------------------*|
            |*  # ACCEPT OFFER                                          *|
            |*----------------------------------------------------------*/
            // if `_value` is more than the amount the following if check will
            // revert due to underflow,
            // intended to stop someone from sending more erc1155 tokens then
            // there are available in the order

            _Offer memory offer = offers[id];

            require(offer.collection == msg.sender && offer.tokenId == _tokenId);

            // subtracting `_value` from offer amount, transaction will revert
            // if underflow, implicitly requiring
            // `offers[id]._erc1155Value >= _value`
            if (offers[id].erc1155Value - _value == 0) delete offers[id];
            else offers[id].erc1155Value -= _value;

            _trade(
                _Order(
                    _tokenId, // uint256 tokenId
                    offer.unitPrice * _value, // uint256 price (unitPrice *
                        // value)
                    _value,
                    msg.sender, // address collection
                    _from, // seller
                    offer.from, // buyer or operator
                    commissionBps, // uint256 commissionBps
                    commissionReceivers // address commissionReceivers
                ),
                id,
                _primaryOffersFee
            );
        } else if (id == 0) {
            /*----------------------------------------------------------*|
            |*  # CREATE ORDER                                          *|
            |*----------------------------------------------------------*/
            // if the order/offer id parameter is 0, create a new order

            _createOrder(_operator, _from, _tokenId, _value, unitPrice, commissionBps, commissionReceivers);
        } else {
            /*----------------------------------------------------------*|
            |*  # UPDATE ORDER                                          *|
            |*----------------------------------------------------------*/
            // if the user supplied a non-zero value for `unitPrice` then the
            // `id` parameter must correspond to an order
            // that needs to be updated
            // the operator, collection and tokenId of the NFT received by the
            // marketplace must match the ones stored at
            // the id provided by the operator
            // in order to avoid operators increasing allowance for orders with
            // different (more valuable) NFTs
            _Order storage order = _orders[id];

            require(order.operator == _operator && order.collection == msg.sender && order.tokenId == _tokenId);
            order.erc1155Value += _value;
            order.unitPrice = unitPrice;

            emit OrderUpdated(id);
        }

        return 0xf23a6e61;
    }

    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this
     * contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the
     * recipient, the transfer will be
     * reverted.
     *
     * The selector can be obtained in Solidity with
     * `IERC721.onERC721Received.selector`.
     * @param _operator The address which called `safeTransferFrom` function
     * @param _from The address which previously owned the token
     * @param _tokenId The NFT identifier which is being transferred
     * @param _data Additional data with no specified format
     */
    function onERC721Received(
        address _operator,
        address _from,
        uint256 _tokenId,
        bytes calldata _data
    )
        external
        returns (bytes4)
    {
        (uint256 id, uint256 unitPrice, uint256[] memory commissionBps, address[] memory commissionReceivers) =
            abi.decode(_data, (uint256, uint256, uint256[], address[]));

        if (id == 0) {
            /*----------------------------------------------------------*|
            |*  # CREATE ORDER                                          *|
            |*----------------------------------------------------------*/
            // if the order/offer id parameter is 0, create a new order
            _createOrder(_operator, _from, _tokenId, 0, unitPrice, commissionBps, commissionReceivers);
        } else {
            /*----------------------------------------------------------*|
            |*  # ACCEPT OFFER                                          *|
            |*----------------------------------------------------------*/
            // if `_value` is more than the amount the following if check will
            // revert due to underflow,
            // intended to stop someone from sending more erc1155 tokens then
            // there are available in the order
            _Offer memory offer = offers[id];

            require(offer.collection == msg.sender && offer.tokenId == _tokenId);

            delete offers[id]; // ERC-721 doesn't have any supply therefore the
                // offer may be deleted after accepting the
                // offer

            _trade(
                _Order(
                    _tokenId, // uint256 tokenId
                    offer.unitPrice, // uint256 price
                    0,
                    msg.sender, // address collection
                    _from, // address buyer or from
                    offer.from, // seller
                    commissionBps, // uint256 commissionBps
                    commissionReceivers
                ),
                id,
                _primaryOffersFee //uint256 primaryFee
            );
        }

        return 0x150b7a02;
    }

    /**
     * @dev ERC-721 tokens are transferred to the buyer via `transferFrom`
     * rather than `safeTransferFrom`
     *      i.e. the caller is responsible to confirm that the recipient is
     * capable of receiving ERC721
     */
    function _transferNFT(
        address _collection,
        address _from,
        address _to,
        uint256 _tokenId,
        uint256 _erc1155Value
    )
        private
    {
        bool success;
        if (_erc1155Value == 0) {
            // bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
            (success,) = _collection.call(abi.encodeWithSelector(0x42842e0e, _from, _to, _tokenId));
        } else {
            // bytes4(keccak256('safeTransferFrom(address,address,uint256,uint256,bytes)')) == 0xf242432a
            (success,) = _collection.call(abi.encodeWithSelector(0xf242432a, _from, _to, _tokenId, _erc1155Value, ""));
        }

        require(success);
    }

    function _trade(_Order memory _order, uint256 _id, uint256 _primaryMarketFee) private {
        uint256 marketplaceAmount; // declare `marketplaceAmount`, its value
            // will be calculated based on whether it is a
            // primary or secondary sale
        uint256 sellerAmount = _order.unitPrice; // sellerAmount is set equal to
            // price and reduced at each step by
            // subtracting fees, royalties and commissions, if any.
        address payable[] memory royaltyRecipients; // declare
            // `royaltyRecipients`, its value will be calculated based
            // on whether it is a primary or secondary sale
        uint256[] memory royaltyAmounts; // declare `royaltyAmounts`, its value
            // will be calculated based on whether it
            // is a primary or secondary sale
        bool checkSecondaryMarket = true;

        if (_order.collection.codehash == _ERC721SovreignV1CodeHash) {
            // it's a v1 721 token, check market registry
            (, bytes memory secondaryMarket) = _PRIMARY_MARKET_REGISTRY.call(
                abi.encodeWithSelector(
                    0x7abab711,
                    _order.collection,
                    _order.tokenId
                ) // bytes4(keccak256("secondaryMarketInfo(address,uint256)")) == 0x7abab711
            );

            checkSecondaryMarket = abi.decode(secondaryMarket, (bool));
        }

        /*----------------------------------------------------------*|
        |*  # PAY ROYALTIES                                         *|
        |*----------------------------------------------------------*/
        // > "Marketplaces that support this standard MUST pay royalties no
        // matter where the sale occurred or in what
        // currency" - https://eips.ethereum.org/EIPS/eip-2981.

        /*----------------------------------------------------------*|
        |*  # IF ROYALTIES SUPPORTED                                *|
        |*----------------------------------------------------------*/
        // The collection implements some royalty standard, otherwise the length
        // of the arrays returned would be 0.
        if (checkSecondaryMarket) {
            (royaltyRecipients, royaltyAmounts) = getRoyalty(_order.collection, _order.tokenId, _order.unitPrice);
        }
        
        uint256 royaltyRecipientsLength = royaltyRecipients.length; // assign to
            // memory variable to save gas
        if (royaltyRecipientsLength > 0) {
            if (_secondaryMarketFee > 0) {
                /*----------------------------------------------------------*|
                |*  # PAY MARKETPLACE FEE                                   *|
                |*----------------------------------------------------------*/
                marketplaceAmount = (_order.unitPrice * _secondaryMarketFee) / _BPS_DENOMINATOR;
                // subtracting primary or secondary fee amount from seller
                // amount, this is a security check (will revert
                // on underflow) as well as a variable assignment.
                sellerAmount -= marketplaceAmount; // subtract before external
                    // call
                _sendValue(_feeRecipient, marketplaceAmount);
            }

            do {
                royaltyRecipientsLength--;
                // subtracting royalty amount from seller amount, this is a
                // security check (will revert on
                // underflow) as well as a variable assignment.
                if(royaltyAmounts[royaltyRecipientsLength] > 0){
                    sellerAmount -= royaltyAmounts[royaltyRecipientsLength]; // subtract
                        // before external call
                    _sendValue(royaltyRecipients[royaltyRecipientsLength], royaltyAmounts[royaltyRecipientsLength]);
                }
            } while (royaltyRecipientsLength > 0);
        } else {
            //case primary
            marketplaceAmount = (_order.unitPrice * _primaryMarketFee) / _BPS_DENOMINATOR;
            sellerAmount -= marketplaceAmount; // subtract before external
            // call
            _sendValue(_feeRecipient, marketplaceAmount);
        }

        /*----------------------------------------------------------*|
        |*  # PAY ORDER COMMISSIONS (if any)                        *|
        |*----------------------------------------------------------*/
        uint256 commissionReceiversLength = _order.commissionReceivers.length; // assign
        if (commissionReceiversLength > 0) {
            do {
                commissionReceiversLength--;
                if(_order.commissionBps[commissionReceiversLength] > 0){
                    uint256 commissionAmount = (_order.commissionBps[commissionReceiversLength] * _order.unitPrice) / _BPS_DENOMINATOR; // calculate
                    sellerAmount -= commissionAmount; // subtract before external

                    _sendValue(_order.commissionReceivers[commissionReceiversLength], commissionAmount);
                }
            } while (commissionReceiversLength > 0);
        }

        /*----------------------------------------------------------*|
        |*  # PAY SELLER                                            *|
        |*----------------------------------------------------------*/
        _sendValue(_order.from, sellerAmount);

        /*----------------------------------------------------------*|
        |*  # TRANSFER NFT                                          *|
        |*----------------------------------------------------------*/

        _transferNFT(
            _order.collection,
            address(this),
            _order.operator, // buyer
            _order.tokenId,
            _order.erc1155Value
        );

        emit Trade(
            _order.collection,
            _order.tokenId,
            _order.from, // seller
            _id,
            _order.unitPrice,
            _order.erc1155Value
        );
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `_amount` wei to
     * `_receiver`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] raises the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {_sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn
     * more].
     *
     * IMPORTANT: because control is transferred to `_receiver`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions
     * pattern].
     */
    function _sendValue(address _receiver, uint256 _amount) private {
        // solhint-disable-next-line avoid-low-level-calls
        (bool success,) = payable(_receiver).call{ value: _amount }("");
        require(success);
    }

    /*----------------------------------------------------------*|
    |*  # ADMIN FUNCTIONS                                       *|
    |*----------------------------------------------------------*/

    /**
     * @param feeRecipient_ address (multisig) controlled by Ninfa that will
     * receive any market fees
     */
    function setFeeRecipient(address feeRecipient_) external onlyOwner {
        _feeRecipient = feeRecipient_;
    }

    function setWhitelist(address whitelist_) external onlyOwner {
        _whitelist = whitelist_;
    }

    /**
     * @notice sets market sale fees for NINFA_ERC721_V2 communal collection.
     * @param primaryOrdersFee_ fee BPS for primary market orders, set to 500
     * BPS (5% shares) at deployment.
     * @param primaryOffersFee_ fee BPS for primary market offers, set to 500
     * BPS (5% shares) at deployment.
     */
    function setMarketFees(
        uint256 primaryOrdersFee_,
        uint256 primaryOffersFee_,
        uint256 secondaryMarketFee_
    )
        external
        onlyOwner
    {
        _primaryOrdersFee = primaryOrdersFee_;
        _primaryOffersFee = primaryOffersFee_;
        _secondaryMarketFee = secondaryMarketFee_;
    }

    function orders(uint256 _orderId) external view returns (_Order memory) {
        return _orders[_orderId];
    }

    /*----------------------------------------------------------*|
    |*  # VIEW FUNCTIONS                                        *|
    |*----------------------------------------------------------*/

    /**
     * @dev See {IERC165-supportsInterface}.
     * Interface ID for IERC165 == 0x01ffc9a7
     * Return value from `onERC1155Received` call if a contract accepts receipt
     * (i.e
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`).
     * In all other cases the ERC1155TokenReceiver rules MUST be followed as
     * appropriate for the implementation (i.e.
     * safe, custom and/or hybrid).
     * Interface ID for IERC721Receiver. A wallet/broker/auction application
     * MUST implement the wallet interface if it
     * will accept safe transfers.
     */
    function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
        return interfaceId == 0x01ffc9a7 || interfaceId == 0xf23a6e61 || interfaceId == 0x150b7a02;
    }

    /*----------------------------------------------------------*|
    |*  # CONSTRUCTOR                                           *|
    |*----------------------------------------------------------*/

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`
     * @dev after deployment admin needs to manually whitelist collections.
     * @param _royaltyRegistry see https://royaltyregistry.xyz/lookup for public
     * addresses
     */
    constructor(
        address _royaltyRegistry,
        address _primaryMarketRegistry,
        address factory_,
        address whitelist_,
        bytes32 ERC721SovreignV1CodeHash
    )
        RoyaltyEngineV1(_royaltyRegistry)
    {
        _PRIMARY_MARKET_REGISTRY = _primaryMarketRegistry;
        _factory = factory_;
        _whitelist = whitelist_;
        _ERC721SovreignV1CodeHash = ERC721SovreignV1CodeHash;
    }
}
IEIP2981.sol 15 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * EIP-2981
 */
interface IEIP2981 {
    /**
     * bytes4(keccak256("royaltyInfo(uint256,uint256)")) == 0x2a55205a
     *
     * => 0x2a55205a = 0x2a55205a
     */
    function royaltyInfo(uint256 tokenId, uint256 value) external view returns (address, uint256);
}
IRarible.sol 27 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

interface IRaribleV1 {
    /*
     * bytes4(keccak256('getFeeBps(uint256)')) == 0x0ebd4c7f
     * bytes4(keccak256('getFeeRecipients(uint256)')) == 0xb9c4d9fb
     *
     * => 0x0ebd4c7f ^ 0xb9c4d9fb == 0xb7799584
     */
    function getFeeBps(uint256 id) external view returns (uint256[] memory);

    function getFeeRecipients(uint256 id) external view returns (address payable[] memory);
}

interface IRaribleV2 {
    /*
     *  bytes4(keccak256('getRaribleV2Royalties(uint256)')) == 0xcad96cca
     */
    struct Part {
        address payable account;
        uint96 value;
    }

    function getRaribleV2Royalties(uint256 id) external view returns (Part[] memory);
}
RoyaltyEngineV1.sol 302 lines
/*----------------------------------------------------------*|
|*          ███    ██ ██ ███    ██ ███████  █████           *|
|*          ████   ██ ██ ████   ██ ██      ██   ██          *|
|*          ██ ██  ██ ██ ██ ██  ██ █████   ███████          *|
|*          ██  ██ ██ ██ ██  ██ ██ ██      ██   ██          *|
|*          ██   ████ ██ ██   ████ ██      ██   ██          *|
|*----------------------------------------------------------*/

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import "./specs/IRoyaltyRegistry.sol";
import "./specs/IRoyaltySplitter.sol";
import "./specs/IManifold.sol";
import "./specs/IRarible.sol";
import "./specs/IFoundation.sol";
import "./specs/ISuperRare.sol";
import "./specs/IEIP2981.sol";
import "./specs/INinfaRoyalty.sol";
import "./specs/IZoraOverride.sol";
import "./specs/IArtBlocksOverride.sol";
import "./specs/IKODAV2Override.sol";

/**
 *
 * @title RoyaltyEngineV1                                    *
 *                                                           *
 * @notice Custom implementation of Manifold RoyaltyEngineV1 *
 *                                                           *
 * @dev > "Marketplaces may choose to directly inherit the   *
 * Royalty Engine to save a bit of gas".                     *
 *                                                           *
 * @dev ERC165 was removed because interface selector will   *
 * be different from Manifold's (0xcb23f816) and this engine *
 * implementation is not meant for used by other contracts   *
 *                                                           *
 * @author Fork of Manifold's RoyaltyRegistryV1              *
 *                                                           *
 * @custom:security-contact [email protected]                    *
 *
 */

contract RoyaltyEngineV1 {
    /**
     * @dev ROYALTY_REGISTRY could be a hardcoded constant, however using an
     * immutable variable
     * is useful for deploying the engine onto other (test) networks where its
     * address differs from Mainnet
     */
    address internal immutable ROYALTY_REGISTRY;
    address internal constant SUPERRARE_REGISTRY = 0x17B0C8564E53f22364A6C8de6F7ca5CE9BEa4e5D;
    address internal constant SUPERRARE_V1 = 0x41A322b28D0fF354040e2CbC676F0320d8c8850d;
    address internal constant SUPERRARE_V2 = 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0;

    error Unauthorized();
    error InvalidAmount(uint256 amount);
    error LengthMismatch(uint256 recipients, uint256 bps); // only used in
        // RoyaltyEngineV1

    /**
     * Get the royalties for a given token and sale amount.
     *
     * @param tokenAddress - address of token
     * @param tokenId - id of token
     * @param value - sale value of token
     * Returns two arrays, first is the list of royalty recipients, second is
     * the amounts for each recipient.
     */
    function getRoyalty(
        address tokenAddress,
        uint256 tokenId,
        uint256 value
    )
        internal
        returns (address payable[] memory recipients, uint256[] memory amounts)
    {
        /**
         * @dev Control-flow hijack and gas griefing vulnerability within
         * Manifold's RoyaltyEngine, mitigated in
         * https://github.com/manifoldxyz/royalty-registry-solidity/commit/c5ba6db3e04e0b364f7afd7aae853a25542a7439.
         *      "To mitigate the griefing vector and other potential
         * vulnerabilities, limit the gas by default that
         * _getRoyalty is given to at most 50,000 gas, but certainly no more
         * than 100,000 gas."
         *      -
         * https://githubrecord.com/issue/manifoldxyz/royalty-registry-solidity/17/1067105243
         *      However, Ninfa's ERC-2981 implementation (ERC2981N) needs to write to
         * storage upon primary sales, this consumes
         * 800,000 at most gas,
         *      while it only reads from storage upon secondary sales, see
         * {ERC2981N-rotaltyInfo}
         */
        try this._getRoyalty{ gas: 1_000_000 }(tokenAddress, tokenId, value) returns (
            address payable[] memory _recipients, uint256[] memory _amounts
        ) {
            return (_recipients, _amounts);
        } catch {
            revert("Royalty lookup failed");
        }
    }

    /**
     * @dev Get the royalty for a given token
     * @dev the original RoyaltyEngineV1 has been modified by removing the
     * _specCache and the associated code,
     * using try catch statements is very cheap, no need to store `_specCache`
     * mapping, see
     * {RoyaltyEngineV1-_specCache}.
     * - https://www.reddit.com/r/ethdev/comments/szot8r/comment/hy5vsxb/
     * @dev EIP-2981 standard lookup is performed first unlike Manifold's
     * implementation, as it is the most prevalent
     * royalty standard as well as the one being used by Ninfa's collections
     * @return recipients array and amounts array, if no royalty standard has
     * been found, the returned arrays will be
     * empty
     */
    function _getRoyalty(
        address tokenAddress,
        uint256 tokenId,
        uint256 value
    )
        external
        returns (address payable[] memory recipients, uint256[] memory amounts)
    {
        address royaltyAddress = IRoyaltyRegistry(ROYALTY_REGISTRY).getRoyaltyLookupAddress(tokenAddress);

        try INinfaRoyalty(royaltyAddress).ninfaRoyaltyInfo(tokenId, value) returns (
            address payable[] memory recipients_, uint256[] memory bps_
        ) {
            require(recipients_.length == bps_.length);
            return (recipients_, _computeAmounts(value, bps_));
        } catch { }

        try IEIP2981(royaltyAddress).royaltyInfo(tokenId, value) returns (address recipient, uint256 amount) {
            if (amount > value) revert InvalidAmount(amount);
            uint32 recipientSize;
            assembly {
                recipientSize := extcodesize(recipient)
            }
            if (recipientSize > 0) {
                try IRoyaltySplitter(recipient).getRecipients() returns (Recipient[] memory splitRecipients) {
                    recipients = new address payable[](splitRecipients.length);
                    amounts = new uint256[](splitRecipients.length);
                    uint256 sum = 0;
                    uint256 splitRecipientsLength = splitRecipients.length;
                    for (uint256 i = 0; i < splitRecipientsLength;) {
                        Recipient memory splitRecipient = splitRecipients[i];
                        recipients[i] = payable(splitRecipient.recipient);
                        uint256 splitAmount = splitRecipient.bps * amount / 10_000;
                        amounts[i] = splitAmount;
                        sum += splitAmount;
                        unchecked {
                            ++i;
                        }
                    }
                    // sum can be less than amount, otherwise small-value listings can break
                    require(sum <= amount, "Invalid split");

                    return (recipients, amounts);
                } catch {}
            }

            try
                IRoyaltySplitter(royaltyAddress).getRecipients(tokenId)
            returns (Recipient[] memory royaltyInfo) {
                uint256 splitRecipientsLength = royaltyInfo.length;
                recipients = new address payable[](splitRecipientsLength);
                amounts = new uint256[](splitRecipientsLength);
                uint256 sum;

                for (uint256 i; i < splitRecipientsLength; ) {
                    Recipient memory splitRecipient = royaltyInfo[i];
                    recipients[i] = payable(splitRecipient.recipient);
                    uint256 splitAmount = (splitRecipient.bps * amount) / 10000;
                    amounts[i] = splitAmount;
                    sum += splitAmount;
                    unchecked {
                        ++i;
                    }
                }
                // sum can be less than amount, otherwise small-value listings can break
                require(sum <= amount, "Invalid split");

                return (recipients, amounts);
            } catch {
                // Supports EIP2981. Return amounts
                recipients = new address payable[](1);
                amounts = new uint256[](1);
                recipients[0] = payable(recipient);
                amounts[0] = amount;

                return (recipients, amounts);
            }
        } catch { }

        try IManifold(royaltyAddress).getRoyalties(tokenId) returns (
            address payable[] memory recipients_, uint256[] memory bps
        ) {
            // Supports manifold interface.  Compute amounts
            require(recipients_.length == bps.length);
            return (recipients_, _computeAmounts(value, bps));
        } catch { }

        // SuperRare handling
        if (tokenAddress == SUPERRARE_V1 || tokenAddress == SUPERRARE_V2) {
            try ISuperRareRegistry(SUPERRARE_REGISTRY).tokenCreator(tokenAddress, tokenId) returns (
                address payable creator
            ) {
                try ISuperRareRegistry(SUPERRARE_REGISTRY).calculateRoyaltyFee(tokenAddress, tokenId, value) returns (
                    uint256 amount
                ) {
                    recipients = new address payable[](1);
                    amounts = new uint256[](1);
                    recipients[0] = creator;
                    amounts[0] = amount;
                    return (recipients, amounts);
                } catch { }
            } catch { }
        }

        try IRaribleV2(royaltyAddress).getRaribleV2Royalties(tokenId) returns (IRaribleV2.Part[] memory royalties) {
            // Supports rarible v2 interface. Compute amounts
            recipients = new address payable[](royalties.length);
            amounts = new uint256[](royalties.length);
            uint256 totalAmount;
            for (uint256 i = 0; i < royalties.length; i++) {
                recipients[i] = royalties[i].account;
                amounts[i] = (value * royalties[i].value) / 10_000;
                totalAmount += amounts[i];
            }
            if (totalAmount > value) revert InvalidAmount(totalAmount);
            return (recipients, amounts);
        } catch { }
        try IRaribleV1(royaltyAddress).getFeeRecipients(tokenId) returns (address payable[] memory recipients_) {
            // Supports rarible v1 interface. Compute amounts
            recipients_ = IRaribleV1(royaltyAddress).getFeeRecipients(tokenId);
            try IRaribleV1(royaltyAddress).getFeeBps(tokenId) returns (uint256[] memory bps) {
                if (recipients_.length != bps.length) {
                    revert LengthMismatch(recipients_.length, bps.length);
                }
                return (recipients_, _computeAmounts(value, bps));
            } catch { }
        } catch { }
        try IFoundation(royaltyAddress).getFees(tokenId) returns (
            address payable[] memory recipients_, uint256[] memory bps
        ) {
            // Supports foundation interface.  Compute amounts
            if (recipients_.length != bps.length) {
                revert LengthMismatch(recipients_.length, bps.length);
            }
            return (recipients_, _computeAmounts(value, bps));
        } catch { }
        try IZoraOverride(royaltyAddress).convertBidShares(tokenAddress, tokenId) returns (
            address payable[] memory recipients_, uint256[] memory bps
        ) {
            // Support Zora override
            if (recipients_.length != bps.length) {
                revert LengthMismatch(recipients_.length, bps.length);
            }
            return (recipients_, _computeAmounts(value, bps));
        } catch { }
        try IArtBlocksOverride(royaltyAddress).getRoyalties(tokenAddress, tokenId) returns (
            address payable[] memory recipients_, uint256[] memory bps
        ) {
            // Support Art Blocks override
            if (recipients_.length != bps.length) {
                revert LengthMismatch(recipients_.length, bps.length);
            }
            return (recipients_, _computeAmounts(value, bps));
        } catch { }
        try IKODAV2Override(royaltyAddress).getKODAV2RoyaltyInfo(tokenAddress, tokenId, value) returns (
            address payable[] memory _recipients, uint256[] memory _amounts
        ) {
            // Support KODA V2 override
            if (_recipients.length != _amounts.length) {
                revert LengthMismatch(_recipients.length, _amounts.length);
            }
            return (_recipients, _amounts);
        } catch { }

        // No supported royalties configured
        return (recipients, amounts);
    }

    /**
     * Compute royalty amounts
     */
    function _computeAmounts(uint256 value, uint256[] memory bps) private pure returns (uint256[] memory amounts) {
        amounts = new uint256[](bps.length);
        uint256 totalAmount;
        for (uint256 i = 0; i < bps.length; i++) {
            amounts[i] = (value * bps[i]) / 10_000;
            totalAmount += amounts[i];
        }
        if (totalAmount > value) revert InvalidAmount(totalAmount);
        return amounts;
    }

    constructor(address _royaltyRegistry) {
        ROYALTY_REGISTRY = _royaltyRegistry;
    }
}
IManifold.sol 19 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/// @author: manifold.xyz

/**
 * @dev Royalty interface for creator core classes
 */
interface IManifold {
    /**
     * @dev Get royalites of a token.  Returns list of receivers and basisPoints
     *
     *  bytes4(keccak256('getRoyalties(uint256)')) == 0xbb3bafd6
     *
     *  => 0xbb3bafd6 = 0xbb3bafd6
     */
    function getRoyalties(uint256 tokenId) external view returns (address payable[] memory, uint256[] memory);
}
ISuperRare.sol 43 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

interface ISuperRareRegistry {
    /**
     * @dev Get the royalty fee percentage for a specific ERC721 contract.
     * @param _contractAddress address ERC721Contract address.
     * @param _tokenId uint256 token ID.
     * @return uint8 wei royalty fee.
     */
    function getERC721TokenRoyaltyPercentage(
        address _contractAddress,
        uint256 _tokenId
    )
        external
        view
        returns (uint8);

    /**
     * @dev Utililty function to calculate the royalty fee for a token.
     * @param _contractAddress address ERC721Contract address.
     * @param _tokenId uint256 token ID.
     * @param _amount uint256 wei amount.
     * @return uint256 wei fee.
     */
    function calculateRoyaltyFee(
        address _contractAddress,
        uint256 _tokenId,
        uint256 _amount
    )
        external
        view
        returns (uint256);

    /**
     * @dev Get the token creator which will receive royalties of the given
     * token
     * @param _contractAddress address ERC721Contract address.
     * @param _tokenId uint256 token ID.
     */
    function tokenCreator(address _contractAddress, uint256 _tokenId) external view returns (address payable);
}
IFoundation.sol 20 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

interface IFoundation {
    /*
     *  bytes4(keccak256('getFees(uint256)')) == 0xd5a06d4c
     *
     *  => 0xd5a06d4c = 0xd5a06d4c
     */
    function getFees(uint256 tokenId) external view returns (address payable[] memory, uint256[] memory);
}

interface IFoundationTreasuryNode {
    function getFoundationTreasury() external view returns (address payable);
}

interface IFoundationTreasury {
    function isAdmin(address account) external view returns (bool);
}
INinfaRoyalty.sol 20 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * EIP-2981
 */
interface INinfaRoyalty {
    /**
     * bytes4(keccak256("royaltyInfo(uint256,uint256)")) == 0x2a55205a
     *
     * => 0x2a55205a = 0x2a55205a
     */
    function ninfaRoyaltyInfo(
        uint256 tokenId,
        uint256 value
    )
        external
        returns (address payable[] memory, uint256[] memory);
}
IZoraOverride.sol 64 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * Paired down version of the Zora Market interface
 */
interface IZoraMarket {
    struct ZoraDecimal {
        uint256 value;
    }

    struct ZoraBidShares {
        // % of sale value that goes to the _previous_ owner of the nft
        ZoraDecimal prevOwner;
        // % of sale value that goes to the original creator of the nft
        ZoraDecimal creator;
        // % of sale value that goes to the seller (current owner) of the nft
        ZoraDecimal owner;
    }

    function bidSharesForToken(uint256 tokenId) external view returns (ZoraBidShares memory);
}

/**
 * Paired down version of the Zora Media interface
 */
interface IZoraMedia {
    /**
     * Auto-generated accessors of public variables
     */
    function marketContract() external view returns (address);

    function previousTokenOwners(uint256 tokenId) external view returns (address);

    function tokenCreators(uint256 tokenId) external view returns (address);

    /**
     * ERC721 function
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);
}

/**
 * Interface for a Zora media override
 */
interface IZoraOverride {
    /**
     * @dev Convert bid share configuration of a Zora Media token into an array
     * of receivers and bps values
     *      Does not support prevOwner and sell-on amounts as that is specific
     * to Zora marketplace implementation
     *      and requires updates on the Zora Media and Marketplace to update the
     * sell-on amounts/previous owner values.
     *      An off-Zora marketplace sale will break the sell-on functionality.
     */
    function convertBidShares(
        address media,
        uint256 tokenId
    )
        external
        view
        returns (address payable[] memory, uint256[] memory);
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP
     * section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
IKODAV2Override.sol 38 lines
// SPDX-License-Identifier: MIT

/// @author: knownorigin.io

pragma solidity 0.8.20;

interface IKODAV2 {
    function editionOfTokenId(uint256 _tokenId) external view returns (uint256 _editionNumber);

    function artistCommission(uint256 _editionNumber)
        external
        view
        returns (address _artistAccount, uint256 _artistCommission);

    function editionOptionalCommission(uint256 _editionNumber)
        external
        view
        returns (uint256 _rate, address _recipient);
}

interface IKODAV2Override {
    /// @notice Emitted when the royalties fee changes
    event CreatorRoyaltiesFeeUpdated(uint256 _oldCreatorRoyaltiesFee, uint256 _newCreatorRoyaltiesFee);

    /// @notice For the given KO NFT and token ID, return the addresses and the
    /// amounts to pay
    function getKODAV2RoyaltyInfo(
        address _tokenAddress,
        uint256 _id,
        uint256 _amount
    )
        external
        view
        returns (address payable[] memory, uint256[] memory);

    /// @notice Allows the owner() to update the creator royalties
    function updateCreatorRoyalties(uint256 _creatorRoyaltiesFee) external;
}
IRoyaltyRegistry.sol 54 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/// @author: manifold.xyz

import "../introspection/IERC165.sol";

/**
 * @dev Royalty registry interface
 */
interface IRoyaltyRegistry is IERC165 {
    event RoyaltyOverride(address owner, address tokenAddress, address royaltyAddress);

    /**
     * Override the location of where to look up royalty information for a given
     * token contract.
     * Allows for backwards compatibility and implementation of royalty logic
     * for contracts that did not previously
     * support them.
     *
     * @param tokenAddress    - The token address you wish to override
     * @param royaltyAddress  - The royalty override address
     */
    function setRoyaltyLookupAddress(address tokenAddress, address royaltyAddress) external returns (bool);

    /**
     * Returns royalty address location.  Returns the tokenAddress by default,
     * or the override if it exists
     *
     * @param tokenAddress    - The token address you are looking up the royalty
     * for
     */
    function getRoyaltyLookupAddress(address tokenAddress) external view returns (address);

    /**
     * Returns the token address that an overrideAddress is set for.
     * Note: will not be accurate if the override was created before this
     * function was added.
     *
     * @param overrideAddress - The override address you are looking up the
     * token for
     */
    function getOverrideLookupTokenAddress(address overrideAddress) external view returns (address);

    /**
     * Whether or not the message sender can override the royalty address for
     * the given token address
     *
     * @param tokenAddress    - The token address you are looking up the royalty
     * for
     */
    function overrideAllowed(address tokenAddress) external view returns (bool);
}
IRoyaltySplitter.sol 20 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/// @author: manifold.xyz
struct Recipient {
    address payable recipient;
    uint16 bps;
}

interface IRoyaltySplitter {
    /**
     * @dev Get the splitter recipients;
     */
    function getRecipients() external view returns (Recipient[] memory);

    function getRecipients(
        uint256 tokenId
    ) external view returns (Recipient[] memory);
}
IArtBlocksOverride.sol 24 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/**
 *  Interface for an Art Blocks override
 */
interface IArtBlocksOverride {
    /**
     * @dev Get royalites of a token at a given tokenAddress.
     *      Returns array of receivers and basisPoints.
     *
     *  bytes4(keccak256('getRoyalties(address,uint256)')) == 0x9ca7dc7a
     *
     *  => 0x9ca7dc7a = 0x9ca7dc7a
     */
    function getRoyalties(
        address tokenAddress,
        uint256 tokenId
    )
        external
        view
        returns (address payable[] memory, uint256[] memory);
}

Read Contract

offers 0x8a72ea6a → uint256, uint256, uint256, address, address
orders 0xa85c38ef → tuple
owner 0x8da5cb5b → address
supportsInterface 0x01ffc9a7 → bool

Write Contract 19 functions

These functions modify contract state and require a wallet transaction to execute.

_getRoyalty 0x7fea43b8
address tokenAddress
uint256 tokenId
uint256 value
returns: address[], uint256[]
acceptListedTokenOffer 0x5333a0a4
uint256 _orderId
uint256 _offerId
uint256[] _commissionBps
address[] _commissionReceivers
createOffer 0xc402b965
address _collection
uint256 _tokenId
uint256 _amount
address _from
uint256 _unitPrice
deleteOffer 0x74268ff2
uint256 _offerId
deleteOrder 0x11a00327
uint256 _orderId
fillOrder 0x32689eb8
uint256 _id
address _buyer
uint256 _erc1155Value
lowerOfferPrice 0x8b4c042a
uint256 _offerId
uint256 _erc1155Amount
uint256 _unitPrice
lowerOrderErc1155Amount 0x1efeff84
uint256 _orderId
uint256 _erc1155RedeemAmount
onERC1155Received 0xf23a6e61
address _operator
address _from
uint256 _tokenId
uint256 _value
bytes _data
returns: bytes4
onERC721Received 0x150b7a02
address _operator
address _from
uint256 _tokenId
bytes _data
returns: bytes4
raiseOfferPrice 0x2b1708b0
uint256 _offerId
uint256 _erc1155Value
uint256 _unitPrice
renounceOwnership 0x715018a6
No parameters
setFeeRecipient 0xe74b981b
address feeRecipient_
setMarketFees 0x3596188d
uint256 primaryOrdersFee_
uint256 primaryOffersFee_
uint256 secondaryMarketFee_
setOrderCommission 0x17a288df
uint256 _orderId
uint256[] _commissionBps
address[] _commissionReceivers
setWhitelist 0x854cff2f
address whitelist_
transferOwnership 0xf2fde38b
address newOwner
updateOrder 0x2ede65df
uint256 _erc1155RedeemAmount
uint256 _unitPrice
uint256 _orderId
updateOrderPrice 0x5e9e78d0
uint256 _orderId
uint256 _unitPrice

Token Balances (1)

View Transfers →
WETH 0

Recent Transactions

No transactions found for this address