Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x2cBe14b7F60Fbe6A323cBA7Db56f2D916C137F3C
Balance 13.0769 ETH
Nonce 2
Code Size 17686 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

17686 bytes
0x6080604052600436106102fd5760003560e01c8063715018a61161018f578063ba7df63d116100e1578063e5a70ef71161008a578063f2fde38b11610064578063f2fde38b146109fc578063f340fa0114610a1c578063fbde051114610a2f57600080fd5b8063e5a70ef7146109a6578063e67726f8146109bc578063ef706adf146109dc57600080fd5b8063cc8887f1116100bb578063cc8887f114610925578063d67c074e14610952578063e52044721461097257600080fd5b8063ba7df63d146108a7578063c02909ce146108c7578063c16973c61461090557600080fd5b806395d89b4111610143578063a03fd9c21161011d578063a03fd9c214610852578063b14f2a3914610867578063b85613e21461088757600080fd5b806395d89b41146107c95780639e80c074146108125780639fef66221461083257600080fd5b8063798289b011610174578063798289b01461075e5780637f86a2771461077e5780638da5cb5b1461079e57600080fd5b8063715018a6146107335780637522a6891461074857600080fd5b8063313ce567116102535780635c1bba38116101fc5780636a1db1bf116101d65780636a1db1bf146106b057806370431d29146106d057806370a08231146106f057600080fd5b80635c1bba381461061957806361d027b31461064657806364750c001461067357600080fd5b80633c5e3f461161022d5780633c5e3f46146105575780634b3c8643146105775780634c94c90c146105f157600080fd5b8063313ce567146104ee5780633644e5151461051557806337a61d721461052a57600080fd5b806325d86456116102b55780632d4353221161028f5780632d4353221461048e5780632e8f3cb8146104ae5780632ff4e454146104ce57600080fd5b806325d864561461042c57806327a9b06d1461044e5780632b8a1c5a1461046e57600080fd5b80630be650b2116102e65780630be650b21461039d57806318017d17146103ef57806318160ddd1461040f57600080fd5b80630633b14a1461030257806306fdde0314610347575b600080fd5b34801561030e57600080fd5b5061033261031d366004613bac565b600a6020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b34801561035357600080fd5b506103906040518060400160405280600a81526020017f5365616c6564204554480000000000000000000000000000000000000000000081525081565b60405161033e9190613bc9565b3480156103a957600080fd5b506003546103ca9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161033e565b3480156103fb57600080fd5b5061033261040a366004613c35565b610a4f565b34801561041b57600080fd5b50475b60405190815260200161033e565b34801561043857600080fd5b5061044c610447366004613c4e565b610a74565b005b34801561045a57600080fd5b5061044c610469366004613caa565b610b14565b34801561047a57600080fd5b5061044c610489366004613d1a565b610b38565b34801561049a57600080fd5b5061044c6104a9366004613d58565b610bcf565b3480156104ba57600080fd5b5061044c6104c9366004613d7a565b610c2e565b3480156104da57600080fd5b5061044c6104e9366004613d58565b610ee9565b3480156104fa57600080fd5b50610503601281565b60405160ff909116815260200161033e565b34801561052157600080fd5b5061041e610fd5565b34801561053657600080fd5b5061041e610545366004613c35565b60096020526000908152604090205481565b34801561056357600080fd5b5061044c610572366004613dc8565b6110d0565b34801561058357600080fd5b5061041e610592366004613e6d565b6040805173ffffffffffffffffffffffffffffffffffffffff96871660208083019190915295909616868201526060860193909352608085019190915260a0808501919091528151808503909101815260c09093019052815191012090565b3480156105fd57600080fd5b506103ca73bc40d21999b4bf120d330ee3a2de415287f626c981565b34801561062557600080fd5b506002546103ca9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561065257600080fd5b506004546103ca9073ffffffffffffffffffffffffffffffffffffffff1681565b34801561067f57600080fd5b506106a361068e366004613c35565b60076020526000908152604090205460ff1681565b60405161033e9190613eed565b3480156106bc57600080fd5b5061044c6106cb366004613c35565b6119a4565b3480156106dc57600080fd5b5061044c6106eb366004613d58565b611a5a565b3480156106fc57600080fd5b5061041e61070b366004613bac565b73ffffffffffffffffffffffffffffffffffffffff1660009081526001602052604090205490565b34801561073f57600080fd5b5061044c611ac4565b34801561075457600080fd5b5061041e60065481565b34801561076a57600080fd5b5061044c610779366004613f8c565b611ad8565b34801561078a57600080fd5b5061044c610799366004613ff4565b611af1565b3480156107aa57600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166103ca565b3480156107d557600080fd5b506103906040518060400160405280600481526020017f534554480000000000000000000000000000000000000000000000000000000081525081565b34801561081e57600080fd5b5061044c61082d366004613c35565b611b25565b34801561083e57600080fd5b5061044c61084d366004613d7a565b611be5565b34801561085e57600080fd5b5061044c611d19565b34801561087357600080fd5b5061044c610882366004613bac565b611e08565b34801561089357600080fd5b5061044c6108a236600461409d565b611e83565b3480156108b357600080fd5b506103326108c23660046140f1565b611f18565b3480156108d357600080fd5b5061041e6108e236600461411d565b600860209081526000938452604080852082529284528284209052825290205481565b34801561091157600080fd5b5061044c610920366004613c35565b611f5f565b34801561093157600080fd5b5061041e610940366004613bac565b600d6020526000908152604090205481565b34801561095e57600080fd5b5061044c61096d366004614152565b612008565b34801561097e57600080fd5b506103ca7f0000000000000000000000001400eca2a4f703b5cf17c793ceb0181dc40e127a81565b3480156109b257600080fd5b5061041e60055481565b3480156109c857600080fd5b5061044c6109d736600461416e565b612214565b3480156109e857600080fd5b5061044c6109f7366004613c35565b61273e565b348015610a0857600080fd5b5061044c610a17366004613bac565b61279b565b61044c610a2a366004613bac565b612852565b348015610a3b57600080fd5b5061044c610a4a3660046141e7565b6128dd565b600881901c6000908152600b6020526040812054600160ff84161b1615155b92915050565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523360048201523060248201526044810183905273ffffffffffffffffffffffffffffffffffffffff8616906323b872dd90606401600060405180830381600087803b158015610ae857600080fd5b505af1158015610afc573d6000803e3d6000fd5b50505050610b0d8585858585612a11565b5050505050565b610b218888888885612b97565b610b2e8885858986612a11565b5050505050505050565b610b40612ded565b73ffffffffffffffffffffffffffffffffffffffff82166000818152600a602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558251938452908301527fc4459d655e1efe034a75446f1f4db087f75d23bdfabccffafa6384cdb23a154491015b60405180910390a15050565b33600081815260086020908152604080832086845282528083208584528252808320929092558151928352820184905281018290527f0138d13a0a4f5066bd396cae651149d3b59142c266597178347e56af428f1bcf90606001610bc3565b604080513360208083019190915273ffffffffffffffffffffffffffffffffffffffff871682840152606082018690526080820185905260a08083018590528351808403909101815260c0909201835281519181019190912060008181526009909252919020548015801590610cb057504260065482610cae9190614244565b105b610d1b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f746f6f20736f6f6e00000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600160008381526007602052604090205460ff166002811115610d4057610d40613ebe565b14610da7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f6e6f74206f70656e0000000000000000000000000000000000000000000000006044820152606401610d12565b600082815260076020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166002179055600982528083209290925590518381527fd88fab4b08bf76f15cf2d6e03e382acf1edd6790ab82967e406abac37db20288910160405180910390a16040517f23b872dd0000000000000000000000000000000000000000000000000000000081523060048201523360248201526044810185905273ffffffffffffffffffffffffffffffffffffffff8716906323b872dd90606401600060405180830381600087803b158015610e9057600080fd5b505af1158015610ea4573d6000803e3d6000fd5b505050507f0dec9dd9ec09eb629e1af32788486e1b19b9b997c96f44ebf030db5e473fb45482604051610ed991815260200190565b60405180910390a1505050505050565b4260065483610ef89190614244565b10610f5f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f746f6f20736f6f6e0000000000000000000000000000000000000000000000006044820152606401610d12565b336000908152600860209081526040808320858452825280832084845290915281208054919055610f8f81612e6e565b60408051338152602081018590529081018390527f6f503e8ca28bec5be28d038c8f1a6af9de4a4a7ee5bdbaa6f8702e169ae663219060600160405180910390a1505050565b60007f000000000000000000000000000000000000000000000000000000000000000146146110ab576110a6604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f2aea0414eb9e6a9b2592f3f7f217c1e8a7593abc3be962bf885d94bc05072dd5918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b905090565b507f2d1a05fa4e0eb5a0d503687c19f364dc1f82e2e6b62a70010bb08e42139e543690565b428560e001351161113d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f21646561646c696e6500000000000000000000000000000000000000000000006044820152606401610d12565b61114d60c0860160a08701613bac565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611276576111988661119360c0880160a08901613bac565b612f23565b6111a860c0860160a08701613bac565b73ffffffffffffffffffffffffffffffffffffffff166111c7876130ce565b73ffffffffffffffffffffffffffffffffffffffff16148015611210575060006111f760c0870160a08801613bac565b73ffffffffffffffffffffffffffffffffffffffff1614155b611276576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f21627579657200000000000000000000000000000000000000000000000000006044820152606401610d12565b61128660e0860160c08701613bac565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113aa576112cc8761119360e0880160c08901613bac565b6112dc60e0860160c08701613bac565b73ffffffffffffffffffffffffffffffffffffffff166112fb886131bf565b73ffffffffffffffffffffffffffffffffffffffff161480156113445750600061132b60e0870160c08801613bac565b73ffffffffffffffffffffffffffffffffffffffff1614155b6113aa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600760248201527f2173656c6c6572000000000000000000000000000000000000000000000000006044820152606401610d12565b600061141b6113bf60e0880160c08901613bac565b6040805173ffffffffffffffffffffffffffffffffffffffff92831660208083019190915292891681830152606081018890526080810187905260a08082018790528251808303909101815260c0909101909152805191012090565b9050600160008281526007602052604090205460ff16600281111561144257611442613ebe565b1480156114525750808660600135145b6114b8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f6261642061756374696f6e2073746174650000000000000000000000000000006044820152606401610d12565b6080860135600160006114d160c08a0160a08b01613bac565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461151a9190614257565b909155506000905061153260c0880160a08901613bac565b73ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef886080013560405161157d91815260200190565b60405180910390a3600081815260076020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166002179055608086013560a08901351480156115da57508660a001358660800135145b611640576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600760248201527f21616d6f756e74000000000000000000000000000000000000000000000000006044820152606401610d12565b6116506080890160608a01613bac565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161480156116c457506116956080880160608901613bac565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16145b61172a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f216e6674436f6e747261637400000000000000000000000000000000000000006044820152606401610d12565b8760800135831480156117405750866080013583145b6117a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f216e6674496400000000000000000000000000000000000000000000000000006044820152606401610d12565b60025473ffffffffffffffffffffffffffffffffffffffff166117c8876131f5565b73ffffffffffffffffffffffffffffffffffffffff1614611845576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f2173657175656e636572000000000000000000000000000000000000000000006044820152606401610d12565b73ffffffffffffffffffffffffffffffffffffffff85166323b872dd3061187260c08a0160a08b01613bac565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff92831660048201529116602482015260448101869052606401600060405180830381600087803b1580156118e657600080fd5b505af11580156118fa573d6000803e3d6000fd5b50505050611920858488608001358960c001602081019061191b9190613bac565b613293565b7fd39db9ec1f20b9c7d4edf4d5702674c36fac678aee167775904bd363e5246b188161195260c0890160a08a01613bac565b6040805192835273ffffffffffffffffffffffffffffffffffffffff90911660208301523390820152610100808a013560608301528a0135608082015260a00160405180910390a15050505050505050565b6119ac612ded565b67016345785d8a0000811115611a1e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f66656520746f6f206869676800000000000000000000000000000000000000006044820152606401610d12565b60058190556040518181527f6bbc57480a46553fa4d156ce702beef5f3ad66303b0ed1a5d4cb44966c6584c3906020015b60405180910390a150565b336000818152600860209081526040808320428085529083528184208685528352928190208690558051938452908301919091528101829052606081018390527f4b329ddc345d75efb083183f247a0b1e09018a8d4572c62794a1da50d353330590608001610bc3565b611acc612ded565b611ad660006134e8565b565b611ae383833361355d565b611aec81612008565b505050565b611b0b8989611b0660c0850160a08601613bac565b61355d565b611b1a87878787878787612214565b505050505050505050565b336000908152600d60205260409020548111611b9d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600760248201527f746f6f206c6f77000000000000000000000000000000000000000000000000006044820152606401610d12565b336000818152600d6020908152604091829020849055815192835282018390527f3a5e248acc8913ccb0680211c01d6d63a570cf10cc8e5b88555b96c292b1a6cd9101611a4f565b604080513360208083019190915273ffffffffffffffffffffffffffffffffffffffff871682840152606082018690526080820185905260a08083018590528351808403909101815260c09092019092528051910120600160008281526007602052604090205460ff166002811115611c6057611c60613ebe565b14611cc7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f6261642061756374696f6e2073746174650000000000000000000000000000006044820152606401610d12565b60008181526009602052604090819020429055517f8f11c790b515664c3e09e327db270b39ee57438bbdd0cb9f57d608fb3848d4a590611d0a9083815260200190565b60405180910390a15050505050565b336000908152600a602052604090205460ff161515600114611d97576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f6e6f7420677561726469616e00000000000000000000000000000000000000006044820152606401610d12565b6002805461dead7fffffffffffffffffffffffff000000000000000000000000000000000000000091821681179092556003805490911690911790556040513381527f4848f4074ec7d0f96ffbb9c01b6907e9feafb4c0efaf3ba96a4cd5ed75ea09359060200160405180910390a1565b611e10612ded565b600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527fc714d22a2f08b695f81e7c707058db484aa5b4d6b4c9fd64beb10fe85832f60890602001611a4f565b611e908585858585612b97565b6040517f23b872dd0000000000000000000000000000000000000000000000000000000081523060048201523360248201526044810184905273ffffffffffffffffffffffffffffffffffffffff8616906323b872dd90606401600060405180830381600087803b158015611f0457600080fd5b505af1158015611b1a573d6000803e3d6000fd5b73ffffffffffffffffffffffffffffffffffffffff82166000908152600c60209081526040808320600885901c8452909152812054600160ff84161b1615155b9392505050565b611f67612ded565b620d2f008110611fd3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f3c313020646179730000000000000000000000000000000000000000000000006044820152606401610d12565b60068190556040518181527f3e0bee5a358b79753e71a357821c8a897cff484fccfa2509d98c25942652cd8e90602001611a4f565b60025473ffffffffffffffffffffffffffffffffffffffff1661202a82613639565b73ffffffffffffffffffffffffffffffffffffffff16146120a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f2173657175656e636572000000000000000000000000000000000000000000006044820152606401610d12565b6120b48160a00135610a4f565b1561211b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f7265706c617965640000000000000000000000000000000000000000000000006044820152606401610d12565b60a0810135600881901c6000908152600b602052604090208054600160ff9093169290921b90911790553361215660e0830160c08401613bac565b73ffffffffffffffffffffffffffffffffffffffff16146121d3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f6e6f742073656e646572000000000000000000000000000000000000000000006044820152606401610d12565b6121e08160800135612e6e565b60405160a082013581527fc80927880e06c47f66a068a03d995487a7cb023cec681c517cd59c3f7ff1530290602001611a4f565b6040805173ffffffffffffffffffffffffffffffffffffffff808a1660208084019190915290891682840152606082018890526080820187905260a08083018790528351808403909101815260c09092019092528051910120600160008281526007602052604090205460ff16600281111561229257612292613ebe565b146122f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f6261642061756374696f6e2073746174650000000000000000000000000000006044820152606401610d12565b600081815260076020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660021790556060820135811480156123465750808360600135145b6123ac576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f2161756374696f6e4964000000000000000000000000000000000000000000006044820152606401610d12565b6080808301359084013581118015906123c55750848110155b61242b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600760248201527f21616d6f756e74000000000000000000000000000000000000000000000000006044820152606401610d12565b61243b60c0840160a08501613bac565b73ffffffffffffffffffffffffffffffffffffffff1661245a85613775565b73ffffffffffffffffffffffffffffffffffffffff16146124d7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600760248201527f2177696e6e6572000000000000000000000000000000000000000000000000006044820152606401610d12565b60035473ffffffffffffffffffffffffffffffffffffffff166124f984613848565b73ffffffffffffffffffffffffffffffffffffffff1614612576576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f21736574746c6553657175656e636572000000000000000000000000000000006044820152606401610d12565b806001600061258b60c0870160a08801613bac565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546125d49190614257565b90915550600090506125ec60c0850160a08601613bac565b73ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161263391815260200190565b60405180910390a373ffffffffffffffffffffffffffffffffffffffff88166323b872dd3061266860c0870160a08801613bac565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff92831660048201529116602482015260448101899052606401600060405180830381600087803b1580156126dc57600080fd5b505af11580156126f0573d6000803e3d6000fd5b505050506127008887838c613293565b6040518281527f9adaa52fec28b7d59b397f9b44bed38628a3d39e2c03278b14e9be3f14c799ba9060200160405180910390a1505050505050505050565b336000818152600c60209081526040808320600886901c84528252918290208054600160ff87161b179055815192835282018390527f899cab278284ae4a91172caa0943607a0bcb19766254c3ebe1139be00650b1029101611a4f565b6127a3612ded565b73ffffffffffffffffffffffffffffffffffffffff8116612846576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610d12565b61284f816134e8565b50565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001602052604081208054349290612887908490614244565b909155505060405134815273ffffffffffffffffffffffffffffffffffffffff8216906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a350565b6128e5612ded565b73ffffffffffffffffffffffffffffffffffffffff82161580159061291f575073ffffffffffffffffffffffffffffffffffffffff811615155b612985576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f3078302073657175656e636572206e6f7420616c6c6f776564000000000000006044820152606401610d12565b6002805473ffffffffffffffffffffffffffffffffffffffff8481167fffffffffffffffffffffffff00000000000000000000000000000000000000009283168117909355600380549185169190921681179091556040805192835260208301919091527f6ec88bae255aa7e73521c3beb17e9bc7940169e669440c5531733c0d2e91110d9101610bc3565b604080513360208083019190915273ffffffffffffffffffffffffffffffffffffffff881682840152606082018690526080820185905260a08083018590528351808403909101815260c090920190925280519101206000808281526007602052604090205460ff166002811115612a8b57612a8b613ebe565b14612af2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f72657065617465642061756374696f6e206964000000000000000000000000006044820152606401610d12565b60008181526007602052604090208054600191907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016828002179055506040805133815273ffffffffffffffffffffffffffffffffffffffff88166020820152908101869052606081018590526080810184905260a081018390527f8d23a5dfd11a91f2265cffdb358ef2106e197d9f5168852ec99ba53fb90526409060c001610ed9565b60025473ffffffffffffffffffffffffffffffffffffffff16612bb9826138c2565b73ffffffffffffffffffffffffffffffffffffffff1614612c36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f2173657175656e636572000000000000000000000000000000000000000000006044820152606401610d12565b604080513360208083019190915273ffffffffffffffffffffffffffffffffffffffff881682840152606082018790526080820186905260a08083018690528351808403909101815260c09092019092528051910120600160008281526007602052604090205460ff166002811115612cb157612cb1613ebe565b14612d18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f62616420737461746500000000000000000000000000000000000000000000006044820152606401610d12565b80826060013514612d85576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f2161756374696f6e4964000000000000000000000000000000000000000000006044820152606401610d12565b60008181526007602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600217905590518281527fd88fab4b08bf76f15cf2d6e03e382acf1edd6790ab82967e406abac37db202889101610ed9565b60005473ffffffffffffffffffffffffffffffffffffffff163314611ad6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610d12565b3360009081526001602052604081208054839290612e8d908490614257565b9091555050604051600090339083908381818185875af1925050503d8060008114612ed4576040519150601f19603f3d011682016040523d82523d6000602084013e612ed9565b606091505b5050905080612ee757600080fd5b60405182815260009033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b428260c0013511612f90576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f21646561646c696e6500000000000000000000000000000000000000000000006044820152606401610d12565b612f9f81836101000135611f18565b15613006576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f216f726465724e6f6e63650000000000000000000000000000000000000000006044820152606401610d12565b336000908152600c60209081526040808320610100860135600881901c85529083528184208054600160ff9093169290921b909117905573ffffffffffffffffffffffffffffffffffffffff84168352600d90915290205460e0830135116130ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f21636f756e7465720000000000000000000000000000000000000000000000006044820152606401610d12565b5050565b6000610a6e7fbd0fdaf172daee893a4c200efd8c7fbcb1bb560cb6526afa5075a17583427f7f6131046080850160608601613bac565b84608001358560a001358660c001358760e00135886101000135604051602001613175979695949392919096875273ffffffffffffffffffffffffffffffffffffffff95909516602087015260408601939093526060850191909152608084015260a083015260c082015260e00190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526131b0602085018561426a565b84602001358560400135613983565b6000610a6e7ff776e24d515f23bab237cdb9dd891d82e2492aea54fe72cdfcb80ee25826f06b6131046080850160608601613bac565b6000610a6e7f5b04f53d4a6314f3487293714df115ceea564ec981a66b83d0353f46b603a2476060840135608085013561323560c0870160a08801613bac565b61324560e0880160c08901613bac565b604080516020810196909652850193909352606084019190915273ffffffffffffffffffffffffffffffffffffffff90811660808401521660a082015260e08085013560c083015201613175565b6040517ff533b80200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602481018490526044810183905260009073bc40d21999b4bf120d330ee3a2de415287f626c99063f533b802906207a1209060640160006040518083038160008887f19350505050801561336957506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052613366919081019061439a565b60015b1561347a57815160059081111561337e575081515b808251101561338b575080515b60005b81811015613401576133d28482815181106133ab576133ab61445f565b60200260200101518483815181106133c5576133c561445f565b6020026020010151613a89565b8281815181106133e4576133e461445f565b6020026020010151856133f79190614244565b945060010161338e565b5061340d60038761448e565b841115613476576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f526f79616c747920746f6f2068696768000000000000000000000000000000006044820152606401610d12565b5050505b6000670de0b6b3a76400006005548561349391906144c9565b61349d919061448e565b6004549091506134c39073ffffffffffffffffffffffffffffffffffffffff1682613a89565b6134e0836134d18385614244565b6134db9087614257565b613a89565b505050505050565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60005b82811015613633577f0000000000000000000000001400eca2a4f703b5cf17c793ceb0181dc40e127a73ffffffffffffffffffffffffffffffffffffffff1663b32fe450620186a08686858181106135ba576135ba61445f565b90506020020135856040518463ffffffff1660e01b81526004016135fe92919091825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b600060405180830381600088803b15801561361857600080fd5b5087f19350505050801561362a575060015b50600101613560565b50505050565b6000816060013542106136a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f3e646561646c696e6500000000000000000000000000000000000000000000006044820152606401610d12565b6000611f587f8eb7a9de735afd3343c073938e4baa2ada22ae66f11efcae55e395d961cf33e16060850135608086013560a08701356136ed60e0890160c08a01613bac565b6040805160208101969096528501939093526060840191909152608083015273ffffffffffffffffffffffffffffffffffffffff1660a082015260c0015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052613766602086018661426a565b85602001358660400135613983565b6000806137c97fb1b810669639f245be94b8ea241be22267602ca9d3c88854583404de1bdb417f8460600135856080013560405160200161372b939291909283526020830191909152604082015260600190565b905073ffffffffffffffffffffffffffffffffffffffff8116610a6e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f73696700000000000000000000000000000000000000000000000000000000006044820152606401610d12565b6000610a6e7fdefc6aec63d5066eb628bf3346255cf84fd74fbb3fdef120d9094162fededf686060840135608085013561388860c0870160a08801613bac565b604080516020810195909552840192909252606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a001613175565b60008160800135421115613932576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f646561646c696e650000000000000000000000000000000000000000000000006044820152606401610d12565b610a6e7f2f569cb45ea15c1cad156c0991f7dfe0af8cb31fefdeb52b6d363915f6ed3a5683606001358460800135604051602001613175939291909283526020830191909152604082015260600190565b6000806001613990610fd5565b87805190602001206040516020016139da9291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff881690820152606081018690526080810185905260a0016020604051602081039080840390855afa158015613a56573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00151979650505050505050565b60008273ffffffffffffffffffffffffffffffffffffffff1682620493e090604051600060405180830381858888f193505050503d8060008114613ae9576040519150601f19603f3d011682016040523d82523d6000602084013e613aee565b606091505b5090915050801515600003611aec5773ffffffffffffffffffffffffffffffffffffffff831660009081526001602052604081208054849290613b32908490614244565b909155505060405182815273ffffffffffffffffffffffffffffffffffffffff8416906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461284f57600080fd5b600060208284031215613bbe57600080fd5b8135611f5881613b8a565b600060208083528351808285015260005b81811015613bf657858101830151858201604001528201613bda565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b600060208284031215613c4757600080fd5b5035919050565b600080600080600060a08688031215613c6657600080fd5b8535613c7181613b8a565b97602087013597506040870135966060810135965060800135945092505050565b600060a08284031215613ca457600080fd5b50919050565b600080600080600080600080610180898b031215613cc757600080fd5b8835613cd281613b8a565b97506020890135965060408901359550606089013594506080890135935060a0890135925060c08901359150613d0b8a60e08b01613c92565b90509295985092959890939650565b60008060408385031215613d2d57600080fd5b8235613d3881613b8a565b915060208301358015158114613d4d57600080fd5b809150509250929050565b60008060408385031215613d6b57600080fd5b50508035926020909101359150565b60008060008060808587031215613d9057600080fd5b8435613d9b81613b8a565b966020860135965060408601359560600135945092505050565b60006101208284031215613ca457600080fd5b60008060008060008060008789036103c0811215613de557600080fd5b613def8a8a613db5565b9750613dff8a6101208b01613db5565b96506101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc082011215613e3257600080fd5b5061024088019450610340880135613e4981613b8a565b969995985093966103608101359561038082013595506103a0909101359350915050565b600080600080600060a08688031215613e8557600080fd5b8535613e9081613b8a565b94506020860135613ea081613b8a565b94979496505050506040830135926060810135926080909101359150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6020810160038310613f28577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91905290565b60008083601f840112613f4057600080fd5b50813567ffffffffffffffff811115613f5857600080fd5b6020830191508360208260051b8501011115613f7357600080fd5b9250929050565b600060e08284031215613ca457600080fd5b60008060006101008486031215613fa257600080fd5b833567ffffffffffffffff811115613fb957600080fd5b613fc586828701613f2e565b9094509250613fd990508560208601613f7a565b90509250925092565b600060c08284031215613ca457600080fd5b60008060008060008060008060006102208a8c03121561401357600080fd5b893567ffffffffffffffff81111561402a57600080fd5b6140368c828d01613f2e565b909a5098505060208a013561404a81613b8a565b965060408a013561405a81613b8a565b955060608a0135945060808a0135935060a08a0135925061407e8b60c08c01613c92565b915061408e8b6101608c01613fe2565b90509295985092959850929598565b600080600080600061012086880312156140b657600080fd5b85356140c181613b8a565b94506020860135935060408601359250606086013591506140e58760808801613c92565b90509295509295909350565b6000806040838503121561410457600080fd5b823561410f81613b8a565b946020939093013593505050565b60008060006060848603121561413257600080fd5b833561413d81613b8a565b95602085013595506040909401359392505050565b600060e0828403121561416457600080fd5b611f588383613f7a565b6000806000806000806000610200888a03121561418a57600080fd5b873561419581613b8a565b965060208801356141a581613b8a565b95506040880135945060608801359350608088013592506141c98960a08a01613c92565b91506141d9896101408a01613fe2565b905092959891949750929550565b600080604083850312156141fa57600080fd5b823561420581613b8a565b91506020830135613d4d81613b8a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b80820180821115610a6e57610a6e614215565b81810381811115610a6e57610a6e614215565b60006020828403121561427c57600080fd5b813560ff81168114611f5857600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156143035761430361428d565b604052919050565b600067ffffffffffffffff8211156143255761432561428d565b5060051b60200190565b600082601f83011261434057600080fd5b815160206143556143508361430b565b6142bc565b82815260059290921b8401810191818101908684111561437457600080fd5b8286015b8481101561438f5780518352918301918301614378565b509695505050505050565b600080604083850312156143ad57600080fd5b825167ffffffffffffffff808211156143c557600080fd5b818501915085601f8301126143d957600080fd5b815160206143e96143508361430b565b82815260059290921b8401810191818101908984111561440857600080fd5b948201945b8386101561442f57855161442081613b8a565b8252948201949082019061440d565b9188015191965090935050508082111561444857600080fd5b506144558582860161432f565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000826144c4577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b8082028115828204841417610a6e57610a6e61421556fea26469706673582212207d4faf3d4ab35bcf2561df2a784101644b99c0c617c5d1c88e4fd1ec6cae691864736f6c63430008120033

