Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x77f48CCB3EDE5bF4aBFB8eeFe61739ED01C240ce
Balance 0 ETH
Nonce 3
Code Size 11484 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

11484 bytes
0x6080604052600436101562000014575b600080fd5b60003560e01c806304789c98146200010f5780630eb92516146200010457806314afd79e14620000f95780634b42e8d514620000ee57806351710e4514620000e357806366a2489f14620000d85780636d43542114620000cd5780637b37e56114620000c2578063906c87cc14620000b7578063dca0938314620000ac5763e89fad5814620000a257600080fd5b6200000f62000d34565b506200000f62000a6c565b506200000f62000a1e565b506200000f6200095d565b506200000f62000835565b506200000f62000797565b506200000f620005ef565b506200000f62000556565b506200000f6200050b565b506200000f62000463565b506200000f62000222565b600435906001600160a01b03821682036200000f57565b606435906001600160a01b03821682036200000f57565b602435906001600160a01b03821682036200000f57565b919082519283825260005b8481106200018c575050826000602080949584010152601f8019910116010190565b6020818301810151848301820152016200016a565b91959492620001d3918352620001c460209760a08986015260a08501906200015f565b9083820360408501526200015f565b818103606083015285808551928381520194019060005b8181106200020d575050506200020a93945060808184039101526200015f565b90565b825186529487019491870191600101620001ea565b50346200000f576020806003193601126200000f57620002416200011a565b906200024d826200157b565b60009160018060a01b0381168352828252604080842092815192608051907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f87527f0aa333075d560d6d2e102e9d7afe8a8ba8b4a3ec41e19473caad880f9eb5fbe683527fe6bbd6277e1bf288eed5e8d1780f9a50b239e86b153736bceebccf4ea79d90b384524660605260805260a0862093835285606052608052620002f76002850162001409565b93620003066003820162001409565b90600581019684518094808a54928381520199835280832092905b8282106200035f575050505060046200034f9162000348856200035b98999a0386620003b2565b0162001409565b925195869586620001a1565b0390f35b83548b52998a01996001938401939091019062000321565b50634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff8111620003a357604052565b620003ad62000377565b604052565b90601f8019910116810190811067ffffffffffffffff821117620003a357604052565b604051906020820182811067ffffffffffffffff821117620003a357604052565b81601f820112156200000f5780359067ffffffffffffffff821162000453575b6040519262000430601f8401601f191660200185620003b2565b828452602083830101116200000f57816000926020809301838601378301015290565b6200045d62000377565b62000416565b50346200000f5760a03660031901126200000f5767ffffffffffffffff6004358181116200000f576200049b903690600401620003f6565b906024358181116200000f57620004b7903690600401620003f6565b916044359182116200000f576200035b92620004dc620004f1933690600401620003f6565b620004e662000131565b9160843593620010d9565b6040516001600160a01b0390911681529081906020820190565b50346200000f5760203660031901126200000f5760206200052b6200011a565b62000536816200157b565b60018060a01b038091166000526000825260406000205416604051908152f35b50346200000f5760203660031901126200000f576040516001600160f81b031960208083019182526bffffffffffffffffffffffff193060601b16602184015260043560358401527fac00cb7082c31222a6e9858037310d59564fa6ef5b4ae03d11cc79ad4c7a2ee860558085019190915283529190620005d9607583620003b2565b905190206040516001600160a01b039091168152f35b50346200000f5760203660031901126200000f576200060d6200011a565b62000618816200157b565b620006596200064d60016200063f8460018060a01b03166000526000602052604060002090565b01546001600160a01b031690565b6001600160a01b031690565b33036200072e576200072b600091827f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da8180a2620006c06001620006af8360018060a01b03166000526000602052604060002090565b0180546001600160a01b0319169055565b6001600160a01b03818116600081815260208190526040902054339216907fc8894f26f396ce8c004245c8b7cd1b92103a6e4302fcbab883987149ac01b7ec8680a46001600160a01b0316600090815260208190526040902080546001600160a01b03191633179055565b80f35b6040516388c3a11560e01b81526001600160a01b03919091166004820152602490fd5b6020908160408183019282815285518094520193019160005b82811062000779575050505090565b83516001600160a01b0316855293810193928101926001016200076a565b50346200000f576020806003193601126200000f57620007b66200011a565b90620007c2826200157b565b60018060a01b0391826000911681528082526007604082200192604051908193808654938481520195845280842093915b8383106200081b576200035b866200080e818a0382620003b2565b6040519182918262000751565b8454811687529581019560019485019490920191620007f3565b50346200000f5760403660031901126200000f57620008536200011a565b6200085d62000148565b906200086981620014b9565b6001600160a01b03828116908115620009435750620008a46200064d60016200063f8560018060a01b03166000526000602052604060002090565b81146200091957620008f76200091793926001927f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da600080a26001600160a01b0316600090815260208190526040902090565b0180546001600160a01b0319166001600160a01b03909216919091179055565b005b506040516365e0406560e11b81526001600160a01b03918216600482015291166024820152604490fd5b60405163a388d26360e01b81529083166004820152602490fd5b50346200000f5760203660031901126200000f576200097b6200011a565b6200098681620014b9565b6001600160a01b03818116600081815260208190526040812060010154909392161562000a065750620009f4600191837f11a3cf439fb225bfe74225716b6774765670ec1060e3796802e62139d69974da8180a26001600160a01b0316600090815260208190526040902090565b0180546001600160a01b031916905580f35b602490604051906335809b0b60e11b82526004820152fd5b50346200000f5760203660031901126200000f57602062000a3e6200011a565b62000a49816200157b565b60018060a01b038091166000526000825260016040600020015416604051908152f35b50346200000f5760603660031901126200000f5762000a8a6200011a565b62000a9462000148565b6044359182151583036200000f5762000aad8162001503565b6001600160a01b03811660009081526020819052604090209262000ad3818486620015b0565b6001600160a01b03828116803b156200000f5760405163f460590b60e01b81526001600160a01b03861660048201528315156024820152906000908290604490829084905af1801562000d24575b62000d06575b5062000b8e62000b7f856006880162000b688662000b5784849060018060a01b0316600052602052604060002090565b9060ff801983541691151516179055565b9060018060a01b0316600052602052604060002090565b805461ff001916610100179055565b811562000bfc575062000bf79062000bcb8460077fb658b57f3a8d73a4f3bb96789edcebed831f44d5cc4a513d4416c6bb25ea0e8b9701620013cd565b604080516001600160a01b03948516815294909316602085015215159183019190915281906060820190565b0390a1005b838116946007019060005b82548082101562000cd457878362000c3962000c24858862001332565b905460039190911b1c6001600160a01b031690565b161462000c4a575060010162000c07565b7fb658b57f3a8d73a4f3bb96789edcebed831f44d5cc4a513d4416c6bb25ea0e8b975062000cce925062000bf794939162000ca462000c9c62000c2462000c9562000cc89562001361565b8662001332565b918462001332565b90919082549060031b9160018060a01b039283811b93849216901b16911916179055565b62001387565b62000bcb565b505050507fb658b57f3a8d73a4f3bb96789edcebed831f44d5cc4a513d4416c6bb25ea0e8b935062000bf79062000bcb565b8062000d1662000d1d926200038e565b8062001326565b3862000b27565b62000d2e62000e83565b62000b21565b50346200000f5760403660031901126200000f5762000d526200011a565b602490813567ffffffffffffffff8082116200000f57366023830112156200000f5781600401359081116200000f57368482840101116200000f5762000d988362001503565b60009260018060a01b03168352602093838552600360408520019262000dcb8362000dc4865462000e90565b8662000ee6565b8495601f841160011462000e0d57509484958394959362000dff575b5050508160011b916000199060031b1c191617905580f35b010135905038808062000de7565b91601f1984169662000e2486600052602060002090565b9387905b89821062000e685750508460019697981062000e4b575b50505050811b01905580f35b60001960f88660031b161c19920101351690553880808062000e3f565b80600184978683959689010135815501960192019062000e28565b506040513d6000823e3d90fd5b90600182811c9216801562000ec2575b602083101462000eac57565b634e487b7160e01b600052602260045260246000fd5b91607f169162000ea0565b81811062000ed9575050565b6000815560010162000ecd565b9190601f811162000ef657505050565b62000f25926000526020600020906020601f840160051c8301931062000f27575b601f0160051c019062000ecd565b565b909150819062000f17565b919091825167ffffffffffffffff81116200100f575b62000f608162000f59845462000e90565b8462000ee6565b602080601f831160011462000f9f57508192939460009262000f93575b50508160011b916000199060031b1c1916179055565b01519050388062000f7d565b90601f1983169562000fb685600052602060002090565b926000905b88821062000ff65750508360019596971062000fdc575b505050811b019055565b015160001960f88460031b161c1916905538808062000fd2565b8060018596829496860151815501950193019062000fbb565b6200101962000377565b62000f48565b80549160019283835580841062001064575b50906000526020908160002060005b8481106200104f575050505050565b825160ff168282015591830191840162001040565b6200107e9083600052846020600020918201910162000ecd565b3862001031565b95949390608093620010b6620010d494620010c59360018060a01b03168a5260a060208b015260a08a01906200015f565b9088820360408a01526200015f565b9086820360608801526200015f565b930152565b9294936001600160a01b0380871694909390919085156200131457338260601c0362001302576040516001600160f81b0319602082019081526bffffffffffffffffffffffff193060601b166021830152603582018490527fac00cb7082c31222a6e9858037310d59564fa6ef5b4ae03d11cc79ad4c7a2ee8605580840191909152825262001184916200064d919062001175607582620003b2565b5190206001600160a01b031690565b97883b620012e257827f106160dca18b77e6926325d074dd8328fbec25f496d6fd313ef44f3e581e2afe949262001275620012839362001230620012118e60009c9b99604051806115ca8082019082821067ffffffffffffffff831117620012d2575b620016dd833903908ff515620012ae576001600160a01b0316600090815260208190526040902090565b80546001600160a01b0319166001600160a01b03909316929092178255565b6200123f866002830162000f32565b6200124e876003830162000f32565b6200125d836004830162000f32565b600562001269620003d5565b6003815291016200101f565b6040519485948c8662001085565b0390a184167fc8894f26f396ce8c004245c8b7cd1b92103a6e4302fcbab883987149ac01b7ec8280a4565b620012b862000e83565b6001600160a01b0316600090815260208190526040902090565b620012dc62000377565b620011e7565b6040516283438560e01b81526001600160a01b038a166004820152602490fd5b6040516332db94d160e21b8152600490fd5b60405163267eaa8160e21b8152600490fd5b60009103126200000f57565b80548210156200134b5760005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b6000198101919082116200137157565b634e487b7160e01b600052601160045260246000fd5b80548015620013b7576000190190620013a1828262001332565b81549060018060a01b039060031b1b1916905555565b634e487b7160e01b600052603160045260246000fd5b9062000ca462000f259280549068010000000000000000821015620013f9575b60018201815562001332565b6200140362000377565b620013ed565b90604051918260008254926200141f8462000e90565b9081845260019485811690816000146200149457506001146200144d575b505062000f2592500383620003b2565b9093915060005260209081600020936000915b8183106200147b57505062000f25935082010138806200143d565b8554888401850152948501948794509183019162001460565b91505062000f2594506020925060ff191682840152151560051b82010138806200143d565b620014c4816200157b565b6001600160a01b039081166000818152602081905260409020549091163303620014eb5750565b6024906040519063d4ed9a1760e01b82526004820152fd5b6200150e816200157b565b60018060a01b03809116908160005260006020526040600020906040600020541633141590816200155b575b50620015435750565b60249060405190636e1f2bef60e01b82526004820152fd5b33600090815260069091016020526040902060ff9150541615386200153a565b6001600160a01b0390811660009081526020819052604090205416156200159e57565b604051631634d3c960e31b8152600490fd5b9080926000146200168257506001600160a01b038216156200167057600601620015f7620015f083839060018060a01b0316600052602052604060002090565b5460ff1690565b6200164f576001600160a01b03821660009081526020919091526040902062001624905460081c60ff1690565b6200162c5750565b604051634e8336ad60e11b81526001600160a01b03919091166004820152602490fd5b6040516327ec359360e21b81526001600160a01b0383166004820152602490fd5b6040516367db084560e11b8152600490fd5b6001600160a01b0316600090815260069190910160205260409020620016b190620016ad90620015f0565b1590565b620016b95750565b604051630de0cce560e41b81526001600160a01b03919091166004820152602490fdfe610160604052346200029f5762000015620002fa565b6020815191012060a0526200002962000322565b6020815191012060c0526040516020810181620000f3620000e6620000bd620000a1620000866200006e87600d906c08a92a06e626488dedac2d2dc5609b1b81520190565b6b1cdd1c9a5b99c81b985b594b60a21b8152600c0190565b6e1cdd1c9a5b99c81d995c9cda5bdb8b608a1b8152600f0190565b6f1d5a5b9d0c8d4d8818da185a5b92590b60821b815260100190565b7f6164647265737320766572696679696e67436f6e747261637400000000000000815260190190565b602960f81b815260010190565b039162000109601f1993848101835282620002d6565b51902060e052604051620001ce602082019282620001c1620000e6620001a86200018a6200016c6200014e8a600c906b0a6d2cedccac89ee4c8cae4560a31b81520190565b711859191c995cdcc8199d5b199a5b1b195c8b60721b815260120190565b711d5a5b9d0d8d08195e1c1a5c985d1a5bdb8b60721b815260120190565b71189e5d195ccccc881bdc99195c92185cda0b60721b815260120190565b6c189e5d195cc818dbdb9d195e1d609a1b8152600d0190565b03908101835282620002d6565b519020610100908152610120468152336080526200021c60e0519060a0519160c0516040519360805192600052602052604052466060523060805260a0600020926040526000606052608052565b610140908152604051917f98a7ac23945182ac62b68fbe5ba35cc0bf5c4c34b3a410ce94a4c2270282d6b5600080a16112849384620003468539608051848181610a6e01528181610b680152610d5d015260a05184610fe6015260c0518461100a015260e05184610fc301525183610e7201525182610f7001525181610f970152f35b600080fd5b604081019081106001600160401b03821117620002c057604052565b634e487b7160e01b600052604160045260246000fd5b601f909101601f19168101906001600160401b03821190821017620002c057604052565b604051906200030982620002a4565b600a8252695369676e65645a6f6e6560b01b6020830152565b604051906200033182620002a4565b60058252640312e302e360dc1b602083015256fe6080604052600436101561001e575b610016610991565b602081519101f35b60003560e01c806301e4d72a146100aa5780630be2ebbe146100a157806317b1f942146100985780632e778efc1461008f57806369c56dc914610086578063c3b1e7051461007d5763d61d79d20361000e576100786106c5565b61000e565b5061007861069b565b50610078610677565b506100786104b0565b50610078610270565b5061007861023b565b346100c9576100b8366100ce565b5062f26b9560e11b60805260206080f35b600080fd5b600319906020818301126100c957600435916001600160401b0383116100c95782610140920301126100c95760040190565b50634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b0382111761013257604052565b61013a610100565b604052565b60e081019081106001600160401b0382111761013257604052565b90601f801991011681019081106001600160401b0382111761013257604052565b6040516d086dedce6d2c8cae4c2e8d2dedc560931b60208201527f52656365697665644974656d5b5d20636f6e73696465726174696f6e00000000602e820152602960f81b604a820152602b8152606081018181106001600160401b038211176101e6575b60405290565b6101ee610100565b6101e0565b60005b8381106102065750506000910152565b81810151838201526020016101f6565b9060209161022f815180928185528580860191016101f3565b601f01601f1916010190565b50346100c95760003660031901126100c95761026c61025861017b565b604051918291602083526020830190610216565b0390f35b50346100c95761027f366100ce565b610287611045565b61029460a08201826106e2565b8235929160c435607d1960248201350161041757604481013560f81c6104055760a181013560f81c6103f3576059013560c01c924284106103dc579061033a6103166103489561034294886102f5886102ed8189610714565b9a9098610725565b9390926103018361105f565b908360a435602401356103b6575b5050610e51565b61031e610f6b565b61190160f01b6000526002526022526042600020906000602252565b9236916107d7565b90610ee5565b61037561037161036a8360018060a01b03166000526000602052604060002090565b5460ff1690565b1590565b61038b57604051630bd8fca160e11b8152602090f35b6040516317c3008960e01b81526001600160a01b039190911660048201526024810191909152604490fd5b6103d06103ca8360806103d5950190610778565b90611110565b611096565b388361030f565b50505063165460716000526020526040526044601cfd5b8463267879996000526020526024601cfd5b8463641157746000526020526024601cfd5b8463d232fd2c6000526020526024601cfd5b61043b60409283835283830190610216565b906020908181840391015283519182815281810182808560051b8401019601946000925b858410610470575050505050505090565b90919293949596858061049f600193601f1986820301885286838d518051845201519181858201520190610216565b99019401940192959493919061045f565b50346100c9576000806003193601126105605760408051916104d183610117565b60018352805b602080821015610504578351602092916104f082610117565b8482526060818301528287010152016104d7565b8461054861026c8660076105178561084b565b515261053a610524610d36565b9086989493989592955195869460208601610887565b03601f19810183528261015a565b60206105538661084b565b5101525192839283610429565b80fd5b6040516c0a4cac6cad2eccac892e8cada5609b1b60208201526e1d5a5b9d0e081a5d195b551e5c194b608a1b602d8201526d1859191c995cdcc81d1bdad95b8b60921b603c820152721d5a5b9d0c8d4d881a59195b9d1a599a595c8b606a1b604a8201526e1d5a5b9d0c8d4d88185b5bdd5b9d0b608a1b605d820152701859191c995cdcc81c9958da5c1a595b9d607a1b606c820152602960f81b607d820152605e8152608081018181106001600160401b038211176101e65760405290565b61062b61017b565b610633610563565b610671602060405180938261065181840197888151938492016101f3565b8201610665825180938680850191016101f3565b0103808452018261015a565b51902090565b50346100c95760003660031901126100c9576020610693610623565b604051908152f35b50346100c95760003660031901126100c95760206106b7610563565b818151910120604051908152f35b50346100c95760003660031901126100c95761026c610258610563565b903590601e19813603018212156100c957018035906001600160401b0382116100c9576020019181360383136100c957565b90605d116100c957601d0190604090565b9092919283605d116100c95783116100c957605d0191605c190190565b919091826004116100c95782116100c9576004916003190190565b919091826024116100c95782116100c9576024916023190190565b903590601e19813603018212156100c957018035906001600160401b0382116100c9576020019160a08202360383136100c957565b6020906001600160401b0381116107ca575b601f01601f19160190565b6107d2610100565b6107bf565b9291926107e3826107ad565b916107f1604051938461015a565b8294818452818301116100c9578281602093846000960137010152565b6020906001600160401b038111610827575b60051b0190565b61082f610100565b610820565b50634e487b7160e01b600052603260045260246000fd5b602090805115610859570190565b610861610834565b0190565b602091815181101561087a575b60051b010190565b610882610834565b610872565b909493919481526108a46020956080878401526080830190610216565b818103604083015285808551928381520194019060005b8181106108db575050506108d89394506060818403910152610216565b90565b8251865294870194918701916001016108bb565b908160209103126100c957356001600160e01b0319811681036100c95790565b6020908160408183019282815285518094520193019160005b828110610936575050505090565b83516001600160a01b031685529381019392810192600101610928565b6001600160a01b038116036100c957565b908160209103126100c957356108d881610953565b908160209103126100c9573580151581036100c95790565b6060906000356001600160e01b03191663f460590b60e01b81036109f457506109f26109d76109cb6109c33636610742565b810190610964565b6001600160a01b031690565b6109ec6109e4363661075d565b810190610979565b90610a6b565b565b6329e12e0360e21b8103610a225750905061053a6108d8610a13610b4c565b6040519283916020830161090f565b6301ffc9a760e01b14610a3157565b905061053a6108d8610a56610a51610a493636610742565b8101906108ef565b610c52565b60408051911515602083015290928391820190565b907f00000000000000000000000000000000000000000000000000000000000000003303610b315715610ae6576001600160a01b031660008181526020818152604091829020805460ff1916600117905590519182527f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f2491a1565b6001600160a01b031660008181526020818152604091829020805460ff1916905590519182527f3525e22824a8a7df2c9a6029941c824cf95b6447f1e13d5128fd3826d35afe8b91a1565b636d5769be6000526004601cfd5b506040513d6000823e3d90fd5b6040516366a2489f60e01b8152306004820152600080826024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa918215610c45575b8192610ba557505090565b9091503d8083833e610bb7818361015a565b81016020918281830312610c3d578051906001600160401b038211610c41570181601f82011215610c3d57805190610bee8261080e565b94610bfc604051968761015a565b828652848087019360051b83010193841161056057508301905b828210610c24575050505090565b8380918351610c3281610953565b815201910190610c16565b8380fd5b8480fd5b610c4d610b3f565b610b9a565b63ffffffff60e01b16630b9de3bf60e21b8114908115610c85575b8115610c77575090565b6301ffc9a760e01b14919050565b630e08a82560e21b81149150610c6d565b81601f820112156100c9578051610cac816107ad565b92610cba604051948561015a565b818452602082840101116100c9576108d891602080850191016101f3565b81601f820112156100c957805191610cef8361080e565b92610cfd604051948561015a565b808452602092838086019260051b8201019283116100c9578301905b828210610d27575050505090565b81518152908301908301610d19565b610d3e610f6b565b604051628f139360e31b815230600482015290919060009081816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa918215610e44575b80928192828092610da3575b505090919293565b9350935050503d8082843e610db8818461015a565b820160a083820312610e40576020830151906001600160401b0391828111610c3d5781610de6918601610c96565b916040850151818111610c415782610dff918701610c96565b946060810151828111610e3c5783610e18918301610cd8565b9460808201519283116105605750610e31929101610c96565b909291903880610d9b565b8580fd5b5080fd5b610e4c610b3f565b610d8f565b9392610e6091929336916107d7565b602081519101206040519260208401947f0000000000000000000000000000000000000000000000000000000000000000865260018060a01b031660408501526001600160401b038093166060850152608084015260a083015260a0825260c082019082821090821117610ed8575b60405251902090565b610ee0610100565b610ecf565b9190916000926000918280528151601f19830192835182604103946001861115610f13575b50505050505050565b90919293949596975060408301948551966060850151891a90610f51575b8452815260208760808360015afa50525252519038808080808080610f0a565b506001600160ff1b038716865260ff87901c601b01610f31565b6000467f000000000000000000000000000000000000000000000000000000000000000003610fb957507f000000000000000000000000000000000000000000000000000000000000000090565b60405190608051907f000000000000000000000000000000000000000000000000000000000000000081527f00000000000000000000000000000000000000000000000000000000000000006020527f0000000000000000000000000000000000000000000000000000000000000000604052466060523060805260a081209260405260605260805290565b60206004350361105157565b6346d5d8956000526004601cfd5b604560c435013560601c9190604435831515848214151661107e575050565b83631bcf9bb76000526020526040526060526064601cfd5b60c43560a201358091036110a8575050565b6359cb96d160005260205260a435608401356040526060526064601cfd5b9160a0918110156110d657020190565b6110de610834565b020190565b805160208092019160005b8281106110fc575050505090565b8351855293810193928101926001016110ee565b9061111a8161080e565b91611128604051938461015a565b818352601f1991826111398261080e565b0136602086013760005b8181106111a95750505061067161119d9161115c610623565b9360405161117e816111726020820180956110e3565b0384810183528261015a565b5190206040805160208101968752908101919091529283906060820190565b0390810183528261015a565b806111bf6111ba60019385876110c6565b6111d0565b6111c98288610865565b5201611143565b6111d8610563565b602081519101209080359060068210156100c9576020810135906111fb82610953565b606060808201359161120c83610953565b6040519460208601968752604086015260018060a01b038094168286015260408101356080860152013560a08401521660c082015260c081526106718161013f56fea2646970667358221220021581f3f24b29ee89b9bf9c0ca61aa5f8a85650b06533d4fdcf5745a8b9828364736f6c63430008110033a2646970667358221220673d02a1aebe6b5a5b25d608ec382b42bfeb749ee00637cb1ca3ada848e5fe5c64736f6c63430008110033

