Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xB024aC5a7c6bC92fbACc8C3387E628a07e1Da016
Balance 0 ETH
Nonce 1
Code Size 20335 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

20335 bytes
0x6080604052600436106103a8575f3560e01c80637d5e81e2116101e9578063c27cabb511610108578063dd4e2ba51161009d578063f294bd921161006d578063f294bd9214610c8b578063f52c93c514610cbe578063fbfa77cf14610cf1578063fc0c546a14610d24575f80fd5b8063dd4e2ba514610bf2578063deaaa7cc14610c06578063eb9019d414610c39578063f046c64614610c58575f80fd5b8063c9167e6c116100d8578063c9167e6c14610b62578063cee8770814610b81578063d11d19c214610ba0578063d48d842314610bd3575f80fd5b8063c27cabb514610add578063c59057e414610af2578063c64d2ee614610b11578063c7f758a814610b30575f80fd5b80638e9d8c341161017e578063a72246871161014e578063a722468714610a61578063b4d87a1214610a8d578063b58131b014610aac578063c01f9e3714610abe575f80fd5b80638e9d8c34146109c557806391ddadf4146109f857806396ccd67314610a235780639b9e9cf814610a42575f80fd5b8063849a83b4116101b9578063849a83b41461092d57806384b0196e1461094c5780638968eb07146109735780638df3227f14610992575f80fd5b80637d5e81e21461083a5780637fcfb164146108595780638233c0a71461088457806383c6a8d3146108fa575f80fd5b80633644e515116102d5578063544ffc9c1161026a57806364a38bf91161023a57806364a38bf9146107a757806370edcbc4146107c657806375691bff146107e55780637b3c71d31461081b575f80fd5b8063544ffc9c146106f557806355a73c531461074a57806356781388146107695780635b18c26a14610788575f80fd5b806343859632116102a5578063438596321461066a5780634bf5d7e9146106a35780634d8e0825146106b7578063519c9587146106d6575f80fd5b80633644e515146105f75780633932abb11461060b5780633bccf4fd1461061f5780633e4f49e61461063e575f80fd5b80631703a0181161034b5780632656227d1161031b5780632656227d146105735780632b20e397146105865780632d63f693146105b957806330717129146105d8575f80fd5b80631703a018146104f3578063189abd1914610506578063230bcb371461052557806324b2c7b314610554575f80fd5b80630dd320e9116103865780630dd320e91461041557806310bf506814610434578063143489d014610453578063160d66ae146104c0575f80fd5b806302a251a3146103ac57806306fdde03146103d357806307a00330146103f4575b5f80fd5b3480156103b7575f80fd5b506103c0610d56565b6040519081526020015b60405180910390f35b3480156103de575f80fd5b506103e7610d62565b6040516103ca9190613b85565b3480156103ff575f80fd5b5061041361040e366004613b97565b610df1565b005b348015610420575f80fd5b506103c061042f366004613d26565b610ea0565b34801561043f575f80fd5b5061041361044e366004613dc2565b610f6a565b34801561045e575f80fd5b5061049b61046d366004613dc2565b5f908152600160205260409020546301000000900473ffffffffffffffffffffffffffffffffffffffff1690565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016103ca565b3480156104cb575f80fd5b5061049b7f0000000000000000000000005983b89fa184f14917013b9c3062afd9434c5b0381565b3480156104fe575f80fd5b5060016103c0565b348015610511575f80fd5b50610413610520366004613dc2565b610ff3565b348015610530575f80fd5b5061054461053f366004613dd9565b61117d565b60405190151581526020016103ca565b34801561055f575f80fd5b506103c061056e366004613e01565b6111c0565b6103c0610581366004614001565b6111d8565b348015610591575f80fd5b5061049b7f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c81565b3480156105c4575f80fd5b506103c06105d3366004613dc2565b6112cd565b3480156105e3575f80fd5b506103c06105f236600461408b565b6112f5565b348015610602575f80fd5b506103c061137d565b348015610616575f80fd5b506103c06113d7565b34801561062a575f80fd5b506103c0610639366004614160565b6113e0565b348015610649575f80fd5b5061065d610658366004613dc2565b611419565b6040516103ca9190614210565b348015610675575f80fd5b5061054461068436600461421e565b600260209081525f928352604080842090915290825290205460ff1681565b3480156106ae575f80fd5b506103e7611501565b3480156106c2575f80fd5b506103c06106d1366004614248565b61150b565b3480156106e1575f80fd5b506104136106f036600461427a565b611515565b348015610700575f80fd5b5061072f61070f366004613dc2565b5f8181526001602081905260408220908101546002909101549093909250565b604080519384526020840192909252908201526060016103ca565b348015610755575f80fd5b506103c06107643660046142b3565b611536565b348015610774575f80fd5b506103c06107833660046142b3565b611548565b348015610793575f80fd5b506103c06107a23660046142d4565b611563565b3480156107b2575f80fd5b506103c06107c1366004614338565b611597565b3480156107d1575f80fd5b506103c06107e0366004614338565b6115b9565b3480156107f0575f80fd5b506103c06107ff366004613dd9565b600760209081525f928352604080842090915290825290205481565b348015610826575f80fd5b506103c06108353660046143dd565b6115f9565b348015610845575f80fd5b506103c0610854366004614427565b61163b565b348015610864575f80fd5b506103c0610873366004613dc2565b60066020525f908152604090205481565b34801561088f575f80fd5b506108ce61089e366004613dc2565b5f908152600560205260409020805460019091015473ffffffffffffffffffffffffffffffffffffffff90911691565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526020830191909152016103ca565b348015610905575f80fd5b506103c07f0000000000000000000000000000000000000000000000000000048c2739500081565b348015610938575f80fd5b506103c0610947366004613e01565b6117c5565b348015610957575f80fd5b50610960611825565b6040516103ca97969594939291906144fb565b34801561097e575f80fd5b506103c061098d366004614583565b61195d565b34801561099d575f80fd5b5061049b7f000000000000000000000000a0dafaeea4a1d44534e1b9227e19cae6358b80fe81565b3480156109d0575f80fd5b506103c07fa891f76027ef63a24501b9dd3b0c779b49ad26d2328e9d423640209d1ad4fcc481565b348015610a03575f80fd5b50610a0c6119cc565b60405165ffffffffffff90911681526020016103ca565b348015610a2e575f80fd5b506103c0610a3d3660046143dd565b6119d5565b348015610a4d575f80fd5b506103c0610a5c366004614610565b6119e2565b348015610a6c575f80fd5b5060035461049b9073ffffffffffffffffffffffffffffffffffffffff1681565b348015610a98575f80fd5b50610413610aa736600461421e565b611a64565b348015610ab7575f80fd5b505f6103c0565b348015610ac9575f80fd5b506103c0610ad8366004613dc2565b611a7a565b348015610ae8575f80fd5b506103c060045481565b348015610afd575f80fd5b506103c0610b0c366004614001565b611a91565b348015610b1c575f80fd5b506103c0610b2b3660046146c4565b611ab4565b348015610b3b575f80fd5b50610b4f610b4a366004613dc2565b611b06565b6040516103ca9796959493929190614737565b348015610b6d575f80fd5b50610413610b7c366004613dd9565b611b6d565b348015610b8c575f80fd5b506103c0610b9b366004614795565b611c8a565b348015610bab575f80fd5b506103c07f7949bd92105c02f48ca245aa185f4a7a4d7185641d59b186ac64abeb44964f0c81565b348015610bde575f80fd5b50610413610bed36600461421e565b611cda565b348015610bfd575f80fd5b506103e7611cec565b348015610c11575f80fd5b506103c07f150214d74d59b7d1e90c73fc22ef3d991dd0a76b046543d4d80ab92d2a50328f81565b348015610c44575f80fd5b506103c0610c53366004613dd9565b611d0c565b348015610c63575f80fd5b506103c07f9a121fc10d6025acfc09275f9709796b68831733b5bbac0d510d13f85b1b730f81565b348015610c96575f80fd5b5061049b7f000000000000000000000000988567fe094570cce1ffda29d1f2d842b70492be81565b348015610cc9575f80fd5b5061049b7f000000000000000000000000886d405949f709bc3f4451491bdd07ff51cdf90a81565b348015610cfc575f80fd5b5061049b7f000000000000000000000000d7298f620b0f752cf41bd818a16c756d9dcaa34f81565b348015610d2f575f80fd5b507f0000000000000000000000005983b89fa184f14917013b9c3062afd9434c5b0361049b565b5f805b61ffff16905090565b60605f8054610d7090614811565b80601f0160208091040260200160405190810160405280929190818152602001828054610d9c90614811565b8015610de75780601f10610dbe57610100808354040283529160200191610de7565b820191905f5260205f20905b815481529060010190602001808311610dca57829003601f168201915b5050505050905090565b610df9611dc5565b6040517f07a0033000000000000000000000000000000000000000000000000000000000815260048101839052602481018290527f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c73ffffffffffffffffffffffffffffffffffffffff16906307a00330906044015b5f604051808303815f87803b158015610e86575f80fd5b505af1158015610e98573d5f803e3d5ffd5b505050505050565b5f610f0587610eff8888604051602001610ebb929190614862565b604051602081830303815290604052805190602001208787604051602001610ee49291906148a2565b60405160208183030381529060405280519060200120611e00565b84611e5c565b610f5f87878787878267ffffffffffffffff811115610f2657610f26613c20565b604051908082528060200260200182016040528015610f5957816020015b6060815260200190600190039081610f445790505b50611ea7565b979650505050505050565b333014801590610fb057503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000886d405949f709bc3f4451491bdd07ff51cdf90a1614155b15610fe7576040517fec4acc1f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ff081612011565b50565b5f610ffd82611419565b90506006816007811115611013576110136141aa565b1415801561103357506003816007811115611030576110306141aa565b14155b1561107557806040517f3c726c1700000000000000000000000000000000000000000000000000000000815260040161106c9190614210565b60405180910390fd5b5f82815260056020526040812060010154908190036110c0576040517f2c66d4a700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f83815260056020908152604080832080547fffffffffffffffffffffffff0000000000000000000000000000000000000000811682556001909101939093555183815273ffffffffffffffffffffffffffffffffffffffff90921691829186917f847147bb46f853e52eedf4997e56154050d17c103e1bfe61a185f02a7b6b66c0910160405180910390a3611177817f000000000000000000000000d7298f620b0f752cf41bd818a16c756d9dcaa34f8461204c565b50505050565b5f8181526006602090815260408083205473ffffffffffffffffffffffffffffffffffffffff861684526007835281842085855290925290912054145b92915050565b5f610f5f33888888886111d3888a6148db565b611ea7565b5f6111e485858561208d565b5f60016111ef612230565b6111f99190614978565b905061121f845f815181106112105761121061499a565b60200260200101518283612239565b5f81815260056020526040902060018101548154929450909173ffffffffffffffffffffffffffffffffffffffff1681156112c1575f85815260056020908152604080832080547fffffffffffffffffffffffff000000000000000000000000000000000000000016815560019081018490559091529020546112c19082906301000000900473ffffffffffffffffffffffffffffffffffffffff168461204c565b50505050949350505050565b5f8181526001602081905260408220546112eb919061ffff16614978565b61ffff1692915050565b5f61135e89610eff8a8a604051602001611310929190614862565b6040516020818303038152906040528051906020012089896040516020016113399291906148a2565b60405160208183030381529060405280519060200120611359898961232d565b612410565b61137089898989896111d3898b6148db565b9998505050505050505050565b5f7f000000000000000000000000000000000000000000000000000000000000000146146113b2576113ad61245c565b905090565b507f3b4f31285744c1c4f8315255017bc616861aaab35cbd1671c0a0be680418551e90565b5f610d596124f6565b5f61140f6113f96113f1888861252c565b868686612573565b878760405180602001604052805f815250612597565b9695505050505050565b5f818152600160205260408120805462010000900460ff161561143f5750600792915050565b5f611448612230565b825490915061ffff165f81900361148b576040517fd9c78e0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8061ffff168261ffff1610156114a557505f949350505050565b8061ffff808216908416036114c05750600195945050505050565b83600101548460020154116114db5750600395945050505050565b8060010161ffff168361ffff1611156114f557600661140f565b60049695505050505050565b60606113ad612642565b5f6111ba82612662565b61151d611dc5565b6115278383612674565b61153183826126ef565b505050565b5f611541838361252c565b9392505050565b5f61154133848460405180602001604052805f815250612597565b5f61157285610eff868661252c565b61158c85858560405180602001604052805f815250612597565b90505b949350505050565b5f61158c33868686868267ffffffffffffffff811115610f2657610f26613c20565b5f61158c85856040516020016115d0929190614862565b604051602081830303815290604052805190602001208484604051602001610ee49291906148a2565b5f61158c33868686868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061259792505050565b5f806116498686868661276a565b61ffff165f8181526006602052604081208054939550919350909161166d906149c7565b91829055506001036116f5577f0000000000000000000000005983b89fa184f14917013b9c3062afd9434c5b0373ffffffffffffffffffffffffffffffffffffffff1663c8f4a8936040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156116de575f80fd5b505af11580156116f0573d5f803e3d5ffd5b505050505b6004545f81900361170757505061158f565b60035460408051808201825273ffffffffffffffffffffffffffffffffffffffff92831680825260208083018681525f8981526005909252939020915182547fffffffffffffffffffffffff00000000000000000000000000000000000000001694169390931781559051600190910155611784813330856129b1565b6117ba576040517f7939f42400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050949350505050565b5f610f5f87876040516020016117dc929190614862565b6040516020818303038152906040528051906020012086866040516020016118059291906148a2565b60405160208183030381529060405280519060200120611359868661232d565b5f60608082808083814630828060405190808252806020026020018201604052801561185b578160200160208202803683370190505b507f0f00000000000000000000000000000000000000000000000000000000000000949392919084805461188e90614811565b80601f01602080910402602001604051908101604052809291908181526020018280546118ba90614811565b80156119055780601f106118dc57610100808354040283529160200191611905565b820191905f5260205f20905b8154815290600101906020018083116118e857829003601f168201915b505050505094506040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525093929190965096509650965096509650965090919293949596565b5f6119c06119a36113f18a8a60405160200161197a929190614862565b604051602081830303815290604052805190602001208989604051602001610ee49291906148a2565b898989898267ffffffffffffffff811115610f2657610f26613c20565b98975050505050505050565b5f610d59612230565b5f61158c85858585612a8f565b5f611a56611a486113f18c8c6040516020016119ff929190614862565b604051602081830303815290604052805190602001208b8b604051602001611a289291906148a2565b604051602081830303815290604052805190602001206113598b8b61232d565b8b8b8b8b6111d38b8d6148db565b9a9950505050505050505050565b611a6c611dc5565b611a7682826126ef565b5050565b5f8181526001602052604081205461ffff166112eb565b5f61158c835f81518110611aa757611aa761499a565b6020026020010151612662565b5f611ac587610eff88888888612a8f565b610f5f87878787878080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061259792505050565b5f818152600160205260408120805461ffff169182918190819081908190611b2d89611419565b600182810154600284015493549a9c999b5091999198929750630100000090910473ffffffffffffffffffffffffffffffffffffffff1695509350915050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0dafaeea4a1d44534e1b9227e19cae6358b80fe1614611bdc576040517fb30b9d4300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611be582612af9565b6040517f286b1d0b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301527f0000000000000000000000005983b89fa184f14917013b9c3062afd9434c5b03169063286b1d0b906024015f604051808303815f87803b158015611c6b575f80fd5b505af1158015611c7d573d5f803e3d5ffd5b50505050611a7681612011565b5f6119c0611c9d6113f18a8a8a8a612a8f565b898989898080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061259792505050565b611ce2611dc5565b611a768282612674565b60606040518060600160405280602f8152602001614f0b602f9139905090565b6040517f3a46b1a800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8381166004830152602482018390525f917f0000000000000000000000005983b89fa184f14917013b9c3062afd9434c5b0390911690633a46b1a890604401602060405180830381865afa158015611da1573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061154191906149fe565b333014611dfe576040517f29c3b7ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b604080517f9a121fc10d6025acfc09275f9709796b68831733b5bbac0d510d13f85b1b730f6020820152908101839052606081018290525f90611541906080015b60405160208183030381529060405280519060200120612bb4565b5f611e68848484612c15565b90505f816005811115611e7d57611e7d6141aa565b03611e885750505050565b611e93848484612c4f565b15611e9e5750505050565b61117781612d97565b5f84808203611ee2576040517f75672da700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b808414611f25576040517ffa5dbe08000000000000000000000000000000000000000000000000000000008152600481018290526024810185905260440161106c565b82518114611f6c5782516040517ffa5dbe0800000000000000000000000000000000000000000000000000000000815261106c918391600401918252602082015260400190565b611f8d886001611f7a612230565b611f849190614978565b61ffff16611d0c565b91505f5b8181101561200557611ffd89848a8a85818110611fb057611fb061499a565b90506020020135898986818110611fc957611fc961499a565b9050602002016020810190611fde9190614a15565b888681518110611ff057611ff061499a565b6020026020010151612f5b565b600101611f91565b50509695505050505050565b60048190556040518181527f541c2b585302c70ad540b5b858177b648b4c1c75073e78ac37aa2edb67dddd869060200160405180910390a150565b612057838383613215565b611531576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82516001146120c8576040517f83913ab500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff16835f815181106120f1576120f161499a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614612146576040517f82d5d76a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8151600114612181576040517f32143ab000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f815181106121935761219361499a565b60200260200101515f146121d3576040517faa7feadc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805160011461220e576040517fc569313f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611531815f815181106122235761222361499a565b602002602001015161326d565b5f6113ad61346d565b5f3415612272576040517faa7feadc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8161ffff165f036122af576040517f0797d96900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8161ffff168361ffff16106122fb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8301926122ed908590613495565b9050805f03611541576122af565b6040517fb696436000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f81818167ffffffffffffffff81111561234957612349613c20565b604051908082528060200260200182016040528015612372578160200160208202803683370190505b5090505f5b828110156123de578585828181106123915761239161499a565b90506020028101906123a39190614a2e565b6040516123b1929190614a8f565b60405180910390208282815181106123cb576123cb61499a565b6020908102919091010152600101612377565b50806040516020016123f09190614a9e565b604051602081830303815290604052805190602001209250505092915050565b604080517fa891f76027ef63a24501b9dd3b0c779b49ad26d2328e9d423640209d1ad4fcc4602082015290810184905260608101839052608081018290525f9061158f9060a001611e41565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f1b5f60405161248e9190614ad3565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f60026125016119cc565b61250b9190614bd5565b65ffffffffffff16600114612521576001612524565b60025b60ff16905090565b604080517f150214d74d59b7d1e90c73fc22ef3d991dd0a76b046543d4d80ab92d2a50328f602082015290810183905260ff821660608201525f9061154190608001611e41565b5f8061258186868686613604565b9250905061258e81612d97565b50949350505050565b5f806125a285611419565b905060018160078111156125b8576125b86141aa565b146125f157806040517f88f40ea000000000000000000000000000000000000000000000000000000000815260040161106c9190614210565b5f858152600160205260409020546126339087907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61ffff9182160116611d0c565b915061258e8683878787612f5b565b60606040518060a0016040528060658152602001614ea660659139905090565b5f6111ba8261266f613711565b613728565b6040517fd48d84230000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301527f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c169063d48d842390604401610e6f565b6040517fb4d87a120000000000000000000000000000000000000000000000000000000081526004810183905273ffffffffffffffffffffffffffffffffffffffff82811660248301527f000000000000000000000000119fbeedd4f4f4298fb59b720d5654442b81ae2c169063b4d87a1290604401610e6f565b5f8061277786868661208d565b61277f613711565b90506127a4845f815181106127965761279661499a565b602002602001015182613728565b5f8181526001602052604090205490925061ffff16156127f0576040517f5c36267800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61291482826040805160c08101825261ffff92831681525f602080830182815233848601908152606085018481526080860185815260a087018681529986526001948590529690942094518554925191519451881677010000000000000000000000000000000000000000000000027fffffffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff96909616630100000002959095167fffffffffffffff00000000000000000000000000000000000000000000ffffff92151562010000027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000090941691909816179190911716949094171781559051918101919091559051600290910155565b7f7d84a6263ae0d98d3329bd7b46bb4e8d6f98cd35a7adb45c274c8b7fd5ebd5e0823388888a5167ffffffffffffffff81111561295357612953613c20565b60405190808252806020026020018201604052801561298657816020015b60608152602001906001900390816129715790505b508987808b6040516129a099989796959493929190614c6f565b60405180910390a194509492505050565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290525f9061158c9086907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261377b565b5f61158c7f7949bd92105c02f48ca245aa185f4a7a4d7185641d59b186ac64abeb44964f0c5f1b86868686604051612ac8929190614a8f565b604051908190038120611e4194939291602001938452602084019290925260ff166040830152606082015260800190565b73ffffffffffffffffffffffffffffffffffffffff8116612b46576040517f9ce2462600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600380547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f80ad633436e52e87151a445d2f18b89f15cc0b126e0b81568f6d19861c827b3b905f90a250565b5f612bbd61137d565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604051602081830303815290604052805190602001209050919050565b5f805f612c22858561382f565b90925090505f826005811115612c3a57612c3a6141aa565b14612c45578161140f565b61140f8682613873565b5f805f8573ffffffffffffffffffffffffffffffffffffffff168585604051602401612c7c929190614d4b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1626ba7e0000000000000000000000000000000000000000000000000000000017905251612cfd9190614d63565b5f60405180830381855afa9150503d805f8114612d35576040519150601f19603f3d011682016040523d82523d5f602084013e612d3a565b606091505b5091509150818015612d4e57506020815110155b801561140f575080517f1626ba7e0000000000000000000000000000000000000000000000000000000090612d8c90830160209081019084016149fe565b149695505050505050565b5f816005811115612daa57612daa6141aa565b03612db25750565b6001816005811115612dc657612dc66141aa565b03612dfd576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002816005811115612e1157612e116141aa565b03612e48576040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003816005811115612e5c57612e5c6141aa565b03612e93576040517fbf4bf5b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004816005811115612ea757612ea76141aa565b03612ede576040517fff551e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005816005811115612ef257612ef26141aa565b03612f29576040517f10c74b0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f612f6584611419565b90506001816007811115612f7b57612f7b6141aa565b14612fb457806040517f88f40ea000000000000000000000000000000000000000000000000000000000815260040161106c9190614210565b612fc186868686866138b6565b5f612fca612230565b61ffff81165f8181526006602090815260408083205473ffffffffffffffffffffffffffffffffffffffff8d1684526007835281842094845293909152812080549394509192613019906149c7565b91829055501461302a57505061320e565b60405161ffff82169073ffffffffffffffffffffffffffffffffffffffff8916907faec1eae443fe2f674be7e8a9e6de65d4b90f5536050fe468320204a765012701905f90a36040517fc580b26900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff88811660048301527f0000000000000000000000005983b89fa184f14917013b9c3062afd9434c5b03169063c580b269906024015f604051808303815f87803b1580156130f6575f80fd5b505af1158015613108573d5f803e3d5ffd5b505050505f61312260018361311d9190614978565b613a68565b61314c887f0000000000000000000000000000000000000000000000000000048c27395000614d7e565b6131569190614d95565b9050805f036131675750505061320e565b6040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8981166004830152602482018390527f000000000000000000000000988567fe094570cce1ffda29d1f2d842b70492be16906340c10f19906044015f604051808303815f87803b1580156131f4575f80fd5b505af1158015613206573d5f803e3d5ffd5b505050505050505b5050505050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290525f9061158f9085907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401612a0d565b5f61327782614da8565b82519091507fffffffff0000000000000000000000000000000000000000000000000000000082167fb4d87a12000000000000000000000000000000000000000000000000000000001480156132cd5750604481145b15801561332857507fffffffff0000000000000000000000000000000000000000000000000000000082167fd48d8423000000000000000000000000000000000000000000000000000000001480156133265750604481145b155b801561338257507fffffffff0000000000000000000000000000000000000000000000000000000082167f519c9587000000000000000000000000000000000000000000000000000000001480156133805750606481145b155b80156133dc57507fffffffff0000000000000000000000000000000000000000000000000000000082167f07a00330000000000000000000000000000000000000000000000000000000001480156133da5750604481145b155b801561343657507fffffffff0000000000000000000000000000000000000000000000000000000082167f10bf5068000000000000000000000000000000000000000000000000000000001480156134345750602481145b155b15611531576040517f1c49f4d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6213c68061348063661bd35042614df7565b61348a9190614d95565b6113ad906001614e0a565b5f6134a08383613728565b5f81815260016020526040902080549192509061ffff8481169116146134c9575f9150506111ba565b60046134d483611419565b60078111156134e5576134e56141aa565b146134f3575f9150506111ba565b80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff16620100001781556040517f712ae1383f79ac853f8d882153778e0260ef8f03b504e2866e0593e04d2b291f906135509084815260200190565b60405180910390a15f803073ffffffffffffffffffffffffffffffffffffffff168660405161357f9190614d63565b5f604051808303815f865af19150503d805f81146135b8576040519150601f19603f3d011682016040523d82523d5f602084013e6135bd565b606091505b5091509150816135fb57806040517f15fcd67500000000000000000000000000000000000000000000000000000000815260040161106c9190613b85565b50505092915050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156136395750600390505f613708565b8460ff16601b1415801561365157508460ff16601c14155b156136615750600490505f613708565b604080515f81526020810180835288905260ff871691810191909152606081018590526080810184905260019060a0016020604051602081039080840390855afa1580156136b1573d5f803e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff8116156136ff575f81613703565b60015f5b915091505b94509492505050565b5f61371a6124f6565b613722612230565b01905090565b5f82823060405160200161373e93929190614e1d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b5f73ffffffffffffffffffffffffffffffffffffffff83163b61379f57505f6111ba565b60608373ffffffffffffffffffffffffffffffffffffffff16836040516137c69190614d63565b5f604051808303815f865af19150503d805f81146137ff576040519150601f19603f3d011682016040523d82523d5f602084013e613804565b606091505b50909250905081801561158f57508051158061158f57508080602001905181019061158f9190614e5f565b5f8082516041146138455750600290505f61386c565b6020830151604084015160608501515f1a919061386487848484613604565b945094505050505b9250929050565b5f8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16146138ae576005611541565b505f92915050565b835f036138ef576040517f8791239e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f83815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff8916845290915290205460ff1615613958576040517f7c9a1cf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f83815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff89168452909152812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558260ff1660018111156139c4576139c46141aa565b60018111156139d5576139d56141aa565b036139f6575f83815260016020819052604090912001805485019055613a0d565b5f8381526001602052604090206002018054850190555b8473ffffffffffffffffffffffffffffffffffffffff167fb8e138887d0aa13bab447e82de9d5c1777041ecd21ca36ba824ff1e6c07ddda484848785604051613a599493929190614e7e565b60405180910390a25050505050565b6040517f5c7e5d5f00000000000000000000000000000000000000000000000000000000815261ffff821660048201525f907f0000000000000000000000005983b89fa184f14917013b9c3062afd9434c5b0373ffffffffffffffffffffffffffffffffffffffff1690635c7e5d5f90602401602060405180830381865afa158015613af6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111ba91906149fe565b5f5b83811015613b34578181015183820152602001613b1c565b50505f910152565b5f8151808452613b53816020860160208601613b1a565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081525f6115416020830184613b3c565b5f8060408385031215613ba8575f80fd5b50508035926020909101359150565b803573ffffffffffffffffffffffffffffffffffffffff81168114613bda575f80fd5b919050565b5f8083601f840112613bef575f80fd5b50813567ffffffffffffffff811115613c06575f80fd5b6020830191508360208260051b850101111561386c575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613c9457613c94613c20565b604052919050565b5f82601f830112613cab575f80fd5b813567ffffffffffffffff811115613cc557613cc5613c20565b613cf660207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613c4d565b818152846020838601011115613d0a575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f805f8060808789031215613d3b575f80fd5b613d4487613bb7565b9550602087013567ffffffffffffffff80821115613d60575f80fd5b613d6c8a838b01613bdf565b90975095506040890135915080821115613d84575f80fd5b613d908a838b01613bdf565b90955093506060890135915080821115613da8575f80fd5b50613db589828a01613c9c565b9150509295509295509295565b5f60208284031215613dd2575f80fd5b5035919050565b5f8060408385031215613dea575f80fd5b613df383613bb7565b946020939093013593505050565b5f805f805f8060608789031215613e16575f80fd5b863567ffffffffffffffff80821115613e2d575f80fd5b613e398a838b01613bdf565b90985096506020890135915080821115613e51575f80fd5b613e5d8a838b01613bdf565b90965094506040890135915080821115613e75575f80fd5b50613e8289828a01613bdf565b979a9699509497509295939492505050565b5f67ffffffffffffffff821115613ead57613ead613c20565b5060051b60200190565b5f82601f830112613ec6575f80fd5b81356020613edb613ed683613e94565b613c4d565b8083825260208201915060208460051b870101935086841115613efc575f80fd5b602086015b84811015613f1f57613f1281613bb7565b8352918301918301613f01565b509695505050505050565b5f82601f830112613f39575f80fd5b81356020613f49613ed683613e94565b8083825260208201915060208460051b870101935086841115613f6a575f80fd5b602086015b84811015613f1f5780358352918301918301613f6f565b5f82601f830112613f95575f80fd5b81356020613fa5613ed683613e94565b82815260059290921b84018101918181019086841115613fc3575f80fd5b8286015b84811015613f1f57803567ffffffffffffffff811115613fe5575f80fd5b613ff38986838b0101613c9c565b845250918301918301613fc7565b5f805f8060808587031215614014575f80fd5b843567ffffffffffffffff8082111561402b575f80fd5b61403788838901613eb7565b9550602087013591508082111561404c575f80fd5b61405888838901613f2a565b9450604087013591508082111561406d575f80fd5b5061407a87828801613f86565b949793965093946060013593505050565b5f805f805f805f8060a0898b0312156140a2575f80fd5b6140ab89613bb7565b9750602089013567ffffffffffffffff808211156140c7575f80fd5b6140d38c838d01613bdf565b909950975060408b01359150808211156140eb575f80fd5b6140f78c838d01613bdf565b909750955060608b013591508082111561410f575f80fd5b61411b8c838d01613bdf565b909550935060808b0135915080821115614133575f80fd5b506141408b828c01613c9c565b9150509295985092959890939650565b803560ff81168114613bda575f80fd5b5f805f805f60a08688031215614174575f80fd5b8535945061418460208701614150565b935061419260408701614150565b94979396509394606081013594506080013592915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6008811061420c577f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b9052565b602081016111ba82846141d7565b5f806040838503121561422f575f80fd5b8235915061423f60208401613bb7565b90509250929050565b5f60208284031215614258575f80fd5b813567ffffffffffffffff81111561426e575f80fd5b61158f84828501613c9c565b5f805f6060848603121561428c575f80fd5b8335925061429c60208501613bb7565b91506142aa60408501613bb7565b90509250925092565b5f80604083850312156142c4575f80fd5b8235915061423f60208401614150565b5f805f80608085870312156142e7575f80fd5b6142f085613bb7565b93506020850135925061430560408601614150565b9150606085013567ffffffffffffffff811115614320575f80fd5b61432c87828801613c9c565b91505092959194509250565b5f805f806040858703121561434b575f80fd5b843567ffffffffffffffff80821115614362575f80fd5b61436e88838901613bdf565b90965094506020870135915080821115614386575f80fd5b5061439387828801613bdf565b95989497509550505050565b5f8083601f8401126143af575f80fd5b50813567ffffffffffffffff8111156143c6575f80fd5b60208301915083602082850101111561386c575f80fd5b5f805f80606085870312156143f0575f80fd5b8435935061440060208601614150565b9250604085013567ffffffffffffffff81111561441b575f80fd5b6143938782880161439f565b5f805f806080858703121561443a575f80fd5b843567ffffffffffffffff80821115614451575f80fd5b61445d88838901613eb7565b95506020870135915080821115614472575f80fd5b61447e88838901613f2a565b94506040870135915080821115614493575f80fd5b61449f88838901613f86565b935060608701359150808211156144b4575f80fd5b5061432c87828801613c9c565b5f815180845260208085019450602084015f5b838110156144f0578151875295820195908201906001016144d4565b509495945050505050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e060208201525f61453560e0830189613b3c565b82810360408401526145478189613b3c565b905086606084015273ffffffffffffffffffffffffffffffffffffffff861660808401528460a084015282810360c0840152611a5681856144c1565b5f805f805f805f60a0888a031215614599575f80fd5b873567ffffffffffffffff808211156145b0575f80fd5b6145bc8b838c01613bdf565b909950975060208a01359150808211156145d4575f80fd5b506145e18a828b01613bdf565b90965094506145f4905060408901614150565b9250606088013591506080880135905092959891949750929550565b5f805f805f805f805f60c08a8c031215614628575f80fd5b893567ffffffffffffffff8082111561463f575f80fd5b61464b8d838e01613bdf565b909b50995060208c0135915080821115614663575f80fd5b61466f8d838e01613bdf565b909950975060408c0135915080821115614687575f80fd5b506146948c828d01613bdf565b90965094506146a7905060608b01614150565b925060808a0135915060a08a013590509295985092959850929598565b5f805f805f8060a087890312156146d9575f80fd5b6146e287613bb7565b9550602087013594506146f760408801614150565b9350606087013567ffffffffffffffff80821115614713575f80fd5b61471f8a838b0161439f565b90955093506080890135915080821115613da8575f80fd5b65ffffffffffff88811682528716602082015260e0810161475b60408301886141d7565b85606083015284608083015273ffffffffffffffffffffffffffffffffffffffff841660a08301528260c083015298975050505050505050565b5f805f805f805f60c0888a0312156147ab575f80fd5b873596506147bb60208901614150565b9550604088013567ffffffffffffffff8111156147d6575f80fd5b6147e28a828b0161439f565b90965094506147f5905060608901614150565b92506080880135915060a0880135905092959891949750929550565b600181811c9082168061482557607f821691505b60208210810361485c577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b5f7f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561488f575f80fd5b8260051b80858437919091019392505050565b5f8184825b858110156148d05760ff6148ba83614150565b16835260209283019291909101906001016148a7565b509095945050505050565b5f6148e8613ed684613e94565b80848252602080830192508560051b850136811115614905575f80fd5b855b8181101561493f57803567ffffffffffffffff811115614925575f80fd5b61493136828a01613c9c565b865250938201938201614907565b50919695505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b61ffff8281168282160390808211156149935761499361494b565b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036149f7576149f761494b565b5060010190565b5f60208284031215614a0e575f80fd5b5051919050565b5f60208284031215614a25575f80fd5b61154182614150565b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614a61575f80fd5b83018035915067ffffffffffffffff821115614a7b575f80fd5b60200191503681900382131561386c575f80fd5b818382375f9101908152919050565b81515f9082906020808601845b83811015614ac757815185529382019390820190600101614aab565b50929695505050505050565b5f8083545f60018260011c91506001831680614af057607f831692505b60208084108203614b28577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b818015614b3c5760018114614b6f57614b9a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086168952841515850289019650614b9a565b5f8a8152602090205f5b86811015614b925781548b820152908501908301614b79565b505084890196505b509498975050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f65ffffffffffff80841680614bed57614bed614ba8565b92169190910692915050565b5f8282518085526020808601955060208260051b840101602086015f5b84811015614c62577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0868403018952614c50838351613b3c565b98840198925090830190600101614c16565b5090979650505050505050565b5f6101208083018c8452602073ffffffffffffffffffffffffffffffffffffffff808e1660208701528360408701528293508c518084526101408701945060208e0193505f5b81811015614cd3578451831686529483019493830193600101614cb5565b50505050508281036060840152614cea818a6144c1565b90508281036080840152614cfe8189614bf9565b905082810360a0840152614d128188614bf9565b61ffff871660c0850152905061ffff851660e0840152828103610100840152614d3b8185613b3c565b9c9b505050505050505050505050565b828152604060208201525f61158f6040830184613b3c565b5f8251614d74818460208701613b1a565b9190910192915050565b80820281158282048414176111ba576111ba61494b565b5f82614da357614da3614ba8565b500490565b5f815160208301517fffffffff0000000000000000000000000000000000000000000000000000000080821693506004831015614def5780818460040360031b1b83161693505b505050919050565b818103818111156111ba576111ba61494b565b808201808211156111ba576111ba61494b565b606081525f614e2f6060830186613b3c565b905061ffff8416602083015273ffffffffffffffffffffffffffffffffffffffff83166040830152949350505050565b5f60208284031215614e6f575f80fd5b81518015158114611541575f80fd5b84815260ff84166020820152826040820152608060608201525f61140f6080830184613b3c56fe6d6f64653d65706f63682665706f6368556e6465726c79696e67536f757263653d626c6f636b54696d657374616d702665706f63685374617274696e6754696d657374616d703d313731333039393630302665706f6368506572696f643d31323936303030737570706f72743d616761696e73742c666f722671756f72756d3d666f7226737563636573733d6d616a6f72697479a2646970667358221220910b9b15080ecc4ba50adffad5058892763d9688bf7f05fbda99784b46bbf72a64736f6c63430008170033

