Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x72a495F2FCB1Dd7625A4f03136095fe51D2A3A06
Balance 0 ETH
Nonce 1
Code Size 21400 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

21400 bytes
0x60806040526004361061031e5760003560e01c80637c6285a1116101a5578063bbf52b15116100ec578063e403a25111610095578063ec87621c1161006f578063ec87621c1461097c578063efdcd974146109b0578063f10f537a146109d0578063fc9b5fc8146109e357600080fd5b8063e403a25114610905578063e506dfbe1461093e578063e8a353921461095e57600080fd5b8063c78b616c116100c6578063c78b616c146108b0578063d547741f146108c5578063d9ea9549146108e557600080fd5b8063bbf52b1514610850578063c291537c14610870578063c5275fb01461089057600080fd5b80639c5204711161014e578063a32fa5b311610128578063a32fa5b3146107f0578063a3c8fdd814610810578063b7792b261461083057600080fd5b80639c520471146107a8578063a217fddf146107c6578063a2d40dd5146107db57600080fd5b806391d148541161017f57806391d148541461072b5780639258d45d1461075b57806396b5a7551461078857600080fd5b80637c6285a1146106a0578063802c3a3a146106d35780638635d59b1461070b57600080fd5b80633196415611610269578063567441f81161021257806370b4768e116101ec57806370b4768e14610626578063727622a41461065357806378bd79351461067357600080fd5b8063567441f8146105b957806361b7aafe146105e657806369a74aaf1461060657600080fd5b80633c4de40f116102435780633c4de40f1461053b5780633e723fe61461057957806341896c541461059957600080fd5b806331964156146104c7578063349b5954146104e757806336568abe1461051b57600080fd5b80631fcc5b8a116102cb578063236ed8f3116102a5578063236ed8f31461045a578063248a9ca31461047a5780632f2ff15d146104a757600080fd5b80631fcc5b8a146103fa578063216fd1f51461041a578063218ddb571461043a57600080fd5b80630c0803c8116102fc5780630c0803c81461038e578063107a274a146103ae57806316002f4a146103db57600080fd5b806305013cdc1461032357806306bdf98e146103595780630858e5ad1461037b575b600080fd5b34801561032f57600080fd5b5061034361033e3660046147fb565b610a03565b6040516103509190614814565b60405180910390f35b34801561036557600080fd5b506103796103743660046148b7565b610aa3565b005b610379610389366004614902565b610abd565b34801561039a57600080fd5b506103796103a93660046147fb565b6111f0565b3480156103ba57600080fd5b506103ce6103c93660046147fb565b61122b565b6040516103509190614a51565b3480156103e757600080fd5b506005545b604051908152602001610350565b34801561040657600080fd5b50610379610415366004614a75565b611336565b34801561042657600080fd5b50610379610435366004614aa0565b611360565b34801561044657600080fd5b50610379610455366004614ad9565b6113bc565b34801561046657600080fd5b506103796104753660046147fb565b611430565b34801561048657600080fd5b506103ec6104953660046147fb565b60009081526001602052604090205490565b3480156104b357600080fd5b506103796104c2366004614afe565b6116e4565b3480156104d357600080fd5b506103796104e2366004614afe565b61177c565b3480156104f357600080fd5b506103ec7f940d6b1946ff1d2b5a9f1909219c3c81a370804b5ba0f91ec0828c99a2e6a68181565b34801561052757600080fd5b50610379610536366004614afe565b6117dd565b34801561054757600080fd5b5061056c60405180604001604052806005815260200164189718171960d91b81525081565b6040516103509190614b47565b34801561058557600080fd5b50610379610594366004614b93565b61183f565b3480156105a557600080fd5b506103796105b4366004614bd3565b611d16565b3480156105c557600080fd5b506105d96105d43660046147fb565b611f69565b6040516103509190614bf8565b3480156105f257600080fd5b506103796106013660046147fb565b61211a565b34801561061257600080fd5b506103ec610621366004614c47565b6123de565b34801561063257600080fd5b506106466106413660046147fb565b61266b565b6040516103509190614c82565b34801561065f57600080fd5b5061037961066e3660046148b7565b61278d565b34801561067f57600080fd5b5061069361068e3660046147fb565b6127a2565b6040516103509190614e0f565b3480156106ac57600080fd5b5061056c604051806040016040528060078152602001661350525393915560ca1b81525081565b3480156106df57600080fd5b506106f36106ee3660046147fb565b61291c565b6040516001600160a01b039091168152602001610350565b34801561071757600080fd5b506103436107263660046147fb565b61298d565b34801561073757600080fd5b5061074b610746366004614afe565b612a1f565b6040519015158152602001610350565b34801561076757600080fd5b5061077b6107763660046147fb565b612a4a565b6040516103509190614e1e565b34801561079457600080fd5b506103796107a33660046147fb565b612c5e565b3480156107b457600080fd5b506002546001600160a01b03166106f3565b3480156107d257600080fd5b506103ec600081565b3480156107e757600080fd5b506006546103ec565b3480156107fc57600080fd5b5061074b61080b366004614afe565b612d72565b34801561081c57600080fd5b506103ec61082b3660046147fb565b612dc4565b34801561083c57600080fd5b5061037961084b366004614e61565b612f5c565b34801561085c57600080fd5b506103ec61086b366004614e8d565b612fa7565b34801561087c57600080fd5b5061077b61088b366004614902565b6133a1565b34801561089c57600080fd5b506105d96108ab366004614902565b6135e4565b3480156108bc57600080fd5b506004546103ec565b3480156108d157600080fd5b506103796108e0366004614afe565b6137b4565b3480156108f157600080fd5b50610646610900366004614a75565b6137cd565b34801561091157600080fd5b5061074b610920366004614a75565b6001600160a01b031660009081526010602052604090205460ff1690565b34801561094a57600080fd5b506106f36109593660046147fb565b6138f3565b34801561096a57600080fd5b506003546001600160a01b03166106f3565b34801561098857600080fd5b506103ec7f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0881565b3480156109bc57600080fd5b506103796109cb366004614a75565b613964565b6103796109de366004614ec9565b61398e565b3480156109ef57600080fd5b506103796109fe366004614afe565b613e79565b60008181527f17bc176d2408558f6e4111feebc3cab4e16b63e967be91cde721f4c8a488b55260209081526040808320805482518185028101850190935280835260609492939192909184015b82821015610a98576000848152602090819020604080518082019091526002850290910180546001600160a01b03168252600190810154828401529083529092019101610a50565b505050509050919050565b610aab613eda565b610ab86001848484613f39565b505050565b6000828152600b6020908152604080832081516101008101909252805466ffffffffffffff811683529192909190830190600160381b900460ff166003811115610b0957610b09614cfd565b6003811115610b1a57610b1a614cfd565b8152815463ffffffff600160401b820481166020840152600160601b82041660408301526001600160401b03600160801b820481166060840152600160c01b909104166080820152600182015460a082015260029091015460c0909101529050600081602001516003811115610b9257610b92614cfd565b03610bb05760405163e6759c6760e01b815260040160405180910390fd5b600181602001516003811115610bc857610bc8614cfd565b14610be6576040516334dc687f60e11b815260040160405180910390fd5b806040015163ffffffff16421015610c2a57604080820151905163ad4db47b60e01b815263ffffffff90911660048201524260248201526044015b60405180910390fd5b42816060015163ffffffff161015610c555760405163dda144a560e01b815260040160405180910390fd5b81600003610c7657604051630d5a8f9960e11b815260040160405180910390fd5b8060c00151821015610cab5760c081015160405163013542db60e31b8152610c21918491600401918252602082015260400190565b348214610cd4576040516334cb937b60e21b815260048101839052346024820152604401610c21565b6000838152600e60205260409020548015610e44576000848152600e602052604081206001908101908290610d099085614f1c565b8152602080820192909252604090810160002081516080808201845282546001600160401b038082168452600160401b820463ffffffff1696840196909652600160601b90046001600160a01b031693820193909352600190910154606082018190529186015190935061271092168102919091040180851015610daa57604051631711707760e31b81526004810186905260248101829052604401610c21565b50600081604001516001600160a01b0316826060015160405160006040518083038185875af1925050503d8060008114610e00576040519150601f19603f3d011682016040523d82523d6000602084013e610e05565b606091505b5050905080610e2757604051634412abcb60e01b815260040160405180910390fd5b6000868152600e60205260409020548314610e4157600080fd5b50505b600e6000858152602001908152602001600020600001600081546001019190508190555060006040518060800160405280866001600160401b031681526020014263ffffffff168152602001336001600160a01b0316815260200185815250905080600e6000878152602001908152602001600020600101600084815260200190815260200160002060008201518160000160006101000a8154816001600160401b0302191690836001600160401b0316021790555060208201518160000160086101000a81548163ffffffff021916908363ffffffff160217905550604082015181600001600c6101000a8154816001600160a01b0302191690836001600160a01b03160217905550606082015181600101559050506000600f6000336001600160a01b03166001600160a01b03168152602001908152602001600020600001600081548092919060010191905055905081600f6000336001600160a01b03166001600160a01b03168152602001908152602001600020600101600083815260200190815260200160002060008201518160000160006101000a8154816001600160401b0302191690836001600160401b0316021790555060208201518160000160086101000a81548163ffffffff021916908363ffffffff160217905550604082015181600001600c6101000a8154816001600160a01b0302191690836001600160a01b0316021790555060608201518160010155905050508260a001516001600160401b031642846060015163ffffffff166110839190614f1c565b116111a65760a083015160608401805190910163ffffffff1690526000858152600b602090815260409091208451815466ffffffffffffff19811666ffffffffffffff90921691821783559286015186939091839167ffffffffffffffff191617600160381b8360038111156110fb576110fb614cfd565b0217905550604082015181546060840151608085015160a08601516fffffffffffffffff000000000000000019909316600160401b63ffffffff9586160263ffffffff60601b191617600160601b9490921693909302176fffffffffffffffffffffffffffffffff16600160801b6001600160401b03938416026001600160c01b031617600160c01b929091169190910217815560c0820151600182015560e0909101516002909101555b83336001600160a01b0316867f81b76daaaa20e11a350a2cbf4c8e7c229b6b0bb852585e708b3f6ca5dc250466866040516111e19190614f2f565b60405180910390a45050505050565b6111f8613eda565b612710811115611226576040516372d6a00960e01b8152612710600482015260248101829052604401610c21565b600655565b6112336146a4565b506040805180820182528281526000928352600760209081529282902082516101c081018452815462ffffff8116825260ff63010000008204168287015263ffffffff640100000000820481169583019590955261ffff600160401b820481166060840152600160501b80830482166080850152600160601b8304821660a08501526001600160501b03600160701b93849004811660c086015260019095015494851660e0850152840486166101008401529083048516610120830152600160901b8304909416610140820152600160b01b82048416610160820152600160c01b82048416610180820152600160d01b9091049092166101a08301529182015290565b61133e613eda565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b6113686140e9565b6001600160a01b038216600081815260106020526040808220805460ff191685151590811790915590519092917f215f66273fb517a22736277e280b3ea8a0b7db6ed54f2edb25b1cbd87e3c28ca91a35050565b6113c4613eda565b806113d05760006113d3565b60015b600083815260076020526040808220805460ff9490941663010000000263ff000000199094169390931790925590518215159184917f3f3590113b880e5ffacda884287cb609f87652a7522ba040e52d072f9179e9b09190a35050565b611438613eda565b6000818152600b60205260408120908154600160381b900460ff16600381111561146457611464614cfd565b036114825760405163e6759c6760e01b815260040160405180910390fd5b60018154600160381b900460ff1660038111156114a1576114a1614cfd565b146114bf576040516334dc687f60e11b815260040160405180910390fd5b8054600160601b900463ffffffff164210156115055780546040516336cc6d4f60e21b8152600160601b90910463ffffffff166004820152426024820152604401610c21565b6000828152600e602052604081205490819003611535576040516359a67b3d60e01b815260040160405180910390fd5b6000838152600e6020526040812060019081019082906115559085614f1c565b81526020808201929092526040908101600020815160808101835281546001600160401b0381168252600160401b810463ffffffff1694820194909452600160601b9093046001600160a01b03169183019190915260010154606082018190526002850154919250106115ee5760608101516002840154604051637e4b9d7d60e01b815260048101929092526024820152604401610c21565b825460ff60381b1916670200000000000000178355604081810151606083015191516000926001600160a01b0390921691908381818185875af1925050503d8060008114611658576040519150601f19603f3d011682016040523d82523d6000602084013e61165d565b606091505b505090508061167f57604051634412abcb60e01b815260040160405180910390fd5b6060828101516040808501518754825166ffffffffffffff90911681526000602082015260019281019290925291926001600160a01b039092169188917f46b848163e03f83edace353ba36892b6d49697bebcbe7aea9f837c69cd58514791016111e1565b6000828152600160205260409020546116fd9033614145565b6000828152602081815260408083206001600160a01b038516845290915290205460ff161561176e5760405162461bcd60e51b815260206004820152601d60248201527f43616e206f6e6c79206772616e7420746f206e6f6e20686f6c646572730000006044820152606401610c21565b61177882826141c3565b5050565b611784613eda565b60008281526009602052604080822080546001600160a01b0319166001600160a01b0385169081179091559051909184917f7bda5ebb0c6f5079561a878d9b8fa1fd8e2443210022b1e25de894fea02509479190a35050565b336001600160a01b038216146118355760405162461bcd60e51b815260206004820152601a60248201527f43616e206f6e6c792072656e6f756e636520666f722073656c660000000000006044820152606401610c21565b611778828261421c565b611847613eda565b60008281526007602052604081208054909162ffffff909116900361187f576040516335aacf6560e11b815260040160405180910390fd5b61189161012083016101008401614f3e565b63ffffffff166118a961014084016101208501614f3e565b63ffffffff161015611902576118c761012083016101008401614f3e565b6118d961014084016101208501614f3e565b6040516322e2aba560e21b815263ffffffff928316600482015291166024820152604401610c21565b61191461014083016101208401614f3e565b63ffffffff1661192b610100840160e08501614f3e565b63ffffffff16116119825761194861014083016101208401614f3e565b611959610100840160e08501614f3e565b604051637a423a8560e01b815263ffffffff928316600482015291166024820152604401610c21565b6119926060830160408401614f64565b61ffff166119a66040840160208501614f64565b61ffff1610156119f7576119c06060830160408401614f64565b6119d06040840160208501614f64565b604051635789d79160e11b815261ffff928316600482015291166024820152604401610c21565b6001810154600160c01b810461ffff908116600160d01b9092048116919091011680611a296040850160208601614f64565b61ffff161015611a6a57611a436040840160208501614f64565b60405163721750bd60e11b815261ffff909116600482015260248101829052604401610c21565b6001820154600160b01b900461ffff16611a8a6060850160408601614f64565b61ffff161015611ad957611aa46060840160408501614f64565b600183015460405163db50aaff60e01b815261ffff9283166004820152600160b01b9091049091166024820152604401610c21565b50611aec61016083016101408401614f88565b611af7576000611afa565b60015b815460ff9190911663010000000263ff00000019909116178155611b246040830160208401614f64565b815467ffffffff00000000191661ffff9190911664010000000002178155611b526060830160408401614f64565b815461ffff91909116600160401b0261ffff60401b19909116178155611b7e6080830160608401614f64565b815461ffff91909116600160501b0261ffff60501b19909116178155611baa60a0830160808401614f64565b815461ffff91909116600160601b0261ffff60601b19909116178155611bd660c0830160a08401614fa5565b81546001600160501b0391909116600160701b0269ffffffffffffffffffff60701b19909116178155611c0f60e0830160c08401614fa5565b60018201805469ffffffffffffffffffff19166001600160501b0392909216919091179055611c45610100830160e08401614f3e565b60018201805463ffffffff92909216600160501b0263ffffffff60501b19909216919091179055611c7e61012083016101008401614f3e565b60018201805463ffffffff92909216600160701b0263ffffffff60701b19909216919091179055611cb761014083016101208401614f3e565b8160010160126101000a81548163ffffffff021916908363ffffffff160217905550827f27354bd467af48a70cfd84dc6c228d49719c9b2848acfa604e818852c9cc517482604051611d099190614fce565b60405180910390a2505050565b611d1e613eda565b6000828152600b60205260408120908154600160381b900460ff166003811115611d4a57611d4a614cfd565b03611d685760405163e6759c6760e01b815260040160405180910390fd5b60018154600160381b900460ff166003811115611d8757611d87614cfd565b14611da5576040516334dc687f60e11b815260040160405180910390fd5b611db56040830160208401614f3e565b63ffffffff16611dcb6060840160408501614f3e565b63ffffffff1611611e1f57611de66040830160208401614f3e565b611df66060840160408501614f3e565b6040516377acf3f160e11b815263ffffffff928316600482015291166024820152604401610c21565b611e2f60808301606084016150e5565b6001600160401b03166127101015611e5a57604051631594b46760e21b815260040160405180910390fd5b611e6a6040830160208401614f3e565b815463ffffffff91909116600160401b0263ffffffff60401b19909116178155611e9a6060830160408401614f3e565b815463ffffffff91909116600160601b0263ffffffff60601b19909116178155611eca60808301606084016150e5565b81546001600160401b0391909116600160801b0267ffffffffffffffff60801b19909116178155611f0160a08301608084016150e5565b81546001600160401b0391909116600160c01b026001600160c01b0390911617815560a0820135600182015560c0820135600282015560405183907f719a0c86470e9c93a183ded6bca73369f7e28afe0beec0a19f90763aae17323b90611d0990849061510e565b6000818152600860205260409020805460609190806001600160401b03811115611f9557611f956151a0565b604051908082528060200260200182016040528015611fce57816020015b611fbb6146a4565b815260200190600190039081611fb35790505b50925060005b818110156121125760008181526001808501602090815260408084205481518083018352818152818652600784529482902082516101c081018452815462ffffff8116825260ff63010000008204168287015263ffffffff640100000000820481169583019590955261ffff600160401b820481166060840152600160501b80830482166080850152600160601b8304821660a08501526001600160501b03600160701b93849004811660c0860152949098015493841660e0840152968304851661010083015282048416610120820152600160901b8204909316610140840152600160b01b81048516610160840152600160c01b81048516610180840152600160d01b90049093166101a08201529083015285519091908690849081106120fe576120fe6151b6565b602090810291909101015250600101611fd4565b505050919050565b6000818152600b60205260408120908154600160381b900460ff16600381111561214657612146614cfd565b036121645760405163e6759c6760e01b815260040160405180910390fd5b60018154600160381b900460ff16600381111561218357612183614cfd565b146121a1576040516334dc687f60e11b815260040160405180910390fd5b8054600160601b900463ffffffff164210156121e75780546040516336cc6d4f60e21b8152600160601b90910463ffffffff166004820152426024820152604401610c21565b6000828152600e602052604081205490819003612217576040516359a67b3d60e01b815260040160405180910390fd5b6000838152600e6020526040812060019081019082906122379085614f1c565b81526020808201929092526040908101600020815160808101835281546001600160401b0381168252600160401b810463ffffffff1694820194909452600160601b9093046001600160a01b0316918301919091526001015460608201819052600285015491925010156122d15760608101516002840154604051635cf8b88960e11b815260048101929092526024820152604401610c21565b825460ff60381b19166702000000000000001783556122ef846138f3565b604082810151855491516308934a5f60e31b81526001600160a01b03918216600482015266ffffffffffffff9092166024830152919091169063449a52f890604401600060405180830381600087803b15801561234b57600080fd5b505af115801561235f573d6000803e3d6000fd5b50505050612373600185836060015161427c565b6060818101516040808401518654825166ffffffffffffff909116815260016020820152600081840152915192936001600160a01b039091169288927f46b848163e03f83edace353ba36892b6d49697bebcbe7aea9f837c69cd58514792908290030190a450505050565b60006123e8613eda565b5060058054600101908190556000818152600b60209081526040918290209161241691908701908701614f3e565b63ffffffff1661242c6060870160408801614f3e565b63ffffffff1611612457576124476040860160208701614f3e565b611df66060870160408801614f3e565b61246760808601606087016150e5565b6001600160401b0316612710101561249257604051631594b46760e21b815260040160405180910390fd5b61249f60208601866151cc565b815466ffffffffffffff9190911667ffffffffffffffff1990911617600160381b1781556124d36040860160208701614f3e565b815463ffffffff91909116600160401b0263ffffffff60401b199091161781556125036060860160408701614f3e565b815463ffffffff91909116600160601b0263ffffffff60601b1990911617815561253360808601606087016150e5565b81546001600160401b0391909116600160801b0267ffffffffffffffff60801b1990911617815561256a60a08601608087016150e5565b81546001600160401b0391909116600160c01b026001600160c01b0390911617815560a0850135600182015560c085013560028201556000600c816125b260208901896151cc565b66ffffffffffffff168152602080820192909252604001600090812080546001810190915592508491600c91906125eb908a018a6151cc565b66ffffffffffffff16815260208082019290925260409081016000908120948152600190940190915290912055821561262b5761262b6001838686613f39565b817f6ecb6e63f6645ff9293c33fc77c67f1bc05beff9ff71806d5c759647b3d2d4028260405161265b919061510e565b60405180910390a2509392505050565b6000818152600e6020526040902054606090806001600160401b03811115612695576126956151a0565b6040519080825280602002602001820160405280156126e757816020015b6040805160808101825260008082526020808301829052928201819052606082015282526000199092019101816126b35790505b50915060005b81811015612786576000848152600e602090815260408083208484526001908101835292819020815160808101835281546001600160401b0381168252600160401b810463ffffffff1694820194909452600160601b9093046001600160a01b0316918301919091529091015460608201528351849083908110612773576127736151b6565b60209081029190910101526001016126ed565b5050919050565b612795613eda565b610ab86000848484613f39565b6127aa614728565b6000828152600e60209081526040808320548151606081018352868152868552600b84529382902082516101008101909352805466ffffffffffffff81168452919493848101939290830190600160381b900460ff16600381111561281157612811614cfd565b600381111561282257612822614cfd565b8152815463ffffffff600160401b82048116602080850191909152600160601b83049091166040808501919091526001600160401b03600160801b840481166060860152600160c01b909304909216608084015260018085015460a085015260029094015460c0909301929092529284526000888152600e82529283209301920190846128b05760006128b5565b600185035b81526020808201929092526040908101600020815160808101835281546001600160401b0381168252600160401b810463ffffffff1694820194909452600160601b9093046001600160a01b03169183019190915260010154606082015290529392505050565b6000818152600960205260408120546001600160a01b03166129715750506000805260096020527fec8156718a8372b1db44bb411437d0870f3e3790d4a08526d024ce1b0b668f6b546001600160a01b031690565b506000908152600960205260409020546001600160a01b031690565b60008181527f4ad3b33220dddc71b994a52d72c06b10862965f7d926534c05c00fb7e819e7b760209081526040808320805482518185028101850190935280835260609492939192909184018215610a98576000848152602090819020604080518082019091526002850290910180546001600160a01b03168252600190810154828401529083529092019101610a50565b6000828152602081815260408083206001600160a01b038516845290915290205460ff165b92915050565b6000818152600c60205260409020805460609190806001600160401b03811115612a7657612a766151a0565b604051908082528060200260200182016040528015612aaf57816020015b612a9c614728565b815260200190600190039081612a945790505b50925060005b81811015612112576000818152600184016020908152604080832054600e8352818420548251606081018452828152828652600b85529483902083516101008101909452805466ffffffffffffff81168552929591949193848301939092830190600160381b900460ff166003811115612b3157612b31614cfd565b6003811115612b4257612b42614cfd565b8152815463ffffffff600160401b82048116602080850191909152600160601b83049091166040808501919091526001600160401b03600160801b840481166060860152600160c01b909304909216608084015260018085015460a085015260029094015460c0909301929092529284526000878152600e8252928320930192019084612bd0576000612bd5565b600185035b81526020808201929092526040908101600020815160808101835281546001600160401b0381168252600160401b810463ffffffff1694820194909452600160601b9093046001600160a01b03169183019190915260010154606082015290528651879085908110612c4957612c496151b6565b60209081029190910101525050600101612ab5565b612c66613eda565b6000818152600b60205260408120908154600160381b900460ff166003811115612c9257612c92614cfd565b03612cb05760405163e6759c6760e01b815260040160405180910390fd5b60018154600160381b900460ff166003811115612ccf57612ccf614cfd565b14612ced576040516334dc687f60e11b815260040160405180910390fd5b6000828152600e602052604090205415612d2e576000828152600e602052604090819020549051636761e76160e11b81526004810191909152602401610c21565b805460ff60381b191667030000000000000017815560405182907f2809c7e17bf978fbc7194c0a694b638c4215e9140cacc6c38ca36010b45697df90600090a25050565b60008281526020818152604080832083805290915281205460ff16612dbb57506000828152602081815260408083206001600160a01b038516845290915290205460ff16612a44565b50600192915050565b6000818152600b6020908152604080832081516101008101909252805466ffffffffffffff811683528493830190600160381b900460ff166003811115612e0d57612e0d614cfd565b6003811115612e1e57612e1e614cfd565b8152815463ffffffff600160401b820481166020840152600160601b82041660408301526001600160401b03600160801b820481166060840152600160c01b909104166080820152600182015460a082015260029091015460c0909101529050600081602001516003811115612e9657612e96614cfd565b03612eb45760405163e6759c6760e01b815260040160405180910390fd5b6000838152600e60205260409020548015612f51576000938452600e60209081526040808620600019939093018652600192830182529485902085516080808201885282546001600160401b038082168452600160401b820463ffffffff1695840195909552600160601b90046001600160a01b031697820197909752920154606090920182905291909301516127109116830204909101919050565b5060c0015192915050565b612f646140e9565b6001600160a01b03821660009081526010602052604090205460ff16612f9d5760405163b13347ad60e01b815260040160405180910390fd5b611778828261438e565b6000612fb1613eda565b5060048054600101908190556000818152600760205260409020612fdd61012086016101008701614f3e565b63ffffffff16612ff561014087016101208801614f3e565b63ffffffff1610156130255761301361012086016101008701614f3e565b6118d961014087016101208801614f3e565b61303761014086016101208701614f3e565b63ffffffff1661304e610100870160e08801614f3e565b63ffffffff161161307c5761306b61014086016101208701614f3e565b611959610100870160e08801614f3e565b61308c6060860160408701614f64565b61ffff166130a06040870160208801614f64565b61ffff1610156130ca576130ba6060860160408701614f64565b6119d06040870160208801614f64565b6130d760208601866151f5565b815462ffffff191662ffffff919091161781556130fc61016086016101408701614f88565b61310757600061310a565b60015b815460ff9190911663010000000263ff000000199091161781556131346040860160208701614f64565b815467ffffffff00000000191661ffff91909116640100000000021781556131626060860160408701614f64565b815461ffff91909116600160401b0261ffff60401b1990911617815561318e6080860160608701614f64565b815461ffff91909116600160501b0261ffff60501b199091161781556131ba60a0860160808701614f64565b815461ffff91909116600160601b0261ffff60601b199091161781556131e660c0860160a08701614fa5565b81546001600160501b0391909116600160701b0269ffffffffffffffffffff60701b1990911617815561321f60e0860160c08701614fa5565b60018201805469ffffffffffffffffffff19166001600160501b0392909216919091179055613255610100860160e08701614f3e565b60018201805463ffffffff92909216600160501b0263ffffffff60501b1990921691909117905561328e61012086016101008701614f3e565b60018201805463ffffffff92909216600160701b0263ffffffff60701b199092169190911790556132c761014086016101208701614f3e565b60018201805463ffffffff92909216600160901b0263ffffffff60901b19909216919091179055600060088161330060208901896151f5565b62ffffff16815260208082019290925260400160009081208054600181019091559250849160089190613335908a018a6151f5565b62ffffff168152602080820192909252604090810160009081209481526001909401909152909120558215613371576133716000838686613f39565b817fc94c0cbf25d6fd3a743d867ba1108f4a8a84dec0254eb483d2e66652b712f4f48260405161265b9190614fce565b60608215806133af57508282105b156133cd576040516304d8557760e31b815260040160405180910390fd5b600060055483116133de57826133e2565b6005545b60010190508381036001600160401b03811115613401576134016151a0565b60405190808252806020026020018201604052801561343a57816020015b613427614728565b81526020019060019003908161341f5790505b509150835b818110156135dc576000818152600e60209081526040808320548151606081018352858152858552600b84529382902082516101008101909352805466ffffffffffffff81168452919493848101939290830190600160381b900460ff1660038111156134ae576134ae614cfd565b60038111156134bf576134bf614cfd565b8152815463ffffffff600160401b82048116602080850191909152600160601b83049091166040808501919091526001600160401b03600160801b840481166060860152600160c01b909304909216608084015260018085015460a085015260029094015460c0909301929092529284526000878152600e825292832093019201908461354d576000613552565b600185035b81526020808201929092526040908101600020815160808101835281546001600160401b0381168252600160401b810463ffffffff1694820194909452600160601b9093046001600160a01b0316918301919091526001015460608201529052845185908885039081106135c8576135c86151b6565b60209081029190910101525060010161343f565b505092915050565b60608215806135f257508282105b15613610576040516313e95aa760e21b815260040160405180910390fd5b600060045483116136215782613625565b6004545b60010190508381036001600160401b03811115613644576136446151a0565b60405190808252806020026020018201604052801561367d57816020015b61366a6146a4565b8152602001906001900390816136625790505b509150835b818110156135dc576040805180820182528281526000838152600760209081529083902083516101c081018552815462ffffff8116825260ff63010000008204168285015263ffffffff640100000000820481169683019690965261ffff600160401b820481166060840152600160501b80830482166080850152600160601b8304821660a08501526001600160501b03600160701b93849004811660c086015260019095015494851660e0850152840487166101008401529083048616610120830152600160901b8304909516610140820152600160b01b82048516610160820152600160c01b82048516610180820152600160d01b9091049093166101a0840152810191909152835184908784039081106137a1576137a16151b6565b6020908102919091010152600101613682565b6000828152600160205260409020546118359033614145565b6001600160a01b0381166000908152600f6020526040902054606090806001600160401b03811115613801576138016151a0565b60405190808252806020026020018201604052801561385357816020015b60408051608081018252600080825260208083018290529282018190526060820152825260001990920191018161381f5790505b50915060005b81811015612786576001600160a01b038085166000908152600f602090815260408083208584526001908101835292819020815160808101835281546001600160401b038116825263ffffffff600160401b82041694820194909452600160601b90930490941690820152910154606082015283518490839081106138e0576138e06151b6565b6020908102919091010152600101613859565b6000818152600d60205260408120546001600160a01b031661394857505060008052600d6020527f81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee546001600160a01b031690565b506000908152600d60205260409020546001600160a01b031690565b61396c613eda565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b60008481526007602052604081208054909162ffffff90911690036139c6576040516335aacf6560e11b815260040160405180910390fd5b80546301000000900460ff166000036139f257604051635acda82f60e01b815260040160405180910390fd5b348214613a1b57604051631162ee2d60e21b815260048101839052346024820152604401610c21565b600181015442600160501b90910463ffffffff161015613a4e576040516389d4186960e01b815260040160405180910390fd5b60018101548154600160c01b820461ffff908116600160d01b90930481169290920190911663ffffffff6401000000009092048216031683811015613ab057604051630d27c2b960e41b81526004810185905260248101829052604401610c21565b506000858152600a60209081526040808320338452909152812054613ad690859061521a565b9050613ae28633614469565b15613c7a576001820154600160701b900463ffffffff16421015613b33576001820154604051637283b3fb60e01b8152600160701b90910463ffffffff166004820152426024820152604401610c21565b8154600160501b900461ffff16811115613b76578154604051632fda893760e21b815260048101839052600160501b90910461ffff166024820152604401610c21565b8154600090613b96908690600160701b90046001600160501b031661522d565b9050838114613bc257604051630bf76b4560e41b81526004810185905260248101829052604401610c21565b6001830154600160901b900463ffffffff16421015613c4e576001830154835461ffff600160b01b9092048216600160401b9091048216031685811015613c2657604051631386b05760e01b81526004810187905260248101829052604401610c21565b5060018301805461ffff600160b01b808304821689019091160261ffff60b01b199091161790555b5060018201805461ffff600160c01b808304821688019091160261ffff60c01b19909116179055613d79565b6001820154600160901b900463ffffffff16421015613cc65760018201546040516381c5e80b60e01b8152600160901b90910463ffffffff166004820152426024820152604401610c21565b8154600160601b900461ffff16811115613d0957815460405163180da9c560e31b815260048101839052600160601b90910461ffff166024820152604401610c21565b6001820154600090613d259086906001600160501b031661522d565b9050838114613d51576040516396e80d5560e01b81526004810185905260248101829052604401610c21565b5060018201805461ffff600160d01b808304821688019091160261ffff60d01b199091161790555b6000868152600a602090815260408083203384529091529020819055613d9e8661291c565b8254604051632c7ef07760e01b81526001600160a01b03888116600483015262ffffff909216602482015260448101879052911690632c7ef07790606401600060405180830381600087803b158015613df657600080fd5b505af1158015613e0a573d6000803e3d6000fd5b50505050613e1a6000878561427c565b81546040805162ffffff90921682526020820186905281018490526001600160a01b03861690339088907f964be453e8b19c8fc2d856baaaeb5c6db9607b1f52fdff3f0a1e6cef6ac3bd019060600160405180910390a4505050505050565b613e81613eda565b6000828152600d602052604080822080546001600160a01b0319166001600160a01b0385169081179091559051909184917f8829ff1f8d6f20e53e317d9e72d94b852e0d669afb69b39a2b7d1923da57f5c09190a35050565b613ee5600033612a1f565b158015613f195750613f177f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0833612a1f565b155b15613f37576040516349349c6360e11b815260040160405180910390fd5b565b60008481526011602090815260408083208684529091528120613f5b916147ad565b60008060005b838110156140705760008781526011602090815260408083208984529091529020858583818110613f9457613f946151b6565b835460018101855560009485526020909420604090910292909201926002029091019050613fc28282615244565b50506003546001600160a01b0316858583818110613fe257613fe26151b6565b613ff89260206040909202019081019150614a75565b6001600160a01b03160361403457848482818110614018576140186151b6565b905060400201602001358261402d919061521a565b915061405e565b848482818110614046576140466151b6565b905060400201602001358361405b919061521a565b92505b806140688161527e565b915050613f61565b5060065482111561409757604051632060c15360e11b815260048101839052602401610c21565b6127106140a4828461521a565b11156140e1576127106140b7828461521a565b6040516372d6a00960e01b81526001600160401b0390921660048301526024820152604401610c21565b505050505050565b6140f4600033612a1f565b15801561412857506141267f940d6b1946ff1d2b5a9f1909219c3c81a370804b5ba0f91ec0828c99a2e6a68133612a1f565b155b15613f375760405162afd36160e01b815260040160405180910390fd5b6000828152602081815260408083206001600160a01b038516845290915290205460ff1661177857614181816001600160a01b03166014614502565b61418c836020614502565b60405160200161419d929190615297565b60408051601f198184030181529082905262461bcd60e51b8252610c2191600401614b47565b6000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916600117905551339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6142268282614145565b6000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000838152601160209081526040808320858452825280832080548251818502810185019093528083529192909190849084015b828210156142f8576000848152602090819020604080518082019091526002850290910180546001600160a01b031682526001908101548284015290835290920191016142b0565b50505050905060005b81518110156143875760006127106001600160401b031683838151811061432a5761432a6151b6565b60200260200101516020015185614341919061522d565b61434b919061530c565b9050614374838381518110614362576143626151b6565b6020026020010151600001518261438e565b508061437f8161527e565b915050614301565b5050505050565b804710156143b857604051635f938ce560e11b815260048101829052476024820152604401610c21565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114614405576040519150601f19603f3d011682016040523d82523d6000602084013e61440a565b606091505b505090508061442c5760405163df108a3b60e01b815260040160405180910390fd5b60405182906001600160a01b0385169033907fd1b078762514fc24641f0f647e92ba3f15f4cb8e732e84ce1d2686f15542837190600090a4505050565b6002546000906001600160a01b0316156144f957600254604051636c096c6d60e11b8152600481018590526001600160a01b0384811660248301529091169063d812d8da90604401602060405180830381865afa1580156144ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144f2919061532e565b9050612a44565b50600092915050565b6060600061451183600261522d565b61451c90600261521a565b6001600160401b03811115614533576145336151a0565b6040519080825280601f01601f19166020018201604052801561455d576020820181803683370190505b509050600360fc1b81600081518110614578576145786151b6565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106145a7576145a76151b6565b60200101906001600160f81b031916908160001a90535060006145cb84600261522d565b6145d690600161521a565b90505b600181111561464e576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061460a5761460a6151b6565b1a60f81b828281518110614620576146206151b6565b60200101906001600160f81b031916908160001a90535060049490941c936146478161534b565b90506145d9565b50831561469d5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610c21565b9392505050565b604080518082018252600080825282516101c0810184528181526020818101839052938101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a0810191909152909182015290565b60405180606001604052806000815260200161478360408051610100810190915260008082526020820190815260006020820181905260408201819052606082018190526080820181905260a0820181905260c09091015290565b81526040805160808101825260008082526020828101829052928201819052606082015291015290565b50805460008255600202906000526020600020908101906147ce91906147d1565b50565b5b808211156147f75780546001600160a01b0319168155600060018201556002016147d2565b5090565b60006020828403121561480d57600080fd5b5035919050565b602080825282518282018190526000919060409081850190868401855b8281101561485f57815180516001600160a01b03168552860151868501529284019290850190600101614831565b5091979650505050505050565b60008083601f84011261487e57600080fd5b5081356001600160401b0381111561489557600080fd5b6020830191508360208260061b85010111156148b057600080fd5b9250929050565b6000806000604084860312156148cc57600080fd5b8335925060208401356001600160401b038111156148e957600080fd5b6148f58682870161486c565b9497909650939450505050565b6000806040838503121561491557600080fd5b50508035926020909101359150565b80518252602081015161493f60208401825162ffffff169052565b602081015160ff8116604085015250604081015163ffffffff8116606085015250606081015161ffff8116608085015250608081015161ffff811660a08501525060a081015161ffff811660c08501525060c08101516001600160501b03811660e08501525060e08101516101006149c1818601836001600160501b03169052565b82015190506101206149da8582018363ffffffff169052565b82015190506101406149f38582018363ffffffff169052565b8201519050610160614a0c8582018363ffffffff169052565b8201519050610180614a238582018361ffff169052565b82015190506101a0614a3a8582018361ffff169052565b919091015161ffff81166101c08501529050505050565b6101e08101612a448284614924565b6001600160a01b03811681146147ce57600080fd5b600060208284031215614a8757600080fd5b813561469d81614a60565b80151581146147ce57600080fd5b60008060408385031215614ab357600080fd5b8235614abe81614a60565b91506020830135614ace81614a92565b809150509250929050565b60008060408385031215614aec57600080fd5b823591506020830135614ace81614a92565b60008060408385031215614b1157600080fd5b823591506020830135614ace81614a60565b60005b83811015614b3e578181015183820152602001614b26565b50506000910152565b6020815260008251806020840152614b66816040850160208701614b23565b601f01601f19169190910160400192915050565b60006101608284031215614b8d57600080fd5b50919050565b6000806101808385031215614ba757600080fd5b82359150614bb88460208501614b7a565b90509250929050565b600060e08284031215614b8d57600080fd5b6000806101008385031215614be757600080fd5b82359150614bb88460208501614bc1565b6020808252825182820181905260009190848201906040850190845b81811015614c3b57614c27838551614924565b928401926101e09290920191600101614c14565b50909695505050505050565b60008060006101008486031215614c5d57600080fd5b614c678585614bc1565b925060e08401356001600160401b038111156148e957600080fd5b6020808252825182820181905260009190848201906040850190845b81811015614c3b57614cea8385516001600160401b03815116825263ffffffff60208201511660208301526001600160a01b036040820151166040830152606081015160608301525050565b9284019260809290920191600101614c9e565b634e487b7160e01b600052602160045260246000fd5b60048110614d3157634e487b7160e01b600052602160045260246000fd5b9052565b66ffffffffffffff81511682526020810151614d546020840182614d13565b50604081015163ffffffff8082166040850152806060840151166060850152505060808101516001600160401b0380821660808501528060a08401511660a0850152505060c081015160c083015260e081015160e08301525050565b805182526020810151614dc66020840182614d35565b5060409081015180516001600160401b0316610120840152602081015163ffffffff16610140840152908101516001600160a01b03166101608301526060015161018090910152565b6101a08101612a448284614db0565b6020808252825182820181905260009190848201906040850190845b81811015614c3b57614e4d838551614db0565b928401926101a09290920191600101614e3a565b60008060408385031215614e7457600080fd5b8235614e7f81614a60565b946020939093013593505050565b60008060006101808486031215614ea357600080fd5b614ead8585614b7a565b92506101608401356001600160401b038111156148e957600080fd5b60008060008060808587031215614edf57600080fd5b843593506020850135614ef181614a60565b93969395505050506040820135916060013590565b634e487b7160e01b600052601160045260246000fd5b81810381811115612a4457612a44614f06565b6101008101612a448284614d35565b600060208284031215614f5057600080fd5b813563ffffffff8116811461469d57600080fd5b600060208284031215614f7657600080fd5b813561ffff8116811461469d57600080fd5b600060208284031215614f9a57600080fd5b813561469d81614a92565b600060208284031215614fb757600080fd5b81356001600160501b038116811461469d57600080fd5b815462ffffff811682526101c0820190601881901c60ff16602084015263ffffffff602082901c8116604085015261ffff604083901c8116606086015261502060808601828560501c1661ffff169052565b61503560a08601828560601c1661ffff169052565b6001600160501b0360709390931c831660c0860152600186015492831660e0860152605083901c821663ffffffff166101008601526150826101208601838560701c1663ffffffff169052565b61509a6101408601838560901c1663ffffffff169052565b6150b06101608601828560b01c1661ffff169052565b6150c66101808601828560c01c1661ffff169052565b6150dc6101a08601828560d01c1661ffff169052565b50505092915050565b6000602082840312156150f757600080fd5b81356001600160401b038116811461469d57600080fd5b815466ffffffffffffff8116825261010082019061513660208401603883901c60ff16614d13565b63ffffffff61515260408501828460401c1663ffffffff169052565b61516960608501828460601c1663ffffffff169052565b50608081811c6001600160401b03169084015260c081901c60a084015250600183015460c083015260029092015460e09091015290565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000602082840312156151de57600080fd5b813566ffffffffffffff8116811461469d57600080fd5b60006020828403121561520757600080fd5b813562ffffff8116811461469d57600080fd5b80820180821115612a4457612a44614f06565b8082028115828204841417612a4457612a44614f06565b813561524f81614a60565b6001600160a01b0381166bffffffffffffffffffffffff60a01b83541617825550602082013560018201555050565b60006001820161529057615290614f06565b5060010190565b7f5065726d697373696f6e733a206163636f756e742000000000000000000000008152600083516152cf816015850160208801614b23565b7001034b99036b4b9b9b4b733903937b6329607d1b6015918401918201528351615300816026840160208801614b23565b01602601949350505050565b60008261532957634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561534057600080fd5b815161469d81614a92565b60008161535a5761535a614f06565b50600019019056fea2646970667358221220b79279b341608ba1feeb3f672ebdbf57b3b6a03b88e66bfbcfe18fbe4304a28f64736f6c63430008140033