Verified Source Code Full Match

Compiler: v0.8.17+commit.8df45f5f EVM: london Optimization: Yes (200 runs)
SignedZoneV16.sol 779 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {ZoneParameters, Schema, ReceivedItem} from "../lib/ConsiderationStructs.sol";

import {ZoneInterfaceV16} from "../interfaces/ZoneInterfaceV16.sol";

import {SignedZoneEventsAndErrors} from "./interfaces/SignedZoneEventsAndErrors.sol";

import {SIP5Interface} from "./interfaces/SIP5Interface.sol";

import {SignedZoneControllerInterface} from "./interfaces/SignedZoneControllerInterface.sol";

import "./lib/SignedZoneConstants.sol";

/**
 * @title  SignedZoneV16
 * @author ryanio, BCLeFevre
 * @custom:modifiedby Tony Snark
 * @notice SignedZoneV16 is an implementation of SIP-7 that requires orders
 *         to be signed by an approved signer.
 *         https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md
 *
 *         Modification:
 *         Removes support for SIP7 sub-standard 1.
 *         Adds support for SIP7 sub-standard 3.
 */
contract SignedZoneV16 is SignedZoneEventsAndErrors, ZoneInterfaceV16, SIP5Interface {
  /// @dev The zone's controller that is set during deployment.
  address private immutable _controller;

  /// @dev The authorized signers, and if they are active
  mapping(address => bool) private _signers;

  /// @dev The EIP-712 digest parameters.
  bytes32 internal immutable _NAME_HASH = keccak256(bytes("SignedZone"));
  bytes32 internal immutable _VERSION_HASH = keccak256(bytes("1.0.0"));
  // prettier-ignore
  bytes32 internal immutable _EIP_712_DOMAIN_TYPEHASH = keccak256(
          abi.encodePacked(
            "EIP712Domain(",
                "string name,",
                "string version,",
                "uint256 chainId,",
                "address verifyingContract",
            ")"
          )
        );
  // prettier-ignore
  bytes32 internal immutable _SIGNED_ORDER_TYPEHASH = keccak256(
          abi.encodePacked(
            "SignedOrder(",
                "address fulfiller,",
                "uint64 expiration,",
                "bytes32 orderHash,",
                "bytes context",
            ")"
          )
        );

  bytes public constant CONSIDERATION_BYTES =
    // prettier-ignore
    abi.encodePacked(
              "Consideration(",
                  "ReceivedItem[] consideration",
              ")"
        );

  bytes public constant RECEIVED_ITEM_BYTES =
    // prettier-ignore
    abi.encodePacked(
              "ReceivedItem(",
                  "uint8 itemType,",
                  "address token,",
                  "uint256 identifier,",
                  "uint256 amount,",
                  "address recipient",
              ")"
        );

  bytes32 public constant RECEIVED_ITEM_HASHTYPE = keccak256(RECEIVED_ITEM_BYTES);

  bytes32 public constant CONSIDERATION_HASHTYPE =
    keccak256(abi.encodePacked(CONSIDERATION_BYTES, RECEIVED_ITEM_BYTES));

  uint256 internal immutable _CHAIN_ID = block.chainid;
  bytes32 internal immutable _DOMAIN_SEPARATOR;

  /**
   * @notice Constructor to deploy the contract.
   */
  constructor() {
    // Set the deployer as the controller.
    _controller = msg.sender;

    // Derive and set the domain separator.
    _DOMAIN_SEPARATOR = _deriveDomainSeparator();

    // Emit an event to signal a SIP-5 contract has been deployed.
    emit SeaportCompatibleContractDeployed();
  }

  /**
   * @dev Authorizes an order before any token fulfillments from any order have been executed by Seaport.
   *
   * @return authorizedOrderMagicValue The magic value that indicates a valid
   *                              order.
   */
  function authorizeOrder(
    ZoneParameters calldata
  ) public view virtual override returns (bytes4 authorizedOrderMagicValue) {
    return this.authorizeOrder.selector;
  }

  /**
   * @notice Check if a given order including extraData is currently valid.
   *
   * @dev This function is called by Seaport whenever any extraData is
   *      provided by the caller.
   *
   * @return validOrderMagicValue A magic value indicating if the order is
   *                              currently valid.
   */
  function validateOrder(
    ZoneParameters calldata zoneParameters
  ) public view virtual override returns (bytes4 validOrderMagicValue) {
    // Check Zone parameters validity.
    _assertValidZoneParameters();

    // Put the extraData and orderHash on the stack for cheaper access.
    bytes calldata extraData = zoneParameters.extraData;
    bytes32 orderHash = zoneParameters.orderHash;
    uint256 considerationLength;
    // Declare a variable to hold the expiration.
    uint64 expiration;

    // Validate the extraData.
    assembly {
      // Get the length of the extraData.
      let extraDataPtr := add(0x24, calldataload(Zone_extraData_cdPtr))
      let extraDataLength := calldataload(extraDataPtr)

      if iszero(eq(extraDataLength, InvalidExtraDataLength_epected_length)) {
        // Store left-padded selector with push4, mem[28:32] = selector
        mstore(0, InvalidExtraDataLength_error_selector)
        mstore(InvalidExtraDataLength_error_orderHash_ptr, orderHash)
        // revert(abi.encodeWithSignature(
        //   "InvalidExtraDataLength(bytes32)", orderHash)
        // )
        revert(0x1c, InvalidExtraDataLength_error_length)
      }

      // extraData bytes 0-1: SIP-6 version byte (MUST be 0x00)
      let versionByte := shr(248, calldataload(add(extraDataPtr, 0x20)))

      if iszero(eq(versionByte, 0x00)) {
        // Store left-padded selector with push4, mem[28:32] = selector
        mstore(0, InvalidSIP6Version_error_selector)
        mstore(InvalidSIP6Version_error_orderHash_ptr, orderHash)
        // revert(abi.encodeWithSignature(
        //   "InvalidSIP6Version(bytes32)", orderHash)
        // )
        revert(0x1c, InvalidSIP6Version_error_length)
      }

      // extraData bytes 93-94: Substandard #1 (MUST be 0x00)
      let subStandardVersionByte := shr(
        248,
        calldataload(add(extraDataPtr, ExtraData_substandard_version_byte_offset))
      )

      if iszero(eq(subStandardVersionByte, 0x00)) {
        // Store left-padded selector with push4, mem[28:32] = selector
        mstore(0, InvalidSubstandardVersion_error_selector)
        mstore(InvalidSubstandardVersion_error_orderHash_ptr, orderHash)
        // revert(abi.encodeWithSignature(
        //   "InvalidSubstandardVersion(bytes32)", orderHash)
        // )
        revert(0x1c, InvalidSubstandardVersion_error_length)
      }

      // extraData bytes 21-29: expiration timestamp (uint64)
      expiration := shr(192, calldataload(add(extraDataPtr, ExtraData_expiration_offset)))
      // Revert if expired.
      if lt(expiration, timestamp()) {
        // Store left-padded selector with push4, mem[28:32] = selector
        mstore(0, SignatureExpired_error_selector)
        mstore(SignatureExpired_error_expiration_ptr, expiration)
        mstore(SignatureExpired_error_orderHash_ptr, orderHash)
        // revert(abi.encodeWithSignature(
        //   "SignatureExpired(uint256, bytes32)", expiration orderHash)
        // )
        revert(0x1c, SignatureExpired_error_length)
      }

      // // Get the length of the consideration array.
      considerationLength := calldataload(add(0x24, calldataload(Zone_consideration_head_cdPtr)))
    }

    // extraData bytes 29-93: signature
    // (strictly requires 64 byte compact sig, EIP-2098)
    bytes calldata signature = extraData[29:93];

    // extraData bytes 93-end: context (optional, variable length)
    bytes calldata context = extraData[93:];

    // Check the validity of the Substandard #1 extraData and get the
    // expected fulfiller address.
    address expectedFulfiller = _getExpectedFulfiller(orderHash);

    // Check the validity of the Substandard #1 extraData and get the
    // expected fulfiller address.
    if (considerationLength > 0) {
      _assertValidSubstandard(_deriveConsiderationHash(zoneParameters.consideration), orderHash);
    }

    // Derive the signedOrder hash.
    bytes32 signedOrderHash = _deriveSignedOrderHash(
      expectedFulfiller,
      expiration,
      orderHash,
      context
    );

    // Derive the EIP-712 digest using the domain separator and signedOrder
    // hash.
    bytes32 digest = _deriveEIP712Digest(_domainSeparator(), signedOrderHash);

    // Recover the signer address from the digest and signature.
    address recoveredSigner = _recoverSigner(digest, signature);

    // Revert if the signer is not active.
    if (!_signers[recoveredSigner]) {
      revert SignerNotActive(recoveredSigner, orderHash);
    }
    // Return the selector of validateOrder as the magic value.
    validOrderMagicValue = ZoneInterfaceV16.validateOrder.selector;
  }

  /**
   * @dev Returns Seaport metadata for this contract, returning the
   *      contract name and supported schemas.
   *
   * @return name The contract name
   * @return schemas  The supported SIPs
   */
  function getSeaportMetadata()
    external
    view
    override(SIP5Interface, ZoneInterfaceV16)
    returns (string memory name, Schema[] memory schemas)
  {
    // Return the supported SIPs.
    schemas = new Schema[](1);
    schemas[0].id = 7;

    // Get the SIP-7 information.
    (
      bytes32 domainSeparator,
      string memory zoneName,
      string memory apiEndpoint,
      uint256[] memory substandards,
      string memory documentationURI
    ) = _sip7Information();

    // Return the zone name.
    name = zoneName;

    // Encode the SIP-7 information.
    schemas[0].metadata = abi.encode(domainSeparator, apiEndpoint, substandards, documentationURI);
  }

  /**
   * @notice The fallback function is used as a dispatcher for the
   *         `updateSigner`, `getActiveSigners` and `supportsInterface`
   *         functions.
   */
  // prettier-ignore
  fallback(bytes calldata) external payable returns (bytes memory output) {
        // Get the function selector.
        bytes4 selector = msg.sig;

        if (selector == 0xf460590b) {
            // updateSigner(address,bool)

            // Get the signer, and active status.
            address signer = abi.decode(msg.data[4:], (address));
            bool active = abi.decode(msg.data[36:], (bool));

            // Call to update the signer.
            _updateSigner(signer, active);
        } else if (selector == 0xa784b80c) {
            // getActiveSigners()

            // Call the internal function to get the active signers.
            return abi.encode(_getActiveSigners());
        } else if (selector == 0x01ffc9a7) {
            // supportsInterface(bytes4)

            // Get the interface ID.
            bytes4 interfaceId = abi.decode(msg.data[4:], (bytes4));

            // Call the internal function to determine if the interface is
            // supported.
            return abi.encode(_supportsInterface(interfaceId));
        }
    }

  /**
   * @notice Add or remove a signer to the zone.
   *         Only the controller can call this function.
   *
   * @param signer The signer address to add or remove.
   */
  function _updateSigner(address signer, bool active) internal {
    // Only the controller can call this function.
    _assertCallerIsController();
    // Add or remove the signer.
    active ? _addSigner(signer) : _removeSigner(signer);
  }

  /**
   * @notice Add a new signer to the zone.
   *         Only the controller or an active signer can call this function.
   *
   * @param signer The new signer address to add.
   */
  function _addSigner(address signer) internal {
    // Set the signer info.
    _signers[signer] = true;
    // Emit an event that the signer was added.
    emit SignerAdded(signer);
  }

  /**
   * @notice Remove an active signer from the zone.
   *         Only the controller or an active signer can call this function.
   *
   * @param signer The signer address to remove.
   */
  function _removeSigner(address signer) internal {
    // Set the signer's active status to false.
    _signers[signer] = false;

    // Emit an event that the signer was removed.
    emit SignerRemoved(signer);
  }

  /**
   * @notice Returns the active signers for the zone.
   *
   * @return signers The active signers.
   */
  function _getActiveSigners() internal view returns (address[] memory signers) {
    // Return the active signers for the zone by calling the controller.
    signers = SignedZoneControllerInterface(_controller).getActiveSigners(address(this));
  }

  /**
   * @notice Returns whether the interface is supported.
   *
   * @param interfaceId The interface id to check against.
   */
  function _supportsInterface(bytes4 interfaceId) internal pure returns (bool supportsInterface) {
    // Determine if the interface is supported.
    supportsInterface =
      interfaceId == type(SIP5Interface).interfaceId || // SIP-5
      interfaceId == type(ZoneInterfaceV16).interfaceId || // ZoneInterface
      interfaceId == 0x01ffc9a7; // ERC-165
  }

  /**
   * @notice Internal call to return the signing information, substandards,
   *         and documentation about the zone.
   *
   * @return domainSeparator  The domain separator used for signing.
   * @return zoneName         The zone name.
   * @return apiEndpoint      The API endpoint for the zone.
   * @return substandards     The substandards supported by the zone.
   * @return documentationURI The documentation URI for the zone.
   */
  function _sip7Information()
    internal
    view
    returns (
      bytes32 domainSeparator,
      string memory zoneName,
      string memory apiEndpoint,
      uint256[] memory substandards,
      string memory documentationURI
    )
  {
    // Return the SIP-7 information.
    domainSeparator = _domainSeparator();

    // Get the SIP-7 information from the controller.
    (, zoneName, apiEndpoint, substandards, documentationURI) = SignedZoneControllerInterface(
      _controller
    ).getAdditionalZoneInformation(address(this));
  }

  /**
   * @dev Derive the signedOrder hash from the orderHash and expiration.
   *
   * @param fulfiller  The expected fulfiller address.
   * @param expiration The signature expiration timestamp.
   * @param orderHash  The order hash.
   * @param context    The optional variable-length context.
   *
   * @return signedOrderHash The signedOrder hash.
   *
   */
  function _deriveSignedOrderHash(
    address fulfiller,
    uint64 expiration,
    bytes32 orderHash,
    bytes calldata context
  ) internal view returns (bytes32 signedOrderHash) {
    // Derive the signed order hash.
    signedOrderHash = keccak256(
      abi.encode(_SIGNED_ORDER_TYPEHASH, fulfiller, expiration, orderHash, keccak256(context))
    );
  }

  /**
   * @dev Internal view function to return the signer of a signature.
   *
   * @param digest    The digest to verify the signature against.
   * @param signature A signature from the signer indicating that the order
   *                  has been approved.
   *
   * @return recoveredSigner The recovered signer.
   */
  function _recoverSigner(
    bytes32 digest,
    bytes memory signature
  ) internal view returns (address recoveredSigner) {
    // Utilize assembly to perform optimized signature verification check.
    assembly {
      // Ensure that first word of scratch space is empty.
      mstore(0, 0)

      // Declare value for v signature parameter.
      let v

      // Get the length of the signature.
      let signatureLength := mload(signature)

      // Get the pointer to the value preceding the signature length.
      // This will be used for temporary memory overrides - either the
      // signature head for isValidSignature or the digest for ecrecover.
      let wordBeforeSignaturePtr := sub(signature, OneWord)

      // Cache the current value behind the signature to restore it later.
      let cachedWordBeforeSignature := mload(wordBeforeSignaturePtr)

      // Declare lenDiff + recoveredSigner scope to manage stack pressure.
      {
        // Take the difference between the max ECDSA signature length
        // and the actual signature length. Overflow desired for any
        // values > 65. If the diff is not 0 or 1, it is not a valid
        // ECDSA signature - move on to EIP1271 check.
        let lenDiff := sub(ECDSA_MaxLength, signatureLength)

        // If diff is 0 or 1, it may be an ECDSA signature.
        // Try to recover signer.
        if iszero(gt(lenDiff, 1)) {
          // Read the signature `s` value.
          let originalSignatureS := mload(add(signature, ECDSA_signature_s_offset))

          // Read the first byte of the word after `s`. If the
          // signature is 65 bytes, this will be the real `v` value.
          // If not, it will need to be modified - doing it this way
          // saves an extra condition.
          v := byte(0, mload(add(signature, ECDSA_signature_v_offset)))

          // If lenDiff is 1, parse 64-byte signature as ECDSA.
          if lenDiff {
            // Extract yParity from highest bit of vs and add 27 to
            // get v.
            v := add(shr(MaxUint8, originalSignatureS), Signature_lower_v)

            // Extract canonical s from vs, all but the highest bit.
            // Temporarily overwrite the original `s` value in the
            // signature.
            mstore(
              add(signature, ECDSA_signature_s_offset),
              and(originalSignatureS, EIP2098_allButHighestBitMask)
            )
          }
          // Temporarily overwrite the signature length with `v` to
          // conform to the expected input for ecrecover.
          mstore(signature, v)

          // Temporarily overwrite the word before the length with
          // `digest` to conform to the expected input for ecrecover.
          mstore(wordBeforeSignaturePtr, digest)

          // Attempt to recover the signer for the given signature. Do
          // not check the call status as ecrecover will return a null
          // address if the signature is invalid.
          pop(
            staticcall(
              gas(),
              Ecrecover_precompile, // Call ecrecover precompile.
              wordBeforeSignaturePtr, // Use data memory location.
              Ecrecover_args_size, // Size of digest, v, r, and s.
              0, // Write result to scratch space.
              OneWord // Provide size of returned result.
            )
          )

          // Restore cached word before signature.
          mstore(wordBeforeSignaturePtr, cachedWordBeforeSignature)

          // Restore cached signature length.
          mstore(signature, signatureLength)

          // Restore cached signature `s` value.
          mstore(add(signature, ECDSA_signature_s_offset), originalSignatureS)

          // Read the recovered signer from the buffer given as return
          // space for ecrecover.
          recoveredSigner := mload(0)
        }
      }

      // Restore the cached values overwritten by selector, digest and
      // signature head.
      mstore(wordBeforeSignaturePtr, cachedWordBeforeSignature)
    }
  }

  /**
   * @dev Internal view function to get the EIP-712 domain separator. If the
   *      chainId matches the chainId set on deployment, the cached domain
   *      separator will be returned; otherwise, it will be derived from
   *      scratch.
   *
   * @return The domain separator.
   */
  function _domainSeparator() internal view returns (bytes32) {
    // prettier-ignore
    return block.chainid == _CHAIN_ID
            ? _DOMAIN_SEPARATOR
            : _deriveDomainSeparator();
  }

  /**
   * @dev Internal view function to derive the EIP-712 domain separator.
   *
   * @return domainSeparator The derived domain separator.
   */
  function _deriveDomainSeparator() internal view returns (bytes32 domainSeparator) {
    bytes32 typehash = _EIP_712_DOMAIN_TYPEHASH;
    bytes32 nameHash = _NAME_HASH;
    bytes32 versionHash = _VERSION_HASH;

    // Leverage scratch space and other memory to perform an efficient hash.
    assembly {
      // Retrieve the free memory pointer; it will be replaced afterwards.
      let freeMemoryPointer := mload(FreeMemoryPointerSlot)

      // Retrieve value at 0x80; it will also be replaced afterwards.
      let slot0x80 := mload(Slot0x80)

      // Place typehash, name hash, and version hash at start of memory.
      mstore(0, typehash)
      mstore(OneWord, nameHash)
      mstore(TwoWords, versionHash)

      // Place chainId in the next memory location.
      mstore(ThreeWords, chainid())

      // Place the address of this contract in the next memory location.
      mstore(FourWords, address())

      // Hash relevant region of memory to derive the domain separator.
      domainSeparator := keccak256(0, FiveWords)

      // Restore the free memory pointer.
      mstore(FreeMemoryPointerSlot, freeMemoryPointer)

      // Restore the zero slot to zero.
      mstore(ZeroSlot, 0)

      // Restore the value at 0x80.
      mstore(Slot0x80, slot0x80)
    }
  }

  /**
   * @dev Internal pure function to efficiently derive an digest to sign for
   *      an order in accordance with EIP-712.
   *
   * @param domainSeparator The domain separator.
   * @param signedOrderHash The signedOrder hash.
   *
   * @return digest The digest hash.
   */
  function _deriveEIP712Digest(
    bytes32 domainSeparator,
    bytes32 signedOrderHash
  ) internal pure returns (bytes32 digest) {
    // Leverage scratch space to perform an efficient hash.
    assembly {
      // Place the EIP-712 prefix at the start of scratch space.
      mstore(0, EIP_712_PREFIX)

      // Place the domain separator in the next region of scratch space.
      mstore(EIP712_DomainSeparator_offset, domainSeparator)

      // Place the signed order hash in scratch space, spilling into the
      // first two bytes of the free memory pointer — this should never be
      // set as memory cannot be expanded to that size, and will be
      // zeroed out after the hash is performed.
      mstore(EIP712_SignedOrderHash_offset, signedOrderHash)

      // Hash the relevant region
      digest := keccak256(0, EIP712_DigestPayload_size)

      // Clear out the dirtied bits in the memory pointer.
      mstore(EIP712_SignedOrderHash_offset, 0)
    }
  }

  /**
   * @dev Private view function to revert if the caller is not the
   *      controller.
   */
  function _assertCallerIsController() internal view {
    // Get the controller address to use in the assembly block.
    address controller = _controller;

    assembly {
      // Revert if the caller is not the controller.
      if iszero(eq(caller(), controller)) {
        // Store left-padded selector with push4, mem[28:32] = selector
        mstore(0, InvalidController_error_selector)
        // revert(abi.encodeWithSignature(
        //   "InvalidController()")
        // )
        revert(0x1c, InvalidController_error_length)
      }
    }
  }

  /**
   * @dev Internal pure function to validate calldata offsets for the
   *      dyanamic type in ZoneParameters. This ensures that functions using
   *      the calldata object normally will be using the same data as the
   *      assembly functions and that values that are bound to a given range
   *      are within that range.
   */
  function _assertValidZoneParameters() internal pure {
    // Utilize assembly in order to read offset data directly from calldata.
    assembly {
      /*
       * Checks:
       * 1. Zone parameters struct offset == 0x20
       */

      // Zone parameters at calldata 0x04 must have offset of 0x20.
      if iszero(eq(calldataload(Zone_parameters_cdPtr), Zone_parameters_ptr)) {
        // Store left-padded selector with push4 (reduces bytecode), mem[28:32] = selector
        mstore(0, InvalidZoneParameterEncoding_error_selector)
        // revert(abi.encodeWithSignature("InvalidZoneParameterEncoding()"))
        revert(0x1c, InvalidZoneParameterEncoding_error_length)
      }
    }
  }

  /**
   * @dev Internal pure function to ensure that the context argument for the
   *      supplied extra data follows the substandard #1 format. Returns the
   *      expected fulfiller of the order for deriving the signed order hash.
   *
   * @param orderHash The order hash.
   *
   * @return expectedFulfiller The expected fulfiller of the order.
   */
  function _getExpectedFulfiller(
    bytes32 orderHash
  ) internal pure returns (address expectedFulfiller) {
    // Revert if the expected fulfiller is not the zero address and does
    // not match the actual fulfiller
    assembly {
      // Get the actual fulfiller.
      let actualFulfiller := calldataload(Zone_parameters_fulfiller_cdPtr)
      let extraDataPtr := calldataload(Zone_extraData_cdPtr)

      // Get the expected fulfiller.
      expectedFulfiller := shr(96, calldataload(add(expectedFulfiller_offset, extraDataPtr)))

      // Revert if expected fulfiller is not the zero address and does
      // not match the actual fulfiller.
      if and(iszero(iszero(expectedFulfiller)), iszero(eq(expectedFulfiller, actualFulfiller))) {
        // Store left-padded selector with push4, mem[28:32] = selector
        mstore(0, InvalidFulfiller_error_selector)
        mstore(InvalidFulfiller_error_expectedFulfiller_ptr, expectedFulfiller)
        mstore(InvalidFulfiller_error_actualFulfiller_ptr, actualFulfiller)
        mstore(InvalidFulfiller_error_orderHash_ptr, orderHash)
        // revert(abi.encodeWithSignature(
        //   "InvalidFulfiller(address,address,bytes32)", expectedFulfiller, actualFulfiller, orderHash)
        // )
        revert(0x1c, InvalidFulfiller_error_length)
      }
    }
  }

  /**
   * @dev Internal pure function to ensure that the context argument for the
   *      supplied extra data follows the substandard #1 format. Returns the
   *      expected fulfiller of the order for deriving the signed order hash.
   *
   */
  function _assertValidSubstandard(bytes32 considerationHash, bytes32 orderHash) internal pure {
    // identifier does not match the actual consideration.
    assembly {
      let extraDataPtr := calldataload(Zone_extraData_cdPtr)
      let considerationPtr := calldataload(Zone_consideration_head_cdPtr)
      // Get the actual consideration.
      let actualConsideration := calldataload(add(actualConsideration_offset, considerationPtr))

      // Get the expected consideration.
      let expectedConsiderationHash := calldataload(
        add(expectedConsideration_offset, extraDataPtr) //TODO rename
      )
      // Revert if expected consideration item does not match the actual
      // consideration item.
      if iszero(eq(considerationHash, expectedConsiderationHash)) {
        // Store left-padded selector with push4, mem[28:32] = selector
        mstore(0, InvalidConsideration_error_selector)
        mstore(InvalidConsideration_error_expectedConsideration_ptr, expectedConsiderationHash)
        mstore(InvalidConsideration_error_actualConsideration_ptr, actualConsideration)
        mstore(InvalidConsideration_error_orderHash_ptr, orderHash)
        // revert(abi.encodeWithSignature(
        //   "InvalidConsideration(uint256,uint256,bytes32)", expectedConsideration, actualConsideration, orderHash)
        // )
        revert(0x1c, InvalidConsideration_error_length)
      }
    }
  }

  /// @dev Calculates consideration hash
  function _deriveConsiderationHash(
    ReceivedItem[] calldata consideration
  ) internal pure returns (bytes32) {
    uint256 numberOfItems = consideration.length;
    bytes32[] memory considerationHashes = new bytes32[](numberOfItems);
    for (uint256 i; i < numberOfItems; ) {
      considerationHashes[i] = _deriveReceivedItemHash(consideration[i]);
      unchecked {
        ++i;
      }
    }
    return
      keccak256(
        abi.encode(CONSIDERATION_HASHTYPE, keccak256(abi.encodePacked(considerationHashes)))
      );
  }

  /// @dev Calculates consideration item hash
  function _deriveReceivedItemHash(
    ReceivedItem calldata receivedItem
  ) internal pure returns (bytes32) {
    return
      keccak256(
        abi.encode(
          RECEIVED_ITEM_HASHTYPE,
          receivedItem.itemType,
          receivedItem.token,
          receivedItem.identifier,
          receivedItem.amount,
          receivedItem.recipient
        )
      );
  }
}
ConsiderationEnums.sol 146 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