Verified Source Code Full Match

Compiler: v0.8.23+commit.f704f362 EVM: shanghai Optimization: Yes (999999 runs)
PureEpochs.sol 34 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

/**
 * @notice Defines epochs as periods away from STARTING_TIMESTAMP timestamp.
 * @author M^0 Labs
 * @dev    Provides a `uint16` epoch clock value.
 */
library PureEpochs {
    /* ============ Variables ============ */

    /// @notice The timestamp of the start of Epoch 1.
    uint40 internal constant STARTING_TIMESTAMP = 1713099600;

    /// @notice The approximate target of seconds an epoch should endure.
    uint40 internal constant EPOCH_PERIOD = 1296000;

    /* ============ Internal View/Pure Functions ============ */

    /// @dev Returns the current epoch number.
    function currentEpoch() internal view returns (uint16) {
        return uint16(((block.timestamp - STARTING_TIMESTAMP) / EPOCH_PERIOD) + 1);
    }

    /// @dev Returns the remaining time in the current epoch.
    function timeRemainingInCurrentEpoch() internal view returns (uint40) {
        return STARTING_TIMESTAMP + (currentEpoch() * EPOCH_PERIOD) - uint40(block.timestamp);
    }

    function clockMode() internal pure returns (string memory) {
        return "mode=epoch&epochUnderlyingSource=blockTimestamp&epochStartingTimestamp=1713099600&epochPeriod=1296000";
    }
}
StandardGovernor.sol 473 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import { ERC20Helper } from "../lib/erc20-helper/src/ERC20Helper.sol";

import { IGovernor } from "./abstract/interfaces/IGovernor.sol";

import { BatchGovernor } from "./abstract/BatchGovernor.sol";

import { IPowerToken } from "./interfaces/IPowerToken.sol";
import { IRegistrar } from "./interfaces/IRegistrar.sol";
import { IStandardGovernor } from "./interfaces/IStandardGovernor.sol";
import { IZeroToken } from "./interfaces/IZeroToken.sol";

/**
 * @title  An instance of a BatchGovernor with a unique and limited set of possible proposals with proposal fees.
 * @author M^0 Labs
 */
