Address Contract Verified
Address
0xe7Ae968823c79CA4022096c0887971358D97ACaa
Balance
0 ETH
Nonce
1
Code Size
24546 bytes
Creator
0xd47e4431...bcCa at tx 0x37053e8c...591ed0
Indexed Transactions
0
Contract Bytecode
24546 bytes
0x60806040526004361015610022575b3615610018575f80fd5b61002061535e565b005b5f3560e01c806306fdde03146103c1578063095ea7b3146103bc57806309fec7f2146103b75780630d92e2c4146103b257806311d96f29146103ad5780631206b054146103a857806318160ddd146103a357806323ab98531461039e57806323b872dd146103995780632d204f18146103945780632f43f19d1461038f578063313ce5671461038a57806332cb6b0c1461038557806334cd584b146103805780633644e5151461037b5780633654ed48146103765780633950935114610371578063399f93ed1461036c5780633aec243f146103675780633cc7dcbf1461036257806342966c681461035d57806344a78c4a14610358578063481c9e4c146103535780635384b9301461034e5780635975f4a6146103495780635b774d36146103445780636bbd4e281461033f57806370a082311461033a57806376dc3c311461033557806379cc6790146103305780637ecebe001461032b57806382027f6b14610326578063840772bb1461032157806384b0196e1461031c57806385514f8914610317578063872900a61461031257806395d89b411461030d5780639d96425f14610308578063a457c2d714610303578063a5521999146102fe578063a9059cbb146102f9578063ad20810a146102f4578063b2654ef5146102ef578063bb9ad4c4146102ea578063bd6b3e47146102e5578063bfd6cc04146102e0578063c6854ea5146102db578063d505accf146102d6578063da28bc92146102d1578063dd62ed3e146102cc578063de29207d146102c7578063e577c3d9146102c2578063e63008af146102bd578063e9ec573a146102b8578063ef7cc6c4146102b3578063f621cc48146102ae578063f77c4791146102a95763fabf7a5b0361000e5761325c565b613236565b6131d5565b612f75565b612f5b565b612e36565b612e14565b612dfa565b612d87565b612b3d565b6129f0565b612997565b612789565b6126a2565b612503565b6124db565b612498565b612461565b6123d8565b6122db565b612134565b61208f565b611d6a565b611d27565b611c2a565b611b57565b611b1f565b611ae3565b611aaf565b6119fb565b6119c0565b611944565b6118ba565b611897565b611833565b6116d2565b611561565b611544565b6114ef565b6113a7565b611224565b611099565b610fee565b610fd4565b610e79565b610ca0565b610c85565b610ba5565b610b64565b610b2f565b6108a4565b61084f565b610654565b6105d7565b610596565b61052c565b6104f7565b61040c565b5f9103126103d057565b5f80fd5b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9060206104099281815201906103d4565b90565b346103d0575f3660031901126103d0576040515f60035461042c81613101565b80845290600181169081156104c25750600114610464575b6104608361045481850382611166565b604051918291826103f8565b0390f35b60035f9081527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b939250905b8082106104a857509091508101602001610454610444565b919260018160209254838588010152019101909291610490565b60ff191660208086019190915291151560051b840190910191506104549050610444565b6001600160a01b038116036103d057565b346103d05760403660031901126103d057610521600435610517816104e6565b60243590336153e6565b602060405160018152f35b346103d05760203660031901126103d0576001600160a01b03600435610551816104e6565b16805f52601160205261056760405f2054613101565b1515908161057e575b506040519015158152602090f35b90505f52601260205260ff60405f205416155f610570565b346103d05760203660031901126103d0576001600160a01b036004356105bb816104e6565b165f526012602052602060ff60405f2054166040519015158152f35b346103d0575f3660031901126103d05760206105f8600e54600f549061339f565b604051908152f35b9060206003198301126103d05760043567ffffffffffffffff81116103d05760040182601f820112156103d05780359267ffffffffffffffff84116103d0576020808301928560051b0101116103d0579190565b346103d05761066236610600565b9061068761067b61067b6009546001600160a01b031690565b6001600160a01b031690565b6020604051809263127d532b60e01b825281806106a733600483016133cc565b03915afa801561084a576106c2915f9161081b575b5061340a565b5f915f5b81811061073157837fb8192629a928914bbc89231d0a59dfb6065146c548bb48db63ac3cee71e014f96040518061072c33944383929190835260606020840152600b60608401526a416464656420557365727360a81b6080840152604060a08401930152565b0390a2005b61074461073f828486613445565b613467565b610767610762826001600160a01b03165f52601160205260405f2090565b613381565b156107f8575b906107f26001926107b061078f61078585888a613445565b6020810190613589565b906107ab846001600160a01b03165f52601160205260405f2090565b61360b565b6107e16107c960406107c386898b613445565b016136d4565b916001600160a01b03165f52601360205260405f2090565b9060ff801983541691151516179055565b016106c6565b93906107f261081160019361080c88613471565b61357b565b959192505061076d565b61083d915060203d602011610843575b6108358183611166565b8101906133b7565b5f6106bc565b503d61082b565b6133ff565b346103d0575f3660031901126103d0576020600254604051908152f35b801515036103d057565b9181601f840112156103d05782359167ffffffffffffffff83116103d057602083818601950101116103d057565b346103d05760803660031901126103d0576004356108c1816104e6565b602435906108ce8261086c565b60443567ffffffffffffffff81116103d0576108ee903690600401610876565b92906064359367ffffffffffffffff85116103d0576020610a16946109e36109d76109d1946109296001600160a01b039a3690600401610876565b96909761095361094d610762336001600160a01b03165f52601160205260405f2090565b156136de565b8915610af657610961613749565b604051610985816109778b820194303387613768565b03601f198101835282611166565b5190206040516109c6816109778b82019485603c917f19457468657265756d205369676e6564204d6573736167653a0a3332000000008252601c8201520190565b5190209236916111d3565b90615552565b828916981688146137af565b6109fb61067b61067b6009546001600160a01b031690565b604051808098819463127d532b60e01b8352600483016133cc565b03915afa91821561084a57610a39610a4c93610a68965f91610ad7575b506137fb565b335f90815260116020526040902061360b565b610a5533613471565b335f9081526013602052604090206107e1565b7fb8192629a928914bbc89231d0a59dfb6065146c548bb48db63ac3cee71e014f96040518061072c43829190825260606020830152601060608301527f557365722053656c662d5265677374640000000000000000000000000000000060808301526001604060a08401930152565b610af0915060203d602011610843576108358183611166565b5f610a33565b610afe61372a565b610961565b60609060031901126103d057600435610b1b816104e6565b90602435610b28816104e6565b9060443590565b346103d057610521610b4036610b03565b91610b54610b4e8383614220565b15613847565b610b5f8333836158ca565b615b2c565b346103d05760203660031901126103d0576001600160a01b03600435610b89816104e6565b165f526013602052602060ff60405f2054166040519015158152f35b5f3660031901126103d057610bbb341515613880565b60405163127d532b60e01b815260208180610bd933600483016138ba565b03816001600160a01b037f00000000000000000000000058d3c86da030b232de0acd06fad21b429a737c50165afa801561084a57610c1e915f91610c66575b506138f4565b610c32610c2d34600e54613950565b600e55565b60405134815233907fb39d01a14e10621cf124609e0b48ce96e2a0f42f85c5e74907d83ecc1c458c7190806020810161072c565b610c7f915060203d602011610843576108358183611166565b5f610c18565b346103d0575f3660031901126103d057602060405160128152f35b346103d0575f3660031901126103d05760206040516b033b2e3cadb136f08f6400008152f35b90602080835192838152019201905f5b818110610ce35750505090565b8251845260209384019390920191600101610cd6565b6104099160c0610d4f610d15845160e0855260e08501906103d4565b6020850151602085015260408501516040850152606085015160608501526080850151608085015260a085015184820360a0860152610cc6565b9201519060c0818403910152610cc6565b9080602083519182815201916020808360051b8301019401925f915b838310610d8b57505050505090565b9091929394602080610dc3600193601f198682030187526040838b516001600160a01b03815116845201519181858201520190610cf9565b97019301930191939290610d7c565b602081016020825282518091526040820191602060408360051b8301019401925f915b838310610e0457505050505090565b9091929394602080610e6a600193603f198682030187528951906001600160a01b0382511681526060610e59610e478685015160808886015260808501906103d4565b60408501518482036040860152610cf9565b920151906060818403910152610d60565b97019301930191939290610df5565b346103d0575f3660031901126103d057600b54610e95816139ab565b905f5b818110610ead57604051806104608582610dd2565b610ecc610eb982612935565b90546001600160a01b039160031b1c1690565b610ee7816001600160a01b03165f52600a60205260405f2090565b60018101908154610ef781613a2c565b925f5b828110610f665750505090610f4060026001959493610f29610f1a611199565b6001600160a01b039096168652565b610f35600982016131ba565b602086015201613ad6565b60408301526060820152610f548286613b43565b52610f5f8185613b43565b5001610e98565b80610f76610eb960019385612982565b610fb3610f948288906001600160a01b03165f5260205260405f2090565b610fae610f9f611188565b6001600160a01b039094168452565b613ad6565b6020820152610fc28288613b43565b52610fcd8187613b43565b5001610efa565b346103d0575f3660031901126103d05760206105f8615639565b346103d057611040610fff36610b03565b9060206001600160a01b0360095416611016612dcd565b604051632345fa8f60e21b8152600481019190915233602482015295869190829081906044820190565b03915afa92831561084a5761106361106894610460965f9161107a575b50613b57565b613be9565b60405190151581529081906020820190565b611093915060203d602011610843576108358183611166565b5f61105d565b346103d05760403660031901126103d0576004356110b6816104e6565b602435335f5260016020526110df8260405f20906001600160a01b03165f5260205260405f2090565b549081018091116110f45761052191336153e6565b61338b565b634e487b7160e01b5f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761112957604052565b6110f9565b6060810190811067ffffffffffffffff82111761112957604052565b60e0810190811067ffffffffffffffff82111761112957604052565b90601f8019910116810190811067ffffffffffffffff82111761112957604052565b60405190611197604083611166565b565b60405190611197608083611166565b60405190611197606083611166565b67ffffffffffffffff811161112957601f01601f191660200190565b9291926111df826111b7565b916111ed6040519384611166565b8294818452818301116103d0578281602093845f960137010152565b9080601f830112156103d057816020610409933591016111d3565b346103d05760603660031901126103d057600435611241816104e6565b60243567ffffffffffffffff81116103d057611261903690600401611209565b906044359061126f8261086c565b61128761067b61067b6009546001600160a01b031690565b906020604051809363127d532b60e01b825281806112a833600483016133cc565b03915afa801561084a57611328946112cd611311926107e1955f9161081b575061340a565b6112eb610762846001600160a01b03165f52601160205260405f2090565b15611399575b61130c836001600160a01b03165f52601160205260405f2090565b613f21565b6001600160a01b03165f52601360205260405f2090565b6040517fb8192629a928914bbc89231d0a59dfb6065146c548bb48db63ac3cee71e014f933918061072c43829190825260606020830152601260608301527f41646465642f757064617465642055736572000000000000000000000000000060808301526001604060a08401930152565b6113a283613471565b6112f1565b5f3660031901126103d057335f908152600a602052604090206113d76113d06009830154613101565b1515613fe1565b6113ef61067b61067b6009546001600160a01b031690565b906020604051809363959c428b60e01b82528180611411323360048401614053565b03915afa91821561084a57600692611430915f916114d0575b506140cf565b6004810161143f348254613950565b90550161144c815461357b565b90555f516020615f8d5f395f51905f52604051806114c1343333845f9294936001600160a01b0360a09316825260c06020830152600d60c08301526c115512081c9958db185a5b5959609a1b60e08301526001600160a01b036101008301966001604085015216606083015260808201520152565b0390a160405160018152602090f35b6114e9915060203d602011610843576108358183611166565b5f61142a565b346103d05760203660031901126103d0576001600160a01b03600435611514816104e6565b165f52600c60205260405f2060018154910154906104606040519283928360209093929193604081019481520152565b346103d05760203660031901126103d05761002060043533615755565b346103d05761156f36610600565b9061158861067b61067b6009546001600160a01b031690565b6020604051809263127d532b60e01b825281806115a833600483016133cc565b03915afa801561084a576115c2915f9161081b575061340a565b5f915f5b81811061162f57837fb8192629a928914bbc89231d0a59dfb6065146c548bb48db63ac3cee71e014f96040518061072c33944383929190835260606020840152600e60608401526d155cd95c9cc81498dd1a5d9d195960921b6080840152604060a08401930152565b61163d61073f828486614151565b61165b610762826001600160a01b03165f52601160205260405f2090565b1515806116a7575b611671575b506001016115c6565b6001919461080c6116966116a0936001600160a01b03165f52601260205260405f2090565b805460ff19169055565b9390611668565b506116cd6116c6826001600160a01b03165f52601260205260405f2090565b5460ff1690565b611663565b346103d05760403660031901126103d0576004356116ef816104e6565b6024359061170e336001600160a01b03165f52600a60205260405f2090565b9161172661171f6009850154613101565b1515614161565b61173e61067b61067b6009546001600160a01b031690565b926020604051809563959c428b60e01b82528180611760323360048401614053565b03915afa93841561084a57836004926117915f516020615f8d5f395f51905f52976117a7945f916114d057506140cf565b906001600160a01b03165f5260205260405f2090565b600281016117b6848254613950565b9055016117c3815461357b565b90556040805133815260c060208201819052600f908201527f546f6b656e207265636c61696d6564000000000000000000000000000000000060e082015260019181019190915230606082015260808101919091526001600160a01b0390911660a08201528061010081016114c1565b346103d05760403660031901126103d0576020611867600435611855816104e6565b602435906118628261086c565b6141ad565b60ff60405191168152f35b60409060031901126103d05760043561188a816104e6565b90602435610409816104e6565b346103d05760206118b06118aa36611872565b90614220565b6040519015158152f35b346103d05760403660031901126103d05761191d6004356118da816104e6565b60243560206001600160a01b03600954166118f3612dcd565b604051632345fa8f60e21b8152600481019190915233602482015294859190829081906044820190565b03915afa91821561084a5761193f61106893610460955f9161107a5750613b57565b61437a565b346103d05760a03660031901126103d057600435611961816104e6565b60243567ffffffffffffffff81116103d057611981903690600401611209565b6044359161198e836104e6565b6064359167ffffffffffffffff83116103d057610460936119b6611068943690600401611209565b9160843593614676565b346103d05760203660031901126103d0576001600160a01b036004356119e5816104e6565b165f525f602052602060405f2054604051908152f35b346103d05760203660031901126103d0576084600435611a1a8161086c565b60206001600160a01b03600954166040519384809263127d532b60e01b825260406004830152600a6044830152692a37b5b2b720b236b4b760b11b60648301523360248301525afa91821561084a5761002092611a7e915f91611a90575b50614cf3565b60ff8019601054169115151617601055565b611aa9915060203d602011610843576108358183611166565b5f611a78565b346103d05760403660031901126103d057610020600435611acf816104e6565b60243590611ade8233836158ca565b615755565b346103d05760203660031901126103d0576001600160a01b03600435611b08816104e6565b165f526007602052602060405f2054604051908152f35b346103d05760203660031901126103d05760206118b0600435611b41816104e6565b614d2c565b906020610409928181520190610d60565b346103d05760203660031901126103d0576001600160a01b03600435611b7c816104e6565b165f52600a60205260405f206001810190815490611b9982613a2c565b925f5b838110611bb157604051806104608782611b46565b806001600160a01b03611bc660019385612982565b90549060031b1c16611c09611bec8287906001600160a01b03165f5260205260405f2090565b610fae60405193611bfc8561110d565b6001600160a01b03168452565b6020820152611c188288613b43565b52611c238187613b43565b5001611b9c565b346103d0575f3660031901126103d057611cce611c667f44656c4e6f72746520436c756220546f6b656e0000000000000000000000001361596b565b611c8f7f31000000000000000000000000000000000000000000000000000000000000016159c4565b6020604051611c9e8282611166565b5f815281611cdc81830194601f198301368737604051978897600f60f81b895260e0858a015260e08901906103d4565b9087820360408901526103d4565b914660608701523060808701525f60a087015285830360c087015251918281520192915f5b828110611d1057505050500390f35b835185528695509381019392810192600101611d01565b346103d05760203660031901126103d0576001600160a01b03600435611d4c816104e6565b165f52600c60205260206105f860405f20600181549101549061339f565b346103d05760803660031901126103d057600435611d87816104e6565b60243567ffffffffffffffff81116103d057611da7903690600401611209565b60443567ffffffffffffffff81116103d057611dc7903690600401611209565b916064359160095493611e046020611ddd612dcd565b604051632345fa8f60e21b8152600481019190915233602482015291829081906044820190565b03816001600160a01b038a165afa801561084a57611e28915f9161107a5750613b57565b611e5e61067b61067b6001600160a01b03861697611e47891515614983565b611e52881515614a26565b61067b893b1515614a71565b6020604051809263127d532b60e01b82528180611e7e89600483016138ba565b03915afa95861561084a576005611fa4945f94611eb25f516020615f8d5f395f51905f529a87968791612070575b50614b1f565b611ecd886001600160a01b03165f52600a60205260405f2090565b916009830190611edd8254613101565b1515871461205957611ef1611f06926131ba565b60208151910120906020815191012014614db1565b60028201908154611f1681613101565b15158061204f575b87146120195750611f31611f46926131ba565b60208151910120906020815191012014614dfd565b611f5288471015614e49565b60038101611f61898254613950565b905501611f6e815461357b565b9055604051866020820191632a05e41960e11b835260048152611f92602482611166565b51925af1611f9e614c78565b50614e95565b6114c160405192839233845f9294936001600160a01b0360a09316825260c06020830152601560c08301527f455448207472616e7366657272656420746f205343000000000000000000000060e08301526001600160a01b036101008301966001604085015216606083015260808201520152565b61202290613101565b1580612045575b612035575b5050611f46565b61203e91613f21565b5f8061202e565b5080511515612029565b5081511515611f1e565b61206291613f21565b61206b846134b6565b611f06565b612089915060203d602011610843576108358183611166565b5f611eac565b346103d0575f3660031901126103d0576040515f6004546120af81613101565b80845290600181169081156104c257506001146120d6576104608361045481850382611166565b60045f9081527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b939250905b80821061211a57509091508101602001610454610444565b919260018160209254838588010152019101909291612102565b346103d05760403660031901126103d057600435612151816104e6565b60405163959c428b60e01b8152602435916001600160a01b037f00000000000000000000000058d3c86da030b232de0acd06fad21b429a737c5016916020818061219f338060048401614ee1565b0381865afa801561084a576121bb915f916122bc575b50614f51565b6121c783471015614f8c565b6020604051809363127d532b60e01b825281806121e786600483016138ba565b03915afa91821561084a576001600160a01b039261220c915f9161229d575b50614fe2565b61222061221b84600f54613950565b600f55565b1690813b156103d057604051632f43f19d60e01b8152905f8260048184875af190811561084a577f3a5fb187f630a03abb03bd602b751f1d5220b3853ba3f8a89e27e3cfbad266d19261072c92612283575b506040519081529081906020820190565b806122915f61229793611166565b806103c6565b5f612272565b6122b6915060203d602011610843576108358183611166565b5f612206565b6122d5915060203d602011610843576108358183611166565b5f6121b5565b346103d05760403660031901126103d0576004356122f8816104e6565b60243590335f5260016020526123228160405f20906001600160a01b03165f5260205260405f2090565b549180831061234357612337920390336153e6565b60405160018152602090f35b60405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608490fd5b60206040818301928281528451809452019201905f5b8181106123b95750505090565b82516001600160a01b03168452602093840193909201916001016123ac565b346103d0575f3660031901126103d05760405180602060145491828152019060145f527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec905f5b818110612442576104608561243681870382611166565b60405191829182612396565b82546001600160a01b031684526020909301926001928301920161241f565b346103d05760403660031901126103d057610521600435612481816104e6565b60243590612492610b4e8233614220565b33615b2c565b346103d0575f3660031901126103d05760206040516001600160a01b037f00000000000000000000000058d3c86da030b232de0acd06fad21b429a737c50168152f35b346103d0575f3660031901126103d057600e54600f5460408051928352602083019190915290f35b346103d05760603660031901126103d057600435612520816104e6565b60243560443567ffffffffffffffff81116103d057612543903690600401611209565b9161258760206001600160a01b036009541661255d612dcd565b604051632345fa8f60e21b8152600481019190915233602482015292839190829081906044820190565b03915afa801561084a576125a1915f9161107a5750613b57565b6125ad82471015614e49565b5f808080856001600160a01b0386165af16125c6614c78565b5015612603576125fe7fbe08187d07e266b4293df92b33faf1fc6182051543f4ecab3ce8866d95d9a0d893604051938493338561503e565b0390a1005b60405162461bcd60e51b815260206004820152601360248201527f455448207472616e73666572206661696c6564000000000000000000000000006044820152606490fd5b60206040818301928281528451809452019201905f5b81811061266b5750505090565b90919260206060600192604087516001600160a01b038151168352848101518584015201516040820152019401910191909161265e565b346103d0575f3660031901126103d057600d546126be8161395d565b906126cc6040519283611166565b808252601f196126db8261395d565b015f5b8181106127605750505f5b8181106126fe57604051806104608582612648565b8061270d610eb9600193612952565b612728816001600160a01b03165f52600c60205260405f2090565b8381549101549061273a610f9f6111a8565b6020830152604082015261274e8286613b43565b526127598185613b43565b50016126e9565b60209060405161276f8161112e565b5f81525f838201525f6040820152828287010152016126de565b346103d05760803660031901126103d0576004356127a6816104e6565b602435604435916127b6836104e6565b60643567ffffffffffffffff81116103d0576127d6903690600401611209565b6127ef60206001600160a01b036009541661255d612dcd565b03915afa801561084a57612809915f9161107a5750613b57565b6001600160a01b0384169361281f85151561506c565b6040516370a0823160e01b8152306004820152602081602481895afa90811561084a576128579186915f916128f2575b5010156150b8565b60405163a9059cbb60e01b81526001600160a01b03841660048201526024810185905294602090869060449082905f905af191821561084a576128c66125fe937fe1424a88578f6b78c0c71d7763af2e27963b0471e2affd78ab99c81d7c6d0a87975f916128d3575b50615104565b6040519485943386615150565b6128ec915060203d602011610843576108358183611166565b5f6128c0565b612914915060203d60201161291a575b61290c8183611166565b810190614c0d565b5f61284f565b503d612902565b634e487b7160e01b5f52603260045260245ffd5b600b5481101561294d57600b5f5260205f2001905f90565b612921565b600d5481101561294d57600d5f5260205f2001905f90565b60145481101561294d5760145f5260205f2001905f90565b805482101561294d575f5260205f2001905f90565b346103d05760203660031901126103d0576004356014548110156103d0576001600160a01b0360209160145f527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec015416604051908152f35b346103d05760e03660031901126103d057600435612a0d816104e6565b602435612a19816104e6565b604435906064359260843560ff811681036103d05760a4359460c435814211612af85761002096612af393612adb8689612ad38a610977612ae0996001600160a01b0386165f52600760205260405f2080549060018201905560405195869460208601988991926001600160a01b0360a09497969592978160c08601997f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98752166020860152166040840152606083015260808201520152565b5190206159fb565b615a21565b6001600160a01b03808416911614615185565b6153e6565b60405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606490fd5b346103d057612b4b36610b03565b60405163959c428b60e01b81527f00000000000000000000000058d3c86da030b232de0acd06fad21b429a737c506001600160a01b03169391929060208180612b98338060048401614ee1565b0381885afa801561084a57612bb3915f916122bc5750614f51565b6040516370a0823160e01b81523060048201526001600160a01b0383169490602081602481895afa90811561084a57612bf79186915f91612d68575b5010156151d1565b6020604051809263127d532b60e01b82528180612c1787600483016138ba565b03915afa801561084a57612c32915f91612d49575b5061521d565b6001612c4f836001600160a01b03165f52600c60205260405f2090565b01612c5b848254613950565b905560405163095ea7b360e01b81526001600160a01b038216600482015260248101849052906020826044815f895af191821561084a576001600160a01b0392612cac915f91612d2a575b50615269565b1691823b156103d057604051633bdf31b160e21b81526001600160a01b03929092166004830152602482018190525f8260448183875af190811561084a577fde7ffce7dbe60759b5cde8c98116d4a9fc208820ec7614e7c5a62998ef08ab9392612d259261228357506040519081529081906020820190565b0390a3005b612d43915060203d602011610843576108358183611166565b5f612ca6565b612d62915060203d602011610843576108358183611166565b5f612c2c565b612d81915060203d60201161291a5761290c8183611166565b5f612bef565b346103d0576020612dc46001600160a01b03612da236611872565b91165f526001835260405f20906001600160a01b03165f5260205260405f2090565b54604051908152f35b600d6020604051612ddf604082611166565b828152016c2a3932b0b9bab93ca0b236b4b760991b81522090565b346103d0575f3660031901126103d05760206105f8612dcd565b346103d0575f3660031901126103d057602060ff601054166040519015158152f35b346103d0575f3660031901126103d0576014545f805b828110612ee15750612e5d906152a6565b905f5f5b828110612e7657604051806104608682612396565b80612e85610eb960019361296a565b612eaa612ea66116c6836001600160a01b03165f52601260205260405f2090565b1590565b612eb6575b5001612e61565b612edb90612ecd612ec68661357b565b9588613b43565b906001600160a01b03169052565b5f612eaf565b612f0d612ea66116c6612ef6610eb98561296a565b6001600160a01b03165f52601260205260405f2090565b612f1a575b600101612e4c565b90612f2660019161357b565b919050612f12565b600d6020604051612f40604082611166565b828152016c14db585c9d10dbdb9d1c9858dd609a1b81522090565b346103d0575f3660031901126103d05760206105f8612f2e565b346103d05760403660031901126103d057600435612f92816104e6565b602435612fa08115156152d8565b60405163127d532b60e01b815260208180612fbe33600483016138ba565b03816001600160a01b037f00000000000000000000000058d3c86da030b232de0acd06fad21b429a737c50165afa801561084a57613003915f916130e2575b50615312565b61305b6001600160a01b0383169261301d83303387615a38565b613038816001600160a01b03165f52600c60205260405f2090565b5415806130bc575b61309c576001600160a01b03165f52600c60205260405f2090565b613066828254613950565b905560405190815233907f6e28d3ed200c688020063de571167e4f1e382016cab9a0b7aaa09912d9f5a34b908060208101612d25565b6130a5816134fb565b6001600160a01b03165f52600c60205260405f2090565b5060016130da826001600160a01b03165f52600c60205260405f2090565b015415613040565b6130fb915060203d602011610843576108358183611166565b5f612ffd565b90600182811c9216801561312f575b602083101461311b57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691613110565b5f929181549161314883613101565b808352926001811690811561319d575060011461316457505050565b5f9081526020812093945091925b838310613183575060209250010190565b600181602092949394548385870101520191019190613172565b915050602093945060ff929192191683830152151560051b010190565b906111976131ce9260405193848092613139565b0383611166565b346103d05760203660031901126103d0576001600160a01b036004356131fa816104e6565b165f52601160205261046061321b61322260405f2060405192838092613139565b0382611166565b6040519182916020835260208301906103d4565b346103d0575f3660031901126103d05760206001600160a01b0360095416604051908152f35b346103d05761326a36610600565b9061328361067b61067b6009546001600160a01b031690565b6020604051809263127d532b60e01b825281806132a333600483016133cc565b03915afa801561084a576132c1915f91613362575b5092919261340a565b5f915f915b80831061332f57837fb8192629a928914bbc89231d0a59dfb6065146c548bb48db63ac3cee71e014f96040518061072c33944383929190835260606020840152600e60608401526d155cd95c9cc8149cdd1c98dd195960921b6080840152604060a08401930152565b90919261335960019161080c61334c612ef661073f89888a614151565b805460ff19166001179055565b930191906132c6565b61337b915060203d602011610843576108358183611166565b5f6132b8565b6104099054613101565b634e487b7160e01b5f52601160045260245ffd5b81810392915f1380158285131691841216176110f457565b908160209103126103d057516104098161086c565b6040808252600990820152682932b3b4b9ba3930b960b91b60608201526001600160a01b03909116602082015260800190565b6040513d5f823e3d90fd5b1561341157565b60405162461bcd60e51b815260206004820152600c60248201526b2737ba102932b3b9ba3930b960a11b6044820152606490fd5b919081101561294d5760051b81013590605e19813603018212156103d0570190565b35610409816104e6565b601454600160401b811015611129576001810160145560145481101561294d576001600160a01b039060145f5260205f200191166001600160a01b0319825416179055565b600b54600160401b8110156111295760018101600b55600b5481101561294d576001600160a01b0390600b5f5260205f200191166001600160a01b0319825416179055565b600d54600160401b8110156111295760018101600d55600d5481101561294d576001600160a01b0390600d5f5260205f200191166001600160a01b0319825416179055565b8054600160401b8110156111295761355d91600182018155612982565b6001600160a01b0380839493549260031b9316831b921b1916179055565b5f1981146110f45760010190565b903590601e19813603018212156103d0570180359067ffffffffffffffff82116103d0576020019181360383136103d057565b601f82116135c957505050565b5f5260205f20906020601f840160051c83019310613601575b601f0160051c01905b8181106135f6575050565b5f81556001016135eb565b90915081906135e2565b90929167ffffffffffffffff8111611129576136318161362b8454613101565b846135bc565b5f601f821160011461366f5781906136609394955f92613664575b50508160011b915f199060031b1c19161790565b9055565b013590505f8061364c565b601f19821694613682845f5260205f2090565b915f5b8781106136bc5750836001959697106136a3575b505050811b019055565b01355f19600384901b60f8161c191690555f8080613699565b90926020600181928686013581550194019101613685565b356104098161086c565b156136e557565b60405162461bcd60e51b815260206004820152601760248201527f5573657220616c726561647920726567697374657265640000000000000000006044820152606490fd5b60405190613739604083611166565b60018252600360fc1b6020830152565b60405190613758604083611166565b60018252603160f81b6020830152565b90926020926028946bffffffffffffffffffffffff199060601b1683526bffffffffffffffffffffffff199060601b1660148301528051928391018483015e01015f815290565b156137b657565b60405162461bcd60e51b815260206004820152601660248201527f536967206e6f742066726f6d20726567697374726172000000000000000000006044820152606490fd5b1561380257565b60405162461bcd60e51b815260206004820152601160248201527f696e76616c6964207265676973747261720000000000000000000000000000006044820152606490fd5b1561384e57565b60405162461bcd60e51b815260206004820152600a6024820152691c995cdd1c9a58dd195960b21b6044820152606490fd5b1561388757565b60405162461bcd60e51b815260206004820152600b60248201526a139bc8115512081cd95b9d60aa1b6044820152606490fd5b6040808252600d908201526c14db585c9d10dbdb9d1c9858dd609a1b606082015290919060206001600160a01b03608083015b9416910152565b156138fb57565b60405162461bcd60e51b815260206004820152602760248201527f53656e646572206973206e6f7420616e206f6666696369616c20536d617274436044820152661bdb9d1c9858dd60ca1b6064820152608490fd5b919082018092116110f457565b67ffffffffffffffff81116111295760051b60200190565b604051906139828261114a565b606060c0838281525f60208201525f60408201525f838201525f60808201528260a08201520152565b906139b58261395d565b6139c26040519182611166565b82815280926139d3601f199161395d565b015f5b8181106139e257505050565b60405190608082019180831067ffffffffffffffff841117611129576020926040525f8152606083820152613a15613975565b6040820152606080820152828286010152016139d6565b90613a368261395d565b613a436040519182611166565b8281528092613a54601f199161395d565b01905f5b828110613a6457505050565b602090604051613a738161110d565b5f8152613a7e613975565b8382015282828501015201613a58565b90604051918281549182825260208201905f5260205f20925f5b818110613abd57505061119792500383611166565b8454835260019485019487945060209093019201613aa8565b90604051613ae38161114a565b60c0613b3e60068395604051613afd8161321b8185613139565b855260018101546020860152600281015460408601526003810154606086015260048101546080860152613b3360058201613a8e565b60a086015201613a8e565b910152565b805182101561294d5760209160051b010190565b15613b5e57565b60405162461bcd60e51b815260206004820152600d60248201526c556e617574682061636365737360981b6044820152606490fd5b662386f26fc0ffff198101919082116110f457565b919082039182116110f457565b8054600160401b81101561112957613bd291600182018155612982565b819291549060031b91821b915f19901b1916179055565b919091613c3b6001613c1085611791856001600160a01b03165f52600a60205260405f2090565b01546002613c3386611791866001600160a01b03165f52600a60205260405f2090565b015490613ba8565b6001613c5c85611791856001600160a01b03165f52600a60205260405f2090565b01548311613e7557808311613dca575060405163baab627360e01b81526001600160a01b03841660048201526024810183905292602084806044810103815f6001600160a01b0387165af190811561084a575f516020615f8d5f395f51905f5294613da592613dad575b506002613ce882611791866001600160a01b03165f52600a60205260405f2090565b01613cf4858254613950565b90556004613d1782611791866001600160a01b03165f52600a60205260405f2090565b01613d22815461357b565b90556040519384933385929360a09295946001600160a01b03809316855260c06020860152602860c08601527f5265636c61696d20544f4b454e2072657175657374207375626d69747465642060e086015267746f2053504f4b4560c01b6101008601528261012086019760016040880152166060860152608085015216910152565b0390a1600190565b613dc59060203d602011610843576108358183611166565b613cc6565b613e09613e6e936006613e035f516020615f8d5f395f51905f5297611791613e0e976001600160a01b03165f52600a60205260405f2090565b01613bb5565b613b93565b6040805133815260c06020808301829052908201527f5265636c61696d207265717565737420626f756e63652c20746f6f206869676860e08201525f918101829052306060820152608081019290925260a0820152908190610100820190565b0390a15f90565b5090613ead816005613e035f516020615f8d5f395f51905f5296611791613e6e976001600160a01b03165f52600a60205260405f2090565b6040805133815260c0602082018190526029908201527f5265636c61696d2072657175657374206578636565647320746f74616c20747260e082015268185b9cd9995c9c995960ba1b6101008201525f918101829052306060820152608081019290925260a0820152908190610120820190565b919091825167ffffffffffffffff811161112957613f438161362b8454613101565b6020601f8211600114613f7d5781906136609394955f92613f725750508160011b915f199060031b1c19161790565b015190505f8061364c565b601f19821690613f90845f5260205f2090565b915f5b818110613fc957509583600195969710613fb157505050811b019055565b01515f1960f88460031b161c191690555f8080613699565b9192602060018192868b015181550194019201613f93565b15613fe857565b60405162461bcd60e51b815260206004820152603160248201527f547265617375727920656e74727920646f6573206e6f7420657869737420666f60448201527f722063616c6c65722061732053504f4b450000000000000000000000000000006064820152608490fd5b6080906001600160a01b036140c460019496959660a084528261409460a08601604090600d81526c14db585c9d10dbdb9d1c9858dd609a1b60208201520190565b911660208501528381036040850152604090600d81526c2a3932b0b9bab93ca0b236b4b760991b60208201520190565b951660608201520152565b156140d657565b60405162461bcd60e51b815260206004820152604760248201527f4f726967696e61746f72206d75737420626520747265617320616e642073656e60448201527f646572206d75737420626520616e206f6666696369616c20736d61727420636f606482015266373a3930b1ba1760c91b608482015260a490fd5b919081101561294d5760051b0190565b1561416857565b60405162461bcd60e51b815260206004820152601d60248201527f547265617375727920656e74727920646f6573206e6f742065786973740000006044820152606490fd5b9080614213575b61420d576001600160a01b0381165f5260116020526141d660405f2054613101565b15614208576141f860ff916001600160a01b03165f52601360205260405f2090565b541661420357600290565b600190565b505f90565b50600390565b5060ff60105416156141b4565b61423e610762826001600160a01b03165f52601160205260405f2090565b151580614356575b61434f57614259612ea660105460ff1690565b8015614340575b61433a5760209061427f61067b61067b6009546001600160a01b031690565b614287612dcd565b91614290612f2e565b61430b61429b612f2e565b946142a4612f2e565b6040519889978896879663d684b3c360e01b885281339660048a019495610100966001600160a01b03809681949c9b96825f9c976101208c019f8c521660208b015260408a015216606088015260808701521660a085015260c08401521660e08201520152565b03915afa90811561084a575f9161432157501590565b612ea6915060203d602011610843576108358183611166565b50505f90565b5061434a82614d2c565b614260565b5050600190565b506143756116c6826001600160a01b03165f52601260205260405f2090565b614246565b906143ba600361439b846001600160a01b03165f52600a60205260405f2090565b01546004613c33856001600160a01b03165f52600a60205260405f2090565b60036143d7846001600160a01b03165f52600a60205260405f2090565b015482116145c25780821161451557506001600160a01b03821691823b156103d05760405163b4b6a12760e01b815260048101839052925f908490602490829084905af192831561084a575f516020615f8d5f395f51905f5293614501575b506004614454826001600160a01b03165f52600a60205260405f2090565b01614460838254613950565b9055600661447f826001600160a01b03165f52600a60205260405f2090565b0161448a815461357b565b9055613da560405192839233845f9294936001600160a01b0360a09316825260c06020830152601d60c08301527f455448207265636c61696d2072657175657374207375626d697474656400000060e08301526001600160a01b036101008301966001604085015216606083015260808201520152565b806122915f61450f93611166565b5f614436565b61454990613e0984936008613e035f516020615f8d5f395f51905f52976001600160a01b03165f52600a60205260405f2090565b6040805133815260c0602082018190526024908201527f455448205265636c61696d207265717565737420626f756e63652c20746f6f2060e0820152630d0d2ced60e31b6101008201525f9181018290526001600160a01b039093166060840152608083019190915260a0820152806101208101613e6e565b50816145f3826007613e035f516020615f8d5f395f51905f52966001600160a01b03165f52600a60205260405f2090565b613e6e60405192839233845f9294936001600160a01b0360a09316825260c06020830152602560c08301527f455448205265636c61696d206578636565647320746f74616c207472616e736660e083015264195c9c995960da1b6101008301526001600160a01b0361012083019685604085015216606083015260808201520152565b91939290936009549161468c6020611ddd612dcd565b03816001600160a01b0388165afa801561084a576146b0915f9161107a5750613b57565b6146c46001600160a01b0385161515614983565b61470661067b61067b6001600160a01b038516956146e38715156149ce565b6146ee891515614a26565b6146fa883b1515614a71565b61067b85511515614ad3565b6020604051809263127d532b60e01b825281806147268a600483016138ba565b03915afa801561084a57614740915f916120705750614b1f565b61475b846001600160a01b03165f52600a60205260405f2090565b95600987019061476b8254613101565b1561496c5761477c614791926131ba565b60208151910120906020815191012014614b75565b6147ac8287906001600160a01b03165f5260205260405f2090565b95826147b88854613101565b6149355760016147c89201613540565b8051614925575b505b6040516370a0823160e01b8152306004820152602081602481865afa95861561084a5784613da59460038893614823855f516020615f8d5f395f51905f529c614846985f91614906575b501015614c1c565b60018101614832868254613950565b90550161483f815461357b565b9055615889565b60405163ef53cc9160e01b602082019081526001600160a01b038516602483015260448201869052614897915f9182916148838160648101610977565b519082885af1614891614c78565b50614ca7565b6040519384933385929360a09295946001600160a01b03809316855260c06020860152601b60c08601527f416d6f756e74207472616e7366657272656420746f2073706f6b65000000000060e08601528261010086019760016040880152166060860152608085015216910152565b61491f915060203d60201161291a5761290c8183611166565b5f61481b565b61492f9086613f21565b5f6147cf565b50508051614944575b506147d1565b61496690614951876131ba565b60208151910120906020815191012014614bc1565b5f61493e565b61497591613f21565b61497e846134b6565b614791565b1561498a57565b606460405162461bcd60e51b815260206004820152602060248201527f5343416464726573732063616e6e6f74206265207a65726f20616464726573736044820152fd5b156149d557565b60405162461bcd60e51b815260206004820152602360248201527f546f6b656e416464726573732063616e6e6f74206265207a65726f206164647260448201526265737360e81b6064820152608490fd5b15614a2d57565b606460405162461bcd60e51b815260206004820152602060248201527f416d6f756e74206d7573742062652067726561746572207468616e207a65726f6044820152fd5b15614a7857565b60405162461bcd60e51b815260206004820152602d60248201527f496e76616c696420636f6e74726163743a20534341646472657373206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608490fd5b15614ada57565b60405162461bcd60e51b815260206004820152601b60248201527f546f6b656e206c6162656c2063616e6e6f7420626520656d70747900000000006044820152606490fd5b15614b2657565b60405162461bcd60e51b815260206004820152602160248201527f53706f6b65206e6f74207965742070617274206f66205343206e6574776f726b6044820152601760f91b6064820152608490fd5b15614b7c57565b60405162461bcd60e51b815260206004820152601e60248201527f416c6c436f696e5472656173757279206c6162656c206d69736d6174636800006044820152606490fd5b15614bc857565b60405162461bcd60e51b815260206004820152601460248201527f546f6b656e206c6162656c206d69736d617463680000000000000000000000006044820152606490fd5b908160209103126103d0575190565b15614c2357565b60405162461bcd60e51b815260206004820152602760248201527f496e73756666696369656e7420746f6b656e2062616c616e636520666f7220746044820152663930b739b332b960c91b6064820152608490fd5b3d15614ca2573d90614c89826111b7565b91614c976040519384611166565b82523d5f602084013e565b606090565b15614cae57565b60405162461bcd60e51b815260206004820152601a60248201527f546172676574206e6f742073706f6b65206f72206261642054520000000000006044820152606490fd5b15614cfa57565b60405162461bcd60e51b815260206004820152600a6024820152694e6f74546b6e4578656360b01b6044820152606490fd5b60ff6010541615908115614d3e575090565b90506001600160a01b03811690815f526011602052614d6060405f2054613101565b15159182614d98575b5081614d73575090565b60ff9150614d92906001600160a01b03165f52601260205260405f2090565b54161590565b5f9081526013602052604081205460ff16159250614d69565b15614db857565b60405162461bcd60e51b815260206004820152601b60248201527f416c6c436f696e5472656173206c6162656c206d69736d6174636800000000006044820152606490fd5b15614e0457565b60405162461bcd60e51b815260206004820152601260248201527f455448206c6162656c206d69736d6174636800000000000000000000000000006044820152606490fd5b15614e5057565b60405162461bcd60e51b815260206004820152601860248201527f496e73756666696369656e74204554482062616c616e636500000000000000006044820152606490fd5b15614e9c57565b60405162461bcd60e51b815260206004820152601e60248201527f546172676574206e6f742073706f6b65206f72206261642054522045544800006044820152606490fd5b6080906001600160a01b036140c45f9496959660a0845282614f2160a08601604090600d81526c2a3932b0b9bab93ca0b236b4b760991b60208201520190565b911660208501528381036040850152604090600d81526c14db585c9d10dbdb9d1c9858dd609a1b60208201520190565b15614f5857565b60405162461bcd60e51b815260206004820152600c60248201526b155b985d5d1a1bdc9a5e995960a21b6044820152606490fd5b15614f9357565b60405162461bcd60e51b815260206004820152602160248201527f496e73756666696369656e7420636f6e7472616374204554482062616c616e636044820152606560f81b6064820152608490fd5b15614fe957565b60405162461bcd60e51b815260206004820152602760248201527f526563697069656e74206e6f7420616e206f6666696369616c20536d617274436044820152661bdb9d1c9858dd60ca1b6064820152608490fd5b90926001600160a01b03608093816104099796168452166020830152604082015281606082015201906103d4565b1561507357565b60405162461bcd60e51b815260206004820152601060248201527f496e76616c696420746f6b2061646472000000000000000000000000000000006044820152606490fd5b156150bf57565b60405162461bcd60e51b815260206004820152601060248201527f496e73756666696320746f6b2062616c000000000000000000000000000000006044820152606490fd5b1561510b57565b60405162461bcd60e51b815260206004820152601560248201527f546f6b656e207472616e73666572206661696c656400000000000000000000006044820152606490fd5b936080936001600160a01b038094816138ed959a999a168852166020870152604086015260a0606086015260a08501906103d4565b1561518c57565b60405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606490fd5b156151d857565b60405162461bcd60e51b815260206004820152601260248201527f496e73756666696320746f6b656e2062616c00000000000000000000000000006044820152606490fd5b1561522457565b60405162461bcd60e51b815260206004820152601960248201527f726563697069656e74206e6f74206f6666696369616c205343000000000000006044820152606490fd5b1561527057565b60405162461bcd60e51b815260206004820152600e60248201526d105c1c1c9bdd994819985a5b195960921b6044820152606490fd5b906152b08261395d565b6152bd6040519182611166565b82815280926152ce601f199161395d565b0190602036910137565b156152df57565b60405162461bcd60e51b815260206004820152600b60248201526a16995c9bc8185b5bdd5b9d60aa1b6044820152606490fd5b1561531957565b60405162461bcd60e51b815260206004820152601660248201527f53656e646572206e6f74206f6666696369616c205343000000000000000000006044820152606490fd5b34156153a1575a60405190348252436020830152604082015232907f32ec960b23a2cb4dc6cb60b74f501d14f6f452cf55cee16c1e49bb20f76fca9360603392a3565b60405162461bcd60e51b815260206004820152601460248201527f5a65726f20455448206e6f7420616c6c6f7765640000000000000000000000006044820152606490fd5b906001600160a01b0382169182156154b3576001600160a01b038216938415615463578061544d7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259461179161545e956001600160a01b03165f52600160205260405f2090565b556040519081529081906020820190565b0390a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608490fd5b60405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b60ff601b9116019060ff82116110f457565b1561551d57565b60405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a59081d881d985b609a1b6044820152606490fd5b60418251036155f4576020915f91838201519160606040820151910151841a92601b84106155dd575b906155bf9161559960ff8616601b81149081156155d2575b50615516565b604051948594859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa1561084a575f5190565b601c9150145f615593565b92906155eb6155bf92615504565b9390915061557b565b60405162461bcd60e51b815260206004820152600f60248201527f496e76616c696420736967206c656e00000000000000000000000000000000006044820152606490fd5b6001600160a01b037f000000000000000000000000e7ae968823c79ca4022096c0887971358d97acaa1630148061572c575b15615694577f3cbf75710b830caeef2a27086e464f07e8f51b4f971a73fc77db77bf2f904f0c90565b60405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f91edecfd340c72427558f4ee9e1c9088bf57ec0639ae24d53cb812b463d4860660408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815261572660c082611166565b51902090565b507f0000000000000000000000000000000000000000000000000000000000000001461461566b565b6001600160a01b03811690811561583a57615780816001600160a01b03165f525f60205260405f2090565b548381106157ea57837fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef926157cc5f9661545e9403916001600160a01b03165f525f60205260405f2090565b556157da8160025403600255565b6040519081529081906020820190565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608490fd5b60405163a9059cbb60e01b60208201526001600160a01b03929092166024830152604480830193909352918152611197916158c5606483611166565b615c40565b906001600160a01b0382165f5260016020526158fa8160405f20906001600160a01b03165f5260205260405f2090565b54926001840161590b575b50505050565b8084106159265761591d9303916153e6565b5f808080615905565b60405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606490fd5b60ff81146159b15760ff811690601f82116159a2576040519161598f604084611166565b6020808452838101919036833783525290565b632cd44ac360e21b5f5260045ffd5b506040516104098161321b816005613139565b60ff81146159e85760ff811690601f82116159a2576040519161598f604084611166565b506040516104098161321b816006613139565b604290615a06615639565b906040519161190160f01b8352600283015260228201522090565b916104099391615a3093615d29565b919091615dd6565b9091926001600160a01b036111979481604051956323b872dd60e01b60208801521660248601521660448401526064830152606482526158c5608483611166565b15615a8057565b60405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b15615ad857565b60405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b916001600160a01b038316918215615bed577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91615bd7826001600160a01b0361545e941696615b7d881515615a79565b615bc084615b9b836001600160a01b03165f525f60205260405f2090565b54615ba882821015615ad1565b03916001600160a01b03165f525f60205260405f2090565b556001600160a01b03165f525f60205260405f2090565b8054820190556040519081529081906020820190565b60405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b906001600160a01b03615ca192165f8060405193615c5f604086611166565b602085527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602086015260208151910182855af1615c9b614c78565b91615efb565b8051908115918215615d0f575b505015615cb757565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608490fd5b615d2292506020809183010191016133b7565b5f80615cae565b9291927f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311615dad57615d815f93602095604051948594859094939260ff6060936080840197845216602083015260408201520152565b838052039060015afa1561084a575f516001600160a01b03811615615da557905f90565b505f90600190565b505050505f90600390565b60051115615dc257565b634e487b7160e01b5f52602160045260245ffd5b615ddf81615db8565b80615de75750565b615df081615db8565b60018103615e425760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015280606481015b0390fd5b615e4b81615db8565b60028103615e985760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606490fd5b80615ea4600392615db8565b14615eab57565b60405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608490fd5b91929015615f5d5750815115615f0f575090565b3b15615f185790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b825190915015615f705750805190602001fd5b60405162461bcd60e51b8152908190615e3e90600483016103f856fe37bf4c2dc9eaf59a19de8e11c7e2131727fdb996c12dff29b51f81644c4d6cd1a264697066735822122039c5ebfd92a62164a6a681f4862d90800ba37d89300bf48c379ad498f99275b364736f6c634300081d0033
Verified Source Code Full Match
Compiler: v0.8.29+commit.ab55807c
EVM: cancun
Optimization: Yes (476 runs)
IERC5267.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.0;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}
ERC20.sol 365 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
ERC20Burnable.sol 39 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
pragma solidity ^0.8.0;
import "../ERC20.sol";
import "../../../utils/Context.sol";
/**
* @dev Extension of {ERC20} that allows token holders to destroy both their own
* tokens and those that they have an allowance for, in a way that can be
* recognized off-chain (via event analysis).
*/
abstract contract ERC20Burnable is Context, ERC20 {
/**
* @dev Destroys `amount` tokens from the caller.
*
* See {ERC20-_burn}.
*/
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}
/**
* @dev Destroys `amount` tokens from `account`, deducting from the caller's
* allowance.
*
* See {ERC20-_burn} and {ERC20-allowance}.
*
* Requirements:
*
* - the caller must have allowance for ``accounts``'s tokens of at least
* `amount`.
*/
function burnFrom(address account, uint256 amount) public virtual {
_spendAllowance(account, _msgSender(), amount);
_burn(account, amount);
}
}
ERC20Permit.sol 95 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/ERC20Permit.sol)
pragma solidity ^0.8.0;
import "./IERC20Permit.sol";
import "../ERC20.sol";
import "../../../utils/cryptography/ECDSA.sol";
import "../../../utils/cryptography/EIP712.sol";
import "../../../utils/Counters.sol";
/**
* @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* _Available since v3.4._
*/
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
using Counters for Counters.Counter;
mapping(address => Counters.Counter) private _nonces;
// solhint-disable-next-line var-name-mixedcase
bytes32 private constant _PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
/**
* @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
* However, to ensure consistency with the upgradeable transpiler, we will continue
* to reserve a slot.
* @custom:oz-renamed-from _PERMIT_TYPEHASH
*/
// solhint-disable-next-line var-name-mixedcase
bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
/**
* @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
*
* It's a good idea to use the same `name` that is defined as the ERC20 token name.
*/
constructor(string memory name) EIP712(name, "1") {}
/**
* @inheritdoc IERC20Permit
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual override {
require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
bytes32 hash = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(hash, v, r, s);
require(signer == owner, "ERC20Permit: invalid signature");
_approve(owner, spender, value);
}
/**
* @inheritdoc IERC20Permit
*/
function nonces(address owner) public view virtual override returns (uint256) {
return _nonces[owner].current();
}
/**
* @inheritdoc IERC20Permit
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view override returns (bytes32) {
return _domainSeparatorV4();
}
/**
* @dev "Consume a nonce": return the current value and increment.
*
* _Available since v4.1._
*/
function _useNonce(address owner) internal virtual returns (uint256 current) {
Counters.Counter storage nonce = _nonces[owner];
current = nonce.current();
nonce.increment();
}
}
IERC20Metadata.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
Counters.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
pragma solidity ^0.8.0;
/**
* @title Counters
* @author Matt Condon (@shrugs)
* @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
* of elements in a mapping, issuing ERC721 ids, or counting request ids.
*
* Include with `using Counters for Counters.Counter;`
*/
library Counters {
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
// this feature: see https://github.com/ethereum/solidity/issues/4637
uint256 _value; // default: 0
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
unchecked {
counter._value += 1;
}
}
function decrement(Counter storage counter) internal {
uint256 value = counter._value;
require(value > 0, "Counter: decrement overflow");
unchecked {
counter._value = value - 1;
}
}
function reset(Counter storage counter) internal {
counter._value = 0;
}
}
ECDSA.sol 217 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}
EIP712.sol 142 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.8;
import "./ECDSA.sol";
import "../ShortStrings.sol";
import "../../interfaces/IERC5267.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
* separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
*
* _Available since v3.4._
*
* @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
*/
abstract contract EIP712 is IERC5267 {
using ShortStrings for *;
bytes32 private constant _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _cachedDomainSeparator;
uint256 private immutable _cachedChainId;
address private immutable _cachedThis;
bytes32 private immutable _hashedName;
bytes32 private immutable _hashedVersion;
ShortString private immutable _name;
ShortString private immutable _version;
string private _nameFallback;
string private _versionFallback;
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
_name = name.toShortStringWithFallback(_nameFallback);
_version = version.toShortStringWithFallback(_versionFallback);
_hashedName = keccak256(bytes(name));
_hashedVersion = keccak256(bytes(version));
_cachedChainId = block.chainid;
_cachedDomainSeparator = _buildDomainSeparator();
_cachedThis = address(this);
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
return _cachedDomainSeparator;
} else {
return _buildDomainSeparator();
}
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev See {EIP-5267}.
*
* _Available since v4.9._
*/
function eip712Domain()
public
view
virtual
override
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
return (
hex"0f", // 01111
_name.toStringWithFallback(_nameFallback),
_version.toStringWithFallback(_versionFallback),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
}
Math.sol 339 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
SignedMath.sol 43 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}
ShortStrings.sol 122 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)
pragma solidity ^0.8.8;
import "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
/// @solidity memory-safe-assembly
assembly {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(_FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
}
StorageSlot.sol 138 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
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:
* ```solidity
* 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`, `uint256`._
* _Available since v4.9 for `string`, `bytes`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes 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
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}
Strings.sol 85 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}
DelNorteClub.sol 296 lines
// SPDX-License-Identifier: UNLICENSED
// Copyright 2025 US Fintech LLC and DelNorte Holdings.
//
// Permission to use, copy, modify, or distribute this software is strictly prohibited
// without prior written consent from either copyright holder.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
// ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// OFFICIAL DEL NORTE NETWORK COMPONENT
// Provides immediate membership access to platform at different levels.
// Required Non US or accredited US registration to swap for DTV token. Registration available within 180 days per terms.delnorte.io .
// This token is minimally tested. Use at your own risk.
// Designed by Ken Silverman as part of his ElasticTreasury (HUB and SPOKE), PeerTreasury and Controller model.
// This deployment is for Trueviewchain Inc. a Panama entity and Del Norte El Salvador S.A a subsidiary of Del Norte Holdings, Delaware USA.
// Permission to change metadata stored on blockchain explorers granted to Del Norte Holdings, DE only.
// Compilation help from Maleeha Naveed. Deployed by Maleeha Naveed. May 5th, 2025
pragma solidity 0.8.29;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import "./ElasticTreasuryHub.sol";
import "./PeerTreasury.sol";
/// @author Ken Silverman
/// @notice DelNorte Club Token is the functional token of DelNorte El Salvador S.A. and all nations of the Del Norte Network.
contract DelNorteClubToken is ERC20, ERC20Permit, ERC20Burnable, ElasticTreasuryHub, PeerTreasury {
event RegistrarEvent(address indexed registrar, uint256 blockNumber, string action, uint256 numUsers);
struct WhitelistEntry {
address user;
string note;
bool isUS;
}
// 825,000,000 DTVC all tokens less DTVX distributed to founders and early purchasers.
// 1,000,000,000 DTVC total supply. (plus one for distinguishing from DTV for bytecode verification)
//175,000,000 remaining to be burned when DTVX is provided.
uint256 public constant MAX_SUPPLY = 1_000_000_001 * 10**18;
bool public gatingActive = false;
mapping(address => string) public whitelistedUsers;
mapping(address => bool) public isUserRestricted;
mapping(address => bool) public whitelistedUsersIsUSA;
address[] public whitelistedUserList;
modifier onlyTokenExecutives() {
require(IController(controller).isOfficialEntity("TokenAdmin",msg.sender),"NotTknExec");
_;
}
// if masterHub is 00000000000 it means SELF (pass it up the line)
// address baseTreasuryAddress, address intialAdmin
/// @author Ken Silverman
/// @notice DelNorte Club Token is the membership club token for DelNorte El Salvador S.A.
/// @dev This token is not functional but does provide immediate access tot the dev.delnorte.io development platform
/// @dev To monitor and help mold the state of DelNorte development progress in real time.
/// @dev Regsitration under US securities law REG S. and REG D. 506C required for swap to DTV.
constructor(address _controller, address initialAdmin)
ERC20("DelNorte Club Token", "DTVC")
ERC20Permit("DelNorte Club Token")
ElasticTreasuryHub(_controller, "DelNorteClubToken")
PeerTreasury(_controller)
{
require(_controller != address(0), "ctrolr is zero");
//bool areLords = IController(_controller).isOfficialEntity("TreasuryAdmin",initialAdmin);
//require(areLords, "First token admin (multi-sig execs) must be initial Controller TreasAdmin.");
_mint(address(this), MAX_SUPPLY);
// Can we as admins be registrar? Yes if we provide KYC
// Controller must mark this SC as SmartContract already
// These calls assume controller is of type IController or castable
// AT LEAST ONe REGISTRAR MUST BE ADDED By a CONTROLLER TREASURYADMIN or nobody wil be able to be whitelisted!
// IController(controller).addOfficialEntity("Registrar", initialAdmin,"ExecGrp", "acting Registrar");
IController(controller).addOfficialEntity("TokenAdmin", initialAdmin,"ExecGrp", "initTknAdmin");
}
// no LP or other retrieval to any non-registered user (except by executive manual add to ReleaseManager).
// USACCRED or Non-US can register. but only NON-US reg can receive (except via ReleaseManager)
// LP considerations: in the common flow:
//
// For user selling tokens (input):
// In transferFrom(sender, recipient, amount):
// sender = user address
// recipient = pool address <== not whitelisted not officialEntity smart contract
// msg.sender = router contract <== Uniswap V2, V3, sushiSwap addresses etc ... are whitelisted officialEntity smart contracts
// so anyone can send. (cannot have unless whitelisted or manually put into trelease manager though)
//
// For user buying tokens (output):
// In transfer(recipient, amount):
// msg.sender = pool address <== not whitelisted not officialEntity smart contract
// recipient = user address <== is receiver nonUS? yes! all good, no!, Will fail from any LP.
function isRestricted(address sender, address receiver) public view returns (bool) {
// Did sender have an address in an unauthorized wrapped token or otherwise become blacklisted for life?
// Not today! BANNED! blacklisted = whitelisted + restricted sender does NOT need to be whitelisted, but cannot be restricted
// That's becaseu a sender cant get it in the first place unless they are whitelisted anyway.
if (bytes(whitelistedUsers[sender]).length > 0 && isUserRestricted[sender]) {
return true;
}
if (!gatingActive || isUserWhitelistedNonUSA(receiver)) {
return false;
}
// What if user is US? How do we guarantee early US adopters get their exact owed tokens but new US users cannot buy or receive?
// and old US users cannot buy or sell anymore then their old SAFT agreement specifies?
// WE manually add all early purchasers to the ReleaseManager which itself is whitelisted as an official smart contract
// Therefore, the US user must be added to the ReleaseManager manaually or via a swap from another token like DTVC
// The line above prevents the US user from receiving except by a whitelisted contract (ReleaseManager)
// The next line allows ANY official smart contract in our system to permit receiving of DTV
// Our release manager is one such contract an the LP router is another.
// This is the only way to prevent US users from buying or receiving any more tokens then the ReleaseManager will grant them.
// They cannot sell accept to the LP. The LP sell is allowed becasue transferFrom msg.sender is the whitelisted router contract.
// (see next line granting permission as the fourth officialEntity)
// Any holder can sell to any LP on any official router. If they hold they must be allowed to sell under:
// REG S or REG D (we have proof they held for more than 12 months).
// All holders/receivers are already registered, held for 12 months or are confirmed non-US residents.
return !(IController(controller).isOfficialQuadrupleEntityFast(KECCAK_TREASURY_ADMIN, sender,KECCAK_SMART_CONTRACT, sender,KECCAK_SMART_CONTRACT, receiver,KECCAK_SMART_CONTRACT,msg.sender,false));
}
function isUnrestrictedWhitelistedUser(address user) public view returns (bool) {
return bytes(whitelistedUsers[user]).length > 0 && !isUserRestricted[user];
}
/// @notice Returns true if `user` is whitelisted and not flagged US
// must be TRUE for DTVC swaps. See TokenSaleAndSwap.sol
function isUserWhitelistedNonUSA(address user) public view returns (bool) {
// must have a registration note + US‑flag == 0
return !gatingActive || (bytes(whitelistedUsers[user]).length > 0 && whitelistedUsersIsUSA[user] == false && !isUserRestricted[user]);
}
function getUserRegistrationStatus(address user, bool notGatedOverride) public view returns (uint8) {
if (notGatedOverride && !gatingActive) {
return 3; // don't care if registered or not
}
if (bytes(whitelistedUsers[user]).length == 0) {
return 0; // Not registered
}
if (whitelistedUsersIsUSA[user]) {
return 1; // US
}
return 2; // Non-US
}
function transfer(address recipient, uint256 amount) public override returns (bool) {
require(!isRestricted(msg.sender, recipient), "restricted");
return super.transfer(recipient, amount);
}
function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
require(!isRestricted(sender, recipient), "restricted");
return super.transferFrom(sender, recipient, amount);
}
function setGating(bool active) external onlyTokenExecutives {
gatingActive = active;
}
function getAllWhitelistedUsers() external view returns (address[] memory) {
return whitelistedUserList;
}
// NOT just for sales but for regular users who are registered
function addWhitelistedUser(address user, string memory registrationNote, bool isUS) external {
require(IController(controller).isOfficialEntity("Registrar", msg.sender), "Not Regstrar");
if (bytes(whitelistedUsers[user]).length == 0) {
whitelistedUserList.push(user);
}
whitelistedUsers[user] = registrationNote;
whitelistedUsersIsUSA[user] = isUS; // added: default to non‑US
emit RegistrarEvent(msg.sender, block.number, "Added/updated User", 1);
}
function addWhitelistedUsers(WhitelistEntry[] calldata entries) external {
require(IController(controller).isOfficialEntity("Registrar", msg.sender), "Not Regstrar");
uint256 numUsers = 0;
for (uint256 i = 0; i < entries.length; ++i) {
address user = entries[i].user;
if (bytes(whitelistedUsers[user]).length == 0) {
whitelistedUserList.push(user);
numUsers++;
}
whitelistedUsers[user] = entries[i].note;
whitelistedUsersIsUSA[user] = entries[i].isUS; // added: default to non‑US
}
emit RegistrarEvent(msg.sender, block.number, "Added Users", numUsers);
}
// Only Registrar can delete a whitelisted user, and ONLY if a mistake was made,
// we dont want to delete a user for accountability we just want to restrict them if necassary for accountability
// can remove this method if needed
//function deleteWhitelistedUser(address user) external {
// require(IController(controller).isOfficialEntity("Registrar",msg.sender));
// delete whitelistedUsers[user];
// emit RegistrarEvent(msg.sender, block.number, "Deleted a User", 1);
//}
function restrictWhitelistedUsers(address[] calldata users) external {
require(IController(controller).isOfficialEntity("Registrar", msg.sender), "Not Regstrar");
uint256 numUsers = 0;
for (uint256 i = 0; i < users.length; ++i) {
address user = users[i];
isUserRestricted[user] = true;
numUsers++;
}
emit RegistrarEvent(msg.sender, block.number, "Users Rstrcted", numUsers);
}
function reactivateWhitelistedUsers(address[] calldata users) external {
require(IController(controller).isOfficialEntity("Registrar", msg.sender), "Not Regstrar");
uint256 numUsers = 0;
for (uint256 i = 0; i < users.length; ++i) {
address user = users[i];
if (bytes(whitelistedUsers[user]).length != 0 && isUserRestricted[user]) {
isUserRestricted[user] = false;
numUsers++;
}
}
emit RegistrarEvent(msg.sender, block.number, "Users Rctivted", numUsers);
}
function getActiveWhitelistedUsers() external view returns (address[] memory) {
uint256 activeCount = 0;
for (uint256 i = 0; i < whitelistedUserList.length; ++i) {
if (!isUserRestricted[whitelistedUserList[i]]) {
activeCount++;
}
}
address[] memory activeUsers = new address[](activeCount);
uint256 j = 0;
for (uint256 i = 0; i < whitelistedUserList.length; ++i) {
address user = whitelistedUserList[i];
if (!isUserRestricted[user]) {
activeUsers[j++] = user;
}
}
return activeUsers;
}
/**
* @notice Allows a user to self-register (whitelist themselves) using an off-chain signature
* provided by an approved Registrar. The registrar signs a hashed message consisting of:
*
* keccak256(abi.encodePacked(userAddress + "_" + thisTokencontractAddress))
*
* - The registration note (string) passed to this function is NOT part of the signed message.
* It is for reference and display only and can be spoofed. All actual verification (e.g., KYC,
* ISO country code, etc.) is done off-chain by registrar, details stored off-chain by address.
*
* - The registrar's address must be registered in the controller as an official entity
* of type "Registrar". registrarSignature = regKey
* - The user (msg.sender) pays the gas and must be the subject of the signed message.
*/
// @rawMessage = user + thisContract + "0"/"1" 1 = USACCRDTD (user and thiscontract are fixed Length 20 bytes, no spearator needed)
// US accredited are NOT allowed for PDTV swaps, that is why we store this flag.
//registrationNote = "isUs
function whitelistUserWithRegKey(address registrarAddress, bool isUS, bytes calldata registrarSignedMessage, string calldata registrationNote) external {
require(bytes(whitelistedUsers[msg.sender]).length == 0, "User already registered");
// build raw message = user + thisContract + "0"/"1" (user and thiscontract are fixed Length 20 bytes, no spearator needed)
bytes memory rawMessage = abi.encodePacked(
msg.sender,
address(this),
isUS ? "1" : "0"
);
bytes32 messageHash = keccak256(rawMessage);
// Ethereum signed message format
bytes32 ethSignedMessageHash = keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash)
);
// Recover signer
address recoveredSigner = recoverSigner(ethSignedMessageHash, registrarSignedMessage);
require(recoveredSigner == registrarAddress, "Sig not from registrar");
require(IController(controller).isOfficialEntity("Registrar", registrarAddress),"invalid registrar");
// Whitelist the user
whitelistedUsers[msg.sender] = registrationNote;
whitelistedUserList.push(msg.sender);
whitelistedUsersIsUSA[msg.sender] = isUS;
emit RegistrarEvent(registrarAddress, block.number, "User Self-Regstd", 1);
}
// Internal helper to recover the signer from signature
function recoverSigner(bytes32 hash, bytes memory signature) internal pure returns (address) {
require(signature.length == 65, "Invalid sig len");
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 32))
s := mload(add(signature, 64))
v := byte(0, mload(add(signature, 96)))
}
if (v < 27) {
v += 27;
}
require(v == 27 || v == 28, "Invalid v val");
return ecrecover(hash, v, r, s);
}
}
ElasticTreasuryHub.sol 378 lines
// SPDX-License-Identifier: UNLICENSED
// Copyright 2025 US Fintech LLC and DelNorte Holdings.
//
// Permission to use, copy, modify, or distribute this software is strictly prohibited
// without prior written consent from either copyright holder.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
// ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// OFFICIAL DEL NORTE NETWORK COMPONENT
// Designed and coded by: Ken Silverman
// Compiled and deployed by Maleeha Naveed on behalf of Del Norte
pragma solidity 0.8.29;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/IController.sol"; // // ⇐ for method signatures only to allow casting during compilation.
import "../interfaces/IElasticTreasurySpoke.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/// @title ElasticTreasuryHub
/// @notice A contract for managing a treasury of ERC20 tokens and ETH
/// @dev This contract is used to manage a treasury of ERC20 tokens and ETH
/// @author Ken Silverman
abstract contract ElasticTreasuryHub {
using SafeERC20 for IERC20;
address public controller;
bytes32 public constant KECCAK_TREASURY_ADMIN = keccak256(bytes("TreasuryAdmin"));
bytes32 public constant KECCAK_SMART_CONTRACT = keccak256(bytes("SmartContract"));
uint256 constant DUST = 0.01 ether;
event HubTreasuryEvent(address adminCaller, string msg, bool success,address smartContractTarget, uint256 amount, address tokenOrZero);
event HubExecutiveTokensWithdrawalEvent(address adminCaller, address receiver, uint256 amt, string note, address tokenSC);
event HubExecutiveEthWithdrawalEvent(address adminCaller, address receiver, uint256 amt, string note);
event HubExternalReceive(address indexed sender,address indexed origin,uint256 amount,uint256 blockNumber,uint256 gasLeft);
struct SingleCoinTreasuryState {
string label;
uint256 totalTransferred;
uint256 totalReclaimed;
uint256 totalTimesTransferred;
uint256 totalTimesReclaimed;
uint256[] failedReclaimAttemptAmounts;
uint256[] bouncedReclaimAmounts;
}
struct AllCoinTreasury {
mapping(address => SingleCoinTreasuryState) tokenTreasury;
address[] tokenTreasuryKeys;
SingleCoinTreasuryState ethTreasury;
string label;
}
mapping(address => AllCoinTreasury) private treasury;
address[] private treasuryKeys;
// elastic treasury events with gas that fail without reversion or require
// controller reference is NOW stored in Recoverable
// if somehow anyone managed a call to a spoke reclaim, (in theory they can't) but if they did, then
// that SC would have the authority to call the reclaim here as a SC exec.
// therefore we require BOTH SC as spoke AND tx.origin to be Treas on reclaim
modifier onlyTreas() {
require(IController(controller).isOfficialEntityFast(KECCAK_TREASURY_ADMIN,msg.sender), "Unauth access");
_; // run the code block referencing this modifier
}
// masterHub can no longer exist because BaseTreasury is a SINGLE entity, not inherited anymore.
// in other words, the ENTIRE Del Norte system uses ONE Base Treasury instance as CONTROL PANEL.
// Controller already has an initial admin, so do not add any more here.
// Admin can call addEntity to add more. (controller has option to receive more in init() but not necessary.
constructor(address _controllerAddress, string memory contractName) {
controller = _controllerAddress;
IController(_controllerAddress).init(address(this), contractName);
}
// EMERGENCY withdraw for admin controlled DIRECT withdrawals to a PERSON (not a smart contract)
// this can ONLY happen for example, when the company wants to send some USDC or ETH from the Sales contract
// to an EXECUTIVE of the company for the purposes of exchanging to USDC, as an example.
// Such USDC can be spent on a capital expense or manually passed into the vesting pool.
// ONLY HUBS can do this. For example, only contracts GENERATING or expected to RECEIVE
// ETH or tokens from an external event (not from a HUB) should be declared as HUBS.
// EXCEPTIONS: send tokens to a launchpad or other noted entity can be a SC.
function executiveWithdrawETH(address personAddress, uint256 amt, string memory note) external onlyTreas {
require(address(this).balance >= amt, "Insufficient ETH balance");
(bool success, ) = payable(personAddress).call{value: amt}("");
require(success, "ETH transfer failed");
emit HubExecutiveEthWithdrawalEvent(msg.sender, personAddress, amt, note);
}
function executiveWithdrawTokens(address personAddress, uint256 amt, address tokenSCAddress, string memory note) external onlyTreas {
require(tokenSCAddress != address(0), "Invalid tok addr");
IERC20 token = IERC20(tokenSCAddress);
require(token.balanceOf(address(this)) >= amt, "Insuffic tok bal");
bool success = token.transfer(personAddress, amt);
require(success, "Token transfer failed");
emit HubExecutiveTokensWithdrawalEvent(msg.sender, personAddress, amt, note, tokenSCAddress);
}
/// @param SCAddress is the spoke address transfer to
/// @param tokenAddress is the transfer from treasury token address
/// @param allCoinLabel is the label of the spoke (contractName preferred)
/// @param tokenLabel is the label of the token (symbol of token prefferred)
/// @param amount is the amount to transfer
// NEW: Add this import at the top of your contract file
function treasuryTransfer(address SCAddress, string memory allCoinLabel, address tokenAddress, string memory tokenLabel,uint256 amount) external onlyTreas returns (bool) {
// NEW: Basic validation checks with specific error messages
require(SCAddress != address(0), "SCAddress cannot be zero address");
require(tokenAddress != address(0), "TokenAddress cannot be zero address");
require(amount > 0, "Amount must be greater than zero");
require(isContract(SCAddress), "Invalid contract: SCAddress is not a contract");
require(bytes(tokenLabel).length > 0, "Token label cannot be empty");
// Add to smartContractAdmins array if not already included
require(IController(controller).isOfficialEntity("SmartContract",SCAddress), "Spoke not yet part of SC network.");
// SELF is added as official SC in parent constructor - and same for spoke
AllCoinTreasury storage coinTreasury = treasury[SCAddress];
if (bytes(coinTreasury.label).length != 0) {
require(keccak256(bytes(coinTreasury.label)) == keccak256(bytes(allCoinLabel)),"AllCoinTreasury label mismatch");
}
else {
coinTreasury.label = allCoinLabel;
treasuryKeys.push(SCAddress); // NEW: add new spoke key
}
SingleCoinTreasuryState storage tokenState = coinTreasury.tokenTreasury[tokenAddress];
// If this is a new token (label is empty), add it to the keys array
if (bytes(tokenState.label).length == 0) {
// This is a new token, add it to the keys array
coinTreasury.tokenTreasuryKeys.push(tokenAddress);
// Also set the label if provided
if (bytes(tokenLabel).length != 0) {
tokenState.label = tokenLabel;
}
}
else if (bytes(tokenLabel).length != 0) {
// Token exists, just verify the label matches
require(keccak256(bytes(tokenState.label)) == keccak256(bytes(tokenLabel)), "Token label mismatch");
}
// NEW: Check token balance before attempting transfer
uint256 contractBalance = IERC20(tokenAddress).balanceOf(address(this));
require(contractBalance >= amount, "Insufficient token balance for transfer");
// NEW: Update state before transfer (prevent reentrancy)
tokenState.totalTransferred += amount;
tokenState.totalTimesTransferred++;
//bytes32 labelHash = keccak256(bytes(tokenLabel));
//if (labelHash == USDT_LABEL_UPPER || labelHash == USDT_LABEL_LOWER) {
// (bool success, ) = tokenAddress.call(abi.encodeWithSelector(0xa9059cbb, SCAddress, amount));
// require(success, "USDT transfer failed");
//}
//else {
// For other tokens, use regular transfer with boolean check
IERC20(tokenAddress).safeTransfer(SCAddress, amount);
//}
// Use low-level call to treasuryReceive - most gas efficient
(bool success,) = SCAddress.call(abi.encodeWithSelector(bytes4(keccak256("treasuryReceive(address,uint256)")),SCAddress,amount));
// Check success and provide meaningful error if possible
require(success, "Target not spoke or bad TR");
emit HubTreasuryEvent(msg.sender, "Amount transferred to spoke", true, SCAddress, amount, tokenAddress);
return true;
}
/// @param SCAddress is the spoke address transfer to
/// @param allCoinLabel is the label of the spoke (contractName preferred)
/// @param ethLabel is the label for ETH ("ETH")
/// @param amount is the amount to transfer
function treasuryTransferETH(address payable SCAddress, string memory allCoinLabel, string memory ethLabel, uint256 amount) external onlyTreas returns (bool) {
// NEW: Basic validation checks with specific error messages
require(SCAddress != address(0), "SCAddress cannot be zero address");
require(amount > 0, "Amount must be greater than zero");
require(isContract(SCAddress), "Invalid contract: SCAddress is not a contract");
// NEW: Check if spoke is registered before proceeding
require(IController(controller).isOfficialEntity("SmartContract", SCAddress), "Spoke not yet part of SC network.");
AllCoinTreasury storage coinTreasury = treasury[SCAddress];
if (bytes(coinTreasury.label).length != 0) {
require(keccak256(bytes(coinTreasury.label)) == keccak256(bytes(allCoinLabel)),"AllCoinTreas label mismatch");
}
else {
coinTreasury.label = allCoinLabel;
// NEW: Add key if it's a new treasury
treasuryKeys.push(SCAddress);
}
SingleCoinTreasuryState storage ethState = coinTreasury.ethTreasury;
if (bytes(ethState.label).length != 0 && bytes(ethLabel).length != 0) {
require(keccak256(bytes(ethState.label)) == keccak256(bytes(ethLabel)),"ETH label mismatch");
}
else if (bytes(ethState.label).length == 0 && bytes(ethLabel).length != 0) {
ethState.label = ethLabel;
}
// Check ETH balance
require(address(this).balance >= amount, "Insufficient ETH balance");
// NEW: Update state before transfer (prevent reentrancy)
ethState.totalTransferred += amount;
ethState.totalTimesTransferred++;
// NEW: Use try-catch for error handling
(bool success,) = SCAddress.call{value: amount}(abi.encodeWithSelector(bytes4(keccak256("treasuryReceiveETH()"))));
require(success, "Target not spoke or bad TR ETH");
// ETH transfer succeeded
//} catch Error(string memory reason) {
// // Revert state changes on failure
// ethState.totalTransferred -= amount;
// ethState.totalTimesTransferred--;
// emit HubTreasuryEvent(msg.sender, reason, false, SCAddress, amount, address(0));
// revert(string(abi.encodePacked("ETH transfer failed: ", reason)));
//} catch {
// // Revert state changes on unknown failure
// ethState.totalTransferred -= amount;
// ethState.totalTimesTransferred--;
// emit HubTreasuryEvent(msg.sender, "Unknown ETH transfer error", false, SCAddress, amount, address(0));
// revert("ETH transfer failed: unknown error");
//}
emit HubTreasuryEvent(msg.sender, "ETH transferred to SC", true, SCAddress, amount, address(0));
return true;
}
// ETH reclaim receiver
function treasuryReceiveReclaimedETH() external payable returns (bool) {
AllCoinTreasury storage coinTreasury = treasury[msg.sender];
require(bytes(coinTreasury.label).length != 0, "Treasury entry does not exist for caller as SPOKE");
require(IController(controller).isOfficialDoubleEntity("SmartContract",msg.sender,"TreasuryAdmin",tx.origin,true),
"Originator must be treas and sender must be an official smart contract.");
SingleCoinTreasuryState storage ethState = coinTreasury.ethTreasury;
ethState.totalReclaimed += msg.value;
ethState.totalTimesReclaimed++;
emit HubTreasuryEvent(msg.sender, "ETH reclaimed", true, msg.sender,msg.value,address(0));
return true;
}
// Token reclaim notifier
function treasuryReceiveReclaimedTokens(address tokenAddress, uint256 amt) external returns (bool) {
AllCoinTreasury storage coinTreasury = treasury[msg.sender];
require(bytes(coinTreasury.label).length != 0, "Treasury entry does not exist");
require(IController(controller).isOfficialDoubleEntity("SmartContract",msg.sender,
"TreasuryAdmin",tx.origin,true),
"Originator must be treas and sender must be an official smart contract.");
SingleCoinTreasuryState storage tokenState = coinTreasury.tokenTreasury[tokenAddress];
tokenState.totalReclaimed += amt;
tokenState.totalTimesReclaimed++;
emit HubTreasuryEvent(msg.sender, "Token reclaimed", true, address(this),amt,tokenAddress);
return true;
}
function treasuryReclaimRequest(address SCAddress, address tokenAddress, uint256 amount) external onlyTreas returns (bool) {
uint256 availableToReclaim = treasury[SCAddress].tokenTreasury[tokenAddress].totalTransferred - treasury[SCAddress].tokenTreasury[tokenAddress].totalReclaimed;
if (amount > treasury[SCAddress].tokenTreasury[tokenAddress].totalTransferred) {
treasury[SCAddress].tokenTreasury[tokenAddress].failedReclaimAttemptAmounts.push(amount);
emit HubTreasuryEvent(msg.sender, "Reclaim request exceeds total transferred", false, address(this),amount,address(0));
return false;
}
if (amount > availableToReclaim) {
treasury[SCAddress].tokenTreasury[tokenAddress].bouncedReclaimAmounts.push(amount);
amount = availableToReclaim - DUST;
emit HubTreasuryEvent(msg.sender, "Reclaim request bounce, too high", false, address(this),amount,address(0));
return false;
}
// if this fails, whole thing will revert, but the idea is if we passed
// the above checks, than the corresponding state in the spoke should match.
// In other words a revert should never happen if we get to this point.
// if it does there is a state mismatch that the web manager will have to
// warn about for manual reconciliation.
// eth logs on chain should group msg.sender so when reclaim succeeds the message
// will follow this event message herein 'request submitted'. If however,
// the transfer fails due to rejection at the spoke,
// the below log entry will never be entered.
IElasticTreasurySpoke(SCAddress).treasuryReclaim(tokenAddress, amount);
treasury[SCAddress].tokenTreasury[tokenAddress].totalReclaimed += amount;
treasury[SCAddress].tokenTreasury[tokenAddress].totalTimesReclaimed++;
emit HubTreasuryEvent(msg.sender, "Reclaim TOKEN request submitted to SPOKE", true,SCAddress,amount,tokenAddress);
return true;
}
function treasuryReclaimRequestETH(address payable SCAddress, uint256 amount) external onlyTreas returns (bool) {
uint256 availableToReclaim = treasury[SCAddress].ethTreasury.totalTransferred - treasury[SCAddress].ethTreasury.totalReclaimed;
if (amount > treasury[SCAddress].ethTreasury.totalTransferred) {
treasury[SCAddress].ethTreasury.failedReclaimAttemptAmounts.push(amount);
emit HubTreasuryEvent(msg.sender, "ETH Reclaim exceeds total transferred", false,SCAddress,amount,address(0));
return false;
}
if (amount > availableToReclaim) {
treasury[SCAddress].ethTreasury.bouncedReclaimAmounts.push(amount);
amount = availableToReclaim - DUST;
emit HubTreasuryEvent(msg.sender, "ETH Reclaim request bounce, too high", false,SCAddress,amount,address(0));
return false;
}
IElasticTreasurySpoke(SCAddress).treasuryReclaimETH(amount);
treasury[SCAddress].ethTreasury.totalReclaimed += amount;
treasury[SCAddress].ethTreasury.totalTimesReclaimed++;
emit HubTreasuryEvent(msg.sender, "ETH reclaim request submitted", true,SCAddress,amount,address(0));
return true;
}
// PURCHASERS on the SALES contract or any other contract which acts as its own HUB
// should not accidently get ETH sent to this contract unless the child implements a receive()
// or fallback or other payable - NOT a good idea (except buy and other planned custom methods) !
// There should be no way to receive ETH (or tokens if it were possible) outside of SALES or other means
// Sales contract has its own payable for example after a purchase is verified by whitelist etc ...
// function customReceiveETH internal virtual payable { do NOT implement
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
// VIEW GETTERS
struct TokenTreasuryView {
address token;
SingleCoinTreasuryState state;
}
struct SpokeTreasuryView {
address spoke;
string label;
SingleCoinTreasuryState ethState;
TokenTreasuryView[] tokenStates;
}
function getAllSpokeTreasuries() external view returns (SpokeTreasuryView[] memory) {
uint256 len = treasuryKeys.length;
SpokeTreasuryView[] memory result = new SpokeTreasuryView[](len);
for (uint256 i = 0; i < len; i++) {
address spoke = treasuryKeys[i];
AllCoinTreasury storage coinTreasury = treasury[spoke];
uint256 tokenCount = coinTreasury.tokenTreasuryKeys.length;
TokenTreasuryView[] memory tokenViews = new TokenTreasuryView[](tokenCount);
for (uint256 j = 0; j < tokenCount; j++) {
address token = coinTreasury.tokenTreasuryKeys[j];
SingleCoinTreasuryState storage tokenState = coinTreasury.tokenTreasury[token];
tokenViews[j] = TokenTreasuryView({
token: token,
state: tokenState
});
}
result[i] = SpokeTreasuryView({
spoke: spoke,
label: coinTreasury.label,
ethState: coinTreasury.ethTreasury,
tokenStates: tokenViews
});
}
return result;
}
function getAllCoinSpokeTreasury(address spokeAddress) external view returns (TokenTreasuryView[] memory) {
AllCoinTreasury storage coinTreasury = treasury[spokeAddress];
uint256 tokenCount = coinTreasury.tokenTreasuryKeys.length;
TokenTreasuryView[] memory tokenViews = new TokenTreasuryView[](tokenCount);
for (uint256 i = 0; i < tokenCount; i++) {
address token = coinTreasury.tokenTreasuryKeys[i];
SingleCoinTreasuryState storage tokenState = coinTreasury.tokenTreasury[token];
tokenViews[i] = TokenTreasuryView({
token: token,
state: tokenState
});
}
return tokenViews;
}
// only HUBS can receive ETH from some remote source (e.g. launchpad escrow contract)
receive() external payable {
require(msg.value > 0, "Zero ETH not allowed");
emit HubExternalReceive(
msg.sender, // the immediate caller (e.g. launchpad escrow contract)
tx.origin, // the original EOA that started the transaction (e.g. the user)
msg.value,
block.number,
gasleft()
);
}
}
ElasticTreasurySpoke.sol 88 lines
// SPDX-License-Identifier: Copyright 2025
// OFFICIAL DEL NORTE NETWORK COMPONENT
// Designed and coded by: Ken Silverman
// Compiled by: Maleeha Naveed May 5th, 2025
pragma solidity 0.8.29;
import "../interfaces/IController.sol";
import "../interfaces/IElasticTreasuryHub.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./Recoverable.sol";
/// @title ElasticTreasurySpoke
/// @notice Spoke contract for ElasticTreasuryHub
/// @author Ken Silverman
abstract contract ElasticTreasurySpoke {
using SafeERC20 for IERC20;
address public immutable controller;
event SpokeReceivedEth(address sender, uint256 amount);
event SpokeReceivedTokens(address sender, uint256 amount, address tokenSCAddress);
event SpokeReclaimEthEvent(address origin, address hubContract, string msg, uint256 amount);
event SpokeReclaimTokensEvent(address origin, address hubContract, address tokenAddress, string msg, uint256 amount);
address public immutable hubTokenAddress;
struct SpokeTreasuryEntry {
uint256 totalReceived;
uint256 totalReclaimed;
}
mapping(address => SpokeTreasuryEntry) public spokeTreasury; // tokenAddress => SpokeTreasuryEntry
SpokeTreasuryEntry public spokeTreasuryETH; // ETH treasury entry explicitly
// Controller already has an initial admin, so do not add any more here.
constructor(address _controller, address _hubTokenAddress, string memory contractName) {
controller = _controller;
IController(controller).init(address(this), contractName);
hubTokenAddress = _hubTokenAddress;
}
function treasuryReceive(address tokenAddress, uint256 amount) external {
require(msg.sender == hubTokenAddress, "sender must be HUB");
spokeTreasury[tokenAddress].totalReceived += amount;
// explicitly tracks ERC20 tokens received from treasuryTransfer
}
// ETH transfers automatically trigger receive() only if no data is sent.
// Here, we're explicitly defining a method (treasuryReceiveETH)
// that's explicitly called by the hub to transfer ETH and record this separately
// treasury-related ETH separately from ONE CALL to THIS METHOD. NO PRIOR TRANSFER!
function treasuryReceiveETH() external payable { // ⇐ MUST DIRECTLY RECEIVE NOW
require(msg.sender == hubTokenAddress,"sender must be HUB");
// confirm amount actually equals msg.value
spokeTreasuryETH.totalReceived += msg.value;
emit SpokeReceivedEth(msg.sender,msg.value);
}
// DO NOT USE ⇒ receive() external payable { // NO NO NO NO receive method! Do NOT allow incoming ETH
// emit SpokeReceivedEth(msg.sender,msg.value); // except by treasRcvETH
// }
// ANY address that this contract has deployed as its HUB is 100% trusted.
// because caller must be hub and HUB is only accessible by TREAS.
function treasuryReclaim(address tokenAddress, uint256 amount) external returns (bool) {
require(spokeTreasury[tokenAddress].totalReceived >= spokeTreasury[tokenAddress].totalReclaimed + amount, "Low balance to reclaim on");
require(hubTokenAddress == msg.sender);
spokeTreasury[tokenAddress].totalReclaimed += amount;
IERC20(tokenAddress).safeTransfer(msg.sender, amount);
IElasticTreasuryHub(msg.sender).treasuryReceiveReclaimedTokens(tokenAddress,amount);
emit SpokeReclaimTokensEvent(tx.origin, msg.sender, tokenAddress, "Spoke sent Reclaim Tokens back to HUB", amount);
return true;
}
// msg.sender must be the HUB contract address here. For safety, verify that.
// no officialSmartContract entity is required here, HUB is 100% trusted as caller.
// because HUB can only be called by TREAS.
function treasuryReclaimETH(uint256 amount) external {
// avoid underflow and overflow
require(spokeTreasuryETH.totalReceived >= spokeTreasuryETH.totalReclaimed + amount, "Low balance to reclaim on");
require(hubTokenAddress == msg.sender);
spokeTreasuryETH.totalReclaimed += amount;
IElasticTreasuryHub(payable(msg.sender)).treasuryReceiveReclaimedETH{value : amount}(); // ⇐MAKES TRANSFER
emit SpokeReclaimEthEvent(tx.origin, msg.sender, "Spoke sent Reclaim ETH back to HUB", amount);
}
}
PeerTreasury.sol 155 lines
// SPDX-License-Identifier: Unlicensed
// Copyright 2025 US Fintech LLC and Del Norte Holdings.
//
// Permission to use, copy, modify, or distribute this software is strictly prohibited
// without prior written consent from either copyright holders.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
// ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// OFFICIAL DEL NORTE NETWORK COMPONENT
// PeerTreasury is a contract that allows for the transfer of ETH and ERC20 tokens between different contracts.
// Designed by Ken Silverman as part of his ElasticTreasury (HUB and SPOKE), PeerTreasury and Controller model.
// This deployment is for Trueviewchain Inc. a Panama entity and Del Norte El Salvador S.A a subsidiary of Del Norte Holdings, Delaware USA.
// Permission to change metadata stored on blockchain explorers and elsewhere granted to Del Norte Holdings, DE only.
// Compilation help from Maleeha Naveed. Deployed by Maleeha Naveed on behalf of Del Norte. May 5th, 2025
pragma solidity 0.8.29;
import "../interfaces/IController.sol";
import "../interfaces/IElasticTreasuryHub.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./ElasticTreasurySpoke.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// Compiled by Maleeha Naveed. May 5th, 2025
// PeerTreasury is a contract that allows for the transfer of ETH and ERC20 tokens between different contracts.
/// @author Ken Silverman
contract PeerTreasury {
using SafeERC20 for IERC20;
event PeerTreasuryReceivedETH(address indexed sender, uint256 amount);
event PeerTreasuryReceivedTokens(address indexed sender, address indexed token, uint256 amount);
event PeerTreasuryTransferredETH(address indexed to, uint256 amount);
event PeerTreasuryTransferredTokens(address indexed to, address indexed token, uint256 amount);
struct TreasuryEntry {
uint256 totalReceived;
uint256 totalWithdrawn;
}
address public immutable peerTreasuryController;
mapping(address => TreasuryEntry) private tokenPeerTreasury;
address[] internal knownTokens; // <-- Needed for enumeration
TreasuryEntry private ethPeerTreasury;
modifier onlyTreasuryAdmin() {
require(
IController(peerTreasuryController).isOfficialDoubleEntity("TreasuryAdmin", msg.sender, "SmartContract", msg.sender, false),
"Unauthorized"
);
_;
}
constructor(address _controller) {
peerTreasuryController = _controller;
}
/// @notice Receive ETH from any source and track it
function peerTreasuryReceiveETH() virtual external payable {
require(msg.value > 0, "No ETH sent");
require(IController(peerTreasuryController).isOfficialEntity("SmartContract", msg.sender), "Sender is not an official SmartContract");
ethPeerTreasury.totalReceived += msg.value;
emit PeerTreasuryReceivedETH(msg.sender, msg.value);
}
/// @notice Transfer ETH to another peer contract (OfficialEntity SmartContract)
function peerTreasuryTransferETH(address payable to, uint256 amount) virtual public onlyTreasuryAdmin {
require(address(this).balance >= amount, "Insufficient contract ETH balance");
require(IController(peerTreasuryController).isOfficialEntity("SmartContract", to), "Recipient not an official SmartContract");
ethPeerTreasury.totalWithdrawn += amount;
PeerTreasury(to).peerTreasuryReceiveETH{value: amount}();
emit PeerTreasuryTransferredETH(to, amount);
}
/// @notice Receive ERC20 tokens and track source
function peerTreasuryReceiveTokens(address token, uint256 amount) virtual external {
require(amount > 0, "Zero amount");
require(IController(peerTreasuryController).isOfficialEntity("SmartContract", msg.sender), "Sender not official SC");
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
// Track the token if it's the first time we're seeing it
if (tokenPeerTreasury[token].totalReceived == 0 && tokenPeerTreasury[token].totalWithdrawn == 0) {
knownTokens.push(token);
}
tokenPeerTreasury[token].totalReceived += amount;
emit PeerTreasuryReceivedTokens(msg.sender, token, amount);
}
/// @notice Transfer ERC20 tokens to another peer contract (OfficialEntity SmartContract)
function peerTreasuryTransferTokens(address token, address to, uint256 amount) virtual public onlyTreasuryAdmin {
require(IERC20(token).balanceOf(address(this)) >= amount, "Insuffic token bal");
require(IController(peerTreasuryController).isOfficialEntity("SmartContract", to), "recipient not official SC");
tokenPeerTreasury[token].totalWithdrawn += amount;
require(IERC20(token).approve(to, amount), "Approve failed");
PeerTreasury(to).peerTreasuryReceiveTokens(token, amount);
emit PeerTreasuryTransferredTokens(to, token, amount);
}
/// @notice Check ETH available (total received - total withdrawn)
/// @return The signed balance (can be negative if more was withdrawn than received - which is OK!)
/// obviously the first lateral movement will be a withdrawal from a peerTreasury source
function peerETHAvailable() external view returns (int256) {
// Convert to int256 before subtraction to allow negative results
return int256(ethPeerTreasury.totalReceived) - int256(ethPeerTreasury.totalWithdrawn);
}
/// @notice Check token available (total received - total withdrawn)
/// @return The signed balance (can be negative if more was withdrawn than received)
/// obviously the first lateral movement will be a withdrawal from a peerTreasury source
function peerTokenAvailable(address token) external view returns (int256) {
TreasuryEntry storage e = tokenPeerTreasury[token];
// Convert to int256 before subtraction to allow negative results
return int256(e.totalReceived) - int256(e.totalWithdrawn);
}
struct TreasurySnapshot {
address token;
uint256 totalReceived;
uint256 totalWithdrawn;
}
/// @notice Return all known peer treasury token entries
function getPeerTreasuries() external view returns (TreasurySnapshot[] memory) {
uint256 len = knownTokens.length;
TreasurySnapshot[] memory result = new TreasurySnapshot[](len);
for (uint256 i = 0; i < len; i++) {
address token = knownTokens[i];
TreasuryEntry storage entry = tokenPeerTreasury[token];
result[i] = TreasurySnapshot({
token: token,
totalReceived: entry.totalReceived,
totalWithdrawn: entry.totalWithdrawn
});
}
return result;
}
/// @notice Return individual peer treasury entry for a token
function getPeerTreasury(address token) external view returns (uint256 received, uint256 withdrawn) {
TreasuryEntry storage entry = tokenPeerTreasury[token];
return (entry.totalReceived, entry.totalWithdrawn);
}
/// @notice Return ETH treasury data
function getPeerTreasuryETH() external view returns (uint256 received, uint256 withdrawn) {
return (ethPeerTreasury.totalReceived, ethPeerTreasury.totalWithdrawn);
}
}
Recoverable.sol 131 lines
// SPDX-License-Identifier: CLOSED LICENSE COPYRIGHT 2025
// OFFICAL DEL NORTE NETWORK COMPONENT
// Designed By Ken Silverman for Del Norte. Implementation help from Tony Sparks
// Compilation help from Maleeha Naveed. May 5th, 2025
pragma solidity 0.8.29;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/IController.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/// @title ReversibleRecoveryBase
/// @notice Base contract tracking / reversing accidental ETH/ERC20 transfers with admin authorization
abstract contract Recoverable {
using SafeERC20 for IERC20;
// STRUCTS
struct ReversibleUserBalance {
uint256 totalReceivedThatIsReversible;
uint256 totalReversed;
uint256 totalReversals;
}
// -----------------------------
// STORAGE
// -----------------------------
address public controller; // Address of the Controller contract (must implement IController)
uint256 public ADMIN_FEE_FIXED = 10 ** 17; // 0.1 ETH
uint256 public totalAdminFeesCollected;
mapping(address => ReversibleUserBalance) public reversibleEthBalances;
mapping(address => mapping(address => ReversibleUserBalance)) public reversibleTokenBalances;
// -----------------------------
// EVENTS
// -----------------------------
event TransferReversed(address indexed user, uint256 refundAmount, address tokenSC, uint256 adminFee);
event AdminFeeUpdated(uint256 newFee);
event ControllerChanged(address newController);
modifier onlyBTExecutives() {
bool temp = IController(controller).isOfficialEntity("TreasuryAdmin", msg.sender) ||
IController(controller).isOfficialEntity("SmartContract", msg.sender);
require(temp, "Unauthorized access");
_; // run the code block referencing this modifier
}
// -----------------------------
// CONSTRUCTOR
// -----------------------------
constructor(address _controller) {
require(_controller != address(0), "Controller address cannot be zero");
controller = _controller;
}
// -----------------------------
// EXTERNAL METHODS
// -----------------------------
function manualUpdateReversibleBalanceETH(address userAddress, uint256 amount)
external onlyBTExecutives {
reversibleEthBalances[userAddress].totalReceivedThatIsReversible += amount;
}
function manualUpdateReversibleBalanceERC20(address userAddress, uint256 amount, address tokenSC)
external onlyBTExecutives {
reversibleTokenBalances[tokenSC][userAddress].totalReceivedThatIsReversible += amount;
}
function reverseAccidentalETH() external payable {
require(msg.value >= ADMIN_FEE_FIXED, "Insufficient admin fee");
require(!IController(controller).isOfficialEntity("Registrar", msg.sender),
"Registrars/launchpads may not be allowed to reverse any amounts they send.");
ReversibleUserBalance storage balance = reversibleEthBalances[msg.sender];
uint256 refundAmount = balance.totalReceivedThatIsReversible - balance.totalReversed;
require(refundAmount > 0, "Nothing to refund");
// Update state before external call
balance.totalReversed += refundAmount;
balance.totalReversals += 1;
totalAdminFeesCollected += msg.value;
// Perform the external call
(bool success, ) = msg.sender.call{value: refundAmount}("");
require(success, "Ether transfer failed");
emit TransferReversed(msg.sender, refundAmount, address(0), msg.value);
}
function reverseAccidentalERC20(address tokenSC) external payable {
require(msg.value >= ADMIN_FEE_FIXED, "Insufficient admin fee");
require(!IController(controller).isOfficialEntity("Registrar", msg.sender), "Registrars/launchpads may not reverse any amounts they send.");
ReversibleUserBalance storage balance = reversibleTokenBalances[tokenSC][msg.sender];
uint256 refundAmount = balance.totalReceivedThatIsReversible - balance.totalReversed;
require(refundAmount > 0, "Nothing to refund");
// Update state before external call
balance.totalReversed += refundAmount;
balance.totalReversals += 1;
totalAdminFeesCollected += msg.value;
// Perform the external call
IERC20(tokenSC).safeTransfer(msg.sender, refundAmount);
emit TransferReversed(msg.sender, refundAmount, tokenSC, msg.value);
}
function changeAdminFee(uint256 newFee) external onlyBTExecutives {
ADMIN_FEE_FIXED = newFee;
emit AdminFeeUpdated(newFee);
}
function changeController(address remote) internal {
controller = remote;
emit ControllerChanged(remote);
}
}
IController.sol 45 lines
// SPDX-License-Identifier: UNLICENSED
// Copyright 2025 US Fintech LLC and DelNorte Holdings.
//
// Permission to use, copy, modify, or distribute this software is strictly prohibited
// without prior written consent from both copyright holders.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
// ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// OFFICIAL DEL NORTE NETWORK COMPONENT
// Provides immediate membership access to platform at different levels.
// Required Non US or accredited US registration to swap for DTV token. Registration available within 180 days per terms.delnorte.io .
// Minimally tesed Conroller Tree for world-wide government administration of, well, anything, including property ownership.
// Designed by Ken Silverman as part of his ElasticTreasury (HUB and SPOKE), PeerTreasury and Controller model.
// @author Ken Silverman
// This deployment is for Del Norte Holdings, Delaware and US Fintech, LLC NY.
// Permission to change metadata stored on blockchain explorers and elsewhere granted to:
// Del Norte Holdings, DE only and/or US Fintech, LLC NY independently
pragma solidity 0.8.29;
interface IController {
struct OfficialEntityStruct {
string fullNameOfEntityOrLabel;
string nationalIdOfEntity;
address pubAddress;
uint256 blockNumber;
uint256 blockTimestamp;
bool active;
}
function addOfficialEntity(string memory, address, string memory, string memory) external returns (bool);
function removeOfficialEntity(string memory, address) external returns (bool);
function isOfficialEntity(string memory, address) external view returns (bool);
function isOfficialEntityFast(bytes32, address) external view returns (bool);
function isOfficialDoubleEntity(string calldata, address, string calldata, address, bool) external view returns (bool);
function isOfficialDoubleEntityFast(bytes32, address, bytes32, address, bool) external view returns (bool);
function isOfficialTripleEntityFast(bytes32, address, bytes32, address, bytes32, address, bool) external view returns (bool);
function isOfficialTripleEntity(string calldata, address, string calldata, address, string calldata, address, bool) external view returns (bool);
function isOfficialQuadrupleEntityFast(bytes32, address, bytes32, address, bytes32, address, bytes32, address, bool) external view returns (bool);
function getOfficialEntity(string calldata, address) external view returns (OfficialEntityStruct memory);
function getAllOfficialEntities(string calldata) external view returns (OfficialEntityStruct[] memory);
function init(address, string calldata) external;
}
IElasticTreasuryHub.sol 13 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.29;
interface IElasticTreasuryHub {
function withdrawETHToPerson(address personAddress, uint256 amt, string calldata note) external;
function withdrawTokensToPerson(address personAddress, uint256 amt, address tokenSCAddress, string calldata note) external;
function treasuryTransfer(address SCAddress, string calldata allCoinLabel, address tokenAddress, string calldata tokenLabel, uint256 amount) external returns (bool);
function treasuryTransferETH(address payable SCAddress, string calldata allCoinLabel, string calldata ethLabel, uint256 amount) external returns (bool);
function treasuryReceiveReclaimedETH() external payable returns (bool);
function treasuryReceiveReclaimedTokens(address tokenAddress, uint256 amt) external returns (bool);
function treasuryReclaimRequest(address SCAddress, address tokenAddress, uint256 amount) external returns (bool);
function treasuryReclaimRequestETH(address payable SCAddress, uint256 amount) external returns (bool);
}
IElasticTreasurySpoke.sol 12 lines
// SPDX-License-Identifier: Copyright 2025
// OFFICIAL DEL NORTE NETWORK COMPONENT
// Designed and coded by: Ken Silverman
// implementation help by Tony Sparks
pragma solidity 0.8.29;
interface IElasticTreasurySpoke {
function treasuryReceive(address tokenAddress, uint256 amount) external;
function treasuryReceiveETH() external payable;
function treasuryReclaim(address tokenAddress, uint256 amount) external returns (bool);
function treasuryReclaimETH(uint256 amount) external;
}
Read Contract
DOMAIN_SEPARATOR 0x3644e515 → bytes32
KECCAK_SMART_CONTRACT 0xe9ec573a → bytes32
KECCAK_TREASURY_ADMIN 0xde29207d → bytes32
MAX_SUPPLY 0x32cb6b0c → uint256
allowance 0xdd62ed3e → uint256
balanceOf 0x70a08231 → uint256
controller 0xf77c4791 → address
decimals 0x313ce567 → uint8
eip712Domain 0x84b0196e → bytes1, string, string, uint256, address, bytes32, uint256[]
gatingActive 0xe577c3d9 → bool
getActiveWhitelistedUsers 0xe63008af → address[]
getAllCoinSpokeTreasury 0x840772bb → tuple[]
getAllSpokeTreasuries 0x34cd584b → tuple[]
getAllWhitelistedUsers 0xa5521999 → address[]
getPeerTreasuries 0xbd6b3e47 → tuple[]
getPeerTreasury 0x3cc7dcbf → uint256, uint256
getPeerTreasuryETH 0xb2654ef5 → uint256, uint256
getUserRegistrationStatus 0x5384b930 → uint8
isRestricted 0x5975f4a6 → bool
isUnrestrictedWhitelistedUser 0x09fec7f2 → bool
isUserRestricted 0x0d92e2c4 → bool
isUserWhitelistedNonUSA 0x82027f6b → bool
name 0x06fdde03 → string
nonces 0x7ecebe00 → uint256
peerETHAvailable 0x11d96f29 → int256
peerTokenAvailable 0x85514f89 → int256
peerTreasuryController 0xad20810a → address
symbol 0x95d89b41 → string
totalSupply 0x18160ddd → uint256
whitelistedUserList 0xc6854ea5 → address
whitelistedUsers 0xf621cc48 → string
whitelistedUsersIsUSA 0x2d204f18 → bool
Write Contract 26 functions
These functions modify contract state and require a wallet transaction to execute.
addWhitelistedUser 0x399f93ed
address user
string registrationNote
bool isUS
addWhitelistedUsers 0xe717e91a
tuple[] entries
approve 0x095ea7b3
address spender
uint256 amount
returns: bool
burn 0x42966c68
uint256 amount
burnFrom 0x79cc6790
address account
uint256 amount
decreaseAllowance 0xa457c2d7
address spender
uint256 subtractedValue
returns: bool
executiveWithdrawETH 0xbb9ad4c4
address personAddress
uint256 amt
string note
executiveWithdrawTokens 0xbfd6cc04
address personAddress
uint256 amt
address tokenSCAddress
string note
increaseAllowance 0x39509351
address spender
uint256 addedValue
returns: bool
peerTreasuryReceiveETH 0x2f43f19d
No parameters
peerTreasuryReceiveTokens 0xef7cc6c4
address token
uint256 amount
peerTreasuryTransferETH 0x9d96425f
address to
uint256 amount
peerTreasuryTransferTokens 0xda28bc92
address token
address to
uint256 amount
permit 0xd505accf
address owner
address spender
uint256 value
uint256 deadline
uint8 v
bytes32 r
bytes32 s
reactivateWhitelistedUsers 0x44a78c4a
address[] users
restrictWhitelistedUsers 0xfabf7a5b
address[] users
setGating 0x76dc3c31
bool active
transfer 0xa9059cbb
address recipient
uint256 amount
returns: bool
transferFrom 0x23b872dd
address sender
address recipient
uint256 amount
returns: bool
treasuryReceiveReclaimedETH 0x3aec243f
No parameters
returns: bool
treasuryReceiveReclaimedTokens 0x481c9e4c
address tokenAddress
uint256 amt
returns: bool
treasuryReclaimRequest 0x3654ed48
address SCAddress
address tokenAddress
uint256 amount
returns: bool
treasuryReclaimRequestETH 0x5b774d36
address SCAddress
uint256 amount
returns: bool
treasuryTransfer 0x6bbd4e28
address SCAddress
string allCoinLabel
address tokenAddress
string tokenLabel
uint256 amount
returns: bool
treasuryTransferETH 0x872900a6
address SCAddress
string allCoinLabel
string ethLabel
uint256 amount
returns: bool
whitelistUserWithRegKey 0x23ab9853
address registrarAddress
bool isUS
bytes registrarSignedMessage
string registrationNote
Token Balances (2)
View Transfers →Recent Transactions
No transactions found for this address