// prettier-ignore
enum OrderType {
    // 0: no partial fills, anyone can execute
    FULL_OPEN,

    // 1: partial fills supported, anyone can execute
    PARTIAL_OPEN,

    // 2: no partial fills, only offerer or zone can execute
    FULL_RESTRICTED,

    // 3: partial fills supported, only offerer or zone can execute
    PARTIAL_RESTRICTED,

    // 4: contract order type
    CONTRACT
}

// prettier-ignore
enum BasicOrderType {
    // 0: no partial fills, anyone can execute
    ETH_TO_ERC721_FULL_OPEN,

    // 1: partial fills supported, anyone can execute
    ETH_TO_ERC721_PARTIAL_OPEN,

    // 2: no partial fills, only offerer or zone can execute
    ETH_TO_ERC721_FULL_RESTRICTED,

    // 3: partial fills supported, only offerer or zone can execute
    ETH_TO_ERC721_PARTIAL_RESTRICTED,

    // 4: no partial fills, anyone can execute
    ETH_TO_ERC1155_FULL_OPEN,

    // 5: partial fills supported, anyone can execute
    ETH_TO_ERC1155_PARTIAL_OPEN,

    // 6: no partial fills, only offerer or zone can execute
    ETH_TO_ERC1155_FULL_RESTRICTED,