contract StandardGovernor is IStandardGovernor, BatchGovernor {
    /* ============ Structs ============ */

    /**
     * @notice The proposal fee info.
     * @param  cashToken The address of the cash token used to pay the fee.
     * @param  fee       The amount of the fee per proposal.
     */
    struct ProposalFeeInfo {
        address cashToken;
        uint256 fee;
    }

    /* ============ Variables ============ */

    /// @inheritdoc IStandardGovernor
    address public immutable emergencyGovernor;

    /// @inheritdoc IStandardGovernor
    address public immutable registrar;

    /// @inheritdoc IStandardGovernor
    address public immutable vault;

    /// @inheritdoc IStandardGovernor
    address public immutable zeroGovernor;

    /// @inheritdoc IStandardGovernor
    address public immutable zeroToken;

    /// @inheritdoc IStandardGovernor
    uint256 public immutable maxTotalZeroRewardPerActiveEpoch;

    /// @inheritdoc IStandardGovernor
    address public cashToken;

    /// @inheritdoc IStandardGovernor
    uint256 public proposalFee;

    /// @dev The proposal fee info per proposal ID.
    mapping(uint256 proposalId => ProposalFeeInfo proposalFee) internal _proposalFees;

    /// @dev The amount of proposals per epoch.
    mapping(uint256 epoch => uint256 count) public numberOfProposalsAt;

    /// @dev The amount of proposals a voter has voted on per epoch.
    mapping(address voter => mapping(uint256 epoch => uint256 count)) public numberOfProposalsVotedOnAt;

    /* ============ Modifiers ============ */

    /// @dev Revert if the caller is not the Zero Governor.
    modifier onlyZeroGovernor() {
        if (msg.sender != zeroGovernor) revert NotZeroGovernor();
        _;
    }

    /// @dev Revert if the caller is not the Standard Governor nor the Emergency Governor.
    modifier onlySelfOrEmergencyGovernor() {
        if (msg.sender != address(this) && msg.sender != emergencyGovernor) revert NotSelfOrEmergencyGovernor();
        _;
    }

    /* ============ Constructor ============ */

    /**
     * @notice Constructs a new StandardGovernor contract.
     * @param  voteToken_                        The address of the Vote Token contract.
     * @param  emergencyGovernor_                The address of the Emergency Governor contract.
     * @param  zeroGovernor_                     The address of the Zero Governor contract.
     * @param  cashToken_                        The address of the Cash Token contract.
     * @param  registrar_                        The address of the Registrar contract.
     * @param  vault_                            The address of the Vault contract.
     * @param  zeroToken_                        The address of the Zero Token contract.
     * @param  proposalFee_                      The proposal fee.
     * @param  maxTotalZeroRewardPerActiveEpoch_ The maximum amount of zero tokens to reward per active epoch.
     */
    constructor(
        address voteToken_,
        address emergencyGovernor_,
        address zeroGovernor_,
        address cashToken_,
        address registrar_,
        address vault_,
        address zeroToken_,
        uint256 proposalFee_,
        uint256 maxTotalZeroRewardPerActiveEpoch_
    ) BatchGovernor("StandardGovernor", voteToken_) {
        if ((emergencyGovernor = emergencyGovernor_) == address(0)) revert InvalidEmergencyGovernorAddress();
        if ((zeroGovernor = zeroGovernor_) == address(0)) revert InvalidZeroGovernorAddress();
        if ((registrar = registrar_) == address(0)) revert InvalidRegistrarAddress();
        if ((vault = vault_) == address(0)) revert InvalidVaultAddress();
        if ((zeroToken = zeroToken_) == address(0)) revert InvalidZeroTokenAddress();

        _setCashToken(cashToken_);
        _setProposalFee(proposalFee_);

        maxTotalZeroRewardPerActiveEpoch = maxTotalZeroRewardPerActiveEpoch_;
    }

    /* ============ Interactive Functions ============ */

    /// @inheritdoc IGovernor
    function execute(
        address[] memory targets_,
        uint256[] memory values_,
        bytes[] memory callDatas_,
        bytes32
    ) external payable returns (uint256 proposalId_) {
        _revertIfInvalidProposal(targets_, values_, callDatas_);

        // Proposals have voteStart=N and voteEnd=N, and can be executed only during epoch N+1.
        uint16 latestPossibleVoteStart_ = _clock() - 1;

        proposalId_ = _tryExecute(callDatas_[0], latestPossibleVoteStart_, latestPossibleVoteStart_);

        ProposalFeeInfo storage proposalFeeInfo_ = _proposalFees[proposalId_];
        uint256 proposalFee_ = proposalFeeInfo_.fee;
        address cashToken_ = proposalFeeInfo_.cashToken;

        if (proposalFee_ > 0) {
            delete _proposalFees[proposalId_];
            _transfer(cashToken_, _proposals[proposalId_].proposer, proposalFee_);
        }
    }

    /// @inheritdoc IGovernor
    function propose(
        address[] memory targets_,
        uint256[] memory values_,
        bytes[] memory callDatas_,
        string memory description_
    ) external override returns (uint256 proposalId_) {
        uint256 voteStart_;

        (proposalId_, voteStart_) = _propose(targets_, values_, callDatas_, description_);

        // If this is the first proposal for the `voteStart_` epoch, inflate its target total supply of `PowerToken`.
        if (++numberOfProposalsAt[voteStart_] == 1) {
            IPowerToken(voteToken).markNextVotingEpochAsActive();
        }

        uint256 proposalFee_ = proposalFee;

        if (proposalFee_ == 0) return proposalId_;

        address cashToken_ = cashToken;

        _proposalFees[proposalId_] = ProposalFeeInfo({ cashToken: cashToken_, fee: proposalFee_ });

        if (!ERC20Helper.transferFrom(cashToken_, msg.sender, address(this), proposalFee_)) revert TransferFromFailed();
    }

    /// @inheritdoc IStandardGovernor
    function setCashToken(address newCashToken_, uint256 newProposalFee_) external onlyZeroGovernor {
        _setCashToken(newCashToken_);

        IPowerToken(voteToken).setNextCashToken(newCashToken_);

        _setProposalFee(newProposalFee_);
    }

    /// @inheritdoc IStandardGovernor
    function sendProposalFeeToVault(uint256 proposalId_) external {
        ProposalState state_ = state(proposalId_);

        // Must be expired or defeated to have the fee sent to the vault
        if (state_ != ProposalState.Expired && state_ != ProposalState.Defeated) revert FeeNotDestinedForVault(state_);

        uint256 proposalFee_ = _proposalFees[proposalId_].fee;

        if (proposalFee_ == 0) revert NoFeeToSend();

        address cashToken_ = _proposalFees[proposalId_].cashToken;

        delete _proposalFees[proposalId_];

        emit ProposalFeeSentToVault(proposalId_, cashToken_, proposalFee_);

        // NOTE: Not calling `distribute` on vault since anyone can do it, anytime, and this contract should not need to
        //       know how the vault works
        _transfer(cashToken_, vault, proposalFee_);
    }

    /* ============ Proposal Functions ============ */

    /// @inheritdoc IStandardGovernor
    function addToList(bytes32 list_, address account_) external onlySelf {
        _addToList(list_, account_);
    }

    /// @inheritdoc IStandardGovernor
    function removeFromList(bytes32 list_, address account_) external onlySelf {
        _removeFromList(list_, account_);
    }

    /// @inheritdoc IStandardGovernor
    function removeFromAndAddToList(bytes32 list_, address accountToRemove_, address accountToAdd_) external onlySelf {
        _removeFromList(list_, accountToRemove_);
        _addToList(list_, accountToAdd_);
    }

    /// @inheritdoc IStandardGovernor
    function setKey(bytes32 key_, bytes32 value_) external onlySelf {
        IRegistrar(registrar).setKey(key_, value_);
    }

    /// @inheritdoc IStandardGovernor
    function setProposalFee(uint256 newProposalFee_) external onlySelfOrEmergencyGovernor {
        _setProposalFee(newProposalFee_);
    }

    /* ============ View/Pure Functions ============ */

    /// @inheritdoc IGovernor
    function COUNTING_MODE() external pure returns (string memory) {
        return "support=against,for&quorum=for&success=majority";
    }

    /// @inheritdoc IStandardGovernor
    function getProposal(
        uint256 proposalId_
    )
        external
        view
        returns (
            uint48 voteStart_,
            uint48 voteEnd_,
            ProposalState state_,
            uint256 noVotes_,
            uint256 yesVotes_,
            address proposer_,
            uint256 quorum_
        )
    {
        Proposal storage proposal_ = _proposals[proposalId_];

        voteStart_ = proposal_.voteStart;
        voteEnd_ = _getVoteEnd(proposal_.voteStart);
        state_ = state(proposalId_);
        noVotes_ = proposal_.noWeight;
        yesVotes_ = proposal_.yesWeight;
        proposer_ = proposal_.proposer;
        quorum_ = 1;
    }

    /// @inheritdoc IStandardGovernor
    function getProposalFee(uint256 proposalId_) external view returns (address cashToken_, uint256 fee_) {
        ProposalFeeInfo storage proposalFee_ = _proposalFees[proposalId_];

        cashToken_ = proposalFee_.cashToken;
        fee_ = proposalFee_.fee;
    }

    /// @inheritdoc IStandardGovernor
    function hasVotedOnAllProposals(address voter_, uint256 epoch_) external view returns (bool) {
        return numberOfProposalsVotedOnAt[voter_][epoch_] == numberOfProposalsAt[epoch_];
    }

    /// @inheritdoc IGovernor
    function quorum() external pure returns (uint256) {
        return 1;
    }

    /// @inheritdoc IGovernor
    function state(uint256 proposalId_) public view override(BatchGovernor, IGovernor) returns (ProposalState) {
        Proposal storage proposal_ = _proposals[proposalId_];

        if (proposal_.executed) return ProposalState.Executed;

        uint16 currentEpoch_ = _clock();
        uint16 voteStart_ = proposal_.voteStart;

        if (voteStart_ == 0) revert ProposalDoesNotExist();

        if (currentEpoch_ < voteStart_) return ProposalState.Pending;

        uint16 voteEnd_ = _getVoteEnd(voteStart_);

        if (currentEpoch_ == voteEnd_) return ProposalState.Active;

        if (proposal_.yesWeight <= proposal_.noWeight) return ProposalState.Defeated;

        unchecked {
            return (currentEpoch_ <= voteEnd_ + 1) ? ProposalState.Succeeded : ProposalState.Expired;
        }
    }

    /* ============ Internal Interactive Functions ============ */

    /**
     * @dev    Cast votes on several proposals for `voter_`.
     * @param  voter_       The address of the voter.
     * @param  proposalIds_ The unique identifiers of the proposals.
     * @param  supportList_ The type of support to cast for each proposal.
     * @param  reasonList_  The list of reason per proposal IDs to cast.
     * @return weight_      The number of votes the voter cast on each proposal.
     */
    function _castVotes(
        address voter_,
        uint256[] calldata proposalIds_,
        uint8[] calldata supportList_,
        string[] memory reasonList_
    ) internal override returns (uint256 weight_) {
        uint256 length_ = proposalIds_.length;

        if (length_ == 0) revert EmptyProposalIdsArray();

        if (length_ != supportList_.length) revert ArrayLengthMismatch(length_, supportList_.length);

        if (length_ != reasonList_.length) revert ArrayLengthMismatch(length_, reasonList_.length);

        // In this governor, since the votingPeriod is 0, the snapshot for all active proposals is the previous epoch.
        weight_ = getVotes(voter_, _clock() - 1);

        for (uint256 index_; index_ < length_; ++index_) {
            _castVote(voter_, weight_, proposalIds_[index_], supportList_[index_], reasonList_[index_]);
        }
    }

    /**
     * @dev   Adds `account` to `list` at the Registrar.
     * @param list_    The key for some list.
     * @param account_ The address of some account to be added.
     */
    function _addToList(bytes32 list_, address account_) internal {
        IRegistrar(registrar).addToList(list_, account_);
    }

    /**
     * @dev   Cast `weight_` votes on a proposal with id `proposalId_` for `voter_`.
     * @param voter_      The address of the voter.
     * @param weight_     The number of votes the voter is casting.
     * @param proposalId_ The unique identifier of the proposal.
     * @param reason_     The reason for which the caller casts their vote, if any.
     * @param support_    The type of support to cast for the proposal.
     */
    function _castVote(
        address voter_,
        uint256 weight_,
        uint256 proposalId_,
        uint8 support_,
        string memory reason_
    ) internal override {
        ProposalState state_ = state(proposalId_);

        if (state_ != ProposalState.Active) revert ProposalInactive(state_);

        super._castVote(voter_, weight_, proposalId_, support_, reason_);

        uint16 currentEpoch_ = _clock();

        // NOTE: Will only get beyond this statement once per epoch as there is no way to vote on more proposals than
        //       exist in this epoch.
        if (++numberOfProposalsVotedOnAt[voter_][currentEpoch_] != numberOfProposalsAt[currentEpoch_]) return;

        emit HasVotedOnAllProposals(voter_, currentEpoch_);

        IPowerToken(voteToken).markParticipation(voter_);

        uint256 reward_ = (maxTotalZeroRewardPerActiveEpoch * weight_) / _getTotalSupply(currentEpoch_ - 1);

        if (reward_ == 0) return;

        IZeroToken(zeroToken).mint(voter_, reward_);
    }

    /**
     * @dev   Creates a new proposal with the given parameters.
     * @param proposalId_ The unique identifier of the proposal.
     * @param voteStart_  The epoch at which the proposal will start collecting votes.
     */
    function _createProposal(uint256 proposalId_, uint16 voteStart_) internal override {
        _proposals[proposalId_] = Proposal({
            voteStart: voteStart_,
            executed: false,
            proposer: msg.sender,
            thresholdRatio: 0,
            noWeight: 0,
            yesWeight: 0
        });
    }

    /**
     * @dev   Removes `account_` from `list_` at the Registrar.
     * @param list_    The key for some list.
     * @param account_ The address of some account to be removed.
     */
    function _removeFromList(bytes32 list_, address account_) internal {
        IRegistrar(registrar).removeFromList(list_, account_);
    }

    /**
     * @dev   Set cash token to `newCashToken_`.
     * @param newCashToken_ The address of the new cash token.
     */
    function _setCashToken(address newCashToken_) internal {
        if (newCashToken_ == address(0)) revert InvalidCashTokenAddress();

        emit CashTokenSet(cashToken = newCashToken_);
    }

    /**
     * @dev   Set proposal fee to `newProposalFee_`.
     * @param newProposalFee_ The new proposal fee.
     */
    function _setProposalFee(uint256 newProposalFee_) internal {
        emit ProposalFeeSet(proposalFee = newProposalFee_);
    }

    /**
     * @dev   Transfer `amount_` of `token_` to `to_`.
     * @param token_  The address of the token to transfer.
     * @param to_     The address of the recipient.
     * @param amount_ The amount of tokens to transfer.
     */
    function _transfer(address token_, address to_, uint256 amount_) internal {
        if (!ERC20Helper.transfer(token_, to_, amount_)) revert TransferFailed();
    }

    /* ============ Internal View/Pure Functions ============ */

    /**
     * @dev    Returns the number of clock values that must elapse before voting begins for a newly created proposal.
     * @return The voting delay.
     */
    function _votingDelay() internal view override returns (uint16) {
        return clock() % 2 == 1 ? 2 : 1; // Voting epochs are odd numbered
    }

    /**
     * @dev   All proposals target this contract itself, and must call one of the listed functions to be valid.
     * @param callData_ The call data to check.
     */
    function _revertIfInvalidCalldata(bytes memory callData_) internal pure override {
        bytes4 func_ = bytes4(callData_);
        uint256 length = callData_.length;

        if (
            !(func_ == this.addToList.selector && length == _SELECTOR_PLUS_2_ARGS) &&
            !(func_ == this.removeFromList.selector && length == _SELECTOR_PLUS_2_ARGS) &&
            !(func_ == this.removeFromAndAddToList.selector && length == _SELECTOR_PLUS_3_ARGS) &&
            !(func_ == this.setKey.selector && length == _SELECTOR_PLUS_2_ARGS) &&
            !(func_ == this.setProposalFee.selector && length == _SELECTOR_PLUS_1_ARGS)
        ) revert InvalidCallData();
    }

    /**
     * @dev    Returns the number of clock values between the vote start and vote end.
     * @return The voting period.
     */
    function _votingPeriod() internal pure override returns (uint16) {
        return 0;
    }
}
IRegistrar.sol 145 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import { IERC6372 } from "../abstract/interfaces/IERC6372.sol";

/**
 * @title  A book of record of TTG-specific contracts and arbitrary key-value pairs and lists.
 * @author M^0 Labs
 */
interface IRegistrar is IERC6372 {
    /* ============ Events ============ */

    /**
     * @notice Emitted when `account` is added to `list`.
     * @param  list    The key for the list.
     * @param  account The address of the added account.
     */
    event AddressAddedToList(bytes32 indexed list, address indexed account);

    /**
     * @notice Emitted when `account` is removed from `list`.
     * @param  list    The key for the list.
     * @param  account The address of the removed account.
     */
    event AddressRemovedFromList(bytes32 indexed list, address indexed account);

    /**
     * @notice Emitted when `key` is set to `value`.
     * @param  key   The key.
     * @param  value The value.
     */
    event KeySet(bytes32 indexed key, bytes32 indexed value);

    /* ============ Custom Errors ============ */

    /// @notice Revert message when the Emergency Governor Deployer retrieved in the constructor is address(0).
    error InvalidEmergencyGovernorDeployerAddress();

    /// @notice Revert message when the Power Token Deployer retrieved in the constructor is address(0).
    error InvalidPowerTokenDeployerAddress();

    /// @notice Revert message when the Standard Governor Deployer retrieved in the constructor is address(0).
    error InvalidStandardGovernorDeployerAddress();

    /// @notice Revert message when the Vault retrieved in the constructor is address(0).
    error InvalidVaultAddress();

    /// @notice Revert message when the Vote Token retrieved in the constructor is address(0).
    error InvalidVoteTokenAddress();

    /// @notice Revert message when the Zero Governor specified in the constructor is address(0).
    error InvalidZeroGovernorAddress();

    /// @notice Revert message when the caller is not the Standard Governor nor the Emergency Governor.
    error NotStandardOrEmergencyGovernor();

    /* ============ Interactive Functions ============ */

    /**
     * @notice Adds `account` to `list`.
     * @param  list    The key for some list.
     * @param  account The address of some account to be added.
     */
    function addToList(bytes32 list, address account) external;

    /**
     * @notice Removes `account` from `list`.
     * @param  list    The key for some list.
     * @param  account The address of some account to be removed.
     */
    function removeFromList(bytes32 list, address account) external;

    /**
     * @notice Sets `key` to `value`.
     * @param  key   Some key.
     * @param  value Some value.
     */
    function setKey(bytes32 key, bytes32 value) external;

    /* ============ View/Pure Functions ============ */

    /// @notice Returns the starting timestamp of Epoch 1.
    function clockStartingTimestamp() external pure returns (uint256);

    /// @notice Returns the period/duration, in seconds, of an epoch.
    function clockPeriod() external pure returns (uint256);

    /**
     * @notice Returns the value of `key`.
     * @param  key Some key.
     * @return Some value.
     */
    function get(bytes32 key) external view returns (bytes32);

    /**
     * @notice Returns the values of `keys` respectively.
     * @param  keys Some keys.
     * @return Some values.
     */
    function get(bytes32[] calldata keys) external view returns (bytes32[] memory);

    /**
     * @notice Returns whether `list` contains `account`.
     * @param  list    The key for some list.
     * @param  account The address of some account.
     * @return Whether `list` contains `account`.
     */
    function listContains(bytes32 list, address account) external view returns (bool);

    /**
     * @notice Returns whether `list` contains all specified accounts.
     * @param  list     The key for some list.
     * @param  accounts An array of addressed of some accounts.
     * @return Whether `list` contains all specified accounts.
     */
    function listContains(bytes32 list, address[] calldata accounts) external view returns (bool);

    /// @notice Returns the address of the Emergency Governor.
    function emergencyGovernor() external view returns (address);

    /// @notice Returns the address of the Emergency Governor Deployer.
    function emergencyGovernorDeployer() external view returns (address);

    /// @notice Returns the address of the Power Token.
    function powerToken() external view returns (address);

    /// @notice Returns the address of the Power Token Deployer.
    function powerTokenDeployer() external view returns (address);

    /// @notice Returns the address of the Standard Governor.
    function standardGovernor() external view returns (address);

    /// @notice Returns the address of the Standard Governor Deployer.
    function standardGovernorDeployer() external view returns (address);

    /// @notice Returns the address of the Vault.
    function vault() external view returns (address);

    /// @notice Returns the address of the Zero Governor.
    function zeroGovernor() external view returns (address);

    /// @notice Returns the address of the Zero Token.
    function zeroToken() external view returns (address);
}
IZeroToken.sol 69 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import { IEpochBasedVoteToken } from "../abstract/interfaces/IEpochBasedVoteToken.sol";

/**
 * @title  An instance of an EpochBasedVoteToken delegating minting control to a Standard Governor,
 *         and enabling range queries for past balances, voting powers, delegations, and  total supplies.
 * @author M^0 Labs
 */
interface IZeroToken is IEpochBasedVoteToken {
    /* ============ Custom Errors ============ */

    /**
     * @notice Revert message when the length of some accounts array does not equal the length of some balances array.
     * @param  accountsLength The length of the accounts array.
     * @param  balancesLength The length of the balances array.
     */
    error ArrayLengthMismatch(uint256 accountsLength, uint256 balancesLength);

    /// @notice Revert message when the Standard Governor Deployer specified in the constructor is address(0).
    error InvalidStandardGovernorDeployerAddress();

    /// @notice Revert message when the caller is not the Standard Governor.
    error NotStandardGovernor();

    /// @notice Revert message when the start of an inclusive range query is larger than the end.
    error StartEpochAfterEndEpoch();

    /* ============ Interactive Functions ============ */

    /**
     * @notice Mints `amount` token to `recipient`.
     * @param  recipient The address of the account receiving minted token.
     * @param  amount    The amount of token to mint.
     */
    function mint(address recipient, uint256 amount) external;

    /* ============ View/Pure Functions ============ */

    /**
     * @notice Returns an array of token balances of `account`
     *         between `startEpoch` and `endEpoch` past inclusive clocks.
     * @param  account    The address of some account.
     * @param  startEpoch The starting epoch number as a clock value.
     * @param  endEpoch   The ending epoch number as a clock value.
     * @return An array of token balances, each relating to an epoch in the inclusive range.
     */
    function pastBalancesOf(
        address account,
        uint256 startEpoch,
        uint256 endEpoch
    ) external view returns (uint256[] memory);

    /**
     * @notice Returns an array of total token supplies between `startEpoch` and `endEpoch` clocks inclusively.
     * @param  startEpoch The starting epoch number as a clock value.
     * @param  endEpoch   The ending epoch number as a clock value.
     * @return An array of total supplies, each relating to an epoch in the inclusive range.
     */
    function pastTotalSupplies(uint256 startEpoch, uint256 endEpoch) external view returns (uint256[] memory);

    /// @notice Returns the address of the Standard Governor.
    function standardGovernor() external view returns (address);

    /// @notice Returns the address of the Standard Governor Deployer.
    function standardGovernorDeployer() external view returns (address);
}
BatchGovernor.sol 764 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import { ERC712Extended } from "../../lib/common/src/ERC712Extended.sol";