Verified Source Code Full Match

Compiler: v0.8.20+commit.a1b79de6 EVM: paris Optimization: Yes (300 runs)
IPermissions.sol 88 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @author thirdweb

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IPermissions {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}
Permissions.sol 168 lines
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @author thirdweb

import "./interface/IPermissions.sol";
import "../lib/TWStrings.sol";

/**
 *  @title   Permissions
 *  @dev     This contracts provides extending-contracts with role-based access control mechanisms
 */
contract Permissions is IPermissions {
    /// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
    mapping(bytes32 => mapping(address => bool)) private _hasRole;

    /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
    mapping(bytes32 => bytes32) private _getRoleAdmin;

    /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles.
    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /// @dev Modifier that checks if an account has the specified role; reverts otherwise.
    modifier onlyRole(bytes32 role) {
        _checkRole(role, msg.sender);
        _;
    }

    /**
     *  @notice         Checks whether an account has a particular role.
     *  @dev            Returns `true` if `account` has been granted `role`.
     *
     *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     *  @param account  Address of the account for which the role is being checked.
     */
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        return _hasRole[role][account];
    }

    /**
     *  @notice         Checks whether an account has a particular role;
     *                  role restrictions can be swtiched on and off.
     *
     *  @dev            Returns `true` if `account` has been granted `role`.
     *                  Role restrictions can be swtiched on and off:
     *                      - If address(0) has ROLE, then the ROLE restrictions
     *                        don't apply.
     *                      - If address(0) does not have ROLE, then the ROLE
     *                        restrictions will apply.
     *
     *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     *  @param account  Address of the account for which the role is being checked.
     */
    function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) {
        if (!_hasRole[role][address(0)]) {
            return _hasRole[role][account];
        }

        return true;
    }

    /**
     *  @notice         Returns the admin role that controls the specified role.
     *  @dev            See {grantRole} and {revokeRole}.
     *                  To change a role's admin, use {_setRoleAdmin}.
     *
     *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     */
    function getRoleAdmin(bytes32 role) external view override returns (bytes32) {
        return _getRoleAdmin[role];
    }

    /**
     *  @notice         Grants a role to an account, if not previously granted.
     *  @dev            Caller must have admin role for the `role`.
     *                  Emits {RoleGranted Event}.
     *
     *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     *  @param account  Address of the account to which the role is being granted.
     */
    function grantRole(bytes32 role, address account) public virtual override {
        _checkRole(_getRoleAdmin[role], msg.sender);
        if (_hasRole[role][account]) {
            revert("Can only grant to non holders");
        }
        _setupRole(role, account);
    }

    /**
     *  @notice         Revokes role from an account.
     *  @dev            Caller must have admin role for the `role`.
     *                  Emits {RoleRevoked Event}.
     *
     *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     *  @param account  Address of the account from which the role is being revoked.
     */
    function revokeRole(bytes32 role, address account) public virtual override {
        _checkRole(_getRoleAdmin[role], msg.sender);
        _revokeRole(role, account);
    }

    /**
     *  @notice         Revokes role from the account.
     *  @dev            Caller must have the `role`, with caller being the same as `account`.
     *                  Emits {RoleRevoked Event}.
     *
     *  @param role     keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
     *  @param account  Address of the account from which the role is being revoked.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        if (msg.sender != account) {
            revert("Can only renounce for self");
        }
        _revokeRole(role, account);
    }

    /// @dev Sets `adminRole` as `role`'s admin role.
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = _getRoleAdmin[role];
        _getRoleAdmin[role] = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /// @dev Sets up `role` for `account`
    function _setupRole(bytes32 role, address account) internal virtual {
        _hasRole[role][account] = true;
        emit RoleGranted(role, account, msg.sender);
    }

    /// @dev Revokes `role` from `account`
    function _revokeRole(bytes32 role, address account) internal virtual {
        _checkRole(role, account);
        delete _hasRole[role][account];
        emit RoleRevoked(role, account, msg.sender);
    }

    /// @dev Checks `role` for `account`. Reverts with a message including the required role.
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!_hasRole[role][account]) {
            revert(
                string(
                    abi.encodePacked(
                        "Permissions: account ",
                        TWStrings.toHexString(uint160(account), 20),
                        " is missing role ",
                        TWStrings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /// @dev Checks `role` for `account`. Reverts with a message including the required role.
    function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual {
        if (!hasRoleWithSwitch(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "Permissions: account ",
                        TWStrings.toHexString(uint160(account), 20),
                        " is missing role ",
                        TWStrings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }
}
TWAddress.sol 222 lines
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;

/// @author thirdweb

/**
 * @dev Collection of functions related to the address type
 */