    // 7: partial fills supported, only offerer or zone can execute
    ETH_TO_ERC1155_PARTIAL_RESTRICTED,

    // 8: no partial fills, anyone can execute
    ERC20_TO_ERC721_FULL_OPEN,

    // 9: partial fills supported, anyone can execute
    ERC20_TO_ERC721_PARTIAL_OPEN,

    // 10: no partial fills, only offerer or zone can execute
    ERC20_TO_ERC721_FULL_RESTRICTED,

    // 11: partial fills supported, only offerer or zone can execute
    ERC20_TO_ERC721_PARTIAL_RESTRICTED,

    // 12: no partial fills, anyone can execute
    ERC20_TO_ERC1155_FULL_OPEN,

    // 13: partial fills supported, anyone can execute
    ERC20_TO_ERC1155_PARTIAL_OPEN,

    // 14: no partial fills, only offerer or zone can execute
    ERC20_TO_ERC1155_FULL_RESTRICTED,

    // 15: partial fills supported, only offerer or zone can execute
    ERC20_TO_ERC1155_PARTIAL_RESTRICTED,

    // 16: no partial fills, anyone can execute
    ERC721_TO_ERC20_FULL_OPEN,

    // 17: partial fills supported, anyone can execute
    ERC721_TO_ERC20_PARTIAL_OPEN,