import { PureEpochs } from "../libs/PureEpochs.sol";

import { IBatchGovernor } from "./interfaces/IBatchGovernor.sol";
import { IEpochBasedVoteToken } from "./interfaces/IEpochBasedVoteToken.sol";
import { IERC6372 } from "./interfaces/IERC6372.sol";
import { IGovernor } from "./interfaces/IGovernor.sol";

/**
 * @title  Extension for Governor with specialized strict proposal parameters, vote batching, and an epoch clock.
 * @author M^0 Labs
 */
abstract contract BatchGovernor is IBatchGovernor, ERC712Extended {
    /* ============ Structs ============ */

    /**
     * @notice Proposal struct for storing all relevant proposal information.
     * @param  voteStart      The epoch at which voting begins, inclusively.
     * @param  executed       Whether or not the proposal has been executed.
     * @param  proposer       The address of the proposer.
     * @param  thresholdRatio The ratio of yes votes required for a proposal to meet quorum and succeed.
     * @param  noWeight       The total number of votes against the proposal.
     * @param  yesWeight      The total number of votes for the proposal.
     */
    struct Proposal {
        // 1st slot
        uint16 voteStart;
        bool executed;
        address proposer;
        uint16 thresholdRatio;
        // 2nd slot
        uint256 noWeight;
        // 3rd slot
        uint256 yesWeight;
    }

    /* ============ Variables ============ */

    /// @dev Length constant for calldata with no argument.
    uint256 internal constant _SELECTOR_PLUS_0_ARGS = 4;

    /// @dev Length constant for calldata with one argument.
    uint256 internal constant _SELECTOR_PLUS_1_ARGS = 36;

    /// @dev Length constant for calldata with two arguments.
    uint256 internal constant _SELECTOR_PLUS_2_ARGS = 68;

    /// @dev Length constant for calldata with three arguments.
    uint256 internal constant _SELECTOR_PLUS_3_ARGS = 100;

    // keccak256("Ballot(uint256 proposalId,uint8 support)")
    /// @inheritdoc IGovernor
    bytes32 public constant BALLOT_TYPEHASH = 0x150214d74d59b7d1e90c73fc22ef3d991dd0a76b046543d4d80ab92d2a50328f;

    // keccak256("BallotWithReason(uint256 proposalId,uint8 support,string reason)")
    /// @inheritdoc IGovernor
    bytes32 public constant BALLOT_WITH_REASON_TYPEHASH =
        0x7949bd92105c02f48ca245aa185f4a7a4d7185641d59b186ac64abeb44964f0c;

    // keccak256("Ballots(uint256[] proposalIds,uint8[] supportList)")
    /// @inheritdoc IBatchGovernor
    bytes32 public constant BALLOTS_TYPEHASH = 0x9a121fc10d6025acfc09275f9709796b68831733b5bbac0d510d13f85b1b730f;

    // keccak256("BallotsWithReason(uint256[] proposalIds,uint8[] supportList,string[] reasonList)")
    /// @inheritdoc IBatchGovernor
    bytes32 public constant BALLOTS_WITH_REASON_TYPEHASH =
        0xa891f76027ef63a24501b9dd3b0c779b49ad26d2328e9d423640209d1ad4fcc4;

    /// @inheritdoc IBatchGovernor
    address public immutable voteToken;

    /// @dev The list of proposals per proposal ID.
    mapping(uint256 proposalId => Proposal proposal) internal _proposals;

    /// @inheritdoc IGovernor
    mapping(uint256 proposalId => mapping(address voter => bool hasVoted)) public hasVoted;

    /* ============ Modifiers ============ */

    /// @dev Reverts if the caller is not the contract itself.
    modifier onlySelf() {
        _revertIfNotSelf();
        _;
    }

    /* ============ Constructor ============ */

    /**
     * @notice Construct a new BatchGovernor contract.
     * @param  name_      The name of the contract. Used to compute EIP712 domain separator.
     * @param  voteToken_ The address of the token used to vote.
     */
    constructor(string memory name_, address voteToken_) ERC712Extended(name_) {
        if ((voteToken = voteToken_) == address(0)) revert InvalidVoteTokenAddress();
    }

    /* ============ Interactive Functions ============ */

    /// @inheritdoc IGovernor
    function castVote(uint256 proposalId_, uint8 support_) external returns (uint256 weight_) {
        return _castVote(msg.sender, proposalId_, support_, "");
    }

    /// @inheritdoc IBatchGovernor
    function castVotes(
        uint256[] calldata proposalIds_,
        uint8[] calldata supportList_
    ) external returns (uint256 weight_) {
        return _castVotes(msg.sender, proposalIds_, supportList_, new string[](proposalIds_.length));
    }

    /// @inheritdoc IGovernor
    function castVoteWithReason(
        uint256 proposalId_,
        uint8 support_,
        string calldata reason_
    ) external returns (uint256 weight_) {
        return _castVote(msg.sender, proposalId_, support_, reason_);
    }

    /// @inheritdoc IBatchGovernor
    function castVotesWithReason(
        uint256[] calldata proposalIds_,
        uint8[] calldata supportList_,
        string[] calldata reasonList_
    ) external returns (uint256 weight_) {
        return _castVotes(msg.sender, proposalIds_, supportList_, reasonList_);
    }

    /// @inheritdoc IGovernor
    function castVoteBySig(
        uint256 proposalId_,
        uint8 support_,
        uint8 v_,
        bytes32 r_,
        bytes32 s_
    ) external returns (uint256 weight_) {
        return
            _castVote(
                _getSignerAndRevertIfInvalidSignature(_getBallotDigest(proposalId_, support_), v_, r_, s_),
                proposalId_,
                support_,
                ""
            );
    }

    /// @inheritdoc IGovernor
    function castVoteBySig(
        address voter_,
        uint256 proposalId_,
        uint8 support_,
        bytes memory signature_
    ) external returns (uint256 weight_) {
        _revertIfInvalidSignature(voter_, _getBallotDigest(proposalId_, support_), signature_);

        return _castVote(voter_, proposalId_, support_, "");
    }

    /// @inheritdoc IBatchGovernor
    function castVotesBySig(
        uint256[] calldata proposalIds_,
        uint8[] calldata supportList_,
        uint8 v_,
        bytes32 r_,
        bytes32 s_
    ) external returns (uint256 weight_) {
        return
            _castVotes(
                _getSignerAndRevertIfInvalidSignature(
                    _getBallotsDigest(
                        keccak256(abi.encodePacked(proposalIds_)),
                        keccak256(abi.encodePacked(supportList_))
                    ),
                    v_,
                    r_,
                    s_
                ),
                proposalIds_,
                supportList_,
                new string[](proposalIds_.length)
            );
    }

    /// @inheritdoc IBatchGovernor
    function castVotesBySig(
        address voter_,
        uint256[] calldata proposalIds_,
        uint8[] calldata supportList_,
        bytes memory signature_
    ) external returns (uint256 weight_) {
        _revertIfInvalidSignature(
            voter_,
            _getBallotsDigest(keccak256(abi.encodePacked(proposalIds_)), keccak256(abi.encodePacked(supportList_))),
            signature_
        );

        return _castVotes(voter_, proposalIds_, supportList_, new string[](proposalIds_.length));
    }

    /// @inheritdoc IGovernor
    function castVoteWithReasonBySig(
        uint256 proposalId_,
        uint8 support_,
        string calldata reason_,
        uint8 v_,
        bytes32 r_,
        bytes32 s_
    ) external returns (uint256 weight_) {
        return
            _castVote(
                _getSignerAndRevertIfInvalidSignature(
                    _getBallotWithReasonDigest(proposalId_, support_, reason_),
                    v_,
                    r_,
                    s_
                ),
                proposalId_,
                support_,
                reason_
            );
    }

    /// @inheritdoc IGovernor
    function castVoteWithReasonBySig(
        address voter_,
        uint256 proposalId_,
        uint8 support_,
        string calldata reason_,
        bytes memory signature_
    ) external returns (uint256 weight_) {
        _revertIfInvalidSignature(voter_, _getBallotWithReasonDigest(proposalId_, support_, reason_), signature_);

        return _castVote(voter_, proposalId_, support_, reason_);
    }

    /// @inheritdoc IBatchGovernor
    function castVotesWithReasonBySig(
        uint256[] calldata proposalIds_,
        uint8[] calldata supportList_,
        string[] calldata reasonList_,
        uint8 v_,
        bytes32 r_,
        bytes32 s_
    ) external returns (uint256 weight_) {
        return
            _castVotes(
                _getSignerAndRevertIfInvalidSignature(
                    _getBallotsWithReasonDigest(
                        keccak256(abi.encodePacked(proposalIds_)),
                        keccak256(abi.encodePacked(supportList_)),
                        _getReasonListHash(reasonList_)
                    ),
                    v_,
                    r_,
                    s_
                ),
                proposalIds_,
                supportList_,
                reasonList_
            );
    }

    /// @inheritdoc IBatchGovernor
    function castVotesWithReasonBySig(
        address voter_,
        uint256[] calldata proposalIds_,
        uint8[] calldata supportList_,
        string[] calldata reasonList_,
        bytes memory signature_
    ) external returns (uint256 weight_) {
        _revertIfInvalidSignature(
            voter_,
            _getBallotsWithReasonDigest(
                keccak256(abi.encodePacked(proposalIds_)),
                keccak256(abi.encodePacked(supportList_)),
                _getReasonListHash(reasonList_)
            ),
            signature_
        );

        return _castVotes(voter_, proposalIds_, supportList_, reasonList_);
    }

    /* ============ View/Pure Functions ============ */

    /// @inheritdoc IGovernor
    function hashProposal(
        address[] memory,
        uint256[] memory,
        bytes[] memory callDatas_,
        bytes32
    ) external view returns (uint256) {
        return _hashProposal(callDatas_[0]);
    }

    /// @inheritdoc IBatchGovernor
    function hashProposal(bytes memory callData_) external view returns (uint256) {
        return _hashProposal(callData_);
    }

    /// @inheritdoc IGovernor
    function name() external view returns (string memory) {
        return _name;
    }

    /// @inheritdoc IGovernor
    function proposalDeadline(uint256 proposalId_) external view returns (uint256) {
        return _getVoteEnd(_proposals[proposalId_].voteStart);
    }

    /// @inheritdoc IGovernor
    function proposalProposer(uint256 proposalId_) external view returns (address) {
        return _proposals[proposalId_].proposer;
    }

    /// @inheritdoc IGovernor
    function proposalSnapshot(uint256 proposalId_) external view returns (uint256) {
        return _proposals[proposalId_].voteStart - 1;
    }

    /// @inheritdoc IGovernor
    function proposalVotes(uint256 proposalId_) external view returns (uint256, uint256, uint256) {
        Proposal storage proposal_ = _proposals[proposalId_];

        return (proposal_.noWeight, proposal_.yesWeight, 0);
    }

    /// @inheritdoc IGovernor
    function token() external view returns (address) {
        return voteToken;
    }

    /// @inheritdoc IERC6372
    function CLOCK_MODE() external pure returns (string memory) {
        return PureEpochs.clockMode();
    }

    /// @inheritdoc IGovernor
    function proposalThreshold() external pure returns (uint256) {
        return 0;
    }

    /// @inheritdoc IERC6372
    function clock() public view returns (uint48) {
        return _clock();
    }

    /// @inheritdoc IBatchGovernor
    function getBallotDigest(uint256 proposalId_, uint8 support_) external view returns (bytes32) {
        return _getBallotDigest(proposalId_, support_);
    }

    /// @inheritdoc IBatchGovernor
    function getBallotsDigest(
        uint256[] calldata proposalIds_,
        uint8[] calldata supportList_
    ) external view returns (bytes32) {
        return _getBallotsDigest(keccak256(abi.encodePacked(proposalIds_)), keccak256(abi.encodePacked(supportList_)));
    }

    /// @inheritdoc IBatchGovernor
    function getBallotWithReasonDigest(
        uint256 proposalId_,
        uint8 support_,
        string calldata reason_
    ) external view returns (bytes32) {
        return _getBallotWithReasonDigest(proposalId_, support_, reason_);
    }

    /// @inheritdoc IBatchGovernor
    function getBallotsWithReasonDigest(
        uint256[] calldata proposalIds_,
        uint8[] calldata supportList_,
        string[] calldata reasonList_
    ) external view returns (bytes32) {
        return
            _getBallotsWithReasonDigest(
                keccak256(abi.encodePacked(proposalIds_)),
                keccak256(abi.encodePacked(supportList_)),
                _getReasonListHash(reasonList_)
            );
    }

    /// @inheritdoc IGovernor
    function getVotes(address account_, uint256 timepoint_) public view returns (uint256) {
        return IEpochBasedVoteToken(voteToken).getPastVotes(account_, timepoint_);
    }

    /// @inheritdoc IGovernor
    function state(uint256 proposalId_) public view virtual returns (ProposalState);

    /// @inheritdoc IGovernor
    function votingDelay() public view returns (uint256) {
        return _votingDelay();
    }

    /// @inheritdoc IGovernor
    function votingPeriod() public view returns (uint256) {
        return _votingPeriod();
    }

    /* ============ Internal Interactive Functions ============ */

    /**
     * @dev    Cast votes on several proposals for `voter_`.
     * @param  voter_       The address of the voter.
     * @param  proposalIds_ The list of unique proposal IDs being voted on.
     * @param  supportList_ The list of support type per proposal IDs to cast.
     * @param  reasonList_  The list of reason per proposal IDs to cast.
     * @return weight_      The number of votes the voter cast on each proposal.
     */
    function _castVotes(
        address voter_,
        uint256[] calldata proposalIds_,
        uint8[] calldata supportList_,
        string[] memory reasonList_
    ) internal virtual returns (uint256 weight_) {
        uint256 length_ = proposalIds_.length;

        if (length_ == 0) revert EmptyProposalIdsArray();

        if (length_ != supportList_.length) revert ArrayLengthMismatch(length_, supportList_.length);

        if (length_ != reasonList_.length) revert ArrayLengthMismatch(length_, reasonList_.length);

        for (uint256 index_; index_ < length_; ++index_) {
            weight_ = _castVote(voter_, proposalIds_[index_], supportList_[index_], reasonList_[index_]);
        }
    }

    /**
     * @dev    Cast votes on proposal for `voter_`.
     * @param  voter_      The address of the voter.
     * @param  proposalId_ The unique identifier of the proposal.
     * @param  support_    The type of support to cast for the proposal.
     * @param  reason_     The reason for which the caller casts their vote, if any.
     * @return weight_     The number of votes cast.
     */
    function _castVote(
        address voter_,
        uint256 proposalId_,
        uint8 support_,
        string memory reason_
    ) internal returns (uint256 weight_) {
        ProposalState state_ = state(proposalId_);

        if (state_ != ProposalState.Active) revert ProposalInactive(state_);

        unchecked {
            // NOTE: Can be done unchecked since `voteStart` is always greater than 0.
            weight_ = getVotes(voter_, _proposals[proposalId_].voteStart - 1);
        }

        _castVote(voter_, weight_, proposalId_, support_, reason_);
    }

    /**
     * @dev   Cast `weight_` votes on a proposal with id `proposalId_` for `voter_`.
     * @param voter_      The address of the voter.
     * @param weight_     The number of votes the voter is casting.
     * @param proposalId_ The unique identifier of the proposal.
     * @param support_    The type of support to cast for the proposal.
     * @param reason_     The reason for which the caller casts their vote, if any.
     */
    function _castVote(
        address voter_,
        uint256 weight_,
        uint256 proposalId_,
        uint8 support_,
        string memory reason_
    ) internal virtual {
        if (weight_ == 0) revert ZeroVotingPower();
        if (hasVoted[proposalId_][voter_]) revert AlreadyVoted();

        hasVoted[proposalId_][voter_] = true;

        unchecked {
            // NOTE: Can be done unchecked since total supply is less than `type(uint256).max`.
            if (VoteType(support_) == VoteType.No) {
                _proposals[proposalId_].noWeight += weight_;
            } else {
                _proposals[proposalId_].yesWeight += weight_;
            }
        }

        emit VoteCast(voter_, proposalId_, support_, weight_, reason_);
    }

    /**
     * @dev   Creates a new proposal with the given parameters.
     * @param proposalId_ The unique identifier of the proposal.
     * @param voteStart_  The epoch at which the proposal will start collecting votes.
     */
    function _createProposal(uint256 proposalId_, uint16 voteStart_) internal virtual;

    /**
     * @dev    Executes a proposal given its call data and voteStart (which are unique to it).
     * @param  callData_   The call data to execute.
     * @param  voteStart_  The epoch at which the proposal started collecting votes.
     * @return proposalId_ The unique identifier of the proposal that matched the criteria.
     */
    function _execute(bytes memory callData_, uint16 voteStart_) internal virtual returns (uint256 proposalId_) {
        proposalId_ = _hashProposal(callData_, voteStart_);

        Proposal storage proposal_ = _proposals[proposalId_];

        if (proposal_.voteStart != voteStart_) return 0;

        if (state(proposalId_) != ProposalState.Succeeded) return 0;

        proposal_.executed = true;

        emit ProposalExecuted(proposalId_);

        // solhint-disable-next-line avoid-low-level-calls
        (bool success_, bytes memory data_) = address(this).call(callData_);

        if (!success_) revert ExecutionFailed(data_);
    }

    /**
     * @dev    Internal handler for making proposals.
     * @param  targets_     An array of addresses that will be called upon the execution.
     * @param  values_      An array of ETH amounts that will be sent to each respective target upon execution.
     * @param  callDatas_   An array of call data used to call each respective target upon execution.
     * @param  description_ The string of the description of the proposal.
     * @return proposalId_  The unique identifier of the proposal.
     * @return voteStart_   The timepoint at which voting on the proposal begins, inclusively.
     */
    function _propose(
        address[] memory targets_,
        uint256[] memory values_,
        bytes[] memory callDatas_,
        string memory description_
    ) internal returns (uint256 proposalId_, uint16 voteStart_) {
        _revertIfInvalidProposal(targets_, values_, callDatas_);

        voteStart_ = _voteStart();

        proposalId_ = _hashProposal(callDatas_[0], voteStart_);

        if (_proposals[proposalId_].voteStart != 0) revert ProposalExists();

        _createProposal(proposalId_, voteStart_);

        emit ProposalCreated(
            proposalId_,
            msg.sender,
            targets_,
            values_,
            new string[](targets_.length),
            callDatas_,
            voteStart_,
            _getVoteEnd(voteStart_),
            description_
        );
    }

    /**
     * @dev    This function tries to execute a proposal based on the call data and a range of possible vote starts.
     *         This is needed due to the fact that proposalId's are generated based on the call data and vote start
     *         time, and so an executed function will need this in order to attempt to find and execute a proposal given
     *         a known range of possible vote start times which depends on how the inheriting implementation
     *         determines the vote start time and expiry of proposals based on the time of the proposal creation.
     * @param  callData_          An array of call data used to call each respective target upon execution.
     * @param  latestVoteStart_   The most recent vote start to use in attempting to search for the proposal.
     * @param  earliestVoteStart_ The least recent vote start to use in attempting to search for the proposal.
     * @return proposalId_        The unique identifier of the most recent proposal that matched the criteria.
     */
    function _tryExecute(
        bytes memory callData_,
        uint16 latestVoteStart_,
        uint16 earliestVoteStart_
    ) internal returns (uint256 proposalId_) {
        if (msg.value != 0) revert InvalidValue();

        // Non-existent proposals have a default vote start of 0
        if (earliestVoteStart_ == 0) revert InvalidVoteStart();

        while (latestVoteStart_ >= earliestVoteStart_) {
            // `proposalId_` will be 0 if no proposal exists for `callData_` and `latestVoteStart_`, or if the proposal
            // is not in  a `Succeeded` state. It will be executed otherwise. (see `_execute`)
            unchecked {
                proposalId_ = _execute(callData_, latestVoteStart_--);
            }

            // If the `proposalId_` is not 0, then a proposal matching `callData_` and `latestVoteStart_` was found, in
            // a Succeeded state, and was executed, so return it.
            if (proposalId_ != 0) return proposalId_;
        }

        revert ProposalCannotBeExecuted(); // No proposal matching the criteria was found/executed.
    }

    /* ============ Internal View/Pure Functions ============ */

    /// @dev Returns the current timepoint according to the mode the contract is operating on.
    function _clock() internal view returns (uint16) {
        return PureEpochs.currentEpoch();
    }

    /**
     * @dev    Returns the vote token's total supply at `timepoint_`.
     * @param  timepoint_ The clock value at which to query the vote token's total supply.
     * @return The vote token's total supply at the `timepoint` clock value.
     */
    function _getTotalSupply(uint16 timepoint_) internal view returns (uint256) {
        return IEpochBasedVoteToken(voteToken).pastTotalSupply(timepoint_);
    }

    /// @dev Returns the timepoint at which voting would start for a proposal created in current timepoint.
    function _voteStart() internal view returns (uint16) {
        unchecked {
            return _clock() + _votingDelay();
        }
    }

    /**
     * @dev    Returns the timepoint at which voting would end given a timepoint at which voting would start.
     * @param  voteStart_ The clock value at which voting would start, inclusively.
     * @return The clock value at which voting would end, inclusively.
     */
    function _getVoteEnd(uint16 voteStart_) internal view returns (uint16) {
        unchecked {
            return voteStart_ + _votingPeriod();
        }
    }

    /**
     * @dev    Returns the ballot digest to be signed, via EIP-712, given an internal digest (i.e. hash struct).
     * @param  proposalId_ The unique proposal ID being voted on.
     * @param  support_    The type of support to cast for the proposal.
     * @return The digest to be signed.
     */
    function _getBallotDigest(uint256 proposalId_, uint8 support_) internal view returns (bytes32) {
        return _getDigest(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId_, support_)));
    }

    /**
     * @dev    Returns the ballots digest to be signed, via EIP-712, given an internal digest (i.e. hash struct).
     * @param  proposalIdsHash_ The hash of the list of unique proposal IDs being voted on.
     * @param  supportListHash_ The hash of the list of support type per proposal IDs to cast.
     * @return The digest to be signed.
     */
    function _getBallotsDigest(bytes32 proposalIdsHash_, bytes32 supportListHash_) internal view returns (bytes32) {
        return _getDigest(keccak256(abi.encode(BALLOTS_TYPEHASH, proposalIdsHash_, supportListHash_)));
    }

    /**
     * @dev    Returns the ballot with reason digest to be signed, via EIP-712,
     *         given an internal digest (i.e. hash struct).
     * @param  proposalId_ The unique proposal ID being voted on.
     * @param  support_    The type of support to cast for the proposal.
     * @param  reason_     The reason for which the caller casts their vote, if any.
     * @return The digest to be signed.
     */
    function _getBallotWithReasonDigest(
        uint256 proposalId_,
        uint8 support_,
        string calldata reason_
    ) internal view returns (bytes32) {
        return
            _getDigest(
                keccak256(abi.encode(BALLOT_WITH_REASON_TYPEHASH, proposalId_, support_, keccak256(bytes(reason_))))
            );
    }

    /**
     * @dev    Returns the ballots digest to be signed, via EIP-712, given an internal digest (i.e. hash struct).
     * @param  proposalIdsHash_ The hash of the list of unique proposal IDs being voted on.
     * @param  supportListHash_ The hash of the list of support type per proposal IDs to cast.
     * @param  reasonListHash_  The hash of the list of reason per proposal IDs to cast.
     * @return The digest to be signed.
     */
    function _getBallotsWithReasonDigest(
        bytes32 proposalIdsHash_,
        bytes32 supportListHash_,
        bytes32 reasonListHash_
    ) internal view returns (bytes32) {
        return
            _getDigest(
                keccak256(abi.encode(BALLOTS_WITH_REASON_TYPEHASH, proposalIdsHash_, supportListHash_, reasonListHash_))
            );
    }

    /**
     * @dev    Returns the hash of the reason list to be used in the ballots digest.
     * @param  reasonList_ The list of reasons to hash.
     * @return The hash of the reason list.
     */
    function _getReasonListHash(string[] calldata reasonList_) internal pure returns (bytes32) {
        uint256 reasonListLength_ = reasonList_.length;
        bytes32[] memory reasonListBytes_ = new bytes32[](reasonListLength_);

        for (uint256 index_; index_ < reasonListLength_; ++index_) {
            reasonListBytes_[index_] = keccak256(bytes(reasonList_[index_]));
        }

        return keccak256(abi.encodePacked(reasonListBytes_));
    }

    /**
     * @dev    Returns the unique identifier for the proposal if it were created at this exact moment.
     * @param  callData_ The single call data used to call this governor upon execution of a proposal.
     * @return The unique identifier for the proposal.
     */
    function _hashProposal(bytes memory callData_) internal view returns (uint256) {
        return _hashProposal(callData_, _voteStart());
    }

    /**
     * @dev    Returns the unique identifier for the proposal if it were to have a given vote start timepoint.
     * @param  callData_  The single call data used to call this governor upon execution of a proposal.
     * @param  voteStart_ The clock value at which voting would start, inclusively.
     * @return The unique identifier for the proposal.
     */
    function _hashProposal(bytes memory callData_, uint16 voteStart_) internal view returns (uint256) {
        return uint256(keccak256(abi.encode(callData_, voteStart_, address(this))));
    }

    /// @dev Reverts if the caller is not the contract itself.
    function _revertIfNotSelf() internal view {
        if (msg.sender != address(this)) revert NotSelf();
    }

    /// @dev Returns the number of clock values that must elapse before voting begins for a newly created proposal.
    function _votingDelay() internal view virtual returns (uint16);

    /// @dev Returns the number of clock values between the vote start and vote end.
    function _votingPeriod() internal view virtual returns (uint16);

    /**
     * @dev   All proposals target this contract itself, and must call one of the listed functions to be valid.
     * @param callData_ The call data to check.
     */
    function _revertIfInvalidCalldata(bytes memory callData_) internal pure virtual;

    /**
     * @dev   Reverts if the proposal arguments are invalid.
     * @param targets_   An array of addresses that will be called upon the execution.
     * @param values_    An array of ETH amounts that will be sent to each respective target upon execution.
     * @param callDatas_ An array of call data used to call each respective target upon execution.
     */
    function _revertIfInvalidProposal(
        address[] memory targets_,
        uint256[] memory values_,
        bytes[] memory callDatas_
    ) internal view {
        if (targets_.length != 1) revert InvalidTargetsLength();
        if (targets_[0] != address(this)) revert InvalidTarget();

        if (values_.length != 1) revert InvalidValuesLength();
        if (values_[0] != 0) revert InvalidValue();

        if (callDatas_.length != 1) revert InvalidCallDatasLength();

        _revertIfInvalidCalldata(callDatas_[0]);
    }
}
IPowerToken.sol 157 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import { IEpochBasedInflationaryVoteToken } from "../abstract/interfaces/IEpochBasedInflationaryVoteToken.sol";