library TWAddress {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * [EIP1884](https://eips.ethereum.org/EIPS/eip-1884) increases 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 `recipient`, 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 payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}
TWStrings.sol 67 lines
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;

/// @author thirdweb

/**
 * @dev String operations.
 */
library TWStrings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}
IProductMarket.sol 307 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @author Syky - Nathan Rempel

interface IProductMarket {
    /*//////////////////////////////////////////////////////////////
                        Structs
    //////////////////////////////////////////////////////////////*/

    /**
     *  @notice A data structure to configure Product Listings
     *  @dev Should compress to 512 bytes.
     *
     *  @param productId The productId of the asset being sold.     (max 16,777,215)
     *  @param quantity The total quantity listed for sale.         (max 65,535)
     *  @param reserved The quantity availablein the VIP window.    (max 65,535)
     *  @param vipLimit The purchase limit for VIPs.                (max 65,535)
     *  @param pubLimit The purchase limit for public.              (max 65,535)
     *  @param vipPrice The price in ETH per item for VIPs.         (max 1,208,925.8 ETH)
     *  @param pubPrice The price in ETH per item for public.       (max 1,208,925.8 ETH)
     *  @param endTimestamp The sale end timestamp for everyone.
     *  @param vipStartTimestamp The sale start timestamp for VIPs.
     *  @param pubStartTimestamp The sale start timestamp for public.
     *  @param enabled The listing is enabled
     */
    struct ListingParams {
        uint24 productId;
        uint16 quantity;
        uint16 reserved;
        uint16 vipLimit;
        uint16 pubLimit;
        uint80 vipPrice;
        uint80 pubPrice;
        uint32 endTimestamp;
        uint32 vipStartTimestamp;
        uint32 pubStartTimestamp;
        bool enabled;
        //spare 160 bytes space capacity
        //compresses to one 256
    }

    /**
     *  @notice A data structure to store Product Listings
     *  @dev Should compress to 512 bytes.
     *
     *  @param productId The productId of the asset being sold.     (max 16,777,215)
     *  @param enabled The listing is enabled                       (value > 0 = true)
     *  @param quantity The total quantity listed for sale.         (max 65,535)
     *  @param reserved The quantity availablein the VIP window.    (max 65,535)
     *  @param vipLimit The purchase limit for VIPs.                (max 65,535)
     *  @param pubLimit The purchase limit for public.              (max 65,535)
     *  @param vipPrice The price in ETH per item for VIPs.         (max 1,208,925.8 ETH)
     *  @param pubPrice The price in ETH per item for public.       (max 1,208,925.8 ETH)
     *  @param endTimestamp The sale end timestamp for everyone.
     *  @param vipStartTimestamp The sale start timestamp for VIPs.
     *  @param pubStartTimestamp The sale start timestamp for public.
     *  @param resPurchased The VIP purchase quantity in the VIP window.
     *  @param vipPurchased The VIP purchase quantity of the sale in any window.
     *  @param pubPurchased The public purchase quantity of the sale.
     */
    struct Listing {
        uint24 productId;
        uint8 enabled;
        uint32 quantity;
        uint16 reserved;
        uint16 vipLimit;
        uint16 pubLimit;
        uint80 vipPrice;
        uint80 pubPrice;
        uint32 endTimestamp;
        uint32 vipStartTimestamp;
        uint32 pubStartTimestamp;
        uint16 resPurchased;
        uint16 vipPurchased;
        uint16 pubPurchased;
    }

    /// @notice A data structure to return a set of listings
    struct ListingQuery {
        uint256 listingId;
        Listing listingData;
    }
    struct PayoutReceiver {
        address payable receiver;
        uint256 percentage;
    }

    enum AuctionStatus {
        UNSET,
        CREATED,
        COMPLETED,
        CANCELLED
    }

    struct AuctionParams {
        uint56 productId;
        uint32 startTimestamp;
        uint32 endTimestamp;
        uint64 bidBufferBps;
        uint64 timeBufferSeconds;
        uint256 minimumBidAmount;
        uint256 reserveBidAmount;
    }

    struct Auction {
        uint56 productId;
        AuctionStatus status;
        uint32 startTimestamp;
        uint32 endTimestamp;
        uint64 bidBufferBps;
        uint64 timeBufferSeconds;
        uint256 minimumBidAmount;
        uint256 reserveBidAmount;
    }

    struct AuctionQuery {
        uint256 auctionId;
        Auction auctionData;
        Bid winningBid;
    }

    struct Bid {
        uint64 auctionId;
        uint32 bidTimestamp;
        address bidder;
        uint256 bidAmount;
    }

    struct BidHistory {
        uint256 count;
        mapping(uint256 => Bid) bids;
    }

    struct ProductLookup {
        uint256 count;
        mapping(uint256 => uint256) id;
    }

    /*//////////////////////////////////////////////////////////////
                        Events
    //////////////////////////////////////////////////////////////*/

    /// @dev Emitted when a new listing is created.
    event ListingAdded(uint256 indexed listingId, Listing listing);

    /// @dev Emitted when the parameters of a listing are updated.
    event ListingUpdated(uint256 indexed listingId, Listing listing);

    /// @dev Emitted when a listing is enabled or disabled.
    event ListingEnabled(uint256 indexed listingId, bool indexed enabled);

    /// @dev Emitted when a listing token contract is updated.
    event ListingContract(uint256 indexed listingId, address indexed tokenContract);

    /// @dev Emitted when a new sale occurs for a listing.
    event NewSale(
        uint256 indexed listingId,
        address indexed buyer,
        address indexed recipient,
        uint256 productId,
        uint256 quantity,
        uint256 price
    );

    /// @dev Emitted when withdrawal recipient is approved or unapproved.
    event WithdrawalRecipientApproved(address indexed recipient, bool indexed approved);

    /// @dev Emitted when market income is withdrawn.
    event IncomeWithdrawal(
        address indexed operator,
        address indexed recipient,
        uint256 indexed amount
    );

    event AuctionAdded(uint256 indexed auctionId, Auction auction);

    event AuctionUpdated(uint256 indexed auctionId, Auction auction);

    event AuctionCancelled(uint256 indexed auctionId);

    /// @dev Emitted when a listing token contract is updated.
    event AuctionContract(uint256 indexed auctionId, address indexed tokenContract);

    event AuctionClosed(
        uint256 indexed auctionId,
        address indexed bidder,
        uint256 indexed bidAmount,
        uint256 productId,
        bool tokenAwarded,
        bool bidRefunded
    );

    event NewBid(
        uint256 indexed auctionId,
        address indexed bidder,
        uint256 indexed bidAmount,
        Auction auction
    );

    /*//////////////////////////////////////////////////////////////
                        Errors
    //////////////////////////////////////////////////////////////*/

    /// @dev Action requires the finance or admin role
    error FinanceRoleRequired();

    /// @dev Action requires the manager or admin role
    error ManagerRoleRequired();

    /// @dev VIP Sale start must be before or equal to Public Sale start
    error ListingVipStartAfterPubStart(
        uint32 vipStartTimestamp,
        uint32 pubStartTimestamp
    );

    /// @dev Public Sale start must be before sale close
    error ListingPubStartAfterEnd(uint32 pubStartTimestamp, uint32 endTimestamp);

    error ListingReserveExceedsQuantity(uint16 reserved, uint16 quantity);

    error ListingDoesNotExist();

    error ListingPurchasesExceedsQuantity(uint16 quantity, uint256 purchased);

    error ListingPurchasesExceedsReserve(uint16 reserve, uint256 purchased);

    error ListingNotEnabled();

    error ListingHasEnded();

    error ListingQueryInvalidRange();

    error ListingExpectedPriceNotEqualValue(
        uint256 expectedTotalPrice,
        uint256 transactionValue
    );

    error ListingInsufficientQuantity(
        uint256 requestedQuantity,
        uint256 availableQuantity
    );

    error ListingVipSaleNotStarted(uint64 vipStartTime, uint256 currentTimestamp);

    error ListingPublicSaleNotStarted(uint64 publicStartTime, uint256 currentTimestamp);

    error ListingVipLimitExceeded(uint256 purchases, uint256 limit);

    error ListingVipReserveExceeded(uint256 requestedQuantity, uint256 availableQuantity);

    error ListingPublicLimitExceeded(uint256 purchases, uint256 limit);

    error ListingVipExpectedPriceInvalid(
        uint256 expectedTotalPrice,
        uint256 actualTotalPrice
    );

    error ListingPublicExpectedPriceInvalid(
        uint256 expectedTotalPrice,
        uint256 actualTotalPrice
    );

    error ListingPercentageOverflow(uint256 total);

    error PayoutPercentageExceedsMax(uint256 percentage, uint256 max);

    error AuctionQueryInvalidRange();

    error AuctionStartAfterEnd(uint32 startTimestamp, uint32 endTimestamp);

    error AuctionBufferBpsExceedsMax();

    error AuctionDoesNotExist();

    error AuctionNotActive();

    error AuctionNotEnded(uint32 endTimestamp, uint256 currentTimestamp);

    error AuctionHasBids(uint256 numberOfBids);

    error AuctionHasNoBids();

    error AuctionReserveNotMet(uint256 highestBidAmount, uint256 reserveBidAmount);

    error AuctionReserveWasMet(uint256 highestBidAmount, uint256 reserveBidAmount);

    error AuctionPreviousBidRefundRejected();

    error AuctionNotStarted(uint32 startTimestamp, uint256 currentTimestamp);

    error AuctionHasEnded();

    error AuctionBidAmountZero();

    error AuctionBidBelowMinimum(uint256 bidAmount, uint256 minimumBidAmount);

    error AuctionBidAmountNotEqualValue(uint256 bidAmount, uint256 transactionValue);

    error AuctionBidAmountInsufficient(uint256 bidAmount, uint256 requiredBidAmount);

    error WithdrawalRecipientNotApproved();

    error WithdrawalRecipientRejectedTransfer();

    error WithdrawalInsufficientBalance(uint256 requested, uint256 available);
}
IProductToken.sol 75 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @author Syky - Nathan Rempel

interface IProductToken {
    /*//////////////////////////////////////////////////////////////
                        Methods
    //////////////////////////////////////////////////////////////*/

    /// @notice Lets an authorized address mint single NFTs to a recipient.
    function mintTo(address _to, uint256 _productId) external;

    function mintTo(address _to, uint256 _productId, bytes memory _data) external;

    function mintTo(
        address _to,
        uint256 _productId,
        string calldata _uri,
        bytes memory _data
    ) external;

    /// @notice Lets an authorized address mint multiple NFTs at once to a recipient.
    function batchMintTo(address _to, uint256 _productId, uint256 _quantity) external;

    function batchMintTo(
        address _to,
        uint256 _productId,
        uint256 _quantity,
        bytes memory _data
    ) external;

    function batchMintTo(
        address _to,
        uint256 _productId,
        uint256 _quantity,
        string[] calldata _uris,
        bytes memory _data
    ) external;

    /*//////////////////////////////////////////////////////////////
                        Events
    //////////////////////////////////////////////////////////////*/

    /// @dev Emitted when all minting is enabled or disabled
    event GlobalMintingRestricted(bool restricted);

    /// @dev Emitted when all transfers are enabled or disabled
    event GlobalTransfersRestricted(bool restricted);

    /// @dev Emitted when all burning is enabled or disabled
    event GlobalBurningRestricted(bool restricted);

    /*//////////////////////////////////////////////////////////////
                        Errors
    //////////////////////////////////////////////////////////////*/

    /// @dev URIs array length for batch mint must match _quantity
    error BatchMintURICountMismatch();

    /// @dev Action requires the manager or admin role
    error ManagerRoleRequired();

    /// @dev Action requires the minting or admin role
    error MintingRoleRequired();

    /// @dev Minting has been disabled via address(0) role
    error GlobalMintingDisabled();

    /// @dev Transfers have been disabled via address(0) role
    error GlobalTransfersDisabled();

    /// @dev Burning has been disabled via address(0) role
    error GlobalBurningDisabled();
}
IVipManager.sol 65 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @author Syky - Nathan Rempel

interface IVipManager {
    struct VipConditions {
        GroupConditions groupConditions;
        ProductConditions[] productConditions;
        ERC721Conditions[] erc721Conditions;
        ERC1155Conditions[] erc1155Conditions;
    }

    struct VipConditionsData {
        uint256 productLength;
        uint256 erc721Length;
        uint256 erc1155Length;
        GroupConditions groupConditions;
        mapping(uint256 => ERC721Conditions) erc721Conditions;
        mapping(uint256 => ERC1155ConditionsData) erc1155Conditions;
        mapping(uint256 => ProductConditionsData) productConditions;
    }

    struct GroupConditions {
        address groupContract;
        uint256 groupId;
    }

    struct ERC721Conditions {
        address tokenContract;
    }

    struct ProductConditions {
        address tokenContract;
        uint256[] productIds;
    }

    struct ERC1155Conditions {
        address tokenContract;
        uint256[] tokenIds;
    }

    struct ERC1155ConditionsData {
        address tokenContract;
        uint96 length;
        mapping(uint256 => uint256) tokenIds;
    }

    struct ProductConditionsData {
        address tokenContract;
        uint96 length;
        mapping(uint256 => uint256) productIds;
    }

    function isVip(uint256 _selectorId, address _member) external view returns (bool);

    error ManagerRoleRequired();

    event VipSelectorConditionsUpdated(
        uint256 indexed selectorId,
        uint256 indexed conditionsId
    );

    event VipConditionsUpdated(uint256 indexed conditionsId, VipConditions conditions);
}
ProductMarket.sol 866 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @author Syky - Nathan Rempel

import "./interface/IProductMarket.sol";
import "./interface/IProductToken.sol";
import "./interface/IVipManager.sol";

import "@thirdweb-dev/contracts/extension/Permissions.sol";
import "@thirdweb-dev/contracts/lib/TWAddress.sol";

contract ProductMarket is IProductMarket, Permissions {
    using TWAddress for address;

    /*//////////////////////////////////////////////////////////////
                            Constants
    //////////////////////////////////////////////////////////////*/

    bytes32 public constant FINANCE_ROLE = keccak256("FINANCE_ROLE");
    bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");

    /// @dev The max bps of the contract. So, 10_000 == 100 %
    uint64 private constant MAX_BPS = 10_000;

    /*//////////////////////////////////////////////////////////////
                            Mappings
    //////////////////////////////////////////////////////////////*/

    address private _vipContract;

    address private _feeReceiver;

    //counter for total listings created
    uint256 private _totalListings;

    //counter for total auctions created
    uint256 private _totalAuctions;

    //max payout distribution for artists
    uint256 private _maxPayoutsDistribution;

    //mapping of sale parameters to listingId
    mapping(uint256 => Listing) private _listings;

    //reverse lookup of listings by productId
    mapping(uint256 => ProductLookup) private _productListings;

    //mapping of product token address to listingId
    mapping(uint256 => address) private _listingContracts;

    //mapping of purchases made by an address to listingId
    mapping(uint256 => mapping(address => uint256)) private _listingPurchases;

    //mapping of auction parameters to auctionId
    mapping(uint256 => Auction) private _auctions;

    //reverse lookup of auctions by productId
    mapping(uint256 => ProductLookup) private _productAuctions;

    //mapping of product token address to auctionId
    mapping(uint256 => address) private _auctionContracts;

    //mapping of auction bids to auctionId
    mapping(uint256 => BidHistory) private _auctionBids;

    //mapping of auction bids to address
    mapping(address => BidHistory) private _addressBids;

    //mapping of approved withdrawal addresses
    mapping(address => bool) private _withdrawalApproved;

    //listing receivers for payouts (0 - Listings / 1 - Auctions)
    mapping(uint256 => mapping(uint256 => PayoutReceiver[])) private _payouts;

    /*//////////////////////////////////////////////////////////////
                            Constructor
    //////////////////////////////////////////////////////////////*/

    constructor(
        address defaultAdmin_,
        address defaultToken_,
        address vipContract_,
        address feeReceiver_
    ) {
        _listingContracts[0] = defaultToken_;
        _auctionContracts[0] = defaultToken_;
        _vipContract = vipContract_;
        _maxPayoutsDistribution = 9_000; // 90% for artists - 10% SYKY fee
        _feeReceiver = feeReceiver_;
        _setupRole(DEFAULT_ADMIN_ROLE, defaultAdmin_);
        _setRoleAdmin(FINANCE_ROLE, DEFAULT_ADMIN_ROLE);
        _setRoleAdmin(MANAGER_ROLE, DEFAULT_ADMIN_ROLE);
    }

    /*//////////////////////////////////////////////////////////////
                        Admin functions
    //////////////////////////////////////////////////////////////*/

    function setVipContract(address _vipContract_) external onlyManager {
        _vipContract = _vipContract_;
    }

    function setFeeReceiver(address _feeReceiver_) external onlyManager {
        _feeReceiver = _feeReceiver_;
    }

    function createListing(
        ListingParams calldata _params,
        PayoutReceiver[] calldata receivers
    ) external onlyManager returns (uint256 listingId) {
        // overflow is impossible short of 2**256-1 listings being created
        unchecked {
            listingId = ++_totalListings;
        }

        Listing storage listing = _listings[listingId];

        if (_params.pubStartTimestamp < _params.vipStartTimestamp)
            revert ListingVipStartAfterPubStart(
                _params.vipStartTimestamp,
                _params.pubStartTimestamp
            );
        if (_params.endTimestamp <= _params.pubStartTimestamp)
            revert ListingPubStartAfterEnd(
                _params.pubStartTimestamp,
                _params.endTimestamp
            );
        if (_params.quantity < _params.reserved)
            revert ListingReserveExceedsQuantity(_params.reserved, _params.quantity);

        listing.productId = _params.productId;
        listing.enabled = _params.enabled ? 1 : 0;
        listing.quantity = _params.quantity;
        listing.reserved = _params.reserved;
        listing.vipLimit = _params.vipLimit;
        listing.pubLimit = _params.pubLimit;
        listing.vipPrice = _params.vipPrice;
        listing.pubPrice = _params.pubPrice;
        listing.endTimestamp = _params.endTimestamp;
        listing.vipStartTimestamp = _params.vipStartTimestamp;
        listing.pubStartTimestamp = _params.pubStartTimestamp;

        //reverse lookup
        unchecked {
            uint256 reverseIdx = _productListings[_params.productId].count++;
            _productListings[_params.productId].id[reverseIdx] = listingId;
        }

        if (receivers.length > 0) {
            _setPayouts(0, listingId, receivers);
        }

        emit ListingAdded(listingId, listing);
    }

    function setListingPayouts(
        uint256 _listingId,
        PayoutReceiver[] calldata receivers
    ) external onlyManager {
        _setPayouts(0, _listingId, receivers);
    }

    function setAuctionPayouts(
        uint256 _auctionId,
        PayoutReceiver[] calldata receivers
    ) external onlyManager {
        _setPayouts(1, _auctionId, receivers);
    }

    function _setPayouts(
        uint256 _index,
        uint256 _id,
        PayoutReceiver[] calldata receivers
    ) internal {
        delete _payouts[_index][_id];

        uint256 percForDesigners;
        uint256 percForSYKY;

        for (uint256 i = 0; i < receivers.length; i++) {
            _payouts[_index][_id].push(receivers[i]);

            if (receivers[i].receiver == _feeReceiver) {
                percForSYKY += receivers[i].percentage;
            } else {
                percForDesigners += receivers[i].percentage;
            }
        }

        if (percForDesigners > _maxPayoutsDistribution) {
            revert ListingPercentageOverflow(percForDesigners);
        }

        if ((percForDesigners + percForSYKY) > MAX_BPS) {
            revert PayoutPercentageExceedsMax(MAX_BPS, (percForDesigners + percForSYKY));
        }
    }

    function updateListing(
        uint256 _listingId,
        ListingParams calldata _params
    ) external onlyManager {
        Listing storage listing = _listings[_listingId];

        if (listing.productId == 0) revert ListingDoesNotExist();
        if (_params.pubStartTimestamp < _params.vipStartTimestamp)
            revert ListingVipStartAfterPubStart(
                _params.vipStartTimestamp,
                _params.pubStartTimestamp
            );
        if (_params.endTimestamp <= _params.pubStartTimestamp)
            revert ListingPubStartAfterEnd(
                _params.pubStartTimestamp,
                _params.endTimestamp
            );
        if (_params.quantity < _params.reserved)
            revert ListingReserveExceedsQuantity(_params.reserved, _params.quantity);

        unchecked {
            uint256 totalPurchased = listing.vipPurchased + listing.pubPurchased;

            if (_params.quantity < totalPurchased)
                revert ListingPurchasesExceedsQuantity(_params.quantity, totalPurchased);
            if (_params.reserved < listing.resPurchased)
                revert ListingPurchasesExceedsReserve(
                    _params.reserved,
                    listing.resPurchased
                );
        }

        listing.enabled = _params.enabled ? 1 : 0;
        listing.quantity = _params.quantity;
        listing.reserved = _params.reserved;
        listing.vipLimit = _params.vipLimit;
        listing.pubLimit = _params.pubLimit;
        listing.vipPrice = _params.vipPrice;
        listing.pubPrice = _params.pubPrice;
        listing.endTimestamp = _params.endTimestamp;
        listing.vipStartTimestamp = _params.vipStartTimestamp;
        listing.pubStartTimestamp = _params.pubStartTimestamp;

        emit ListingUpdated(_listingId, listing);
    }

    function enableListing(uint256 _listingId, bool _enabled) external onlyManager {
        _listings[_listingId].enabled = _enabled ? 1 : 0;
        emit ListingEnabled(_listingId, _enabled);
    }

    function setListingContract(
        uint256 _listingId,
        address _tokenContract
    ) external onlyManager {
        _listingContracts[_listingId] = _tokenContract;
        emit ListingContract(_listingId, _tokenContract);
    }

    function setAuctionContract(
        uint256 _auctionId,
        address _tokenContract
    ) external onlyManager {
        _auctionContracts[_auctionId] = _tokenContract;
        emit AuctionContract(_auctionId, _tokenContract);
    }

    function createAuction(
        AuctionParams calldata _params,
        PayoutReceiver[] calldata receivers
    ) external onlyManager returns (uint256 auctionId) {
        unchecked {
            auctionId = ++_totalAuctions;
        }

        Auction storage auction = _auctions[auctionId];

        if (_params.endTimestamp <= _params.startTimestamp)
            revert AuctionStartAfterEnd(_params.startTimestamp, _params.endTimestamp);
        if (MAX_BPS < _params.bidBufferBps) revert AuctionBufferBpsExceedsMax();

        auction.productId = _params.productId;
        auction.status = AuctionStatus.CREATED;
        auction.startTimestamp = _params.startTimestamp;
        auction.endTimestamp = _params.endTimestamp;
        auction.bidBufferBps = _params.bidBufferBps;
        auction.timeBufferSeconds = _params.timeBufferSeconds;
        auction.minimumBidAmount = _params.minimumBidAmount;
        auction.reserveBidAmount = _params.reserveBidAmount;

        unchecked {
            uint256 reverseIdx = _productAuctions[_params.productId].count++;
            _productAuctions[_params.productId].id[reverseIdx] = auctionId;
        }

        if (receivers.length > 0) {
            _setPayouts(1, auctionId, receivers);
        }

        emit AuctionAdded(auctionId, auction);
    }

    function updateAuction(
        uint256 _auctionId,
        AuctionParams calldata _params
    ) external onlyManager {
        Auction storage auction = _auctions[_auctionId];

        if (auction.status == AuctionStatus.UNSET) revert AuctionDoesNotExist();
        if (auction.status != AuctionStatus.CREATED) revert AuctionNotActive();
        if (_params.endTimestamp <= _params.startTimestamp)
            revert AuctionStartAfterEnd(_params.startTimestamp, _params.endTimestamp);
        if (MAX_BPS < _params.bidBufferBps) revert AuctionBufferBpsExceedsMax();

        auction.startTimestamp = _params.startTimestamp;
        auction.endTimestamp = _params.endTimestamp;
        auction.bidBufferBps = _params.bidBufferBps;
        auction.timeBufferSeconds = _params.timeBufferSeconds;
        auction.minimumBidAmount = _params.minimumBidAmount;
        auction.reserveBidAmount = _params.reserveBidAmount;

        emit AuctionUpdated(_auctionId, auction);
    }

    function cancelAuction(uint256 _auctionId) external onlyManager {
        Auction storage auction = _auctions[_auctionId];

        if (auction.status == AuctionStatus.UNSET) revert AuctionDoesNotExist();
        if (auction.status != AuctionStatus.CREATED) revert AuctionNotActive();
        if (_auctionBids[_auctionId].count != 0)
            revert AuctionHasBids(_auctionBids[_auctionId].count);

        auction.status = AuctionStatus.CANCELLED;

        emit AuctionCancelled(_auctionId);
    }

    function rewardAuction(uint256 _auctionId) external {
        Auction storage auction = _auctions[_auctionId];

        if (auction.status == AuctionStatus.UNSET) revert AuctionDoesNotExist();
        if (auction.status != AuctionStatus.CREATED) revert AuctionNotActive();
        if (block.timestamp < auction.endTimestamp)
            revert AuctionNotEnded(auction.endTimestamp, block.timestamp);

        uint256 numberOfBids = _auctionBids[_auctionId].count;
        if (numberOfBids == 0) revert AuctionHasNoBids();

        Bid memory winningBid = _auctionBids[_auctionId].bids[numberOfBids - 1];
        if (winningBid.bidAmount < auction.reserveBidAmount)
            revert AuctionReserveNotMet(winningBid.bidAmount, auction.reserveBidAmount);

        //reentrancy protection
        auction.status = AuctionStatus.COMPLETED;

        IProductToken(getAuctionContract(_auctionId)).mintTo(
            winningBid.bidder,
            auction.productId
        );

        _processPayment(1, _auctionId, winningBid.bidAmount);

        emit AuctionClosed(
            _auctionId,
            winningBid.bidder,
            winningBid.bidAmount,
            auction.productId,
            true,
            false
        );
    }

    function closeAuction(uint256 _auctionId) external onlyManager {
        Auction storage auction = _auctions[_auctionId];

        if (auction.status == AuctionStatus.UNSET) revert AuctionDoesNotExist();
        if (auction.status != AuctionStatus.CREATED) revert AuctionNotActive();
        if (block.timestamp < auction.endTimestamp)
            revert AuctionNotEnded(auction.endTimestamp, block.timestamp);

        uint256 numberOfBids = _auctionBids[_auctionId].count;
        if (numberOfBids == 0) revert AuctionHasNoBids();

        Bid memory winningBid = _auctionBids[_auctionId].bids[numberOfBids - 1];
        if (auction.reserveBidAmount <= winningBid.bidAmount)
            revert AuctionReserveWasMet(winningBid.bidAmount, auction.reserveBidAmount);

        //reentrancy protection
        auction.status = AuctionStatus.COMPLETED;

        (bool success, ) = winningBid.bidder.call{value: winningBid.bidAmount}("");
        if (!success) revert AuctionPreviousBidRefundRejected();

        emit AuctionClosed(
            _auctionId,
            winningBid.bidder,
            winningBid.bidAmount,
            auction.productId,
            false,
            true
        );
    }

    /// @dev Sets `_recipient` as `_approved` for withdrawing income.
    function approveWithdrawalRecipient(
        address _recipient,
        bool _approved
    ) external onlyFinance {
        _withdrawalApproved[_recipient] = _approved;

        emit WithdrawalRecipientApproved(_recipient, _approved);
    }

    /// @dev Transfers `_amount` of native token to `_account`. `_account` must be approved.
    function withdrawIncome(
        address payable _recipient,
        uint256 _amount
    ) external onlyFinance {
        if (!_withdrawalApproved[_recipient]) revert WithdrawalRecipientNotApproved();
        _processWithdrawal(_recipient, _amount);
    }

    function _processWithdrawal(address payable _recipient, uint256 _amount) internal {
        if (address(this).balance < _amount)
            revert WithdrawalInsufficientBalance(_amount, address(this).balance);

        (bool success, ) = _recipient.call{value: _amount}("");
        if (!success) revert WithdrawalRecipientRejectedTransfer();

        emit IncomeWithdrawal(msg.sender, _recipient, _amount);
    }

    function _processPayment(uint256 _index, uint256 _id, uint256 _value) internal {
        // Distribute the funds of the sale - 0: listing / 1: auction
        PayoutReceiver[] memory receivers = _payouts[_index][_id];
        for (uint256 i = 0; i < receivers.length; i++) {
            uint256 amount = (_value * receivers[i].percentage) / MAX_BPS;
            _processWithdrawal(receivers[i].receiver, amount);
        }
    }

    /*//////////////////////////////////////////////////////////////
                        Public functions
    //////////////////////////////////////////////////////////////*/

    function buyFromListing(
        uint256 _listingId,
        address _buyFor,
        uint256 _quantity,
        uint256 _expectedTotalPrice
    ) external payable {
        Listing storage listing = _listings[_listingId];

        if (listing.productId == 0) revert ListingDoesNotExist();
        if (listing.enabled == 0) revert ListingNotEnabled();
        if (_expectedTotalPrice != msg.value)
            revert ListingExpectedPriceNotEqualValue(_expectedTotalPrice, msg.value);
        if (listing.endTimestamp < block.timestamp) revert ListingHasEnded();
        unchecked {
            uint256 availableQuantity = listing.quantity -
                (listing.vipPurchased + listing.pubPurchased);

            if (availableQuantity < _quantity)
                revert ListingInsufficientQuantity(_quantity, availableQuantity);
        }

        uint256 listingPurchases = _listingPurchases[_listingId][msg.sender] + _quantity;

        //usually prefer non-duplicate code but this allows for custom messages and less memory use
        if (_checkListingVip(_listingId, msg.sender)) {
            if (block.timestamp < listing.vipStartTimestamp)
                revert ListingVipSaleNotStarted(
                    listing.vipStartTimestamp,
                    block.timestamp
                );
            if (listing.vipLimit < listingPurchases)
                revert ListingVipLimitExceeded(listingPurchases, listing.vipLimit);

            uint256 actualTotalPrice = listing.vipPrice * _quantity;
            if (actualTotalPrice != _expectedTotalPrice)
                revert ListingVipExpectedPriceInvalid(
                    _expectedTotalPrice,
                    actualTotalPrice
                );

            unchecked {
                //if VIP purchases are occuring BEFORE the external window, use the reserve
                if (block.timestamp < listing.pubStartTimestamp) {
                    //not allowed in creation or update to exceed
                    uint256 reserveAvailable = listing.reserved - listing.resPurchased;
                    if (reserveAvailable < _quantity)
                        revert ListingVipReserveExceeded(_quantity, reserveAvailable);
                    listing.resPurchased += uint16(_quantity);
                }

                listing.vipPurchased += uint16(_quantity);
            }
        } else {
            if (block.timestamp < listing.pubStartTimestamp)
                revert ListingPublicSaleNotStarted(
                    listing.pubStartTimestamp,
                    block.timestamp
                );
            if (listing.pubLimit < listingPurchases)
                revert ListingPublicLimitExceeded(listingPurchases, listing.pubLimit);

            uint256 actualTotalPrice = listing.pubPrice * _quantity;
            if (actualTotalPrice != _expectedTotalPrice)
                revert ListingPublicExpectedPriceInvalid(
                    _expectedTotalPrice,
                    actualTotalPrice
                );

            unchecked {
                listing.pubPurchased += uint16(_quantity);
            }
        }

        //perform updates before transfer to avoid re-entrancy
        _listingPurchases[_listingId][msg.sender] = listingPurchases;

        IProductToken(getListingContract(_listingId)).batchMintTo(
            _buyFor,
            listing.productId,
            _quantity
        );

        _processPayment(0, _listingId, _expectedTotalPrice);

        emit NewSale(
            _listingId,
            msg.sender,
            _buyFor,
            listing.productId,
            _quantity,
            _expectedTotalPrice
        );
    }

    function bidInAuction(uint256 _auctionId, uint256 _bidAmount) external payable {
        Auction memory auction = _auctions[_auctionId];

        if (auction.status == AuctionStatus.UNSET) revert AuctionDoesNotExist();
        if (auction.status != AuctionStatus.CREATED) revert AuctionNotActive();
        if (block.timestamp < auction.startTimestamp)
            revert AuctionNotStarted(auction.startTimestamp, block.timestamp);
        if (auction.endTimestamp < block.timestamp) revert AuctionHasEnded();
        if (_bidAmount == 0) revert AuctionBidAmountZero();
        if (_bidAmount < auction.minimumBidAmount)
            revert AuctionBidBelowMinimum(_bidAmount, auction.minimumBidAmount);
        if (_bidAmount != msg.value)
            revert AuctionBidAmountNotEqualValue(_bidAmount, msg.value);

        uint256 numberOfBids = _auctionBids[_auctionId].count;
        if (numberOfBids > 0) {
            Bid memory previousBid = _auctionBids[_auctionId].bids[numberOfBids - 1];

            unchecked {
                uint256 requiredBidAmount = previousBid.bidAmount +
                    ((previousBid.bidAmount * auction.bidBufferBps) / MAX_BPS);

                if (_bidAmount < requiredBidAmount)
                    revert AuctionBidAmountInsufficient(_bidAmount, requiredBidAmount);
            }
            (bool success, ) = previousBid.bidder.call{value: previousBid.bidAmount}("");
            if (!success) revert AuctionPreviousBidRefundRejected();

            // Reentrancy protection
            if (_auctionBids[_auctionId].count != numberOfBids) revert();
        }

        unchecked {
            ++_auctionBids[_auctionId].count;
        }

        Bid memory newBid = Bid({
            auctionId: uint64(_auctionId),
            bidTimestamp: uint32(block.timestamp),
            bidder: msg.sender,
            bidAmount: _bidAmount
        });

        _auctionBids[_auctionId].bids[numberOfBids] = newBid;

        unchecked {
            uint256 addressBids = _addressBids[msg.sender].count++;

            _addressBids[msg.sender].bids[addressBids] = newBid;
        }

        if (auction.endTimestamp - block.timestamp <= auction.timeBufferSeconds) {
            unchecked {
                auction.endTimestamp += uint32(auction.timeBufferSeconds);
            }
            _auctions[_auctionId] = auction;
        }

        emit NewBid(_auctionId, msg.sender, _bidAmount, auction);
    }

    /// @notice Sets the max payout distribution percentage for artists.
    /// @param _newMaxPayoutsDistribution The new max payout distribution value.
    /// @dev Only callable by users with the manager role.
    function setPayoutsMaxDistribution(
        uint256 _newMaxPayoutsDistribution
    ) external onlyManager {
        if (_newMaxPayoutsDistribution > MAX_BPS) {
            revert PayoutPercentageExceedsMax(MAX_BPS, _newMaxPayoutsDistribution);
        }
        _maxPayoutsDistribution = _newMaxPayoutsDistribution;
    }

    /*//////////////////////////////////////////////////////////////
                        Public getters
    //////////////////////////////////////////////////////////////*/

    function getVipContract() external view returns (address) {
        return _vipContract;
    }

    function totalListings() external view returns (uint256) {
        return _totalListings;
    }

    function getListingContract(uint256 _listingId) public view returns (address) {
        if (_listingContracts[_listingId] == address(0)) {
            return _listingContracts[0];
        } else {
            return _listingContracts[_listingId];
        }
    }

    function getListing(
        uint256 _listingId
    ) external view returns (ListingQuery memory _listing) {
        return ListingQuery({listingId: _listingId, listingData: _listings[_listingId]});
    }

    function getListingPayouts(
        uint256 _listingId
    ) external view returns (PayoutReceiver[] memory) {
        return _payouts[0][_listingId];
    }

    function getAuctionPayouts(
        uint256 _auctionId
    ) external view returns (PayoutReceiver[] memory) {
        return _payouts[1][_auctionId];
    }

    function getAllProductListings(
        uint256 _productId
    ) external view returns (ListingQuery[] memory _allListings) {
        ProductLookup storage productLookup = _productListings[_productId];
        uint256 count = productLookup.count;
        _allListings = new ListingQuery[](count);
        for (uint256 i; i < count; ) {
            uint256 listingId = productLookup.id[i];
            _allListings[i] = ListingQuery({
                listingId: listingId,
                listingData: _listings[listingId]
            });
            unchecked {
                ++i;
            }
        }
    }

    function getAllListings(
        uint256 _startId,
        uint256 _endId
    ) external view returns (ListingQuery[] memory _allListings) {
        if (_startId == 0 || _endId < _startId) revert ListingQueryInvalidRange();
        unchecked {
            uint256 maxId = (_endId > _totalListings ? _totalListings : _endId) + 1;

            _allListings = new ListingQuery[](maxId - _startId);

            for (uint256 i = _startId; i < maxId; ) {
                _allListings[i - _startId] = ListingQuery({
                    listingId: i,
                    listingData: _listings[i]
                });
                ++i;
            }
        }
    }

    function getWithdrawalApproved(address _recipient) external view returns (bool) {
        return _withdrawalApproved[_recipient];
    }

    function totalAuctions() external view returns (uint256) {
        return _totalAuctions;
    }

    function getAuctionContract(uint256 _auctionId) public view returns (address) {
        if (_auctionContracts[_auctionId] == address(0)) {
            return _auctionContracts[0];
        } else {
            return _auctionContracts[_auctionId];
        }
    }

    function getAuction(
        uint256 _auctionId
    ) external view returns (AuctionQuery memory _auction) {
        unchecked {
            uint256 numberOfBids = _auctionBids[_auctionId].count;

            return
                AuctionQuery({
                    auctionId: _auctionId,
                    auctionData: _auctions[_auctionId],
                    winningBid: _auctionBids[_auctionId].bids[
                        numberOfBids > 0 ? numberOfBids - 1 : 0
                    ]
                });
        }
    }

    function getAllProductAuctions(
        uint256 _productId
    ) external view returns (AuctionQuery[] memory _allAuctions) {
        ProductLookup storage productLookup = _productAuctions[_productId];
        uint256 count = productLookup.count;
        _allAuctions = new AuctionQuery[](count);
        for (uint256 i; i < count; ) {
            uint256 auctionId = productLookup.id[i];
            uint256 numberOfBids = _auctionBids[i].count;
            unchecked {
                _allAuctions[i] = AuctionQuery({
                    auctionId: auctionId,
                    auctionData: _auctions[auctionId],
                    winningBid: _auctionBids[auctionId].bids[
                        numberOfBids > 0 ? numberOfBids - 1 : 0
                    ]
                });
                ++i;
            }
        }
    }

    function getAllAuctions(
        uint256 _startId,
        uint256 _endId
    ) external view returns (AuctionQuery[] memory _allAuctions) {
        if (_startId == 0 || _endId < _startId) revert AuctionQueryInvalidRange();
        unchecked {
            uint256 maxId = (_endId > _totalAuctions ? _totalAuctions : _endId) + 1;

            _allAuctions = new AuctionQuery[](maxId - _startId);

            for (uint256 i = _startId; i < maxId; ) {
                uint256 numberOfBids = _auctionBids[i].count;

                _allAuctions[i - _startId] = AuctionQuery({
                    auctionId: i,
                    auctionData: _auctions[i],
                    winningBid: _auctionBids[i].bids[
                        numberOfBids > 0 ? numberOfBids - 1 : 0
                    ]
                });

                ++i;
            }
        }
    }

    function getAuctionBids(
        uint256 _auctionId
    ) external view returns (Bid[] memory _bids) {
        uint256 numberOfBids = _auctionBids[_auctionId].count;
        _bids = new Bid[](numberOfBids);
        for (uint256 i; i < numberOfBids; ) {
            _bids[i] = _auctionBids[_auctionId].bids[i];
            unchecked {
                ++i;
            }
        }
    }

    function getAddressBids(address _bidder) external view returns (Bid[] memory _bids) {
        uint256 numberOfBids = _addressBids[_bidder].count;
        _bids = new Bid[](numberOfBids);
        for (uint256 i; i < numberOfBids; ) {
            _bids[i] = _addressBids[_bidder].bids[i];
            unchecked {
                ++i;
            }
        }
    }

    function getWinningBidAmount(uint256 _auctionId) external view returns (uint256) {
        Auction memory auction = _auctions[_auctionId];
        if (auction.status == AuctionStatus.UNSET) revert AuctionDoesNotExist();

        uint256 numberOfBids = _auctionBids[_auctionId].count;
        if (numberOfBids > 0) {
            unchecked {
                Bid memory previousBid = _auctionBids[_auctionId].bids[numberOfBids - 1];
                return
                    previousBid.bidAmount +
                    ((previousBid.bidAmount * auction.bidBufferBps) / MAX_BPS);
            }
        }

        return auction.minimumBidAmount;
    }

    /// @notice Returns the current max payout distribution percentage for artists.
    function getPayoutsMaxDistribution() external view returns (uint256) {
        return _maxPayoutsDistribution;
    }

    /// @notice Returns the address that should receive SYKY fees.
    function getFeeReceiver() external view returns (address) {
        return _feeReceiver;
    }

    /*//////////////////////////////////////////////////////////////
                            Modifiers
    //////////////////////////////////////////////////////////////*/

    /// @dev Modifier that checks if an account has admin or finance role; reverts otherwise.
    modifier onlyFinance() {
        _checkFinanceAdmin();
        _;
    }

    /// @dev Modifier that checks if an account has admin or manager role; reverts otherwise.
    modifier onlyManager() {
        _checkManagerAdmin();
        _;
    }

    /*//////////////////////////////////////////////////////////////
                        Internal functions
    //////////////////////////////////////////////////////////////*/

    /// @dev Function that checks if an account has admin or finance role; reverts otherwise.
    function _checkFinanceAdmin() internal view {
        if (
            !hasRole(DEFAULT_ADMIN_ROLE, msg.sender) && !hasRole(FINANCE_ROLE, msg.sender)
        ) {
            revert FinanceRoleRequired();
        }
    }

    /// @dev Function that checks if an account has admin or manager role; reverts otherwise.
    function _checkManagerAdmin() internal view {
        if (
            !hasRole(DEFAULT_ADMIN_ROLE, msg.sender) && !hasRole(MANAGER_ROLE, msg.sender)
        ) {
            revert ManagerRoleRequired();
        }
    }

    function _checkListingVip(
        uint256 _listingId,
        address _member
    ) internal view returns (bool) {
        if (_vipContract != address(0))
            return IVipManager(_vipContract).isVip(_listingId, _member);
        return false;
    }
}
SykyMarket.sol 42 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @author Syky - Nathan Rempel

/*
           @@@   @@@@@   @@@@@@@         @@@@    @@@@@@@      @@@@@     @@@@@@@        @@@@
         @@@@      @@@     @@@@@@        @@       @@@@@@       @@        @@@@@@        @@
         @@@@@      @@      @@@@@       @@        @@@@@       @            @@@@@      @@
         @@@@@@      @@      @@@@@     @@         @@@@@     @               @@@@     @@
          @@@@@@      @       @@@@@   @@          @@@@@    @                @@@@@    @
           @@@@@@@             @@@@@ @@           @@@@@  @@@                 @@@@@  @
             @@@@@@             @@@@@@            @@@@@@@@@@@                 @@@@@@
               @@@@@@           @@@@@             @@@@@  @@@@@                 @@@@@
                @@@@@@@         @@@@@             @@@@@   @@@@@                @@@@@
         @        @@@@@@        @@@@@             @@@@@    @@@@@               @@@@@
         @@        @@@@@        @@@@@             @@@@@     @@@@@              @@@@@
         @@@@      @@@@@        @@@@@             @@@@@@     @@@@@@           @@@@@@
         @@@@@@   @@@@         @@@@@@@           @@@@@@@     @@@@@@@          @@@@@@@
*/

import "../base/ProductMarket.sol";

contract SykyMarket is ProductMarket {
    /*//////////////////////////////////////////////////////////////
                            Version Info
    //////////////////////////////////////////////////////////////*/

    string public constant ENV = "MAINNET";
    string public constant VER = "1.0.2";

    /*//////////////////////////////////////////////////////////////
                            Constructor
    //////////////////////////////////////////////////////////////*/

    constructor(
        address defaultAdmin_,
        address defaultToken_,
        address vipContract_,
        address feeReceiver_
    ) ProductMarket(defaultAdmin_, defaultToken_, vipContract_, feeReceiver_) {}
}

Read Contract

DEFAULT_ADMIN_ROLE 0xa217fddf → bytes32
ENV 0x7c6285a1 → string
FINANCE_ROLE 0x349b5954 → bytes32
MANAGER_ROLE 0xec87621c → bytes32
VER 0x3c4de40f → string
getAddressBids 0xd9ea9549 → tuple[]
getAllAuctions 0xc291537c → tuple[]
getAllListings 0xc5275fb0 → tuple[]
getAllProductAuctions 0x9258d45d → tuple[]
getAllProductListings 0x567441f8 → tuple[]
getAuction 0x78bd7935 → tuple
getAuctionBids 0x70b4768e → tuple[]
getAuctionContract 0xe506dfbe → address
getAuctionPayouts 0x05013cdc → tuple[]
getFeeReceiver 0xe8a35392 → address
getListing 0x107a274a → tuple
getListingContract 0x802c3a3a → address
getListingPayouts 0x8635d59b → tuple[]
getPayoutsMaxDistribution 0xa2d40dd5 → uint256
getRoleAdmin 0x248a9ca3 → bytes32
getVipContract 0x9c520471 → address
getWinningBidAmount 0xa3c8fdd8 → uint256
getWithdrawalApproved 0xe403a251 → bool
hasRole 0x91d14854 → bool
hasRoleWithSwitch 0xa32fa5b3 → bool
totalAuctions 0x16002f4a → uint256
totalListings 0xc78b616c → uint256

Write Contract 22 functions

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

approveWithdrawalRecipient 0x216fd1f5
address _recipient
bool _approved
bidInAuction 0x0858e5ad
uint256 _auctionId
uint256 _bidAmount
buyFromListing 0xf10f537a
uint256 _listingId
address _buyFor
uint256 _quantity
uint256 _expectedTotalPrice
cancelAuction 0x96b5a755
uint256 _auctionId
closeAuction 0x236ed8f3
uint256 _auctionId
createAuction 0xa24eceff
tuple _params
tuple[] receivers
returns: uint256
createListing 0xd8ed8020
tuple _params
tuple[] receivers
returns: uint256
enableListing 0x218ddb57
uint256 _listingId
bool _enabled
grantRole 0x2f2ff15d
bytes32 role
address account
renounceRole 0x36568abe
bytes32 role
address account
revokeRole 0xd547741f
bytes32 role
address account
rewardAuction 0x61b7aafe
uint256 _auctionId
setAuctionContract 0xfc9b5fc8
uint256 _auctionId
address _tokenContract
setAuctionPayouts 0x0f37cece
uint256 _auctionId
tuple[] receivers
setFeeReceiver 0xefdcd974
address _feeReceiver_
setListingContract 0x31964156
uint256 _listingId
address _tokenContract
setListingPayouts 0xa1e2fc1e
uint256 _listingId
tuple[] receivers
setPayoutsMaxDistribution 0x0c0803c8
uint256 _newMaxPayoutsDistribution
setVipContract 0x1fcc5b8a
address _vipContract_
updateAuction 0xe29fc9bb
uint256 _auctionId
tuple _params
updateListing 0xdd7ad64e
uint256 _listingId
tuple _params
withdrawIncome 0xb7792b26
address _recipient
uint256 _amount

Recent Transactions

No transactions found for this address