    // 18: no partial fills, only offerer or zone can execute
    ERC721_TO_ERC20_FULL_RESTRICTED,

    // 19: partial fills supported, only offerer or zone can execute
    ERC721_TO_ERC20_PARTIAL_RESTRICTED,

    // 20: no partial fills, anyone can execute
    ERC1155_TO_ERC20_FULL_OPEN,

    // 21: partial fills supported, anyone can execute
    ERC1155_TO_ERC20_PARTIAL_OPEN,

    // 22: no partial fills, only offerer or zone can execute
    ERC1155_TO_ERC20_FULL_RESTRICTED,

    // 23: partial fills supported, only offerer or zone can execute
    ERC1155_TO_ERC20_PARTIAL_RESTRICTED
}

// prettier-ignore
enum BasicOrderRouteType {
    // 0: provide Ether (or other native token) to receive offered ERC721 item.
    ETH_TO_ERC721,

    // 1: provide Ether (or other native token) to receive offered ERC1155 item.
    ETH_TO_ERC1155,

    // 2: provide ERC20 item to receive offered ERC721 item.
    ERC20_TO_ERC721,

    // 3: provide ERC20 item to receive offered ERC1155 item.
    ERC20_TO_ERC1155,

    // 4: provide ERC721 item to receive offered ERC20 item.
    ERC721_TO_ERC20,

    // 5: provide ERC1155 item to receive offered ERC20 item.
    ERC1155_TO_ERC20
}

// prettier-ignore
enum ItemType {
    // 0: ETH on mainnet, MATIC on polygon, etc.
    NATIVE,

    // 1: ERC20 items (ERC777 and ERC20 analogues could also technically work)
    ERC20,

    // 2: ERC721 items
    ERC721,

    // 3: ERC1155 items
    ERC1155,

    // 4: ERC721 items where a number of tokenIds are supported
    ERC721_WITH_CRITERIA,

    // 5: ERC1155 items where a number of ids are supported
    ERC1155_WITH_CRITERIA
}

// prettier-ignore
enum Side {
    // 0: Items that can be spent
    OFFER,

    // 1: Items that must be received
    CONSIDERATION
}
ConsiderationStructs.sol 266 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import { OrderType, BasicOrderType, ItemType, Side } from "./ConsiderationEnums.sol";

/**
 * @dev An order contains eleven components: an offerer, a zone (or account that
 *      can cancel the order or restrict who can fulfill the order depending on
 *      the type), the order type (specifying partial fill support as well as
 *      restricted order status), the start and end time, a hash that will be
 *      provided to the zone when validating restricted orders, a salt, a key
 *      corresponding to a given conduit, a counter, and an arbitrary number of
 *      offer items that can be spent along with consideration items that must
 *      be received by their respective recipient.
 */
struct OrderComponents {
    address offerer;
    address zone;
    OfferItem[] offer;
    ConsiderationItem[] consideration;
    OrderType orderType;
    uint256 startTime;
    uint256 endTime;
    bytes32 zoneHash;
    uint256 salt;
    bytes32 conduitKey;
    uint256 counter;
}

/**
 * @dev An offer item has five components: an item type (ETH or other native
 *      tokens, ERC20, ERC721, and ERC1155, as well as criteria-based ERC721 and
 *      ERC1155), a token address, a dual-purpose "identifierOrCriteria"
 *      component that will either represent a tokenId or a merkle root
 *      depending on the item type, and a start and end amount that support
 *      increasing or decreasing amounts over the duration of the respective
 *      order.
 */
struct OfferItem {
    ItemType itemType;
    address token;
    uint256 identifierOrCriteria;
    uint256 startAmount;
    uint256 endAmount;
}

/**
 * @dev A consideration item has the same five components as an offer item and
 *      an additional sixth component designating the required recipient of the
 *      item.
 */
struct ConsiderationItem {
    ItemType itemType;
    address token;
    uint256 identifierOrCriteria;
    uint256 startAmount;
    uint256 endAmount;
    address payable recipient;
}

/**
 * @dev A spent item is translated from a utilized offer item and has four
 *      components: an item type (ETH or other native tokens, ERC20, ERC721, and
 *      ERC1155), a token address, a tokenId, and an amount.
 */
struct SpentItem {
    ItemType itemType;
    address token;
    uint256 identifier;
    uint256 amount;
}

/**
 * @dev A received item is translated from a utilized consideration item and has
 *      the same four components as a spent item, as well as an additional fifth
 *      component designating the required recipient of the item.
 */
struct ReceivedItem {
    ItemType itemType;
    address token;
    uint256 identifier;
    uint256 amount;
    address payable recipient;
}

/**
 * @dev For basic orders involving ETH / native / ERC20 <=> ERC721 / ERC1155
 *      matching, a group of six functions may be called that only requires a
 *      subset of the usual order arguments. Note the use of a "basicOrderType"
 *      enum; this represents both the usual order type as well as the "route"
 *      of the basic order (a simple derivation function for the basic order
 *      type is `basicOrderType = orderType + (4 * basicOrderRoute)`.)
 */
struct BasicOrderParameters {
    // calldata offset
    address considerationToken; // 0x24
    uint256 considerationIdentifier; // 0x44
    uint256 considerationAmount; // 0x64
    address payable offerer; // 0x84
    address zone; // 0xa4
    address offerToken; // 0xc4
    uint256 offerIdentifier; // 0xe4
    uint256 offerAmount; // 0x104
    BasicOrderType basicOrderType; // 0x124
    uint256 startTime; // 0x144
    uint256 endTime; // 0x164
    bytes32 zoneHash; // 0x184
    uint256 salt; // 0x1a4
    bytes32 offererConduitKey; // 0x1c4
    bytes32 fulfillerConduitKey; // 0x1e4
    uint256 totalOriginalAdditionalRecipients; // 0x204
    AdditionalRecipient[] additionalRecipients; // 0x224
    bytes signature; // 0x244
    // Total length, excluding dynamic array data: 0x264 (580)
}

/**
 * @dev Basic orders can supply any number of additional recipients, with the
 *      implied assumption that they are supplied from the offered ETH (or other
 *      native token) or ERC20 token for the order.
 */
struct AdditionalRecipient {
    uint256 amount;
    address payable recipient;
}

/**
 * @dev The full set of order components, with the exception of the counter,
 *      must be supplied when fulfilling more sophisticated orders or groups of
 *      orders. The total number of original consideration items must also be
 *      supplied, as the caller may specify additional consideration items.
 */
struct OrderParameters {
    address offerer; // 0x00
    address zone; // 0x20
    OfferItem[] offer; // 0x40
    ConsiderationItem[] consideration; // 0x60
    OrderType orderType; // 0x80
    uint256 startTime; // 0xa0
    uint256 endTime; // 0xc0
    bytes32 zoneHash; // 0xe0
    uint256 salt; // 0x100
    bytes32 conduitKey; // 0x120
    uint256 totalOriginalConsiderationItems; // 0x140
    // offer.length                          // 0x160
}

/**
 * @dev Orders require a signature in addition to the other order parameters.
 */
struct Order {
    OrderParameters parameters;
    bytes signature;
}

/**
 * @dev Advanced orders include a numerator (i.e. a fraction to attempt to fill)
 *      and a denominator (the total size of the order) in addition to the
 *      signature and other order parameters. It also supports an optional field
 *      for supplying extra data; this data will be provided to the zone if the
 *      order type is restricted and the zone is not the caller, or will be
 *      provided to the offerer as context for contract order types.
 */
struct AdvancedOrder {
    OrderParameters parameters;
    uint120 numerator;
    uint120 denominator;
    bytes signature;
    bytes extraData;
}

/**
 * @dev Orders can be validated (either explicitly via `validate`, or as a
 *      consequence of a full or partial fill), specifically cancelled (they can
 *      also be cancelled in bulk via incrementing a per-zone counter), and
 *      partially or fully filled (with the fraction filled represented by a
 *      numerator and denominator).
 */
struct OrderStatus {
    bool isValidated;
    bool isCancelled;
    uint120 numerator;
    uint120 denominator;
}

/**
 * @dev A criteria resolver specifies an order, side (offer vs. consideration),
 *      and item index. It then provides a chosen identifier (i.e. tokenId)
 *      alongside a merkle proof demonstrating the identifier meets the required
 *      criteria.
 */
struct CriteriaResolver {
    uint256 orderIndex;
    Side side;
    uint256 index;
    uint256 identifier;
    bytes32[] criteriaProof;
}

/**
 * @dev A fulfillment is applied to a group of orders. It decrements a series of
 *      offer and consideration items, then generates a single execution
 *      element. A given fulfillment can be applied to as many offer and
 *      consideration items as desired, but must contain at least one offer and
 *      at least one consideration that match. The fulfillment must also remain
 *      consistent on all key parameters across all offer items (same offerer,
 *      token, type, tokenId, and conduit preference) as well as across all
 *      consideration items (token, type, tokenId, and recipient).
 */
struct Fulfillment {
    FulfillmentComponent[] offerComponents;
    FulfillmentComponent[] considerationComponents;
}

/**
 * @dev Each fulfillment component contains one index referencing a specific
 *      order and another referencing a specific offer or consideration item.
 */
struct FulfillmentComponent {
    uint256 orderIndex;
    uint256 itemIndex;
}

/**
 * @dev An execution is triggered once all consideration items have been zeroed
 *      out. It sends the item in question from the offerer to the item's
 *      recipient, optionally sourcing approvals from either this contract
 *      directly or from the offerer's chosen conduit if one is specified. An
 *      execution is not provided as an argument, but rather is derived via
 *      orders, criteria resolvers, and fulfillments (where the total number of
 *      executions will be less than or equal to the total number of indicated
 *      fulfillments) and returned as part of `matchOrders`.
 */
struct Execution {
    ReceivedItem item;
    address offerer;
    bytes32 conduitKey;
}

/**
 * @dev Restricted orders are validated post-execution by calling validateOrder
 *      on the zone. This struct provides context about the order fulfillment
 *      and any supplied extraData, as well as all order hashes fulfilled in a
 *      call to a match or fulfillAvailable method.
 */
struct ZoneParameters {
    bytes32 orderHash;
    address fulfiller;
    address offerer;
    SpentItem[] offer;
    ReceivedItem[] consideration;
    bytes extraData;
    bytes32[] orderHashes;
    uint256 startTime;
    uint256 endTime;
    bytes32 zoneHash;
}

/**
 * @dev Zones and contract offerers can communicate which schemas they implement
 *      along with any associated metadata related to each schema.
 */
struct Schema {
    uint256 id;
    bytes metadata;
}
ZoneInterfaceV16.sol 22 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {ZoneParameters, Schema} from "../lib/ConsiderationStructs.sol";

interface ZoneInterfaceV16 {
  function authorizeOrder(
    ZoneParameters calldata zoneParameters
  ) external returns (bytes4 authorizeOrderMagicValue);

  function validateOrder(
    ZoneParameters calldata zoneParameters
  ) external returns (bytes4 validOrderMagicValue);

  function getSeaportMetadata()
    external
    view
    returns (
      string memory name,
      Schema[] memory schemas // map to Seaport Improvement Proposal IDs
    );
}
SignedZoneControllerV16.sol 605 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {SignedZoneV16} from "./SignedZoneV16.sol";

import {SignedZoneInterface} from "./interfaces/SignedZoneInterface.sol";

import {SignedZoneControllerInterface} from "./interfaces/SignedZoneControllerInterface.sol";