/**
 * @title  An instance of an EpochBasedInflationaryVoteToken delegating control to a Standard Governor,
 *         and enabling auctioning of the unowned inflated supply.
 * @author M^0 Labs
 */
interface IPowerToken is IEpochBasedInflationaryVoteToken {
    /* ============ Events ============ */

    /**
     * @notice Emitted when `buyer` has bought `amount` tokens from the auction, as a total cash token value of `cost`.
     * @param  buyer  The address of account that bought tokens from the auction.
     * @param  amount The amount of tokens bought.
     * @param  cost   The total cash token cost of the purchase.
     */
    event Buy(address indexed buyer, uint240 amount, uint256 cost);

    /**
     * @notice Emitted when the cash token is queued to become `nextCashToken` at the start of epoch `startingEpoch`.
     * @param  startingEpoch The epoch number as a clock value in which the new cash token takes effect.
     * @param  nextCashToken The address of the cash token taking effect at `startingEpoch`.
     */
    event NextCashTokenSet(uint16 indexed startingEpoch, address indexed nextCashToken);

    /**
     * @notice Emitted in the constructor at deployment.
     * @param  tagline The tagline of the contract.
     */
    event Tagline(string tagline);

    /**
     * @notice Emitted when the target supply is queued to become `targetSupply` at the start of epoch `targetEpoch`.
     * @param  targetEpoch  The epoch number as a clock value in which the new target supply takes effect.
     * @param  targetSupply The target supply taking effect at `startingEpoch`.
     */
    event TargetSupplyInflated(uint16 indexed targetEpoch, uint240 indexed targetSupply);

    /* ============ Custom Errors ============ */

    /// @notice Revert message when the total supply of the bootstrap token is larger than `type(uint240).max`.
    error BootstrapSupplyTooLarge();

    /// @notice Revert message when the total supply of the bootstrap token is 0.
    error BootstrapSupplyZero();

    /**
     * @notice Revert message when the amount available for auction is less than the minimum requested to buy.
     * @param  amountToAuction    The amount available for auction.
     * @param  minAmountRequested The minimum amount that was requested to buy.
     */
    error InsufficientAuctionSupply(uint240 amountToAuction, uint240 minAmountRequested);

    /// @notice Revert message when the Bootstrap Token specified in the constructor is address(0).
    error InvalidBootstrapTokenAddress();

    /// @notice Revert message when the Cash Token specified in the constructor is address(0).
    error InvalidCashTokenAddress();

    /// @notice Revert message when the Standard Governor specified in the constructor is address(0).
    error InvalidStandardGovernorAddress();

    /// @notice Revert message when the Vault specified in the constructor is address(0).
    error InvalidVaultAddress();

    /// @notice Revert message when the caller is not the Standard Governor.
    error NotStandardGovernor();

    /// @notice Revert message when a token transferFrom fails.
    error TransferFromFailed();

    /// @notice Revert message when auction calculations use zero as denominator.
    error DivisionByZero();

    /// @notice Revert message when the buy order has expired using epoch-based expiration clock.
    error ExpiredBuyOrder();

    /// @notice Revert message when the buy order has zero maximum and minimum amounts.
    error ZeroPurchaseAmount();

    /**
     * @notice Revert message when trying to sync to an epoch that is before the bootstrap epoch.
     * @param  bootstrapEpoch The bootstrap epoch.
     * @param  epoch          The epoch attempting to be synced to, not inclusively.
     */
    error SyncBeforeBootstrap(uint16 bootstrapEpoch, uint16 epoch);

    /* ============ Interactive Functions ============ */

    /**
     * @notice Allows a caller to buy `amount` tokens from the auction.
     * @param  minAmount   The minimum amount of tokens the caller is interested in buying.
     * @param  maxAmount   The maximum amount of tokens the caller is interested in buying.
     * @param  destination The address of the account to send the bought tokens.
     * @param  expiryEpoch The epoch number at the end of which the buy order expires.
     * @return amount      The amount of token bought.
     * @return cost        The total cash token cost of the purchase.
     */
    function buy(
        uint256 minAmount,
        uint256 maxAmount,
        address destination,
        uint16 expiryEpoch
    ) external returns (uint240 amount, uint256 cost);

    /// @notice Marks the next voting epoch as targeted for inflation.
    function markNextVotingEpochAsActive() external;

    /**
     * @notice Marks `delegatee` as having participated in the current epoch, thus receiving voting power inflation.
     * @param  delegatee The address of the account being marked as having participated.
     */
    function markParticipation(address delegatee) external;

    /**
     * @notice Queues the cash token that will take effect from the next epoch onward.
     * @param  nextCashToken The address of the cash token taking effect from the next epoch onward.
     */
    function setNextCashToken(address nextCashToken) external;

    /* ============ View/Pure Functions ============ */

    /// @notice Returns the amount of tokens that can be bought in the auction.
    function amountToAuction() external view returns (uint240);

    /// @notice Returns the epoch from which token balances and voting powers are bootstrapped.
    function bootstrapEpoch() external view returns (uint16);

    /// @notice Returns the address of the token in which token balances and voting powers are bootstrapped.
    function bootstrapToken() external view returns (address);

    /// @notice Returns the address of the cash token required to buy from the token auction.
    function cashToken() external view returns (address);

    /**
     * @notice Returns the total cost, in cash token, of purchasing `amount` tokens from the auction.
     * @param  amount Some amount of tokens.
     * @return The total cost, in cash token, of `amount` tokens.
     */
    function getCost(uint256 amount) external view returns (uint256);

    /// @notice Returns the address of the Standard Governor.
    function standardGovernor() external view returns (address);

    /// @notice Returns the target supply, which helps determine the amount of tokens up for auction.
    function targetSupply() external view returns (uint256);

    /// @notice Returns the address of the Vault.
    function vault() external view returns (address);

    /// @notice Returns the initial supply of the token.
    function INITIAL_SUPPLY() external pure returns (uint240);
}
ERC712Extended.sol 198 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import { IERC712 } from "./interfaces/IERC712.sol";
import { IERC712Extended } from "./interfaces/IERC712Extended.sol";

import { SignatureChecker } from "./libs/SignatureChecker.sol";

/**
 * @title  Typed structured data hashing and signing via EIP-712, extended by EIP-5267.
 * @author M^0 Labs
 * @dev    An abstract implementation to satisfy EIP-712: https://eips.ethereum.org/EIPS/eip-712
 */
abstract contract ERC712Extended is IERC712Extended {
    /* ============ Variables ============ */

    /// @dev keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
    bytes32 internal constant _EIP712_DOMAIN_HASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    /// @dev keccak256("1")
    bytes32 internal constant _EIP712_VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;

    /// @dev Initial Chain ID set at deployment.
    uint256 internal immutable _INITIAL_CHAIN_ID;

    /// @dev Initial EIP-712 domain separator set at deployment.
    bytes32 internal immutable _INITIAL_DOMAIN_SEPARATOR;

    /// @dev The name of the contract.
    string internal _name;

    /* ============ Constructor ============ */

    /**
     * @notice Constructs the EIP-712 domain separator.
     * @param  name_ The name of the contract.
     */
    constructor(string memory name_) {
        _name = name_;

        _INITIAL_CHAIN_ID = block.chainid;
        _INITIAL_DOMAIN_SEPARATOR = _getDomainSeparator();
    }

    /* ============ View/Pure Functions ============ */

    /// @inheritdoc IERC712Extended
    function eip712Domain()
        external
        view
        virtual
        returns (
            bytes1 fields_,
            string memory name_,
            string memory version_,
            uint256 chainId_,
            address verifyingContract_,
            bytes32 salt_,
            uint256[] memory extensions_
        )
    {
        return (
            hex"0f", // 01111
            _name,
            "1",
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }

    /// @inheritdoc IERC712
    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == _INITIAL_CHAIN_ID ? _INITIAL_DOMAIN_SEPARATOR : _getDomainSeparator();
    }

    /* ============ Internal View/Pure Functions ============ */

    /**
     * @dev    Computes the EIP-712 domain separator.
     * @return The EIP-712 domain separator.
     */
    function _getDomainSeparator() internal view returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    _EIP712_DOMAIN_HASH,
                    keccak256(bytes(_name)),
                    _EIP712_VERSION_HASH,
                    block.chainid,
                    address(this)
                )
            );
    }

    /**
     * @dev    Returns the digest to be signed, via EIP-712, given an internal digest (i.e. hash struct).
     * @param  internalDigest_ The internal digest.
     * @return The digest to be signed.
     */
    function _getDigest(bytes32 internalDigest_) internal view returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), internalDigest_));
    }

    /**
     * @dev   Revert if the signature is expired.
     * @param expiry_ Timestamp at which the signature expires or max uint256 for no expiry.
     */
    function _revertIfExpired(uint256 expiry_) internal view {
        if (block.timestamp > expiry_) revert SignatureExpired(expiry_, block.timestamp);
    }

    /**
     * @dev   Revert if the signature is invalid.
     * @dev   We first validate if the signature is a valid ECDSA signature and return early if it is the case.
     *        Then, we validate if it is a valid ERC-1271 signature, and return early if it is the case.
     *        If not, we revert with the error from the ECDSA signature validation.
     * @param signer_    The signer of the signature.
     * @param digest_    The digest that was signed.
     * @param signature_ The signature.
     */
    function _revertIfInvalidSignature(address signer_, bytes32 digest_, bytes memory signature_) internal view {
        SignatureChecker.Error error_ = SignatureChecker.validateECDSASignature(signer_, digest_, signature_);

        if (error_ == SignatureChecker.Error.NoError) return;

        if (SignatureChecker.isValidERC1271Signature(signer_, digest_, signature_)) return;

        _revertIfError(error_);
    }

    /**
     * @dev    Returns the signer of a signed digest, via EIP-712, and reverts if the signature is invalid.
     * @param  digest_ The digest that was signed.
     * @param  v_      v of the signature.
     * @param  r_      r of the signature.
     * @param  s_      s of the signature.
     * @return signer_ The signer of the digest.
     */
    function _getSignerAndRevertIfInvalidSignature(
        bytes32 digest_,
        uint8 v_,
        bytes32 r_,
        bytes32 s_
    ) internal pure returns (address signer_) {
        SignatureChecker.Error error_;

        (error_, signer_) = SignatureChecker.recoverECDSASigner(digest_, v_, r_, s_);

        _revertIfError(error_);
    }

    /**
     * @dev   Revert if the signature is invalid.
     * @param signer_ The signer of the signature.
     * @param digest_ The digest that was signed.
     * @param r_      An ECDSA/secp256k1 signature parameter.
     * @param vs_     An ECDSA/secp256k1 short signature parameter.
     */
    function _revertIfInvalidSignature(address signer_, bytes32 digest_, bytes32 r_, bytes32 vs_) internal pure {
        _revertIfError(SignatureChecker.validateECDSASignature(signer_, digest_, r_, vs_));
    }

    /**
     * @dev   Revert if the signature is invalid.
     * @param signer_ The signer of the signature.
     * @param digest_ The digest that was signed.
     * @param v_      v of the signature.
     * @param r_      r of the signature.
     * @param s_      s of the signature.
     */
    function _revertIfInvalidSignature(
        address signer_,
        bytes32 digest_,
        uint8 v_,
        bytes32 r_,
        bytes32 s_
    ) internal pure {
        _revertIfError(SignatureChecker.validateECDSASignature(signer_, digest_, v_, r_, s_));
    }

    /**
     * @dev   Revert if error.
     * @param error_ The SignatureChecker Error enum.
     */
    function _revertIfError(SignatureChecker.Error error_) private pure {
        if (error_ == SignatureChecker.Error.NoError) return;
        if (error_ == SignatureChecker.Error.InvalidSignature) revert InvalidSignature();
        if (error_ == SignatureChecker.Error.InvalidSignatureLength) revert InvalidSignatureLength();
        if (error_ == SignatureChecker.Error.InvalidSignatureS) revert InvalidSignatureS();
        if (error_ == SignatureChecker.Error.InvalidSignatureV) revert InvalidSignatureV();
        if (error_ == SignatureChecker.Error.SignerMismatch) revert SignerMismatch();

        revert InvalidSignature();
    }
}
IERC20.sol 85 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

/**
 * @title  ERC20 Token Standard.
 * @author M^0 Labs
 * @dev    The interface as defined by EIP-20: https://eips.ethereum.org/EIPS/eip-20
 */
interface IERC20 {
    /* ============ Events ============ */

    /**
     * @notice Emitted when `spender` has been approved for `amount` of the token balance of `account`.
     * @param  account The address of the account.
     * @param  spender The address of the spender being approved for the allowance.
     * @param  amount  The amount of the allowance being approved.
     */
    event Approval(address indexed account, address indexed spender, uint256 amount);

    /**
     * @notice Emitted when `amount` tokens is transferred from `sender` to `recipient`.
     * @param  sender    The address of the sender who's token balance is decremented.
     * @param  recipient The address of the recipient who's token balance is incremented.
     * @param  amount    The amount of tokens being transferred.
     */
    event Transfer(address indexed sender, address indexed recipient, uint256 amount);

    /* ============ Interactive Functions ============ */

    /**
     * @notice Allows a calling account to approve `spender` to spend up to `amount` of its token balance.
     * @dev    MUST emit an `Approval` event.
     * @param  spender The address of the account being allowed to spend up to the allowed amount.
     * @param  amount  The amount of the allowance being approved.
     * @return Whether or not the approval was successful.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @notice Allows a calling account to transfer `amount` tokens to `recipient`.
     * @param  recipient The address of the recipient who's token balance will be incremented.
     * @param  amount    The amount of tokens being transferred.
     * @return Whether or not the transfer was successful.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @notice Allows a calling account to transfer `amount` tokens from `sender`, with allowance, to a `recipient`.
     * @param  sender    The address of the sender who's token balance will be decremented.
     * @param  recipient The address of the recipient who's token balance will be incremented.
     * @param  amount    The amount of tokens being transferred.
     * @return Whether or not the transfer was successful.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /* ============ View/Pure Functions ============ */

    /**
     * @notice Returns the allowance `spender` is allowed to spend on behalf of `account`.
     * @param  account The address of the account who's token balance `spender` is allowed to spend.
     * @param  spender The address of an account allowed to spend on behalf of `account`.
     * @return The amount `spender` can spend on behalf of `account`.
     */
    function allowance(address account, address spender) external view returns (uint256);