Verified Source Code Full Match

Compiler: v0.8.18+commit.87f61d96 EVM: paris Optimization: Yes (4294967 runs)
EIP712.sol 237 lines
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.4;

abstract contract EIP712 {
    /// -----------------------------------------------------------------------
    /// Structs
    /// -----------------------------------------------------------------------

    /// @param v Part of the ECDSA signature
    /// @param r Part of the ECDSA signature
    /// @param s Part of the ECDSA signature
    struct WithdrawalPacket {
        uint8 v;
        bytes32 r;
        bytes32 s;
        uint256 deadline;
        uint256 amount;
        uint256 nonce;
        address account;
    }

    /// -----------------------------------------------------------------------
    /// Immutable parameters
    /// -----------------------------------------------------------------------

    /// @notice The chain ID used by EIP-712
    uint256 internal immutable INITIAL_CHAIN_ID;

    /// @notice The domain separator used by EIP-712
    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    /// -----------------------------------------------------------------------
    /// Constructor
    /// -----------------------------------------------------------------------

    constructor() {
        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
    }

    /// -----------------------------------------------------------------------
    /// Packet verification
    /// -----------------------------------------------------------------------

    function _verifySig(bytes memory data, uint8 v, bytes32 r, bytes32 s) internal virtual returns (address) {
        // verify signature
        address recoveredAddress =
            ecrecover(keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), keccak256(data))), v, r, s);
        return recoveredAddress;
    }

    /// @notice Verifies whether a packet is valid and returns the result.
    /// @dev The deadline, request, and signature are verified.
    /// @param packet The packet provided by the offchain data provider
    function _verifyWithdrawal(WithdrawalPacket calldata packet) internal virtual returns (address) {
        // verify deadline
        require(block.timestamp < packet.deadline, ">deadline");

        // verify signature
        address recoveredAddress = _verifySig(
            abi.encode(
                keccak256("VerifyWithdrawal(uint256 deadline,uint256 amount,uint256 nonce,address account)"),
                packet.deadline,
                packet.amount,
                packet.nonce,
                packet.account
            ),
            packet.v,
            packet.r,
            packet.s
        );
        return recoveredAddress; // Invariant: sequencer != address(0), we maintain this every time sequencer is set
    }

    struct Bid {
        uint8 v;
        bytes32 r;
        bytes32 s;
        bytes32 auctionId;
        uint256 maxAmount;
    }

    function _verifyBid(Bid calldata packet) internal virtual returns (address) {
        address recoveredAddress = _verifySig(
            abi.encode(keccak256("Bid(bytes32 auctionId,uint256 maxAmount)"), packet.auctionId, packet.maxAmount),
            packet.v,
            packet.r,
            packet.s
        );
        require(recoveredAddress != address(0), "sig");
        return recoveredAddress;
    }

    struct BidWinner {
        uint8 v;
        bytes32 r;
        bytes32 s;
        bytes32 auctionId;
        uint256 amount;
        address winner;
    }

    function _verifyBidWinner(BidWinner calldata packet) internal virtual returns (address) {
        return _verifySig(
            abi.encode(
                keccak256("BidWinner(bytes32 auctionId,uint256 amount,address winner)"),
                packet.auctionId,
                packet.amount,
                packet.winner
            ),
            packet.v,
            packet.r,
            packet.s
        );
    }

    struct CancelAuction {
        uint8 v;
        bytes32 r;
        bytes32 s;
        bytes32 auctionId;
        uint256 deadline;
    }

    function _verifyCancelAuction(CancelAuction calldata packet) internal virtual returns (address) {
        require(block.timestamp <= packet.deadline, "deadline");
        return _verifySig(
            abi.encode(
                keccak256("CancelAuction(bytes32 auctionId,uint256 deadline)"), packet.auctionId, packet.deadline
            ),
            packet.v,
            packet.r,
            packet.s
        );
    }

    struct Offer {
        uint8 v;
        bytes32 r;
        bytes32 s;
        address nftContract;
        uint256 nftId;
        uint256 amount;
        uint256 deadline;
        uint256 counter;
        uint256 nonce;
    }

    function _verifyBuyOffer(Offer calldata packet) internal virtual returns (address) {
        return _verifySig(
            abi.encode(
                keccak256(
                    "BuyOffer(address nftContract,uint256 nftId,uint256 amount,uint256 deadline,uint256 counter,uint256 nonce)"
                ),
                packet.nftContract,
                packet.nftId,
                packet.amount,
                packet.deadline,
                packet.counter,
                packet.nonce
            ),
            packet.v,
            packet.r,
            packet.s
        );
    }

    function _verifySellOffer(Offer calldata packet) internal virtual returns (address) {
        return _verifySig(
            abi.encode(
                keccak256(
                    "SellOffer(address nftContract,uint256 nftId,uint256 amount,uint256 deadline,uint256 counter,uint256 nonce)"
                ),
                packet.nftContract,
                packet.nftId,
                packet.amount,
                packet.deadline,
                packet.counter,
                packet.nonce
            ),
            packet.v,
            packet.r,
            packet.s
        );
    }

    struct OfferAttestation {
        uint8 v;
        bytes32 r;
        bytes32 s;
        bytes32 auctionId;
        uint256 amount;
        address buyer;
        address seller;
        uint256 deadline;
    }

    function _verifyOfferAttestation(OfferAttestation calldata packet) internal virtual returns (address) {
        return _verifySig(
            abi.encode(
                keccak256(
                    "OfferAttestation(bytes32 auctionId,uint256 amount,address buyer,address seller,uint256 deadline)"
                ),
                packet.auctionId,
                packet.amount,
                packet.buyer,
                packet.seller,
                packet.deadline
            ),
            packet.v,
            packet.r,
            packet.s
        );
    }

    /// -----------------------------------------------------------------------
    /// EIP-712 compliance
    /// -----------------------------------------------------------------------

    /// @notice The domain separator used by EIP-712
    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : _computeDomainSeparator();
    }

    /// @notice Computes the domain separator used by EIP-712
    function _computeDomainSeparator() internal view virtual returns (bytes32) {
        return keccak256(
            abi.encode(
                keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                keccak256("SealedArtMarket"),
                keccak256("1"),
                block.chainid,
                address(this)
            )
        );
    }
}
SealedFunding.sol 18 lines
pragma solidity ^0.8.7;

