Address Contract Verified
Address
0x138D8Aef5Cbbbb9Ea8da98CC0847FE0F3b573b40
Balance
0 ETH
Nonce
1
Code Size
11478 bytes
Creator
0xEE5DB9d9...06aE at tx 0xab85dc2a...389d87
Indexed Transactions
0
Contract Bytecode
11478 bytes
0x6080604052600436106200019f5760003560e01c806379ba509711620000e7578063c4d66de81162000089578063f2fde38b1162000060578063f2fde38b14620005f8578063f3d93f1e146200061d578063ffc4992f146200064257600080fd5b8063c4d66de8146200058e578063d560fd2d14620005b3578063e30c397814620005cb57600080fd5b8063906f634311620000be578063906f6343146200045e5780639bb8dcfd1462000509578063a0a8e460146200053f57600080fd5b806379ba509714620003ee578063852e98fc14620004065780638da5cb5b146200042b57600080fd5b80633af4b97c11620001515780634f1ef28611620001285780634f1ef286146200034357806352d1902d146200035a57806361ae74b9146200038157600080fd5b80633af4b97c14620002a1578063428eb53814620002d75780634dfbbdaa146200030d57600080fd5b8063264aa22a1162000186578063264aa22a14620001fb5780633659cfe61462000257578063395db2cd146200027c57600080fd5b806323452b9c14620001a4578063239cad4f14620001be575b600080fd5b348015620001b157600080fd5b50620001bc62000678565b005b348015620001cb57600080fd5b50620001e3620001dd3660046200207b565b6200074b565b604051620001f2919062002112565b60405180910390f35b3480156200020857600080fd5b50620002317f00000000000000000000000046ea3fd17deb7b291aea60e67e5cb3a104fea11d81565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001620001f2565b3480156200026457600080fd5b50620001bc620002763660046200207b565b6200083e565b3480156200028957600080fd5b50620001bc6200029b3660046200207b565b6200099d565b348015620002ae57600080fd5b50620002317f0000000000000000000000005a28eef0ed8cce44cda9d7097ecce041bb51b9d481565b348015620002e457600080fd5b50620002317f000000000000000000000000785708d09b89c470ad7b5b3f8ac804ce72b6b28281565b3480156200031a57600080fd5b50620002317f0000000000000000000000003bdafe0d299168f6ebb6e1b4e1e9702a30f6364d81565b620001bc6200035436600462002296565b62000a72565b3480156200036757600080fd5b506200037262000bc3565b604051908152602001620001f2565b3480156200038e57600080fd5b50620003a6620003a036600462002370565b62000c5a565b6040805173ffffffffffffffffffffffffffffffffffffffff968716815294861660208601529285169284019290925283166060830152909116608082015260a001620001f2565b348015620003fb57600080fd5b50620001bc620013ba565b3480156200041357600080fd5b50620001bc6200042536600462002438565b620014c5565b3480156200043857600080fd5b5060005462010000900473ffffffffffffffffffffffffffffffffffffffff1662000231565b3480156200046b57600080fd5b50620004c86200047d3660046200207b565b73ffffffffffffffffffffffffffffffffffffffff90811660009081526003602081905260409091208054600182015460028301549290930154908416949284169391821692911690565b6040805173ffffffffffffffffffffffffffffffffffffffff95861681529385166020850152918416918301919091529091166060820152608001620001f2565b3480156200051657600080fd5b506200052e6200052836600462002438565b620015b7565b6040519015158152602001620001f2565b3480156200054c57600080fd5b50604080518082018252600581527f312e322e3000000000000000000000000000000000000000000000000000000060208201529051620001f2919062002470565b3480156200059b57600080fd5b50620001bc620005ad3660046200207b565b620015f4565b348015620005c057600080fd5b50620001e362001781565b348015620005d857600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff1662000231565b3480156200060557600080fd5b50620001bc620006173660046200207b565b620018b1565b3480156200062a57600080fd5b50620001bc6200063c36600462002438565b62001914565b3480156200064f57600080fd5b50620002317f000000000000000000000000aed75d1e5c1821e2ec29d5d24b794b13c34c5d6381565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff163314620006d0576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001546000805460405173ffffffffffffffffffffffffffffffffffffffff9384169362010000909204909116917f682679deecef4dcd49674845cc1e3a075fea9073680aa445a8207d5a4bdea3da91a3600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6200077e6040518060a0016040528060608152602001606081526020016060815260200160608152602001606081525090565b600080600080620007d48673ffffffffffffffffffffffffffffffffffffffff90811660009081526003602081905260409091208054600182015460028301549290930154908416949284169391821692911690565b93509350935093506040518060a00160405280620007f28862001a02565b8152602001620008028662001a02565b8152602001620008128562001a02565b8152602001620008228462001a02565b8152602001620008328362001a02565b90529695505050505050565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000138d8aef5cbbbb9ea8da98cc0847fe0f3b573b40163003620008ae576040517f43d22ee900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000138d8aef5cbbbb9ea8da98cc0847fe0f3b573b4073ffffffffffffffffffffffffffffffffffffffff16620009247f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff161462000972576040517fe74d90a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6200097d8162001aaf565b6200099a8160405180602001604052806000815250600062001b07565b50565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff163314620009f5576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8381169182179092556000805460405192936201000090910416917f4f2638f5949b9614ef8d5e268cb51348ad7f434a34812bf64b6e95014fbd357e9190a350565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000138d8aef5cbbbb9ea8da98cc0847fe0f3b573b4016300362000ae2576040517f43d22ee900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000138d8aef5cbbbb9ea8da98cc0847fe0f3b573b4073ffffffffffffffffffffffffffffffffffffffff1662000b587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff161462000ba6576040517fe74d90a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62000bb18262001aaf565b62000bbf8282600162001b07565b5050565b60003073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000138d8aef5cbbbb9ea8da98cc0847fe0f3b573b40161462000c35576040517f575bc92e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc90565b60008080808080808b8b828162000c755762000c7562002485565b62000c8d92602060609092020190810191506200207b565b91508173ffffffffffffffffffffffffffffffffffffffff160362000cde576040517f7c2e503b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000aed75d1e5c1821e2ec29d5d24b794b13c34c5d6360405162000d0d9062002048565b73ffffffffffffffffffffffffffffffffffffffff9091168152604060208201819052600090820152606001604051809103906000f08015801562000d56573d6000803e3d6000fd5b506040519096507fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088901b169081907f0000000000000000000000005a28eef0ed8cce44cda9d7097ecce041bb51b9d49062000db49062002048565b73ffffffffffffffffffffffffffffffffffffffff90911681526040602082018190526000908201526060018190604051809103906000f590508015801562000e01573d6000803e3d6000fd5b509550807f000000000000000000000000785708d09b89c470ad7b5b3f8ac804ce72b6b28260405162000e349062002048565b73ffffffffffffffffffffffffffffffffffffffff90911681526040602082018190526000908201526060018190604051809103906000f590508015801562000e81573d6000803e3d6000fd5b509450807f0000000000000000000000003bdafe0d299168f6ebb6e1b4e1e9702a30f6364d60405162000eb49062002048565b73ffffffffffffffffffffffffffffffffffffffff90911681526040602082018190526000908201526060018190604051809103906000f590508015801562000f01573d6000803e3d6000fd5b509350807f00000000000000000000000046ea3fd17deb7b291aea60e67e5cb3a104fea11d60405162000f349062002048565b73ffffffffffffffffffffffffffffffffffffffff90911681526040602082018190526000908201526060018190604051809103906000f590508015801562000f81573d6000803e3d6000fd5b506040805160808101825273ffffffffffffffffffffffffffffffffffffffff808a1682528881166020808401918252898316848601908152838716606086019081528e8516600081815260039485905297909720955186549086167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216178755935160018701805491871691861691909117905590516002860180549186169185169190911790555193018054939092169216919091179055909350636097bf628d8d620010538e80620024b4565b8b8b896040518863ffffffff1660e01b81526004016200107a97969594939291906200256c565b600060405180830381600087803b1580156200109557600080fd5b505af1158015620010aa573d6000803e3d6000fd5b50505073ffffffffffffffffffffffffffffffffffffffff8716905063cce2df03620010d78c80620024b4565b8a6040518463ffffffff1660e01b8152600401620010f89392919062002653565b600060405180830381600087803b1580156200111357600080fd5b505af115801562001128573d6000803e3d6000fd5b50506040517fa6b63eb800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a811660048301528581166024830152878116604483015260208d013560648301528c3560848301528816925063a6b63eb8915060a401600060405180830381600087803b158015620011b757600080fd5b505af1158015620011cc573d6000803e3d6000fd5b50506040517fcd6dc68700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301528b3560248301528716925063cd6dc6879150604401600060405180830381600087803b1580156200124157600080fd5b505af115801562001256573d6000803e3d6000fd5b50505073ffffffffffffffffffffffffffffffffffffffff84169050632b4656c885896200128b60c08d0160a08e016200207b565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e087901b16815273ffffffffffffffffffffffffffffffffffffffff948516600482015292841660248401529216604482015260208c01356064820152908b0135608482015260608b013560a482015260808b013560c482015260e401600060405180830381600087803b1580156200132957600080fd5b505af11580156200133e573d6000803e3d6000fd5b50506040805173ffffffffffffffffffffffffffffffffffffffff8b811682528a811660208301528981168284015288811660608301528716608082015290517f456d2baf5a87d70e586ec06fb91c2d7849778dd41d80fa826a6ea5bf8d28e3a693509081900360a0019150a150509550955095509550959050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146200140c576040517f065cd53100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805460405133926201000090920473ffffffffffffffffffffffffffffffffffffffff16917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7691a360018054600080547fffffffffffffffffffff0000000000000000000000000000000000000000ffff1673ffffffffffffffffffffffffffffffffffffffff831662010000021790557fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff1633146200151d576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82811660008181526002602090815260408083209486168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905581519283528201929092527fab6a7dc54721d6a1a284ca865830f8981d6f12fbddb3618d1774b71c0036805991015b60405180910390a15050565b73ffffffffffffffffffffffffffffffffffffffff80831660009081526002602090815260408083209385168352929052205460ff165b92915050565b600054610100900460ff161580158062001612575060005460ff1615155b8015620016305750303b15158062001630575060005460ff16600114155b1562001668576040517f439a74c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015620016c757600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b73ffffffffffffffffffffffffffffffffffffffff821662001715576040517f66e7950900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b620017208262001c69565b801562000bbf57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527fbe9b076dc5b65990cca9dd9d7366682482e7817a6f6bc7f4faf4dc32af497f3290602001620015ab565b620017b46040518060a0016040528060608152602001606081526020016060815260200160608152602001606081525090565b6040518060a00160405280620017ea7f000000000000000000000000aed75d1e5c1821e2ec29d5d24b794b13c34c5d6362001a02565b81526020016200181a7f0000000000000000000000005a28eef0ed8cce44cda9d7097ecce041bb51b9d462001a02565b81526020016200184a7f000000000000000000000000785708d09b89c470ad7b5b3f8ac804ce72b6b28262001a02565b81526020016200187a7f0000000000000000000000003bdafe0d299168f6ebb6e1b4e1e9702a30f6364d62001a02565b8152602001620018aa7f00000000000000000000000046ea3fd17deb7b291aea60e67e5cb3a104fea11d62001a02565b9052919050565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff16331462001909576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6200099a8162001d23565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff1633146200196c576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82811660008181526002602090815260408083209486168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905581519283528201929092527f0ebd98f6f75e38ba2f0751378f5c86205cafca83e206cb62795f45fcea7283339101620015ab565b3b151590565b60608173ffffffffffffffffffffffffffffffffffffffff1663a0a8e4606040518163ffffffff1660e01b8152600401600060405180830381865afa92505050801562001a9157506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405262001a8e91908101906200268f565b60015b620015ee57505060408051602081019091526000815290565b919050565b60005462010000900473ffffffffffffffffffffffffffffffffffffffff1633146200099a576040517fd238ed5900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff161562001b425762001b3d8362001df0565b505050565b8273ffffffffffffffffffffffffffffffffffffffff166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801562001bca575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925262001bc79181019062002706565b60015b62001c01576040517fc0bb20b200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc811462001c5b576040517f0849b49600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5062001b3d83838362001eab565b600054610100900460ff1662001cab576040517f624bb4ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffff0000000000000000000000000000000000000000ffff166201000073ffffffffffffffffffffffffffffffffffffffff84169081029190911782556040519091907f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d76908290a350565b6000805460405173ffffffffffffffffffffffffffffffffffffffff808516936201000090930416917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d7691a36000805473ffffffffffffffffffffffffffffffffffffffff80841662010000027fffffffffffffffffffff0000000000000000000000000000000000000000ffff9092169190911790915560015416156200099a57600180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905550565b803b62001e45576040517fc40d973400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260240160405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b62001eb68362001edc565b60008251118062001ec45750805b1562001b3d5762001ed6838362001f33565b50505050565b62001ee78162001df0565b60405173ffffffffffffffffffffffffffffffffffffffff821681527fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b9060200160405180910390a150565b6060823b62001f6e576040517f37f2022900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808473ffffffffffffffffffffffffffffffffffffffff168460405162001f98919062002720565b600060405180830381855af49150503d806000811462001fd5576040519150601f19603f3d011682016040523d82523d6000602084013e62001fda565b606091505b509150915062001feb828262001ff4565b95945050505050565b6060821562002005575080620015ee565b815115620020165781518083602001fd5b6040517f62536b1000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610562806200273f83390190565b803573ffffffffffffffffffffffffffffffffffffffff8116811462001aaa57600080fd5b6000602082840312156200208e57600080fd5b620020998262002056565b9392505050565b60005b83811015620020bd578181015183820152602001620020a3565b50506000910152565b60008151808452620020e0816020860160208601620020a0565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000825160a060208401526200213060c0840182620020c6565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0808584030160408601526200216e8383620020c6565b925060408601519150808584030160608601526200218d8383620020c6565b92506060860151915080858403016080860152620021ac8383620020c6565b925060808601519150808584030160a08601525062001feb8282620020c6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715620022455762002245620021cc565b604052919050565b600067ffffffffffffffff8211156200226a576200226a620021cc565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60008060408385031215620022aa57600080fd5b620022b58362002056565b9150602083013567ffffffffffffffff811115620022d257600080fd5b8301601f81018513620022e457600080fd5b8035620022fb620022f5826200224d565b620021fb565b8181528660208385010111156200231157600080fd5b816020840160208301376000602083830101528093505050509250929050565b6000602082840312156200234457600080fd5b50919050565b6000604082840312156200234457600080fd5b600060c082840312156200234457600080fd5b600080600080600061014086880312156200238a57600080fd5b853567ffffffffffffffff80821115620023a357600080fd5b818801915088601f830112620023b857600080fd5b813581811115620023c857600080fd5b896020606083028501011115620023de57600080fd5b602092830197509550908701359080821115620023fa57600080fd5b50620024098882890162002331565b9350506200241b87604088016200234a565b91506200242c87608088016200235d565b90509295509295909350565b600080604083850312156200244c57600080fd5b620024578362002056565b9150620024676020840162002056565b90509250929050565b602081526000620020996020830184620020c6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112620024ea57600080fd5b83018035915067ffffffffffffffff8211156200250657600080fd5b6020019150368190038213156200251c57600080fd5b9250929050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60a0808252810187905260008860c08301825b8a811015620025d25773ffffffffffffffffffffffffffffffffffffffff620025a88462002056565b1682526020838101359083015260408084013590830152606092830192909101906001016200257f565b508381036020850152620025e881898b62002523565b925050506200260f604083018673ffffffffffffffffffffffffffffffffffffffff169052565b73ffffffffffffffffffffffffffffffffffffffff8416606083015273ffffffffffffffffffffffffffffffffffffffff8316608083015298975050505050505050565b6040815260006200266960408301858762002523565b905073ffffffffffffffffffffffffffffffffffffffff83166020830152949350505050565b600060208284031215620026a257600080fd5b815167ffffffffffffffff811115620026ba57600080fd5b8201601f81018413620026cc57600080fd5b8051620026dd620022f5826200224d565b818152856020838501011115620026f357600080fd5b62001feb826020830160208601620020a0565b6000602082840312156200271957600080fd5b5051919050565b6000825162002734818460208701620020a0565b919091019291505056fe60806040526040516105623803806105628339810160408190526100229161025c565b61002e82826000610035565b5050610346565b61003e8361006b565b60008251118061004b5750805b156100665761006483836100b360201b6100291760201c565b505b505050565b6100748161014a565b6040516001600160a01b03821681527fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b9060200160405180910390a150565b6060823b6100d4576040516337f2022960e01b815260040160405180910390fd5b600080846001600160a01b0316846040516100ef919061032a565b600060405180830381855af49150503d806000811461012a576040519150601f19603f3d011682016040523d82523d6000602084013e61012f565b606091505b50909250905061013f82826101e7565b925050505b92915050565b61015d816101e160201b6100e51760201c565b6101895760405163310365cd60e21b81526001600160a01b038216600482015260240160405180910390fd5b806101c07f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b61021f60201b6100eb1760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b3b151590565b606082156101f6575080610144565b8151156102065781518083602001fd5b60405163062536b160e41b815260040160405180910390fd5b90565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561025357818101518382015260200161023b565b50506000910152565b6000806040838503121561026f57600080fd5b82516001600160a01b038116811461028657600080fd5b60208401519092506001600160401b03808211156102a357600080fd5b818501915085601f8301126102b757600080fd5b8151818111156102c9576102c9610222565b604051601f8201601f19908116603f011681019083821181831017156102f1576102f1610222565b8160405282815288602084870101111561030a57600080fd5b61031b836020830160208801610238565b80955050505050509250929050565b6000825161033c818460208701610238565b9190910192915050565b61020d806103556000396000f3fe60806040523661001357610011610017565b005b6100115b6100276100226100ee565b610133565b565b6060823b610063576040517f37f2022900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808473ffffffffffffffffffffffffffffffffffffffff168460405161008b91906101a8565b600060405180830381855af49150503d80600081146100c6576040519150601f19603f3d011682016040523d82523d6000602084013e6100cb565b606091505b50915091506100da8282610157565b925050505b92915050565b3b151590565b90565b600061012e7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b3660008037600080366000845af43d6000803e808015610152573d6000f35b3d6000fd5b606082156101665750806100df565b8151156101765781518083602001fd5b6040517f62536b1000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000825160005b818110156101c957602081860181015185830152016101af565b50600092019182525091905056fea2646970667358221220dc4d3f0096ef746736792d21828a33955017294e8f127fdabcf6bf06082a501464736f6c63430008100033a26469706673582212205cf3afb1dff2a9652d261c1a4d2bbbc8da4dbd387a6ee69f2fc0a852bd8566b464736f6c63430008100033
Verified Source Code Full Match
Compiler: v0.8.16+commit.07a7930e
EVM: london
Optimization: Yes (500000 runs)
Token.sol 471 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { UUPS } from "../lib/proxy/UUPS.sol";
import { ReentrancyGuard } from "../lib/utils/ReentrancyGuard.sol";
import { ERC721Votes } from "../lib/token/ERC721Votes.sol";
import { ERC721 } from "../lib/token/ERC721.sol";
import { Ownable } from "../lib/utils/Ownable.sol";
import { TokenStorageV1 } from "./storage/TokenStorageV1.sol";
import { TokenStorageV2 } from "./storage/TokenStorageV2.sol";
import { IBaseMetadata } from "./metadata/interfaces/IBaseMetadata.sol";
import { IManager } from "../manager/IManager.sol";
import { IAuction } from "../auction/IAuction.sol";
import { IToken } from "./IToken.sol";
import { VersionedContract } from "../VersionedContract.sol";
/// @title Token
/// @author Rohan Kulkarni
/// @custom:repo github.com/ourzora/nouns-protocol
/// @notice A DAO's ERC-721 governance token
contract Token is IToken, VersionedContract, UUPS, Ownable, ReentrancyGuard, ERC721Votes, TokenStorageV1, TokenStorageV2 {
/// ///
/// IMMUTABLES ///
/// ///
/// @notice The contract upgrade manager
IManager private immutable manager;
/// ///
/// MODIFIERS ///
/// ///
/// @notice Reverts if caller is not an authorized minter
modifier onlyAuctionOrMinter() {
if (msg.sender != settings.auction && !minter[msg.sender]) {
revert ONLY_AUCTION_OR_MINTER();
}
_;
}
/// ///
/// CONSTRUCTOR ///
/// ///
/// @param _manager The contract upgrade manager address
constructor(address _manager) payable initializer {
manager = IManager(_manager);
}
/// ///
/// INITIALIZER ///
/// ///
/// @notice Initializes a DAO's ERC-721 token contract
/// @param _founders The DAO founders
/// @param _initStrings The encoded token and metadata initialization strings
/// @param _metadataRenderer The token's metadata renderer
/// @param _auction The token's auction house
/// @param _initialOwner The initial owner of the token
function initialize(
IManager.FounderParams[] calldata _founders,
bytes calldata _initStrings,
address _metadataRenderer,
address _auction,
address _initialOwner
) external initializer {
// Ensure the caller is the contract manager
if (msg.sender != address(manager)) {
revert ONLY_MANAGER();
}
// Initialize the reentrancy guard
__ReentrancyGuard_init();
// Setup ownable
__Ownable_init(_initialOwner);
// Store the founders and compute their allocations
_addFounders(_founders);
// Decode the token name and symbol
(string memory _name, string memory _symbol, , , , ) = abi.decode(_initStrings, (string, string, string, string, string, string));
// Initialize the ERC-721 token
__ERC721_init(_name, _symbol);
// Store the metadata renderer and auction house
settings.metadataRenderer = IBaseMetadata(_metadataRenderer);
settings.auction = _auction;
}
/// @notice Called by the auction upon the first unpause / token mint to transfer ownership from founder to treasury
/// @dev Only callable by the auction contract
function onFirstAuctionStarted() external override {
if (msg.sender != settings.auction) {
revert ONLY_AUCTION();
}
// Force transfer ownership to the treasury
_transferOwnership(IAuction(settings.auction).treasury());
}
/// @notice Called upon initialization to add founders and compute their vesting allocations
/// @dev We do this by reserving an mapping of [0-100] token indices, such that if a new token mint ID % 100 is reserved, it's sent to the appropriate founder.
/// @param _founders The list of DAO founders
function _addFounders(IManager.FounderParams[] calldata _founders) internal {
// Used to store the total percent ownership among the founders
uint256 totalOwnership;
uint8 numFoundersAdded = 0;
unchecked {
// For each founder:
for (uint256 i; i < _founders.length; ++i) {
// Cache the percent ownership
uint256 founderPct = _founders[i].ownershipPct;
// Continue if no ownership is specified
if (founderPct == 0) {
continue;
}
// Update the total ownership and ensure it's valid
totalOwnership += founderPct;
// Check that founders own less than 100% of tokens
if (totalOwnership > 99) {
revert INVALID_FOUNDER_OWNERSHIP();
}
// Compute the founder's id
uint256 founderId = numFoundersAdded++;
// Get the pointer to store the founder
Founder storage newFounder = founder[founderId];
// Store the founder's vesting details
newFounder.wallet = _founders[i].wallet;
newFounder.vestExpiry = uint32(_founders[i].vestExpiry);
// Total ownership cannot be above 100 so this fits safely in uint8
newFounder.ownershipPct = uint8(founderPct);
// Compute the vesting schedule
uint256 schedule = 100 / founderPct;
// Used to store the base token id the founder will recieve
uint256 baseTokenId;
// For each token to vest:
for (uint256 j; j < founderPct; ++j) {
// Get the available token id
baseTokenId = _getNextTokenId(baseTokenId);
// Store the founder as the recipient
tokenRecipient[baseTokenId] = newFounder;
emit MintScheduled(baseTokenId, founderId, newFounder);
// Update the base token id
baseTokenId = (baseTokenId + schedule) % 100;
}
}
// Store the founders' details
settings.totalOwnership = uint8(totalOwnership);
settings.numFounders = numFoundersAdded;
}
}
/// @dev Finds the next available base token id for a founder
/// @param _tokenId The ERC-721 token id
function _getNextTokenId(uint256 _tokenId) internal view returns (uint256) {
unchecked {
while (tokenRecipient[_tokenId].wallet != address(0)) {
_tokenId = (++_tokenId) % 100;
}
return _tokenId;
}
}
/// ///
/// MINT ///
/// ///
/// @notice Mints tokens to the caller and handles founder vesting
function mint() external nonReentrant onlyAuctionOrMinter returns (uint256 tokenId) {
tokenId = _mintWithVesting(msg.sender);
}
/// @notice Mints tokens to the recipient and handles founder vesting
function mintTo(address recipient) external nonReentrant onlyAuctionOrMinter returns (uint256 tokenId) {
tokenId = _mintWithVesting(recipient);
}
/// @notice Mints the specified amount of tokens to the recipient and handles founder vesting
function mintBatchTo(uint256 amount, address recipient) external nonReentrant onlyAuctionOrMinter returns (uint256[] memory tokenIds) {
tokenIds = new uint256[](amount);
for (uint256 i = 0; i < amount; ) {
tokenIds[i] = _mintWithVesting(recipient);
unchecked {
++i;
}
}
}
function _mintWithVesting(address recipient) internal returns (uint256 tokenId) {
// Cannot realistically overflow
unchecked {
do {
// Get the next token to mint
tokenId = settings.mintCount++;
// Lookup whether the token is for a founder, and mint accordingly if so
} while (_isForFounder(tokenId));
}
// Mint the next available token to the recipient for bidding
_mint(recipient, tokenId);
}
/// @dev Overrides _mint to include attribute generation
/// @param _to The token recipient
/// @param _tokenId The ERC-721 token id
function _mint(address _to, uint256 _tokenId) internal override {
// Mint the token
super._mint(_to, _tokenId);
// Increment the total supply
unchecked {
++settings.totalSupply;
}
// Generate the token attributes
if (!settings.metadataRenderer.onMinted(_tokenId)) revert NO_METADATA_GENERATED();
}
/// @dev Checks if a given token is for a founder and mints accordingly
/// @param _tokenId The ERC-721 token id
function _isForFounder(uint256 _tokenId) private returns (bool) {
// Get the base token id
uint256 baseTokenId = _tokenId % 100;
// If there is no scheduled recipient:
if (tokenRecipient[baseTokenId].wallet == address(0)) {
return false;
// Else if the founder is still vesting:
} else if (block.timestamp < tokenRecipient[baseTokenId].vestExpiry) {
// Mint the token to the founder
_mint(tokenRecipient[baseTokenId].wallet, _tokenId);
return true;
// Else the founder has finished vesting:
} else {
// Remove them from future lookups
delete tokenRecipient[baseTokenId];
return false;
}
}
/// ///
/// BURN ///
/// ///
/// @notice Burns a token owned by the caller
/// @param _tokenId The ERC-721 token id
function burn(uint256 _tokenId) external onlyAuctionOrMinter {
if (ownerOf(_tokenId) != msg.sender) {
revert ONLY_TOKEN_OWNER();
}
_burn(_tokenId);
}
function _burn(uint256 _tokenId) internal override {
super._burn(_tokenId);
unchecked {
--settings.totalSupply;
}
}
/// ///
/// METADATA ///
/// ///
/// @notice The URI for a token
/// @param _tokenId The ERC-721 token id
function tokenURI(uint256 _tokenId) public view override(IToken, ERC721) returns (string memory) {
return settings.metadataRenderer.tokenURI(_tokenId);
}
/// @notice The URI for the contract
function contractURI() public view override(IToken, ERC721) returns (string memory) {
return settings.metadataRenderer.contractURI();
}
/// ///
/// FOUNDERS ///
/// ///
/// @notice The number of founders
function totalFounders() external view returns (uint256) {
return settings.numFounders;
}
/// @notice The founders total percent ownership
function totalFounderOwnership() external view returns (uint256) {
return settings.totalOwnership;
}
/// @notice The vesting details of a founder
/// @param _founderId The founder id
function getFounder(uint256 _founderId) external view returns (Founder memory) {
return founder[_founderId];
}
/// @notice The vesting details of all founders
function getFounders() external view returns (Founder[] memory) {
// Cache the number of founders
uint256 numFounders = settings.numFounders;
// Get a temporary array to hold all founders
Founder[] memory founders = new Founder[](numFounders);
// Cannot realistically overflow
unchecked {
// Add each founder to the array
for (uint256 i; i < numFounders; ++i) {
founders[i] = founder[i];
}
}
return founders;
}
/// @notice The founder scheduled to receive the given token id
/// NOTE: If a founder is returned, there's no guarantee they'll receive the token as vesting expiration is not considered
/// @param _tokenId The ERC-721 token id
function getScheduledRecipient(uint256 _tokenId) external view returns (Founder memory) {
return tokenRecipient[_tokenId % 100];
}
/// @notice Update the list of allocation owners
/// @param newFounders the full list of founders
function updateFounders(IManager.FounderParams[] calldata newFounders) external onlyOwner {
// Cache the number of founders
uint256 numFounders = settings.numFounders;
// Get a temporary array to hold all founders
Founder[] memory cachedFounders = new Founder[](numFounders);
// Cannot realistically overflow
unchecked {
// Add each founder to the array
for (uint256 i; i < numFounders; ++i) {
cachedFounders[i] = founder[i];
}
}
// Keep a mapping of all the reserved token IDs we're set to clear.
bool[] memory clearedTokenIds = new bool[](100);
unchecked {
// for each existing founder:
for (uint256 i; i < cachedFounders.length; ++i) {
// copy the founder into memory
Founder memory cachedFounder = cachedFounders[i];
// Delete the founder from the stored mapping
delete founder[i];
// Some DAOs were initialized with 0 percentage ownership.
// This skips them to avoid a division by zero error.
if (cachedFounder.ownershipPct == 0) {
continue;
}
// using the ownership percentage, get reserved token percentages
uint256 schedule = 100 / cachedFounder.ownershipPct;
// Used to reverse engineer the indices the founder has reserved tokens in.
uint256 baseTokenId;
for (uint256 j; j < cachedFounder.ownershipPct; ++j) {
// Get the next index that hasn't already been cleared
while (clearedTokenIds[baseTokenId] != false) {
baseTokenId = (++baseTokenId) % 100;
}
delete tokenRecipient[baseTokenId];
clearedTokenIds[baseTokenId] = true;
emit MintUnscheduled(baseTokenId, i, cachedFounder);
// Update the base token id
baseTokenId = (baseTokenId + schedule) % 100;
}
}
}
settings.numFounders = 0;
settings.totalOwnership = 0;
emit FounderAllocationsCleared(newFounders);
_addFounders(newFounders);
}
/// ///
/// SETTINGS ///
/// ///
/// @notice The total supply of tokens
function totalSupply() external view returns (uint256) {
return settings.totalSupply;
}
/// @notice The address of the auction house
function auction() external view returns (address) {
return settings.auction;
}
/// @notice The address of the metadata renderer
function metadataRenderer() external view returns (address) {
return address(settings.metadataRenderer);
}
function owner() public view override(IToken, Ownable) returns (address) {
return super.owner();
}
/// @notice Update minters
/// @param _minters Array of structs containing address status as a minter
function updateMinters(MinterParams[] calldata _minters) external onlyOwner {
// Update each minter
for (uint256 i; i < _minters.length; ++i) {
// Skip if the minter is already set to the correct value
if (minter[_minters[i].minter] == _minters[i].allowed) continue;
emit MinterUpdated(_minters[i].minter, _minters[i].allowed);
// Update the minter
minter[_minters[i].minter] = _minters[i].allowed;
}
}
/// @notice Check if an address is a minter
/// @param _minter Address to check
function isMinter(address _minter) external view returns (bool) {
return minter[_minter];
}
/// ///
/// TOKEN UPGRADE ///
/// ///
/// @notice Ensures the caller is authorized to upgrade the contract and that the new implementation is valid
/// @dev This function is called in `upgradeTo` & `upgradeToAndCall`
/// @param _newImpl The new implementation address
function _authorizeUpgrade(address _newImpl) internal view override {
// Ensure the caller is the shared owner of the token and metadata renderer
if (msg.sender != owner()) revert ONLY_OWNER();
// Ensure the implementation is valid
if (!manager.isRegisteredUpgrade(_getImplementation(), _newImpl)) revert INVALID_UPGRADE(_newImpl);
}
}
IToken.sol 142 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../lib/interfaces/IUUPS.sol";
import { IERC721Votes } from "../lib/interfaces/IERC721Votes.sol";
import { IManager } from "../manager/IManager.sol";
import { TokenTypesV1 } from "./types/TokenTypesV1.sol";
import { TokenTypesV2 } from "./types/TokenTypesV2.sol";
/// @title IToken
/// @author Rohan Kulkarni
/// @notice The external Token events, errors and functions
interface IToken is IUUPS, IERC721Votes, TokenTypesV1, TokenTypesV2 {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a token is scheduled to be allocated
/// @param baseTokenId The
/// @param founderId The founder's id
/// @param founder The founder's vesting details
event MintScheduled(uint256 baseTokenId, uint256 founderId, Founder founder);
/// @notice Emitted when a token allocation is unscheduled (removed)
/// @param baseTokenId The token ID % 100
/// @param founderId The founder's id
/// @param founder The founder's vesting details
event MintUnscheduled(uint256 baseTokenId, uint256 founderId, Founder founder);
/// @notice Emitted when a tokens founders are deleted from storage
/// @param newFounders the list of founders
event FounderAllocationsCleared(IManager.FounderParams[] newFounders);
/// @notice Emitted when minters are updated
/// @param minter Address of added or removed minter
/// @param allowed Whether address is allowed to mint
event MinterUpdated(address minter, bool allowed);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the founder ownership exceeds 100 percent
error INVALID_FOUNDER_OWNERSHIP();
/// @dev Reverts if the caller was not the auction contract
error ONLY_AUCTION();
/// @dev Reverts if the caller was not a minter
error ONLY_AUCTION_OR_MINTER();
/// @dev Reverts if the caller was not the token owner
error ONLY_TOKEN_OWNER();
/// @dev Reverts if no metadata was generated upon mint
error NO_METADATA_GENERATED();
/// @dev Reverts if the caller was not the contract manager
error ONLY_MANAGER();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Initializes a DAO's ERC-721 token
/// @param founders The founding members to receive vesting allocations
/// @param initStrings The encoded token and metadata initialization strings
/// @param metadataRenderer The token's metadata renderer
/// @param auction The token's auction house
function initialize(
IManager.FounderParams[] calldata founders,
bytes calldata initStrings,
address metadataRenderer,
address auction,
address initialOwner
) external;
/// @notice Mints tokens to the caller and handles founder vesting
function mint() external returns (uint256 tokenId);
/// @notice Mints tokens to the recipient and handles founder vesting
function mintTo(address recipient) external returns (uint256 tokenId);
/// @notice Mints the specified amount of tokens to the recipient and handles founder vesting
function mintBatchTo(uint256 amount, address recipient) external returns (uint256[] memory tokenIds);
/// @notice Burns a token owned by the caller
/// @param tokenId The ERC-721 token id
function burn(uint256 tokenId) external;
/// @notice The URI for a token
/// @param tokenId The ERC-721 token id
function tokenURI(uint256 tokenId) external view returns (string memory);
/// @notice The URI for the contract
function contractURI() external view returns (string memory);
/// @notice The number of founders
function totalFounders() external view returns (uint256);
/// @notice The founders total percent ownership
function totalFounderOwnership() external view returns (uint256);
/// @notice The vesting details of a founder
/// @param founderId The founder id
function getFounder(uint256 founderId) external view returns (Founder memory);
/// @notice The vesting details of all founders
function getFounders() external view returns (Founder[] memory);
/// @notice Update the list of allocation owners
/// @param newFounders the full list of FounderParam structs
function updateFounders(IManager.FounderParams[] calldata newFounders) external;
/// @notice The founder scheduled to receive the given token id
/// NOTE: If a founder is returned, there's no guarantee they'll receive the token as vesting expiration is not considered
/// @param tokenId The ERC-721 token id
function getScheduledRecipient(uint256 tokenId) external view returns (Founder memory);
/// @notice The total supply of tokens
function totalSupply() external view returns (uint256);
/// @notice The token's auction house
function auction() external view returns (address);
/// @notice The token's metadata renderer
function metadataRenderer() external view returns (address);
/// @notice The owner of the token and metadata renderer
function owner() external view returns (address);
/// @notice Update minters
/// @param _minters Array of structs containing address status as a minter
function updateMinters(MinterParams[] calldata _minters) external;
/// @notice Check if an address is a minter
/// @param _minter Address to check
function isMinter(address _minter) external view returns (bool);
/// @notice Callback called by auction on first auction started to transfer ownership to treasury from founder
function onFirstAuctionStarted() external;
}
UUPS.sol 64 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../interfaces/IUUPS.sol";
import { ERC1967Upgrade } from "./ERC1967Upgrade.sol";
/// @title UUPS
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/utils/UUPSUpgradeable.sol)
/// - Uses custom errors declared in IUUPS
/// - Inherits a modern, minimal ERC1967Upgrade
abstract contract UUPS is IUUPS, ERC1967Upgrade {
/// ///
/// IMMUTABLES ///
/// ///
/// @dev The address of the implementation
address private immutable __self = address(this);
/// ///
/// MODIFIERS ///
/// ///
/// @dev Ensures that execution is via proxy delegatecall with the correct implementation
modifier onlyProxy() {
if (address(this) == __self) revert ONLY_DELEGATECALL();
if (_getImplementation() != __self) revert ONLY_PROXY();
_;
}
/// @dev Ensures that execution is via direct call
modifier notDelegated() {
if (address(this) != __self) revert ONLY_CALL();
_;
}
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Hook to authorize an implementation upgrade
/// @param _newImpl The new implementation address
function _authorizeUpgrade(address _newImpl) internal virtual;
/// @notice Upgrades to an implementation
/// @param _newImpl The new implementation address
function upgradeTo(address _newImpl) external onlyProxy {
_authorizeUpgrade(_newImpl);
_upgradeToAndCallUUPS(_newImpl, "", false);
}
/// @notice Upgrades to an implementation with an additional function call
/// @param _newImpl The new implementation address
/// @param _data The encoded function call
function upgradeToAndCall(address _newImpl, bytes memory _data) external payable onlyProxy {
_authorizeUpgrade(_newImpl);
_upgradeToAndCallUUPS(_newImpl, _data, true);
}
/// @notice The storage slot of the implementation address
function proxiableUUID() external view notDelegated returns (bytes32) {
return _IMPLEMENTATION_SLOT;
}
}
Manager.sol 248 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { UUPS } from "../lib/proxy/UUPS.sol";
import { Ownable } from "../lib/utils/Ownable.sol";
import { ERC1967Proxy } from "../lib/proxy/ERC1967Proxy.sol";
import { ManagerStorageV1 } from "./storage/ManagerStorageV1.sol";
import { IManager } from "./IManager.sol";
import { IToken } from "../token/IToken.sol";
import { IBaseMetadata } from "../token/metadata/interfaces/IBaseMetadata.sol";
import { IAuction } from "../auction/IAuction.sol";
import { ITreasury } from "../governance/treasury/ITreasury.sol";
import { IGovernor } from "../governance/governor/IGovernor.sol";
import { VersionedContract } from "../VersionedContract.sol";
import { IVersionedContract } from "../lib/interfaces/IVersionedContract.sol";
/// @title Manager
/// @author Rohan Kulkarni
/// @custom:repo github.com/ourzora/nouns-protocol
/// @notice The DAO deployer and upgrade manager
contract Manager is IManager, VersionedContract, UUPS, Ownable, ManagerStorageV1 {
/// ///
/// IMMUTABLES ///
/// ///
/// @notice The token implementation address
address public immutable tokenImpl;
/// @notice The metadata renderer implementation address
address public immutable metadataImpl;
/// @notice The auction house implementation address
address public immutable auctionImpl;
/// @notice The treasury implementation address
address public immutable treasuryImpl;
/// @notice The governor implementation address
address public immutable governorImpl;
/// ///
/// CONSTRUCTOR ///
/// ///
constructor(
address _tokenImpl,
address _metadataImpl,
address _auctionImpl,
address _treasuryImpl,
address _governorImpl
) payable initializer {
tokenImpl = _tokenImpl;
metadataImpl = _metadataImpl;
auctionImpl = _auctionImpl;
treasuryImpl = _treasuryImpl;
governorImpl = _governorImpl;
}
/// ///
/// INITIALIZER ///
/// ///
/// @notice Initializes ownership of the manager contract
/// @param _newOwner The owner address to set (will be transferred to the Builder DAO once its deployed)
function initialize(address _newOwner) external initializer {
// Ensure an owner is specified
if (_newOwner == address(0)) revert ADDRESS_ZERO();
// Set the contract owner
__Ownable_init(_newOwner);
}
/// ///
/// DAO DEPLOY ///
/// ///
/// @notice Deploys a DAO with custom token, auction, and governance settings
/// @param _founderParams The DAO founders
/// @param _tokenParams The ERC-721 token settings
/// @param _auctionParams The auction settings
/// @param _govParams The governance settings
function deploy(
FounderParams[] calldata _founderParams,
TokenParams calldata _tokenParams,
AuctionParams calldata _auctionParams,
GovParams calldata _govParams
)
external
returns (
address token,
address metadata,
address auction,
address treasury,
address governor
)
{
// Used to store the address of the first (or only) founder
// This founder is responsible for adding token artwork and launching the first auction -- they're also free to transfer this responsiblity
address founder;
// Ensure at least one founder is provided
if ((founder = _founderParams[0].wallet) == address(0)) revert FOUNDER_REQUIRED();
// Deploy the DAO's ERC-721 governance token
token = address(new ERC1967Proxy(tokenImpl, ""));
// Use the token address to precompute the DAO's remaining addresses
bytes32 salt = bytes32(uint256(uint160(token)) << 96);
// Deploy the remaining DAO contracts
metadata = address(new ERC1967Proxy{ salt: salt }(metadataImpl, ""));
auction = address(new ERC1967Proxy{ salt: salt }(auctionImpl, ""));
treasury = address(new ERC1967Proxy{ salt: salt }(treasuryImpl, ""));
governor = address(new ERC1967Proxy{ salt: salt }(governorImpl, ""));
daoAddressesByToken[token] = DAOAddresses({ metadata: metadata, auction: auction, treasury: treasury, governor: governor });
// Initialize each instance with the provided settings
IToken(token).initialize({
founders: _founderParams,
initStrings: _tokenParams.initStrings,
metadataRenderer: metadata,
auction: auction,
initialOwner: founder
});
IBaseMetadata(metadata).initialize({ initStrings: _tokenParams.initStrings, token: token });
IAuction(auction).initialize({
token: token,
founder: founder,
treasury: treasury,
duration: _auctionParams.duration,
reservePrice: _auctionParams.reservePrice
});
ITreasury(treasury).initialize({ governor: governor, timelockDelay: _govParams.timelockDelay });
IGovernor(governor).initialize({
treasury: treasury,
token: token,
vetoer: _govParams.vetoer,
votingDelay: _govParams.votingDelay,
votingPeriod: _govParams.votingPeriod,
proposalThresholdBps: _govParams.proposalThresholdBps,
quorumThresholdBps: _govParams.quorumThresholdBps
});
emit DAODeployed({ token: token, metadata: metadata, auction: auction, treasury: treasury, governor: governor });
}
/// ///
/// DAO ADDRESSES ///
/// ///
/// @notice A DAO's contract addresses from its token
/// @param _token The ERC-721 token address
/// @return metadata Metadata deployed address
/// @return auction Auction deployed address
/// @return treasury Treasury deployed address
/// @return governor Governor deployed address
function getAddresses(address _token)
public
view
returns (
address metadata,
address auction,
address treasury,
address governor
)
{
DAOAddresses storage addresses = daoAddressesByToken[_token];
metadata = addresses.metadata;
auction = addresses.auction;
treasury = addresses.treasury;
governor = addresses.governor;
}
/// ///
/// DAO UPGRADES ///
/// ///
/// @notice If an implementation is registered by the Builder DAO as an optional upgrade
/// @param _baseImpl The base implementation address
/// @param _upgradeImpl The upgrade implementation address
function isRegisteredUpgrade(address _baseImpl, address _upgradeImpl) external view returns (bool) {
return isUpgrade[_baseImpl][_upgradeImpl];
}
/// @notice Called by the Builder DAO to offer implementation upgrades for created DAOs
/// @param _baseImpl The base implementation address
/// @param _upgradeImpl The upgrade implementation address
function registerUpgrade(address _baseImpl, address _upgradeImpl) external onlyOwner {
isUpgrade[_baseImpl][_upgradeImpl] = true;
emit UpgradeRegistered(_baseImpl, _upgradeImpl);
}
/// @notice Called by the Builder DAO to remove an upgrade
/// @param _baseImpl The base implementation address
/// @param _upgradeImpl The upgrade implementation address
function removeUpgrade(address _baseImpl, address _upgradeImpl) external onlyOwner {
delete isUpgrade[_baseImpl][_upgradeImpl];
emit UpgradeRemoved(_baseImpl, _upgradeImpl);
}
/// @notice Safely get the contract version of a target contract.
/// @dev Assume `target` is a contract
/// @return Contract version if found, empty string if not.
function _safeGetVersion(address target) internal view returns (string memory) {
try IVersionedContract(target).contractVersion() returns (string memory version) {
return version;
} catch {
return '';
}
}
function getDAOVersions(address token) external view returns (DAOVersionInfo memory) {
(address metadata, address auction, address treasury, address governor) = getAddresses(token);
return DAOVersionInfo({
token: _safeGetVersion(token),
metadata: _safeGetVersion(metadata),
auction: _safeGetVersion(auction),
treasury: _safeGetVersion(treasury),
governor: _safeGetVersion(governor)
});
}
function getLatestVersions() external view returns (DAOVersionInfo memory) {
return DAOVersionInfo({
token: _safeGetVersion(tokenImpl),
metadata: _safeGetVersion(metadataImpl),
auction: _safeGetVersion(auctionImpl),
treasury: _safeGetVersion(treasuryImpl),
governor: _safeGetVersion(governorImpl)
});
}
/// ///
/// MANAGER UPGRADE ///
/// ///
/// @notice Ensures the caller is authorized to upgrade the contract
/// @dev This function is called in `upgradeTo` & `upgradeToAndCall`
/// @param _newImpl The new implementation address
function _authorizeUpgrade(address _newImpl) internal override onlyOwner {}
}
IAuction.sol 156 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../lib/interfaces/IUUPS.sol";
import { IOwnable } from "../lib/interfaces/IOwnable.sol";
import { IPausable } from "../lib/interfaces/IPausable.sol";
/// @title IAuction
/// @author Rohan Kulkarni
/// @notice The external Auction events, errors, and functions
interface IAuction is IUUPS, IOwnable, IPausable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a bid is placed
/// @param tokenId The ERC-721 token id
/// @param bidder The address of the bidder
/// @param amount The amount of ETH
/// @param extended If the bid extended the auction
/// @param endTime The end time of the auction
event AuctionBid(uint256 tokenId, address bidder, uint256 amount, bool extended, uint256 endTime);
/// @notice Emitted when an auction is settled
/// @param tokenId The ERC-721 token id of the settled auction
/// @param winner The address of the winning bidder
/// @param amount The amount of ETH raised from the winning bid
event AuctionSettled(uint256 tokenId, address winner, uint256 amount);
/// @notice Emitted when an auction is created
/// @param tokenId The ERC-721 token id of the created auction
/// @param startTime The start time of the created auction
/// @param endTime The end time of the created auction
event AuctionCreated(uint256 tokenId, uint256 startTime, uint256 endTime);
/// @notice Emitted when the auction duration is updated
/// @param duration The new auction duration
event DurationUpdated(uint256 duration);
/// @notice Emitted when the reserve price is updated
/// @param reservePrice The new reserve price
event ReservePriceUpdated(uint256 reservePrice);
/// @notice Emitted when the min bid increment percentage is updated
/// @param minBidIncrementPercentage The new min bid increment percentage
event MinBidIncrementPercentageUpdated(uint256 minBidIncrementPercentage);
/// @notice Emitted when the time buffer is updated
/// @param timeBuffer The new time buffer
event TimeBufferUpdated(uint256 timeBuffer);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if a bid is placed for the wrong token
error INVALID_TOKEN_ID();
/// @dev Reverts if a bid is placed for an auction thats over
error AUCTION_OVER();
/// @dev Reverts if a bid is placed for an auction that hasn't started
error AUCTION_NOT_STARTED();
/// @dev Reverts if attempting to settle an active auction
error AUCTION_ACTIVE();
/// @dev Reverts if attempting to settle an auction that was already settled
error AUCTION_SETTLED();
/// @dev Reverts if a bid does not meet the reserve price
error RESERVE_PRICE_NOT_MET();
/// @dev Reverts if a bid does not meet the minimum bid
error MINIMUM_BID_NOT_MET();
/// @dev Error for when the bid increment is set to 0.
error MIN_BID_INCREMENT_1_PERCENT();
/// @dev Reverts if the contract does not have enough ETH
error INSOLVENT();
/// @dev Reverts if the caller was not the contract manager
error ONLY_MANAGER();
/// @dev Thrown if the WETH contract throws a failure on transfer
error FAILING_WETH_TRANSFER();
/// @dev Thrown if the auction creation failed
error AUCTION_CREATE_FAILED_TO_LAUNCH();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Initializes a DAO's auction house
/// @param token The ERC-721 token address
/// @param founder The founder responsible for starting the first auction
/// @param treasury The treasury address where ETH will be sent
/// @param duration The duration of each auction
/// @param reservePrice The reserve price of each auction
function initialize(
address token,
address founder,
address treasury,
uint256 duration,
uint256 reservePrice
) external;
/// @notice Creates a bid for the current token
/// @param tokenId The ERC-721 token id
function createBid(uint256 tokenId) external payable;
/// @notice Settles the current auction and creates the next one
function settleCurrentAndCreateNewAuction() external;
/// @notice Settles the latest auction when the contract is paused
function settleAuction() external;
/// @notice Pauses the auction house
function pause() external;
/// @notice Unpauses the auction house
function unpause() external;
/// @notice The time duration of each auction
function duration() external view returns (uint256);
/// @notice The reserve price of each auction
function reservePrice() external view returns (uint256);
/// @notice The minimum amount of time to place a bid during an active auction
function timeBuffer() external view returns (uint256);
/// @notice The minimum percentage an incoming bid must raise the highest bid
function minBidIncrement() external view returns (uint256);
/// @notice Updates the time duration of each auction
/// @param duration The new time duration
function setDuration(uint256 duration) external;
/// @notice Updates the reserve price of each auction
/// @param reservePrice The new reserve price
function setReservePrice(uint256 reservePrice) external;
/// @notice Updates the time buffer of each auction
/// @param timeBuffer The new time buffer
function setTimeBuffer(uint256 timeBuffer) external;
/// @notice Updates the minimum bid increment of each subsequent bid
/// @param percentage The new percentage
function setMinimumBidIncrement(uint256 percentage) external;
/// @notice Get the address of the treasury
function treasury() external returns (address);
}
ERC721.sol 250 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IERC721 } from "../interfaces/IERC721.sol";
import { Initializable } from "../utils/Initializable.sol";
import { ERC721TokenReceiver } from "../utils/TokenReceiver.sol";
import { Address } from "../utils/Address.sol";
/// @title ERC721
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC721/ERC721Upgradeable.sol)
/// - Uses custom errors declared in IERC721
abstract contract ERC721 is IERC721, Initializable {
/// ///
/// STORAGE ///
/// ///
/// @notice The token name
string public name;
/// @notice The token symbol
string public symbol;
/// @notice The token owners
/// @dev ERC-721 token id => Owner
mapping(uint256 => address) internal owners;
/// @notice The owner balances
/// @dev Owner => Balance
mapping(address => uint256) internal balances;
/// @notice The token approvals
/// @dev ERC-721 token id => Manager
mapping(uint256 => address) internal tokenApprovals;
/// @notice The balance approvals
/// @dev Owner => Operator => Approved
mapping(address => mapping(address => bool)) internal operatorApprovals;
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Initializes an ERC-721 token
/// @param _name The ERC-721 token name
/// @param _symbol The ERC-721 token symbol
function __ERC721_init(string memory _name, string memory _symbol) internal onlyInitializing {
name = _name;
symbol = _symbol;
}
/// @notice The token URI
/// @param _tokenId The ERC-721 token id
function tokenURI(uint256 _tokenId) public view virtual returns (string memory) {}
/// @notice The contract URI
function contractURI() public view virtual returns (string memory) {}
/// @notice If the contract implements an interface
/// @param _interfaceId The interface id
function supportsInterface(bytes4 _interfaceId) external pure returns (bool) {
return
_interfaceId == 0x01ffc9a7 || // ERC165 Interface ID
_interfaceId == 0x80ac58cd || // ERC721 Interface ID
_interfaceId == 0x5b5e139f; // ERC721Metadata Interface ID
}
/// @notice The account approved to manage a token
/// @param _tokenId The ERC-721 token id
function getApproved(uint256 _tokenId) external view returns (address) {
return tokenApprovals[_tokenId];
}
/// @notice If an operator is authorized to manage all of an owner's tokens
/// @param _owner The owner address
/// @param _operator The operator address
function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
return operatorApprovals[_owner][_operator];
}
/// @notice The number of tokens owned
/// @param _owner The owner address
function balanceOf(address _owner) public view returns (uint256) {
if (_owner == address(0)) revert ADDRESS_ZERO();
return balances[_owner];
}
/// @notice The owner of a token
/// @param _tokenId The ERC-721 token id
function ownerOf(uint256 _tokenId) public view returns (address) {
address owner = owners[_tokenId];
if (owner == address(0)) revert INVALID_OWNER();
return owner;
}
/// @notice Authorizes an account to manage a token
/// @param _to The account address
/// @param _tokenId The ERC-721 token id
function approve(address _to, uint256 _tokenId) external {
address owner = owners[_tokenId];
if (msg.sender != owner && !operatorApprovals[owner][msg.sender]) revert INVALID_APPROVAL();
tokenApprovals[_tokenId] = _to;
emit Approval(owner, _to, _tokenId);
}
/// @notice Authorizes an account to manage all tokens
/// @param _operator The account address
/// @param _approved If permission is being given or removed
function setApprovalForAll(address _operator, bool _approved) external {
operatorApprovals[msg.sender][_operator] = _approved;
emit ApprovalForAll(msg.sender, _operator, _approved);
}
/// @notice Transfers a token from sender to recipient
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function transferFrom(
address _from,
address _to,
uint256 _tokenId
) public {
if (_from != owners[_tokenId]) revert INVALID_OWNER();
if (_to == address(0)) revert ADDRESS_ZERO();
if (msg.sender != _from && !operatorApprovals[_from][msg.sender] && msg.sender != tokenApprovals[_tokenId]) revert INVALID_APPROVAL();
_beforeTokenTransfer(_from, _to, _tokenId);
unchecked {
--balances[_from];
++balances[_to];
}
owners[_tokenId] = _to;
delete tokenApprovals[_tokenId];
emit Transfer(_from, _to, _tokenId);
_afterTokenTransfer(_from, _to, _tokenId);
}
/// @notice Safe transfers a token from sender to recipient
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
) external {
transferFrom(_from, _to, _tokenId);
if (
Address.isContract(_to) &&
ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, "") != ERC721TokenReceiver.onERC721Received.selector
) revert INVALID_RECIPIENT();
}
/// @notice Safe transfers a token from sender to recipient with additional data
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes calldata _data
) external {
transferFrom(_from, _to, _tokenId);
if (
Address.isContract(_to) &&
ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) != ERC721TokenReceiver.onERC721Received.selector
) revert INVALID_RECIPIENT();
}
/// @dev Mints a token to a recipient
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function _mint(address _to, uint256 _tokenId) internal virtual {
if (_to == address(0)) revert ADDRESS_ZERO();
if (owners[_tokenId] != address(0)) revert ALREADY_MINTED();
_beforeTokenTransfer(address(0), _to, _tokenId);
unchecked {
++balances[_to];
}
owners[_tokenId] = _to;
emit Transfer(address(0), _to, _tokenId);
_afterTokenTransfer(address(0), _to, _tokenId);
}
/// @dev Burns a token to a recipient
/// @param _tokenId The ERC-721 token id
function _burn(uint256 _tokenId) internal virtual {
address owner = owners[_tokenId];
if (owner == address(0)) revert NOT_MINTED();
_beforeTokenTransfer(owner, address(0), _tokenId);
unchecked {
--balances[owner];
}
delete owners[_tokenId];
delete tokenApprovals[_tokenId];
emit Transfer(owner, address(0), _tokenId);
_afterTokenTransfer(owner, address(0), _tokenId);
}
/// @dev Hook called before a token transfer
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function _beforeTokenTransfer(
address _from,
address _to,
uint256 _tokenId
) internal virtual {}
/// @dev Hook called after a token transfer
/// @param _from The sender address
/// @param _to The recipient address
/// @param _tokenId The ERC-721 token id
function _afterTokenTransfer(
address _from,
address _to,
uint256 _tokenId
) internal virtual {}
}
EIP712.sol 71 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IEIP712 } from "../interfaces/IEIP712.sol";
import { Initializable } from "../utils/Initializable.sol";
/// @title EIP712
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (utils/cryptography/draft-EIP712Upgradeable.sol)
/// - Uses custom errors declared in IEIP712
/// - Caches `INITIAL_CHAIN_ID` and `INITIAL_DOMAIN_SEPARATOR` upon initialization
/// - Adds mapping for account nonces
abstract contract EIP712 is IEIP712, Initializable {
/// ///
/// CONSTANTS ///
/// ///
/// @dev The EIP-712 domain typehash
bytes32 internal constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// ///
/// STORAGE ///
/// ///
/// @notice The hash of the EIP-712 domain name
bytes32 internal HASHED_NAME;
/// @notice The hash of the EIP-712 domain version
bytes32 internal HASHED_VERSION;
/// @notice The domain separator computed upon initialization
bytes32 internal INITIAL_DOMAIN_SEPARATOR;
/// @notice The chain id upon initialization
uint256 internal INITIAL_CHAIN_ID;
/// @notice The account nonces
/// @dev Account => Nonce
mapping(address => uint256) internal nonces;
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Initializes EIP-712 support
/// @param _name The EIP-712 domain name
/// @param _version The EIP-712 domain version
function __EIP712_init(string memory _name, string memory _version) internal onlyInitializing {
HASHED_NAME = keccak256(bytes(_name));
HASHED_VERSION = keccak256(bytes(_version));
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
}
/// @notice The current nonce for an account
/// @param _account The account address
function nonce(address _account) external view returns (uint256) {
return nonces[_account];
}
/// @notice The EIP-712 domain separator
function DOMAIN_SEPARATOR() public view returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : _computeDomainSeparator();
}
/// @dev Computes the EIP-712 domain separator
function _computeDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(DOMAIN_TYPEHASH, HASHED_NAME, HASHED_VERSION, block.chainid, address(this)));
}
}
IManager.sol 157 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../lib/interfaces/IUUPS.sol";
import { IOwnable } from "../lib/interfaces/IOwnable.sol";
/// @title IManager
/// @author Rohan Kulkarni
/// @notice The external Manager events, errors, structs and functions
interface IManager is IUUPS, IOwnable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a DAO is deployed
/// @param token The ERC-721 token address
/// @param metadata The metadata renderer address
/// @param auction The auction address
/// @param treasury The treasury address
/// @param governor The governor address
event DAODeployed(address token, address metadata, address auction, address treasury, address governor);
/// @notice Emitted when an upgrade is registered by the Builder DAO
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
event UpgradeRegistered(address baseImpl, address upgradeImpl);
/// @notice Emitted when an upgrade is unregistered by the Builder DAO
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
event UpgradeRemoved(address baseImpl, address upgradeImpl);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if at least one founder is not provided upon deploy
error FOUNDER_REQUIRED();
/// ///
/// STRUCTS ///
/// ///
/// @notice The founder parameters
/// @param wallet The wallet address
/// @param ownershipPct The percent ownership of the token
/// @param vestExpiry The timestamp that vesting expires
struct FounderParams {
address wallet;
uint256 ownershipPct;
uint256 vestExpiry;
}
/// @notice DAO Version Information information struct
struct DAOVersionInfo {
string token;
string metadata;
string auction;
string treasury;
string governor;
}
/// @notice The ERC-721 token parameters
/// @param initStrings The encoded token name, symbol, collection description, collection image uri, renderer base uri
struct TokenParams {
bytes initStrings;
}
/// @notice The auction parameters
/// @param reservePrice The reserve price of each auction
/// @param duration The duration of each auction
struct AuctionParams {
uint256 reservePrice;
uint256 duration;
}
/// @notice The governance parameters
/// @param timelockDelay The time delay to execute a queued transaction
/// @param votingDelay The time delay to vote on a created proposal
/// @param votingPeriod The time period to vote on a proposal
/// @param proposalThresholdBps The basis points of the token supply required to create a proposal
/// @param quorumThresholdBps The basis points of the token supply required to reach quorum
/// @param vetoer The address authorized to veto proposals (address(0) if none desired)
struct GovParams {
uint256 timelockDelay;
uint256 votingDelay;
uint256 votingPeriod;
uint256 proposalThresholdBps;
uint256 quorumThresholdBps;
address vetoer;
}
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The token implementation address
function tokenImpl() external view returns (address);
/// @notice The metadata renderer implementation address
function metadataImpl() external view returns (address);
/// @notice The auction house implementation address
function auctionImpl() external view returns (address);
/// @notice The treasury implementation address
function treasuryImpl() external view returns (address);
/// @notice The governor implementation address
function governorImpl() external view returns (address);
/// @notice Deploys a DAO with custom token, auction, and governance settings
/// @param founderParams The DAO founder(s)
/// @param tokenParams The ERC-721 token settings
/// @param auctionParams The auction settings
/// @param govParams The governance settings
function deploy(
FounderParams[] calldata founderParams,
TokenParams calldata tokenParams,
AuctionParams calldata auctionParams,
GovParams calldata govParams
)
external
returns (
address token,
address metadataRenderer,
address auction,
address treasury,
address governor
);
/// @notice A DAO's remaining contract addresses from its token address
/// @param token The ERC-721 token address
function getAddresses(address token)
external
returns (
address metadataRenderer,
address auction,
address treasury,
address governor
);
/// @notice If an implementation is registered by the Builder DAO as an optional upgrade
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
function isRegisteredUpgrade(address baseImpl, address upgradeImpl) external view returns (bool);
/// @notice Called by the Builder DAO to offer opt-in implementation upgrades for all other DAOs
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
function registerUpgrade(address baseImpl, address upgradeImpl) external;
/// @notice Called by the Builder DAO to remove an upgrade
/// @param baseImpl The base implementation address
/// @param upgradeImpl The upgrade implementation address
function removeUpgrade(address baseImpl, address upgradeImpl) external;
}
VersionedContract.sol 8 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
abstract contract VersionedContract {
function contractVersion() external pure returns (string memory) {
return "1.2.0";
}
}
Address.sol 61 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title EIP712
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (utils/Address.sol)
/// - Uses custom errors `INVALID_TARGET()` & `DELEGATE_CALL_FAILED()`
/// - Adds util converting address to bytes32
library Address {
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the target of a delegatecall is not a contract
error INVALID_TARGET();
/// @dev Reverts if a delegatecall has failed
error DELEGATE_CALL_FAILED();
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Utility to convert an address to bytes32
function toBytes32(address _account) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_account)) << 96);
}
/// @dev If an address is a contract
function isContract(address _account) internal view returns (bool rv) {
assembly {
rv := gt(extcodesize(_account), 0)
}
}
/// @dev Performs a delegatecall on an address
function functionDelegateCall(address _target, bytes memory _data) internal returns (bytes memory) {
if (!isContract(_target)) revert INVALID_TARGET();
(bool success, bytes memory returndata) = _target.delegatecall(_data);
return verifyCallResult(success, returndata);
}
/// @dev Verifies a delegatecall was successful
function verifyCallResult(bool _success, bytes memory _returndata) internal pure returns (bytes memory) {
if (_success) {
return _returndata;
} else {
if (_returndata.length > 0) {
assembly {
let returndata_size := mload(_returndata)
revert(add(32, _returndata), returndata_size)
}
} else {
revert DELEGATE_CALL_FAILED();
}
}
}
}
Ownable.sol 101 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IOwnable } from "../interfaces/IOwnable.sol";
import { Initializable } from "../utils/Initializable.sol";
/// @title Ownable
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (access/OwnableUpgradeable.sol)
/// - Uses custom errors declared in IOwnable
/// - Adds optional two-step ownership transfer (`safeTransferOwnership` + `acceptOwnership`)
abstract contract Ownable is IOwnable, Initializable {
/// ///
/// STORAGE ///
/// ///
/// @dev The address of the owner
address internal _owner;
/// @dev The address of the pending owner
address internal _pendingOwner;
/// ///
/// MODIFIERS ///
/// ///
/// @dev Ensures the caller is the owner
modifier onlyOwner() {
if (msg.sender != _owner) revert ONLY_OWNER();
_;
}
/// @dev Ensures the caller is the pending owner
modifier onlyPendingOwner() {
if (msg.sender != _pendingOwner) revert ONLY_PENDING_OWNER();
_;
}
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Initializes contract ownership
/// @param _initialOwner The initial owner address
function __Ownable_init(address _initialOwner) internal onlyInitializing {
_owner = _initialOwner;
emit OwnerUpdated(address(0), _initialOwner);
}
/// @notice The address of the owner
function owner() public virtual view returns (address) {
return _owner;
}
/// @notice The address of the pending owner
function pendingOwner() public view returns (address) {
return _pendingOwner;
}
/// @notice Forces an ownership transfer from the last owner
/// @param _newOwner The new owner address
function transferOwnership(address _newOwner) public onlyOwner {
_transferOwnership(_newOwner);
}
/// @notice Forces an ownership transfer from any sender
/// @param _newOwner New owner to transfer contract to
/// @dev Ensure is called only from trusted internal code, no access control checks.
function _transferOwnership(address _newOwner) internal {
emit OwnerUpdated(_owner, _newOwner);
_owner = _newOwner;
if (_pendingOwner != address(0)) delete _pendingOwner;
}
/// @notice Initiates a two-step ownership transfer
/// @param _newOwner The new owner address
function safeTransferOwnership(address _newOwner) public onlyOwner {
_pendingOwner = _newOwner;
emit OwnerPending(_owner, _newOwner);
}
/// @notice Accepts an ownership transfer
function acceptOwnership() public onlyPendingOwner {
emit OwnerUpdated(_owner, msg.sender);
_owner = _pendingOwner;
delete _pendingOwner;
}
/// @notice Cancels a pending ownership transfer
function cancelOwnershipTransfer() public onlyOwner {
emit OwnerCanceled(_owner, _pendingOwner);
delete _pendingOwner;
}
}
SafeCast.sol 50 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (utils/math/SafeCast.sol)
/// - Uses custom error `UNSAFE_CAST()`
library SafeCast {
error UNSAFE_CAST();
function toUint128(uint256 x) internal pure returns (uint128) {
if (x > type(uint128).max) revert UNSAFE_CAST();
return uint128(x);
}
function toUint64(uint256 x) internal pure returns (uint64) {
if (x > type(uint64).max) revert UNSAFE_CAST();
return uint64(x);
}
function toUint48(uint256 x) internal pure returns (uint48) {
if (x > type(uint48).max) revert UNSAFE_CAST();
return uint48(x);
}
function toUint40(uint256 x) internal pure returns (uint40) {
if (x > type(uint40).max) revert UNSAFE_CAST();
return uint40(x);
}
function toUint32(uint256 x) internal pure returns (uint32) {
if (x > type(uint32).max) revert UNSAFE_CAST();
return uint32(x);
}
function toUint16(uint256 x) internal pure returns (uint16) {
if (x > type(uint16).max) revert UNSAFE_CAST();
return uint16(x);
}
function toUint8(uint256 x) internal pure returns (uint8) {
if (x > type(uint8).max) revert UNSAFE_CAST();
return uint8(x);
}
}
IUUPS.sol 36 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import { IERC1822Proxiable } from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import { IERC1967Upgrade } from "./IERC1967Upgrade.sol";
/// @title IUUPS
/// @author Rohan Kulkarni
/// @notice The external UUPS errors and functions
interface IUUPS is IERC1967Upgrade, IERC1822Proxiable {
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if not called directly
error ONLY_CALL();
/// @dev Reverts if not called via delegatecall
error ONLY_DELEGATECALL();
/// @dev Reverts if not called via proxy
error ONLY_PROXY();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Upgrades to an implementation
/// @param newImpl The new implementation address
function upgradeTo(address newImpl) external;
/// @notice Upgrades to an implementation with an additional function call
/// @param newImpl The new implementation address
/// @param data The encoded function call
function upgradeToAndCall(address newImpl, bytes memory data) external payable;
}
ERC721Votes.sol 322 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IERC721Votes } from "../interfaces/IERC721Votes.sol";
import { ERC721 } from "../token/ERC721.sol";
import { EIP712 } from "../utils/EIP712.sol";
/// @title ERC721Votes
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC721/extensions/draft-ERC721Votes.sol) & Nouns DAO ERC721Checkpointable.sol commit 2cbe6c7 - licensed under the BSD-3-Clause license.
/// - Uses custom errors defined in IERC721Votes
/// - Checkpoints are based on timestamps instead of block numbers
/// - Tokens are self-delegated by default
/// - The total number of votes is the token supply itself
abstract contract ERC721Votes is IERC721Votes, EIP712, ERC721 {
/// ///
/// CONSTANTS ///
/// ///
/// @dev The EIP-712 typehash to delegate with a signature
bytes32 internal constant DELEGATION_TYPEHASH = keccak256("Delegation(address from,address to,uint256 nonce,uint256 deadline)");
/// ///
/// STORAGE ///
/// ///
/// @notice The delegate for an account
/// @notice Account => Delegate
mapping(address => address) internal delegation;
/// @notice The number of checkpoints for an account
/// @dev Account => Num Checkpoints
mapping(address => uint256) internal numCheckpoints;
/// @notice The checkpoint for an account
/// @dev Account => Checkpoint Id => Checkpoint
mapping(address => mapping(uint256 => Checkpoint)) internal checkpoints;
/// ///
/// VOTING WEIGHT ///
/// ///
/// @notice The current number of votes for an account
/// @param _account The account address
function getVotes(address _account) public view returns (uint256) {
// Get the account's number of checkpoints
uint256 nCheckpoints = numCheckpoints[_account];
// Cannot underflow as `nCheckpoints` is ensured to be greater than 0 if reached
unchecked {
// Return the number of votes at the latest checkpoint if applicable
return nCheckpoints != 0 ? checkpoints[_account][nCheckpoints - 1].votes : 0;
}
}
/// @notice The number of votes for an account at a past timestamp
/// @param _account The account address
/// @param _timestamp The past timestamp
function getPastVotes(address _account, uint256 _timestamp) public view returns (uint256) {
// Ensure the given timestamp is in the past
if (_timestamp >= block.timestamp) revert INVALID_TIMESTAMP();
// Get the account's number of checkpoints
uint256 nCheckpoints = numCheckpoints[_account];
// If there are none return 0
if (nCheckpoints == 0) return 0;
// Get the account's checkpoints
mapping(uint256 => Checkpoint) storage accountCheckpoints = checkpoints[_account];
unchecked {
// Get the latest checkpoint id
// Cannot underflow as `nCheckpoints` is ensured to be greater than 0
uint256 lastCheckpoint = nCheckpoints - 1;
// If the latest checkpoint has a valid timestamp, return its number of votes
if (accountCheckpoints[lastCheckpoint].timestamp <= _timestamp) return accountCheckpoints[lastCheckpoint].votes;
// If the first checkpoint doesn't have a valid timestamp, return 0
if (accountCheckpoints[0].timestamp > _timestamp) return 0;
// Otherwise, find a checkpoint with a valid timestamp
// Use the latest id as the initial upper bound
uint256 high = lastCheckpoint;
uint256 low;
uint256 middle;
// Used to temporarily hold a checkpoint
Checkpoint memory cp;
// While a valid checkpoint is to be found:
while (high > low) {
// Find the id of the middle checkpoint
middle = high - (high - low) / 2;
// Get the middle checkpoint
cp = accountCheckpoints[middle];
// If the timestamp is a match:
if (cp.timestamp == _timestamp) {
// Return the voting weight
return cp.votes;
// Else if the timestamp is before the one looking for:
} else if (cp.timestamp < _timestamp) {
// Update the lower bound
low = middle;
// Else update the upper bound
} else {
high = middle - 1;
}
}
return accountCheckpoints[low].votes;
}
}
/// ///
/// DELEGATION ///
/// ///
/// @notice The delegate for an account
/// @param _account The account address
function delegates(address _account) public view returns (address) {
address current = delegation[_account];
return current == address(0) ? _account : current;
}
/// @notice Delegates votes to an account
/// @param _to The address delegating votes to
function delegate(address _to) external {
_delegate(msg.sender, _to);
}
/// @notice Delegates votes from a signer to an account
/// @param _from The address delegating votes from
/// @param _to The address delegating votes to
/// @param _deadline The signature deadline
/// @param _v The 129th byte and chain id of the signature
/// @param _r The first 64 bytes of the signature
/// @param _s Bytes 64-128 of the signature
function delegateBySig(
address _from,
address _to,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) external {
// Ensure the signature has not expired
if (block.timestamp > _deadline) revert EXPIRED_SIGNATURE();
// Used to store the digest
bytes32 digest;
// Cannot realistically overflow
unchecked {
// Compute the hash of the domain seperator with the typed delegation data
digest = keccak256(
abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), keccak256(abi.encode(DELEGATION_TYPEHASH, _from, _to, nonces[_from]++, _deadline)))
);
}
// Recover the message signer
address recoveredAddress = ecrecover(digest, _v, _r, _s);
// Ensure the recovered signer is the voter
if (recoveredAddress == address(0) || recoveredAddress != _from) revert INVALID_SIGNATURE();
// Update the delegate
_delegate(_from, _to);
}
/// @dev Updates delegate addresses
/// @param _from The address delegating votes from
/// @param _to The address delegating votes to
function _delegate(address _from, address _to) internal {
// If address(0) is being delegated to, update the op as a self-delegate
if (_to == address(0)) _to = _from;
// Get the previous delegate
address prevDelegate = delegates(_from);
// Store the new delegate
delegation[_from] = _to;
emit DelegateChanged(_from, prevDelegate, _to);
// Transfer voting weight from the previous delegate to the new delegate
_moveDelegateVotes(prevDelegate, _to, balanceOf(_from));
}
/// @dev Transfers voting weight
/// @param _from The address delegating votes from
/// @param _to The address delegating votes to
/// @param _amount The number of votes delegating
function _moveDelegateVotes(
address _from,
address _to,
uint256 _amount
) internal {
unchecked {
// If voting weight is being transferred:
if (_from != _to && _amount > 0) {
// If this isn't a token mint:
if (_from != address(0)) {
// Get the sender's number of checkpoints
uint256 newCheckpointId = numCheckpoints[_from];
// Used to store their previous checkpoint id
uint256 prevCheckpointId;
// Used to store their previous checkpoint's voting weight
uint256 prevTotalVotes;
// Used to store their previous checkpoint's timestamp
uint256 prevTimestamp;
// If this isn't the sender's first checkpoint:
if (newCheckpointId != 0) {
// Get their previous checkpoint's id
prevCheckpointId = newCheckpointId - 1;
// Get their previous checkpoint's voting weight
prevTotalVotes = checkpoints[_from][prevCheckpointId].votes;
// Get their previous checkpoint's timestamp
prevTimestamp = checkpoints[_from][prevCheckpointId].timestamp;
}
// Update their voting weight
_writeCheckpoint(_from, newCheckpointId, prevCheckpointId, prevTimestamp, prevTotalVotes, prevTotalVotes - _amount);
}
// If this isn't a token burn:
if (_to != address(0)) {
// Get the recipients's number of checkpoints
uint256 nCheckpoints = numCheckpoints[_to];
// Used to store their previous checkpoint id
uint256 prevCheckpointId;
// Used to store their previous checkpoint's voting weight
uint256 prevTotalVotes;
// Used to store their previous checkpoint's timestamp
uint256 prevTimestamp;
// If this isn't the recipient's first checkpoint:
if (nCheckpoints != 0) {
// Get their previous checkpoint's id
prevCheckpointId = nCheckpoints - 1;
// Get their previous checkpoint's voting weight
prevTotalVotes = checkpoints[_to][prevCheckpointId].votes;
// Get their previous checkpoint's timestamp
prevTimestamp = checkpoints[_to][prevCheckpointId].timestamp;
}
// Update their voting weight
_writeCheckpoint(_to, nCheckpoints, prevCheckpointId, prevTimestamp, prevTotalVotes, prevTotalVotes + _amount);
}
}
}
}
/// @dev Records a checkpoint
/// @param _account The account address
/// @param _newId The new checkpoint id
/// @param _prevId The previous checkpoint id
/// @param _prevTimestamp The previous checkpoint timestamp
/// @param _prevTotalVotes The previous checkpoint voting weight
/// @param _newTotalVotes The new checkpoint voting weight
function _writeCheckpoint(
address _account,
uint256 _newId,
uint256 _prevId,
uint256 _prevTimestamp,
uint256 _prevTotalVotes,
uint256 _newTotalVotes
) private {
unchecked {
// If the new checkpoint is not the user's first AND has the timestamp of the previous checkpoint:
if (_newId > 0 && _prevTimestamp == block.timestamp) {
// Just update the previous checkpoint's votes
checkpoints[_account][_prevId].votes = uint192(_newTotalVotes);
// Else write a new checkpoint:
} else {
// Get the pointer to store the checkpoint
Checkpoint storage checkpoint = checkpoints[_account][_newId];
// Store the new voting weight and the current time
checkpoint.votes = uint192(_newTotalVotes);
checkpoint.timestamp = uint64(block.timestamp);
// Increment the account's number of checkpoints
++numCheckpoints[_account];
}
emit DelegateVotesChanged(_account, _prevTotalVotes, _newTotalVotes);
}
}
/// @dev Enables each NFT to equal 1 vote
/// @param _from The token sender
/// @param _to The token recipient
/// @param _tokenId The ERC-721 token id
function _afterTokenTransfer(
address _from,
address _to,
uint256 _tokenId
) internal override {
// Transfer 1 vote from the sender to the recipient
_moveDelegateVotes(delegates(_from), delegates(_to), 1);
super._afterTokenTransfer(_from, _to, _tokenId);
}
}
IEIP712.sol 28 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IEIP712
/// @author Rohan Kulkarni
/// @notice The external EIP712 errors and functions
interface IEIP712 {
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the deadline has passed to submit a signature
error EXPIRED_SIGNATURE();
/// @dev Reverts if the recovered signature is invalid
error INVALID_SIGNATURE();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The sig nonce for an account
/// @param account The account address
function nonce(address account) external view returns (uint256);
/// @notice The EIP-712 domain separator
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IERC721.sol 111 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IERC721
/// @author Rohan Kulkarni
/// @notice The external ERC721 events, errors, and functions
interface IERC721 {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a token is transferred from sender to recipient
/// @param from The sender address
/// @param to The recipient address
/// @param tokenId The ERC-721 token id
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/// @notice Emitted when an owner approves an account to manage a token
/// @param owner The owner address
/// @param approved The account address
/// @param tokenId The ERC-721 token id
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/// @notice Emitted when an owner sets an approval for a spender to manage all tokens
/// @param owner The owner address
/// @param operator The spender address
/// @param approved If the approval is being set or removed
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if a caller is not authorized to approve or transfer a token
error INVALID_APPROVAL();
/// @dev Reverts if a transfer is called with the incorrect token owner
error INVALID_OWNER();
/// @dev Reverts if a transfer is attempted to address(0)
error INVALID_RECIPIENT();
/// @dev Reverts if an existing token is called to be minted
error ALREADY_MINTED();
/// @dev Reverts if a non-existent token is called to be burned
error NOT_MINTED();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The number of tokens owned
/// @param owner The owner address
function balanceOf(address owner) external view returns (uint256);
/// @notice The owner of a token
/// @param tokenId The ERC-721 token id
function ownerOf(uint256 tokenId) external view returns (address);
/// @notice The account approved to manage a token
/// @param tokenId The ERC-721 token id
function getApproved(uint256 tokenId) external view returns (address);
/// @notice If an operator is authorized to manage all of an owner's tokens
/// @param owner The owner address
/// @param operator The operator address
function isApprovedForAll(address owner, address operator) external view returns (bool);
/// @notice Authorizes an account to manage a token
/// @param to The account address
/// @param tokenId The ERC-721 token id
function approve(address to, uint256 tokenId) external;
/// @notice Authorizes an account to manage all tokens
/// @param operator The account address
/// @param approved If permission is being given or removed
function setApprovalForAll(address operator, bool approved) external;
/// @notice Safe transfers a token from sender to recipient with additional data
/// @param from The sender address
/// @param to The recipient address
/// @param tokenId The ERC-721 token id
/// @param data The additional data sent in the call to the recipient
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
/// @notice Safe transfers a token from sender to recipient
/// @param from The sender address
/// @param to The recipient address
/// @param tokenId The ERC-721 token id
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
/// @notice Transfers a token from sender to recipient
/// @param from The sender address
/// @param to The recipient address
/// @param tokenId The ERC-721 token id
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
}
ERC1967Proxy.sol 33 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol";
import { IERC1967Upgrade } from "../interfaces/IERC1967Upgrade.sol";
import { ERC1967Upgrade } from "./ERC1967Upgrade.sol";
/// @title ERC1967Proxy
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/ERC1967/ERC1967Proxy.sol)
/// - Inherits a modern, minimal ERC1967Upgrade
contract ERC1967Proxy is IERC1967Upgrade, Proxy, ERC1967Upgrade {
/// ///
/// CONSTRUCTOR ///
/// ///
/// @dev Initializes the proxy with an implementation contract and encoded function call
/// @param _logic The implementation address
/// @param _data The encoded function call
constructor(address _logic, bytes memory _data) payable {
_upgradeToAndCall(_logic, _data, false);
}
/// ///
/// FUNCTIONS ///
/// ///
/// @dev The address of the current implementation
function _implementation() internal view virtual override returns (address) {
return ERC1967Upgrade._getImplementation();
}
}
IOwnable.sol 60 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IOwnable
/// @author Rohan Kulkarni
/// @notice The external Ownable events, errors, and functions
interface IOwnable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when ownership has been updated
/// @param prevOwner The previous owner address
/// @param newOwner The new owner address
event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
/// @notice Emitted when an ownership transfer is pending
/// @param owner The current owner address
/// @param pendingOwner The pending new owner address
event OwnerPending(address indexed owner, address indexed pendingOwner);
/// @notice Emitted when a pending ownership transfer has been canceled
/// @param owner The current owner address
/// @param canceledOwner The canceled owner address
event OwnerCanceled(address indexed owner, address indexed canceledOwner);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if an unauthorized user calls an owner function
error ONLY_OWNER();
/// @dev Reverts if an unauthorized user calls a pending owner function
error ONLY_PENDING_OWNER();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The address of the owner
function owner() external view returns (address);
/// @notice The address of the pending owner
function pendingOwner() external view returns (address);
/// @notice Forces an ownership transfer
/// @param newOwner The new owner address
function transferOwnership(address newOwner) external;
/// @notice Initiates a two-step ownership transfer
/// @param newOwner The new owner address
function safeTransferOwnership(address newOwner) external;
/// @notice Accepts an ownership transfer
function acceptOwnership() external;
/// @notice Cancels a pending ownership transfer
function cancelOwnershipTransfer() external;
}
Initializable.sol 83 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IInitializable } from "../interfaces/IInitializable.sol";
import { Address } from "../utils/Address.sol";
/// @title Initializable
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/utils/Initializable.sol)
/// - Uses custom errors declared in IInitializable
abstract contract Initializable is IInitializable {
/// ///
/// STORAGE ///
/// ///
/// @dev Indicates the contract has been initialized
uint8 internal _initialized;
/// @dev Indicates the contract is being initialized
bool internal _initializing;
/// ///
/// MODIFIERS ///
/// ///
/// @dev Ensures an initialization function is only called within an `initializer` or `reinitializer` function
modifier onlyInitializing() {
if (!_initializing) revert NOT_INITIALIZING();
_;
}
/// @dev Enables initializing upgradeable contracts
modifier initializer() {
bool isTopLevelCall = !_initializing;
if ((!isTopLevelCall || _initialized != 0) && (Address.isContract(address(this)) || _initialized != 1)) revert ALREADY_INITIALIZED();
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/// @dev Enables initializer versioning
/// @param _version The version to set
modifier reinitializer(uint8 _version) {
if (_initializing || _initialized >= _version) revert ALREADY_INITIALIZED();
_initialized = _version;
_initializing = true;
_;
_initializing = false;
emit Initialized(_version);
}
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Prevents future initialization
function _disableInitializers() internal virtual {
if (_initializing) revert INITIALIZING();
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
}
TokenReceiver.sol 37 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC721/utils/ERC721Holder.sol)
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return this.onERC721Received.selector;
}
}
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (token/ERC1155/utils/ERC1155Holder.sol)
abstract contract ERC1155TokenReceiver {
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] calldata,
uint256[] calldata,
bytes calldata
) external virtual returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}
IPausable.sol 36 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IPausable
/// @author Rohan Kulkarni
/// @notice The external Pausable events, errors, and functions
interface IPausable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when the contract is paused
/// @param user The address that paused the contract
event Paused(address user);
/// @notice Emitted when the contract is unpaused
/// @param user The address that unpaused the contract
event Unpaused(address user);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if called when the contract is paused
error PAUSED();
/// @dev Reverts if called when the contract is unpaused
error UNPAUSED();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice If the contract is paused
function paused() external view returns (bool);
}
ERC1967Upgrade.sol 86 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IERC1822Proxiable } from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import { StorageSlot } from "@openzeppelin/contracts/utils/StorageSlot.sol";
import { IERC1967Upgrade } from "../interfaces/IERC1967Upgrade.sol";
import { Address } from "../utils/Address.sol";
/// @title ERC1967Upgrade
/// @author Rohan Kulkarni
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (proxy/ERC1967/ERC1967Upgrade.sol)
/// - Uses custom errors declared in IERC1967Upgrade
/// - Removes ERC1967 admin and beacon support
abstract contract ERC1967Upgrade is IERC1967Upgrade {
/// ///
/// CONSTANTS ///
/// ///
/// @dev bytes32(uint256(keccak256('eip1967.proxy.rollback')) - 1)
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/// @dev bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Upgrades to an implementation with security checks for UUPS proxies and an additional function call
/// @param _newImpl The new implementation address
/// @param _data The encoded function call
function _upgradeToAndCallUUPS(
address _newImpl,
bytes memory _data,
bool _forceCall
) internal {
if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
_setImplementation(_newImpl);
} else {
try IERC1822Proxiable(_newImpl).proxiableUUID() returns (bytes32 slot) {
if (slot != _IMPLEMENTATION_SLOT) revert UNSUPPORTED_UUID();
} catch {
revert ONLY_UUPS();
}
_upgradeToAndCall(_newImpl, _data, _forceCall);
}
}
/// @dev Upgrades to an implementation with an additional function call
/// @param _newImpl The new implementation address
/// @param _data The encoded function call
function _upgradeToAndCall(
address _newImpl,
bytes memory _data,
bool _forceCall
) internal {
_upgradeTo(_newImpl);
if (_data.length > 0 || _forceCall) {
Address.functionDelegateCall(_newImpl, _data);
}
}
/// @dev Performs an implementation upgrade
/// @param _newImpl The new implementation address
function _upgradeTo(address _newImpl) internal {
_setImplementation(_newImpl);
emit Upgraded(_newImpl);
}
/// @dev Stores the address of an implementation
/// @param _impl The implementation address
function _setImplementation(address _impl) private {
if (!Address.isContract(_impl)) revert INVALID_UPGRADE(_impl);
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = _impl;
}
/// @dev The address of the current implementation
function _getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
}
TokenTypesV1.sol 35 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IBaseMetadata } from "../metadata/interfaces/IBaseMetadata.sol";
/// @title TokenTypesV1
/// @author Rohan Kulkarni
/// @notice The Token custom data types
interface TokenTypesV1 {
/// @notice The settings type
/// @param auction The DAO auction house
/// @param totalSupply The number of active tokens
/// @param numFounders The number of vesting recipients
/// @param metadatarenderer The token metadata renderer
/// @param mintCount The number of minted tokens
/// @param totalPercentage The total percentage owned by founders
struct Settings {
address auction;
uint88 totalSupply;
uint8 numFounders;
IBaseMetadata metadataRenderer;
uint88 mintCount;
uint8 totalOwnership;
}
/// @notice The founder type
/// @param wallet The address where tokens are sent
/// @param ownershipPct The percentage of token ownership
/// @param vestExpiry The timestamp when vesting ends
struct Founder {
address wallet;
uint8 ownershipPct;
uint32 vestExpiry;
}
}
TokenTypesV2.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title TokenTypesV2
/// @author James Geary
/// @notice The Token custom data types
interface TokenTypesV2 {
struct MinterParams {
address minter;
bool allowed;
}
}
ReentrancyGuard.sol 48 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { Initializable } from "../utils/Initializable.sol";
/// @notice Modified from OpenZeppelin Contracts v4.7.3 (security/ReentrancyGuardUpgradeable.sol)
/// - Uses custom error `REENTRANCY()`
abstract contract ReentrancyGuard is Initializable {
/// ///
/// STORAGE ///
/// ///
/// @dev Indicates a function has not been entered
uint256 internal constant _NOT_ENTERED = 1;
/// @dev Indicates a function has been entered
uint256 internal constant _ENTERED = 2;
/// @notice The reentrancy status of a function
uint256 internal _status;
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if attempted reentrancy
error REENTRANCY();
/// ///
/// FUNCTIONS ///
/// ///
/// @dev Initializes the reentrancy guard
function __ReentrancyGuard_init() internal onlyInitializing {
_status = _NOT_ENTERED;
}
/// @dev Ensures a function cannot be reentered
modifier nonReentrant() {
if (_status == _ENTERED) revert REENTRANCY();
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
IERC721Votes.sol 76 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IERC721 } from "./IERC721.sol";
import { IEIP712 } from "./IEIP712.sol";
/// @title IERC721Votes
/// @author Rohan Kulkarni
/// @notice The external ERC721Votes events, errors, and functions
interface IERC721Votes is IERC721, IEIP712 {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when an account changes their delegate
event DelegateChanged(address indexed delegator, address indexed from, address indexed to);
/// @notice Emitted when a delegate's number of votes is updated
event DelegateVotesChanged(address indexed delegate, uint256 prevTotalVotes, uint256 newTotalVotes);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the timestamp provided isn't in the past
error INVALID_TIMESTAMP();
/// ///
/// STRUCTS ///
/// ///
/// @notice The checkpoint data type
/// @param timestamp The recorded timestamp
/// @param votes The voting weight
struct Checkpoint {
uint64 timestamp;
uint192 votes;
}
/// ///
/// FUNCTIONS ///
/// ///
/// @notice The current number of votes for an account
/// @param account The account address
function getVotes(address account) external view returns (uint256);
/// @notice The number of votes for an account at a past timestamp
/// @param account The account address
/// @param timestamp The past timestamp
function getPastVotes(address account, uint256 timestamp) external view returns (uint256);
/// @notice The delegate for an account
/// @param account The account address
function delegates(address account) external view returns (address);
/// @notice Delegates votes to an account
/// @param to The address delegating votes to
function delegate(address to) external;
/// @notice Delegates votes from a signer to an account
/// @param from The address delegating votes from
/// @param to The address delegating votes to
/// @param deadline The signature deadline
/// @param v The 129th byte and chain id of the signature
/// @param r The first 64 bytes of the signature
/// @param s Bytes 64-128 of the signature
function delegateBySig(
address from,
address to,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
Treasury.sol 279 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { UUPS } from "../../lib/proxy/UUPS.sol";
import { Ownable } from "../../lib/utils/Ownable.sol";
import { ERC721TokenReceiver, ERC1155TokenReceiver } from "../../lib/utils/TokenReceiver.sol";
import { SafeCast } from "../../lib/utils/SafeCast.sol";
import { TreasuryStorageV1 } from "./storage/TreasuryStorageV1.sol";
import { ITreasury } from "./ITreasury.sol";
import { ProposalHasher } from "../governor/ProposalHasher.sol";
import { IManager } from "../../manager/IManager.sol";
import { VersionedContract } from "../../VersionedContract.sol";
/// @title Treasury
/// @author Rohan Kulkarni
/// @notice A DAO's treasury and transaction executor
/// @custom:repo github.com/ourzora/nouns-protocol
/// Modified from:
/// - OpenZeppelin Contracts v4.7.3 (governance/TimelockController.sol)
/// - NounsDAOExecutor.sol commit 2cbe6c7 - licensed under the BSD-3-Clause license.
contract Treasury is ITreasury, VersionedContract, UUPS, Ownable, ProposalHasher, TreasuryStorageV1 {
/// ///
/// CONSTANTS ///
/// ///
/// @notice The default grace period setting
uint128 private constant INITIAL_GRACE_PERIOD = 2 weeks;
/// ///
/// IMMUTABLES ///
/// ///
/// @notice The contract upgrade manager
IManager private immutable manager;
/// ///
/// CONSTRUCTOR ///
/// ///
/// @param _manager The contract upgrade manager address
constructor(address _manager) payable initializer {
manager = IManager(_manager);
}
/// ///
/// INITIALIZER ///
/// ///
/// @notice Initializes an instance of a DAO's treasury
/// @param _governor The DAO's governor address
/// @param _delay The time delay to execute a queued transaction
function initialize(address _governor, uint256 _delay) external initializer {
// Ensure the caller is the contract manager
if (msg.sender != address(manager)) revert ONLY_MANAGER();
// Ensure a governor address was provided
if (_governor == address(0)) revert ADDRESS_ZERO();
// Grant ownership to the governor
__Ownable_init(_governor);
// Store the time delay
settings.delay = SafeCast.toUint128(_delay);
// Set the default grace period
settings.gracePeriod = INITIAL_GRACE_PERIOD;
emit DelayUpdated(0, _delay);
}
/// ///
/// TRANSACTION STATE ///
/// ///
/// @notice The timestamp that a proposal is valid to execute
/// @param _proposalId The proposal id
function timestamp(bytes32 _proposalId) external view returns (uint256) {
return timestamps[_proposalId];
}
/// @notice If a queued proposal can no longer be executed
/// @param _proposalId The proposal id
function isExpired(bytes32 _proposalId) external view returns (bool) {
unchecked {
return block.timestamp > (timestamps[_proposalId] + settings.gracePeriod);
}
}
/// @notice If a proposal is queued
/// @param _proposalId The proposal id
function isQueued(bytes32 _proposalId) public view returns (bool) {
return timestamps[_proposalId] != 0;
}
/// @notice If a proposal is ready to execute (does not consider expiration)
/// @param _proposalId The proposal id
function isReady(bytes32 _proposalId) public view returns (bool) {
return timestamps[_proposalId] != 0 && block.timestamp >= timestamps[_proposalId];
}
/// ///
/// QUEUE PROPOSAL ///
/// ///
/// @notice Schedules a proposal for execution
/// @param _proposalId The proposal id
function queue(bytes32 _proposalId) external onlyOwner returns (uint256 eta) {
// Ensure the proposal was not already queued
if (isQueued(_proposalId)) revert PROPOSAL_ALREADY_QUEUED();
// Cannot realistically overflow
unchecked {
// Compute the timestamp that the proposal will be valid to execute
eta = block.timestamp + settings.delay;
}
// Store the timestamp
timestamps[_proposalId] = eta;
emit TransactionScheduled(_proposalId, eta);
}
/// ///
/// EXECUTE PROPOSAL ///
/// ///
/// @notice Executes a queued proposal
/// @param _targets The target addresses to call
/// @param _values The ETH values of each call
/// @param _calldatas The calldata of each call
/// @param _descriptionHash The hash of the description
/// @param _proposer The proposal creator
function execute(
address[] calldata _targets,
uint256[] calldata _values,
bytes[] calldata _calldatas,
bytes32 _descriptionHash,
address _proposer
) external payable onlyOwner {
// Get the proposal id
bytes32 proposalId = hashProposal(_targets, _values, _calldatas, _descriptionHash, _proposer);
// Ensure the proposal is ready to execute
if (!isReady(proposalId)) revert EXECUTION_NOT_READY(proposalId);
// Remove the proposal from the queue
delete timestamps[proposalId];
// Cache the number of targets
uint256 numTargets = _targets.length;
// Cannot realistically overflow
unchecked {
// For each target:
for (uint256 i = 0; i < numTargets; ++i) {
// Execute the transaction
(bool success, ) = _targets[i].call{ value: _values[i] }(_calldatas[i]);
// Ensure the transaction succeeded
if (!success) revert EXECUTION_FAILED(i);
}
}
emit TransactionExecuted(proposalId, _targets, _values, _calldatas);
}
/// ///
/// CANCEL PROPOSAL ///
/// ///
/// @notice Removes a queued proposal
/// @param _proposalId The proposal id
function cancel(bytes32 _proposalId) external onlyOwner {
// Ensure the proposal is queued
if (!isQueued(_proposalId)) revert PROPOSAL_NOT_QUEUED();
// Remove the proposal from the queue
delete timestamps[_proposalId];
emit TransactionCanceled(_proposalId);
}
/// ///
/// TREASURY SETTINGS ///
/// ///
/// @notice The time delay to execute a queued transaction
function delay() external view returns (uint256) {
return settings.delay;
}
/// @notice The time period to execute a proposal
function gracePeriod() external view returns (uint256) {
return settings.gracePeriod;
}
/// ///
/// UPDATE SETTINGS ///
/// ///
/// @notice Updates the transaction delay
/// @param _newDelay The new time delay
function updateDelay(uint256 _newDelay) external {
// Ensure the caller is the treasury itself
if (msg.sender != address(this)) revert ONLY_TREASURY();
emit DelayUpdated(settings.delay, _newDelay);
// Update the delay
settings.delay = SafeCast.toUint128(_newDelay);
}
/// @notice Updates the execution grace period
/// @param _newGracePeriod The new grace period
function updateGracePeriod(uint256 _newGracePeriod) external {
// Ensure the caller is the treasury itself
if (msg.sender != address(this)) revert ONLY_TREASURY();
emit GracePeriodUpdated(settings.gracePeriod, _newGracePeriod);
// Update the grace period
settings.gracePeriod = SafeCast.toUint128(_newGracePeriod);
}
/// ///
/// RECEIVE TOKENS ///
/// ///
/// @dev Accepts all ERC-721 transfers
function onERC721Received(
address,
address,
uint256,
bytes memory
) public pure returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
/// @dev Accepts all ERC-1155 single id transfers
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes memory
) public pure returns (bytes4) {
return ERC1155TokenReceiver.onERC1155Received.selector;
}
/// @dev Accept all ERC-1155 batch id transfers
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public pure returns (bytes4) {
return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
}
/// @dev Accepts ETH transfers
receive() external payable {}
/// ///
/// TREASURY UPGRADE ///
/// ///
/// @notice Ensures the caller is authorized to upgrade the contract and that the new implementation is valid
/// @dev This function is called in `upgradeTo` & `upgradeToAndCall`
/// @param _newImpl The new implementation address
function _authorizeUpgrade(address _newImpl) internal view override {
// Ensure the caller is the treasury itself
if (msg.sender != address(this)) revert ONLY_TREASURY();
// Ensure the new implementation is a registered upgrade
if (!manager.isRegisteredUpgrade(_getImplementation(), _newImpl)) revert INVALID_UPGRADE(_newImpl);
}
}
ManagerTypesV1.sol 19 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title ManagerTypesV1
/// @author Iain Nash & Rohan Kulkarni
/// @notice The external Base Metadata errors and functions
interface ManagerTypesV1 {
/// @notice Stores deployed addresses for a given token's DAO
struct DAOAddresses {
/// @notice Address for deployed metadata contract
address metadata;
/// @notice Address for deployed auction contract
address auction;
/// @notice Address for deployed treasury contract
address treasury;
/// @notice Address for deployed governor contract
address governor;
}
}
TokenStorageV1.sol 20 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { TokenTypesV1 } from "../types/TokenTypesV1.sol";
/// @title TokenStorageV1
/// @author Rohan Kulkarni
/// @notice The Token storage contract
contract TokenStorageV1 is TokenTypesV1 {
/// @notice The token settings
Settings internal settings;
/// @notice The vesting details of a founder
/// @dev Founder id => Founder
mapping(uint256 => Founder) internal founder;
/// @notice The recipient of a token
/// @dev ERC-721 token id => Founder
mapping(uint256 => Founder) internal tokenRecipient;
}
TokenStorageV2.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { TokenTypesV2 } from "../types/TokenTypesV2.sol";
/// @title TokenStorageV2
/// @author James Geary
/// @notice The Token storage contract
contract TokenStorageV2 is TokenTypesV2 {
/// @notice The minter status of an address
mapping(address => bool) public minter;
}
IGovernor.sol 297 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../../lib/interfaces/IUUPS.sol";
import { IOwnable } from "../../lib/utils/Ownable.sol";
import { IEIP712 } from "../../lib/utils/EIP712.sol";
import { GovernorTypesV1 } from "./types/GovernorTypesV1.sol";
import { IManager } from "../../manager/IManager.sol";
/// @title IGovernor
/// @author Rohan Kulkarni
/// @notice The external Governor events, errors and functions
interface IGovernor is IUUPS, IOwnable, IEIP712, GovernorTypesV1 {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a proposal is created
event ProposalCreated(
bytes32 proposalId,
address[] targets,
uint256[] values,
bytes[] calldatas,
string description,
bytes32 descriptionHash,
Proposal proposal
);
/// @notice Emitted when a proposal is queued
event ProposalQueued(bytes32 proposalId, uint256 eta);
/// @notice Emitted when a proposal is executed
/// @param proposalId The proposal id
event ProposalExecuted(bytes32 proposalId);
/// @notice Emitted when a proposal is canceled
event ProposalCanceled(bytes32 proposalId);
/// @notice Emitted when a proposal is vetoed
event ProposalVetoed(bytes32 proposalId);
/// @notice Emitted when a vote is casted for a proposal
event VoteCast(address voter, bytes32 proposalId, uint256 support, uint256 weight, string reason);
/// @notice Emitted when the governor's voting delay is updated
event VotingDelayUpdated(uint256 prevVotingDelay, uint256 newVotingDelay);
/// @notice Emitted when the governor's voting period is updated
event VotingPeriodUpdated(uint256 prevVotingPeriod, uint256 newVotingPeriod);
/// @notice Emitted when the basis points of the governor's proposal threshold is updated
event ProposalThresholdBpsUpdated(uint256 prevBps, uint256 newBps);
/// @notice Emitted when the basis points of the governor's quorum votes is updated
event QuorumVotesBpsUpdated(uint256 prevBps, uint256 newBps);
//// @notice Emitted when the governor's vetoer is updated
event VetoerUpdated(address prevVetoer, address newVetoer);
/// ///
/// ERRORS ///
/// ///
error INVALID_PROPOSAL_THRESHOLD_BPS();
error INVALID_QUORUM_THRESHOLD_BPS();
error INVALID_VOTING_DELAY();
error INVALID_VOTING_PERIOD();
/// @dev Reverts if a proposal already exists
/// @param proposalId The proposal id
error PROPOSAL_EXISTS(bytes32 proposalId);
/// @dev Reverts if a proposal isn't queued
/// @param proposalId The proposal id
error PROPOSAL_NOT_QUEUED(bytes32 proposalId);
/// @dev Reverts if the proposer didn't specify a target address
error PROPOSAL_TARGET_MISSING();
/// @dev Reverts if the number of targets, values, and calldatas does not match
error PROPOSAL_LENGTH_MISMATCH();
/// @dev Reverts if a proposal didn't succeed
error PROPOSAL_UNSUCCESSFUL();
/// @dev Reverts if a proposal was already executed
error PROPOSAL_ALREADY_EXECUTED();
/// @dev Reverts if a specified proposal doesn't exist
error PROPOSAL_DOES_NOT_EXIST();
/// @dev Reverts if the proposer's voting weight is below the proposal threshold
error BELOW_PROPOSAL_THRESHOLD();
/// @dev Reverts if a vote was prematurely casted
error VOTING_NOT_STARTED();
/// @dev Reverts if the caller wasn't the vetoer
error ONLY_VETOER();
/// @dev Reverts if the caller already voted
error ALREADY_VOTED();
/// @dev Reverts if a proposal was attempted to be canceled incorrectly
error INVALID_CANCEL();
/// @dev Reverts if a vote was attempted to be casted incorrectly
error INVALID_VOTE();
/// @dev Reverts if the caller was not the contract manager
error ONLY_MANAGER();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Initializes a DAO's governor
/// @param treasury The DAO's treasury address
/// @param token The DAO's governance token address
/// @param vetoer The address eligible to veto proposals
/// @param votingDelay The voting delay
/// @param votingPeriod The voting period
/// @param proposalThresholdBps The proposal threshold basis points
/// @param quorumThresholdBps The quorum threshold basis points
function initialize(
address treasury,
address token,
address vetoer,
uint256 votingDelay,
uint256 votingPeriod,
uint256 proposalThresholdBps,
uint256 quorumThresholdBps
) external;
/// @notice Creates a proposal
/// @param targets The target addresses to call
/// @param values The ETH values of each call
/// @param calldatas The calldata of each call
/// @param description The proposal description
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) external returns (bytes32);
/// @notice Casts a vote
/// @param proposalId The proposal id
/// @param support The support value (0 = Against, 1 = For, 2 = Abstain)
function castVote(bytes32 proposalId, uint256 support) external returns (uint256);
/// @notice Casts a vote with a reason
/// @param proposalId The proposal id
/// @param support The support value (0 = Against, 1 = For, 2 = Abstain)
/// @param reason The vote reason
function castVoteWithReason(
bytes32 proposalId,
uint256 support,
string memory reason
) external returns (uint256);
/// @notice Casts a signed vote
/// @param voter The voter address
/// @param proposalId The proposal id
/// @param support The support value (0 = Against, 1 = For, 2 = Abstain)
/// @param deadline The signature deadline
/// @param v The 129th byte and chain id of the signature
/// @param r The first 64 bytes of the signature
/// @param s Bytes 64-128 of the signature
function castVoteBySig(
address voter,
bytes32 proposalId,
uint256 support,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint256);
/// @notice Queues a proposal
/// @param proposalId The proposal id
function queue(bytes32 proposalId) external returns (uint256 eta);
/// @notice Executes a proposal
/// @param targets The target addresses to call
/// @param values The ETH values of each call
/// @param calldatas The calldata of each call
/// @param descriptionHash The hash of the description
/// @param proposer The proposal creator
function execute(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash,
address proposer
) external payable returns (bytes32);
/// @notice Cancels a proposal
/// @param proposalId The proposal id
function cancel(bytes32 proposalId) external;
/// @notice Vetoes a proposal
/// @param proposalId The proposal id
function veto(bytes32 proposalId) external;
/// @notice The state of a proposal
/// @param proposalId The proposal id
function state(bytes32 proposalId) external view returns (ProposalState);
/// @notice The voting weight of an account at a timestamp
/// @param account The account address
/// @param timestamp The specific timestamp
function getVotes(address account, uint256 timestamp) external view returns (uint256);
/// @notice The current number of votes required to submit a proposal
function proposalThreshold() external view returns (uint256);
/// @notice The current number of votes required to be in favor of a proposal in order to reach quorum
function quorum() external view returns (uint256);
/// @notice The details of a proposal
/// @param proposalId The proposal id
function getProposal(bytes32 proposalId) external view returns (Proposal memory);
/// @notice The timestamp when voting starts for a proposal
/// @param proposalId The proposal id
function proposalSnapshot(bytes32 proposalId) external view returns (uint256);
/// @notice The timestamp when voting ends for a proposal
/// @param proposalId The proposal id
function proposalDeadline(bytes32 proposalId) external view returns (uint256);
/// @notice The vote counts for a proposal
/// @param proposalId The proposal id
function proposalVotes(bytes32 proposalId)
external
view
returns (
uint256 againstVotes,
uint256 forVotes,
uint256 abstainVotes
);
/// @notice The timestamp valid to execute a proposal
/// @param proposalId The proposal id
function proposalEta(bytes32 proposalId) external view returns (uint256);
/// @notice The minimum basis points of the total token supply required to submit a proposal
function proposalThresholdBps() external view returns (uint256);
/// @notice The minimum basis points of the total token supply required to reach quorum
function quorumThresholdBps() external view returns (uint256);
/// @notice The amount of time until voting begins after a proposal is created
function votingDelay() external view returns (uint256);
/// @notice The amount of time to vote on a proposal
function votingPeriod() external view returns (uint256);
/// @notice The address eligible to veto any proposal (address(0) if burned)
function vetoer() external view returns (address);
/// @notice The address of the governance token
function token() external view returns (address);
/// @notice The address of the DAO treasury
function treasury() external view returns (address);
/// @notice Updates the voting delay
/// @param newVotingDelay The new voting delay
function updateVotingDelay(uint256 newVotingDelay) external;
/// @notice Updates the voting period
/// @param newVotingPeriod The new voting period
function updateVotingPeriod(uint256 newVotingPeriod) external;
/// @notice Updates the minimum proposal threshold
/// @param newProposalThresholdBps The new proposal threshold basis points
function updateProposalThresholdBps(uint256 newProposalThresholdBps) external;
/// @notice Updates the minimum quorum threshold
/// @param newQuorumVotesBps The new quorum votes basis points
function updateQuorumThresholdBps(uint256 newQuorumVotesBps) external;
/// @notice Updates the vetoer
/// @param newVetoer The new vetoer addresss
function updateVetoer(address newVetoer) external;
/// @notice Burns the vetoer
function burnVetoer() external;
/// @notice The EIP-712 typehash to vote with a signature
function VOTE_TYPEHASH() external view returns (bytes32);
}
ITreasury.sol 117 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IOwnable } from "../../lib/utils/Ownable.sol";
import { IUUPS } from "../../lib/interfaces/IUUPS.sol";
/// @title ITreasury
/// @author Rohan Kulkarni
/// @notice The external Treasury events, errors and functions
interface ITreasury is IUUPS, IOwnable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when a transaction is scheduled
event TransactionScheduled(bytes32 proposalId, uint256 timestamp);
/// @notice Emitted when a transaction is canceled
event TransactionCanceled(bytes32 proposalId);
/// @notice Emitted when a transaction is executed
event TransactionExecuted(bytes32 proposalId, address[] targets, uint256[] values, bytes[] payloads);
/// @notice Emitted when the transaction delay is updated
event DelayUpdated(uint256 prevDelay, uint256 newDelay);
/// @notice Emitted when the grace period is updated
event GracePeriodUpdated(uint256 prevGracePeriod, uint256 newGracePeriod);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if tx was already queued
error PROPOSAL_ALREADY_QUEUED();
/// @dev Reverts if tx was not queued
error PROPOSAL_NOT_QUEUED();
/// @dev Reverts if a tx isn't ready to execute
/// @param proposalId The proposal id
error EXECUTION_NOT_READY(bytes32 proposalId);
/// @dev Reverts if a tx failed
/// @param txIndex The index of the tx
error EXECUTION_FAILED(uint256 txIndex);
/// @dev Reverts if execution was attempted after the grace period
error EXECUTION_EXPIRED();
/// @dev Reverts if the caller was not the treasury itself
error ONLY_TREASURY();
/// @dev Reverts if the caller was not the contract manager
error ONLY_MANAGER();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Initializes a DAO's treasury
/// @param governor The governor address
/// @param timelockDelay The time delay to execute a queued transaction
function initialize(address governor, uint256 timelockDelay) external;
/// @notice The timestamp that a proposal is valid to execute
/// @param proposalId The proposal id
function timestamp(bytes32 proposalId) external view returns (uint256);
/// @notice If a proposal has been queued
/// @param proposalId The proposal ids
function isQueued(bytes32 proposalId) external view returns (bool);
/// @notice If a proposal is ready to execute (does not consider if a proposal has expired)
/// @param proposalId The proposal id
function isReady(bytes32 proposalId) external view returns (bool);
/// @notice If a proposal has expired to execute
/// @param proposalId The proposal id
function isExpired(bytes32 proposalId) external view returns (bool);
/// @notice Schedules a proposal for execution
/// @param proposalId The proposal id
function queue(bytes32 proposalId) external returns (uint256 eta);
/// @notice Removes a queued proposal
/// @param proposalId The proposal id
function cancel(bytes32 proposalId) external;
/// @notice Executes a queued proposal
/// @param targets The target addresses to call
/// @param values The ETH values of each call
/// @param calldatas The calldata of each call
/// @param descriptionHash The hash of the description
/// @param proposer The proposal creator
function execute(
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata calldatas,
bytes32 descriptionHash,
address proposer
) external payable;
/// @notice The time delay to execute a queued transaction
function delay() external view returns (uint256);
/// @notice The time period to execute a transaction
function gracePeriod() external view returns (uint256);
/// @notice Updates the time delay
/// @param newDelay The new time delay
function updateDelay(uint256 newDelay) external;
/// @notice Updates the grace period
/// @param newGracePeriod The grace period
function updateGracePeriod(uint256 newGracePeriod) external;
}
IInitializable.sol 30 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IInitializable
/// @author Rohan Kulkarni
/// @notice The external Initializable events and errors
interface IInitializable {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when the contract has been initialized or reinitialized
event Initialized(uint256 version);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if incorrectly initialized with address(0)
error ADDRESS_ZERO();
/// @dev Reverts if disabling initializers during initialization
error INITIALIZING();
/// @dev Reverts if calling an initialization function outside of initialization
error NOT_INITIALIZING();
/// @dev Reverts if reinitializing incorrectly
error ALREADY_INITIALIZED();
}
IERC1967Upgrade.sol 29 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title IERC1967Upgrade
/// @author Rohan Kulkarni
/// @notice The external ERC1967Upgrade events and errors
interface IERC1967Upgrade {
/// ///
/// EVENTS ///
/// ///
/// @notice Emitted when the implementation is upgraded
/// @param impl The address of the implementation
event Upgraded(address impl);
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if an implementation is an invalid upgrade
/// @param impl The address of the invalid implementation
error INVALID_UPGRADE(address impl);
/// @dev Reverts if an implementation upgrade is not stored at the storage slot of the original
error UNSUPPORTED_UUID();
/// @dev Reverts if an implementation does not support ERC1822 proxiableUUID()
error ONLY_UUPS();
}
ManagerStorageV1.sol 17 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { ManagerTypesV1 } from "../types/ManagerTypesV1.sol";
/// @notice Manager Storage V1
/// @author Rohan Kulkarni
/// @notice The Manager storage contract
contract ManagerStorageV1 is ManagerTypesV1 {
/// @notice If a contract has been registered as an upgrade
/// @dev Base impl => Upgrade impl
mapping(address => mapping(address => bool)) internal isUpgrade;
/// @notice Registers deployed addresses
/// @dev Token deployed address => Struct of all other DAO addresses
mapping(address => DAOAddresses) internal daoAddressesByToken;
}
IVersionedContract.sol 6 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
interface IVersionedContract {
function contractVersion() external pure returns (string memory);
}
ProposalHasher.sol 27 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @title ProposalHasher
/// @author tbtstl
/// @notice Helper contract to ensure proposal hashing functions are unified
abstract contract ProposalHasher {
/// ///
/// HASH PROPOSAL ///
/// ///
/// @notice Hashes a proposal's details into a proposal id
/// @param _targets The target addresses to call
/// @param _values The ETH values of each call
/// @param _calldatas The calldata of each call
/// @param _descriptionHash The hash of the description
/// @param _proposer The original proposer of the transaction
function hashProposal(
address[] memory _targets,
uint256[] memory _values,
bytes[] memory _calldatas,
bytes32 _descriptionHash,
address _proposer
) public pure returns (bytes32) {
return keccak256(abi.encode(_targets, _values, _calldatas, _descriptionHash, _proposer));
}
}
IBaseMetadata.sol 46 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { IUUPS } from "../../../lib/interfaces/IUUPS.sol";
/// @title IBaseMetadata
/// @author Rohan Kulkarni
/// @notice The external Base Metadata errors and functions
interface IBaseMetadata is IUUPS {
/// ///
/// ERRORS ///
/// ///
/// @dev Reverts if the caller was not the contract manager
error ONLY_MANAGER();
/// ///
/// FUNCTIONS ///
/// ///
/// @notice Initializes a DAO's token metadata renderer
/// @param initStrings The encoded token and metadata initialization strings
/// @param token The associated ERC-721 token address
function initialize(
bytes calldata initStrings,
address token
) external;
/// @notice Generates attributes for a token upon mint
/// @param tokenId The ERC-721 token id
function onMinted(uint256 tokenId) external returns (bool);
/// @notice The token URI
/// @param tokenId The ERC-721 token id
function tokenURI(uint256 tokenId) external view returns (string memory);
/// @notice The contract URI
function contractURI() external view returns (string memory);
/// @notice The associated ERC-721 token
function token() external view returns (address);
/// @notice Get metadata owner address
function owner() external view returns (address);
}
GovernorTypesV1.sol 69 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { Token } from "../../../token/Token.sol";
import { Treasury } from "../../treasury/Treasury.sol";
/// @title GovernorTypesV1
/// @author Rohan Kulkarni
/// @notice The Governor custom data types
interface GovernorTypesV1 {
/// @notice The governor settings
/// @param token The DAO governance token
/// @param proposalThresholdBps The basis points of the token supply required to create a proposal
/// @param quorumThresholdBps The basis points of the token supply required to reach quorum
/// @param treasury The DAO treasury
/// @param votingDelay The time delay to vote on a created proposal
/// @param votingPeriod The time period to vote on a proposal
/// @param vetoer The address with the ability to veto proposals
struct Settings {
Token token;
uint16 proposalThresholdBps;
uint16 quorumThresholdBps;
Treasury treasury;
uint48 votingDelay;
uint48 votingPeriod;
address vetoer;
}
/// @notice A governance proposal
/// @param proposer The proposal creator
/// @param timeCreated The timestamp that the proposal was created
/// @param againstVotes The number of votes against
/// @param forVotes The number of votes in favor
/// @param abstainVotes The number of votes abstained
/// @param voteStart The timestamp that voting starts
/// @param voteEnd The timestamp that voting ends
/// @param proposalThreshold The proposal threshold when the proposal was created
/// @param quorumVotes The quorum threshold when the proposal was created
/// @param executed If the proposal was executed
/// @param canceled If the proposal was canceled
/// @param vetoed If the proposal was vetoed
struct Proposal {
address proposer;
uint32 timeCreated;
uint32 againstVotes;
uint32 forVotes;
uint32 abstainVotes;
uint32 voteStart;
uint32 voteEnd;
uint32 proposalThreshold;
uint32 quorumVotes;
bool executed;
bool canceled;
bool vetoed;
}
/// @notice The proposal state type
enum ProposalState {
Pending,
Active,
Canceled,
Defeated,
Succeeded,
Queued,
Expired,
Executed,
Vetoed
}
}
TreasuryTypesV1.sol 15 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
/// @notice TreasuryTypesV1
/// @author Rohan Kulkarni
/// @notice The treasury's custom data types
contract TreasuryTypesV1 {
/// @notice The settings type
/// @param gracePeriod The time period to execute a proposal
/// @param delay The time delay to execute a queued transaction
struct Settings {
uint128 gracePeriod;
uint128 delay;
}
}
Proxy.sol 86 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
pragma solidity ^0.8.0;
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/
abstract contract Proxy {
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual {
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @dev This is a virtual function that should be overridden so it returns the address to which the fallback function
* and {_fallback} should delegate.
*/
function _implementation() internal view virtual returns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _fallback() internal virtual {
_beforeFallback();
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback() external payable virtual {
_fallback();
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
* is empty.
*/
receive() external payable virtual {
_fallback();
}
/**
* @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
* call, or as part of the Solidity `fallback` or `receive` functions.
*
* If overridden should call `super._beforeFallback()`.
*/
function _beforeFallback() internal virtual {}
}
TreasuryStorageV1.sol 16 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
import { TreasuryTypesV1 } from "../types/TreasuryTypesV1.sol";
/// @notice TreasuryStorageV1
/// @author Rohan Kulkarni
/// @notice The Treasury storage contract
contract TreasuryStorageV1 is TreasuryTypesV1 {
/// @notice The treasury settings
Settings internal settings;
/// @notice The timestamp that a queued proposal is ready to execute
/// @dev Proposal Id => Timestamp
mapping(bytes32 => uint256) internal timestamps;
}
StorageSlot.sol 88 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
}
draft-IERC1822.sol 20 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}
Read Contract
auctionImpl 0x428eb538 → address
contractVersion 0xa0a8e460 → string
getAddresses 0x906f6343 → address, address, address, address
getDAOVersions 0x239cad4f → tuple
getLatestVersions 0xd560fd2d → tuple
governorImpl 0x264aa22a → address
isRegisteredUpgrade 0x9bb8dcfd → bool
metadataImpl 0x3af4b97c → address
owner 0x8da5cb5b → address
pendingOwner 0xe30c3978 → address
proxiableUUID 0x52d1902d → bytes32
tokenImpl 0xffc4992f → address
treasuryImpl 0x4dfbbdaa → address
Write Contract 10 functions
These functions modify contract state and require a wallet transaction to execute.
acceptOwnership 0x79ba5097
No parameters
cancelOwnershipTransfer 0x23452b9c
No parameters
deploy 0x0cb20b3f
tuple[] _founderParams
tuple _tokenParams
tuple _auctionParams
tuple _govParams
returns: address, address, address, address, address
initialize 0xc4d66de8
address _newOwner
registerUpgrade 0x852e98fc
address _baseImpl
address _upgradeImpl
removeUpgrade 0xf3d93f1e
address _baseImpl
address _upgradeImpl
safeTransferOwnership 0x395db2cd
address _newOwner
transferOwnership 0xf2fde38b
address _newOwner
upgradeTo 0x3659cfe6
address _newImpl
upgradeToAndCall 0x4f1ef286
address _newImpl
bytes _data
Recent Transactions
No transactions found for this address