    /**
     * @notice Returns the token balance of `account`.
     * @param  account The address of some account.
     * @return The token balance of `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /// @notice Returns the number of decimals UIs should assume all amounts have.
    function decimals() external view returns (uint8);

    /// @notice Returns the name of the contract/token.
    function name() external view returns (string memory);

    /// @notice Returns the symbol of the token.
    function symbol() external view returns (string memory);

    /// @notice Returns the current total supply of the token.
    function totalSupply() external view returns (uint256);
}
ERC20Helper.sol 43 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

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

/**
 * @title Small Library to standardize erc20 token interactions.
 */
library ERC20Helper {

    /**************************************************************************************************************************************/
    /*** Internal Functions                                                                                                             ***/
    /**************************************************************************************************************************************/

    function transfer(address token_, address to_, uint256 amount_) internal returns (bool success_) {
        return _call(token_, abi.encodeWithSelector(IERC20Like.transfer.selector, to_, amount_));
    }

    function transferFrom(address token_, address from_, address to_, uint256 amount_) internal returns (bool success_) {
        return _call(token_, abi.encodeWithSelector(IERC20Like.transferFrom.selector, from_, to_, amount_));
    }

    function approve(address token_, address spender_, uint256 amount_) internal returns (bool success_) {
        // If setting approval to zero fails, return false.
        if (!_call(token_, abi.encodeWithSelector(IERC20Like.approve.selector, spender_, uint256(0)))) return false;

        // If `amount_` is zero, return true as the previous step already did this.
        if (amount_ == uint256(0)) return true;

        // Return the result of setting the approval to `amount_`.
        return _call(token_, abi.encodeWithSelector(IERC20Like.approve.selector, spender_, amount_));
    }

    function _call(address token_, bytes memory data_) private returns (bool success_) {
        if (token_.code.length == uint256(0)) return false;

        bytes memory returnData;
        ( success_, returnData ) = token_.call(data_);

        return success_ && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
    }

}
IERC5805.sol 87 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import { IStatefulERC712 } from "../../../lib/common/src/interfaces/IStatefulERC712.sol";

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

/**
 * @title  Voting with voting weight tracking and delegation support.
 * @author M^0 Labs
 * @dev    The interface as defined by EIP-5805: https://eips.ethereum.org/EIPS/eip-5805
 */
interface IERC5805 is IStatefulERC712, IERC6372 {
    /* ============ Events ============ */

    /**
     * @notice Emitted when `delegator` changes its voting power delegation from `fromDelegatee` to `toDelegatee`.
     * @param  delegator     The address of the account changing its voting power delegation.
     * @param  fromDelegatee The previous account the voting power of `delegator` was delegated to.
     * @param  toDelegatee   The new account the voting power of `delegator` is delegated to.
     */
    event DelegateChanged(address indexed delegator, address indexed fromDelegatee, address indexed toDelegatee);

    /**
     * @notice Emitted when the available voting power of `delegatee` changes from `previousBalance` to `newBalance`.
     * @param  delegatee       The address of the account whose voting power is changed.
     * @param  previousBalance The previous voting power of `delegatee`.
     * @param  newBalance      The new voting power of `delegatee`.
     */
    event DelegateVotesChanged(address indexed delegatee, uint256 previousBalance, uint256 newBalance);

    /* ============ Custom Errors ============ */

    /**
     * @notice Revert message when a query for past values is for a timepoint greater or equal to the current clock.
     * @param  timepoint The timepoint being queried.
     * @param  clock     The current timepoint.
     */
    error NotPastTimepoint(uint48 timepoint, uint48 clock);

    /* ============ Interactive Functions ============ */

    /**
     * @notice Allows a calling account to change its voting power delegation to `delegatee`.
     * @param  delegatee The address of the account the caller's voting power will be delegated to.
     */
    function delegate(address delegatee) external;

    /**
     * @notice Changes the signing account's voting power delegation to `delegatee`.
     * @param  delegatee The address of the account the signing account's voting power will be delegated to.
     * @param  nonce     The nonce of the account delegating.
     * @param  expiry    The timestamp until which the signature is still valid.
     * @param  v         A signature parameter.
     * @param  r         A signature parameter.
     * @param  s         A signature parameter.
     */
    function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external;

    /* ============ View/Pure Functions ============ */

    /// @notice Returns the EIP712 typehash used in the encoding of the digest for the delegateBySig function.
    function DELEGATION_TYPEHASH() external view returns (bytes32);

    /**
     * @notice Returns the delegatee the voting power of `account` is delegated to.
     * @param  account The address of the account that can delegate its voting power.
     * @return The address of the account the voting power of `account` will be delegated to.
     */
    function delegates(address account) external view returns (address);

    /**
     * @notice Returns the total voting power of `account` at a past clock value `timepoint`.
     * @param  account   The address of some account.
     * @param  timepoint The point in time, according to the clock mode the contract is operating on.
     * @return The total voting power of `account` at clock value `timepoint`.
     */
    function getPastVotes(address account, uint256 timepoint) external view returns (uint256);

    /**
     * @notice Returns the total voting power of `account`.
     * @param  account The address of some account.
     * @return The total voting power of `account`.
     */
    function getVotes(address account) external view returns (uint256);
}
IERC6372.sol 16 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

/**
 * @title  Contract clock properties.
 * @author M^0 Labs
 * @dev    The interface as defined by EIP-6372: https://eips.ethereum.org/EIPS/eip-6372
 */
interface IERC6372 {
    /// @notice Returns a machine-readable string description of the clock the contract is operating on.
    function CLOCK_MODE() external view returns (string memory);

    /// @notice Returns the current timepoint according to the mode the contract is operating on.
    function clock() external view returns (uint48);
}
IStandardGovernor.sol 216 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import { IBatchGovernor } from "../abstract/interfaces/IBatchGovernor.sol";

/**
 * @title  An instance of a BatchGovernor with a unique and limited set of possible proposals with proposal fees.
 * @author M^0 Labs
 */
interface IStandardGovernor is IBatchGovernor {
    /* ============ Events ============ */

    /**
     * @notice Emitted when the cash token is set to `cashToken`.
     * @param  cashToken The address of the cash token taking effect.
     */
    event CashTokenSet(address indexed cashToken);

    /**
     * @notice Emitted when `voter` has voted on all the proposals in the current epoch `currentEpoch`.
     * @param  voter        The address of the account voting.
     * @param  currentEpoch The current epoch number as a clock value.
     */
    event HasVotedOnAllProposals(address indexed voter, uint256 indexed currentEpoch);

    /**
     * @notice Emitted when the proposal fee for the proposal, with identifier `proposalFee`, is sent to the vault.
     * @param  proposalId The unique identifier of the proposal.
     * @param  cashToken  The address of the cash token for this particular proposal fee.
     * @param  amount     The amount of cash token of the proposal fee.
     */
    event ProposalFeeSentToVault(uint256 indexed proposalId, address indexed cashToken, uint256 amount);

    /**
     * @notice Emitted when the proposal fee is set to `proposalFee`.
     * @param  proposalFee The amount of cash token required onwards to create proposals.
     */
    event ProposalFeeSet(uint256 proposalFee);

    /* ============ Custom Errors ============ */

    /**
     * @notice Revert message when the proposal fee for a yet defeated or yet expired proposal is trying to be moved.
     * @param  state The current state of the proposal.
     */
    error FeeNotDestinedForVault(ProposalState state);

    /// @notice Revert message when the Cash Token specified in the constructor is address(0).
    error InvalidCashTokenAddress();

    /// @notice Revert message when the Emergency Governor specified in the constructor is address(0).
    error InvalidEmergencyGovernorAddress();

    /// @notice Revert message when the Registrar specified in the constructor is address(0).
    error InvalidRegistrarAddress();

    /// @notice Revert message when the Vault specified in the constructor is address(0).
    error InvalidVaultAddress();

    /// @notice Revert message when the Zero Governor specified in the constructor is address(0).
    error InvalidZeroGovernorAddress();

    /// @notice Revert message when the Zero Token specified in the constructor is address(0).
    error InvalidZeroTokenAddress();

    /// @notice Revert message when proposal fee trying to be moved to the vault is 0.
    error NoFeeToSend();

    /// @notice Revert message when the caller is not this contract itself nor the Emergency Governor.
    error NotSelfOrEmergencyGovernor();

    /// @notice Revert message when the caller is not the Zero Governor.
    error NotZeroGovernor();

    /// @notice Revert message when a token transfer, from this contract, fails.
    error TransferFailed();

    /// @notice Revert message when a token transferFrom fails.
    error TransferFromFailed();

    /* ============ Interactive Functions ============ */

    /**
     * @notice Sends the proposal fee for proposal `proposalId` to the vault, if it is Defeated or Expired.
     * @param  proposalId The unique identifier of the proposal.
     */
    function sendProposalFeeToVault(uint256 proposalId) external;

    /**
     * @notice Set the cash token and proposal fee to be used to create proposals going forward.
     * @param  newCashToken   The address of the new cash token.
     * @param  newProposalFee The amount of cash token required onwards to create proposals.
     */
    function setCashToken(address newCashToken, uint256 newProposalFee) external;

    /* ============ Proposal Functions ============ */

    /**
     * @notice One of the valid proposals. Adds `account` to `list` at the Registrar.
     * @param  list    The key for some list.
     * @param  account The address of some account to be added.
     */
    function addToList(bytes32 list, address account) external;

    /**
     * @notice One of the valid proposals. Removes `account` to `list` at the Registrar.
     * @param  list    The key for some list.
     * @param  account The address of some account to be removed.
     */
    function removeFromList(bytes32 list, address account) external;

    /**
     * @notice One of the valid proposals. Removes `accountToRemove` and adds `accountToAdd` to `list` at the Registrar.
     * @param  list            The key for some list.
     * @param  accountToRemove The address of some account to be removed.
     * @param  accountToAdd    The address of some account to be added.
     */
    function removeFromAndAddToList(bytes32 list, address accountToRemove, address accountToAdd) external;

    /**
     * @notice One of the valid proposals. Sets `key` to `value` at the Registrar.
     * @param  key   Some key.
     * @param  value Some value.
     */
    function setKey(bytes32 key, bytes32 value) external;

    /**
     * @notice One of the valid proposals. Sets the proposal fee of the Standard Governor.
     * @param  newProposalFee The new proposal fee.
     */
    function setProposalFee(uint256 newProposalFee) external;

    /* ============ View/Pure Functions ============ */

    /// @notice Returns the required amount of cashToken it costs an account to create a proposal.
    function proposalFee() external view returns (uint256);

    /**
     * @notice Returns all the proposal details for a proposal with identifier `proposalId`.
     * @param  proposalId The unique identifier of the proposal.
     * @return voteStart  The first clock value when voting on the proposal is allowed.
     * @return voteEnd    The last clock value when voting on the proposal is allowed.
     * @return state      The state of the proposal.
     * @return noVotes    The amount of votes cast against the proposal.
     * @return yesVotes   The amount of votes cast for the proposal.
     * @return proposer   The address of the account that created the proposal.
     * @return quorum     The number of votes required to meet quorum.
     */
    function getProposal(
        uint256 proposalId
    )
        external
        view
        returns (
            uint48 voteStart,
            uint48 voteEnd,
            ProposalState state,
            uint256 noVotes,
            uint256 yesVotes,
            address proposer,
            uint256 quorum
        );

    /**
     * @notice Returns the proposal fee information.
     * @param  proposalId The unique identifier of the proposal.
     * @return cashToken  The address of the cash token for this particular proposal fee.
     * @return amount     The amount of cash token of the proposal fee.
     */
    function getProposalFee(uint256 proposalId) external view returns (address cashToken, uint256 amount);

    /// @notice Returns the maximum amount of Zero Token that can be rewarded to all vote casters per active epoch.
    function maxTotalZeroRewardPerActiveEpoch() external view returns (uint256);

    /**
     * @notice Returns the number of proposals at epoch `epoch`.
     * @param  epoch The epoch as a clock value.
     * @return The number of proposals at epoch `epoch`.
     */
    function numberOfProposalsAt(uint256 epoch) external view returns (uint256);

    /**
     * @notice Returns the number of proposals that were voted on at `epoch`.
     * @param  voter The address of some account.
     * @param  epoch The epoch as a clock value.
     * @return The number of proposals at `epoch`.
     */
    function numberOfProposalsVotedOnAt(address voter, uint256 epoch) external view returns (uint256);

    /**
     * @notice Returns whether `voter` has voted on all proposals in `epoch`.
     * @param  voter The address of some account.
     * @param  epoch The epoch as a clock value.
     * @return Whether `voter` has voted on all proposals in `epoch`.
     */
    function hasVotedOnAllProposals(address voter, uint256 epoch) external view returns (bool);

    /// @notice Returns the address of the Cash Token.
    function cashToken() external view returns (address);

    /// @notice Returns the address of the Emergency Governor.
    function emergencyGovernor() external view returns (address);

    /// @notice Returns the address of the Registrar.
    function registrar() external view returns (address);

    /// @notice Returns the address of the Vault.
    function vault() external view returns (address);

    /// @notice Returns the address of the Zero Governor.
    function zeroGovernor() external view returns (address);

    /// @notice Returns the address of the Zero Token.
    function zeroToken() external view returns (address);
}
IERC712.sol 39 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

/**
 * @title  Typed structured data hashing and signing via EIP-712.
 * @author M^0 Labs
 * @dev    The interface as defined by EIP-712: https://eips.ethereum.org/EIPS/eip-712
 */
interface IERC712 {
    /* ============ Custom Errors ============ */

    /// @notice Revert message when an invalid signature is detected.
    error InvalidSignature();

    /// @notice Revert message when a signature with invalid length is detected.
    error InvalidSignatureLength();

    /// @notice Revert message when the S portion of a signature is invalid.
    error InvalidSignatureS();

    /// @notice Revert message when the V portion of a signature is invalid.
    error InvalidSignatureV();

    /**
     * @notice Revert message when a signature is being used beyond its deadline (i.e. expiry).
     * @param  deadline  The deadline of the signature.
     * @param  timestamp The current timestamp.
     */
    error SignatureExpired(uint256 deadline, uint256 timestamp);

    /// @notice Revert message when a recovered signer does not match the account being purported to have signed.
    error SignerMismatch();

    /* ============ View/Pure Functions ============ */

    /// @notice Returns the EIP712 domain separator used in the encoding of a signed digest.
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IGovernor.sol 306 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import { IERC712 } from "../../../lib/common/src/interfaces/IERC712.sol";

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

/**
 * @title  Minimal OpenZeppelin-style, Tally-compatible governor.
 * @author M^0 Labs
 */
interface IGovernor is IERC6372, IERC712 {
    /* ============ Enums ============ */

    /**
     * @notice Proposal state.
     * @param  Pending   The proposal has been created, but the vote has not started yet.
     * @param  Active    The proposal is currently in the voting period.
     * @param  Canceled  The proposal has been canceled.
     * @param  Defeated  The proposal has been defeated.
     * @param  Succeeded The proposal has succeeded.
     * @param  Queued    The proposal has been queued.
     * @param  Expired   The proposal has expired.
     * @param  Executed  The proposal has been executed.
     */
    enum ProposalState {
        Pending,
        Active,
        Canceled, // never used by TTG.
        Defeated,
        Succeeded,
        Queued, // never used by TTG.
        Expired,
        Executed
    }

    /* ============ Events ============ */

    /**
     * @notice Emitted when a proposal has been created.
     * @param  proposalId  The unique identifier for the proposal.
     * @param  proposer    The address of the account that created the proposal.
     * @param  targets     An array of addresses that will be called upon the execution.
     * @param  values      An array of ETH amounts that will be sent to each respective target upon execution.
     * @param  signatures  Empty string array required to be compatible with OZ governor contract.
     * @param  callDatas   An array of call data used to call each respective target upon execution.
     * @param  voteStart   The first clock value when voting on the proposal is allowed.
     * @param  voteEnd     The last clock value when voting on the proposal is allowed.
     * @param  description The string of the description of the proposal.
     */
    event ProposalCreated(
        uint256 proposalId,
        address proposer,
        address[] targets,
        uint256[] values,
        string[] signatures,
        bytes[] callDatas,
        uint256 voteStart,
        uint256 voteEnd,
        string description
    );

    /**
     * @notice Emitted when a proposal has been executed.
     * @param  proposalId The unique identifier for the proposal.
     */
    event ProposalExecuted(uint256 proposalId);

    /**
     * @notice Emitted when a vote for a proposal with id `proposalId` has been cast by `voter`.
     * @param  voter      The address of the account that has casted their vote.
     * @param  proposalId The unique identifier for the proposal.
     * @param  support    The type of support that has been cast for the proposal.
     * @param  weight     The number of votes cast.
     * @param  reason     The string of the reason `voter` has cast their vote, if any.
     */
    event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);

    /* ============ Interactive Functions ============ */

    /**
     * @notice Allows the caller to cast a vote on a proposal with id `proposalId`.
     * @param  proposalId The unique identifier for the proposal.
     * @param  support    The type of support to cast for the proposal.
     * @return weight     The number of votes cast.
     */
    function castVote(uint256 proposalId, uint8 support) external returns (uint256 weight);

    /**
     * @notice Allows a signer to cast a vote on a proposal with id `proposalId` via an ECDSA secp256k1 signature.
     * @param  proposalId The unique identifier for the proposal.
     * @param  support    The type of support to cast for the proposal.
     * @param  v          An ECDSA secp256k1 signature parameter.
     * @param  r          An ECDSA secp256k1 signature parameter.
     * @param  s          An ECDSA secp256k1 signature parameter.
     * @return weight     The number of votes cast.
     */
    function castVoteBySig(
        uint256 proposalId,
        uint8 support,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 weight);

    /**
     * @notice Allows `voter` to cast a vote on a proposal with id `proposalId` via an arbitrary signature.
     * @param  voter      The address of the account that casting their vote, and purported to have signed.
     * @param  proposalId The unique identifier for the proposal.
     * @param  support    The type of support to cast for the proposal.
     * @param  signature  An arbitrary signature.
     * @return weight     The number of votes cast.
     */
    function castVoteBySig(
        address voter,
        uint256 proposalId,
        uint8 support,
        bytes memory signature
    ) external returns (uint256 weight);

    /**
     * @notice Allows the caller to cast a vote with reason on a proposal with id `proposalId`.
     * @param  proposalId The unique identifier for the proposal.
     * @param  support    The type of support to cast for the proposal.
     * @param  reason     The reason for which the caller casts their vote, if any.
     * @return weight     The number of votes cast.
     */
    function castVoteWithReason(
        uint256 proposalId,
        uint8 support,
        string calldata reason
    ) external returns (uint256 weight);

    /**
     * @notice Allows a signer to cast a vote with reason on a proposal with id `proposalId`
     *         via an ECDSA secp256k1 signature.
     * @param  proposalId The unique identifier for the proposal.
     * @param  support    The type of support to cast for the proposal.
     * @param  reason     The reason for which the caller casts their vote, if any.
     * @param  v          An ECDSA secp256k1 signature parameter.
     * @param  r          An ECDSA secp256k1 signature parameter.
     * @param  s          An ECDSA secp256k1 signature parameter.
     * @return weight     The number of votes cast.
     */
    function castVoteWithReasonBySig(
        uint256 proposalId,
        uint8 support,
        string calldata reason,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 weight);

    /**
     * @notice Allows `voter` to cast a vote with reason on a proposal with id `proposalId` via an arbitrary signature.
     * @param  voter      The address of the account that casting their vote, and purported to have signed.
     * @param  proposalId The unique identifier for the proposal.
     * @param  support    The type of support to cast for the proposal.
     * @param  reason     The reason for which the caller casts their vote, if any.
     * @param  signature  An arbitrary signature.
     * @return weight     The number of votes cast.
     */
    function castVoteWithReasonBySig(
        address voter,
        uint256 proposalId,
        uint8 support,
        string calldata reason,
        bytes memory signature
    ) external returns (uint256 weight);

    /**
     * @notice Allows the caller to execute a proposal.
     * @param  targets         An array of addresses that will be called upon the execution.
     * @param  values          An array of ETH amounts that will be sent to each respective target upon execution.
     * @param  callDatas       An array of call data used to call each respective target upon execution.
     * @param  descriptionHash The hash of the string of the description of the proposal.
     * @return proposalId      The unique identifier for the proposal.
     */
    function execute(
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory callDatas,
        bytes32 descriptionHash
    ) external payable returns (uint256 proposalId);

    /**
     * @notice Allows the caller to create a proposal.
     * @param  targets     An array of addresses that will be called upon the execution.
     * @param  values      An array of ETH amounts that will be sent to each respective target upon execution.
     * @param  callDatas   An array of call data used to call each respective target upon execution.
     * @param  description The string of the description of the proposal.
     * @return proposalId  The unique identifier for the proposal.
     */
    function propose(
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory callDatas,
        string memory description
    ) external returns (uint256 proposalId);

    /* ============ View/Pure Functions ============ */

    /**
     * @notice module:voting
     * @dev    A description of the possible "support" values for castVote and the way these votes are counted, meant to
     *         be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded
     *         sequence of key-value pairs that each describe one aspect, for example `support=for,against&quorum=for`.
     *         The string can be decoded by the standard URLSearchParams JavaScript class.
     */
    function COUNTING_MODE() external view returns (string memory);

    /**
     * @notice Returns the voting power of `account` at clock value `timepoint`.
     * @param  account   The address of the account with voting power.
     * @param  timepoint The point in time, according to the clock mode the contract is operating on.
     * @return The voting power of `account` at `timepoint`.
     */
    function getVotes(address account, uint256 timepoint) external view returns (uint256);