interface IExchange {
    function deposit(address receiver) external payable;
}

contract SealedFunding {
    constructor(address _owner, address _exchange) {
        IExchange(_exchange).deposit{value: address(this).balance}(_owner);
        assembly {
            // Ensures the runtime bytecode is a single opcode: `INVALID`. This reduces contract
            // deploy costs & ensures that no one can accidentally send ETH to the contract once
            // deployed.
            mstore8(0, 0xfe)
            return(0, 1)
        }
    }
}
SealedArtMarket.sol 457 lines
pragma solidity ^0.8.7;

import "./EIP712.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./SealedFundingFactory.sol";
import {BitMaps} from "@openzeppelin/contracts/utils/structs/BitMaps.sol";

interface IERC721 {
    function ownerOf(uint256 _tokenId) external view returns (address);
    function transferFrom(address _from, address _to, uint256 _tokenId) external;
}

interface RoyaltyEngine {
    function getRoyalty(address tokenAddress, uint256 tokenId, uint256 value)
        external
        returns (address payable[] memory recipients, uint256[] memory amounts);
}

contract SealedArtMarket is EIP712, Ownable {
    using BitMaps for BitMaps.BitMap;

    event Transfer(address indexed from, address indexed to, uint256 value);

    mapping(address => uint256) private _balances;
    string public constant name = "Sealed ETH";
    string public constant symbol = "SETH";
    uint8 public constant decimals = 18;

    function totalSupply() public view returns (uint256) {
        return address(this).balance;
    }

    // sequencer and settleSequencer are separated as an extra security measure against key leakage through side attacks
    // If a side channel attack is possible that requires multiple signatures to be made, settleSequencer will be more protected
    // against it because each signature will require an onchain action, which will make the attack extremely expensive
    // It also allows us to use different security systems for the two keys, since settleSequencer is much more sensitive
    address public sequencer; // Invariant: always different than address(0)
    address public settleSequencer; // Invariant: always different than address(0)
    address payable public treasury;
    SealedFundingFactory public immutable sealedFundingFactory;
    uint256 internal constant MAX_PROTOCOL_FEE = 0.1e18; // 10%
    uint256 public feeMultiplier;
    uint256 public forcedWithdrawDelay = 2 days;
    RoyaltyEngine public constant royaltyEngine = RoyaltyEngine(0xBc40d21999b4BF120d330Ee3a2DE415287f626C9);

    enum AuctionState {
        NONE, // 0 -> doesnt exist, default state
        CREATED,
        CLOSED
    }

    mapping(bytes32 => AuctionState) public auctionState;
    mapping(address => mapping(uint256 => mapping(uint256 => uint256))) public pendingWithdrawals;
    mapping(bytes32 => uint256) public pendingAuctionCancels;
    mapping(address => bool) public guardians;

    BitMaps.BitMap private usedNonces;
    mapping(address => BitMaps.BitMap) private usedOrderNonces;
    mapping(address => uint256) public accountCounter;

    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }

    constructor(address _sequencer, address payable _treasury, address _settleSequencer) {
        require(_sequencer != address(0) && _settleSequencer != address(0), "0x0 sequencer not allowed");
        sequencer = _sequencer;
        treasury = _treasury;
        settleSequencer = _settleSequencer;
        sealedFundingFactory = new SealedFundingFactory(address(this));
    }

    event SequencerChanged(address newSequencer, address newSettleSequencer);

    function changeSequencer(address newSequencer, address newSettleSequencer) external onlyOwner {
        require(newSequencer != address(0) && newSettleSequencer != address(0), "0x0 sequencer not allowed");
        sequencer = newSequencer;
        settleSequencer = newSettleSequencer;
        emit SequencerChanged(newSequencer, newSettleSequencer);
    }

    event ForcedWithdrawDelayChanged(uint256 newDelay);

    function changeForcedWithdrawDelay(uint256 newDelay) external onlyOwner {
        require(newDelay < 10 days, "<10 days");
        forcedWithdrawDelay = newDelay;
        emit ForcedWithdrawDelayChanged(newDelay);
    }

    event TreasuryChanged(address newTreasury);

    function changeTreasury(address payable newTreasury) external onlyOwner {
        treasury = newTreasury;
        emit TreasuryChanged(newTreasury);
    }

    event GuardianSet(address guardian, bool value);

    function setGuardian(address guardian, bool value) external onlyOwner {
        guardians[guardian] = value;
        emit GuardianSet(guardian, value);
    }

    event SequencerDisabled(address guardian);

    function emergencyDisableSequencer() external {
        require(guardians[msg.sender] == true, "not guardian");
        // Maintain the invariant that sequencers are not 0x0
        sequencer = address(0x000000000000000000000000000000000000dEaD);
        settleSequencer = address(0x000000000000000000000000000000000000dEaD);
        emit SequencerDisabled(msg.sender);
    }

    event FeeChanged(uint256 newFeeMultiplier);

    function changeFee(uint256 newFeeMultiplier) external onlyOwner {
        require(newFeeMultiplier <= MAX_PROTOCOL_FEE, "fee too high");
        feeMultiplier = newFeeMultiplier;
        emit FeeChanged(newFeeMultiplier);
    }

    function deposit(address receiver) public payable {
        _balances[receiver] += msg.value;
        emit Transfer(address(0), receiver, msg.value);
    }

    function _withdraw(uint256 amount) internal {
        _balances[msg.sender] -= amount;
        (bool success,) = payable(msg.sender).call{value: amount}("");
        require(success);
        emit Transfer(msg.sender, address(0), amount);
    }

    event WithdrawNonceUsed(uint256 nonce);

    function withdraw(WithdrawalPacket calldata packet) public {
        require(_verifyWithdrawal(packet) == sequencer, "!sequencer");
        require(nonceState(packet.nonce) == false, "replayed");
        usedNonces.set(packet.nonce);
        require(packet.account == msg.sender, "not sender");
        _withdraw(packet.amount);
        emit WithdrawNonceUsed(packet.nonce);
    }

    event StartWithdrawal(address owner, uint256 timestamp, uint256 nonce, uint256 amount);

    function startWithdrawal(uint256 amount, uint256 nonce) external {
        pendingWithdrawals[msg.sender][block.timestamp][nonce] = amount;
        emit StartWithdrawal(msg.sender, block.timestamp, nonce, amount);
    }

    event CancelWithdrawal(address owner, uint256 timestamp, uint256 nonce);

    function cancelPendingWithdrawal(uint256 timestamp, uint256 nonce) external {
        pendingWithdrawals[msg.sender][timestamp][nonce] = 0;
        emit CancelWithdrawal(msg.sender, timestamp, nonce);
    }

    event ExecuteDelayedWithdrawal(address owner, uint256 timestamp, uint256 nonce);

    function executePendingWithdrawal(uint256 timestamp, uint256 nonce) external {
        require(timestamp + forcedWithdrawDelay < block.timestamp, "too soon");
        uint256 amount = pendingWithdrawals[msg.sender][timestamp][nonce];
        pendingWithdrawals[msg.sender][timestamp][nonce] = 0;
        _withdraw(amount);
        emit ExecuteDelayedWithdrawal(msg.sender, timestamp, nonce);
    }

    function calculateAuctionHash(
        address owner,
        address nftContract,
        bytes32 auctionType,
        uint256 nftId,
        uint256 reserve
    ) public pure returns (bytes32) {
        return keccak256(abi.encode(owner, nftContract, auctionType, nftId, reserve));
    }

    event AuctionCreated(
        address owner, address nftContract, uint256 auctionDuration, bytes32 auctionType, uint256 nftId, uint256 reserve
    );

    function _createAuction(
        address nftContract,
        uint256 auctionDuration,
        bytes32 auctionType,
        uint256 nftId,
        uint256 reserve
    ) internal {
        bytes32 auctionId = calculateAuctionHash(msg.sender, nftContract, auctionType, nftId, reserve);
        require(auctionState[auctionId] == AuctionState.NONE, "repeated auction id"); // maybe this is not needed?
        auctionState[auctionId] = AuctionState.CREATED;
        emit AuctionCreated(msg.sender, nftContract, auctionDuration, auctionType, nftId, reserve);
    }

    function createAuction(
        address nftContract,
        uint256 auctionDuration,
        bytes32 auctionType,
        uint256 nftId,
        uint256 reserve
    ) external {
        IERC721(nftContract).transferFrom(msg.sender, address(this), nftId);
        _createAuction(nftContract, auctionDuration, auctionType, nftId, reserve);
    }

    event AuctionCancelled(bytes32 auctionId);

    function _cancelAuction(
        address nftContract,
        bytes32 auctionType,
        uint256 nftId,
        uint256 reserve,
        CancelAuction calldata cancelAuctionPacket
    ) internal {
        require(_verifyCancelAuction(cancelAuctionPacket) == sequencer, "!sequencer");
        bytes32 auctionId = calculateAuctionHash(msg.sender, nftContract, auctionType, nftId, reserve);
        require(auctionState[auctionId] == AuctionState.CREATED, "bad state");
        require(cancelAuctionPacket.auctionId == auctionId, "!auctionId");
        auctionState[auctionId] = AuctionState.CLOSED;
        emit AuctionCancelled(auctionId);
    }

    function cancelAuction(
        address nftContract,
        bytes32 auctionType,
        uint256 nftId,
        uint256 reserve,
        CancelAuction calldata cancelAuctionPacket
    ) external {
        _cancelAuction(nftContract, auctionType, nftId, reserve, cancelAuctionPacket);
        IERC721(nftContract).transferFrom(address(this), msg.sender, nftId);
    }

    function changeAuction(
        address nftContract,
        bytes32 auctionType,
        uint256 nftId,
        uint256 reserve,
        uint256 newAuctionDuration,
        bytes32 newAuctionType,
        uint256 newReserve,
        CancelAuction calldata cancelAuctionPacket
    ) external {
        _cancelAuction(nftContract, auctionType, nftId, reserve, cancelAuctionPacket);
        _createAuction(nftContract, newAuctionDuration, newAuctionType, nftId, newReserve);
    }

    event StartDelayedAuctionCancel(bytes32 auctionId);

    function startCancelAuction(
        address nftContract,
        bytes32 auctionType,
        uint256 nftId,
        uint256 reserve
    ) external {
        bytes32 auctionId = calculateAuctionHash(msg.sender, nftContract, auctionType, nftId, reserve);
        require(auctionState[auctionId] == AuctionState.CREATED, "bad auction state");
        pendingAuctionCancels[auctionId] = block.timestamp;
        emit StartDelayedAuctionCancel(auctionId);
    }

    event ExecuteDelayedAuctionCancel(bytes32 auctionId);

    function executeCancelAuction(
        address nftContract,
        bytes32 auctionType,
        uint256 nftId,
        uint256 reserve
    ) external {
        bytes32 auctionId = calculateAuctionHash(msg.sender, nftContract, auctionType, nftId, reserve);
        uint256 timestamp = pendingAuctionCancels[auctionId];
        require(timestamp != 0 && (timestamp + forcedWithdrawDelay) < block.timestamp, "too soon");
        require(auctionState[auctionId] == AuctionState.CREATED, "not open");
        auctionState[auctionId] = AuctionState.CLOSED;
        pendingAuctionCancels[auctionId] = 0;
        emit AuctionCancelled(auctionId);
        IERC721(nftContract).transferFrom(address(this), msg.sender, nftId);
        emit ExecuteDelayedAuctionCancel(auctionId);
    }

    function _transferETH(address payable receiver, uint256 amount) internal {
        (bool success,) = receiver.call{value: amount, gas: 300_000}("");
        if (success == false) {
            _balances[receiver] += amount;
            emit Transfer(address(0), receiver, amount);
        }
    }

    function _distributeSale(address nftContract, uint256 nftId, uint256 amount, address payable seller) internal {
        uint256 totalRoyalty = 0;
        try royaltyEngine.getRoyalty{gas: 500_000}(nftContract, nftId, amount) returns (address payable[] memory recipients, uint256[] memory amounts) {
            uint length = 5; // Use a maximum of 5 items to avoid attacks that blow up gas limit
            if(recipients.length < length){
                length = recipients.length;
            }
            if(amounts.length < length){
                length = amounts.length;
            }
            for (uint256 i; i < length;) {
                _transferETH(recipients[i], amounts[i]);
                totalRoyalty += amounts[i];
                unchecked {
                    ++i;
                }
            }
            require(totalRoyalty <= (amount / 3), "Royalty too high"); // Protect against royalty hacks
        } catch {}
        uint256 feeAmount = (amount * feeMultiplier) / 1e18;
        _transferETH(treasury, feeAmount);
        _transferETH(seller, amount - (totalRoyalty + feeAmount)); // totalRoyalty+feeAmount <= amount*0.43
    }

    event AuctionSettled(bytes32 auctionId);

    function settleAuction(
        address payable nftOwner,
        address nftContract,
        bytes32 auctionType,
        uint256 nftId,
        uint256 reserve,
        Bid calldata bid,
        BidWinner calldata bidWinner
    ) public {
        bytes32 auctionId = calculateAuctionHash(nftOwner, nftContract, auctionType, nftId, reserve);
        require(auctionState[auctionId] == AuctionState.CREATED, "bad auction state");
        auctionState[auctionId] = AuctionState.CLOSED;
        require(bidWinner.auctionId == auctionId && bid.auctionId == auctionId, "!auctionId");
        uint256 amount = bidWinner.amount;
        require(amount <= bid.maxAmount && amount >= reserve, "!amount");
        require(_verifyBid(bid) == bidWinner.winner, "!winner");
        require(_verifyBidWinner(bidWinner) == settleSequencer, "!settleSequencer");
        _balances[bidWinner.winner] -= amount;
        emit Transfer(bidWinner.winner, address(0), amount);
        IERC721(nftContract).transferFrom(address(this), bidWinner.winner, nftId);
        _distributeSale(nftContract, nftId, amount, nftOwner);
        emit AuctionSettled(auctionId);
    }

    function _revealBids(bytes32[] calldata salts, address owner) internal {
        for (uint256 i = 0; i < salts.length;) {
            // We use try/catch here to prevent a griefing attack where someone could deploySealedFunding() one of the
            // sealed fundings of the buyer right before another user calls this function, thus making it revert
            // It's still possible for the buyer to perform this attack by frontrunning the call with a withdraw()
            // but that's trivial to solve by just revealing all the salts of the griefing user
            try sealedFundingFactory.deploySealedFunding{gas: 100_000}(salts[i], owner) {} // cost of deploySealedFunding() is between 55k and 82k
                catch {}
            unchecked {
                ++i;
            }
        }
    }

    function settleAuctionWithSealedBids(
        bytes32[] calldata salts,
        address payable nftOwner,
        address nftContract,
        bytes32 auctionType,
        uint256 nftId,
        uint256 reserve,
        Bid calldata bid,
        BidWinner calldata bidWinner
    ) external {
        _revealBids(salts, bidWinner.winner);
        settleAuction(nftOwner, nftContract, auctionType, nftId, reserve, bid, bidWinner);
    }

    function withdrawWithSealedBids(bytes32[] calldata salts, WithdrawalPacket calldata packet) external {
        _revealBids(salts, msg.sender);
        withdraw(packet);
    }

    event CounterIncreased(address account, uint256 newCounter);

    function increaseCounter(uint256 newCounter) external {
        require(newCounter > accountCounter[msg.sender], "too low");
        accountCounter[msg.sender] = newCounter;
        emit CounterIncreased(msg.sender, newCounter);
    }

    event OfferCancelled(address account, uint256 nonce);

    function cancelOffer(uint256 nonce) external {
        usedOrderNonces[msg.sender].set(nonce);
        emit OfferCancelled(msg.sender, nonce);
    }

    function _verifyOffer(Offer calldata offer, address creator) private {
        require(offer.deadline > block.timestamp, "!deadline");
        require(orderNonces(creator, offer.nonce) == false, "!orderNonce");
        usedOrderNonces[msg.sender].set(offer.nonce);
        require(offer.counter > accountCounter[creator], "!counter");
    }

    event OrdersMatched(bytes32 auctionId, address buyer, address sender, uint256 buyerNonce, uint256 sellerNonce);

    function matchOrders(
        Offer calldata sellerOffer,
        Offer calldata buyerOffer,
        OfferAttestation calldata sequencerStamp,
        address nftContract,
        bytes32 auctionType,
        uint256 nftId,
        uint256 reserve
    ) external {
        // First run verifications that can fail due to a delayed tx
        require(sequencerStamp.deadline > block.timestamp, "!deadline");
        if (msg.sender != sequencerStamp.buyer) {
            _verifyOffer(buyerOffer, sequencerStamp.buyer);
            require(_verifyBuyOffer(buyerOffer) == sequencerStamp.buyer && sequencerStamp.buyer != address(0), "!buyer");
        }
        if (msg.sender != sequencerStamp.seller) {
            _verifyOffer(sellerOffer, sequencerStamp.seller);
            require(
                _verifySellOffer(sellerOffer) == sequencerStamp.seller && sequencerStamp.seller != address(0), "!seller"
            );
        }
        // Verify NFT is owned by seller
        bytes32 auctionId = calculateAuctionHash(
            sequencerStamp.seller,
            nftContract,
            auctionType,
            nftId,
            reserve
        );
        require(auctionState[auctionId] == AuctionState.CREATED && sequencerStamp.auctionId == auctionId, "bad auction state");
        // Execute sale
        _balances[sequencerStamp.buyer] -= sequencerStamp.amount;
        emit Transfer(sequencerStamp.buyer, address(0), sequencerStamp.amount);
        auctionState[auctionId] = AuctionState.CLOSED;

        // Run verifications that can't fail due to external factors
        require(sequencerStamp.amount == sellerOffer.amount && sequencerStamp.amount == buyerOffer.amount, "!amount");
        require(
            nftContract == sellerOffer.nftContract
                && nftContract == buyerOffer.nftContract,
            "!nftContract"
        );
        require(nftId == sellerOffer.nftId && nftId == buyerOffer.nftId, "!nftId");
        require(_verifyOfferAttestation(sequencerStamp) == sequencer, "!sequencer"); // This needs sequencer approval to avoid someone rugging their bids by buying another NFT

        // Finish executing sale
        IERC721(nftContract).transferFrom(address(this), sequencerStamp.buyer, nftId);
        _distributeSale(
            nftContract, nftId, sequencerStamp.amount, payable(sequencerStamp.seller)
        );
        emit OrdersMatched(auctionId, sequencerStamp.buyer, msg.sender, buyerOffer.nonce, sellerOffer.nonce);
    }

    function nonceState(uint256 nonce) public view returns (bool) {
        return usedNonces.get(nonce);
    }

    function orderNonces(address account, uint256 nonce) public view returns (bool) {
        return usedOrderNonces[account].get(nonce);
    }
}
SealedFundingFactory.sol 40 lines
pragma solidity ^0.8.7;