import {SignedZoneControllerEventsAndErrors} from "./interfaces/SignedZoneControllerEventsAndErrors.sol";

import "./lib/SignedZoneConstants.sol";

/**
 * @title  SignedZoneControllerV16
 * @author BCLeFevre
 * @notice SignedZoneControllerV16 enables the deploying of SignedZones.
 *         SignedZones are an implementation of SIP-7 that requires orders to
 *         be signed by  an approved signer.
 *         https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md
 */
contract SignedZoneControllerV16 is
  SignedZoneControllerInterface,
  SignedZoneControllerEventsAndErrors
{
  /**
   * @dev The struct for storing signer info.
   */
  struct SignerInfo {
    /// @dev If the signer is currently active.
    bool active;
    /// @dev If the signer has been active before.
    bool previouslyActive;
  }

  // Properties used by the signed zone, stored on the controller.
  struct SignedZoneProperties {
    /// @dev Owner of the signed zone (used for permissioned functions)
    address owner;
    /// @dev Potential owner of the signed zone
    address potentialOwner;
    /// @dev The name for this zone returned in getSeaportMetadata().
    string zoneName;
    /// @dev The API endpoint where orders for this zone can be signed.
    ///      Request and response payloads are defined in SIP-7.
    string apiEndpoint;
    /// @dev The URI to the documentation describing the behavior of the
    ///      contract.
    string documentationURI;
    /// @dev The substandards supported by this zone.
    ///      Substandards are defined in SIP-7.
    uint256[] substandards;
    /// @dev Mapping of signer information keyed by signer Address
    mapping(address => SignerInfo) signers;
    /// @dev List of active signers
    address[] activeSignerList;
  }

  /// @dev Mapping of signed zone properties keyed by the Signed Zone
  ///      address.
  mapping(address => SignedZoneProperties) internal _signedZones;

  /// @dev The EIP-712 digest parameters for the SignedZone.
  bytes32 internal immutable _NAME_HASH = keccak256(bytes("SignedZone"));
  bytes32 internal immutable _VERSION_HASH = keccak256(bytes("1.0"));
  // prettier-ignore
  bytes32 internal immutable _EIP_712_DOMAIN_TYPEHASH = keccak256(
          abi.encodePacked(
            "EIP712Domain(",
                "string name,",
                "string version,",
                "uint256 chainId,",
                "address verifyingContract",
            ")"
          )
        );
  uint256 internal immutable _CHAIN_ID = block.chainid;

  // Set the signed zone creation code as an immutable argument.
  bytes32 internal immutable _SIGNED_ZONE_CREATION_CODE_HASH;

  /**
   * @dev Initialize contract
   */
  constructor() {
    // Derive the signed zone creation code hash and set it as an
    // immutable.
    _SIGNED_ZONE_CREATION_CODE_HASH = keccak256(type(SignedZoneV16).creationCode);
  }

  /**
   * @notice Deploy a SignedZoneV16 to a precomputed address.
   *
   * @param zoneName    The name for the zone returned in
   *                    getSeaportMetadata().
   * @param apiEndpoint The API endpoint where orders for this zone can be
   *                    signed.
   * @param documentationURI The URI to the documentation describing the
   *                         behavior of the contract.
   *                    Request and response payloads are defined in SIP-7.
   * @param salt        The salt to be used to derive the zone address
   * @param initialOwner The initial owner to set for the new zone.
   *
   * @return derivedAddress The derived address for the zone.
   */
  function createZone(
    string memory zoneName,
    string memory apiEndpoint,
    string memory documentationURI,
    address initialOwner,
    bytes32 salt
  ) external override returns (address derivedAddress) {
    // Ensure that an initial owner has been supplied.
    if (initialOwner == address(0)) {
      revert InvalidInitialOwner();
    }

    // Ensure the first 20 bytes of the salt are the same as the msg.sender.
    if ((address(uint160(bytes20(salt))) != msg.sender)) {
      // Revert with an error indicating that the creator is invalid.
      revert InvalidCreator();
    }

    // Derive the SignedZoneV16 address from the deployer, salt and creation
    // code hash.
    derivedAddress = address(
      uint160(
        uint256(
          keccak256(
            abi.encodePacked(bytes1(0xff), address(this), salt, _SIGNED_ZONE_CREATION_CODE_HASH)
          )
        )
      )
    );

    // TODO : Check runtime code hash to ensure that the zone is not already
    // deployed.
    // Revert if a zone is currently deployed to the derived address.
    if (derivedAddress.code.length != 0) {
      revert ZoneAlreadyExists(derivedAddress);
    }

    // Deploy the zone using the supplied salt.
    new SignedZoneV16{salt: salt}();

    // Initialize storage variable referencing signed zone properties.
    SignedZoneProperties storage signedZoneProperties = _signedZones[derivedAddress];

    // Set the supplied intial owner as the owner of the zone.
    signedZoneProperties.owner = initialOwner;
    // Set the zone name.
    signedZoneProperties.zoneName = zoneName;
    // Set the API endpoint.
    signedZoneProperties.apiEndpoint = apiEndpoint;
    // Set the documentation URI.
    signedZoneProperties.documentationURI = documentationURI;
    // Set the substandard.
    signedZoneProperties.substandards = [3];

    // Emit an event signifying that the zone was created.
    emit ZoneCreated(derivedAddress, zoneName, apiEndpoint, documentationURI, salt);

    // Emit an event indicating that zone ownership has been assigned.
    emit OwnershipTransferred(derivedAddress, address(0), initialOwner);
  }

  /**
   * @notice Initiate zone ownership transfer by assigning a new potential
   *         owner for the given zone. Once set, the new potential owner
   *         may call `acceptOwnership` to claim ownership of the zone.
   *         Only the owner of the zone in question may call this function.
   *
   * @param zone The zone for which to initiate ownership transfer.
   * @param newPotentialOwner The new potential owner of the zone.
   */
  function transferOwnership(address zone, address newPotentialOwner) external override {
    // Ensure the caller is the current owner of the zone in question.
    _assertCallerIsZoneOwner(zone);

    // Ensure the new potential owner is not an invalid address.
    if (newPotentialOwner == address(0)) {
      revert NewPotentialOwnerIsZeroAddress(zone);
    }

    // Ensure the new potential owner is not already set.
    if (newPotentialOwner == _signedZones[zone].potentialOwner) {
      revert NewPotentialOwnerAlreadySet(zone, newPotentialOwner);
    }

    // Emit an event indicating that the potential owner has been updated.
    emit PotentialOwnerUpdated(newPotentialOwner);

    // Set the new potential owner as the potential owner of the zone.
    _signedZones[zone].potentialOwner = newPotentialOwner;
  }

  /**
   * @notice Clear the currently set potential owner, if any, from a zone.
   *         Only the owner of the zone in question may call this function.
   *
   * @param zone The zone for which to cancel ownership transfer.
   */
  function cancelOwnershipTransfer(address zone) external override {
    // Ensure the caller is the current owner of the zone in question.
    _assertCallerIsZoneOwner(zone);

    // Ensure that ownership transfer is currently possible.
    if (_signedZones[zone].potentialOwner == address(0)) {
      revert NoPotentialOwnerCurrentlySet(zone);
    }

    // Emit an event indicating that the potential owner has been cleared.
    emit PotentialOwnerUpdated(address(0));

    // Clear the current new potential owner from the zone.
    _signedZones[zone].potentialOwner = address(0);
  }

  /**
   * @notice Accept ownership of a supplied zone. Only accounts that the
   *         current owner has set as the new potential owner may call this
   *         function.
   *
   * @param zone The zone for which to accept ownership.
   */
  function acceptOwnership(address zone) external override {
    // Ensure that the zone in question exists.
    _assertZoneExists(zone);

    // If caller does not match current potential owner of the zone...
    if (msg.sender != _signedZones[zone].potentialOwner) {
      // Revert, indicating that caller is not current potential owner.
      revert CallerIsNotNewPotentialOwner(zone);
    }

    // Emit an event indicating that the potential owner has been cleared.
    emit PotentialOwnerUpdated(address(0));

    // Clear the current new potential owner from the zone.
    _signedZones[zone].potentialOwner = address(0);

    // Emit an event indicating zone ownership has been transferred.
    emit OwnershipTransferred(zone, _signedZones[zone].owner, msg.sender);

    // Set the caller as the owner of the zone.
    _signedZones[zone].owner = msg.sender;
  }

  /**
   * @notice Retrieve the current owner of a deployed zone.
   *
   * @param zone The zone for which to retrieve the associated owner.
   *
   * @return owner The owner of the supplied zone.
   */
  function ownerOf(address zone) external view override returns (address owner) {
    // Ensure that the zone in question exists.
    _assertZoneExists(zone);

    // Retrieve the current owner of the zone in question.
    owner = _signedZones[zone].owner;
  }

  /**
   * @notice Retrieve the potential owner, if any, for a given zone. The
   *         current owner may set a new potential owner via
   *         `transferOwnership` and that owner may then accept ownership of
   *         the zone in question via `acceptOwnership`.
   *
   * @param zone The zone for which to retrieve the potential owner.
   *
   * @return potentialOwner The potential owner, if any, for the zone.
   */
  function getPotentialOwner(address zone) external view override returns (address potentialOwner) {
    // Ensure that the zone in question exists.
    _assertZoneExists(zone);

    // Retrieve the current potential owner of the zone in question.
    potentialOwner = _signedZones[zone].potentialOwner;
  }

  /**
   * @notice Returns the active signers for the zone.
   *
   * @param zone The zone to return the active signers for.
   *
   * @return signers The active signers.
   */
  function getActiveSigners(
    address zone
  ) external view override returns (address[] memory signers) {
    // Ensure that the zone in question exists.
    _assertZoneExists(zone);

    // Retrieve storage region where the singers for the signedZone are
    // stored.
    SignedZoneProperties storage signedZoneProperties = _signedZones[zone];

    // Return the active signers for the zone.
    signers = signedZoneProperties.activeSignerList;
  }

  /**
   * @notice Update the API endpoint returned by a zone.
   *         Only the owner or an active signer of the supplied zone can call
   *         this function.
   *
   * @param zone     The signed zone to update the API endpoint for.
   * @param newApiEndpoint The new API endpoint.
   */
  function updateAPIEndpoint(address zone, string calldata newApiEndpoint) external override {
    // Ensure the caller is the owner or an active signer of the signed zone.
    _assertCallerIsZoneOwnerOrSigner(zone);

    // Retrieve storage region where the singers for the signedZone are
    // stored.
    SignedZoneProperties storage signedZoneProperties = _signedZones[zone];

    // Update the API endpoint on the signed zone.
    signedZoneProperties.apiEndpoint = newApiEndpoint;
  }

  /**
   * @notice Add or remove a signer from the supplied zone.
   *         Only the owner or an active signer of the supplied zone can call
   *         this function.
   *
   * @param zone     The signed zone to update the signer permissions for.
   * @param signer   The signer to update the permissions for.
   * @param active   Whether the signer should be active or not.
   */
  function updateSigner(address zone, address signer, bool active) external override {
    // Ensure the caller is the owner or an active signer of the signed zone.
    _assertCallerIsZoneOwnerOrSigner(zone);

    // Retrieve storage region where the singers for the signedZone are
    // stored.
    SignedZoneProperties storage signedZoneProperties = _signedZones[zone];

    // Validate signer permissions.
    _assertSignerPermissions(signedZoneProperties, signer, active);

    // Update the signer on the signed zone.
    SignedZoneInterface(zone).updateSigner(signer, active);

    // Update the signer information.
    signedZoneProperties.signers[signer].active = active;
    signedZoneProperties.signers[signer].previouslyActive = true;
    // Add the signer to the list of signers if they are active.
    if (active) {
      signedZoneProperties.activeSignerList.push(signer);
    } else {
      // Remove the signer from the list of signers.
      for (uint256 i = 0; i < signedZoneProperties.activeSignerList.length; ) {
        if (signedZoneProperties.activeSignerList[i] == signer) {
          signedZoneProperties.activeSignerList[i] = signedZoneProperties.activeSignerList[
            signedZoneProperties.activeSignerList.length - 1
          ];
          signedZoneProperties.activeSignerList.pop();
          break;
        }

        unchecked {
          ++i;
        }
      }
    }

    // Emit an event signifying that the signer was updated.
    emit SignerUpdated(zone, signer, active);
  }

  /**
   * @notice Derive the zone address associated with a salt.
   *
   * @param salt  The salt to be used to derive the zone address.
   *
   * @return derivedAddress The derived address of the signed zone.
   */
  function getZone(bytes32 salt) external view override returns (address derivedAddress) {
    // Derive the SignedZoneV16 address from deployer, salt and creation code
    // hash.
    derivedAddress = address(
      uint160(
        uint256(
          keccak256(
            abi.encodePacked(bytes1(0xff), address(this), salt, _SIGNED_ZONE_CREATION_CODE_HASH)
          )
        )
      )
    );
  }

  /**
   * @notice External call to return the signing information, substandards,
   *         and documentation about the zone.
   *
   * @return domainSeparator  The domain separator used for signing.
   * @return zoneName         The name of the zone.
   * @return apiEndpoint      The API endpoint for the zone.
   * @return substandards     The substandards supported by the zone.
   * @return documentationURI The documentation URI for the zone.
   */
  function getAdditionalZoneInformation(
    address zone
  )
    external
    view
    override
    returns (
      bytes32 domainSeparator,
      string memory zoneName,
      string memory apiEndpoint,
      uint256[] memory substandards,
      string memory documentationURI
    )
  {
    // Ensure the zone exists.
    _assertZoneExists(zone);

    // Return the zone's additional information.
    return _additionalZoneInformation(zone);
  }

  /**
   * @notice Internal call to return the signing information, substandards,
   *         and documentation about the zone.
   *
   * @return domainSeparator  The domain separator used for signing.
   * @return zoneName         The name of the zone.
   * @return apiEndpoint      The API endpoint for the zone.
   * @return substandards     The substandards supported by the zone.
   * @return documentationURI The documentation URI for the zone.
   */
  function _additionalZoneInformation(
    address zone
  )
    internal
    view
    returns (
      bytes32 domainSeparator,
      string memory zoneName,
      string memory apiEndpoint,
      uint256[] memory substandards,
      string memory documentationURI
    )
  {
    // Get the zone properties.
    SignedZoneProperties storage signedZoneProperties = _signedZones[zone];

    // Return the SIP-7 information.
    domainSeparator = _domainSeparator(zone);
    zoneName = signedZoneProperties.zoneName;
    apiEndpoint = signedZoneProperties.apiEndpoint;
    substandards = signedZoneProperties.substandards;
    documentationURI = signedZoneProperties.documentationURI;
  }

  /**
   * @dev Internal view function to get the EIP-712 domain separator. If the
   *      chainId matches the chainId set on deployment, the cached domain
   *      separator will be returned; otherwise, it will be derived from
   *      scratch.
   *
   * @return The domain separator.
   */
  function _domainSeparator(address zone) internal view returns (bytes32) {
    // prettier-ignore
    return _deriveDomainSeparator(zone);
  }

  /**
   * @dev Internal view function to derive the EIP-712 domain separator.
   *
   * @return domainSeparator The derived domain separator.
   */
  function _deriveDomainSeparator(address zone) internal view returns (bytes32 domainSeparator) {
    bytes32 typehash = _EIP_712_DOMAIN_TYPEHASH;
    bytes32 nameHash = _NAME_HASH;
    bytes32 versionHash = _VERSION_HASH;

    // Leverage scratch space and other memory to perform an efficient hash.
    assembly {
      // Retrieve the free memory pointer; it will be replaced afterwards.
      let freeMemoryPointer := mload(FreeMemoryPointerSlot)

      // Retrieve value at 0x80; it will also be replaced afterwards.
      let slot0x80 := mload(Slot0x80)

      // Place typehash, name hash, and version hash at start of memory.
      mstore(0, typehash)
      mstore(OneWord, nameHash)
      mstore(TwoWords, versionHash)

      // Place chainId in the next memory location.
      mstore(ThreeWords, chainid())

      // Place the address of the signed zone contract in the next memory location.
      mstore(FourWords, zone)

      // Hash relevant region of memory to derive the domain separator.
      domainSeparator := keccak256(0, FiveWords)

      // Restore the free memory pointer.
      mstore(FreeMemoryPointerSlot, freeMemoryPointer)

      // Restore the zero slot to zero.
      mstore(ZeroSlot, 0)

      // Restore the value at 0x80.
      mstore(Slot0x80, slot0x80)
    }
  }

  /**
   * @dev Private view function to revert if the caller is not the owner of a
   *      given zone.
   *
   * @param zone The zone for which to assert ownership.
   */
  function _assertCallerIsZoneOwner(address zone) private view {
    // Ensure that the zone in question exists.
    _assertZoneExists(zone);

    // If the caller does not match the current owner of the zone...
    if (msg.sender != _signedZones[zone].owner) {
      // Revert, indicating that the caller is not the owner.
      revert CallerIsNotOwner(zone);
    }
  }

  /**
   * @dev Private view function to revert if the caller is not the owner or
   *      an active signer of a given zone.
   *
   * @param zone The zone for which to assert ownership.
   */
  function _assertCallerIsZoneOwnerOrSigner(address zone) private view {
    // Ensure that the zone in question exists.
    _assertZoneExists(zone);

    // Initialize storage variable referencing signed zone properties.
    SignedZoneProperties storage signedZoneProperties = _signedZones[zone];

    // Ensure the caller is the owner or an active signer of the signed zone.
    if (
      msg.sender != _signedZones[zone].owner && !signedZoneProperties.signers[msg.sender].active
    ) {
      // Revert, indicating that the caller is not the owner.
      revert CallerIsNotOwnerOrSigner(zone);
    }
  }

  /**
   * @dev Private view function to revert if a given zone does not exist.
   *
   * @param zone The zone for which to assert existence.
   */
  function _assertZoneExists(address zone) private view {
    // Attempt to retrieve a the owner for the zone in question.
    if (_signedZones[zone].owner == address(0)) {
      // Revert if no ownerwas located.
      revert NoZone();
    }
  }

  /**
   * @dev Private view function to revert if a signer being added to a zone
   *      is the zero address or the signer already exists, or the signer was
   *      previously authorized.  If the signer is being removed, the
   *      function will revert if the signer is not active.
   *
   * @param signedZoneProperties The signed zone properties for the zone.
   * @param signer The signer to add or remove.
   * @param active Whether the signer is being added or removed.
   */
  function _assertSignerPermissions(
    SignedZoneProperties storage signedZoneProperties,
    address signer,
    bool active
  ) private view {
    // If the signer is being added...
    if (active) {
      // Do not allow the zero address to be added as a signer.
      if (signer == address(0)) {
        revert SignerCannotBeZeroAddress();
      }

      // Revert if the signer is already added.
      if (signedZoneProperties.signers[signer].active) {
        revert SignerAlreadyAdded(signer);
      }

      // Revert if the signer was previously authorized.
      if (signedZoneProperties.signers[signer].previouslyActive) {
        revert SignerCannotBeReauthorized(signer);
      }
    } else {
      // Revert if the signer is not active.
      if (!signedZoneProperties.signers[signer].active) {
        revert SignerNotPresent(signer);
      }
    }
  }
}
SignedZoneConstants.sol 171 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/// @dev ECDSA signature offsets.
uint256 constant ECDSA_MaxLength = 65;
uint256 constant ECDSA_signature_s_offset = 0x40;
uint256 constant ECDSA_signature_v_offset = 0x60;