    /**
     * @notice Returns the unique identifier for the proposal if it were created at this exact moment.
     * @param  targets         An array of addresses that will be called upon the execution.
     * @param  values          An array of ETH amounts that will be sent to each respective target upon execution.
     * @param  callDatas       An array of call data used to call each respective target upon execution.
     * @param  descriptionHash The hash of the string of the description of the proposal.
     * @return The unique identifier for the proposal.
     */
    function hashProposal(
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory callDatas,
        bytes32 descriptionHash
    ) external view returns (uint256);

    /**
     * @notice Returns whether `account` has voted on the proposal with identifier `proposalId`.
     * @param  proposalId The unique identifier for the proposal.
     * @param  account    The address of some account.
     * @return Whether `account` has already voted on the proposal.
     */
    function hasVoted(uint256 proposalId, address account) external view returns (bool);

    /// @notice Returns the name of the contract.
    function name() external view returns (string memory);

    /**
     * @notice Returns the last clock value when voting on the proposal with identifier `proposalId` is allowed.
     * @param  proposalId The unique identifier for the proposal.
     * @return The last clock value when voting on the proposal is allowed.
     */
    function proposalDeadline(uint256 proposalId) external view returns (uint256);

    /**
     * @notice Returns the account that created the proposal with identifier `proposalId`.
     * @param  proposalId The unique identifier for the proposal.
     * @return The address of the account that created the proposal.
     */
    function proposalProposer(uint256 proposalId) external view returns (address);

    /**
     * @notice Returns the clock value used to retrieve voting power to vote on proposal with identifier `proposalId`.
     * @param  proposalId The unique identifier for the proposal.
     * @return The clock value used to retrieve voting power.
     */
    function proposalSnapshot(uint256 proposalId) external view returns (uint256);

    /// @notice Returns the required voting power an account needs to create a proposal.
    function proposalThreshold() external view returns (uint256);

    /**
     * @notice Returns the vote support totals for the proposal with identifier `proposalId`.
     * @param  proposalId   The unique identifier for the proposal.
     * @return noVotes      The amount of votes cast against the proposal.
     * @return yesVotes     The amount of votes cast for the proposal.
     * @return abstainVotes The amount of votes cast in abstention the proposal.
     */
    function proposalVotes(
        uint256 proposalId
    ) external view returns (uint256 noVotes, uint256 yesVotes, uint256 abstainVotes);

    /// @notice Returns the minimum number of eligible (COUNTING_MODE) votes for a proposal to succeed.
    function quorum() external view returns (uint256);

    /**
     * @notice Returns the state of a proposal with identifier `proposalId`.
     * @param  proposalId The unique identifier for the proposal.
     * @return The state of the proposal.
     */
    function state(uint256 proposalId) external view returns (ProposalState);

    /// @notice Returns the EIP-5805 token contact used for determine voting power and total supplies.
    function token() external view returns (address);

    /// @notice Returns the number of clock values that must elapse before voting begins for a newly created proposal.
    function votingDelay() external view returns (uint256);

    /// @notice Returns the number of clock values between the vote start and vote end.
    function votingPeriod() external view returns (uint256);

    /// @notice Returns the EIP712 typehash used in the encoding of the digest for `castVoteBySig` function.
    function BALLOT_TYPEHASH() external pure returns (bytes32);

    /// @notice Returns the EIP712 typehash used in the encoding of the digest for `castVoteWithReasonBySig` function.
    function BALLOT_WITH_REASON_TYPEHASH() external pure returns (bytes32);
}
IERC1271.sol 18 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

/**
 * @title  Standard Signature Validation Method for Contracts via EIP-1271.
 * @author M^0 Labs
 * @dev    The interface as defined by EIP-1271: https://eips.ethereum.org/EIPS/eip-1271
 */
interface IERC1271 {
    /**
     * @dev    Returns a specific magic value if the provided signature is valid for the provided digest.
     * @param  digest     Hash of the data purported to have been signed.
     * @param  signature  Signature byte array associated with the digest.
     * @return magicValue Magic value 0x1626ba7e if the signature is valid.
     */
    function isValidSignature(bytes32 digest, bytes memory signature) external view returns (bytes4 magicValue);
}
IERC3009.sol 248 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

/**
 * @title  Transfer via signed authorization following EIP-3009 standard.
 * @author M^0 Labs
 * @dev    The interface as defined by EIP-3009: https://eips.ethereum.org/EIPS/eip-3009
 */
interface IERC3009 is IStatefulERC712 {
    /* ============ Events ============ */

    /**
     * @notice Emitted when an authorization has been canceled.
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the canceled authorization.
     */
    event AuthorizationCanceled(address indexed authorizer, bytes32 indexed nonce);

    /**
     * @notice Emitted when an authorization has been used.
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the used authorization.
     */
    event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);

    /* ============ Custom Errors ============ */

    /**
     * @notice Emitted when an authorization has already been used.
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the used authorization.
     */
    error AuthorizationAlreadyUsed(address authorizer, bytes32 nonce);

    /**
     * @notice Emitted when an authorization is expired.
     * @param  timestamp   Timestamp at which the transaction was submitted.
     * @param  validBefore Timestamp before which the authorization would have been valid.
     */
    error AuthorizationExpired(uint256 timestamp, uint256 validBefore);

    /**
     * @notice Emitted when an authorization is not yet valid.
     * @param  timestamp  Timestamp at which the transaction was submitted.
     * @param  validAfter Timestamp after which the authorization will be valid.
     */
    error AuthorizationNotYetValid(uint256 timestamp, uint256 validAfter);

    /**
     * @notice Emitted when the caller of `receiveWithAuthorization` is not the payee.
     * @param  caller Caller's address.
     * @param  payee  Payee's address.
     */
    error CallerMustBePayee(address caller, address payee);

    /* ============ Interactive Functions ============ */

    /**
     * @notice Execute a transfer with a signed authorization.
     * @param  from        Payer's address (Authorizer).
     * @param  to          Payee's address.
     * @param  value       Amount to be transferred.
     * @param  validAfter  The time after which this is valid (unix time).
     * @param  validBefore The time before which this is valid (unix time).
     * @param  nonce       Unique nonce.
     * @param  signature   A byte array ECDSA/secp256k1 signature (encoded r, s, v).
     */
    function transferWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        bytes memory signature
    ) external;

    /**
     * @notice Execute a transfer with a signed authorization.
     * @param  from        Payer's address (Authorizer).
     * @param  to          Payee's address.
     * @param  value       Amount to be transferred.
     * @param  validAfter  The time after which this is valid (unix time).
     * @param  validBefore The time before which this is valid (unix time).
     * @param  nonce       Unique nonce.
     * @param  r           An ECDSA/secp256k1 signature parameter.
     * @param  vs          An ECDSA/secp256k1 short signature parameter.
     */
    function transferWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        bytes32 r,
        bytes32 vs
    ) external;

    /**
     * @notice Execute a transfer with a signed authorization.
     * @param  from        Payer's address (Authorizer).
     * @param  to          Payee's address.
     * @param  value       Amount to be transferred.
     * @param  validAfter  The time after which this is valid (unix time).
     * @param  validBefore The time before which this is valid (unix time).
     * @param  nonce       Unique nonce.
     * @param  v           v of the signature.
     * @param  r           r of the signature.
     * @param  s           s of the signature.
     */
    function transferWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Receive a transfer with a signed authorization from the payer.
     * @dev    This has an additional check to ensure that the payee's address matches
     *         the caller of this function to prevent front-running attacks.
     *         (See security considerations)
     * @param  from        Payer's address (Authorizer).
     * @param  to          Payee's address.
     * @param  value       Amount to be transferred.
     * @param  validAfter  The time after which this is valid (unix time).
     * @param  validBefore The time before which this is valid (unix time).
     * @param  nonce       Unique nonce.
     * @param  signature   A byte array ECDSA/secp256k1 signature (encoded r, s, v).
     */
    function receiveWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        bytes memory signature
    ) external;

    /**
     * @notice Receive a transfer with a signed authorization from the payer.
     * @dev    This has an additional check to ensure that the payee's address matches
     *         the caller of this function to prevent front-running attacks.
     *         (See security considerations)
     * @param  from        Payer's address (Authorizer).
     * @param  to          Payee's address.
     * @param  value       Amount to be transferred.
     * @param  validAfter  The time after which this is valid (unix time).
     * @param  validBefore The time before which this is valid (unix time).
     * @param  nonce       Unique nonce.
     * @param  r           An ECDSA/secp256k1 signature parameter.
     * @param  vs          An ECDSA/secp256k1 short signature parameter.
     */
    function receiveWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        bytes32 r,
        bytes32 vs
    ) external;

    /**
     * @notice Receive a transfer with a signed authorization from the payer.
     * @dev    This has an additional check to ensure that the payee's address matches
     *         the caller of this function to prevent front-running attacks.
     *         (See security considerations)
     * @param  from        Payer's address (Authorizer).
     * @param  to          Payee's address.
     * @param  value       Amount to be transferred.
     * @param  validAfter  The time after which this is valid (unix time).
     * @param  validBefore The time before which this is valid (unix time).
     * @param  nonce       Unique nonce.
     * @param  v           v of the signature.
     * @param  r           r of the signature.
     * @param  s           s of the signature.
     */
    function receiveWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Attempt to cancel an authorization.
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the authorization.
     * @param  signature  A byte array ECDSA/secp256k1 signature (encoded r, s, v).
     */
    function cancelAuthorization(address authorizer, bytes32 nonce, bytes memory signature) external;

    /**
     * @notice Attempt to cancel an authorization.
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the authorization.
     * @param  r          An ECDSA/secp256k1 signature parameter.
     * @param  vs         An ECDSA/secp256k1 short signature parameter.
     */
    function cancelAuthorization(address authorizer, bytes32 nonce, bytes32 r, bytes32 vs) external;

    /**
     * @notice Attempt to cancel an authorization.
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the authorization.
     * @param  v          v of the signature.
     * @param  r          r of the signature.
     * @param  s          s of the signature.
     */
    function cancelAuthorization(address authorizer, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) external;

    /* ============ View/Pure Functions ============ */

    /**
     * @notice Returns the state of an authorization.
     * @dev    Nonces are randomly generated 32-byte data unique to the authorizer's address
     * @param  authorizer Authorizer's address.
     * @param  nonce      Nonce of the authorization.
     * @return True if the nonce is used.
     */
    function authorizationState(address authorizer, bytes32 nonce) external view returns (bool);

    /// @notice Returns `transferWithAuthorization` typehash.
    function TRANSFER_WITH_AUTHORIZATION_TYPEHASH() external view returns (bytes32);

    /// @notice Returns `receiveWithAuthorization` typehash.
    function RECEIVE_WITH_AUTHORIZATION_TYPEHASH() external view returns (bytes32);

    /// @notice Returns `cancelAuthorization` typehash.
    function CANCEL_AUTHORIZATION_TYPEHASH() external view returns (bytes32);
}
SignatureChecker.sol 261 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

/**
 * @title  A library to handle ECDSA/secp256k1 and ERC1271 signatures, individually or in arbitrarily in combination.
 * @author M^0 Labs
 */
