Address Contract Verified
Address
0x7ae267232fa94AcF622794828b63dc9E6dcd0299
Balance
0.034508 ETH ($71.14)
Nonce
1
Code Size
12811 bytes
Creator
0xD840F059...2B47 at tx 0x6b901009...d078b1
Indexed Transactions
0
Contract Bytecode
12811 bytes
0x608060405260043610610228575f3560e01c806379ba509711610122578063b0fb162f116100aa578063f2fde38b1161006e578063f2fde38b1461071f578063f3ae24151461073e578063f83d08ba1461076c578063fc2a88c314610780578063fd915ef21461079557610268565b8063b0fb162f14610663578063be6e7a4f14610698578063df15c37e146106b7578063e76d5168146106d9578063f2c399921461070b57610268565b8063a2fb1175116100f1578063a2fb1175146105a9578063a4e2d634146105e8578063a57848b614610611578063a57b8b1414610625578063a86099ba1461064457610268565b806379ba50971461050357806389b66b0a146105175780638da5cb5b146105465780639ed0868d1461057657610268565b806333096e68116101b05780634129b2c9116101745780634129b2c914610455578063497138111461049b5780635bc07e62146104b0578063622b6af4146104cf5780636e67b2a9146104ee57610268565b806333096e68146103ce57806336d152a8146103ed5780633e4840101461040c5780633e66a6471461042b5780633f20b4c91461044057610268565b806312c515d3116101f757806312c515d31461031d57806318e60a32146103325780631fe543e31461036957806321f1d6771461038857806324f746971461039d57610268565b8063049229601461029d57806307b18bde146102c7578063097315b5146102e857806312065fe01461030b57610268565b3661026857604080513381523460208201527ff07fc9e1f2c699914d655cb8501692de5ce105473f7534bd30ed9f10cab4e58991015b60405180910390a1005b604080513381523460208201527ff07fc9e1f2c699914d655cb8501692de5ce105473f7534bd30ed9f10cab4e589910161025e565b3480156102a8575f5ffd5b506102b16107b3565b6040516102be9190612977565b60405180910390f35b3480156102d2575f5ffd5b506102e66102e136600461299d565b61083f565b005b3480156102f3575f5ffd5b506102fd600f5481565b6040519081526020016102be565b348015610316575f5ffd5b50476102fd565b348015610328575f5ffd5b506102fd600a5481565b34801561033d575f5ffd5b5061035161034c3660046129c7565b6109f7565b6040516001600160401b0390911681526020016102be565b348015610374575f5ffd5b506102e6610383366004612a01565b610a6a565b348015610393575f5ffd5b506102fd60095481565b3480156103a8575f5ffd5b50600d546103b99063ffffffff1681565b60405163ffffffff90911681526020016102be565b3480156103d9575f5ffd5b506102e66103e8366004612ad2565b610ad4565b3480156103f8575f5ffd5b506102e6610407366004612b17565b610b43565b348015610417575f5ffd5b506102e6610426366004612b75565b610dc1565b348015610436575f5ffd5b506102fd60105481565b34801561044b575f5ffd5b506102fd60115481565b348015610460575f5ffd5b5061047461046f366004612b17565b610f14565b6040805182516001600160401b0390811682526020938401511692810192909252016102be565b3480156104a6575f5ffd5b506102fd60055481565b3480156104bb575f5ffd5b506102e66104ca366004612bb3565b610fda565b3480156104da575f5ffd5b506102e66104e9366004612b17565b611267565b3480156104f9575f5ffd5b506102fd600b5481565b34801561050e575f5ffd5b506102e66112ae565b348015610522575f5ffd5b5061052b611357565b604080519384526020840192909252908201526060016102be565b348015610551575f5ffd5b505f546001600160a01b03165b6040516001600160a01b0390911681526020016102be565b348015610581575f5ffd5b5061055e7f00000000000000000000000002aae1a04f9828517b3007f83f6181900cad910c81565b3480156105b4575f5ffd5b506105c86105c3366004612b17565b6113cb565b604080516001600160401b039384168152929091166020830152016102be565b3480156105f3575f5ffd5b506004546106019060ff1681565b60405190151581526020016102be565b34801561061c575f5ffd5b506102fd6113fe565b348015610630575f5ffd5b506102e661063f366004612c1d565b6116ac565b34801561064f575f5ffd5b506102e661065e366004612c73565b611788565b34801561066e575f5ffd5b50600d5461068590640100000000900461ffff1681565b60405161ffff90911681526020016102be565b3480156106a3575f5ffd5b506102e66106b2366004612b17565b61186f565b3480156106c2575f5ffd5b506106cb6118d9565b6040516102be929190612d26565b3480156106e4575f5ffd5b507f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca61055e565b348015610716575f5ffd5b506102e6611a31565b34801561072a575f5ffd5b506102e6610739366004612d53565b611b1e565b348015610749575f5ffd5b50610601610758366004612d53565b60026020525f908152604090205460ff1681565b348015610777575f5ffd5b506102e6611b2f565b34801561078b575f5ffd5b506102fd600e5481565b3480156107a0575f5ffd5b5060045461060190610100900460ff1681565b600380546107c090612d6e565b80601f01602080910402602001604051908101604052809291908181526020018280546107ec90612d6e565b80156108375780601f1061080e57610100808354040283529160200191610837565b820191905f5260205f20905b81548152906001019060200180831161081a57829003601f168201915b505050505081565b610847611c29565b6001600160a01b03821661088c5760405162461bcd60e51b81526020600482015260076024820152667a65726f20746f60c81b60448201526064015b60405180910390fd5b5f81116108cc5760405162461bcd60e51b815260206004820152600e60248201526d616d6f756e74206973207a65726f60901b6044820152606401610883565b47818111806108da57508181145b61091d5760405162461bcd60e51b8152602060048201526014602482015273696e73756666696369656e742062616c616e636560601b6044820152606401610883565b5f836001600160a01b0316836040515f6040518083038185875af1925050503d805f8114610966576040519150601f19603f3d011682016040523d82523d5f602084013e61096b565b606091505b50509050806109ae5760405162461bcd60e51b815260206004820152600f60248201526e1dda5d1a191c985dc819985a5b1959608a1b6044820152606401610883565b604080516001600160a01b0386168152602081018590527fd50b71a2790ecccf5881141fe9ae079e17c66aace5d50ba383d443ecd398ffa591015b60405180910390a150505050565b6001600160401b0381165f90815260086020526040812054808203610a1e57505f92915050565b6007610a2b600183612dba565b81548110610a3b57610a3b612dcd565b905f5260205f2090600491828204019190066008029054906101000a90046001600160401b0316915050919050565b7f00000000000000000000000002aae1a04f9828517b3007f83f6181900cad910c336001600160a01b03821614610ac5576040516345d498b760e11b81523360048201526001600160a01b0382166024820152604401610883565b610acf8383611c7d565b505050565b5f546001600160a01b0316331480610afa5750335f9081526002602052604090205460ff165b610b165760405162461bcd60e51b815260040161088390612de1565b600d805461ffff9092166401000000000265ffffffffffff1990921663ffffffff90931692909217179055565b5f546001600160a01b0316331480610b695750335f9081526002602052604090205460ff165b610b855760405162461bcd60e51b815260040161088390612de1565b60045460ff16610bcb5760405162461bcd60e51b81526020600482015260116024820152701cd9585cdbdb881b9bdd081b1bd8dad959607a1b6044820152606401610883565b600f545f03610c125760405162461bcd60e51b81526020600482015260136024820152721c985b991bdb481cd95959081b9bdd081cd95d606a1b6044820152606401610883565b600554600c5410610c655760405162461bcd60e51b815260206004820152601d60248201527f616c6c2077696e6e65727320616c72656164792066696e616c697a65640000006044820152606401610883565b5f8111610cab5760405162461bcd60e51b815260206004820152601460248201527306d61785069636b73206d757374206265203e20360641b6044820152606401610883565b600c546005545f91610cc7918491610cc291612dba565b611d0a565b6007549091505f610cd9826001612e10565b6001600160401b03811115610cf057610cf06129ed565b604051908082528060200260200182016040528015610d19578160200160208202803683370190505b509050610d2581611d23565b5f610d2f83611d90565b90505f5b84811015610d61575f610d47848487611db6565b90508015610d585750505050505050565b50600101610d33565b50600554600c5403610db9576004805461ff001916610100179055600c546040517f8930cc2aff212dc313d7e7085b6bd11e02c7dee98e13587e317ff140ac96dd6691610db091600390612ea2565b60405180910390a15b505050505b50565b5f546001600160a01b0316331480610de75750335f9081526002602052604090205460ff165b610e035760405162461bcd60e51b815260040161088390612de1565b60045460ff1615610e265760405162461bcd60e51b815260040161088390612eba565b5f5b81811015610edb575f60085f858585818110610e4657610e46612dcd565b9050602002016020810190610e5b91906129c7565b6001600160401b0316815260208101919091526040015f205490508015610ed2575f6007610e8a600184612dba565b81548110610e9a57610e9a612dcd565b905f5260205f2090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b031602179055505b50600101610e28565b506040518181527f8c5fd9915c371d916ccb8c3b6991f1765de9b59f6d382e034c7e19af928cbf54906020015b60405180910390a15050565b604080518082019091525f80825260208201528115801590610f425750600c54610f3f906001612e10565b82105b610f835760405162461bcd60e51b815260206004820152601260248201527172616e6b206f7574206f6620626f756e647360701b6044820152606401610883565b600c610f90600184612dba565b81548110610fa057610fa0612dcd565b5f918252602091829020604080518082019091529101546001600160401b038082168352600160401b909104169181019190915292915050565b5f546001600160a01b03163314806110005750335f9081526002602052604090205460ff165b61101c5760405162461bcd60e51b815260040161088390612de1565b60045460ff161561103f5760405162461bcd60e51b815260040161088390612eba565b8281146110865760405162461bcd60e51b81526020600482015260156024820152740c2e4e4c2f240d8cadccee8d040dad2e6dac2e8c6d605b1b6044820152606401610883565b5f5b83811015611236575f8585838181106110a3576110a3612dcd565b90506020020160208101906110b891906129c7565b6001600160401b0381165f908152600860205260408120549192508190036111b057600680546001810182555f919091527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f6004820401805460039092166008026101000a6001600160401b038181021990931692851602919091179055600785858581811061114a5761114a612dcd565b905060200201602081019061115f91906129c7565b81546001810183555f9283526020808420600483040180546001600160401b039485166008600390951685026101000a9081029086021990911617905560065492861684525260409091205561122c565b8484848181106111c2576111c2612dcd565b90506020020160208101906111d791906129c7565b60076111e4600184612dba565b815481106111f4576111f4612dcd565b905f5260205f2090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b031602179055505b5050600101611088565b506040518381527f15ef75d1021675c2796bc9ad1d872efa8a9dc26491c4d7aaac8ff3d120ee05da906020016109e9565b5f546001600160a01b031633148061128d5750335f9081526002602052604090205460ff165b6112a95760405162461bcd60e51b815260040161088390612de1565b601155565b6001546001600160a01b031633146113015760405162461bcd60e51b815260206004820152601660248201527526bab9ba10313290383937b837b9b2b21037bbb732b960511b6044820152606401610883565b5f8054336001600160a01b0319808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6006545f80828180805b838110156113bf575f6007828154811061137d5761137d612dcd565b5f918252602090912060048204015460039091166008026101000a90046001600160401b0316905080156113b657600190930192918201915b50600101611361565b50949590949350915050565b600c81815481106113da575f80fd5b5f918252602090912001546001600160401b038082169250600160401b9091041682565b5f80546001600160a01b03163314806114255750335f9081526002602052604090205460ff165b6114415760405162461bcd60e51b815260040161088390612de1565b60045460ff166114875760405162461bcd60e51b81526020600482015260116024820152701cd9585cdbdb881b9bdd081b1bd8dad959607a1b6044820152606401610883565b600454610100900460ff16156114d35760405162461bcd60e51b8152602060048201526011602482015270185b1c9958591e48199a5b985b1a5e9959607a1b6044820152606401610883565b600f54156115165760405162461bcd60e51b815260206004820152601060248201526f1cd9595908185b1c9958591e481cd95d60821b6044820152606401610883565b600e5415801590611528575060115415155b15611593575f60115460105461153e9190612e10565b90508042118061154d57508042145b6115915760405162461bcd60e51b81526020600482015260156024820152741c195b991a5b99cec81dd85a5d081d1a5b595bdd5d605a1b6044820152606401610883565b505b5f600554116115dc5760405162461bcd60e51b815260206004820152601560248201527477696e6e65727320636f756e74206973207a65726f60581b6044820152606401610883565b5f6115f760405180602001604052806001151581525061203a565b600d549091505f9061161f9063ffffffff811690640100000000900461ffff166001856120ab565b600e8290554260105560405191945091507f918ff637e95f50ea1b787bf5110005d58dfae3d285a530aa9b625e7813f5f7eb90611663908590600190600390612ee1565b60405180910390a17f6d26274d28864a34dc2e77a2751b72d7792490d15be753b53400a7e74756e8998382600360405161169f93929190612f01565b60405180910390a1505090565b6116b4611c29565b5f5b82811015611782578160025f8686858181106116d4576116d4612dcd565b90506020020160208101906116e99190612d53565b6001600160a01b0316815260208101919091526040015f20805460ff19169115159190911790557fff83ce179bad4fbdb0e98074011487cde624295a52d8189d92d5d8b06c914eda84848381811061174357611743612dcd565b90506020020160208101906117589190612d53565b604080516001600160a01b03909216825284151560208301520160405180910390a16001016116b6565b50505050565b5f546001600160a01b03163314806117ae5750335f9081526002602052604090205460ff165b6117ca5760405162461bcd60e51b815260040161088390612de1565b60045460ff1615806117e35750600454610100900460ff165b61182f5760405162461bcd60e51b815260206004820152601d60248201527f70726576696f757320736561736f6e206e6f742066696e616c697a65640000006044820152606401610883565b610acf83838080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508592506121e4915050565b5f546001600160a01b03163314806118955750335f9081526002602052604090205460ff165b6118b15760405162461bcd60e51b815260040161088390612de1565b60045460ff16156118d45760405162461bcd60e51b815260040161088390612eba565b600555565b600c546060908190806001600160401b038111156118f9576118f96129ed565b604051908082528060200260200182016040528015611922578160200160208202803683370190505b509250806001600160401b0381111561193d5761193d6129ed565b604051908082528060200260200182016040528015611966578160200160208202803683370190505b5091505f5b81811015611a2b575f600c828154811061198757611987612dcd565b5f918252602091829020604080518082019091529101546001600160401b03808216808452600160401b909204169282019290925286519092508690849081106119d3576119d3612dcd565b60200260200101906001600160401b031690816001600160401b0316815250508060200151848381518110611a0a57611a0a612dcd565b6001600160401b03909216602092830291909101909101525060010161196b565b50509091565b5f546001600160a01b0316331480611a575750335f9081526002602052604090205460ff165b611a735760405162461bcd60e51b815260040161088390612de1565b60045460ff168015611a8d5750600454610100900460ff16155b611ad95760405162461bcd60e51b815260206004820181905260248201527f6d757374206265206c6f636b656420616e64206e6f742066696e616c697a65646044820152606401610883565b6004805460ff191690556040517f1e100df956c8c0fa7f1fbfccc7997e2a17ab9f80062ac4d9837fc318aa29e18990611b1490600390612f1f565b60405180910390a1565b611b26611c29565b610dbe81612304565b5f546001600160a01b0316331480611b555750335f9081526002602052604090205460ff165b611b715760405162461bcd60e51b815260040161088390612de1565b60045460ff1615611bb55760405162461bcd60e51b815260206004820152600e60248201526d185b1c9958591e481b1bd8dad95960921b6044820152606401610883565b5f5f611bbf6123ac565b600a8290556009819055600b8190556005549193509150821015611be35760058290555b6004805460ff191660011790556005546040517f5cf5237fc5bdc7d9c5b783d4aaf1681853e4a046db42712850457d79dafd9cce91610f08916003918691869190612f31565b5f546001600160a01b03163314611c7b5760405162461bcd60e51b815260206004820152601660248201527527b7363c9031b0b63630b1363290313c9037bbb732b960511b6044820152606401610883565b565b60045460ff168015611c975750600454610100900460ff16155b611c9f575050565b600f5415611cab575050565b805f81518110611cbd57611cbd612dcd565b6020908102919091010151600f8190555f600e8190556010556040517f1261a23477ef791f38c1069ec26ee0c69a67ebde6969d82d1bfd0a394bd8a12b91610f0891859190600390612f01565b5f818310611d185781611d1a565b825b90505b92915050565b6007545f5b81811015610acf575f60078281548110611d4457611d44612dcd565b5f918252602090912060048204015460039091166008026101000a90046001600160401b031690508015611d8757611d8784611d81846001612e10565b8361264b565b50600101611d28565b60015b611d9e826001612e10565b600182901b1015611db15760011b611d93565b919050565b600b545f90808203611e17576004805461ff001916610100179055600c546040517f8930cc2aff212dc313d7e7085b6bd11e02c7dee98e13587e317ff140ac96dd6691611e0591600390612ea2565b60405180910390a16001915050612033565b600f54600c546040515f92611e3492909160039190602001612f5f565b60408051601f19818403018152919052805160209091012090505f611e598383612f9b565b90505f611e72888888611e6d866001612e10565b6126bc565b90505f611e80600183612dba565b90505f60068281548110611e9657611e96612dcd565b905f5260205f2090600491828204019190066008029054906101000a90046001600160401b031690505f60078381548110611ed357611ed3612dcd565b5f91825260208083206004830401546040805180820182526001600160401b03888116825260086003968716026101000a9093048316938101848152600c80546001810182559781905291517fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7909701805491518516600160401b026001600160801b0319909216979094169690961795909517909155925492519093507f1f909cbd03f14ad8c7a18bfe2b3f7f1459a42bf2bbe470166832244602d7a05792611fa292909186918691612fae565b60405180910390a16001600160401b03811615611fdd57611fcd8b85836001600160401b0316612765565b6001600160401b0381168703600b555b5f60078481548110611ff157611ff1612dcd565b905f5260205f2090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b031602179055505f9750505050505050505b9392505050565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa8260405160240161207391511515815260200190565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915292915050565b6040516313c34b7f60e01b815263ffffffff8086166004830152831660248201525f9081906001600160a01b037f00000000000000000000000002aae1a04f9828517b3007f83f6181900cad910c16906313c34b7f90604401602060405180830381865afa15801561211f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121439190612fee565b90507f00000000000000000000000002aae1a04f9828517b3007f83f6181900cad910c6001600160a01b0316639cfc058e82888888886040518663ffffffff1660e01b81526004016121989493929190613005565b60206040518083038185885af11580156121b4573d5f5f3e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906121d99190612fee565b915094509492505050565b5f5b6006548110156122435760085f6006838154811061220657612206612dcd565b5f918252602080832060048304015460039092166008026101000a9091046001600160401b031683528201929092526040018120556001016121e6565b5061224f60065f6128d5565b61225a60075f6128d5565b612265600c5f6128f7565b5f6009819055600a819055600b8190556004805461ffff19169055600f819055600e5581511561229557816122be565b61229e436127d6565b6040516020016122ae9190613039565b6040516020818303038152906040525b6003906122cb90826130a6565b5060058190556040517fe63b64ea7bded5e54c330a5bd177fac256472cb3b6ea2d4ee74bc31796ad2b8690610f08906003908490613160565b336001600160a01b0382160361235c5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610883565b600180546001600160a01b0319166001600160a01b038381169182179092555f8054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6006545f81815b838110156125bc575f600682815481106123cf576123cf612dcd565b905f5260205f2090600491828204019190066008029054906101000a90046001600160401b031690505f6007838154811061240c5761240c612dcd565b5f9182526020822060048204015460039091166008026101000a90046001600160401b031691508190036125a0575f612446600188612dba565b90505f6006828154811061245c5761245c612dcd565b905f5260205f2090600491828204019190066008029054906101000a90046001600160401b031690505f6007838154811061249957612499612dcd565b5f91825260208083206004830401546001600160401b03898116855260089283905260408520949094556003909216026101000a90041690508583146125955781600687815481106124ed576124ed612dcd565b905f5260205f2090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b03160217905550806007878154811061253857612538612dcd565b905f5260205f2090600491828204019190066008026101000a8154816001600160401b0302191690836001600160401b0316021790555085600161257c9190612e10565b6001600160401b0383165f908152600860205260409020555b8298505050506125b5565b806001600160401b0316850194508260010192505b50506123b3565b600654841015611a2b5760068054806125d7576125d7613181565b5f8281526020902060045f199092019182040180546001600160401b03600860038516026101000a02191690559055600780548061261757612617613181565b5f8281526020902060045f199092019182040180546001600160401b03600860038516026101000a021916905590556125bc565b5f6001845161265a9190612dba565b90505f612668826001612e10565b90505b80841015610db9578285858151811061268657612686612dcd565b6020026020010181815161269a9190612e10565b9052506126a984196001612e10565b6126b590851685612e10565b935061266b565b5f80806126ca856001612e10565b90505b851561274f575f6126de8785612e10565b905081811080156127125750848882815181106126fd576126fd612dcd565b6020026020010151846127109190612e10565b105b156127425787818151811061272957612729612dcd565b60200260200101518361273c9190612e10565b92508093505b50600186901c95506126cd565b61275a836001612e10565b979650505050505050565b5f600184516127749190612dba565b90505f612782826001612e10565b90505b80841015610db957828585815181106127a0576127a0612dcd565b602002602001018181516127b49190612dba565b9052506127c384196001612e10565b6127cf90851685612e10565b9350612785565b6060815f036127fc5750506040805180820190915260018152600360fc1b602082015290565b815f5b81156128225761280e81613195565b905061281b600a836131ad565b91506127ff565b5f816001600160401b0381111561283b5761283b6129ed565b6040519080825280601f01601f191660200182016040528015612865576020820181803683370190505b5090505b84156128cd57612878826131c0565b9150612885600a86612f9b565b612890906030612e10565b60f81b8183815181106128a5576128a5612dcd565b60200101906001600160f81b03191690815f1a9053506128c6600a866131ad565b9450612869565b949350505050565b5080545f825560030160049004905f5260205f2090810190610dbe9190612912565b5080545f8255905f5260205f2090810190610dbe919061292a565b5b80821115612926575f8155600101612913565b5090565b5b808211156129265780546001600160801b031916815560010161292b565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f611d1a6020830184612949565b6001600160a01b0381168114610dbe575f5ffd5b5f5f604083850312156129ae575f5ffd5b82356129b981612989565b946020939093013593505050565b5f602082840312156129d7575f5ffd5b81356001600160401b0381168114612033575f5ffd5b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215612a12575f5ffd5b8235915060208301356001600160401b03811115612a2e575f5ffd5b8301601f81018513612a3e575f5ffd5b80356001600160401b03811115612a5757612a576129ed565b8060051b604051601f19603f83011681018181106001600160401b0382111715612a8357612a836129ed565b604052918252602081840181019290810188841115612aa0575f5ffd5b6020850194505b83851015612ac357843580825260209586019590935001612aa7565b50809450505050509250929050565b5f5f60408385031215612ae3575f5ffd5b823563ffffffff81168114612af6575f5ffd5b9150602083013561ffff81168114612b0c575f5ffd5b809150509250929050565b5f60208284031215612b27575f5ffd5b5035919050565b5f5f83601f840112612b3e575f5ffd5b5081356001600160401b03811115612b54575f5ffd5b6020830191508360208260051b8501011115612b6e575f5ffd5b9250929050565b5f5f60208385031215612b86575f5ffd5b82356001600160401b03811115612b9b575f5ffd5b612ba785828601612b2e565b90969095509350505050565b5f5f5f5f60408587031215612bc6575f5ffd5b84356001600160401b03811115612bdb575f5ffd5b612be787828801612b2e565b90955093505060208501356001600160401b03811115612c05575f5ffd5b612c1187828801612b2e565b95989497509550505050565b5f5f5f60408486031215612c2f575f5ffd5b83356001600160401b03811115612c44575f5ffd5b612c5086828701612b2e565b90945092505060208401358015158114612c68575f5ffd5b809150509250925092565b5f5f5f60408486031215612c85575f5ffd5b83356001600160401b03811115612c9a575f5ffd5b8401601f81018613612caa575f5ffd5b80356001600160401b03811115612cbf575f5ffd5b866020828401011115612cd0575f5ffd5b6020918201979096509401359392505050565b5f8151808452602084019350602083015f5b82811015612d1c5781516001600160401b0316865260209586019590910190600101612cf5565b5093949350505050565b604081525f612d386040830185612ce3565b8281036020840152612d4a8185612ce3565b95945050505050565b5f60208284031215612d63575f5ffd5b813561203381612989565b600181811c90821680612d8257607f821691505b602082108103612da057634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115611d1d57611d1d612da6565b634e487b7160e01b5f52603260045260245ffd5b60208082526015908201527437b7363c9037bbb732b91037b91036b0b730b3b2b960591b604082015260600190565b80820180821115611d1d57611d1d612da6565b5f8154612e2f81612d6e565b808552600182168015612e495760018114612e6557612e99565b60ff1983166020870152602082151560051b8701019350612e99565b845f5260205f205f5b83811015612e905781546020828a010152600182019150602081019050612e6e565b87016020019450505b50505092915050565b828152604060208201525f6128cd6040830184612e23565b6020808252600d908201526c1cd9585cdbdb881b1bd8dad959609a1b604082015260600190565b8381528215156020820152606060408201525f612d4a6060830184612e23565b838152826020820152606060408201525f612d4a6060830184612e23565b602081525f611d1a6020830184612e23565b608081525f612f436080830187612e23565b6020830195909552506040810192909252606090910152919050565b838152606060208201525f612f776060830185612e23565b9050826040830152949350505050565b634e487b7160e01b5f52601260045260245ffd5b5f82612fa957612fa9612f87565b500690565b8481526001600160401b03841660208201526001600160401b0383166040820152608060608201525f612fe46080830184612e23565b9695505050505050565b5f60208284031215612ffe575f5ffd5b5051919050565b63ffffffff8516815261ffff8416602082015263ffffffff83166040820152608060608201525f612fe46080830184612949565b66029b2b0b9b7b7160cd1b81525f82518060208501600785015e5f920160070191825250919050565b601f821115610acf57805f5260205f20601f840160051c810160208510156130875750805b601f840160051c820191505b81811015610db9575f8155600101613093565b81516001600160401b038111156130bf576130bf6129ed565b6130d3816130cd8454612d6e565b84613062565b6020601f821160018114613105575f83156130ee5750848201515b5f19600385901b1c1916600184901b178455610db9565b5f84815260208120601f198516915b828110156131345787850151825560209485019460019092019101613114565b508482101561315157868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b604081525f6131726040830185612e23565b90508260208301529392505050565b634e487b7160e01b5f52603160045260245ffd5b5f600182016131a6576131a6612da6565b5060010190565b5f826131bb576131bb612f87565b500490565b5f816131ce576131ce612da6565b505f19019056fea264697066735822122062df0d41eb6fe90f09759142c6be8143b9e90b0bc04653b8b0ee38d17ae32a6464736f6c634300081f0033
Verified Source Code Full Match
Compiler: v0.8.31+commit.fd3a2265
EVM: osaka
Optimization: Yes (200 runs)
LotteryVrfContract.sol 715 lines
// SPDX-License-Identifier: -- DG --
pragma solidity ^0.8.24;
/**
* @title WeightedLotteryVRF
* @notice Weighted lottery with ordered winners and seasonal resets, using Chainlink VRF v2.5 Wrapper (Direct Funding).
* @author Decentral Games Team
* @dev
* - Simple UX: Uses VRFV2PlusWrapperConsumerBase
* - Lifecycle: upsert/remove -> lock() -> requestDraw() -> fulfillRandomWords() -> finalizeWinners().
* - Randomness: Direct Funding via wrapper; always pays in native (ETH)
* - Sampling: Weighted without replacement via Fenwick tree built in memory per finalize call.
* Rationale: maintaining a Fenwick tree in storage during upserts/lock significantly increases SSTORE cost
* (O(n log n) storage writes across a season) and can trigger out-of-gas with large batches. Rebuilding the
* Fenwick in memory at finalization shifts the cost to transient memory (cheap, no long-term rent), keeps the
* hot paths (upsert/remove/lock) inexpensive, and enables chunked finalization (maxPicks) by recomputing a
* consistent tree each call. Trade-offs: we re-do O(n log n) work per finalize chunk; acceptable because
* finalize is called infrequently (once or a few times per season) vs many upserts. This also avoids storage
* complexity across season resets and simplifies audits.
*/
import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
import {VRFV2PlusWrapperConsumerBase} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapperConsumerBase.sol";
import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
/// @title WeightedLotteryVRF
/// @notice Seasonal weighted lottery using Chainlink VRF v2.5 Wrapper with native funding.
/// @author Lottery-VRF Team
/// @dev See function-level docs for lifecycle, invariants, and Fenwick rationale.
contract WeightedLotteryVRF is VRFV2PlusWrapperConsumerBase, ConfirmedOwner {
// --- Access model & state machine ---
// Access: owner + managers via onlyAdmin for operational actions
// Lifecycle:
// 1) Pre-lock: upsert/remove entries; winner count configurable. Mapping indexPlus1 may include IDs with zero counts.
// 2) lock(): compacts in-place (swap-with-last), updates indexPlus1 on-the-fly, sets snapshot totals.
// 3) requestDraw(): requests 1 VRF word via wrapper (native-only).
// 4) fulfillRandomWords(): idempotently stores vrfSeed and clears pending markers.
// 5) finalizeWinners(): in chunks, builds Fenwick tree in memory, picks weighted winners without replacement until done.
// After finalize completes: drawn=true. emergencyUnlock() is only allowed while locked and not finalized.
// Roles
/// @notice Manager accounts allowed to execute operational calls via onlyAdmin.
mapping(address => bool) public isManager;
modifier onlyAdmin() {
require(
msg.sender == owner() || isManager[msg.sender],
"only owner or manager"
);
_;
}
// Season state
/// @notice Current season identifier.
string public seasonId;
/// @notice True once lock() has been called for the season.
bool public isLocked;
/// @notice True when all winners have been finalized for the season.
bool public drawn;
/// @notice Target number of winners for the season.
uint256 public winnersCount;
// Entries
uint64[] private idList;
uint64[] private counts;
mapping(uint64 => uint256) private indexPlus1; // id => idx+1
// Invariant (post-lock): indexPlus1 mirrors idList exactly (id => position+1). Pre-lock it may include zero-count IDs.
// Snapshot
/// @notice Total weight snapshot at lock() (sum of counts > 0).
uint256 public totalEntriesAtLock;
/// @notice Number of unique IDs with non-zero entries at lock().
uint256 public nonZeroIdsAtLock;
/// @notice Remaining total weight not yet selected during finalization, in case of batched finalization.
uint256 public remainingWeight;
// Winners
struct Winner {
uint64 id;
uint64 entriesAtLock;
}
/// @notice Ordered list of winners picked so far.
Winner[] public winners;
// VRF via Wrapper
/// @notice Gas limit provided to the VRF wrapper's callback.
uint32 public callbackGasLimit = 300000;
/// @notice Number of block confirmations to wait before VRF fulfills.
uint16 public requestConfirmations = 3;
// Payment mode fixed to native; LINK payments are intentionally not supported for simplicity.
/// @notice Last VRF request id (0 when no request is pending).
uint256 public lastRequestId;
/// @notice Random seed set by fulfillRandomWords (0 until set).
uint256 public vrfSeed;
/// @notice Timestamp when last VRF request was made (0 when none pending).
uint256 public requestTimestamp;
/// @notice Minimum seconds to wait before retrying a pending request (0 disables).
uint256 public requestTimeout = 1 hours; // admin-configurable safety timeout
// Events
/// @notice Emitted when a manager account is added/removed.
/// @param account The manager address.
/// @param enabled True if enabled, false if removed.
event ManagerSet(address account, bool enabled);
/// @notice Emitted when a new season starts.
/// @param seasonId Human-readable season id.
/// @param defaultWinners Initial winners target for the season.
event SeasonStarted(string seasonId, uint256 defaultWinners);
/// @notice Emitted after upsertEntries runs.
/// @param count Number of ids processed.
event EntriesUpserted(uint256 count);
/// @notice Emitted after removeEntries runs.
/// @param count Number of ids processed.
event EntriesRemoved(uint256 count);
/// @notice Emitted when the season is locked with snapshot totals.
/// @param totalIds Number of ids with non-zero entries at lock.
/// @param totalEntries Sum of all entries at lock.
/// @param winnersCount Winners target (may be clamped to totalIds).
event Locked(
string seasonId,
uint256 totalIds,
uint256 totalEntries,
uint256 winnersCount
);
/// @notice Emitted when an emergency unlock occurs before finalization.
event EmergencyUnlocked(string seasonId);
/// @notice Emitted when a VRF request is made.
/// @param requestId The VRF request id.
/// @param nativePayment Always true; wrapper paid in native token.
event VRFRequested(uint256 requestId, bool nativePayment, string seasonId);
/// @notice Emitted with the fee actually charged for a VRF request (in wei of native token).
/// @param requestId The VRF request id.
/// @param feeWei The fee paid (wrapper + coordinator + premium) in native wei.
event VRFFeeCharged(uint256 requestId, uint256 feeWei, string seasonId);
/// @notice Emitted when the VRF seed is set by the callback.
/// @param requestId The VRF request id.
/// @param seed The random seed.
event VRFSeedSet(uint256 requestId, uint256 seed, string seasonId);
/// @notice Emitted per winner pick (rank starts at 1).
/// @param rank The 1-based rank in winners array.
/// @param id The winning id.
/// @param entriesAtLock The winner's weight at lock.
event WinnerPicked(
uint256 rank,
uint64 id,
uint64 entriesAtLock,
string seasonId
);
/// @notice Emitted when all winners are finalized.
/// @param totalWinners Number of winners finalized.
event DrawFinalized(uint256 totalWinners, string seasonId);
// Native funding events
/// @notice Emitted when native funds are received.
/// @param from Sender address.
/// @param amount Amount received.
event NativeFundsReceived(address from, uint256 amount);
/// @notice Emitted when native funds are withdrawn by the owner.
/// @param to Recipient address.
/// @param amount Amount withdrawn.
event NativeFundsWithdrawn(address to, uint256 amount);
/// @notice Initializes the contract with a VRF wrapper and initial season.
/// @param vrfWrapper Address of the Chainlink VRF v2.5 Wrapper.
/// @param _initialSeasonId Initial season identifier.
/// @param _defaultWinners Initial winners target for the first season.
constructor(
address vrfWrapper,
string memory _initialSeasonId,
uint256 _defaultWinners
) VRFV2PlusWrapperConsumerBase(vrfWrapper) ConfirmedOwner(msg.sender) {
_startSeason(_initialSeasonId, _defaultWinners);
}
// Admin
/// @notice Adds or removes manager accounts.
/// @param addrs List of addresses to update.
/// @param enabled True to enable, false to disable.
function setManagers(
address[] calldata addrs,
bool enabled
) external onlyOwner {
for (uint256 i = 0; i < addrs.length; ++i) {
isManager[addrs[i]] = enabled;
emit ManagerSet(addrs[i], enabled);
}
}
/// @notice Starts a new season; previous season must be unlocked or finalized.
/// @param newSeasonId New season identifier.
/// @param defaultWinners Winners target for the new season.
function startNewSeason(
string calldata newSeasonId,
uint256 defaultWinners
) external onlyAdmin {
require(!isLocked || drawn, "previous season not finalized");
_startSeason(newSeasonId, defaultWinners);
}
/// @notice Internal season initializer; resets state and clears mappings.
/// @param newSeasonId New season identifier.
/// @param defaultWinners Winners target for the new season.
function _startSeason(
string memory newSeasonId,
uint256 defaultWinners
) internal {
// Clear-first vs selective clearing rationale:
// - We clear indexPlus1 for ALL current IDs here, then delete/rebuild arrays and repopulate the mapping.
// - Selective clearing (only deleting keys for removed IDs) is error-prone while compaction/rebuild happens,
// because indices shift and some IDs drop to zero. We'd also have to update moved IDs' indices on-the-fly.
// - Clear-first is linear and simple: remove all mapping entries, rebuild compact arrays, then repopulate an exact
// mapping mirror (id => newIndex+1). This avoids stale pointers and out-of-bounds risks.
// - Gas trade-off is acceptable since season resets are rare vs many upserts.
// clear mapping for existing IDs
for (uint256 i = 0; i < idList.length; ) {
delete indexPlus1[idList[i]];
unchecked {
++i;
}
}
delete idList;
delete counts;
delete winners;
totalEntriesAtLock = 0;
nonZeroIdsAtLock = 0;
remainingWeight = 0;
isLocked = false;
drawn = false;
vrfSeed = 0;
lastRequestId = 0;
seasonId = bytes(newSeasonId).length == 0
? string(abi.encodePacked("Season ", _toString(block.number)))
: newSeasonId;
winnersCount = defaultWinners;
emit SeasonStarted(seasonId, defaultWinners);
}
// Pre-lock mutations
/// @notice Sets the winners target for the current season (pre-lock only).
/// @param n New winners target.
function setWinnersCount(uint256 n) external onlyAdmin {
require(!isLocked, "season locked");
winnersCount = n;
}
/// @notice Upserts entries for ids (pre-lock only). Replaces existing counts.
/// @param ids List of ids to upsert.
/// @param newCounts Corresponding weights for each id.
function upsertEntries(
uint64[] calldata ids,
uint64[] calldata newCounts
) external onlyAdmin {
require(!isLocked, "season locked");
require(ids.length == newCounts.length, "array length mismatch");
for (uint256 i = 0; i < ids.length; ) {
uint64 id = ids[i];
uint256 idxp1 = indexPlus1[id];
if (idxp1 == 0) {
idList.push(id);
counts.push(newCounts[i]);
indexPlus1[id] = idList.length; // idx+1
} else {
counts[idxp1 - 1] = newCounts[i];
}
unchecked {
++i;
}
}
emit EntriesUpserted(ids.length);
}
/// @notice Sets counts to zero for the given ids (pre-lock only).
/// @param ids List of ids to remove.
function removeEntries(uint64[] calldata ids) external onlyAdmin {
require(!isLocked, "season locked");
for (uint256 i = 0; i < ids.length; ) {
uint256 idxp1 = indexPlus1[ids[i]];
if (idxp1 != 0) counts[idxp1 - 1] = 0;
unchecked {
++i;
}
}
emit EntriesRemoved(ids.length);
}
// Lock snapshot
/// @notice Locks the season, compacts arrays in-place, and snapshots totals.
function lock() external onlyAdmin {
require(!isLocked, "already locked");
(uint256 end, uint256 total) = _compactInPlace();
nonZeroIdsAtLock = end;
totalEntriesAtLock = total;
remainingWeight = total;
if (winnersCount > end) winnersCount = end;
isLocked = true;
emit Locked(seasonId, end, total, winnersCount);
}
/// @notice Compacts idList/counts in-place via swap-with-last and returns (newLength, totalWeight).
/// @dev Updates indexPlus1 for moved IDs and deletes mapping for removed IDs. Physically pops tail.
/// @return end The new compacted logical length after removals.
/// @return total The total weight (sum of remaining counts) after compaction.
function _compactInPlace() internal returns (uint256 end, uint256 total) {
// Invariants during the loop:
// - We maintain an active region idList[0..end-1] and counts[0..end-1]. Elements in [end..len-1] are garbage.
// - Mapping correctness: For any position j in [0, end), indexPlus1[idList[j]] == j+1. For removed IDs, the
// mapping entry is deleted. We only ever set mapping for IDs we place inside [0, end).
// - Progress variables: 0 <= i <= end <= len. We increment i only when we keep an element. When we remove at i,
// we swap in the last element (end-1) and decrement end, then re-process the same i without incrementing.
// - Sum correctness: 'total' accumulates the sum of counts for all kept (non-zero) elements encountered so far.
// Removed elements contribute 0 to 'total'.
uint256 len = idList.length;
end = len; // new logical length after compaction
total = 0;
uint256 i = 0;
while (i < end) {
uint64 cid = idList[i];
uint64 c = counts[i];
if (c == 0) {
// Remove this ID by swapping in the last element (if different)
uint256 lastIndex = end - 1;
uint64 lastId = idList[lastIndex];
uint64 lastCount = counts[lastIndex];
// Delete mapping for removed id
delete indexPlus1[cid];
if (i != lastIndex) {
idList[i] = lastId;
counts[i] = lastCount;
// Update mapping for the moved id
indexPlus1[lastId] = i + 1;
}
// Shrink logical end; do not increment i to process the swapped-in element
end = lastIndex;
} else {
// Keep; accumulate total and advance
unchecked {
total += c;
}
unchecked {
++i;
}
}
}
// Physically shrink arrays to 'end'
while (idList.length > end) {
idList.pop();
counts.pop();
}
// Post-conditions: arrays compact; mapping mirrors idList; 'total' equals sum of remaining counts.
return (end, total);
}
/// @notice Unlocks a locked season before finalization (emergency only).
function emergencyUnlock() external onlyAdmin {
require(isLocked && !drawn, "must be locked and not finalized");
isLocked = false;
emit EmergencyUnlocked(seasonId);
}
// VRF Wrapper config
/// @notice Sets VRF wrapper config parameters.
/// @param _callbackGasLimit Gas limit for the wrapper callback.
/// @param _confirmations Number of block confirmations to wait.
function setWrapperConfig(
uint32 _callbackGasLimit,
uint16 _confirmations
) external onlyAdmin {
callbackGasLimit = _callbackGasLimit;
requestConfirmations = _confirmations;
}
/// @notice Sets the timeout window to allow retrying a stuck VRF request.
/// @param seconds_ Seconds to wait before allowing a retry (0 disables).
function setRequestTimeout(uint256 seconds_) external onlyAdmin {
// 0 disables timeout protection (not recommended)
requestTimeout = seconds_;
}
// --- Native funding (for wrapper payments) ---
// The VRF v2.5 Wrapper will pull native funds from this contract when using requestRandomnessPayInNative.
// Fund this contract by sending native tokens (ETH) directly to it. We expose receive/fallback and
// a simple withdraw for the owner.
/// @notice Accept native token funding for VRF payments.
receive() external payable {
emit NativeFundsReceived(msg.sender, msg.value);
}
/// @notice Fallback to accept native token funding.
fallback() external payable {
emit NativeFundsReceived(msg.sender, msg.value);
}
/// @notice Withdraws native funds to an address (owner-only).
/// @param to Recipient address.
/// @param amount Amount to withdraw.
function withdrawNative(
address payable to,
uint256 amount
) external onlyOwner {
require(to != address(0), "zero to");
require(amount > 0, "amount is zero");
// Strict-inequality friendly form: allow equality via logical OR
uint256 bal = address(this).balance;
require(bal > amount || bal == amount, "insufficient balance");
(bool ok, ) = to.call{value: amount}("");
require(ok, "withdraw failed");
emit NativeFundsWithdrawn(to, amount);
}
// Request randomness via wrapper
/// @notice Requests VRF randomness (native payment pulled from this contract's balance; non-payable like Chainlink sample).
/// @return requestId The VRF request identifier.
function requestDraw() external onlyAdmin returns (uint256 requestId) {
require(isLocked, "season not locked");
require(!drawn, "already finalized");
require(vrfSeed == 0, "seed already set");
if (lastRequestId != 0 && requestTimeout != 0) {
uint256 readyAt = requestTimestamp + requestTimeout;
// Strict-inequality friendly form: allow equality via logical OR
require(
block.timestamp > readyAt || block.timestamp == readyAt,
"pending; wait timeout"
);
}
require(winnersCount > 0, "winners count is zero");
// We only need a single 256-bit word; we stretch it deterministically
// for multiple picks during finalization. Requesting more words costs
// more and increases callback gas without improving security here.
bytes memory extraArgs = VRFV2PlusClient._argsToBytes(
VRFV2PlusClient.ExtraArgsV1({nativePayment: true})
);
uint256 feeWei;
(requestId, feeWei) = requestRandomnessPayInNative(
callbackGasLimit,
requestConfirmations,
1,
extraArgs
);
lastRequestId = requestId;
requestTimestamp = block.timestamp;
emit VRFRequested(requestId, true, seasonId);
emit VRFFeeCharged(requestId, feeWei, seasonId);
return requestId;
}
// VRF callback
/// @notice VRF callback hook sets the seed once; late/duplicate calls are ignored.
/// @param requestId The VRF request id.
/// @param randomWords Array with a single random word.
function fulfillRandomWords(
uint256 requestId,
uint256[] memory randomWords
) internal override {
/// @dev Idempotent callback: ignores late/duplicate callbacks instead of reverting.
if (!(isLocked && !drawn)) return;
if (vrfSeed != 0) return;
vrfSeed = randomWords[0];
// Clear pending markers to reflect success
lastRequestId = 0;
requestTimestamp = 0;
emit VRFSeedSet(requestId, vrfSeed, seasonId);
}
// Finalization
/// @notice Picks up to maxPicks winners (weighted without replacement) until winnersCount is reached.
/// @param maxPicks Maximum winners to finalize in this transaction. To be used to batch the operation in case revealing all winners in a single call passes the gas limits.
function finalizeWinners(uint256 maxPicks) external onlyAdmin {
// Trade-off rationale:
// - Pros: avoids O(n log n) SSTORE during upserts/lock; reduces gas on hot paths; no storage state to keep in sync
// across season resets; enables chunked finalize (we recompute a clean tree each call for maxPicks).
// - Cons: recomputation costs O(n log n) memory ops per call; acceptable since finalize is rare compared to upserts.
// - Security: we zero out picked counts and use remainingWeight so recomputation remains consistent across chunks.
require(isLocked, "season not locked");
require(vrfSeed != 0, "random seed not set");
require(winners.length < winnersCount, "all winners already finalized");
require(maxPicks > 0, "maxPicks must be > 0");
uint256 picks = _min(maxPicks, winnersCount - winners.length);
uint256 n = counts.length;
uint256[] memory fenwickMem = new uint256[](n + 1);
_buildFenwickFromCounts(fenwickMem);
uint256 bit = _largestPowerOfTwoLE(n);
for (uint256 i = 0; i < picks; ) {
bool done = _pickOneWinner(fenwickMem, bit, n);
if (done) return;
unchecked {
++i;
}
}
if (winners.length == winnersCount) {
drawn = true;
emit DrawFinalized(winners.length, seasonId);
}
}
/// @notice Builds Fenwick tree in memory from current counts[].
/// @param fenwickMem The Fenwick array (length n+1) to populate from counts[].
function _buildFenwickFromCounts(
uint256[] memory fenwickMem
) internal view {
uint256 n = counts.length;
for (uint256 i = 0; i < n; ) {
uint256 c = counts[i];
if (c > 0) _fenwickAddMem(fenwickMem, i + 1, c);
unchecked {
++i;
}
}
}
/// @notice Returns the largest power of two <= n using strict inequality form.
/// @param n The number of elements in the Fenwick tree (counts length).
/// @return bit The largest power-of-two step not exceeding n.
function _largestPowerOfTwoLE(
uint256 n
) internal pure returns (uint256 bit) {
bit = 1;
while ((bit << 1) < (n + 1)) {
bit <<= 1;
}
}
/// @notice Picks one winner, mutating fenwickMem, counts, remainingWeight; finalizes if none left.
/// @param fenwickMem The Fenwick array over current (non-zero) counts.
/// @param bit The largest power-of-two step size for Fenwick search.
/// @param n The number of entries (counts length).
/// @return done True if drawing completed in this call (remainingWeight == 0), and DrawFinalized emitted.
function _pickOneWinner(
uint256[] memory fenwickMem,
uint256 bit,
uint256 n
) internal returns (bool done) {
uint256 rw = remainingWeight;
if (rw == 0) {
drawn = true;
emit DrawFinalized(winners.length, seasonId);
return true;
}
uint256 rnd = uint256(
keccak256(abi.encode(vrfSeed, seasonId, winners.length))
);
uint256 ticket = rnd % rw;
uint256 idx = _fenwickFindMem(fenwickMem, bit, n, ticket + 1);
uint256 arrIdx = idx - 1;
uint64 idVal = idList[arrIdx];
uint64 cnt = counts[arrIdx];
winners.push(Winner({id: idVal, entriesAtLock: cnt}));
emit WinnerPicked(winners.length, idVal, cnt, seasonId);
if (cnt > 0) {
_fenwickSubMem(fenwickMem, idx, cnt);
unchecked {
remainingWeight = rw - cnt;
}
}
counts[arrIdx] = 0;
return false;
}
// Views
/// @notice Returns the current entries count for an id.
/// @param id The id to query.
/// @return entries The current count.
function entryOf(uint64 id) external view returns (uint64 entries) {
uint256 p = indexPlus1[id];
if (p == 0) return 0;
return counts[p - 1];
}
/// @notice Returns the winner at given 1-based rank.
/// @param rank1 1-based rank in winners array.
/// @return w Winner struct at that rank.
function getWinner(uint256 rank1) external view returns (Winner memory w) {
// Use strict inequality form
require(rank1 != 0 && rank1 < winners.length + 1, "rank out of bounds");
return winners[rank1 - 1];
}
/// @notice Returns parallel arrays of winner ids and their entries at lock.
/// @return ids Winner ids in order.
/// @return entriesAtLock Entries-at-lock for each winner.
function getWinners()
external
view
returns (uint64[] memory ids, uint64[] memory entriesAtLock)
{
uint256 n = winners.length;
ids = new uint64[](n);
entriesAtLock = new uint64[](n);
for (uint256 i = 0; i < n; ) {
Winner memory w = winners[i];
ids[i] = w.id;
entriesAtLock[i] = w.entriesAtLock;
unchecked {
++i;
}
}
}
/// @notice Computes live totals over entries regardless of lock state.
/// @dev O(n) over the current tracked ids. Safe for off-chain calls; avoid heavy on-chain use.
/// @return idsTracked Number of ids currently tracked (pre-lock may include zero-count ids).
/// @return nonZeroIds Number of ids with count > 0 at the moment of the call.
/// @return totalWeight Sum of all counts > 0 at the moment of the call.
function currentTotals()
external
view
returns (uint256 idsTracked, uint256 nonZeroIds, uint256 totalWeight)
{
uint256 len = idList.length;
idsTracked = len;
uint256 nz = 0;
uint256 sum = 0;
for (uint256 i = 0; i < len; ) {
uint256 c = counts[i];
if (c > 0) {
unchecked {
++nz;
sum += c;
}
}
unchecked {
++i;
}
}
return (idsTracked, nz, sum);
}
// Fenwick helpers (memory)
// Note: memory-only Fenwick helpers; we deliberately avoid storage writes for gas and simplicity reasons.
// Fenwick basics:
// - f[] encodes partial sums so that adding at index i updates f[i], f[i + LSB(i)], f[i + 2*LSB(i)], ...
// - LSB(i) (least significant set bit) is computed as i & (~i + 1) (two's complement trick).
// - Prefix sums and find-by-prefix operations run in O(log n).
/// @notice Fenwick tree point add (memory-only, 1-based index).
/// @param f Fenwick array (length n+1).
/// @param i Index (1-based).
/// @param delta Amount to add.
function _fenwickAddMem(
uint256[] memory f,
uint256 i,
uint256 delta
) internal pure {
uint256 n = f.length - 1;
uint256 limit = n + 1;
while (i < limit) {
f[i] += delta;
i += i & (~i + 1); // advance by LSB(i)
}
}
/// @notice Fenwick tree point subtract (memory-only, 1-based index).
/// @param f Fenwick array (length n+1).
/// @param i Index (1-based).
/// @param amount Amount to subtract.
function _fenwickSubMem(
uint256[] memory f,
uint256 i,
uint256 amount
) internal pure {
uint256 n = f.length - 1;
uint256 limit = n + 1;
while (i < limit) {
f[i] -= amount;
i += i & (~i + 1); // advance by LSB(i)
}
}
/// @notice Fenwick find-by-prefix-sum (memory-only).
/// @param f Fenwick array (length n+1).
/// @param bit Largest power-of-two step <= n.
/// @param n Number of elements (not including f[0]).
/// @param target The 1-based rank to find.
/// @return idx 1-based index of the found element.
function _fenwickFindMem(
uint256[] memory f,
uint256 bit,
uint256 n,
uint256 target
) internal pure returns (uint256 idx) {
idx = 0;
uint256 sum = 0;
uint256 limit = n + 1;
for (; bit > 0; bit >>= 1) {
uint256 next = idx + bit;
if (next < limit && sum + f[next] < target) {
sum += f[next];
idx = next;
}
}
return idx + 1;
}
// Utils
/// @notice Returns the smaller of two numbers.
/// @param a First number.
/// @param b Second number.
/// @return The minimum of a and b.
function _min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/// @notice Converts uint256 to decimal string without leading zeros.
/// @param value The value to convert.
/// @return str The decimal string.
function _toString(
uint256 value
) internal pure returns (string memory str) {
if (value == 0) return "0";
uint256 temp = value;
uint256 digits;
while (temp != 0) {
++digits;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
--digits;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
}
VRFV2PlusClient.sol 27 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
// End consumer library.
library VRFV2PlusClient {
// extraArgs will evolve to support new features
bytes4 public constant EXTRA_ARGS_V1_TAG = bytes4(keccak256("VRF ExtraArgsV1"));
struct ExtraArgsV1 {
bool nativePayment;
}
struct RandomWordsRequest {
bytes32 keyHash;
uint256 subId;
uint16 requestConfirmations;
uint32 callbackGasLimit;
uint32 numWords;
bytes extraArgs;
}
function _argsToBytes(
ExtraArgsV1 memory extraArgs
) internal pure returns (bytes memory bts) {
return abi.encodeWithSelector(EXTRA_ARGS_V1_TAG, extraArgs);
}
}
VRFV2PlusWrapperConsumerBase.sol 118 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol";
import {IVRFV2PlusWrapper} from "./interfaces/IVRFV2PlusWrapper.sol";
/**
*
* @notice Interface for contracts using VRF randomness through the VRF V2 wrapper
* ********************************************************************************
* @dev PURPOSE
*
* @dev Create VRF V2+ requests without the need for subscription management. Rather than creating
* @dev and funding a VRF V2+ subscription, a user can use this wrapper to create one off requests,
* @dev paying up front rather than at fulfillment.
*
* @dev Since the price is determined using the gas price of the request transaction rather than
* @dev the fulfillment transaction, the wrapper charges an additional premium on callback gas
* @dev usage, in addition to some extra overhead costs associated with the VRFV2Wrapper contract.
* *****************************************************************************
* @dev USAGE
*
* @dev Calling contracts must inherit from VRFV2PlusWrapperConsumerBase. The consumer must be funded
* @dev with enough LINK or ether to make the request, otherwise requests will revert. To request randomness,
* @dev call the 'requestRandomWords' function with the desired VRF parameters. This function handles
* @dev paying for the request based on the current pricing.
*
* @dev Consumers must implement the fullfillRandomWords function, which will be called during
* @dev fulfillment with the randomness result.
*/
abstract contract VRFV2PlusWrapperConsumerBase {
error OnlyVRFWrapperCanFulfill(address have, address want);
LinkTokenInterface internal immutable i_linkToken;
IVRFV2PlusWrapper public immutable i_vrfV2PlusWrapper;
/**
* @param _vrfV2PlusWrapper is the address of the VRFV2Wrapper contract
*/
constructor(
address _vrfV2PlusWrapper
) {
IVRFV2PlusWrapper vrfV2PlusWrapper = IVRFV2PlusWrapper(_vrfV2PlusWrapper);
i_linkToken = LinkTokenInterface(vrfV2PlusWrapper.link());
i_vrfV2PlusWrapper = vrfV2PlusWrapper;
}
/**
* @dev Requests randomness from the VRF V2+ wrapper.
*
* @param _callbackGasLimit is the gas limit that should be used when calling the consumer's
* fulfillRandomWords function.
* @param _requestConfirmations is the number of confirmations to wait before fulfilling the
* request. A higher number of confirmations increases security by reducing the likelihood
* that a chain re-org changes a published randomness outcome.
* @param _numWords is the number of random words to request.
*
* @return requestId is the VRF V2+ request ID of the newly created randomness request.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function requestRandomness(
uint32 _callbackGasLimit,
uint16 _requestConfirmations,
uint32 _numWords,
bytes memory extraArgs
) internal returns (uint256 requestId, uint256 reqPrice) {
reqPrice = i_vrfV2PlusWrapper.calculateRequestPrice(_callbackGasLimit, _numWords);
i_linkToken.transferAndCall(
address(i_vrfV2PlusWrapper), reqPrice, abi.encode(_callbackGasLimit, _requestConfirmations, _numWords, extraArgs)
);
return (i_vrfV2PlusWrapper.lastRequestId(), reqPrice);
}
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function requestRandomnessPayInNative(
uint32 _callbackGasLimit,
uint16 _requestConfirmations,
uint32 _numWords,
bytes memory extraArgs
) internal returns (uint256 requestId, uint256 requestPrice) {
requestPrice = i_vrfV2PlusWrapper.calculateRequestPriceNative(_callbackGasLimit, _numWords);
return (
i_vrfV2PlusWrapper.requestRandomWordsInNative{value: requestPrice}(
_callbackGasLimit, _requestConfirmations, _numWords, extraArgs
),
requestPrice
);
}
/**
* @notice fulfillRandomWords handles the VRF V2 wrapper response. The consuming contract must
* @notice implement it.
*
* @param _requestId is the VRF V2 request ID.
* @param _randomWords is the randomness result.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal virtual;
function rawFulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) external {
address vrfWrapperAddr = address(i_vrfV2PlusWrapper);
if (msg.sender != vrfWrapperAddr) {
revert OnlyVRFWrapperCanFulfill(msg.sender, vrfWrapperAddr);
}
fulfillRandomWords(_requestId, _randomWords);
}
/// @notice getBalance returns the native balance of the consumer contract
function getBalance() public view returns (uint256) {
return address(this).balance;
}
/// @notice getLinkToken returns the link token contract
function getLinkToken() public view returns (LinkTokenInterface) {
return i_linkToken;
}
}
ConfirmedOwner.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
constructor(
address newOwner
) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}
IVRFV2PlusWrapper.sol 83 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IVRFV2PlusWrapper {
/**
* @return the request ID of the most recent VRF V2 request made by this wrapper. This should only
* be relied option within the same transaction that the request was made.
*/
function lastRequestId() external view returns (uint256);
/**
* @notice Calculates the price of a VRF request with the given callbackGasLimit at the current
* @notice block.
*
* @dev This function relies on the transaction gas price which is not automatically set during
* @dev simulation. To estimate the price at a specific gas price, use the estimatePrice function.
*
* @param _callbackGasLimit is the gas limit used to estimate the price.
* @param _numWords is the number of words to request.
*/
function calculateRequestPrice(uint32 _callbackGasLimit, uint32 _numWords) external view returns (uint256);
/**
* @notice Calculates the price of a VRF request in native with the given callbackGasLimit at the current
* @notice block.
*
* @dev This function relies on the transaction gas price which is not automatically set during
* @dev simulation. To estimate the price at a specific gas price, use the estimatePrice function.
*
* @param _callbackGasLimit is the gas limit used to estimate the price.
* @param _numWords is the number of words to request.
*/
function calculateRequestPriceNative(uint32 _callbackGasLimit, uint32 _numWords) external view returns (uint256);
/**
* @notice Estimates the price of a VRF request with a specific gas limit and gas price.
*
* @dev This is a convenience function that can be called in simulation to better understand
* @dev pricing.
*
* @param _callbackGasLimit is the gas limit used to estimate the price.
* @param _numWords is the number of words to request.
* @param _requestGasPriceWei is the gas price in wei used for the estimation.
*/
function estimateRequestPrice(
uint32 _callbackGasLimit,
uint32 _numWords,
uint256 _requestGasPriceWei
) external view returns (uint256);
/**
* @notice Estimates the price of a VRF request in native with a specific gas limit and gas price.
*
* @dev This is a convenience function that can be called in simulation to better understand
* @dev pricing.
*
* @param _callbackGasLimit is the gas limit used to estimate the price.
* @param _numWords is the number of words to request.
* @param _requestGasPriceWei is the gas price in wei used for the estimation.
*/
function estimateRequestPriceNative(
uint32 _callbackGasLimit,
uint32 _numWords,
uint256 _requestGasPriceWei
) external view returns (uint256);
/**
* @notice Requests randomness from the VRF V2 wrapper, paying in native token.
*
* @param _callbackGasLimit is the gas limit for the request.
* @param _requestConfirmations number of request confirmations to wait before serving a request.
* @param _numWords is the number of words to request.
*/
function requestRandomWordsInNative(
uint32 _callbackGasLimit,
uint16 _requestConfirmations,
uint32 _numWords,
bytes calldata extraArgs
) external payable returns (uint256 requestId);
function link() external view returns (address);
function linkNativeFeed() external view returns (address);
}
LinkTokenInterface.sol 31 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface LinkTokenInterface {
function allowance(address owner, address spender) external view returns (uint256 remaining);
function approve(address spender, uint256 value) external returns (bool success);
function balanceOf(
address owner
) external view returns (uint256 balance);
function decimals() external view returns (uint8 decimalPlaces);
function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
function increaseApproval(address spender, uint256 subtractedValue) external;
function name() external view returns (string memory tokenName);
function symbol() external view returns (string memory tokenSymbol);
function totalSupply() external view returns (uint256 totalTokensIssued);
function transfer(address to, uint256 value) external returns (bool success);
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success);
function transferFrom(address from, address to, uint256 value) external returns (bool success);
}
ConfirmedOwnerWithProposal.sol 72 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IOwnable} from "../interfaces/IOwnable.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwnerWithProposal is IOwnable {
address private s_owner;
address private s_pendingOwner;
event OwnershipTransferRequested(address indexed from, address indexed to);
event OwnershipTransferred(address indexed from, address indexed to);
constructor(address newOwner, address pendingOwner) {
// solhint-disable-next-line gas-custom-errors
require(newOwner != address(0), "Cannot set owner to zero");
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
/// @notice Allows an owner to begin transferring ownership to a new address.
function transferOwnership(
address to
) public override onlyOwner {
_transferOwnership(to);
}
/// @notice Allows an ownership transfer to be completed by the recipient.
function acceptOwnership() external override {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_pendingOwner, "Must be proposed owner");
address oldOwner = s_owner;
s_owner = msg.sender;
s_pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
/// @notice Get the current owner
function owner() public view override returns (address) {
return s_owner;
}
/// @notice validate, transfer ownership, and emit relevant events
function _transferOwnership(
address to
) private {
// solhint-disable-next-line gas-custom-errors
require(to != msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
/// @notice validate access
function _validateOwnership() internal view {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_owner, "Only callable by owner");
}
/// @notice Reverts if called by anyone other than the contract owner.
modifier onlyOwner() {
_validateOwnership();
_;
}
}
IOwnable.sol 12 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external returns (address);
function transferOwnership(
address recipient
) external;
function acceptOwnership() external;
}
Read Contract
callbackGasLimit 0x24f74697 → uint32
currentTotals 0x89b66b0a → uint256, uint256, uint256
drawn 0xfd915ef2 → bool
entryOf 0x18e60a32 → uint64
getBalance 0x12065fe0 → uint256
getLinkToken 0xe76d5168 → address
getWinner 0x4129b2c9 → tuple
getWinners 0xdf15c37e → uint64[], uint64[]
i_vrfV2PlusWrapper 0x9ed0868d → address
isLocked 0xa4e2d634 → bool
isManager 0xf3ae2415 → bool
lastRequestId 0xfc2a88c3 → uint256
nonZeroIdsAtLock 0x12c515d3 → uint256
owner 0x8da5cb5b → address
remainingWeight 0x6e67b2a9 → uint256
requestConfirmations 0xb0fb162f → uint16
requestTimeout 0x3f20b4c9 → uint256
requestTimestamp 0x3e66a647 → uint256
seasonId 0x04922960 → string
totalEntriesAtLock 0x21f1d677 → uint256
vrfSeed 0x097315b5 → uint256
winners 0xa2fb1175 → uint64, uint64
winnersCount 0x49713811 → uint256
Write Contract 15 functions
These functions modify contract state and require a wallet transaction to execute.
acceptOwnership 0x79ba5097
No parameters
emergencyUnlock 0xf2c39992
No parameters
finalizeWinners 0x36d152a8
uint256 maxPicks
lock 0xf83d08ba
No parameters
rawFulfillRandomWords 0x1fe543e3
uint256 _requestId
uint256[] _randomWords
removeEntries 0x3e484010
uint64[] ids
requestDraw 0xa57848b6
No parameters
returns: uint256
setManagers 0xa57b8b14
address[] addrs
bool enabled
setRequestTimeout 0x622b6af4
uint256 seconds_
setWinnersCount 0xbe6e7a4f
uint256 n
setWrapperConfig 0x33096e68
uint32 _callbackGasLimit
uint16 _confirmations
startNewSeason 0xa86099ba
string newSeasonId
uint256 defaultWinners
transferOwnership 0xf2fde38b
address to
upsertEntries 0x5bc07e62
uint64[] ids
uint64[] newCounts
withdrawNative 0x07b18bde
address to
uint256 amount
Recent Transactions
No transactions found for this address