/// @dev Helpers for memory offsets.
uint256 constant OneWord = 0x20;
uint256 constant TwoWords = 0x40;
uint256 constant ThreeWords = 0x60;
uint256 constant FourWords = 0x80;
uint256 constant FiveWords = 0xa0;
uint256 constant Signature_lower_v = 27;
uint256 constant MaxUint8 = 0xff;
bytes32 constant EIP2098_allButHighestBitMask = (
    0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
);
uint256 constant Ecrecover_precompile = 1;
uint256 constant Ecrecover_args_size = 0x80;
uint256 constant FreeMemoryPointerSlot = 0x40;
uint256 constant ZeroSlot = 0x60;
uint256 constant Slot0x80 = 0x80;

/// @dev The EIP-712 digest offsets.
uint256 constant EIP712_DomainSeparator_offset = 0x02;
uint256 constant EIP712_SignedOrderHash_offset = 0x22;
uint256 constant EIP712_DigestPayload_size = 0x42;
uint256 constant EIP_712_PREFIX = (
    0x1901000000000000000000000000000000000000000000000000000000000000
);

/*
 *  error InvalidController()
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 * Revert buffer is memory[0x1c:0x20]
 */
uint256 constant InvalidController_error_selector = 0x6d5769be;
uint256 constant InvalidController_error_length = 0x04;

/*
 *  error InvalidFulfiller(address expectedFulfiller, address actualFulfiller, bytes32 orderHash)
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 *    - 0x20: expectedFulfiller
 *    - 0x40: actualFullfiller
 *    - 0x60: orderHash
 * Revert buffer is memory[0x1c:0x80]
 */
uint256 constant InvalidFulfiller_error_selector = 0x1bcf9bb7;
uint256 constant InvalidFulfiller_error_expectedFulfiller_ptr = 0x20;
uint256 constant InvalidFulfiller_error_actualFulfiller_ptr = 0x40;
uint256 constant InvalidFulfiller_error_orderHash_ptr = 0x60;
uint256 constant InvalidFulfiller_error_length = 0x64;

/*
 *  error InvalidConsideration(uint256 expectedConsideration, uint256 actualConsideration, bytes32 orderHash)
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 *    - 0x20: expectedConsideration
 *    - 0x40: actualConsideration
 *    - 0x60: orderHash
 * Revert buffer is memory[0x1c:0x80]
 */
uint256 constant InvalidConsideration_error_selector = 0x59cb96d1;
uint256 constant InvalidConsideration_error_expectedConsideration_ptr = 0x20;
uint256 constant InvalidConsideration_error_actualConsideration_ptr = 0x40;
uint256 constant InvalidConsideration_error_orderHash_ptr = 0x60;
uint256 constant InvalidConsideration_error_length = 0x64;

/*
 *  error InvalidZoneParameterEncoding()
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 * Revert buffer is memory[0x1c:0x20]
 */
uint256 constant InvalidZoneParameterEncoding_error_selector = 0x46d5d895;
uint256 constant InvalidZoneParameterEncoding_error_length = 0x04;

/*
 * error InvalidExtraDataLength()
 *   - Defined in SignedZoneEventsAndErrors.sol
 * Memory layout:
 *   - 0x00: Left-padded selector (data begins at 0x1c)
 *   - 0x20: orderHash
 * Revert buffer is memory[0x1c:0x40]
 */
uint256 constant InvalidExtraDataLength_error_selector = 0xd232fd2c;
uint256 constant InvalidExtraDataLength_error_orderHash_ptr = 0x20;
uint256 constant InvalidExtraDataLength_error_length = 0x24;
uint256 constant InvalidExtraDataLength_epected_length = 0x7e;

uint256 constant ExtraData_expiration_offset = 0x35;
uint256 constant ExtraData_substandard_version_byte_offset = 0x7d;
/*
 *  error InvalidSIP6Version()
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 *    - 0x20: orderHash
 * Revert buffer is memory[0x1c:0x40]
 */
uint256 constant InvalidSIP6Version_error_selector = 0x64115774;
uint256 constant InvalidSIP6Version_error_orderHash_ptr = 0x20;
uint256 constant InvalidSIP6Version_error_length = 0x24;

/*
 *  error InvalidSubstandardVersion()
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 *    - 0x20: orderHash
 * Revert buffer is memory[0x1c:0x40]
 */
uint256 constant InvalidSubstandardVersion_error_selector = 0x26787999;
uint256 constant InvalidSubstandardVersion_error_orderHash_ptr = 0x20;
uint256 constant InvalidSubstandardVersion_error_length = 0x24;

/*
 *  error InvalidSubstandardSupport()
 *    - Defined in SignedZoneEventsAndErrors.sol
 *  Memory layout:
 *    - 0x00: Left-padded selector (data begins at 0x1c)
 *    - 0x20: reason
 *    - 0x40: substandardVersion
 *    - 0x60: orderHash
 * Revert buffer is memory[0x1c:0xe0]
 */
uint256 constant InvalidSubstandardSupport_error_selector = 0x2be76224;
uint256 constant InvalidSubstandardSupport_error_reason_offset_ptr = 0x20;
uint256 constant InvalidSubstandardSupport_error_substandard_version_ptr = 0x40;
uint256 constant InvalidSubstandardSupport_error_orderHash_ptr = 0x60;
uint256 constant InvalidSubstandardSupport_error_reason_length_ptr = 0x80;
uint256 constant InvalidSubstandardSupport_error_reason_ptr = 0xa0;
uint256 constant InvalidSubstandardSupport_error_reason_2_ptr = 0xc0;
uint256 constant InvalidSubstandardSupport_error_length = 0xc4;

/*
 * error SignatureExpired()
 *   - Defined in SignedZoneEventsAndErrors.sol
 * Memory layout:
 *   - 0x00: Left-padded selector (data begins at 0x1c)
 *   - 0x20: expiration
 *   - 0x40: orderHash
 * Revert buffer is memory[0x1c:0x60]
 */
uint256 constant SignatureExpired_error_selector = 0x16546071;
uint256 constant SignatureExpired_error_expiration_ptr = 0x20;
uint256 constant SignatureExpired_error_orderHash_ptr = 0x40;
uint256 constant SignatureExpired_error_length = 0x44;

// Zone parameter calldata pointers
uint256 constant Zone_parameters_cdPtr = 0x04;
uint256 constant Zone_parameters_fulfiller_cdPtr = 0x44;
uint256 constant Zone_consideration_head_cdPtr = 0xa4;
uint256 constant Zone_extraData_cdPtr = 0xc4;

// Zone parameter memory pointers
uint256 constant Zone_parameters_ptr = 0x20;

