Address Contract Verified
Address
0xB024aC5a7c6bC92fbACc8C3387E628a07e1Da016
Balance
0 ETH
Nonce
1
Code Size
20335 bytes
Creator
0x9ffF4Be0...45C8 at tx 0xd1ed01be...8bc61f
Indexed Transactions
0
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 →Recent Transactions
No transactions found for this address