import "./SealedFunding.sol";

contract SealedFundingFactory {
    address public immutable exchange;

    constructor(address _exchange) {
        exchange = _exchange;
    }

    event SealedFundingRevealed(bytes32 salt, address owner);

    function deploySealedFunding(bytes32 salt, address owner) public {
        new SealedFunding{salt: salt}(owner, exchange);
        emit SealedFundingRevealed(salt, owner);
    }

    function computeSealedFundingAddress(bytes32 salt, address owner)
        external
        view
        returns (address predictedAddress, bool isDeployed)
    {
        predictedAddress = address(
            uint160(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            bytes1(0xff),
                            address(this),
                            salt,
                            keccak256(abi.encodePacked(type(SealedFunding).creationCode, abi.encode(owner, exchange)))
                        )
                    )
                )
            )
        );
        isDeployed = predictedAddress.code.length != 0;
    }
}
Context.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
Ownable.sol 83 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

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

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

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
BitMaps.sol 51 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/BitMaps.sol)
pragma solidity ^0.8.0;

/**
 * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential.
 * Largely inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
 */
library BitMaps {
    struct BitMap {
        mapping(uint256 => uint256) _data;
    }

    /**
     * @dev Returns whether the bit at `index` is set.
     */
    function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        return bitmap._data[bucket] & mask != 0;
    }

    /**
     * @dev Sets the bit at `index` to the boolean `value`.
     */
    function setTo(BitMap storage bitmap, uint256 index, bool value) internal {
        if (value) {
            set(bitmap, index);
        } else {
            unset(bitmap, index);
        }
    }

    /**
     * @dev Sets the bit at `index`.
     */
    function set(BitMap storage bitmap, uint256 index) internal {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        bitmap._data[bucket] |= mask;
    }

    /**
     * @dev Unsets the bit at `index`.
     */
    function unset(BitMap storage bitmap, uint256 index) internal {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        bitmap._data[bucket] &= ~mask;
    }
}