// Zone parameter offsets
uint256 constant Zone_parameters_offset = 0x24;
uint256 constant expectedFulfiller_offset = 0x45;
uint256 constant actualConsideration_offset = 0x84;
uint256 constant expectedConsideration_offset = 0xa2;
SIP5Interface.sol 27 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import { Schema } from "../../lib/ConsiderationStructs.sol";

/**
 * @dev SIP-5: Contract Metadata Interface for Seaport Contracts
 *      https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-5.md
 */
interface SIP5Interface {
    /**
     * @dev An event that is emitted when a SIP-5 compatible contract is deployed.
     */
    event SeaportCompatibleContractDeployed();

    /**
     * @dev Returns Seaport metadata for this contract, returning the
     *      contract name and supported schemas.
     *
     * @return name    The contract name
     * @return schemas The supported SIPs
     */
    function getSeaportMetadata()
        external
        view
        returns (string memory name, Schema[] memory schemas);
}
SignedZoneInterface.sol 30 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/**
 * @title  SignedZone
 * @author ryanio, BCLeFevre
 * @notice SignedZone is an implementation of SIP-7 that requires orders
 *         to be signed by an approved signer.
 *         https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md
 *
 */
interface SignedZoneInterface {
    /**
     * @notice Update the active status of a signer.
     *
     * @param signer The signer address to update.
     * @param active The new active status of the signer.
     */
    function updateSigner(address signer, bool active) external;

    /**
     * @notice Returns the active signers for the zone.
     *
     * @return signers The active signers.
     */
    function getActiveSigners()
        external
        view
        returns (address[] memory signers);
}
SignedZoneEventsAndErrors.sol 92 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/**
 * @notice SignedZoneEventsAndErrors contains errors and events
 *         related to zone interaction.
 */
interface SignedZoneEventsAndErrors {
    /**
     * @dev Emit an event when a new signer is added.
     */
    event SignerAdded(address signer);

    /**
     * @dev Emit an event when a signer is removed.
     */
    event SignerRemoved(address signer);

    /**
     * @dev Revert with an error if msg.sender is not the owner
     *      or an active signer.
     */
    error OnlyOwnerOrActiveSigner();

    /**
     * @dev Revert with an error when the signature has expired.
     */
    error SignatureExpired(uint256 expiration, bytes32 orderHash);

    /**
     * @dev Revert with an error when attempting to update the signers of a
     *      the zone from a caller that is not the zone's controller.
     */
    error InvalidController();

    /**
     * @dev Revert with an error if supplied order extraData is an invalid
     *      length.
     */
    error InvalidExtraDataLength(bytes32 orderHash);

    /**
     * @dev Revert with an error if the supplied order extraData does not
     *      support the zone's SIP6 version.
     */
    error InvalidSIP6Version(bytes32 orderHash);

    /**
     * @dev Revert with an error if the supplied order extraData does not
     *      support the zone's substandard requirements.
     */
    error InvalidSubstandardSupport(
        string reason,
        uint256 substandardVersion,
        bytes32 orderHash
    );

    /**
     * @dev Revert with an error if the supplied order extraData does not
     *      support the zone's substandard version.
     */
    error InvalidSubstandardVersion(bytes32 orderHash);

    /**
     * @dev Revert with an error if the fulfiller does not match.
     */
    error InvalidFulfiller(
        address expectedFulfiller,
        address actualFulfiller,
        bytes32 orderHash
    );

    /**
     * @dev Revert with an error if the consideration does not match.
     */
    error InvalidConsideration(
        uint256 expectedConsiderationHash,
        uint256 actualConsiderationHash,
        bytes32 orderHash
    );

    /**
     * @dev Revert with an error if the zone parameter encoding is invalid.
     */
    error InvalidZoneParameterEncoding();

    /**
     * @dev Revert with an error when an order is signed with a signer
     *      that is not active.
     */
    error SignerNotActive(address signer, bytes32 orderHash);
}
SignedZoneControllerInterface.sol 160 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/**
 * @title  SignedZoneControllerInterface
 * @author BCLeFevre
 * @notice SignedZoneControllerInterface enables the deploying of SignedZones.
 *         SignedZones are an implementation of SIP-7 that requires orders
 *         to be signed by an approved signer.
 *         https://github.com/ProjectOpenSea/SIPs/blob/main/SIPS/sip-7.md
 *
 */
interface SignedZoneControllerInterface {
    /**
     * @notice Deploy a SignedZone to a precomputed address.
     *
     * @param zoneName    The name for the zone returned in
     *                    getSeaportMetadata().
     * @param apiEndpoint The API endpoint where orders for this zone can be
     *                    signed.
     * @param documentationURI The URI to the documentation describing the
     *                         behavior of the contract.
     *                    Request and response payloads are defined in SIP-7.
     * @param salt        The salt to be used to derive the zone address
     * @param initialOwner The initial owner to set for the new zone.
     *
     * @return derivedAddress The derived address for the zone.
     */
    function createZone(
        string memory zoneName,
        string memory apiEndpoint,
        string memory documentationURI,
        address initialOwner,
        bytes32 salt
    ) external returns (address derivedAddress);

    /**
     * @notice Returns the active signers for the zone.
     *
     * @param signedZone The signed zone to get the active signers for.
     *
     * @return signers The active signers.
     */
    function getActiveSigners(address signedZone)
        external
        view
        returns (address[] memory signers);

    /**
     * @notice Returns additional information about the zone.
     *
     * @param zone The zone to get the additional information for.
     *
     * @return domainSeparator  The domain separator used for signing.
     * @return zoneName         The name of the zone.
     * @return apiEndpoint      The API endpoint for the zone.
     * @return substandards     The substandards supported by the zone.
     * @return documentationURI The documentation URI for the zone.
     */
    function getAdditionalZoneInformation(address zone)
        external
        view
        returns (
            bytes32 domainSeparator,
            string memory zoneName,
            string memory apiEndpoint,
            uint256[] memory substandards,
            string memory documentationURI
        );

    /**
     * @notice Update the API endpoint returned by the supplied zone.
     *         Only the owner or an active signer can call this function.
     *
     * @param signedZone    The signed zone to update the API endpoint for.
     * @param newApiEndpoint The new API endpoint.
     */
    function updateAPIEndpoint(
        address signedZone,
        string calldata newApiEndpoint
    ) external;

    /**
     * @notice Update the signer for a given signed zone.
     *
     * @param signedZone The signed zone to update the signer for.
     * @param signer     The signer to update.
     * @param active     If the signer should be active or not.
     */
    function updateSigner(
        address signedZone,
        address signer,
        bool active
    ) external;

    /**
     * @notice Initiate zone ownership transfer by assigning a new potential
     *         owner for the given zone. Once set, the new potential owner
     *         may call `acceptOwnership` to claim ownership of the zone.
     *         Only the owner of the zone in question may call this function.
     *
     * @param zone The zone for which to initiate ownership transfer.
     * @param newPotentialOwner The new potential owner of the zone.
     */
    function transferOwnership(address zone, address newPotentialOwner)
        external;

    /**
     * @notice Clear the currently set potential owner, if any, from a zone.
     *         Only the owner of the zone in question may call this function.
     *
     * @param zone The zone for which to cancel ownership transfer.
     */
    function cancelOwnershipTransfer(address zone) external;

    /**
     * @notice Accept ownership of a supplied zone. Only accounts that the
     *         current owner has set as the new potential owner may call this
     *         function.
     *
     * @param zone The zone for which to accept ownership.
     */
    function acceptOwnership(address zone) external;

    /**
     * @notice Retrieve the current owner of a deployed zone.
     *
     * @param zone The zone for which to retrieve the associated owner.
     *
     * @return owner The owner of the supplied zone.
     */
    function ownerOf(address zone) external view returns (address owner);

    /**
     * @notice Retrieve the potential owner, if any, for a given zone. The
     *         current owner may set a new potential owner via
     *         `transferOwnership` and that owner may then accept ownership of
     *         the zone in question via `acceptOwnership`.
     *
     * @param zone The zone for which to retrieve the potential owner.
     *
     * @return potentialOwner The potential owner, if any, for the zone.
     */
    function getPotentialOwner(address zone)
        external
        view
        returns (address potentialOwner);

    /**
     * @notice Derive the zone address associated with a salt.
     *
     * @param salt        The salt to be used to derive the zone address
     *
     * @return derivedAddress The derived address of the signed zone.
     */
    function getZone(bytes32 salt)
        external
        view
        returns (address derivedAddress);
}
SignedZoneControllerEventsAndErrors.sol 141 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/**
 * @notice SignedZoneControllerEventsAndErrors contains errors and events
 *         related to deploying and managing new signed zones.
 */
interface SignedZoneControllerEventsAndErrors {
    /**
     * @dev Emit an event whenever a new zone is created.
     *
     * @param zoneAddress The address of the zone.
     * @param zoneName    The name for the zone returned in
     *                    getSeaportMetadata().
     * @param apiEndpoint The API endpoint where orders for this zone can be
     *                    signed.
     * @param documentationURI The URI to the documentation describing the
     *                         behavior of the contract.
     *                    Request and response payloads are defined in SIP-7.
     * @param salt        The salt used to deploy the zone.
     */
    event ZoneCreated(
        address zoneAddress,
        string zoneName,
        string apiEndpoint,
        string documentationURI,
        bytes32 salt
    );

    /**
     * @dev Emit an event whenever zone ownership is transferred.
     *
     * @param zone          The zone for which ownership has been
     *                      transferred.
     * @param previousOwner The previous owner of the zone.
     * @param newOwner      The new owner of the zone.
     */
    event OwnershipTransferred(
        address indexed zone,
        address indexed previousOwner,
        address indexed newOwner
    );

    /**
     * @dev Emit an event whenever a zone owner registers a new potential
     *      owner for that zone.
     *
     * @param newPotentialOwner The new potential owner of the zone.
     */
    event PotentialOwnerUpdated(address indexed newPotentialOwner);

    /**
     * @dev Emit an event when a signer has been updated.
     */
    event SignerUpdated(address signedZone, address signer, bool active);

    /**
     * @dev Revert with an error when attempting to update channels or transfer
     *      ownership of a zone when the caller is not the owner of the
     *      zone in question.
     */
    error CallerIsNotOwner(address zone);

    /**
     * @dev Revert with an error when the caller is not the owner or an active
     *      signer of the signed zone in question.
     */
    error CallerIsNotOwnerOrSigner(address zone);

    /**
     * @dev Revert with an error when attempting to claim ownership of a zone
     *      with a caller that is not the current potential owner for the
     *      zone in question.
     */
    error CallerIsNotNewPotentialOwner(address zone);

    /**
     * @dev Revert with an error when attempting to create a new signed zone
     *      using a salt where the first twenty bytes do not match the address
     *      of the caller or are not set to zero.
     */
    error InvalidCreator();

    /**
     * @dev Revert with an error when attempting to create a new zone when no
     *      initial owner address is supplied.
     */
    error InvalidInitialOwner();

    /**
     * @dev Revert with an error when attempting to set a new potential owner
     *      that is already set.
     */
    error NewPotentialOwnerAlreadySet(address zone, address newPotentialOwner);

    /**
     * @dev Revert with an error when attempting to cancel ownership transfer
     *      when no new potential owner is currently set.
     */
    error NoPotentialOwnerCurrentlySet(address zone);
    /**
     * @dev Revert with an error when attempting to register a new potential
     *      owner and supplying the null address.
     */
    error NewPotentialOwnerIsZeroAddress(address zone);

    /**
     * @dev Revert with an error when attempting to interact with a zone that
     *      does not yet exist.
     */
    error NoZone();

    /**
     * @dev Revert with an error if trying to add a signer that is
     *      already active.
     */
    error SignerAlreadyAdded(address signer);

    /**
     * @dev Revert with an error if a new signer is the zero address.
     */
    error SignerCannotBeZeroAddress();

    /**
     * @dev Revert with an error if a removed signer is trying to be
     *      reauthorized.
     */
    error SignerCannotBeReauthorized(address signer);

    /**
     * @dev Revert with an error if trying to remove a signer that is
     *      not present.
     */
    error SignerNotPresent(address signer);

    /**
     * @dev Revert with an error when attempting to deploy a zone that is
     *      currently deployed.
     */
    error ZoneAlreadyExists(address zone);
}

Read Contract

getActiveSigners 0x66a2489f → address[]
getAdditionalZoneInformation 0x04789c98 → bytes32, string, string, uint256[], string
getPotentialOwner 0x906c87cc → address
getZone 0x4b42e8d5 → address
ownerOf 0x14afd79e → address

Write Contract 6 functions

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

acceptOwnership 0x51710e45
address zone
cancelOwnershipTransfer 0x7b37e561
address zone
createZone 0x0eb92516
string zoneName
string apiEndpoint
string documentationURI
address initialOwner
bytes32 salt
returns: address
transferOwnership 0x6d435421
address zone
address newPotentialOwner
updateAPIEndpoint 0xe89fad58
address zone
string newApiEndpoint
updateSigner 0xdca09383
address zone
address signer
bool active

Recent Transactions

No transactions found for this address