library SignatureChecker {
    /* ============ Enums ============ */

    /**
     * @notice An enum representing the possible errors that can be emitted during signature validation.
     * @param  NoError                No error occurred during signature validation.
     * @param  InvalidSignature       The signature is invalid.
     * @param  InvalidSignatureLength The signature length is invalid.
     * @param  InvalidSignatureS      The signature parameter S is invalid.
     * @param  InvalidSignatureV      The signature parameter V is invalid.
     * @param  SignerMismatch         The signer does not match the recovered signer.
     */
    enum Error {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV,
        SignerMismatch
    }

    /* ============ Internal View/Pure Functions ============ */

    /**
     * @dev    Returns whether a signature is valid (ECDSA/secp256k1 or ERC1271) for a signer and digest.
     * @dev    Signatures must not be used as unique identifiers since the `ecrecover` EVM opcode
     *         allows for malleable (non-unique) signatures.
     *         See https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-4h98-2769-gh6h
     * @param  signer    The address of the account purported to have signed.
     * @param  digest    The hash of the data that was signed.
     * @param  signature A byte array signature.
     * @return           Whether the signature is valid or not.
     */
    function isValidSignature(address signer, bytes32 digest, bytes memory signature) internal view returns (bool) {
        return isValidECDSASignature(signer, digest, signature) || isValidERC1271Signature(signer, digest, signature);
    }

    /**
     * @dev    Returns whether an ERC1271 signature is valid for a signer and digest.
     * @param  signer    The address of the account purported to have signed.
     * @param  digest    The hash of the data that was signed.
     * @param  signature A byte array ERC1271 signature.
     * @return           Whether the signature is valid or not.
     */
    function isValidERC1271Signature(
        address signer,
        bytes32 digest,
        bytes memory signature
    ) internal view returns (bool) {
        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeCall(IERC1271.isValidSignature, (digest, signature))
        );

        return
            success &&
            result.length >= 32 &&
            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector);
    }

    /**
     * @dev    Decodes an ECDSA/secp256k1 signature from a byte array to standard v, r, and s parameters.
     * @param  signature A byte array ECDSA/secp256k1 signature.
     * @return v         An ECDSA/secp256k1 signature parameter.
     * @return r         An ECDSA/secp256k1 signature parameter.
     * @return s         An ECDSA/secp256k1 signature parameter.
     */
    function decodeECDSASignature(bytes memory signature) internal pure returns (uint8 v, bytes32 r, bytes32 s) {
        // ecrecover takes the signature parameters, and they can be decoded using assembly.
        /// @solidity memory-safe-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }
    }

    /**
     * @dev    Decodes an ECDSA/secp256k1 short signature as defined by EIP2098
     *         from a byte array to standard v, r, and s parameters.
     * @param  signature A byte array ECDSA/secp256k1 short signature.
     * @return r         An ECDSA/secp256k1 signature parameter.
     * @return vs        An ECDSA/secp256k1 short signature parameter.
     */
    function decodeShortECDSASignature(bytes memory signature) internal pure returns (bytes32 r, bytes32 vs) {
        // ecrecover takes the signature parameters, and they can be decoded using assembly.
        /// @solidity memory-safe-assembly
        assembly {
            r := mload(add(signature, 0x20))
            vs := mload(add(signature, 0x40))
        }
    }

    /**
     * @dev    Returns whether an ECDSA/secp256k1 signature is valid for a signer and digest.
     * @param  signer    The address of the account purported to have signed.
     * @param  digest    The hash of the data that was signed.
     * @param  signature A byte array ECDSA/secp256k1 signature (encoded r, s, v).
     * @return           Whether the signature is valid or not.
     */
    function isValidECDSASignature(
        address signer,
        bytes32 digest,
        bytes memory signature
    ) internal pure returns (bool) {
        if (signature.length == 64) {
            (bytes32 r, bytes32 vs) = decodeShortECDSASignature(signature);
            return isValidECDSASignature(signer, digest, r, vs);
        }

        return validateECDSASignature(signer, digest, signature) == Error.NoError;
    }

    /**
     * @dev    Returns whether an ECDSA/secp256k1 short signature is valid for a signer and digest.
     * @param  signer  The address of the account purported to have signed.
     * @param  digest  The hash of the data that was signed.
     * @param  r       An ECDSA/secp256k1 signature parameter.
     * @param  vs      An ECDSA/secp256k1 short signature parameter.
     * @return         Whether the signature is valid or not.
     */
    function isValidECDSASignature(address signer, bytes32 digest, bytes32 r, bytes32 vs) internal pure returns (bool) {
        return validateECDSASignature(signer, digest, r, vs) == Error.NoError;
    }

    /**
     * @dev    Returns the signer of an ECDSA/secp256k1 signature for some digest.
     * @param  digest    The hash of the data that was signed.
     * @param  signature A byte array ECDSA/secp256k1 signature.
     * @return           An error, if any, that occurred during the signer recovery.
     * @return           The address of the account recovered form the signature (0 if error).
     */
    function recoverECDSASigner(bytes32 digest, bytes memory signature) internal pure returns (Error, address) {
        if (signature.length != 65) return (Error.InvalidSignatureLength, address(0));

        (uint8 v, bytes32 r, bytes32 s) = decodeECDSASignature(signature);

        return recoverECDSASigner(digest, v, r, s);
    }

    /**
     * @dev    Returns the signer of an ECDSA/secp256k1 short signature for some digest.
     * @dev    See https://eips.ethereum.org/EIPS/eip-2098
     * @param  digest The hash of the data that was signed.
     * @param  r      An ECDSA/secp256k1 signature parameter.
     * @param  vs     An ECDSA/secp256k1 short signature parameter.
     * @return        An error, if any, that occurred during the signer recovery.
     * @return        The address of the account recovered form the signature (0 if error).
     */
    function recoverECDSASigner(bytes32 digest, bytes32 r, bytes32 vs) internal pure returns (Error, address) {
        unchecked {
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            return recoverECDSASigner(digest, v, r, s);
        }
    }

    /**
     * @dev    Returns the signer of an ECDSA/secp256k1 signature for some digest.
     * @param  digest The hash of the data that was signed.
     * @param  v      An ECDSA/secp256k1 signature parameter.
     * @param  r      An ECDSA/secp256k1 signature parameter.
     * @param  s      An ECDSA/secp256k1 signature parameter.
     * @return        An error, if any, that occurred during the signer recovery.
     * @return signer The address of the account recovered form the signature (0 if error).
     */
    function recoverECDSASigner(
        bytes32 digest,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (Error, address signer) {
        // Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}.
        if (uint256(s) > uint256(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0))
            return (Error.InvalidSignatureS, address(0));

        if (v != 27 && v != 28) return (Error.InvalidSignatureV, address(0));

        signer = ecrecover(digest, v, r, s);

        return (signer == address(0)) ? (Error.InvalidSignature, address(0)) : (Error.NoError, signer);
    }

    /**
     * @dev    Returns an error, if any, in validating an ECDSA/secp256k1 signature for a signer and digest.
     * @param  signer    The address of the account purported to have signed.
     * @param  digest    The hash of the data that was signed.
     * @param  signature A byte array ERC1271 signature.
     * @return           An error, if any, that occurred during the signer recovery.
     */
    function validateECDSASignature(
        address signer,
        bytes32 digest,
        bytes memory signature
    ) internal pure returns (Error) {
        (Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, signature);

        return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
    }

    /**
     * @dev    Returns an error, if any, in validating an ECDSA/secp256k1 short signature for a signer and digest.
     * @param  signer The address of the account purported to have signed.
     * @param  digest The hash of the data that was signed.
     * @param  r      An ECDSA/secp256k1 signature parameter.
     * @param  vs     An ECDSA/secp256k1 short signature parameter.
     * @return        An error, if any, that occurred during the signer recovery.
     */
    function validateECDSASignature(
        address signer,
        bytes32 digest,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (Error) {
        (Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, r, vs);

        return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
    }

    /**
     * @dev    Returns an error, if any, in validating an ECDSA/secp256k1 signature for a signer and digest.
     * @param  signer The address of the account purported to have signed.
     * @param  digest The hash of the data that was signed.
     * @param  v      An ECDSA/secp256k1 signature parameter.
     * @param  r      An ECDSA/secp256k1 signature parameter.
     * @param  s      An ECDSA/secp256k1 signature parameter.
     * @return        An error, if any, that occurred during the signer recovery.
     */
    function validateECDSASignature(
        address signer,
        bytes32 digest,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (Error) {
        (Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, v, r, s);

        return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
    }

    /**
     * @dev    Returns an error if `signer` is not `recoveredSigner`.
     * @param  signer          The address of the some signer.
     * @param  recoveredSigner The address of the some recoveredSigner.
     * @return                 An error if `signer` is not `recoveredSigner`.
     */
    function validateRecoveredSigner(address signer, address recoveredSigner) internal pure returns (Error) {
        return (signer == recoveredSigner) ? Error.NoError : Error.SignerMismatch;
    }
}
IBatchGovernor.sol 241 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

/**
 * @title  Extension for Governor with specialized strict proposal parameters, vote batching, and an epoch clock.
 * @author M^0 Labs
 */
interface IBatchGovernor is IGovernor {
    /* ============ Enums ============ */

    /**
     * @notice The type of support to cast for a proposal.
     * @param  No  The voter does not support the proposal.
     * @param  Yes The voter supports the proposal.
     */
    enum VoteType {
        No,
        Yes
    }

    /* ============ Custom Errors ============ */

    /// @notice Revert message when a voter is trying to vote on a proposal they already voted on.
    error AlreadyVoted();

    /// @notice Revert message when input arrays do not match in length.
    error ArrayLengthMismatch(uint256 length1, uint256 length2);

    /// @notice Revert message when the proposal IDs array is empty.
    error EmptyProposalIdsArray();

    /**
     * @notice Revert message when execution of a proposal fails.
     * @param  data The revert data returned due to the failed execution.
     */
    error ExecutionFailed(bytes data);

    /// @notice Revert message when a proposal's call data is not specifically supported.
    error InvalidCallData();

    /// @notice Revert message when a proposal's call data array is not of length 1.
    error InvalidCallDatasLength();

    /// @notice Revert message when a proposal target is not this governor itself.
    error InvalidTarget();

    /// @notice Revert message when a proposal's targets array is not of length 1.
    error InvalidTargetsLength();

    /// @notice Revert message when a proposal value is not 0 ETH.
    error InvalidValue();

    /// @notice Revert message when a proposal's values array is not of length 1.
    error InvalidValuesLength();

    /// @notice Revert message when a an invalid vote start is detected.
    error InvalidVoteStart();

    /// @notice Revert message when the vote token specified in the constructor is address(0).
    error InvalidVoteTokenAddress();

    /// @notice Revert message when the caller of a governance-controlled function is not this governor itself.
    error NotSelf();

    /// @notice Revert message when the proposal information provided cannot be executed.
    error ProposalCannotBeExecuted();

    /// @notice Revert message when the proposal does not exist.
    error ProposalDoesNotExist();

    /// @notice Revert message when the proposal already exists.
    error ProposalExists();

    /**
     * @notice Revert message when voting on a proposal that is not in an active state (i.e. not collecting votes).
     * @param  state The current state of the proposal.
     */
    error ProposalInactive(ProposalState state);

    /// @notice Revert message when voting on a proposal with a zero voting weight.
    error ZeroVotingPower();

    /* ============ Interactive Functions ============ */

    /**
     * @notice Allows the caller to cast votes on multiple proposals.
     * @param  proposalIds The list of unique proposal IDs being voted on.
     * @param  supportList The list of support type per proposal IDs to cast.
     * @return weight      The number of votes cast for each proposal (the same for all of them).
     */
    function castVotes(uint256[] calldata proposalIds, uint8[] calldata supportList) external returns (uint256 weight);

    /**
     * @notice Allows a signer to cast votes on multiple proposals via an ECDSA secp256k1 signature.
     * @param  proposalIds The list of unique proposal IDs being voted on.
     * @param  supportList The list of support type per proposal IDs to cast.
     * @param  v           An ECDSA secp256k1 signature parameter.
     * @param  r           An ECDSA secp256k1 signature parameter.
     * @param  s           An ECDSA secp256k1 signature parameter.
     * @return weight      The number of votes cast for each proposal (the same for all of them).
     */
    function castVotesBySig(
        uint256[] calldata proposalIds,
        uint8[] calldata supportList,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 weight);

    /**
     * @notice Allows a signer to cast votes on multiple proposals via an arbitrary signature.
     * @param  voter       The address of the account casting the votes.
     * @param  proposalIds The list of unique proposal IDs being voted on.
     * @param  supportList The list of support type per proposal IDs to cast.
     * @param  signature   An arbitrary signature
     * @return weight      The number of votes cast for each proposal (the same for all of them).
     */
    function castVotesBySig(
        address voter,
        uint256[] calldata proposalIds,
        uint8[] calldata supportList,
        bytes memory signature
    ) external returns (uint256 weight);

    /**
     * @notice Allows the caller to cast votes with reason on multiple proposals.
     * @param  proposalIds The list of unique proposal IDs being voted on.
     * @param  supportList The list of support type per proposal IDs to cast.
     * @param  reasonList  The list of reason per proposal IDs to cast.
     * @return weight      The number of votes cast for each proposal (the same for all of them).
     */
    function castVotesWithReason(
        uint256[] calldata proposalIds,
        uint8[] calldata supportList,
        string[] calldata reasonList
    ) external returns (uint256 weight);

    /**
     * @notice Allows a signer to cast votes with reason on multiple proposals via an ECDSA secp256k1 signature.
     * @param  proposalIds The list of unique proposal IDs being voted on.
     * @param  supportList The list of support type per proposal IDs to cast.
     * @param  reasonList  The list of reason per proposal IDs to cast.
     * @param  v           An ECDSA secp256k1 signature parameter.
     * @param  r           An ECDSA secp256k1 signature parameter.
     * @param  s           An ECDSA secp256k1 signature parameter.
     * @return weight      The number of votes cast for each proposal (the same for all of them).
     */
    function castVotesWithReasonBySig(
        uint256[] calldata proposalIds,
        uint8[] calldata supportList,
        string[] calldata reasonList,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256 weight);

    /**
     * @notice Allows a signer to cast votes with reason on multiple proposals via an arbitrary signature.
     * @param  voter       The address of the account casting the votes.
     * @param  proposalIds The list of unique proposal IDs being voted on.
     * @param  supportList The list of support type per proposal IDs to cast.
     * @param  reasonList  The list of reason per proposal IDs to cast.
     * @param  signature   An arbitrary signature
     * @return weight      The number of votes cast for each proposal (the same for all of them).
     */
    function castVotesWithReasonBySig(
        address voter,
        uint256[] calldata proposalIds,
        uint8[] calldata supportList,
        string[] calldata reasonList,
        bytes memory signature
    ) external returns (uint256 weight);

    /* ============ View/Pure Functions ============ */

    /**
     * @notice Returns the ballot digest to be signed, via EIP-712, given an internal digest (i.e. hash struct).
     * @param  proposalId The unique proposal ID being voted on.
     * @param  support    The type of support to cast for the proposal.
     * @return The digest to be signed.
     */
    function getBallotDigest(uint256 proposalId, uint8 support) external view returns (bytes32);

    /**
     * @notice Returns the ballots digest to be signed, via EIP-712, given an internal digest (i.e. hash struct).
     * @param  proposalIds The list of unique proposal IDs being voted on.
     * @param  supportList The list of support type per proposal IDs to cast.
     * @return The digest to be signed.
     */
    function getBallotsDigest(
        uint256[] calldata proposalIds,
        uint8[] calldata supportList
    ) external view returns (bytes32);

    /**
     * @notice Returns the ballot with reason digest to be signed, via EIP-712,
     *         given an internal digest (i.e. hash struct).
     * @param  proposalId The unique proposal ID being voted on.
     * @param  support    The type of support to cast for the proposal.
     * @param  reason     The reason for which the caller casts their vote, if any.
     * @return The digest to be signed.
     */
    function getBallotWithReasonDigest(
        uint256 proposalId,
        uint8 support,
        string calldata reason
    ) external view returns (bytes32);

    /**
     * @notice Returns the ballots with reason digest to be signed, via EIP-712,
     *         given an internal digest (i.e. hash struct).
     * @param  proposalIds The list of unique proposal IDs being voted on.
     * @param  supportList The list of support type per proposal IDs to cast.
     * @param  reasonList  The list of reason per proposal IDs to cast.
     * @return The digest to be signed.
     */
    function getBallotsWithReasonDigest(
        uint256[] calldata proposalIds,
        uint8[] calldata supportList,
        string[] calldata reasonList
    ) external view returns (bytes32);

    /**
     * @notice Returns the unique identifier for the proposal if it were created at this exact moment.
     * @param  callData The single call data used to call this governor upon execution of a proposal.
     * @return The unique identifier for the proposal.
     */
    function hashProposal(bytes memory callData) external view returns (uint256);

    /// @notice Returns the EIP-5805 token contact used for determine voting power and total supplies.
    function voteToken() external view returns (address);

    /// @notice Returns the EIP712 typehash used in the encoding of the digest for `castVotesBySig` function.
    function BALLOTS_TYPEHASH() external pure returns (bytes32);

    /// @notice Returns the EIP712 typehash used in the encoding of the digest for `castVotesWithReasonBySig` function.
    function BALLOTS_WITH_REASON_TYPEHASH() external pure returns (bytes32);
}
IERC20Extended.sol 73 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import { IERC20 } from "./IERC20.sol";
import { IERC3009 } from "./IERC3009.sol";

/**
 * @title  An ERC20 token extended with EIP-2612 permits for signed approvals (via EIP-712
 *         and with EIP-1271 compatibility), and extended with EIP-3009 transfer with authorization (via EIP-712).
 * @author M^0 Labs
 * @dev    The additional interface as defined by EIP-2612: https://eips.ethereum.org/EIPS/eip-2612
 */
interface IERC20Extended is IERC20, IERC3009 {
    /* ============ Custom Errors ============ */

    /**
     * @notice Revert message when spender's allowance is not sufficient.
     * @param  spender    Address that may be allowed to operate on tokens without being their owner.
     * @param  allowance  Amount of tokens a `spender` is allowed to operate with.
     * @param  needed     Minimum amount required to perform a transfer.
     */
    error InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @notice Revert message emitted when the transferred amount is insufficient.
     * @param  amount Amount transferred.
     */
    error InsufficientAmount(uint256 amount);

    /**
     * @notice Revert message emitted when the recipient of a token is invalid.
     * @param  recipient Address of the invalid recipient.
     */
    error InvalidRecipient(address recipient);

    /* ============ Interactive Functions ============ */

    /**
     * @notice Approves `spender` to spend up to `amount` of the token balance of `owner`, via a signature.
     * @param  owner    The address of the account who's token balance is being approved to be spent by `spender`.
     * @param  spender  The address of an account allowed to spend on behalf of `owner`.
     * @param  value    The amount of the allowance being approved.
     * @param  deadline The last block number where the signature is still valid.
     * @param  v        An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     * @param  r        An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     * @param  s        An ECDSA secp256k1 signature parameter (EIP-2612 via EIP-712).
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Approves `spender` to spend up to `amount` of the token balance of `owner`, via a signature.
     * @param  owner     The address of the account who's token balance is being approved to be spent by `spender`.
     * @param  spender   The address of an account allowed to spend on behalf of `owner`.
     * @param  value     The amount of the allowance being approved.
     * @param  deadline  The last block number where the signature is still valid.
     * @param  signature An arbitrary signature (EIP-712).
     */
    function permit(address owner, address spender, uint256 value, uint256 deadline, bytes memory signature) external;

    /* ============ View/Pure Functions ============ */

    /// @notice Returns the EIP712 typehash used in the encoding of the digest for the permit function.
    function PERMIT_TYPEHASH() external view returns (bytes32);
}
IERC712Extended.sol 33 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

/**
 * @title  EIP-712 extended by EIP-5267.
 * @author M^0 Labs
 * @dev    The additional interface as defined by EIP-5267: https://eips.ethereum.org/EIPS/eip-5267
 */
interface IERC712Extended is IERC712 {
    /* ============ Events ============ */

    /// @notice MAY be emitted to signal that the domain could have changed.
    event EIP712DomainChanged();

    /* ============ View/Pure Functions ============ */

    /// @notice Returns the fields and values that describe the domain separator used by this contract for EIP-712.
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}
IStatefulERC712.sol 29 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

/**
 * @title  Stateful Extension for EIP-712 typed structured data hashing and signing with nonces.
 * @author M^0 Labs
 */
interface IStatefulERC712 is IERC712Extended {
    /* ============ Custom Errors ============ */

    /**
     * @notice Revert message when a signing account's nonce is not the expected current nonce.
     * @param  nonce         The nonce used in the signature.
     * @param  expectedNonce The expected nonce to be used in a signature by the signing account.
     */
    error InvalidAccountNonce(uint256 nonce, uint256 expectedNonce);

    /* ============ View/Pure Functions ============ */

    /**
     * @notice Returns the next nonce to be used in a signature by `account`.
     * @param  account The address of some account.
     * @return nonce   The next nonce to be used in a signature by `account`.
     */
    function nonces(address account) external view returns (uint256 nonce);
}
IERC20Like.sol 13 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

/// @title Interface of the ERC20 standard as needed by ERC20Helper.
interface IERC20Like {

    function approve(address spender_, uint256 amount_) external returns (bool success_);

    function transfer(address recipient_, uint256 amount_) external returns (bool success_);

    function transferFrom(address owner_, address recipient_, uint256 amount_) external returns (bool success_);

}
IEpochBasedVoteToken.sol 70 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import { IERC20Extended } from "../../../lib/common/src/interfaces/IERC20Extended.sol";

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

/**
 * @title  Extension for an ERC5805 token that uses epochs as its clock mode and delegation via IERC1271.
 * @author M^0 Labs
 */
interface IEpochBasedVoteToken is IERC5805, IERC20Extended {
    /* ============ Custom Errors ============ */

    /// @notice Revert message when the provided epoch is zero.
    error EpochZero();

    /* ============ Interactive Functions ============ */

    /**
     * @notice Changes the voting power delegation for `account` to `delegatee`.
     * @param  account   The purported address of the signing account.
     * @param  delegatee The address the voting power of `account` will be delegated to.
     * @param  nonce     The nonce used for the signature.
     * @param  expiry    The timestamp until which the signature is still valid.
     * @param  signature A byte array signature.
     */
    function delegateBySig(
        address account,
        address delegatee,
        uint256 nonce,
        uint256 expiry,
        bytes memory signature
    ) external;

    /* ============ View/Pure Functions ============ */

    /**
     * @notice Returns the digest to be signed, via EIP-712, given an internal digest (i.e. hash struct).
     * @param  delegatee The address of the delegatee to delegate to.
     * @param  nonce     The nonce of the account delegating.
     * @param  expiry    The timestamp until which the signature is still valid.
     * @return The digest to be signed.
     */
    function getDelegationDigest(address delegatee, uint256 nonce, uint256 expiry) external view returns (bytes32);

    /**
     * @notice Returns the token balance of `account` at a past clock value `epoch`.
     * @param  account The address of some account.
     * @param  epoch   The epoch number as a clock value.
     * @return The token balance `account` at `epoch`.
     */
    function pastBalanceOf(address account, uint256 epoch) external view returns (uint256);

    /**
     * @notice Returns the delegatee of `account` at a past clock value `epoch`.
     * @param  account The address of some account.
     * @param  epoch   The epoch number as a clock value.
     * @return The delegatee of the voting power of `account` at `epoch`.
     */
    function pastDelegates(address account, uint256 epoch) external view returns (address);

    /**
     * @notice Returns the total token supply at a past clock value `epoch`.
     * @param  epoch The epoch number as a clock value.
     * @return The total token supply at `epoch`.
     */
    function pastTotalSupply(uint256 epoch) external view returns (uint256);
}
IEpochBasedInflationaryVoteToken.sol 64 lines
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

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

/**
 * @title  Extension for an EpochBasedVoteToken token that allows for inflating tokens and voting power.
 * @author M^0 Labs
 */
interface IEpochBasedInflationaryVoteToken is IEpochBasedVoteToken {
    /* ============ Events ============ */

    /**
     * @notice Emitted when `account` is manually synced.
     * @param  account The address of an account that is synced.
     */
    event Sync(address indexed account);

    /* ============ Custom Errors ============ */

    /// @notice Revert message when trying to mark an account as participated in an epoch where it already participated.
    error AlreadyParticipated();

    /**
     * @notice Revert message when the proposed epoch is larger than the current epoch.
     * @param  currentEpoch The current epoch clock value.
     * @param  epoch        The handled epoch clock value.
     */
    error FutureEpoch(uint16 currentEpoch, uint16 epoch);

    /// @notice Revert message when trying to construct contact with inflation above 100%.
    error InflationTooHigh();

    /// @notice Revert message when trying to perform an action not allowed outside of designated voting epochs.
    error NotVoteEpoch();

    /// @notice Revert message when trying to perform an action not allowed during designated voting epochs.
    error VoteEpoch();

    /* ============ Interactive Functions ============ */

    /**
     * @dev   Syncs `account` so that its balance Snap array in storage, reflects their unrealized inflation.
     * @param account The address of the account to sync.
     */
    function sync(address account) external;

    /* ============ View/Pure Functions ============ */

    /**
     * @notice Returns whether `delegatee` has participated in voting during clock value `epoch`.
     * @param  delegatee The address of a delegatee with voting power.
     * @param  epoch     The epoch number as a clock value.
     * @return Whether `delegatee` has participated in voting during `epoch`.
     */
    function hasParticipatedAt(address delegatee, uint256 epoch) external view returns (bool);

    /// @notice Returns the participation inflation rate used to inflate tokens for participation.
    function participationInflation() external view returns (uint16);

    /// @notice Returns 100% in basis point, to be used to correctly ascertain the participation inflation rate.
    function ONE() external pure returns (uint16);
}

Read Contract

BALLOTS_TYPEHASH 0xf046c646 → bytes32
BALLOTS_WITH_REASON_TYPEHASH 0x8e9d8c34 → bytes32
BALLOT_TYPEHASH 0xdeaaa7cc → bytes32
BALLOT_WITH_REASON_TYPEHASH 0xd11d19c2 → bytes32
CLOCK_MODE 0x4bf5d7e9 → string
COUNTING_MODE 0xdd4e2ba5 → string
DOMAIN_SEPARATOR 0x3644e515 → bytes32
cashToken 0xa7224687 → address
clock 0x91ddadf4 → uint48
eip712Domain 0x84b0196e → bytes1, string, string, uint256, address, bytes32, uint256[]
emergencyGovernor 0xf52c93c5 → address
getBallotDigest 0x55a73c53 → bytes32
getBallotWithReasonDigest 0x96ccd673 → bytes32
getBallotsDigest 0x70edcbc4 → bytes32
getBallotsWithReasonDigest 0x849a83b4 → bytes32
getProposal 0xc7f758a8 → uint48, uint48, uint8, uint256, uint256, address, uint256
getProposalFee 0x8233c0a7 → address, uint256
getVotes 0xeb9019d4 → uint256
hasVoted 0x43859632 → bool
hasVotedOnAllProposals 0x230bcb37 → bool
hashProposal 0x4d8e0825 → uint256
hashProposal 0xc59057e4 → uint256
maxTotalZeroRewardPerActiveEpoch 0x83c6a8d3 → uint256
name 0x06fdde03 → string
numberOfProposalsAt 0x7fcfb164 → uint256
numberOfProposalsVotedOnAt 0x75691bff → uint256
proposalDeadline 0xc01f9e37 → uint256
proposalFee 0xc27cabb5 → uint256
proposalProposer 0x143489d0 → address
proposalSnapshot 0x2d63f693 → uint256
proposalThreshold 0xb58131b0 → uint256
proposalVotes 0x544ffc9c → uint256, uint256, uint256
quorum 0x1703a018 → uint256
registrar 0x2b20e397 → address
state 0x3e4f49e6 → uint8
token 0xfc0c546a → address
vault 0xfbfa77cf → address
voteToken 0x160d66ae → address
votingDelay 0x3932abb1 → uint256
votingPeriod 0x02a251a3 → uint256
zeroGovernor 0x8df3227f → address
zeroToken 0xf294bd92 → address

Write Contract 21 functions

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

addToList 0xb4d87a12
bytes32 list_
address account_
castVote 0x56781388
uint256 proposalId_
uint8 support_
returns: uint256
castVoteBySig 0x3bccf4fd
uint256 proposalId_
uint8 support_
uint8 v_
bytes32 r_
bytes32 s_
returns: uint256
castVoteBySig 0x5b18c26a
address voter_
uint256 proposalId_
uint8 support_
bytes signature_
returns: uint256
castVoteWithReason 0x7b3c71d3
uint256 proposalId_
uint8 support_
string reason_
returns: uint256
castVoteWithReasonBySig 0xc64d2ee6
address voter_
uint256 proposalId_
uint8 support_
string reason_
bytes signature_
returns: uint256
castVoteWithReasonBySig 0xcee87708
uint256 proposalId_
uint8 support_
string reason_
uint8 v_
bytes32 r_
bytes32 s_
returns: uint256
castVotes 0x64a38bf9
uint256[] proposalIds_
uint8[] supportList_
returns: uint256
castVotesBySig 0x0dd320e9
address voter_
uint256[] proposalIds_
uint8[] supportList_
bytes signature_
returns: uint256
castVotesBySig 0x8968eb07
uint256[] proposalIds_
uint8[] supportList_
uint8 v_
bytes32 r_
bytes32 s_
returns: uint256
castVotesWithReason 0x24b2c7b3
uint256[] proposalIds_
uint8[] supportList_
string[] reasonList_
returns: uint256
castVotesWithReasonBySig 0x30717129
address voter_
uint256[] proposalIds_
uint8[] supportList_
string[] reasonList_
bytes signature_
returns: uint256
castVotesWithReasonBySig 0x9b9e9cf8
uint256[] proposalIds_
uint8[] supportList_
string[] reasonList_
uint8 v_
bytes32 r_
bytes32 s_
returns: uint256
execute 0x2656227d
address[] targets_
uint256[] values_
bytes[] callDatas_
bytes32
returns: uint256
propose 0x7d5e81e2
address[] targets_
uint256[] values_
bytes[] callDatas_
string description_
returns: uint256
removeFromAndAddToList 0x519c9587
bytes32 list_
address accountToRemove_
address accountToAdd_
removeFromList 0xd48d8423
bytes32 list_
address account_
sendProposalFeeToVault 0x189abd19
uint256 proposalId_
setCashToken 0xc9167e6c
address newCashToken_
uint256 newProposalFee_
setKey 0x07a00330
bytes32 key_
bytes32 value_
setProposalFee 0x10bf5068
uint256 newProposalFee_

Token Balances (1)

View Transfers →
WETH 5.2

Recent Transactions

No transactions found for this address