Read Contract

DOMAIN_SEPARATOR 0x3644e515 → bytes32
accountCounter 0xcc8887f1 → uint256
auctionState 0x64750c00 → uint8
balanceOf 0x70a08231 → uint256
calculateAuctionHash 0x4b3c8643 → bytes32
decimals 0x313ce567 → uint8
feeMultiplier 0xe5a70ef7 → uint256
forcedWithdrawDelay 0x7522a689 → uint256
guardians 0x0633b14a → bool
name 0x06fdde03 → string
nonceState 0x18017d17 → bool
orderNonces 0xba7df63d → bool
owner 0x8da5cb5b → address
pendingAuctionCancels 0x37a61d72 → uint256
pendingWithdrawals 0xc02909ce → uint256
royaltyEngine 0x4c94c90c → address
sealedFundingFactory 0xe5204472 → address
sequencer 0x5c1bba38 → address
settleSequencer 0x0be650b2 → address
symbol 0x95d89b41 → string
totalSupply 0x18160ddd → uint256
treasury 0x61d027b3 → address

Write Contract 24 functions

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

cancelAuction 0x758d1b35
address nftContract
bytes32 auctionType
uint256 nftId
uint256 reserve
tuple cancelAuctionPacket
cancelOffer 0xef706adf
uint256 nonce
cancelPendingWithdrawal 0x2d435322
uint256 timestamp
uint256 nonce
changeAuction 0xb1189979
address nftContract
bytes32 auctionType
uint256 nftId
uint256 reserve
uint256 newAuctionDuration
bytes32 newAuctionType
uint256 newReserve
tuple cancelAuctionPacket
changeFee 0x6a1db1bf
uint256 newFeeMultiplier
changeForcedWithdrawDelay 0xc16973c6
uint256 newDelay
changeSequencer 0xfbde0511
address newSequencer
address newSettleSequencer
changeTreasury 0xb14f2a39
address newTreasury
createAuction 0x25d86456
address nftContract
uint256 auctionDuration
bytes32 auctionType
uint256 nftId
uint256 reserve
deposit 0xf340fa01
address receiver
emergencyDisableSequencer 0xa03fd9c2
No parameters
executeCancelAuction 0x2e8f3cb8
address nftContract
bytes32 auctionType
uint256 nftId
uint256 reserve
executePendingWithdrawal 0x2ff4e454
uint256 timestamp
uint256 nonce
increaseCounter 0x9e80c074
uint256 newCounter
matchOrders 0x76af1953
tuple sellerOffer
tuple buyerOffer
tuple sequencerStamp
address nftContract
bytes32 auctionType
uint256 nftId
uint256 reserve
renounceOwnership 0x715018a6
No parameters
setGuardian 0x2b8a1c5a
address guardian
bool value
settleAuction 0xc3b84f27
address nftOwner
address nftContract
bytes32 auctionType
uint256 nftId
uint256 reserve
tuple bid
tuple bidWinner
settleAuctionWithSealedBids 0xd56ca6b2
bytes32[] salts
address nftOwner
address nftContract
bytes32 auctionType
uint256 nftId
uint256 reserve
tuple bid
tuple bidWinner
startCancelAuction 0x9fef6622
address nftContract
bytes32 auctionType
uint256 nftId
uint256 reserve
startWithdrawal 0x70431d29
uint256 amount
uint256 nonce
transferOwnership 0xf2fde38b
address newOwner
withdraw 0xa3c62f34
tuple packet
withdrawWithSealedBids 0x052dabef
bytes32[] salts
tuple packet

Token Balances (1)

View Transfers →
WETH 0

Recent Transactions

No transactions found for this address