Address Contract Verified
Address
0x8a4B4C2aCAdeAa7206Df96F00052e41d74a015CE
Balance
0.041003 ETH
Nonce
1
Code Size
24099 bytes
Creator
0x1b9dFC56...911c at tx 0x2a3b775a...a74da6
Indexed Transactions
0
Contract Bytecode
24099 bytes
0x6080604052600436106103905760003560e01c80636ea69d62116101dc57806390e360f811610102578063b4f7fa34116100a0578063eb505dd51161006f578063eb505dd514610af8578063eecefef814610b25578063f2fde38b14610b52578063fa52c7d814610b7257600080fd5b8063b4f7fa3414610a5f578063c8f9f98414610a7f578063dcfdc1e114610ab8578063e909156d14610ad857600080fd5b8063960dc08a116100dc578063960dc08a146109955780639b19251a146109c9578063a310624f146109f9578063acc62ccf14610a3f57600080fd5b806390e360f8146109255780639146f1101461095557806392bb243c1461097557600080fd5b806382dc1ec41161017a57806388d996e81161014957806388d996e8146108b257806389f9aab5146108d25780638a74d5fe146108e75780638da5cb5b1461090757600080fd5b806382dc1ec4146108515780638338f0e51461087157806383cfb318146108875780638456cb591461089d57600080fd5b806371bc0216116101b657806371bc0216146107cc5780637a50dbd2146107ec57806380f51c121461080c57806382d7b4b81461083c57600080fd5b80636ea69d62146107825780636ef8d66d146107a2578063715018a6146107b757600080fd5b8063410ae02c116102c1578063525eba211161025f578063682dbc221161022e578063682dbc22146106ec57806368706e541461070c5780636b2c0f551461072c5780636d3087831461074c57600080fd5b8063525eba21146106775780635c975abb146106975780635e593eff146106b657806365d5d420146106d657600080fd5b806347abfdbf1161029b57806347abfdbf146105fd57806349955e391461061d57806351508f0a1461063d57806351fb012d1461065d57600080fd5b8063410ae02c1461058457806346fbf68e146105a4578063473849bd146105dd57600080fd5b80632fa4d12b1161032e5780633985c4e6116103085780633985c4e6146104e45780633af32abf146105045780633f4ba83a1461054d5780634021d4d51461056257600080fd5b80632fa4d12b1461048257806336f1635f146104ba578063386c024a146104cf57600080fd5b8063145aa1161161036a578063145aa116146103fe5780631a2032571461041e5780631cfe4f0b1461043e578063291d95491461046257600080fd5b8063026e402b1461039c578063052d9e7e146103be57806310154bad146103de57600080fd5b3661039757005b600080fd5b3480156103a857600080fd5b506103bc6103b7366004615309565b610c10565b005b3480156103ca57600080fd5b506103bc6103d9366004615341565b610e90565b3480156103ea57600080fd5b506103bc6103f936600461535e565b610eeb565b34801561040a57600080fd5b506103bc610419366004615379565b610ff7565b34801561042a57600080fd5b506103bc610439366004615379565b6110cf565b34801561044a57600080fd5b506006545b6040519081526020015b60405180910390f35b34801561046e57600080fd5b506103bc61047d36600461535e565b611145565b34801561048e57600080fd5b50600c546104a2906001600160a01b031681565b6040516001600160a01b039091168152602001610459565b3480156104c657600080fd5b506103bc611246565b3480156104db57600080fd5b5061044f6115d4565b3480156104f057600080fd5b506103bc6104ff366004615420565b611601565b34801561051057600080fd5b5061053d61051f36600461535e565b6001600160a01b031660009081526002602052604090205460ff1690565b6040519015158152602001610459565b34801561055957600080fd5b506103bc611da7565b34801561056e57600080fd5b50610577611e10565b604051610459919061548c565b34801561059057600080fd5b5061044f61059f3660046154f3565b611f11565b3480156105b057600080fd5b5061053d6105bf36600461535e565b6001600160a01b031660009081526001602052604090205460ff1690565b3480156105e957600080fd5b506103bc6105f836600461535e565b611f50565b34801561060957600080fd5b5061053d61061836600461550e565b612258565b34801561062957600080fd5b506103bc61063836600461555d565b612312565b34801561064957600080fd5b506103bc61065836600461535e565b612474565b34801561066957600080fd5b5060035461053d9060ff1681565b34801561068357600080fd5b506103bc610692366004615578565b6124de565b3480156106a357600080fd5b50600054600160a01b900460ff1661053d565b3480156106c257600080fd5b506103bc6106d1366004615379565b612980565b3480156106e257600080fd5b5061044f60045481565b3480156106f857600080fd5b506103bc61070736600461566b565b612b99565b34801561071857600080fd5b506103bc61072736600461535e565b612bfc565b34801561073857600080fd5b506103bc61074736600461535e565b612c66565b34801561075857600080fd5b506104a261076736600461535e565b6009602052600090815260409020546001600160a01b031681565b34801561078e57600080fd5b50600d546104a2906001600160a01b031681565b3480156107ae57600080fd5b506103bc612cb7565b3480156107c357600080fd5b506103bc612cc0565b3480156107d857600080fd5b506103bc6107e736600461535e565b612d12565b3480156107f857600080fd5b506103bc61080736600461535e565b612e5a565b34801561081857600080fd5b5061053d61082736600461535e565b60016020526000908152604090205460ff1681565b34801561084857600080fd5b506103bc61308c565b34801561085d57600080fd5b506103bc61086c36600461535e565b613123565b34801561087d57600080fd5b5061044f600e5481565b34801561089357600080fd5b5061044f60055481565b3480156108a957600080fd5b506103bc613174565b3480156108be57600080fd5b506103bc6108cd366004615309565b6131db565b3480156108de57600080fd5b5060075461044f565b3480156108f357600080fd5b5061053d6109023660046157af565b6132d9565b34801561091357600080fd5b506000546001600160a01b03166104a2565b34801561093157600080fd5b5061053d610940366004615379565b600a6020526000908152604090205460ff1681565b34801561096157600080fd5b506103bc610970366004615827565b6134bd565b34801561098157600080fd5b506104a2610990366004615379565b61358b565b3480156109a157600080fd5b506104a27f0000000000000000000000004f9254c83eb525f9fcf346490bbb3ed28a81c66781565b3480156109d557600080fd5b5061053d6109e436600461535e565b60026020526000908152604090205460ff1681565b348015610a0557600080fd5b50610a32610a1436600461535e565b6001600160a01b031660009081526008602052604090205460ff1690565b60405161045991906158e0565b348015610a4b57600080fd5b506104a2610a5a366004615379565b6135b5565b348015610a6b57600080fd5b5061053d610a7a36600461535e565b6135c5565b348015610a8b57600080fd5b5061044f610a9a36600461535e565b6001600160a01b031660009081526008602052604090206001015490565b348015610ac457600080fd5b506103bc610ad3366004615309565b6135fd565b348015610ae457600080fd5b506103bc610af33660046158ee565b6136f5565b348015610b0457600080fd5b5061044f610b133660046154f3565b600b6020526000908152604090205481565b348015610b3157600080fd5b50610b45610b4036600461590a565b6137f8565b604051610459919061593d565b348015610b5e57600080fd5b506103bc610b6d36600461535e565b613af1565b348015610b7e57600080fd5b50610bfa610b8d36600461535e565b600860205260009081526040902080546001820154600283015460038401546004850154600686015460079096015460ff8616966101009096046001600160a01b0316959067ffffffffffffffff80821691680100000000000000008104821691600160801b909104168a565b6040516104599a999897969594939291906159dc565b600054600160a01b900460ff1615610c625760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064015b60405180910390fd5b33670de0b6b3a7640000821015610cbb5760405162461bcd60e51b815260206004820152601860248201527f4d696e696d616c20616d6f756e7420697320312043454c5200000000000000006044820152606401610c59565b6001600160a01b038316600090815260086020526040812090815460ff166003811115610cea57610cea6158a8565b1415610d385760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206973206e6f7420696e697469616c697a6564000000006044820152606401610c59565b6000610d4d8483600101548460020154613bbe565b6001600160a01b0384166000908152600584016020526040812080549293509183918391610d7c908490615a5e565b9250508190555081836002016000828254610d979190615a5e565b9250508190555084836001016000828254610db29190615a5e565b9091555060039050835460ff166003811115610dd057610dd06158a8565b1415610dfb578460046000828254610de89190615a5e565b90915550506001830154610dfb90613beb565b610e306001600160a01b037f0000000000000000000000004f9254c83eb525f9fcf346490bbb3ed28a81c66716853088613d0e565b6001830154815460408051928352602083019190915281018690526001600160a01b0380861691908816907f2e289e5a72f8e92e344eb866e0a32621f332835d2df2cf1f76e5a345b23cf1ea9060600160405180910390a3505050505050565b6000546001600160a01b03163314610ed85760405162461bcd60e51b81526020600482018190526024820152600080516020615dce8339815191526044820152606401610c59565b6003805460ff1916911515919091179055565b6000546001600160a01b03163314610f335760405162461bcd60e51b81526020600482018190526024820152600080516020615dce8339815191526044820152606401610c59565b6001600160a01b03811660009081526002602052604090205460ff1615610f9c5760405162461bcd60e51b815260206004820152601360248201527f416c72656164792077686974656c6973746564000000000000000000000000006044820152606401610c59565b6001600160a01b038116600081815260026020908152604091829020805460ff1916600117905590519182527fee1504a83b6d4a361f4c1dc78ab59bfa30d6a3b6612c403e86bb01ef2984295f91015b60405180910390a150565b600054600160a01b900460ff166110505760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610c59565b6000546001600160a01b031633146110985760405162461bcd60e51b81526020600482018190526024820152600080516020615dce8339815191526044820152606401610c59565b6110cc6001600160a01b037f0000000000000000000000004f9254c83eb525f9fcf346490bbb3ed28a81c667163383613da6565b50565b6000546001600160a01b031633146111175760405162461bcd60e51b81526020600482018190526024820152600080516020615dce8339815191526044820152606401610c59565b6008600052600b6020527f71f482bdabd1ea844d62c952b094e632959690d7448ca2aab34034ec9856935855565b6000546001600160a01b0316331461118d5760405162461bcd60e51b81526020600482018190526024820152600080516020615dce8339815191526044820152606401610c59565b6001600160a01b03811660009081526002602052604090205460ff166111f55760405162461bcd60e51b815260206004820152600f60248201527f4e6f742077686974656c697374656400000000000000000000000000000000006044820152606401610c59565b6001600160a01b038116600081815260026020908152604091829020805460ff1916905590519182527f270d9b30cf5b0793bbfd54c9d5b94aeb49462b8148399000265144a8722da6b69101610fec565b336000818152600960205260409020546001600160a01b03161561127f5750336000908152600960205260409020546001600160a01b03165b6001600160a01b03811660009081526008602052604090206001815460ff1660038111156112af576112af6158a8565b14806112d057506002815460ff1660038111156112ce576112ce6158a8565b145b61131c5760405162461bcd60e51b815260206004820152601860248201527f496e76616c69642076616c696461746f722073746174757300000000000000006044820152606401610c59565b600781015467ffffffffffffffff1643101561137a5760405162461bcd60e51b815260206004820152601660248201527f426f6e6420626c6f636b206e6f742072656163686564000000000000000000006044820152606401610c59565b6005544310156113cc5760405162461bcd60e51b815260206004820152601b60248201527f546f6f206672657175656e742076616c696461746f7220626f6e6400000000006044820152606401610c59565b6007600052600b6020527ff5559028dc9ba50d75343c779b2f75e13a84a14662932fc67a486f263ca31a96546114029043615a5e565b600555611410826001612258565b61145c5760405162461bcd60e51b815260206004820152601360248201527f4e6f742068617665206d696e20746f6b656e73000000000000000000000000006044820152606401610c59565b6003600052600b6020527f64c15cc42be7899b001f818cf4433057002112c418d1d3a67cd5cb453051d33e546007548111156114ad5761149b83613dd6565b6114a88260010154613beb565b505050565b6000196000805b83811015611562578260086000600784815481106114d4576114d4615a76565b60009182526020808320909101546001600160a01b03168352820192909252604001902060010154101561155057809150600860006007838154811061151c5761151c615a76565b60009182526020808320909101546001600160a01b0316835282019290925260400190206001015492508261155057611562565b8061155a81615a8c565b9150506114b4565b50818460010154116115b65760405162461bcd60e51b815260206004820152601360248201527f496e73756666696369656e7420746f6b656e73000000000000000000000000006044820152606401610c59565b6115c08582613e2a565b6115cd8460010154613beb565b5050505050565b6000600360045460026115e79190615aa7565b6115f19190615ac6565b6115fc906001615a5e565b905090565b600054600160a01b900460ff161561164e5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610c59565b600046306040516020016116a492919091825260601b6bffffffffffffffffffffffff191660208201527f536c617368000000000000000000000000000000000000000000000000000000603482015260390190565b6040516020818303038152906040528051906020012090506116ee8186866040516020016116d493929190615ae8565b60408051601f198184030181529190526109028486615b02565b50600061173086868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613ea592505050565b9050806060015167ffffffffffffffff16421061178f5760405162461bcd60e51b815260206004820152600d60248201527f536c6173682065787069726564000000000000000000000000000000000000006044820152606401610c59565b620f4240816040015167ffffffffffffffff1611156117f05760405162461bcd60e51b815260206004820152601460248201527f496e76616c696420736c61736820666163746f720000000000000000000000006044820152606401610c59565b6008600052600b6020527f71f482bdabd1ea844d62c952b094e632959690d7448ca2aab34034ec9856935854604082015167ffffffffffffffff1611156118795760405162461bcd60e51b815260206004820152601760248201527f457863656564206d617820736c61736820666163746f720000000000000000006044820152606401610c59565b60208082015167ffffffffffffffff166000908152600a909152604090205460ff16156118e85760405162461bcd60e51b815260206004820152601060248201527f5573656420736c617368206e6f6e6365000000000000000000000000000000006044820152606401610c59565b60208082015167ffffffffffffffff166000908152600a82526040808220805460ff1916600117905583516001600160a01b0381168352600890935290206003815460ff16600381111561193e5761193e6158a8565b148061195f57506002815460ff16600381111561195d5761195d6158a8565b145b6119ab5760405162461bcd60e51b815260206004820152601860248201527f496e76616c69642076616c696461746f722073746174757300000000000000006044820152606401610c59565b6000620f4240846040015167ffffffffffffffff1683600101546119cf9190615aa7565b6119d99190615ac6565b9050808260010160008282546119ef9190615b0f565b9091555060039050825460ff166003811115611a0d57611a0d6158a8565b1415611a5c578060046000828254611a259190615b0f565b9091555050608084015167ffffffffffffffff16151580611a4e5750611a4c836001612258565b155b15611a5c57611a5c83614117565b6002825460ff166003811115611a7457611a746158a8565b148015611a8f57506000846080015167ffffffffffffffff16115b15611ad2576080840151611aad9067ffffffffffffffff1643615a5e565b60078301805467ffffffffffffffff191667ffffffffffffffff929092169190911790555b60006001600160a01b0316836001600160a01b03167f2e289e5a72f8e92e344eb866e0a32621f332835d2df2cf1f76e5a345b23cf1ea8460010154600085611b1990615b26565b6040805193845260208401929092529082015260600160405180910390a36000620f4240856040015167ffffffffffffffff168460030154611b5b9190615aa7565b611b659190615ac6565b905080836003016000828254611b7b9190615b0f565b90915550611b8b90508183615a5e565b91506000805b8660a0015151811015611d255760008760a001518281518110611bb657611bb6615a76565b6020026020010151905084816020015184611bd19190615a5e565b1115611be757611be18386615b0f565b60208201525b602081015115611d12576020810151611c009084615a5e565b81519093506001600160a01b0316611c8b576020810151611c4d906001600160a01b037f0000000000000000000000004f9254c83eb525f9fcf346490bbb3ed28a81c66716903390613da6565b60208082015160405190815233917fb1375221b23a15d2f6887c7dbdc6745a07d9a5245076d51fb41879590ebbd2a3910160405180910390a2611d12565b80516020820151611cc6916001600160a01b037f0000000000000000000000004f9254c83eb525f9fcf346490bbb3ed28a81c6671691613da6565b80600001516001600160a01b03167fb1375221b23a15d2f6887c7dbdc6745a07d9a5245076d51fb41879590ebbd2a38260200151604051611d0991815260200190565b60405180910390a25b5080611d1d81615a8c565b915050611b91565b50611d308184615b0f565b600e6000828254611d419190615a5e565b90915550506020808701516040805167ffffffffffffffff90921682529181018590526001600160a01b038716917f10863f35bc5db9fda133333468bf7b1ceaaa88cb4263c061f890f97b79bf9008910160405180910390a25050505050505050505050565b3360009081526001602052604090205460ff16611e065760405162461bcd60e51b815260206004820152601460248201527f43616c6c6572206973206e6f74207061757365720000000000000000000000006044820152606401610c59565b611e0e61427d565b565b60075460609060009067ffffffffffffffff811115611e3157611e316155b4565b604051908082528060200260200182016040528015611e7657816020015b6040805180820190915260008082526020820152815260200190600190039081611e4f5790505b50905060005b600754811015611f0b57600060078281548110611e9b57611e9b615a76565b60009182526020808320909101546040805180820182526001600160a01b039092168083528085526008845293206001015491810191909152845191925090849084908110611eec57611eec615a76565b6020026020010181905250508080611f0390615a8c565b915050611e7c565b50919050565b6000600b6000836008811115611f2957611f296158a8565b6008811115611f3a57611f3a6158a8565b8152602001908152602001600020549050919050565b6001600160a01b03811660009081526008602052604081203391815460ff166003811115611f8057611f806158a8565b1415611fce5760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206973206e6f7420696e697469616c697a6564000000006044820152606401610c59565b6001600160a01b03821660009081526005820160209081526040822060028352600b9091527fa50eece07c7db1631545c0069bd8f5f54d5935e215d59097edf258a44ba916345483549192909160019060ff166003811115612032576120326158a8565b60028501549114915063ffffffff1660005b600285015463ffffffff640100000000909104811690831610156120f6578280612094575063ffffffff821660009081526001808701602052604090912001544390612091908690615a5e565b11155b156120df5763ffffffff821660009081526001860160205260409020546120bb9082615a5e565b63ffffffff83166000908152600180880160205260408220828155015590506120e4565b6120f6565b816120ee81615b43565b925050612044565b60028501805463ffffffff191663ffffffff8416179055806121805760405162461bcd60e51b815260206004820152602560248201527f4e6f20756e64656c65676174696f6e20726561647920746f20626520636f6d7060448201527f6c657465640000000000000000000000000000000000000000000000000000006064820152608401610c59565b60006121958288600301548960040154614323565b9050818760040160008282546121ab9190615b0f565b92505081905550808760030160008282546121c69190615b0f565b9091555061220090506001600160a01b037f0000000000000000000000004f9254c83eb525f9fcf346490bbb3ed28a81c667168983613da6565b876001600160a01b0316896001600160a01b03167f4d10bd049775c77bd7f255195afba5088028ecb3c7c277d393ccff7934f2f92c8360405161224591815260200190565b60405180910390a3505050505050505050565b6001600160a01b03821660009081526008602090815260408220600181015460048452600b9092527f12d0c11577e2f0950f57c455c117796550b79f444811db8ba2f69c57b646c784549091908110156122b75760009250505061230c565b8315612305576001600160a01b038516600090815260058301602052604081205460028401546122e991908490614323565b90508260060154811015612303576000935050505061230c565b505b6001925050505b92915050565b33600081815260086020526040812090815460ff166003811115612338576123386158a8565b14156123865760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206973206e6f7420696e697469616c697a6564000000006044820152606401610c59565b6127108367ffffffffffffffff1611156123e25760405162461bcd60e51b815260206004820152601060248201527f496e76616c6964206e65772072617465000000000000000000000000000000006044820152606401610c59565b60078101805467ffffffffffffffff60801b1916600160801b67ffffffffffffffff8616908102919091179091556040805160208101929092526001600160a01b038416917f3683b59f352bc42833c21c736ba7631d3e35fed49723ebac8298d4e0f36e512c910160408051601f198184030181529082905261246791600090615bbf565b60405180910390a2505050565b6000546001600160a01b031633146124bc5760405162461bcd60e51b81526020600482018190526024820152600080516020615dce8339815191526044820152606401610c59565b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b600054600160a01b900460ff161561252b5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610c59565b60035460ff1615612595573360009081526002602052604090205460ff166125955760405162461bcd60e51b815260206004820152601960248201527f43616c6c6572206973206e6f742077686974656c6973746564000000000000006044820152606401610c59565b33600081815260086020526040812090815460ff1660038111156125bb576125bb6158a8565b146126085760405162461bcd60e51b815260206004820152601860248201527f56616c696461746f7220697320696e697469616c697a656400000000000000006044820152606401610c59565b6001600160a01b03851660009081526008602052604081205460ff166003811115612635576126356158a8565b146126825760405162461bcd60e51b815260206004820152601960248201527f5369676e6572206973206f746865722076616c696461746f72000000000000006044820152606401610c59565b6001600160a01b0382811660009081526009602052604090205416156126ea5760405162461bcd60e51b815260206004820152601960248201527f56616c696461746f72206973206f74686572207369676e6572000000000000006044820152606401610c59565b6001600160a01b0385811660009081526009602052604090205416156127525760405162461bcd60e51b815260206004820152601360248201527f5369676e657220616c72656164792075736564000000000000000000000000006044820152606401610c59565b6127108367ffffffffffffffff1611156127ae5760405162461bcd60e51b815260206004820152601760248201527f496e76616c696420636f6d6d697373696f6e20726174650000000000000000006044820152606401610c59565b6005600052600b6020527febae6141bae5521e99e0a8d610356b0f501fea54980b59c84841db43ba7204f4548410156128295760405162461bcd60e51b815260206004820181905260248201527f496e73756666696369656e74206d696e2073656c662064656c65676174696f6e6044820152606401610c59565b80547fffffffffffffffffffffff0000000000000000000000000000000000000000001660ff196101006001600160a01b03888116918202929092169290921760019081178455600680850188905560078501805467ffffffffffffffff60801b1916600160801b67ffffffffffffffff8a1602179055805491820190557ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b031990811692861692831790915560009283526009602052604090922080549092161790556129018285610c10565b604080516001600160a01b03878116602083015291810186905267ffffffffffffffff85166060820152908316907f3683b59f352bc42833c21c736ba7631d3e35fed49723ebac8298d4e0f36e512c9060800160408051601f198184030181529082905261297191600090615c1e565b60405180910390a25050505050565b33600081815260086020526040812090815460ff1660038111156129a6576129a66158a8565b14156129f45760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206973206e6f7420696e697469616c697a6564000000006044820152606401610c59565b6005600052600b6020527febae6141bae5521e99e0a8d610356b0f501fea54980b59c84841db43ba7204f454831015612a6f5760405162461bcd60e51b815260206004820181905260248201527f496e73756666696369656e74206d696e2073656c662064656c65676174696f6e6044820152606401610c59565b8060060154831015612b3c576003815460ff166003811115612a9357612a936158a8565b1415612ae15760405162461bcd60e51b815260206004820152601360248201527f56616c696461746f7220697320626f6e646564000000000000000000000000006044820152606401610c59565b6006600052600b6020527f0387e9d1203691d8e3362a7e4c6723de358a4010d7f31ecbec3fbfc61d1c75fc54612b179043615a5e565b60078201805467ffffffffffffffff191667ffffffffffffffff929092169190911790555b6006810183905560408051602081018590526001600160a01b038416917f3683b59f352bc42833c21c736ba7631d3e35fed49723ebac8298d4e0f36e512c910160408051601f198184030181529082905261246791600090615c4c565b612ba7876109028789615b02565b612bf35760405162461bcd60e51b815260206004820152601560248201527f4661696c656420746f20766572696679207369677300000000000000000000006044820152606401610c59565b50505050505050565b6000546001600160a01b03163314612c445760405162461bcd60e51b81526020600482018190526024820152600080516020615dce8339815191526044820152606401610c59565b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b03163314612cae5760405162461bcd60e51b81526020600482018190526024820152600080516020615dce8339815191526044820152606401610c59565b6110cc8161433c565b611e0e3361433c565b6000546001600160a01b03163314612d085760405162461bcd60e51b81526020600482018190526024820152600080516020615dce8339815191526044820152606401610c59565b611e0e60006143f5565b6001600160a01b03811660009081526008602052604090206002815460ff166003811115612d4257612d426158a8565b14612d8f5760405162461bcd60e51b815260206004820152601760248201527f56616c696461746f72206e6f7420756e626f6e64696e670000000000000000006044820152606401610c59565b600781015468010000000000000000900467ffffffffffffffff16431015612df95760405162461bcd60e51b815260206004820152601860248201527f556e626f6e6420626c6f636b206e6f74207265616368656400000000000000006044820152606401610c59565b805460ff1916600190811782556007820180546fffffffffffffffff0000000000000000191690555b6040516001600160a01b038416907fd5e59fa85493a77fb57f6bf9080f2f71fde9de0eadc62b27b43b6263f3f1f59a90600090a35050565b33600081815260086020526040812090815460ff166003811115612e8057612e806158a8565b1415612ece5760405162461bcd60e51b815260206004820152601960248201527f56616c696461746f72206e6f7420696e697469616c697a6564000000000000006044820152606401610c59565b6001600160a01b038381166000908152600960205260409020541615612f365760405162461bcd60e51b815260206004820152601360248201527f5369676e657220616c72656164792075736564000000000000000000000000006044820152606401610c59565b816001600160a01b0316836001600160a01b031614612fc9576001600160a01b03831660009081526008602052604081205460ff166003811115612f7c57612f7c6158a8565b14612fc95760405162461bcd60e51b815260206004820152601960248201527f5369676e6572206973206f746865722076616c696461746f72000000000000006044820152606401610c59565b8054610100908190046001600160a01b03908116600090815260096020908152604080832080546001600160a01b031990811690915586547fffffffffffffffffffffff0000000000000000000000000000000000000000ff1689861696870217875585845292819020805490931693871693841790925581519081019390935290917f3683b59f352bc42833c21c736ba7631d3e35fed49723ebac8298d4e0f36e512c910160408051601f198184030181529082905261246791600090615c93565b6000600e54116130de5760405162461bcd60e51b815260206004820152601260248201527f4e6f7468696e6720746f20636f6c6c65637400000000000000000000000000006044820152606401610c59565b600d54600e5461311c916001600160a01b037f0000000000000000000000004f9254c83eb525f9fcf346490bbb3ed28a81c667811692911690613da6565b6000600e55565b6000546001600160a01b0316331461316b5760405162461bcd60e51b81526020600482018190526024820152600080516020615dce8339815191526044820152606401610c59565b6110cc81614445565b3360009081526001602052604090205460ff166131d35760405162461bcd60e51b815260206004820152601460248201527f43616c6c6572206973206e6f74207061757365720000000000000000000000006044820152606401610c59565b611e0e614503565b670de0b6b3a76400008110156132335760405162461bcd60e51b815260206004820152601960248201527f4d696e696d616c20616d6f756e742069732031207368617265000000000000006044820152606401610c59565b6001600160a01b038216600090815260086020526040812090815460ff166003811115613262576132626158a8565b14156132b05760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206973206e6f7420696e697469616c697a6564000000006044820152606401610c59565b60006132c58383600101548460020154614323565b90506132d38285838661458b565b50505050565b60008061333a84805190602001206040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01604051602081830303815290604052805190602001209050919050565b9050600080806133486115d4565b905060005b865181101561347457600061338488838151811061336d5761336d615a76565b60200260200101518761492e90919063ffffffff16565b9050836001600160a01b0316816001600160a01b0316116133e75760405162461bcd60e51b815260206004820152601e60248201527f5369676e657273206e6f7420696e20617363656e64696e67206f7264657200006044820152606401610c59565b6001600160a01b038082166000908152600960209081526040808320549093168252600890522090935083906003815460ff16600381111561342b5761342b6158a8565b14613437575050613462565b60018101546134469087615a5e565b955083861061345f57600197505050505050505061230c565b50505b8061346c81615a8c565b91505061334d565b5060405162461bcd60e51b815260206004820152601260248201527f51756f72756d206e6f74207265616368656400000000000000000000000000006044820152606401610c59565b6001600160a01b038516600090815260086020526040812090815460ff1660038111156134ec576134ec6158a8565b141561353a5760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206973206e6f7420696e697469616c697a6564000000006044820152606401610c59565b856001600160a01b03167f3683b59f352bc42833c21c736ba7631d3e35fed49723ebac8298d4e0f36e512c868686863360405161357b959493929190615d03565b60405180910390a2505050505050565b6006818154811061359b57600080fd5b6000918252602090912001546001600160a01b0316905081565b6007818154811061359b57600080fd5b600060036001600160a01b03831660009081526008602052604090205460ff1660038111156135f6576135f66158a8565b1492915050565b670de0b6b3a76400008110156136555760405162461bcd60e51b815260206004820152601860248201527f4d696e696d616c20616d6f756e7420697320312043454c5200000000000000006044820152606401610c59565b6001600160a01b038216600090815260086020526040812090815460ff166003811115613684576136846158a8565b14156136d25760405162461bcd60e51b815260206004820152601c60248201527f56616c696461746f72206973206e6f7420696e697469616c697a6564000000006044820152606401610c59565b60006136e78383600101548460020154613bbe565b90506132d38285858461458b565b600c546001600160a01b0316331461374f5760405162461bcd60e51b815260206004820152601a60248201527f43616c6c6572206973206e6f7420676f7620636f6e74726163740000000000006044820152606401610c59565b6003826008811115613763576137636158a8565b14156137bb576007548110156137bb5760405162461bcd60e51b815260206004820152600d60248201527f696e76616c69642076616c7565000000000000000000000000000000000000006044820152606401610c59565b80600b60008460088111156137d2576137d26158a8565b60088111156137e3576137e36158a8565b81526020810191909152604001600020555050565b61383a6040518060c0016040528060006001600160a01b0316815260200160008152602001600081526020016060815260200160008152602001600081525090565b6001600160a01b038084166000908152600860209081526040808320938616835260058401909152812080546001840154600285015492939261387e929190614323565b60026000908152600b6020527fa50eece07c7db1631545c0069bd8f5f54d5935e215d59097edf258a44ba916345485549293509091829190829060019060ff1660038111156138cf576138cf6158a8565b6002880154911491506000906138f69063ffffffff80821691640100000000900416615d46565b63ffffffff16905060008167ffffffffffffffff811115613919576139196155b4565b60405190808252806020026020018201604052801561395e57816020015b60408051808201909152600080825260208201528152602001906001900390816139375790505b50905060005b82811015613a7557600289015460018a01906000906139899063ffffffff1684615a5e565b8152602001908152602001600020604051806040016040529081600082015481526020016001820154815250508282815181106139c8576139c8615a76565b60200260200101819052508181815181106139e5576139e5615a76565b602002602001015160000151876139fc9190615a5e565b96508380613a3257504385838381518110613a1957613a19615a76565b602002602001015160200151613a2f9190615a5e565b11155b15613a6357818181518110613a4957613a49615a76565b60200260200101516000015186613a609190615a5e565b95505b80613a6d81615a8c565b915050613964565b506000613a8b878b600301548c60040154614323565b90506000613aa2878c600301548d60040154614323565b90506040518060c001604052808f6001600160a01b031681526020018a81526020018b600001548152602001848152602001838152602001828152509b50505050505050505050505092915050565b6000546001600160a01b03163314613b395760405162461bcd60e51b81526020600482018190526024820152600080516020615dce8339815191526044820152606401610c59565b6001600160a01b038116613bb55760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610c59565b6110cc816143f5565b600082613bcc575082613be4565b82613bd78386615aa7565b613be19190615ac6565b90505b9392505050565b6007546002811480613bfd5750806003145b15613c8257613c0a6115d4565b8210613c7e5760405162461bcd60e51b815260206004820152602e60248201527f53696e676c652076616c696461746f722073686f756c64206e6f74206861766560448201527f2071756f72756d20746f6b656e730000000000000000000000000000000000006064820152608401610c59565b5050565b6003811115613c7e576003600454613c9a9190615ac6565b8210613c7e5760405162461bcd60e51b815260206004820152602b60248201527f53696e676c652076616c696461746f722073686f756c64206e6f74206861766560448201527f20312f3320746f6b656e730000000000000000000000000000000000000000006064820152608401610c59565b6040516001600160a01b03808516602483015283166044820152606481018290526132d39085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526149d2565b6040516001600160a01b0383166024820152604481018290526114a890849063a9059cbb60e01b90606401613d42565b600780546001810182556000919091527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6880180546001600160a01b0319166001600160a01b0383161790556110cc81614ab7565b613e5a60078281548110613e4057613e40615a76565b6000918252602090912001546001600160a01b0316614b1d565b8160078281548110613e6e57613e6e615a76565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550613c7e82614ab7565b6040805160c0810182526000808252602080830182905282840182905260608084018390526080840183905260a084015283518085019094528184528301849052909190613ef4826006614bc4565b905080600681518110613f0957613f09615a76565b602002602001015167ffffffffffffffff811115613f2957613f296155b4565b604051908082528060200260200182016040528015613f6e57816020015b6040805180820190915260008082526020820152815260200190600190039081613f475790505b508360a00181905250600081600681518110613f8c57613f8c615a76565b6020026020010181815250506000805b6020840151518451101561410e57613fb384614c7e565b90925090508160011415613fe257613fd2613fcd85614cb8565b614d75565b6001600160a01b03168552613f9c565b816002141561400857613ff484614d80565b67ffffffffffffffff166020860152613f9c565b816003141561402e5761401a84614d80565b67ffffffffffffffff166040860152613f9c565b81600414156140545761404084614d80565b67ffffffffffffffff166060860152613f9c565b816005141561407a5761406684614d80565b67ffffffffffffffff166080860152613f9c565b81600614156140ff5761409461408f85614cb8565b614e02565b8560a00151846006815181106140ac576140ac615a76565b6020026020010151815181106140c4576140c4615a76565b6020026020010181905250826006815181106140e2576140e2615a76565b6020026020010180518091906140f790615a8c565b905250613f9c565b6141098482614ea9565b613f9c565b50505050919050565b60075460009061412990600190615b0f565b905060005b60075481101561423457826001600160a01b03166007828154811061415557614155615a76565b6000918252602090912001546001600160a01b0316141561422257818110156141e6576007828154811061418b5761418b615a76565b600091825260209091200154600780546001600160a01b0390921691839081106141b7576141b7615a76565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b60078054806141f7576141f7615d6b565b600082815260209020810160001990810180546001600160a01b03191690550190556114a883614b1d565b8061422c81615a8c565b91505061412e565b5060405162461bcd60e51b815260206004820152601460248201527f4e6f7420626f6e6465642076616c696461746f720000000000000000000000006044820152606401610c59565b600054600160a01b900460ff166142d65760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610c59565b6000805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600081614331575082613be4565b81613bd78486615aa7565b6001600160a01b03811660009081526001602052604090205460ff166143a45760405162461bcd60e51b815260206004820152601560248201527f4163636f756e74206973206e6f742070617573657200000000000000000000006044820152606401610c59565b6001600160a01b038116600081815260016020908152604091829020805460ff1916905590519182527fcd265ebaf09df2871cc7bd4133404a235ba12eff2041bb89d9c714a2621c7c7e9101610fec565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b03811660009081526001602052604090205460ff16156144ae5760405162461bcd60e51b815260206004820152601960248201527f4163636f756e7420697320616c726561647920706175736572000000000000006044820152606401610c59565b6001600160a01b038116600081815260016020818152604092839020805460ff191690921790915590519182527f6719d08c1888103bea251a4ed56406bd0c3e69723c8a1686e017e7bbe159b6f89101610fec565b600054600160a01b900460ff16156145505760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610c59565b6000805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586143063390565b33600081815260058601602052604081208054909184918391906145b0908490615b0f565b92505081905550828660020160008282546145cb9190615b0f565b92505081905550838660010160008282546145e69190615b0f565b9091555050600286015460018701541480159061460557508054600210155b1561462a578054600287018054600090614620908490615b0f565b9091555050600081555b8054158061464157508054670de0b6b3a764000011155b61468d5760405162461bcd60e51b815260206004820152601b60248201527f6e6f7420656e6f7567682072656d61696e696e672073686172657300000000006044820152606401610c59565b6001865460ff1660038111156146a5576146a56158a8565b1415614733576146df6001600160a01b037f0000000000000000000000004f9254c83eb525f9fcf346490bbb3ed28a81c667168386613da6565b816001600160a01b0316856001600160a01b03167f4d10bd049775c77bd7f255195afba5088028ecb3c7c277d393ccff7934f2f92c8660405161472491815260200190565b60405180910390a350506132d3565b6003865460ff16600381111561474b5761474b6158a8565b14156147955783600460008282546147639190615b0f565b9250508190555061478885866001600160a01b0316846001600160a01b031614612258565b6147955761479585614117565b6002810154600a906147b89063ffffffff80821691640100000000900416615d46565b63ffffffff161061480b5760405162461bcd60e51b815260206004820152601f60248201527f457863656564206d617820756e64656c65676174696f6e20656e7472696573006044820152606401610c59565b60006148208588600301548960040154613bbe565b9050808760040160008282546148369190615a5e565b92505081905550848760030160008282546148519190615a5e565b909155505060028201805463ffffffff6401000000009182900481166000908152600180870160205260409091208581554391810191909155835490939290041690600461489e83615b43565b91906101000a81548163ffffffff021916908363ffffffff16021790555050836001600160a01b0316876001600160a01b03167f2e289e5a72f8e92e344eb866e0a32621f332835d2df2cf1f76e5a345b23cf1ea8a6001015486600001548a61490690615b26565b6040805193845260208401929092529082015260600160405180910390a35050505050505050565b60008151604114156149625760208201516040830151606084015160001a61495886828585614f1b565b935050505061230c565b81516040141561498a57602082015160408301516149818583836150c4565b9250505061230c565b60405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610c59565b6000614a27826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166151079092919063ffffffff16565b8051909150156114a85780806020019051810190614a459190615d81565b6114a85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610c59565b6001600160a01b0381166000908152600860205260408120805460ff191660031781556007810180546fffffffffffffffff00000000000000001916905560018101546004805492939192909190614b10908490615a5e565b9091555060039050612e22565b6001600160a01b03811660009081526008602090815260408220805460ff191660029081178255909252600b90527fa50eece07c7db1631545c0069bd8f5f54d5935e215d59097edf258a44ba9163454614b779043615a5e565b8160070160086101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550806001015460046000828254614bb79190615b0f565b9091555060029050612e22565b8151606090614bd4836001615a5e565b67ffffffffffffffff811115614bec57614bec6155b4565b604051908082528060200260200182016040528015614c15578160200160208202803683370190505b5091506000805b60208601515186511015614c7557614c3386614c7e565b80925081935050506001848381518110614c4f57614c4f615a76565b60200260200101818151614c639190615a5e565b905250614c708682614ea9565b614c1c565b50509092525090565b6000806000614c8c84614d80565b9050614c99600882615ac6565b9250806007166005811115614cb057614cb06158a8565b915050915091565b60606000614cc583614d80565b90506000818460000151614cd99190615a5e565b9050836020015151811115614ced57600080fd5b8167ffffffffffffffff811115614d0657614d066155b4565b6040519080825280601f01601f191660200182016040528015614d30576020820181803683370190505b50602080860151865192955091818601919083010160005b85811015614d6a578181015183820152614d63602082615a5e565b9050614d48565b505050935250919050565b600061230c82615116565b602080820151825181019091015160009182805b600a811015614dfc5783811a9150614dad816007615aa7565b82607f16901b851794508160801660001415614dea57614dce816001615a5e565b86518790614ddd908390615a5e565b9052509395945050505050565b80614df481615a8c565b915050614d94565b50600080fd5b6040805180820182526000808252602080830182905283518085019094528184528301849052909190805b60208301515183511015614ea157614e4483614c7e565b90925090508160011415614e6e57614e5e613fcd84614cb8565b6001600160a01b03168452614e2d565b8160021415614e9257614e88614e8384614cb8565b61513e565b6020850152614e2d565b614e9c8382614ea9565b614e2d565b505050919050565b6000816005811115614ebd57614ebd6158a8565b1415614ecc576114a882614d80565b6002816005811115614ee057614ee06158a8565b1415610397576000614ef183614d80565b90508083600001818151614f059190615a5e565b905250602083015151835111156114a857600080fd5b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0821115614f985760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610c59565b8360ff16601b1480614fad57508360ff16601c145b6150045760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b6064820152608401610c59565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa158015615058573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166150bb5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610c59565b95945050505050565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821660ff83901c601b016150fd86828785614f1b565b9695505050505050565b6060613be18484600085615175565b6000815160141461512657600080fd5b50602001516c01000000000000000000000000900490565b600060208251111561514f57600080fd5b60208201519050815160206151649190615b0f565b61516f906008615aa7565b1c919050565b6060824710156151ed5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610c59565b843b61523b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610c59565b600080866001600160a01b031685876040516152579190615d9e565b60006040518083038185875af1925050503d8060008114615294576040519150601f19603f3d011682016040523d82523d6000602084013e615299565b606091505b50915091506152a98282866152b4565b979650505050505050565b606083156152c3575081613be4565b8251156152d35782518084602001fd5b8160405162461bcd60e51b8152600401610c599190615dba565b80356001600160a01b038116811461530457600080fd5b919050565b6000806040838503121561531c57600080fd5b615325836152ed565b946020939093013593505050565b80151581146110cc57600080fd5b60006020828403121561535357600080fd5b8135613be481615333565b60006020828403121561537057600080fd5b613be4826152ed565b60006020828403121561538b57600080fd5b5035919050565b60008083601f8401126153a457600080fd5b50813567ffffffffffffffff8111156153bc57600080fd5b6020830191508360208285010111156153d457600080fd5b9250929050565b60008083601f8401126153ed57600080fd5b50813567ffffffffffffffff81111561540557600080fd5b6020830191508360208260051b85010111156153d457600080fd5b6000806000806040858703121561543657600080fd5b843567ffffffffffffffff8082111561544e57600080fd5b61545a88838901615392565b9096509450602087013591508082111561547357600080fd5b50615480878288016153db565b95989497509550505050565b602080825282518282018190526000919060409081850190868401855b828110156154d757815180516001600160a01b031685528601518685015292840192908501906001016154a9565b5091979650505050505050565b80356009811061530457600080fd5b60006020828403121561550557600080fd5b613be4826154e4565b6000806040838503121561552157600080fd5b61552a836152ed565b9150602083013561553a81615333565b809150509250929050565b803567ffffffffffffffff8116811461530457600080fd5b60006020828403121561556f57600080fd5b613be482615545565b60008060006060848603121561558d57600080fd5b615596846152ed565b9250602084013591506155ab60408501615545565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156155f3576155f36155b4565b604052919050565b600082601f83011261560c57600080fd5b813567ffffffffffffffff811115615626576156266155b4565b615639601f8201601f19166020016155ca565b81815284602083860101111561564e57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060008060006080888a03121561568657600080fd5b873567ffffffffffffffff8082111561569e57600080fd5b6156aa8b838c016155fb565b985060208a01359150808211156156c057600080fd5b6156cc8b838c016153db565b909850965060408a01359150808211156156e557600080fd5b6156f18b838c016153db565b909650945060608a013591508082111561570a57600080fd5b506157178a828b016153db565b989b979a50959850939692959293505050565b600067ffffffffffffffff80841115615745576157456155b4565b8360051b60206157568183016155ca565b8681529350908401908084018783111561576f57600080fd5b855b838110156157a3578035858111156157895760008081fd5b6157958a828a016155fb565b835250908201908201615771565b50505050509392505050565b600080604083850312156157c257600080fd5b823567ffffffffffffffff808211156157da57600080fd5b6157e6868387016155fb565b935060208501359150808211156157fc57600080fd5b508301601f8101851361580e57600080fd5b61581d8582356020840161572a565b9150509250929050565b60008060008060006060868803121561583f57600080fd5b615848866152ed565b9450602086013567ffffffffffffffff8082111561586557600080fd5b61587189838a01615392565b9096509450604088013591508082111561588a57600080fd5b5061589788828901615392565b969995985093965092949392505050565b634e487b7160e01b600052602160045260246000fd5b600481106158dc57634e487b7160e01b600052602160045260246000fd5b9052565b6020810161230c82846158be565b6000806040838503121561590157600080fd5b615325836154e4565b6000806040838503121561591d57600080fd5b615926836152ed565b9150615934602084016152ed565b90509250929050565b6000602080835260e083016001600160a01b038551168285015281850151604081818701528087015160608701526060870151915060c06080870152828251808552610100880191508584019450600093505b808410156159b95784518051835286015186830152938501936001939093019290820190615990565b50608088015160a088015260a088015160c0880152809550505050505092915050565b61014081016159eb828d6158be565b6001600160a01b039a909a16602082015260408101989098526060880196909652608087019490945260a086019290925260c085015267ffffffffffffffff90811660e08501529081166101008401521661012090910152919050565b634e487b7160e01b600052601160045260246000fd5b60008219821115615a7157615a71615a48565b500190565b634e487b7160e01b600052603260045260246000fd5b6000600019821415615aa057615aa0615a48565b5060010190565b6000816000190483118215151615615ac157615ac1615a48565b500290565b600082615ae357634e487b7160e01b600052601260045260246000fd5b500490565b838152818360208301376000910160200190815292915050565b6000613be436848461572a565b600082821015615b2157615b21615a48565b500390565b6000600160ff1b821415615b3c57615b3c615a48565b5060000390565b600063ffffffff80831681811415615b5d57615b5d615a48565b6001019392505050565b60005b83811015615b82578181015183820152602001615b6a565b838111156132d35750506000910152565b60008151808452615bab816020860160208601615b67565b601f01601f19169290920160200192915050565b60608152600a60608201527f636f6d6d697373696f6e00000000000000000000000000000000000000000000608082015260a060208201526000615c0660a0830185615b93565b90506001600160a01b03831660408301529392505050565b6060815260046060820152631a5b9a5d60e21b608082015260a060208201526000615c0660a0830185615b93565b60608152601360608201527f6d696e2d73656c662d64656c65676174696f6e00000000000000000000000000608082015260a060208201526000615c0660a0830185615b93565b60608152600660608201527f7369676e65720000000000000000000000000000000000000000000000000000608082015260a060208201526000615c0660a0830185615b93565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b606081526000615d17606083018789615cda565b8281036020840152615d2a818688615cda565b9150506001600160a01b03831660408301529695505050505050565b600063ffffffff83811690831681811015615d6357615d63615a48565b039392505050565b634e487b7160e01b600052603160045260246000fd5b600060208284031215615d9357600080fd5b8151613be481615333565b60008251615db0818460208701615b67565b9190910192915050565b602081526000613be46020830184615b9356fe4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572a26469706673582212201e41fbe148ca51c46448ba41ffc013bb54a58fc4e688ebf2d9e4fc84d2b1e7d164736f6c63430008090033
Verified Source Code Full Match
Compiler: v0.8.9+commit.e5eed63a
EVM: london
Optimization: Yes (800 runs)
Pauser.sol 58 lines
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.9;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
abstract contract Pauser is Ownable, Pausable {
mapping(address => bool) public pausers;
event PauserAdded(address account);
event PauserRemoved(address account);
constructor() {
_addPauser(msg.sender);
}
modifier onlyPauser() {
require(isPauser(msg.sender), "Caller is not pauser");
_;
}
function pause() public onlyPauser {
_pause();
}
function unpause() public onlyPauser {
_unpause();
}
function isPauser(address account) public view returns (bool) {
return pausers[account];
}
function addPauser(address account) public onlyOwner {
_addPauser(account);
}
function removePauser(address account) public onlyOwner {
_removePauser(account);
}
function renouncePauser() public {
_removePauser(msg.sender);
}
function _addPauser(address account) private {
require(!isPauser(account), "Account is already pauser");
pausers[account] = true;
emit PauserAdded(account);
}
function _removePauser(address account) private {
require(isPauser(account), "Account is not pauser");
pausers[account] = false;
emit PauserRemoved(account);
}
}
Staking.sol 769 lines
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.9;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {DataTypes as dt} from "./libraries/DataTypes.sol";
import "./interfaces/ISigsVerifier.sol";
import "./libraries/PbStaking.sol";
import "./Whitelist.sol";
import "./Pauser.sol";
/**
* @title A Staking contract shared by all external sidechains and apps
*/
contract Staking is ISigsVerifier, Pauser, Whitelist {
using SafeERC20 for IERC20;
using ECDSA for bytes32;
IERC20 public immutable CELER_TOKEN;
uint256 public bondedTokens;
uint256 public nextBondBlock;
address[] public valAddrs;
address[] public bondedValAddrs;
mapping(address => dt.Validator) public validators; // key is valAddr
mapping(address => address) public signerVals; // signerAddr -> valAddr
mapping(uint256 => bool) public slashNonces;
mapping(dt.ParamName => uint256) public params;
address public govContract;
address public rewardContract;
uint256 public forfeiture;
/* Events */
event ValidatorNotice(address indexed valAddr, string key, bytes data, address from);
event ValidatorStatusUpdate(address indexed valAddr, dt.ValidatorStatus indexed status);
event DelegationUpdate(
address indexed valAddr,
address indexed delAddr,
uint256 valTokens,
uint256 delShares,
int256 tokenDiff
);
event Undelegated(address indexed valAddr, address indexed delAddr, uint256 amount);
event Slash(address indexed valAddr, uint64 nonce, uint256 slashAmt);
event SlashAmtCollected(address indexed recipient, uint256 amount);
/**
* @notice Staking constructor
* @param _celerTokenAddress address of Celer Token Contract
* @param _proposalDeposit required deposit amount for a governance proposal
* @param _votingPeriod voting timeout for a governance proposal
* @param _unbondingPeriod the locking time for funds locked before withdrawn
* @param _maxBondedValidators the maximum number of bonded validators
* @param _minValidatorTokens the global minimum token amount requirement for bonded validator
* @param _minSelfDelegation minimal amount of self-delegated tokens
* @param _advanceNoticePeriod the wait time after the announcement and prior to the effective date of an update
* @param _validatorBondInterval min interval between bondValidator
* @param _maxSlashFactor maximal slashing factor (1e6 = 100%)
*/
constructor(
address _celerTokenAddress,
uint256 _proposalDeposit,
uint256 _votingPeriod,
uint256 _unbondingPeriod,
uint256 _maxBondedValidators,
uint256 _minValidatorTokens,
uint256 _minSelfDelegation,
uint256 _advanceNoticePeriod,
uint256 _validatorBondInterval,
uint256 _maxSlashFactor
) {
CELER_TOKEN = IERC20(_celerTokenAddress);
params[dt.ParamName.ProposalDeposit] = _proposalDeposit;
params[dt.ParamName.VotingPeriod] = _votingPeriod;
params[dt.ParamName.UnbondingPeriod] = _unbondingPeriod;
params[dt.ParamName.MaxBondedValidators] = _maxBondedValidators;
params[dt.ParamName.MinValidatorTokens] = _minValidatorTokens;
params[dt.ParamName.MinSelfDelegation] = _minSelfDelegation;
params[dt.ParamName.AdvanceNoticePeriod] = _advanceNoticePeriod;
params[dt.ParamName.ValidatorBondInterval] = _validatorBondInterval;
params[dt.ParamName.MaxSlashFactor] = _maxSlashFactor;
}
receive() external payable {}
/*********************************
* External and Public Functions *
*********************************/
/**
* @notice Initialize a validator candidate
* @param _signer signer address
* @param _minSelfDelegation minimal amount of tokens staked by the validator itself
* @param _commissionRate the self-declaimed commission rate
*/
function initializeValidator(
address _signer,
uint256 _minSelfDelegation,
uint64 _commissionRate
) external whenNotPaused onlyWhitelisted {
address valAddr = msg.sender;
dt.Validator storage validator = validators[valAddr];
require(validator.status == dt.ValidatorStatus.Null, "Validator is initialized");
require(validators[_signer].status == dt.ValidatorStatus.Null, "Signer is other validator");
require(signerVals[valAddr] == address(0), "Validator is other signer");
require(signerVals[_signer] == address(0), "Signer already used");
require(_commissionRate <= dt.COMMISSION_RATE_BASE, "Invalid commission rate");
require(_minSelfDelegation >= params[dt.ParamName.MinSelfDelegation], "Insufficient min self delegation");
validator.signer = _signer;
validator.status = dt.ValidatorStatus.Unbonded;
validator.minSelfDelegation = _minSelfDelegation;
validator.commissionRate = _commissionRate;
valAddrs.push(valAddr);
signerVals[_signer] = valAddr;
delegate(valAddr, _minSelfDelegation);
emit ValidatorNotice(valAddr, "init", abi.encode(_signer, _minSelfDelegation, _commissionRate), address(0));
}
/**
* @notice Update validator signer address
* @param _signer signer address
*/
function updateValidatorSigner(address _signer) external {
address valAddr = msg.sender;
dt.Validator storage validator = validators[valAddr];
require(validator.status != dt.ValidatorStatus.Null, "Validator not initialized");
require(signerVals[_signer] == address(0), "Signer already used");
if (_signer != valAddr) {
require(validators[_signer].status == dt.ValidatorStatus.Null, "Signer is other validator");
}
delete signerVals[validator.signer];
validator.signer = _signer;
signerVals[_signer] = valAddr;
emit ValidatorNotice(valAddr, "signer", abi.encode(_signer), address(0));
}
/**
* @notice Candidate claims to become a bonded validator
* @dev caller can be either validator owner or signer
*/
function bondValidator() external {
address valAddr = msg.sender;
if (signerVals[msg.sender] != address(0)) {
valAddr = signerVals[msg.sender];
}
dt.Validator storage validator = validators[valAddr];
require(
validator.status == dt.ValidatorStatus.Unbonded || validator.status == dt.ValidatorStatus.Unbonding,
"Invalid validator status"
);
require(block.number >= validator.bondBlock, "Bond block not reached");
require(block.number >= nextBondBlock, "Too frequent validator bond");
nextBondBlock = block.number + params[dt.ParamName.ValidatorBondInterval];
require(hasMinRequiredTokens(valAddr, true), "Not have min tokens");
uint256 maxBondedValidators = params[dt.ParamName.MaxBondedValidators];
// if the number of validators has not reached the max_validator_num,
// add validator directly
if (bondedValAddrs.length < maxBondedValidators) {
_bondValidator(valAddr);
_decentralizationCheck(validator.tokens);
return;
}
// if the number of validators has already reached the max_validator_num,
// add validator only if its tokens is more than the current least bonded validator tokens
uint256 minTokens = dt.MAX_INT;
uint256 minTokensIndex;
for (uint256 i = 0; i < maxBondedValidators; i++) {
if (validators[bondedValAddrs[i]].tokens < minTokens) {
minTokensIndex = i;
minTokens = validators[bondedValAddrs[i]].tokens;
if (minTokens == 0) {
break;
}
}
}
require(validator.tokens > minTokens, "Insufficient tokens");
_replaceBondedValidator(valAddr, minTokensIndex);
_decentralizationCheck(validator.tokens);
}
/**
* @notice Confirm validator status from Unbonding to Unbonded
* @param _valAddr the address of the validator
*/
function confirmUnbondedValidator(address _valAddr) external {
dt.Validator storage validator = validators[_valAddr];
require(validator.status == dt.ValidatorStatus.Unbonding, "Validator not unbonding");
require(block.number >= validator.unbondBlock, "Unbond block not reached");
validator.status = dt.ValidatorStatus.Unbonded;
delete validator.unbondBlock;
emit ValidatorStatusUpdate(_valAddr, dt.ValidatorStatus.Unbonded);
}
/**
* @notice Delegate CELR tokens to a validator
* @dev Minimal amount per delegate operation is 1 CELR
* @param _valAddr validator to delegate
* @param _tokens the amount of delegated CELR tokens
*/
function delegate(address _valAddr, uint256 _tokens) public whenNotPaused {
address delAddr = msg.sender;
require(_tokens >= dt.CELR_DECIMAL, "Minimal amount is 1 CELR");
dt.Validator storage validator = validators[_valAddr];
require(validator.status != dt.ValidatorStatus.Null, "Validator is not initialized");
uint256 shares = _tokenToShare(_tokens, validator.tokens, validator.shares);
dt.Delegator storage delegator = validator.delegators[delAddr];
delegator.shares += shares;
validator.shares += shares;
validator.tokens += _tokens;
if (validator.status == dt.ValidatorStatus.Bonded) {
bondedTokens += _tokens;
_decentralizationCheck(validator.tokens);
}
CELER_TOKEN.safeTransferFrom(delAddr, address(this), _tokens);
emit DelegationUpdate(_valAddr, delAddr, validator.tokens, delegator.shares, int256(_tokens));
}
/**
* @notice Undelegate shares from a validator
* @dev Tokens are delegated by the msgSender to the validator
* @param _valAddr the address of the validator
* @param _shares undelegate shares
*/
function undelegateShares(address _valAddr, uint256 _shares) external {
require(_shares >= dt.CELR_DECIMAL, "Minimal amount is 1 share");
dt.Validator storage validator = validators[_valAddr];
require(validator.status != dt.ValidatorStatus.Null, "Validator is not initialized");
uint256 tokens = _shareToToken(_shares, validator.tokens, validator.shares);
_undelegate(validator, _valAddr, tokens, _shares);
}
/**
* @notice Undelegate shares from a validator
* @dev Tokens are delegated by the msgSender to the validator
* @param _valAddr the address of the validator
* @param _tokens undelegate tokens
*/
function undelegateTokens(address _valAddr, uint256 _tokens) external {
require(_tokens >= dt.CELR_DECIMAL, "Minimal amount is 1 CELR");
dt.Validator storage validator = validators[_valAddr];
require(validator.status != dt.ValidatorStatus.Null, "Validator is not initialized");
uint256 shares = _tokenToShare(_tokens, validator.tokens, validator.shares);
_undelegate(validator, _valAddr, _tokens, shares);
}
/**
* @notice Complete pending undelegations from a validator
* @param _valAddr the address of the validator
*/
function completeUndelegate(address _valAddr) external {
address delAddr = msg.sender;
dt.Validator storage validator = validators[_valAddr];
require(validator.status != dt.ValidatorStatus.Null, "Validator is not initialized");
dt.Delegator storage delegator = validator.delegators[delAddr];
uint256 unbondingPeriod = params[dt.ParamName.UnbondingPeriod];
bool isUnbonded = validator.status == dt.ValidatorStatus.Unbonded;
// for all pending undelegations
uint32 i;
uint256 undelegationShares;
for (i = delegator.undelegations.head; i < delegator.undelegations.tail; i++) {
if (isUnbonded || delegator.undelegations.queue[i].creationBlock + unbondingPeriod <= block.number) {
// complete undelegation when the validator becomes unbonded or
// the unbondingPeriod for the pending undelegation is up.
undelegationShares += delegator.undelegations.queue[i].shares;
delete delegator.undelegations.queue[i];
continue;
}
break;
}
delegator.undelegations.head = i;
require(undelegationShares > 0, "No undelegation ready to be completed");
uint256 tokens = _shareToToken(undelegationShares, validator.undelegationTokens, validator.undelegationShares);
validator.undelegationShares -= undelegationShares;
validator.undelegationTokens -= tokens;
CELER_TOKEN.safeTransfer(delAddr, tokens);
emit Undelegated(_valAddr, delAddr, tokens);
}
/**
* @notice Update commission rate
* @param _newRate new commission rate
*/
function updateCommissionRate(uint64 _newRate) external {
address valAddr = msg.sender;
dt.Validator storage validator = validators[valAddr];
require(validator.status != dt.ValidatorStatus.Null, "Validator is not initialized");
require(_newRate <= dt.COMMISSION_RATE_BASE, "Invalid new rate");
validator.commissionRate = _newRate;
emit ValidatorNotice(valAddr, "commission", abi.encode(_newRate), address(0));
}
/**
* @notice Update minimal self delegation value
* @param _minSelfDelegation minimal amount of tokens staked by the validator itself
*/
function updateMinSelfDelegation(uint256 _minSelfDelegation) external {
address valAddr = msg.sender;
dt.Validator storage validator = validators[valAddr];
require(validator.status != dt.ValidatorStatus.Null, "Validator is not initialized");
require(_minSelfDelegation >= params[dt.ParamName.MinSelfDelegation], "Insufficient min self delegation");
if (_minSelfDelegation < validator.minSelfDelegation) {
require(validator.status != dt.ValidatorStatus.Bonded, "Validator is bonded");
validator.bondBlock = uint64(block.number + params[dt.ParamName.AdvanceNoticePeriod]);
}
validator.minSelfDelegation = _minSelfDelegation;
emit ValidatorNotice(valAddr, "min-self-delegation", abi.encode(_minSelfDelegation), address(0));
}
/**
* @notice Slash a validator and its delegators
* @param _slashRequest slash request bytes coded in protobuf
* @param _sigs list of validator signatures
*/
function slash(bytes calldata _slashRequest, bytes[] calldata _sigs) external whenNotPaused {
bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), "Slash"));
verifySignatures(abi.encodePacked(domain, _slashRequest), _sigs);
PbStaking.Slash memory request = PbStaking.decSlash(_slashRequest);
require(block.timestamp < request.expireTime, "Slash expired");
require(request.slashFactor <= dt.SLASH_FACTOR_DECIMAL, "Invalid slash factor");
require(request.slashFactor <= params[dt.ParamName.MaxSlashFactor], "Exceed max slash factor");
require(!slashNonces[request.nonce], "Used slash nonce");
slashNonces[request.nonce] = true;
address valAddr = request.validator;
dt.Validator storage validator = validators[valAddr];
require(
validator.status == dt.ValidatorStatus.Bonded || validator.status == dt.ValidatorStatus.Unbonding,
"Invalid validator status"
);
// slash delegated tokens
uint256 slashAmt = (validator.tokens * request.slashFactor) / dt.SLASH_FACTOR_DECIMAL;
validator.tokens -= slashAmt;
if (validator.status == dt.ValidatorStatus.Bonded) {
bondedTokens -= slashAmt;
if (request.jailPeriod > 0 || !hasMinRequiredTokens(valAddr, true)) {
_unbondValidator(valAddr);
}
}
if (validator.status == dt.ValidatorStatus.Unbonding && request.jailPeriod > 0) {
validator.bondBlock = uint64(block.number + request.jailPeriod);
}
emit DelegationUpdate(valAddr, address(0), validator.tokens, 0, -int256(slashAmt));
// slash pending undelegations
uint256 slashUndelegation = (validator.undelegationTokens * request.slashFactor) / dt.SLASH_FACTOR_DECIMAL;
validator.undelegationTokens -= slashUndelegation;
slashAmt += slashUndelegation;
uint256 collectAmt;
for (uint256 i = 0; i < request.collectors.length; i++) {
PbStaking.AcctAmtPair memory collector = request.collectors[i];
if (collectAmt + collector.amount > slashAmt) {
collector.amount = slashAmt - collectAmt;
}
if (collector.amount > 0) {
collectAmt += collector.amount;
if (collector.account == address(0)) {
CELER_TOKEN.safeTransfer(msg.sender, collector.amount);
emit SlashAmtCollected(msg.sender, collector.amount);
} else {
CELER_TOKEN.safeTransfer(collector.account, collector.amount);
emit SlashAmtCollected(collector.account, collector.amount);
}
}
}
forfeiture += slashAmt - collectAmt;
emit Slash(valAddr, request.nonce, slashAmt);
}
function collectForfeiture() external {
require(forfeiture > 0, "Nothing to collect");
CELER_TOKEN.safeTransfer(rewardContract, forfeiture);
forfeiture = 0;
}
/**
* @notice Validator notice event, could be triggered by anyone
*/
function validatorNotice(
address _valAddr,
string calldata _key,
bytes calldata _data
) external {
dt.Validator storage validator = validators[_valAddr];
require(validator.status != dt.ValidatorStatus.Null, "Validator is not initialized");
emit ValidatorNotice(_valAddr, _key, _data, msg.sender);
}
function setParamValue(dt.ParamName _name, uint256 _value) external {
require(msg.sender == govContract, "Caller is not gov contract");
if (_name == dt.ParamName.MaxBondedValidators) {
require(bondedValAddrs.length <= _value, "invalid value");
}
params[_name] = _value;
}
function setGovContract(address _addr) external onlyOwner {
govContract = _addr;
}
function setRewardContract(address _addr) external onlyOwner {
rewardContract = _addr;
}
/**
* @notice Set max slash factor
*/
function setMaxSlashFactor(uint256 _maxSlashFactor) external onlyOwner {
params[dt.ParamName.MaxSlashFactor] = _maxSlashFactor;
}
/**
* @notice Owner drains tokens when the contract is paused
* @dev emergency use only
* @param _amount drained token amount
*/
function drainToken(uint256 _amount) external whenPaused onlyOwner {
CELER_TOKEN.safeTransfer(msg.sender, _amount);
}
/**************************
* Public View Functions *
**************************/
/**
* @notice Validate if a message is signed by quorum tokens
* @param _msg signed message
* @param _sigs list of validator signatures
*/
function verifySignatures(bytes memory _msg, bytes[] memory _sigs) public view returns (bool) {
bytes32 hash = keccak256(_msg).toEthSignedMessageHash();
uint256 signedTokens;
address prev = address(0);
uint256 quorum = getQuorumTokens();
for (uint256 i = 0; i < _sigs.length; i++) {
address signer = hash.recover(_sigs[i]);
require(signer > prev, "Signers not in ascending order");
prev = signer;
dt.Validator storage validator = validators[signerVals[signer]];
if (validator.status != dt.ValidatorStatus.Bonded) {
continue;
}
signedTokens += validator.tokens;
if (signedTokens >= quorum) {
return true;
}
}
revert("Quorum not reached");
}
/**
* @notice Verifies that a message is signed by a quorum among the validators.
* @param _msg signed message
* @param _sigs the list of signatures
*/
function verifySigs(
bytes memory _msg,
bytes[] calldata _sigs,
address[] calldata,
uint256[] calldata
) public view override {
require(verifySignatures(_msg, _sigs), "Failed to verify sigs");
}
/**
* @notice Get quorum amount of tokens
* @return the quorum amount
*/
function getQuorumTokens() public view returns (uint256) {
return (bondedTokens * 2) / 3 + 1;
}
/**
* @notice Get validator info
* @param _valAddr the address of the validator
* @return Validator token amount
*/
function getValidatorTokens(address _valAddr) public view returns (uint256) {
return validators[_valAddr].tokens;
}
/**
* @notice Get validator info
* @param _valAddr the address of the validator
* @return Validator status
*/
function getValidatorStatus(address _valAddr) public view returns (dt.ValidatorStatus) {
return validators[_valAddr].status;
}
/**
* @notice Check the given address is a validator or not
* @param _addr the address to check
* @return the given address is a validator or not
*/
function isBondedValidator(address _addr) public view returns (bool) {
return validators[_addr].status == dt.ValidatorStatus.Bonded;
}
/**
* @notice Get the number of validators
* @return the number of validators
*/
function getValidatorNum() public view returns (uint256) {
return valAddrs.length;
}
/**
* @notice Get the number of bonded validators
* @return the number of bonded validators
*/
function getBondedValidatorNum() public view returns (uint256) {
return bondedValAddrs.length;
}
/**
* @return addresses and token amounts of bonded validators
*/
function getBondedValidatorsTokens() public view returns (dt.ValidatorTokens[] memory) {
dt.ValidatorTokens[] memory infos = new dt.ValidatorTokens[](bondedValAddrs.length);
for (uint256 i = 0; i < bondedValAddrs.length; i++) {
address valAddr = bondedValAddrs[i];
infos[i] = dt.ValidatorTokens(valAddr, validators[valAddr].tokens);
}
return infos;
}
/**
* @notice Check if min token requirements are met
* @param _valAddr the address of the validator
* @param _checkSelfDelegation check self delegation
*/
function hasMinRequiredTokens(address _valAddr, bool _checkSelfDelegation) public view returns (bool) {
dt.Validator storage v = validators[_valAddr];
uint256 valTokens = v.tokens;
if (valTokens < params[dt.ParamName.MinValidatorTokens]) {
return false;
}
if (_checkSelfDelegation) {
uint256 selfDelegation = _shareToToken(v.delegators[_valAddr].shares, valTokens, v.shares);
if (selfDelegation < v.minSelfDelegation) {
return false;
}
}
return true;
}
/**
* @notice Get the delegator info of a specific validator
* @param _valAddr the address of the validator
* @param _delAddr the address of the delegator
* @return DelegatorInfo from the given validator
*/
function getDelegatorInfo(address _valAddr, address _delAddr) public view returns (dt.DelegatorInfo memory) {
dt.Validator storage validator = validators[_valAddr];
dt.Delegator storage d = validator.delegators[_delAddr];
uint256 tokens = _shareToToken(d.shares, validator.tokens, validator.shares);
uint256 undelegationShares;
uint256 withdrawableUndelegationShares;
uint256 unbondingPeriod = params[dt.ParamName.UnbondingPeriod];
bool isUnbonded = validator.status == dt.ValidatorStatus.Unbonded;
uint256 len = d.undelegations.tail - d.undelegations.head;
dt.Undelegation[] memory undelegations = new dt.Undelegation[](len);
for (uint256 i = 0; i < len; i++) {
undelegations[i] = d.undelegations.queue[i + d.undelegations.head];
undelegationShares += undelegations[i].shares;
if (isUnbonded || undelegations[i].creationBlock + unbondingPeriod <= block.number) {
withdrawableUndelegationShares += undelegations[i].shares;
}
}
uint256 undelegationTokens = _shareToToken(
undelegationShares,
validator.undelegationTokens,
validator.undelegationShares
);
uint256 withdrawableUndelegationTokens = _shareToToken(
withdrawableUndelegationShares,
validator.undelegationTokens,
validator.undelegationShares
);
return
dt.DelegatorInfo(
_valAddr,
tokens,
d.shares,
undelegations,
undelegationTokens,
withdrawableUndelegationTokens
);
}
/**
* @notice Get the value of a specific uint parameter
* @param _name the key of this parameter
* @return the value of this parameter
*/
function getParamValue(dt.ParamName _name) public view returns (uint256) {
return params[_name];
}
/*********************
* Private Functions *
*********************/
function _undelegate(
dt.Validator storage validator,
address _valAddr,
uint256 _tokens,
uint256 _shares
) private {
address delAddr = msg.sender;
dt.Delegator storage delegator = validator.delegators[delAddr];
delegator.shares -= _shares;
validator.shares -= _shares;
validator.tokens -= _tokens;
if (validator.tokens != validator.shares && delegator.shares <= 2) {
// Remove residual share caused by rounding error when total shares and tokens are not equal
validator.shares -= delegator.shares;
delegator.shares = 0;
}
require(delegator.shares == 0 || delegator.shares >= dt.CELR_DECIMAL, "not enough remaining shares");
if (validator.status == dt.ValidatorStatus.Unbonded) {
CELER_TOKEN.safeTransfer(delAddr, _tokens);
emit Undelegated(_valAddr, delAddr, _tokens);
return;
} else if (validator.status == dt.ValidatorStatus.Bonded) {
bondedTokens -= _tokens;
if (!hasMinRequiredTokens(_valAddr, delAddr == _valAddr)) {
_unbondValidator(_valAddr);
}
}
require(
delegator.undelegations.tail - delegator.undelegations.head < dt.MAX_UNDELEGATION_ENTRIES,
"Exceed max undelegation entries"
);
uint256 undelegationShares = _tokenToShare(_tokens, validator.undelegationTokens, validator.undelegationShares);
validator.undelegationShares += undelegationShares;
validator.undelegationTokens += _tokens;
dt.Undelegation storage undelegation = delegator.undelegations.queue[delegator.undelegations.tail];
undelegation.shares = undelegationShares;
undelegation.creationBlock = block.number;
delegator.undelegations.tail++;
emit DelegationUpdate(_valAddr, delAddr, validator.tokens, delegator.shares, -int256(_tokens));
}
/**
* @notice Set validator to bonded
* @param _valAddr the address of the validator
*/
function _setBondedValidator(address _valAddr) private {
dt.Validator storage validator = validators[_valAddr];
validator.status = dt.ValidatorStatus.Bonded;
delete validator.unbondBlock;
bondedTokens += validator.tokens;
emit ValidatorStatusUpdate(_valAddr, dt.ValidatorStatus.Bonded);
}
/**
* @notice Set validator to unbonding
* @param _valAddr the address of the validator
*/
function _setUnbondingValidator(address _valAddr) private {
dt.Validator storage validator = validators[_valAddr];
validator.status = dt.ValidatorStatus.Unbonding;
validator.unbondBlock = uint64(block.number + params[dt.ParamName.UnbondingPeriod]);
bondedTokens -= validator.tokens;
emit ValidatorStatusUpdate(_valAddr, dt.ValidatorStatus.Unbonding);
}
/**
* @notice Bond a validator
* @param _valAddr the address of the validator
*/
function _bondValidator(address _valAddr) private {
bondedValAddrs.push(_valAddr);
_setBondedValidator(_valAddr);
}
/**
* @notice Replace a bonded validator
* @param _valAddr the address of the new validator
* @param _index the index of the validator to be replaced
*/
function _replaceBondedValidator(address _valAddr, uint256 _index) private {
_setUnbondingValidator(bondedValAddrs[_index]);
bondedValAddrs[_index] = _valAddr;
_setBondedValidator(_valAddr);
}
/**
* @notice Unbond a validator
* @param _valAddr validator to be removed
*/
function _unbondValidator(address _valAddr) private {
uint256 lastIndex = bondedValAddrs.length - 1;
for (uint256 i = 0; i < bondedValAddrs.length; i++) {
if (bondedValAddrs[i] == _valAddr) {
if (i < lastIndex) {
bondedValAddrs[i] = bondedValAddrs[lastIndex];
}
bondedValAddrs.pop();
_setUnbondingValidator(_valAddr);
return;
}
}
revert("Not bonded validator");
}
/**
* @notice Check if one validator as too much power
* @param _valTokens token amounts of the validator
*/
function _decentralizationCheck(uint256 _valTokens) private view {
uint256 bondedValNum = bondedValAddrs.length;
if (bondedValNum == 2 || bondedValNum == 3) {
require(_valTokens < getQuorumTokens(), "Single validator should not have quorum tokens");
} else if (bondedValNum > 3) {
require(_valTokens < bondedTokens / 3, "Single validator should not have 1/3 tokens");
}
}
/**
* @notice Convert token to share
*/
function _tokenToShare(
uint256 tokens,
uint256 totalTokens,
uint256 totalShares
) private pure returns (uint256) {
if (totalTokens == 0) {
return tokens;
}
return (tokens * totalShares) / totalTokens;
}
/**
* @notice Convert share to token
*/
function _shareToToken(
uint256 shares,
uint256 totalTokens,
uint256 totalShares
) private pure returns (uint256) {
if (totalShares == 0) {
return shares;
}
return (shares * totalTokens) / totalShares;
}
}
Whitelist.sol 52 lines
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.9;
import "@openzeppelin/contracts/access/Ownable.sol";
abstract contract Whitelist is Ownable {
mapping(address => bool) public whitelist;
bool public whitelistEnabled;
event WhitelistedAdded(address account);
event WhitelistedRemoved(address account);
modifier onlyWhitelisted() {
if (whitelistEnabled) {
require(isWhitelisted(msg.sender), "Caller is not whitelisted");
}
_;
}
/**
* @notice Set whitelistEnabled
*/
function setWhitelistEnabled(bool _whitelistEnabled) external onlyOwner {
whitelistEnabled = _whitelistEnabled;
}
/**
* @notice Add an account to whitelist
*/
function addWhitelisted(address account) external onlyOwner {
require(!isWhitelisted(account), "Already whitelisted");
whitelist[account] = true;
emit WhitelistedAdded(account);
}
/**
* @notice Remove an account from whitelist
*/
function removeWhitelisted(address account) external onlyOwner {
require(isWhitelisted(account), "Not whitelisted");
whitelist[account] = false;
emit WhitelistedRemoved(account);
}
/**
* @return is account whitelisted
*/
function isWhitelisted(address account) public view returns (bool) {
return whitelist[account];
}
}
Pb.sol 192 lines
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.9;
// runtime proto sol library
library Pb {
enum WireType {
Varint,
Fixed64,
LengthDelim,
StartGroup,
EndGroup,
Fixed32
}
struct Buffer {
uint256 idx; // the start index of next read. when idx=b.length, we're done
bytes b; // hold serialized proto msg, readonly
}
// create a new in-memory Buffer object from raw msg bytes
function fromBytes(bytes memory raw) internal pure returns (Buffer memory buf) {
buf.b = raw;
buf.idx = 0;
}
// whether there are unread bytes
function hasMore(Buffer memory buf) internal pure returns (bool) {
return buf.idx < buf.b.length;
}
// decode current field number and wiretype
function decKey(Buffer memory buf) internal pure returns (uint256 tag, WireType wiretype) {
uint256 v = decVarint(buf);
tag = v / 8;
wiretype = WireType(v & 7);
}
// count tag occurrences, return an array due to no memory map support
// have to create array for (maxtag+1) size. cnts[tag] = occurrences
// should keep buf.idx unchanged because this is only a count function
function cntTags(Buffer memory buf, uint256 maxtag) internal pure returns (uint256[] memory cnts) {
uint256 originalIdx = buf.idx;
cnts = new uint256[](maxtag + 1); // protobuf's tags are from 1 rather than 0
uint256 tag;
WireType wire;
while (hasMore(buf)) {
(tag, wire) = decKey(buf);
cnts[tag] += 1;
skipValue(buf, wire);
}
buf.idx = originalIdx;
}
// read varint from current buf idx, move buf.idx to next read, return the int value
function decVarint(Buffer memory buf) internal pure returns (uint256 v) {
bytes10 tmp; // proto int is at most 10 bytes (7 bits can be used per byte)
bytes memory bb = buf.b; // get buf.b mem addr to use in assembly
v = buf.idx; // use v to save one additional uint variable
assembly {
tmp := mload(add(add(bb, 32), v)) // load 10 bytes from buf.b[buf.idx] to tmp
}
uint256 b; // store current byte content
v = 0; // reset to 0 for return value
for (uint256 i = 0; i < 10; i++) {
assembly {
b := byte(i, tmp) // don't use tmp[i] because it does bound check and costs extra
}
v |= (b & 0x7F) << (i * 7);
if (b & 0x80 == 0) {
buf.idx += i + 1;
return v;
}
}
revert(); // i=10, invalid varint stream
}
// read length delimited field and return bytes
function decBytes(Buffer memory buf) internal pure returns (bytes memory b) {
uint256 len = decVarint(buf);
uint256 end = buf.idx + len;
require(end <= buf.b.length); // avoid overflow
b = new bytes(len);
bytes memory bufB = buf.b; // get buf.b mem addr to use in assembly
uint256 bStart;
uint256 bufBStart = buf.idx;
assembly {
bStart := add(b, 32)
bufBStart := add(add(bufB, 32), bufBStart)
}
for (uint256 i = 0; i < len; i += 32) {
assembly {
mstore(add(bStart, i), mload(add(bufBStart, i)))
}
}
buf.idx = end;
}
// return packed ints
function decPacked(Buffer memory buf) internal pure returns (uint256[] memory t) {
uint256 len = decVarint(buf);
uint256 end = buf.idx + len;
require(end <= buf.b.length); // avoid overflow
// array in memory must be init w/ known length
// so we have to create a tmp array w/ max possible len first
uint256[] memory tmp = new uint256[](len);
uint256 i = 0; // count how many ints are there
while (buf.idx < end) {
tmp[i] = decVarint(buf);
i++;
}
t = new uint256[](i); // init t with correct length
for (uint256 j = 0; j < i; j++) {
t[j] = tmp[j];
}
return t;
}
// move idx pass current value field, to beginning of next tag or msg end
function skipValue(Buffer memory buf, WireType wire) internal pure {
if (wire == WireType.Varint) {
decVarint(buf);
} else if (wire == WireType.LengthDelim) {
uint256 len = decVarint(buf);
buf.idx += len; // skip len bytes value data
require(buf.idx <= buf.b.length); // avoid overflow
} else {
revert();
} // unsupported wiretype
}
// type conversion help utils
function _bool(uint256 x) internal pure returns (bool v) {
return x != 0;
}
function _uint256(bytes memory b) internal pure returns (uint256 v) {
require(b.length <= 32); // b's length must be smaller than or equal to 32
assembly {
v := mload(add(b, 32))
} // load all 32bytes to v
v = v >> (8 * (32 - b.length)); // only first b.length is valid
}
function _address(bytes memory b) internal pure returns (address v) {
v = _addressPayable(b);
}
function _addressPayable(bytes memory b) internal pure returns (address payable v) {
require(b.length == 20);
//load 32bytes then shift right 12 bytes
assembly {
v := div(mload(add(b, 32)), 0x1000000000000000000000000)
}
}
function _bytes32(bytes memory b) internal pure returns (bytes32 v) {
require(b.length == 32);
assembly {
v := mload(add(b, 32))
}
}
// uint[] to uint8[]
function uint8s(uint256[] memory arr) internal pure returns (uint8[] memory t) {
t = new uint8[](arr.length);
for (uint256 i = 0; i < t.length; i++) {
t[i] = uint8(arr[i]);
}
}
function uint32s(uint256[] memory arr) internal pure returns (uint32[] memory t) {
t = new uint32[](arr.length);
for (uint256 i = 0; i < t.length; i++) {
t[i] = uint32(arr[i]);
}
}
function uint64s(uint256[] memory arr) internal pure returns (uint64[] memory t) {
t = new uint64[](arr.length);
for (uint256 i = 0; i < t.length; i++) {
t[i] = uint64(arr[i]);
}
}
function bools(uint256[] memory arr) internal pure returns (bool[] memory t) {
t = new bool[](arr.length);
for (uint256 i = 0; i < t.length; i++) {
t[i] = arr[i] != 0;
}
}
}
DataTypes.sol 87 lines
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.9;
library DataTypes {
uint256 constant CELR_DECIMAL = 1e18;
uint256 constant MAX_INT = 2**256 - 1;
uint256 constant COMMISSION_RATE_BASE = 10000; // 1 commissionRate means 0.01%
uint256 constant MAX_UNDELEGATION_ENTRIES = 10;
uint256 constant SLASH_FACTOR_DECIMAL = 1e6;
enum ValidatorStatus {
Null,
Unbonded,
Unbonding,
Bonded
}
enum ParamName {
ProposalDeposit,
VotingPeriod,
UnbondingPeriod,
MaxBondedValidators,
MinValidatorTokens,
MinSelfDelegation,
AdvanceNoticePeriod,
ValidatorBondInterval,
MaxSlashFactor
}
struct Undelegation {
uint256 shares;
uint256 creationBlock;
}
struct Undelegations {
mapping(uint256 => Undelegation) queue;
uint32 head;
uint32 tail;
}
struct Delegator {
uint256 shares;
Undelegations undelegations;
}
struct Validator {
ValidatorStatus status;
address signer;
uint256 tokens; // sum of all tokens delegated to this validator
uint256 shares; // sum of all delegation shares
uint256 undelegationTokens; // tokens being undelegated
uint256 undelegationShares; // shares of tokens being undelegated
mapping(address => Delegator) delegators;
uint256 minSelfDelegation;
uint64 bondBlock; // cannot become bonded before this block
uint64 unbondBlock; // cannot become unbonded before this block
uint64 commissionRate; // equal to real commission rate * COMMISSION_RATE_BASE
}
// used for external view output
struct ValidatorTokens {
address valAddr;
uint256 tokens;
}
// used for external view output
struct ValidatorInfo {
address valAddr;
ValidatorStatus status;
address signer;
uint256 tokens;
uint256 shares;
uint256 minSelfDelegation;
uint64 commissionRate;
}
// used for external view output
struct DelegatorInfo {
address valAddr;
uint256 tokens;
uint256 shares;
Undelegation[] undelegations;
uint256 undelegationTokens;
uint256 withdrawableUndelegationTokens;
}
}
PbStaking.sol 99 lines
// SPDX-License-Identifier: GPL-3.0-only
// Code generated by protoc-gen-sol. DO NOT EDIT.
// source: contracts/libraries/proto/staking.proto
pragma solidity 0.8.9;
import "./Pb.sol";
library PbStaking {
using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj
struct StakingReward {
address recipient; // tag: 1
uint256 cumulativeRewardAmount; // tag: 2
} // end struct StakingReward
function decStakingReward(bytes memory raw) internal pure returns (StakingReward memory m) {
Pb.Buffer memory buf = Pb.fromBytes(raw);
uint256 tag;
Pb.WireType wire;
while (buf.hasMore()) {
(tag, wire) = buf.decKey();
if (false) {}
// solidity has no switch/case
else if (tag == 1) {
m.recipient = Pb._address(buf.decBytes());
} else if (tag == 2) {
m.cumulativeRewardAmount = Pb._uint256(buf.decBytes());
} else {
buf.skipValue(wire);
} // skip value of unknown tag
}
} // end decoder StakingReward
struct Slash {
address validator; // tag: 1
uint64 nonce; // tag: 2
uint64 slashFactor; // tag: 3
uint64 expireTime; // tag: 4
uint64 jailPeriod; // tag: 5
AcctAmtPair[] collectors; // tag: 6
} // end struct Slash
function decSlash(bytes memory raw) internal pure returns (Slash memory m) {
Pb.Buffer memory buf = Pb.fromBytes(raw);
uint256[] memory cnts = buf.cntTags(6);
m.collectors = new AcctAmtPair[](cnts[6]);
cnts[6] = 0; // reset counter for later use
uint256 tag;
Pb.WireType wire;
while (buf.hasMore()) {
(tag, wire) = buf.decKey();
if (false) {}
// solidity has no switch/case
else if (tag == 1) {
m.validator = Pb._address(buf.decBytes());
} else if (tag == 2) {
m.nonce = uint64(buf.decVarint());
} else if (tag == 3) {
m.slashFactor = uint64(buf.decVarint());
} else if (tag == 4) {
m.expireTime = uint64(buf.decVarint());
} else if (tag == 5) {
m.jailPeriod = uint64(buf.decVarint());
} else if (tag == 6) {
m.collectors[cnts[6]] = decAcctAmtPair(buf.decBytes());
cnts[6]++;
} else {
buf.skipValue(wire);
} // skip value of unknown tag
}
} // end decoder Slash
struct AcctAmtPair {
address account; // tag: 1
uint256 amount; // tag: 2
} // end struct AcctAmtPair
function decAcctAmtPair(bytes memory raw) internal pure returns (AcctAmtPair memory m) {
Pb.Buffer memory buf = Pb.fromBytes(raw);
uint256 tag;
Pb.WireType wire;
while (buf.hasMore()) {
(tag, wire) = buf.decKey();
if (false) {}
// solidity has no switch/case
else if (tag == 1) {
m.account = Pb._address(buf.decBytes());
} else if (tag == 2) {
m.amount = Pb._uint256(buf.decBytes());
} else {
buf.skipValue(wire);
} // skip value of unknown tag
}
} // end decoder AcctAmtPair
}
ISigsVerifier.sol 19 lines
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.9;
interface ISigsVerifier {
/**
* @notice Verifies that a message is signed by a quorum among the signers.
* @param _msg signed message
* @param _sigs list of signatures sorted by signer addresses
* @param _signers sorted list of current signers
* @param _powers powers of current signers
*/
function verifySigs(
bytes memory _msg,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external view;
}
Address.sol 210 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 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://diligence.consensys.net/posts/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.5.11/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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) private pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// 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
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
Context.sol 23 lines
// SPDX-License-Identifier: MIT
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;
}
}
Ownable.sol 71 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_setOwner(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_setOwner(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Pausable.sol 90 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
require(!paused(), "Pausable: paused");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
require(paused(), "Pausable: not paused");
_;
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
IERC20.sol 81 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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 `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, 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 `sender` to `recipient` 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 sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @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);
}
ECDSA.sol 139 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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 {
/**
* @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.
*
* 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]
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
// Check the signature length
// - case 65: r,s,v signature (standard)
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
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.
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return recover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return recover(hash, r, vs);
} else {
revert("ECDSA: invalid signature length");
}
}
/**
* @dev Overload of {ECDSA-recover} 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.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
bytes32 s;
uint8 v;
assembly {
s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
v := add(shr(255, vs), 27)
}
return recover(hash, v, r, s);
}
/**
* @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) {
// 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 (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): 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.
require(
uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
"ECDSA: invalid signature 's' value"
);
require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
require(signer != address(0), "ECDSA: invalid signature");
return signer;
}
/**
* @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) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @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) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}
SafeERC20.sol 98 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC20.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;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
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));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
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");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @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");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
Read Contract
CELER_TOKEN 0x960dc08a → address
bondedTokens 0x65d5d420 → uint256
bondedValAddrs 0xacc62ccf → address
forfeiture 0x8338f0e5 → uint256
getBondedValidatorNum 0x89f9aab5 → uint256
getBondedValidatorsTokens 0x4021d4d5 → tuple[]
getDelegatorInfo 0xeecefef8 → tuple
getParamValue 0x410ae02c → uint256
getQuorumTokens 0x386c024a → uint256
getValidatorNum 0x1cfe4f0b → uint256
getValidatorStatus 0xa310624f → uint8
getValidatorTokens 0xc8f9f984 → uint256
govContract 0x2fa4d12b → address
hasMinRequiredTokens 0x47abfdbf → bool
isBondedValidator 0xb4f7fa34 → bool
isPauser 0x46fbf68e → bool
isWhitelisted 0x3af32abf → bool
nextBondBlock 0x83cfb318 → uint256
owner 0x8da5cb5b → address
params 0xeb505dd5 → uint256
paused 0x5c975abb → bool
pausers 0x80f51c12 → bool
rewardContract 0x6ea69d62 → address
signerVals 0x6d308783 → address
slashNonces 0x90e360f8 → bool
valAddrs 0x92bb243c → address
validators 0xfa52c7d8 → uint8, address, uint256, uint256, uint256, uint256, uint256, uint64, uint64, uint64
verifySignatures 0x8a74d5fe → bool
verifySigs 0x682dbc22
whitelist 0x9b19251a → bool
whitelistEnabled 0x51fb012d → bool
Write Contract 28 functions
These functions modify contract state and require a wallet transaction to execute.
addPauser 0x82dc1ec4
address account
addWhitelisted 0x10154bad
address account
bondValidator 0x36f1635f
No parameters
collectForfeiture 0x82d7b4b8
No parameters
completeUndelegate 0x473849bd
address _valAddr
confirmUnbondedValidator 0x71bc0216
address _valAddr
delegate 0x026e402b
address _valAddr
uint256 _tokens
drainToken 0x145aa116
uint256 _amount
initializeValidator 0x525eba21
address _signer
uint256 _minSelfDelegation
uint64 _commissionRate
pause 0x8456cb59
No parameters
removePauser 0x6b2c0f55
address account
removeWhitelisted 0x291d9549
address account
renounceOwnership 0x715018a6
No parameters
renouncePauser 0x6ef8d66d
No parameters
setGovContract 0x68706e54
address _addr
setMaxSlashFactor 0x1a203257
uint256 _maxSlashFactor
setParamValue 0xe909156d
uint8 _name
uint256 _value
setRewardContract 0x51508f0a
address _addr
setWhitelistEnabled 0x052d9e7e
bool _whitelistEnabled
slash 0x3985c4e6
bytes _slashRequest
bytes[] _sigs
transferOwnership 0xf2fde38b
address newOwner
undelegateShares 0x88d996e8
address _valAddr
uint256 _shares
undelegateTokens 0xdcfdc1e1
address _valAddr
uint256 _tokens
unpause 0x3f4ba83a
No parameters
updateCommissionRate 0x49955e39
uint64 _newRate
updateMinSelfDelegation 0x5e593eff
uint256 _minSelfDelegation
updateValidatorSigner 0x7a50dbd2
address _signer
validatorNotice 0x9146f110
address _valAddr
string _key
bytes _data
Recent Transactions
No transactions found for this address