diff --git a/.openzeppelin/unknown-421613.json b/.openzeppelin/arbitrum-goerli.json similarity index 86% rename from .openzeppelin/unknown-421613.json rename to .openzeppelin/arbitrum-goerli.json index 2b1e9e36..24e485c6 100644 --- a/.openzeppelin/unknown-421613.json +++ b/.openzeppelin/arbitrum-goerli.json @@ -115,6 +115,16 @@ "address": "0xf0ac76EB619DA28ed6907965C17eDbFd16B15CE0", "txHash": "0x6af9b2809ba19a4709889d797bb0d9fd0237e1f77f1aeb8e8c48d605458dda21", "kind": "uups" + }, + { + "address": "0xdBC4779B69bdC0f657ba35648e434bAe042a00E0", + "txHash": "0x7e583894113c09b8e02723728092b4eea5b163e20780ac1594240b67854a66a8", + "kind": "uups" + }, + { + "address": "0xb727c1B1159B8731fE4a20C4C41106D6F85F276E", + "txHash": "0x1f886e5cae824e9467d4c2ec8416027514743c745829ac2ce5a23bf02e0f0e39", + "kind": "uups" } ], "impls": { @@ -9484,6 +9494,1468 @@ } } } + }, + "88f2844ab653a895340db2d77e25547c94ecf20623755ac05982c5edff450445": { + "address": "0xb5a356c93901E6E3111A160581383Fd1A532e2a2", + "txHash": "0xd986cc56c696068fc4fae90cd35d46e9f2f070dfd896c82681f5acf47f2cb7f2", + "layout": { + "solcVersion": "0.8.17", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC165Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41" + }, + { + "label": "_roles", + "offset": 0, + "slot": "101", + "type": "t_mapping(t_bytes32,t_struct(RoleData)179_storage)", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:62" + }, + { + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:260" + }, + { + "label": "_roleMembers", + "offset": 0, + "slot": "151", + "type": "t_mapping(t_bytes32,t_struct(AddressSet)3390_storage)", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:76" + }, + { + "label": "__gap", + "offset": 0, + "slot": "201", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC1967UpgradeUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:169" + }, + { + "label": "__gap", + "offset": 0, + "slot": "251", + "type": "t_array(t_uint256)50_storage", + "contract": "UUPSUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:111" + }, + { + "label": "locks", + "offset": 0, + "slot": "301", + "type": "t_mapping(t_bytes32,t_struct(Lock)4859_storage)", + "contract": "LockUpgradeable", + "src": "contracts/lock/LockUpgradeable.sol:18" + }, + { + "label": "lockWaitTime", + "offset": 0, + "slot": "302", + "type": "t_mapping(t_bytes32,t_uint256)", + "contract": "LockUpgradeable", + "src": "contracts/lock/LockUpgradeable.sol:19" + }, + { + "label": "__gap", + "offset": 0, + "slot": "303", + "type": "t_array(t_uint256)48_storage", + "contract": "LockUpgradeable", + "src": "contracts/lock/LockUpgradeable.sol:21" + }, + { + "label": "__gap_0", + "offset": 0, + "slot": "351", + "type": "t_array(t_uint256)500_storage", + "contract": "MarketV1", + "src": "contracts/enclaves/MarketV1.sol:27" + }, + { + "label": "__gap_1", + "offset": 0, + "slot": "851", + "type": "t_array(t_uint256)50_storage", + "contract": "MarketV1", + "src": "contracts/enclaves/MarketV1.sol:62" + }, + { + "label": "providers", + "offset": 0, + "slot": "901", + "type": "t_mapping(t_address,t_struct(Provider)3939_storage)", + "contract": "MarketV1", + "src": "contracts/enclaves/MarketV1.sol:97" + }, + { + "label": "__gap_2", + "offset": 0, + "slot": "902", + "type": "t_array(t_uint256)49_storage", + "contract": "MarketV1", + "src": "contracts/enclaves/MarketV1.sol:99" + }, + { + "label": "jobs", + "offset": 0, + "slot": "951", + "type": "t_mapping(t_bytes32,t_struct(Job)4135_storage)", + "contract": "MarketV1", + "src": "contracts/enclaves/MarketV1.sol:158" + }, + { + "label": "jobIndex", + "offset": 0, + "slot": "952", + "type": "t_uint256", + "contract": "MarketV1", + "src": "contracts/enclaves/MarketV1.sol:159" + }, + { + "label": "token", + "offset": 0, + "slot": "953", + "type": "t_contract(IERC20)3755", + "contract": "MarketV1", + "src": "contracts/enclaves/MarketV1.sol:161" + }, + { + "label": "__gap_3", + "offset": 0, + "slot": "954", + "type": "t_array(t_uint256)47_storage", + "contract": "MarketV1", + "src": "contracts/enclaves/MarketV1.sol:164" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)47_storage": { + "label": "uint256[47]", + "numberOfBytes": "1504" + }, + "t_array(t_uint256)48_storage": { + "label": "uint256[48]", + "numberOfBytes": "1536" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)500_storage": { + "label": "uint256[500]", + "numberOfBytes": "16000" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(IERC20)3755": { + "label": "contract IERC20", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_struct(Provider)3939_storage)": { + "label": "mapping(address => struct MarketV1.Provider)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(AddressSet)3390_storage)": { + "label": "mapping(bytes32 => struct EnumerableSetUpgradeable.AddressSet)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(Job)4135_storage)": { + "label": "mapping(bytes32 => struct MarketV1.Job)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(Lock)4859_storage)": { + "label": "mapping(bytes32 => struct LockUpgradeable.Lock)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(RoleData)179_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(AddressSet)3390_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)3075_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Job)4135_storage": { + "label": "struct MarketV1.Job", + "members": [ + { + "label": "metadata", + "type": "t_string_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "owner", + "type": "t_address", + "offset": 0, + "slot": "1" + }, + { + "label": "provider", + "type": "t_address", + "offset": 0, + "slot": "2" + }, + { + "label": "rate", + "type": "t_uint256", + "offset": 0, + "slot": "3" + }, + { + "label": "balance", + "type": "t_uint256", + "offset": 0, + "slot": "4" + }, + { + "label": "lastSettled", + "type": "t_uint256", + "offset": 0, + "slot": "5" + } + ], + "numberOfBytes": "192" + }, + "t_struct(Lock)4859_storage": { + "label": "struct LockUpgradeable.Lock", + "members": [ + { + "label": "unlockTime", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "iValue", + "type": "t_uint256", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Provider)3939_storage": { + "label": "struct MarketV1.Provider", + "members": [ + { + "label": "cp", + "type": "t_string_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(RoleData)179_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "0" + }, + { + "label": "adminRole", + "type": "t_bytes32", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Set)3075_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + } + } + }, + "46d16b236cd60daffbd54fed17b0fe9a4a23c0ff8d8c7718ea014e6f62dc4b6f": { + "address": "0x4051802cE442479763156ECC0e36069eD9a00B3D", + "txHash": "0xc97b5de4fd41657956755262ef0b1910f5481deb555a2057a65a477fc60c27db", + "layout": { + "solcVersion": "0.8.17", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC165Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41" + }, + { + "label": "_roles", + "offset": 0, + "slot": "101", + "type": "t_mapping(t_bytes32,t_struct(RoleData)179_storage)", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:62" + }, + { + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:260" + }, + { + "label": "_roleMembers", + "offset": 0, + "slot": "151", + "type": "t_mapping(t_bytes32,t_struct(AddressSet)4257_storage)", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:76" + }, + { + "label": "__gap", + "offset": 0, + "slot": "201", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC1967UpgradeUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:169" + }, + { + "label": "__gap", + "offset": 0, + "slot": "251", + "type": "t_array(t_uint256)50_storage", + "contract": "UUPSUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:111" + }, + { + "label": "__gap0", + "offset": 0, + "slot": "301", + "type": "t_array(t_uint256)500_storage", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:28" + }, + { + "label": "__gap1", + "offset": 0, + "slot": "801", + "type": "t_array(t_uint256)50_storage", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:63" + }, + { + "label": "clusterRewards", + "offset": 0, + "slot": "851", + "type": "t_mapping(t_address,t_uint256)", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:119" + }, + { + "label": "rewardWeight", + "offset": 0, + "slot": "852", + "type": "t_mapping(t_bytes32,t_uint256)", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:121" + }, + { + "label": "totalRewardWeight", + "offset": 0, + "slot": "853", + "type": "t_uint256", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:122" + }, + { + "label": "totalRewardsPerEpoch", + "offset": 0, + "slot": "854", + "type": "t_uint256", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:123" + }, + { + "label": "__unused_payoutDenomination", + "offset": 0, + "slot": "855", + "type": "t_uint256", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:125" + }, + { + "label": "__unused_rewardDistributedPerEpoch", + "offset": 0, + "slot": "856", + "type": "t_mapping(t_uint256,t_uint256)", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:128" + }, + { + "label": "__unused_latestNewEpochRewardAt", + "offset": 0, + "slot": "857", + "type": "t_uint256", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:130" + }, + { + "label": "__unused_rewardDistributionWaitTime", + "offset": 0, + "slot": "858", + "type": "t_uint256", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:132" + }, + { + "label": "ticketsIssued", + "offset": 0, + "slot": "859", + "type": "t_mapping(t_address,t_mapping(t_uint256,t_uint256))", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:134" + }, + { + "label": "clusterSelectors", + "offset": 0, + "slot": "860", + "type": "t_mapping(t_bytes32,t_contract(IClusterSelector)7307)", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:135" + }, + { + "label": "receiverStaking", + "offset": 0, + "slot": "861", + "type": "t_contract(ReceiverStaking)7098", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:136" + }, + { + "label": "receiverRewardPayment", + "offset": 0, + "slot": "862", + "type": "t_mapping(t_address,t_struct(ReceiverPayment)7106_storage)", + "contract": "ClusterRewards", + "src": "contracts/staking/ClusterRewards.sol:430" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)500_storage": { + "label": "uint256[500]", + "numberOfBytes": "16000" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(IClusterSelector)7307": { + "label": "contract IClusterSelector", + "numberOfBytes": "20" + }, + "t_contract(ReceiverStaking)7098": { + "label": "contract ReceiverStaking", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_mapping(t_uint256,t_uint256))": { + "label": "mapping(address => mapping(uint256 => uint256))", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_struct(ReceiverPayment)7106_storage)": { + "label": "mapping(address => struct IClusterRewards.ReceiverPayment)", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_contract(IClusterSelector)7307)": { + "label": "mapping(bytes32 => contract IClusterSelector)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(AddressSet)4257_storage)": { + "label": "mapping(bytes32 => struct EnumerableSetUpgradeable.AddressSet)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(RoleData)179_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_uint256,t_uint256)": { + "label": "mapping(uint256 => uint256)", + "numberOfBytes": "32" + }, + "t_struct(AddressSet)4257_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)3942_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_struct(ReceiverPayment)7106_storage": { + "label": "struct IClusterRewards.ReceiverPayment", + "members": [ + { + "label": "rewardRemaining", + "type": "t_uint128", + "offset": 0, + "slot": "0" + }, + { + "label": "rewardPerEpoch", + "type": "t_uint128", + "offset": 16, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(RoleData)179_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "0" + }, + { + "label": "adminRole", + "type": "t_bytes32", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Set)3942_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint128": { + "label": "uint128", + "numberOfBytes": "16" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + } + } + }, + "5d770492a9ed8cdd6c443261651f6fa7abc25718a92ce46cfa06b3bc6ae37e44": { + "address": "0x177DDBB47ff73B9A884F23061c287B142e274c68", + "txHash": "0x554414232d828dc1c9f83c9bc8e22703460649c344eb77aee11c7847d0c14024", + "layout": { + "solcVersion": "0.8.17", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC165Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41" + }, + { + "label": "_roles", + "offset": 0, + "slot": "101", + "type": "t_mapping(t_bytes32,t_struct(RoleData)179_storage)", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:62" + }, + { + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:260" + }, + { + "label": "_roleMembers", + "offset": 0, + "slot": "151", + "type": "t_mapping(t_bytes32,t_struct(AddressSet)4669_storage)", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:76" + }, + { + "label": "__gap", + "offset": 0, + "slot": "201", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC1967UpgradeUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:169" + }, + { + "label": "__gap", + "offset": 0, + "slot": "251", + "type": "t_array(t_uint256)50_storage", + "contract": "UUPSUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:111" + }, + { + "label": "__gap0", + "offset": 0, + "slot": "301", + "type": "t_array(t_uint256)500_storage", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:31" + }, + { + "label": "__gap1", + "offset": 0, + "slot": "801", + "type": "t_array(t_uint256)50_storage", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:71" + }, + { + "label": "clusters", + "offset": 0, + "slot": "851", + "type": "t_mapping(t_address,t_struct(Cluster)8677_storage)", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:146" + }, + { + "label": "stakeAddress", + "offset": 0, + "slot": "852", + "type": "t_address", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:148" + }, + { + "label": "rewardFactor", + "offset": 0, + "slot": "853", + "type": "t_mapping(t_bytes32,t_uint256)", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:149" + }, + { + "label": "tokenIndex", + "offset": 0, + "slot": "854", + "type": "t_mapping(t_bytes32,t_uint256)", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:150" + }, + { + "label": "tokenList", + "offset": 0, + "slot": "855", + "type": "t_array(t_bytes32)dyn_storage", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:151" + }, + { + "label": "clusterRewards", + "offset": 0, + "slot": "856", + "type": "t_contract(IClusterRewards)10429", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:152" + }, + { + "label": "clusterRegistry", + "offset": 0, + "slot": "857", + "type": "t_contract(IClusterRegistry)10337", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:153" + }, + { + "label": "PONDToken", + "offset": 0, + "slot": "858", + "type": "t_contract(IERC20Upgradeable)1938", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:154" + }, + { + "label": "thresholdForSelection", + "offset": 0, + "slot": "859", + "type": "t_mapping(t_bytes32,t_uint256)", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:156" + }, + { + "label": "tokenWeights", + "offset": 0, + "slot": "860", + "type": "t_mapping(t_bytes32,t_struct(TokenWeight)8682_storage)", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:157" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)500_storage": { + "label": "uint256[500]", + "numberOfBytes": "16000" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(IClusterRegistry)10337": { + "label": "contract IClusterRegistry", + "numberOfBytes": "20" + }, + "t_contract(IClusterRewards)10429": { + "label": "contract IClusterRewards", + "numberOfBytes": "20" + }, + "t_contract(IERC20Upgradeable)1938": { + "label": "contract IERC20Upgradeable", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_mapping(t_bytes32,t_uint256))": { + "label": "mapping(address => mapping(bytes32 => uint256))", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_struct(Cluster)8677_storage)": { + "label": "mapping(address => struct RewardDelegators.Cluster)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(AddressSet)4669_storage)": { + "label": "mapping(bytes32 => struct EnumerableSetUpgradeable.AddressSet)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(RoleData)179_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(TokenWeight)8682_storage)": { + "label": "mapping(bytes32 => struct RewardDelegators.TokenWeight)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)", + "numberOfBytes": "32" + }, + "t_struct(AddressSet)4669_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)4354_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Cluster)8677_storage": { + "label": "struct RewardDelegators.Cluster", + "members": [ + { + "label": "totalDelegations", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "0" + }, + { + "label": "delegators", + "type": "t_mapping(t_address,t_mapping(t_bytes32,t_uint256))", + "offset": 0, + "slot": "1" + }, + { + "label": "rewardDebt", + "type": "t_mapping(t_address,t_mapping(t_bytes32,t_uint256))", + "offset": 0, + "slot": "2" + }, + { + "label": "accRewardPerShare", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "3" + } + ], + "numberOfBytes": "128" + }, + "t_struct(RoleData)179_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "0" + }, + { + "label": "adminRole", + "type": "t_bytes32", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Set)4354_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(TokenWeight)8682_storage": { + "label": "struct RewardDelegators.TokenWeight", + "members": [ + { + "label": "forThreshold", + "type": "t_uint128", + "offset": 0, + "slot": "0" + }, + { + "label": "forDelegation", + "type": "t_uint128", + "offset": 16, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_uint128": { + "label": "uint128", + "numberOfBytes": "16" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + } + } + }, + "b8b99a29c046025f0eab1958c68021190640be594bb0622c4480794cf22d0df0": { + "address": "0xE4F1436844136f1c97B6300d024B4630179CFCf8", + "txHash": "0x7ed20dab48c884e99c7db63b172f69a99f300c0dc8aca7b08eee50b82dcaf8a3", + "layout": { + "solcVersion": "0.8.17", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "label": "__gap", + "offset": 0, + "slot": "51", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC165Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol:41" + }, + { + "label": "_roles", + "offset": 0, + "slot": "101", + "type": "t_mapping(t_bytes32,t_struct(RoleData)179_storage)", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:62" + }, + { + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol:260" + }, + { + "label": "_roleMembers", + "offset": 0, + "slot": "151", + "type": "t_mapping(t_bytes32,t_struct(AddressSet)3802_storage)", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "AccessControlEnumerableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol:76" + }, + { + "label": "__gap", + "offset": 0, + "slot": "201", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC1967UpgradeUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:169" + }, + { + "label": "__gap", + "offset": 0, + "slot": "251", + "type": "t_array(t_uint256)50_storage", + "contract": "UUPSUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:111" + }, + { + "label": "__gap0", + "offset": 0, + "slot": "301", + "type": "t_array(t_uint256)500_storage", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:31" + }, + { + "label": "__gap1", + "offset": 0, + "slot": "801", + "type": "t_array(t_uint256)50_storage", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:71" + }, + { + "label": "clusters", + "offset": 0, + "slot": "851", + "type": "t_mapping(t_address,t_struct(Cluster)5260_storage)", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:146" + }, + { + "label": "stakeAddress", + "offset": 0, + "slot": "852", + "type": "t_address", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:148" + }, + { + "label": "rewardFactor", + "offset": 0, + "slot": "853", + "type": "t_mapping(t_bytes32,t_uint256)", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:149" + }, + { + "label": "tokenIndex", + "offset": 0, + "slot": "854", + "type": "t_mapping(t_bytes32,t_uint256)", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:150" + }, + { + "label": "tokenList", + "offset": 0, + "slot": "855", + "type": "t_array(t_bytes32)dyn_storage", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:151" + }, + { + "label": "clusterRewards", + "offset": 0, + "slot": "856", + "type": "t_contract(IClusterRewards)7012", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:152" + }, + { + "label": "clusterRegistry", + "offset": 0, + "slot": "857", + "type": "t_contract(IClusterRegistry)6920", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:153" + }, + { + "label": "PONDToken", + "offset": 0, + "slot": "858", + "type": "t_contract(IERC20Upgradeable)1326", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:154" + }, + { + "label": "thresholdForSelection", + "offset": 0, + "slot": "859", + "type": "t_mapping(t_bytes32,t_uint256)", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:156" + }, + { + "label": "tokenWeights", + "offset": 0, + "slot": "860", + "type": "t_mapping(t_bytes32,t_struct(TokenWeight)5265_storage)", + "contract": "RewardDelegators", + "src": "contracts/staking/RewardDelegators.sol:157" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)500_storage": { + "label": "uint256[500]", + "numberOfBytes": "16000" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(IClusterRegistry)6920": { + "label": "contract IClusterRegistry", + "numberOfBytes": "20" + }, + "t_contract(IClusterRewards)7012": { + "label": "contract IClusterRewards", + "numberOfBytes": "20" + }, + "t_contract(IERC20Upgradeable)1326": { + "label": "contract IERC20Upgradeable", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_mapping(t_bytes32,t_uint256))": { + "label": "mapping(address => mapping(bytes32 => uint256))", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_struct(Cluster)5260_storage)": { + "label": "mapping(address => struct RewardDelegators.Cluster)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(AddressSet)3802_storage)": { + "label": "mapping(bytes32 => struct EnumerableSetUpgradeable.AddressSet)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(RoleData)179_storage)": { + "label": "mapping(bytes32 => struct AccessControlUpgradeable.RoleData)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_struct(TokenWeight)5265_storage)": { + "label": "mapping(bytes32 => struct RewardDelegators.TokenWeight)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)", + "numberOfBytes": "32" + }, + "t_struct(AddressSet)3802_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)3487_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Cluster)5260_storage": { + "label": "struct RewardDelegators.Cluster", + "members": [ + { + "label": "totalDelegations", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "0" + }, + { + "label": "delegators", + "type": "t_mapping(t_address,t_mapping(t_bytes32,t_uint256))", + "offset": 0, + "slot": "1" + }, + { + "label": "rewardDebt", + "type": "t_mapping(t_address,t_mapping(t_bytes32,t_uint256))", + "offset": 0, + "slot": "2" + }, + { + "label": "accRewardPerShare", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "3" + } + ], + "numberOfBytes": "128" + }, + "t_struct(RoleData)179_storage": { + "label": "struct AccessControlUpgradeable.RoleData", + "members": [ + { + "label": "members", + "type": "t_mapping(t_address,t_bool)", + "offset": 0, + "slot": "0" + }, + { + "label": "adminRole", + "type": "t_bytes32", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Set)3487_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(TokenWeight)5265_storage": { + "label": "struct RewardDelegators.TokenWeight", + "members": [ + { + "label": "forThreshold", + "type": "t_uint128", + "offset": 0, + "slot": "0" + }, + { + "label": "forDelegation", + "type": "t_uint128", + "offset": 16, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_uint128": { + "label": "uint128", + "numberOfBytes": "16" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + } + } } } } diff --git a/address.json b/address.json index cda968e4..b5508459 100644 --- a/address.json +++ b/address.json @@ -40,7 +40,6 @@ }, "421613": { "Pond": "0xa9472a9C135Cb33221de441a4DEd393FD515a69C", - "MarketV1": "0x29cbA3c69CB4Cf89a4e8b5f698df7C607A488e4f", "MPond": "0x0B3d9b224496C2A2Fa1a4096D8EB4D350eFd9079", "Bridge": "0xfeEa9a34e51e4E90b8B62F38120A345650164110", "RewardDelegators": "0xe62CC1181Fb74c1954f13246b6863A8E26a0D77d", @@ -48,6 +47,7 @@ "ClusterRegistry": "0x1fe9f98C4c0eC29a012f8B8fFDe962a13fCECe1E", "ClusterSelector_ETH": "0x9c17478fC7300AB513111d93455Bee5105792FB4", "ReceiverStaking": "0x49fB661B8Cd1A76DEF8E11eB6d16e7c61C2C9270", - "ClusterRewards": "0xf0ac76EB619DA28ed6907965C17eDbFd16B15CE0" + "ClusterRewards": "0xf0ac76EB619DA28ed6907965C17eDbFd16B15CE0", + "MarketV1": "0xb727c1B1159B8731fE4a20C4C41106D6F85F276E" } } \ No newline at end of file diff --git a/contracts/staking/ClusterRewards.sol b/contracts/staking/ClusterRewards.sol index 04ef6c47..ad0eb60c 100644 --- a/contracts/staking/ClusterRewards.sol +++ b/contracts/staking/ClusterRewards.sol @@ -8,6 +8,7 @@ import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeabl import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; import "./interfaces/IClusterSelector.sol"; import "./ReceiverStaking.sol"; @@ -105,7 +106,6 @@ contract ClusterRewards is } totalRewardWeight = _weight; _changeRewardPerEpoch(_totalRewardsPerEpoch); - payoutDenomination = 1e18; } //-------------------------------- Initializer end --------------------------------// @@ -113,8 +113,7 @@ contract ClusterRewards is //-------------------------------- Admin functions start --------------------------------// bytes32 public constant CLAIMER_ROLE = keccak256("CLAIMER_ROLE"); - bytes32 public constant FEEDER_ROLE = keccak256("FEEDER_ROLE"); - uint256 public constant RECEIVER_TICKETS_PER_EPOCH = 2**16; + uint256 public constant RECEIVER_TICKETS_PER_EPOCH = type(uint16).max; uint256 constant SWITCHING_PERIOD = 33 days; mapping(address => uint256) public clusterRewards; @@ -122,11 +121,11 @@ contract ClusterRewards is mapping(bytes32 => uint256) public rewardWeight; uint256 public totalRewardWeight; uint256 public totalRewardsPerEpoch; - uint256 public payoutDenomination; + uint256 public __unused_payoutDenomination; - mapping(uint256 => uint256) public rewardDistributedPerEpoch; - uint256 public latestNewEpochRewardAt; - uint256 public rewardDistributionWaitTime; + mapping(uint256 => uint256) public __unused_rewardDistributedPerEpoch; + uint256 public __unused_latestNewEpochRewardAt; + uint256 public __unused_rewardDistributionWaitTime; mapping(address => mapping(uint256 => uint256)) public ticketsIssued; mapping(bytes32 => IClusterSelector) public clusterSelectors; // networkId -> clusterSelector @@ -139,12 +138,7 @@ contract ClusterRewards is event ReceiverStakingUpdated(address receiverStaking); event RewardPerEpochChanged(uint256 updatedRewardPerEpoch); event RewardDistributionWaitTimeChanged(uint256 updatedWaitTime); - event TicketsIssued(bytes32 indexed networkId, uint256 indexed epoch, address indexed user); - - modifier onlyFeeder() { - require(hasRole(FEEDER_ROLE, _msgSender()), "only feeder"); - _; - } + event TicketsIssued(bytes32 indexed networkId, uint256 indexed epoch, address indexed signer); function addNetwork(bytes32 _networkId, uint256 _rewardWeight, address _clusterSelector) external onlyAdmin { require(rewardWeight[_networkId] == 0, "CRW:AN-Network already exists"); @@ -205,68 +199,40 @@ contract ClusterRewards is emit RewardPerEpochChanged(_updatedRewardPerEpoch); } - function updateRewardWaitTime(uint256 _updatedWaitTime) external onlyAdmin { - _updateRewardWaitTime(_updatedWaitTime); - } - - function _updateRewardWaitTime(uint256 _updatedWaitTime) internal { - rewardDistributionWaitTime = _updatedWaitTime; - emit RewardDistributionWaitTimeChanged(_updatedWaitTime); - } - //-------------------------------- Admin functions end --------------------------------// //-------------------------------- User functions start --------------------------------// - function feed( - bytes32 _networkId, - address[] calldata _clusters, - uint256[] calldata _payouts, - uint256 _epoch - ) external onlyFeeder { - require(receiverStaking.START_TIME() + SWITCHING_PERIOD + 1 days > block.timestamp, "CRW:F-Invalid method"); - uint256 rewardDistributed = rewardDistributedPerEpoch[_epoch]; - if(rewardDistributed == 0) { - require( - block.timestamp > latestNewEpochRewardAt + rewardDistributionWaitTime, - "CRW:F-Cant distribute reward for new epoch within such short interval" - ); - latestNewEpochRewardAt = block.timestamp; - } - uint256 currentPayoutDenomination = payoutDenomination; - uint256 networkRewardWeight = rewardWeight[_networkId]; - uint256 currentTotalRewardsPerEpoch = totalRewardsPerEpoch*1 days/receiverStaking.EPOCH_LENGTH()*networkRewardWeight/totalRewardWeight; - for(uint256 i=0; i < _clusters.length; i++) { - uint256 clusterReward = (currentTotalRewardsPerEpoch * _payouts[i]) / currentPayoutDenomination; - rewardDistributed = rewardDistributed + clusterReward; - clusterRewards[_clusters[i]] = clusterRewards[_clusters[i]] + clusterReward; + function _getRewardShare( + uint256 _totalNetworkRewardsPerEpoch, + uint256 _epochTotalStake, + uint256 _epochReceiverStake + ) internal pure returns (uint256 _rewardShare) { + unchecked { + // Note: multiplication can't overflow as max token supply is 10^38, hence max value of multiplication is 10^38*10^38 < 2^256 + _rewardShare = (_totalNetworkRewardsPerEpoch * _epochReceiverStake) / _epochTotalStake; } - require( - rewardDistributed <= currentTotalRewardsPerEpoch, - "CRW:F-Reward Distributed cant be more than totalRewardPerEpoch" - ); - rewardDistributedPerEpoch[_epoch] = rewardDistributed; - emit ClusterRewarded(_networkId); } - function _processReceiverTickets(address _receiver, uint256 _epoch, address[] memory _selectedClusters, uint16[] memory _tickets, uint256 _totalNetworkRewardsPerEpoch, uint256 _epochTotalStake, uint256 _epochReceiverStake) internal { + function _processReceiverTickets(address _receiver, uint256 _epoch, address[] memory _selectedClusters, uint16[] memory _tickets, uint256 _rewardShare) internal { require(!_isTicketsIssued(_receiver, _epoch), "CRW:IPRT-Tickets already issued"); - unchecked { require(_selectedClusters.length <= _tickets.length + 1, "CRW:IPRT-Tickets length not matching selected clusters"); - uint256 _rewardShare = _totalNetworkRewardsPerEpoch * _epochReceiverStake / _epochTotalStake; + uint256 _totalTickets; uint256 i; - for(; i < _selectedClusters.length - 1; ++i) { + for (; i < _selectedClusters.length - 1; ++i) { // cant overflow as max supply of POND is 1e28, so max value of multiplication is 1e28*2^16 < uint256 // value that can be added per iteration is < 1e28*2^16/2^16, so clusterRewards for cluster cant overflow - clusterRewards[_selectedClusters[i]] += _rewardShare * uint256(_tickets[i]) / RECEIVER_TICKETS_PER_EPOCH; + clusterRewards[_selectedClusters[i]] += (_rewardShare * uint256(_tickets[i])) / RECEIVER_TICKETS_PER_EPOCH; // cant overflow as tickets[i] <= 2^16 _totalTickets += uint256(_tickets[i]); } require(RECEIVER_TICKETS_PER_EPOCH >= _totalTickets, "CRW:IPRT-Total ticket count invalid"); - clusterRewards[_selectedClusters[i]] += _rewardShare * uint256(RECEIVER_TICKETS_PER_EPOCH - _totalTickets)/RECEIVER_TICKETS_PER_EPOCH; + clusterRewards[_selectedClusters[i]] += + (_rewardShare * (RECEIVER_TICKETS_PER_EPOCH - _totalTickets)) / + RECEIVER_TICKETS_PER_EPOCH; } _markAsIssued(_receiver, _epoch); @@ -295,28 +261,40 @@ contract ClusterRewards is } function issueTickets(bytes32 _networkId, uint24[] calldata _epochs, uint16[][] calldata _tickets) external { - uint256 numberOfEpochs = _epochs.length; - require(numberOfEpochs == _tickets.length, "CRW:MIT-invalid inputs"); + require(_epochs.length == _tickets.length, "CRW:MIT-invalid inputs"); + + address _receiver = receiverStaking.signerToStaker(msg.sender); + + ReceiverPayment memory receiverPayment = receiverRewardPayment[_receiver]; + uint256 _totalNetworkRewardsPerEpoch = getRewardForEpoch(_networkId); + uint256 _rewardToGive; + unchecked { - for(uint256 i=0; i < numberOfEpochs; ++i) { - uint256 _totalNetworkRewardsPerEpoch = getRewardForEpoch(_epochs[i], _networkId); - (uint256 _epochTotalStake, uint256 _currentEpoch) = receiverStaking.getEpochInfo(_epochs[i]); - require(_epochs[i] < _currentEpoch, "CRW:IT-Epoch not completed"); + for (uint256 i = 0; i < _epochs.length; ++i) { + _rewardToGive = MathUpgradeable.min(receiverPayment.rewardRemaining, receiverPayment.rewardPerEpoch); + uint256 _epochTotalStake; + { + uint256 _currentEpoch; + (_epochTotalStake, _currentEpoch) = receiverStaking.getEpochInfo(_epochs[i]); + + require(_epochs[i] < _currentEpoch, "CRW:IT-Epoch not completed"); + } address[] memory _selectedClusters = clusterSelectors[_networkId].getClusters(_epochs[i]); - (uint256 _epochReceiverStake, address _receiver) = receiverStaking.balanceOfSignerAt(msg.sender, _epochs[i]); - _processReceiverTickets(_receiver, _epochs[i], _selectedClusters, _tickets[i], _totalNetworkRewardsPerEpoch, _epochTotalStake, _epochReceiverStake); + uint256 _epochReceiverStake = receiverStaking.balanceOfAt(_receiver, _epochs[i]); + + uint256 _rewardShare = _getRewardShare(_totalNetworkRewardsPerEpoch, _epochTotalStake, _epochReceiverStake) + _rewardToGive; + + _processReceiverTickets(_receiver, _epochs[i], _selectedClusters, _tickets[i], _rewardShare); + // Note: no checks before casting as inputs are uint128 + receiverPayment.rewardRemaining -= uint128(_rewardToGive); emit TicketsIssued(_networkId, _epochs[i], msg.sender); } } + receiverRewardPayment[_receiver].rewardRemaining = receiverPayment.rewardRemaining; } function issueTickets(bytes calldata _ticketInfo) external { - ( - bytes32 _networkId, - uint256 _fromEpoch, - uint256 _noOfEpochs, - uint16[][] memory _tickets - ) = _parseTicketInfo(_ticketInfo); + (bytes32 _networkId, uint256 _fromEpoch, uint256 _noOfEpochs, uint16[][] memory _tickets) = _parseTicketInfo(_ticketInfo); ReceiverStaking _receiverStaking = receiverStaking; require(_fromEpoch + _noOfEpochs <= _receiverStaking.getCurrentEpoch(), "CRW:ITC-Epochs not completed"); @@ -324,17 +302,25 @@ contract ClusterRewards is uint256[] memory _stakes = _receiverStaking.totalSupplyAtRanged(_fromEpoch, _noOfEpochs); (uint256[] memory _balances, address _receiver) = _receiverStaking.balanceOfSignerAtRanged(msg.sender, _fromEpoch, _noOfEpochs); address[][] memory _selectedClusters = clusterSelectors[_networkId].getClustersRanged(_fromEpoch, _noOfEpochs); - uint256 _epochLength = _receiverStaking.EPOCH_LENGTH(); - uint256 _totalNetworkRewardsPerEpoch; + + ReceiverPayment memory receiverPayment = receiverRewardPayment[_receiver]; + uint256 _totalNetworkRewardsPerEpoch = getRewardForEpoch(_networkId); + uint256 _rewardToGive; unchecked { - for(uint256 i=0; i < _noOfEpochs; ++i) { - _totalNetworkRewardsPerEpoch = _getRewardForEpoch(_fromEpoch, _networkId, _epochLength); - _processReceiverTickets(_receiver, _fromEpoch, _selectedClusters[i], _tickets[i], _totalNetworkRewardsPerEpoch, _stakes[i], _balances[i]); + for (uint256 i = 0; i < _noOfEpochs; ++i) { + _rewardToGive = MathUpgradeable.min(receiverPayment.rewardRemaining, receiverPayment.rewardPerEpoch); + uint256 _rewardShare = _getRewardShare(_totalNetworkRewardsPerEpoch, _stakes[i], _balances[i]) + _rewardToGive; + + _processReceiverTickets(_receiver, _fromEpoch, _selectedClusters[i], _tickets[i], _rewardShare); emit TicketsIssued(_networkId, _fromEpoch, msg.sender); + // Note: no checks before casting as inputs are uint128 + receiverPayment.rewardRemaining -= uint128(_rewardToGive); ++_fromEpoch; } } + + receiverRewardPayment[_receiver].rewardRemaining = receiverPayment.rewardRemaining; } function _parseTicketInfo(bytes memory ticketInfo) internal view returns( @@ -402,14 +388,21 @@ contract ClusterRewards is require(_epoch < _currentEpoch, "CRW:IT-Epoch not completed"); - address[] memory _selectedClusters = clusterSelectors[_networkId].getClusters(_epoch); + (uint256 _epochReceiverStake, address _receiver) = receiverStaking.balanceOfSignerAt(msg.sender, _epoch); + ReceiverPayment memory receiverPayment = receiverRewardPayment[_receiver]; - uint256 _totalNetworkRewardsPerEpoch = getRewardForEpoch(_epoch, _networkId); + address[] memory _selectedClusters = clusterSelectors[_networkId].getClusters(_epoch); + uint256 _totalNetworkRewardsPerEpoch = getRewardForEpoch(_networkId); + uint256 _rewardToGive = MathUpgradeable.min(receiverPayment.rewardRemaining, receiverPayment.rewardPerEpoch); - (uint256 _epochReceiverStake, address _receiver) = receiverStaking.balanceOfSignerAt(msg.sender, _epoch); - _processReceiverTickets(_receiver, _epoch, _selectedClusters, _tickets, _totalNetworkRewardsPerEpoch, _epochTotalStake, _epochReceiverStake); + uint256 _rewardShare = _getRewardShare(_totalNetworkRewardsPerEpoch, _epochTotalStake, _epochReceiverStake) + _rewardToGive; + _processReceiverTickets(_receiver, _epoch, _selectedClusters, _tickets, _rewardShare); emit TicketsIssued(_networkId, _epoch, msg.sender); + + // Note: no checks before casting as inputs are uint128 + receiverPayment.rewardRemaining -= uint128(_rewardToGive); + receiverRewardPayment[_receiver].rewardRemaining = receiverPayment.rewardRemaining; } function claimReward(address _cluster) external onlyRole(CLAIMER_ROLE) returns(uint256) { @@ -422,14 +415,22 @@ contract ClusterRewards is return 0; } - function _getRewardForEpoch(uint256 _epoch, bytes32 _networkId, uint256 _epochLength) internal view returns(uint256) { - if(_epoch < SWITCHING_PERIOD/_epochLength) return 0; + function getRewardForEpoch(bytes32 _networkId) public view override returns (uint256) { return (totalRewardsPerEpoch * rewardWeight[_networkId]) / totalRewardWeight; } - function getRewardForEpoch(uint256 _epoch, bytes32 _networkId) public view returns(uint256) { - return _getRewardForEpoch(_epoch, _networkId, receiverStaking.EPOCH_LENGTH()); +//-------------------------------- User functions end --------------------------------// + + bytes32 public constant RECEIVER_PAYMENTS_MANAGER = keccak256("RECEIVER_PAYMENTS_MANAGER"); + + mapping(address => ReceiverPayment) public receiverRewardPayment; + + function _increaseReceiverBalance(address staker, uint128 amount) external override onlyRole(RECEIVER_PAYMENTS_MANAGER) { + receiverRewardPayment[staker].rewardRemaining += amount; } -//-------------------------------- User functions end --------------------------------// -} + function _setReceiverRewardPerEpoch(address staker, uint128 rewardPerEpoch) external override onlyRole(RECEIVER_PAYMENTS_MANAGER) { + require(staker != address(0), "CRW: address 0"); + receiverRewardPayment[staker].rewardPerEpoch = rewardPerEpoch; + } +} \ No newline at end of file diff --git a/contracts/staking/RewardDelegators.sol b/contracts/staking/RewardDelegators.sol index 8d05c9cf..4f5dca2e 100755 --- a/contracts/staking/RewardDelegators.sol +++ b/contracts/staking/RewardDelegators.sol @@ -524,4 +524,29 @@ contract RewardDelegators is return 0; } } + + // ------- receiver payments ------------------ // + + event ReceiverBalanceAdded(address indexed receiver, uint256 amount); + event ReceiverRewardPerEpochUpdated(address indexed receiver, uint256 amount); + + function addReceiverBalance(address receiver, uint128 amount) public { + require(receiver != address(0), "RD: address 0"); + require(amount != 0, "RD: amount 0"); + PONDToken.transferFrom(msg.sender, address(this), amount); + clusterRewards._increaseReceiverBalance(receiver, amount); + emit ReceiverBalanceAdded(receiver, amount); + } + + function setReceiverRewardPerEpoch(uint128 rewardPerEpoch) public { + address _sender = _msgSender(); + clusterRewards._setReceiverRewardPerEpoch(_sender, rewardPerEpoch); + emit ReceiverRewardPerEpochUpdated(_sender, rewardPerEpoch); + } + + function setupReceiverReward(uint128 amount, uint128 rewardPerEpoch) external { + address _sender = _msgSender(); + addReceiverBalance(_sender, amount); + setReceiverRewardPerEpoch(rewardPerEpoch); + } } diff --git a/contracts/staking/interfaces/IClusterRewards.sol b/contracts/staking/interfaces/IClusterRewards.sol index 302c7d6f..e17a1283 100644 --- a/contracts/staking/interfaces/IClusterRewards.sol +++ b/contracts/staking/interfaces/IClusterRewards.sol @@ -5,14 +5,20 @@ pragma solidity ^0.8.0; import "./IClusterSelector.sol"; interface IClusterRewards { + struct ReceiverPayment { + uint128 rewardRemaining; + uint128 rewardPerEpoch; + } function clusterSelectors(bytes32 networkId) external returns (IClusterSelector); function clusterRewards(address cluster) external returns(uint256); function rewardWeight(bytes32 networkId) external returns(uint256); function totalRewardsPerEpoch() external returns(uint256); - function addNetwork(bytes32 networkId, uint256 rewardWeight, address clusterSelector) external; + function addNetwork(bytes32 networkId, uint256 _rewardWeight, address clusterSelector) external; function removeNetwork(bytes32 networkId) external; function updateNetwork(bytes32 networkId, uint256 updatedRewardWeight, address updatedClusterSelector) external; - function getRewardForEpoch(uint256 epoch, bytes32 networkId) external view returns(uint256); + function getRewardForEpoch(bytes32 networkId) external view returns(uint256); function claimReward(address cluster) external returns(uint256); function changeRewardPerEpoch(uint256 updatedRewardPerEpoch) external; -} + function _increaseReceiverBalance(address receiver, uint128 amount) external; + function _setReceiverRewardPerEpoch(address signer, uint128 rewardPerEpoch) external; +} \ No newline at end of file diff --git a/deployments/oyster/MarketV1.ts b/deployments/oyster/MarketV1.ts new file mode 100644 index 00000000..9b8417df --- /dev/null +++ b/deployments/oyster/MarketV1.ts @@ -0,0 +1,72 @@ +import { ethers, upgrades, run } from 'hardhat'; +import { Contract } from 'ethers'; +import * as fs from 'fs'; +import { upgrade as upgradeUtil } from '../utils/Upgrade'; + +export async function deploy(noLog?: boolean): Promise { + let chainId = (await ethers.provider.getNetwork()).chainId; + + const MarketV1 = await ethers.getContractFactory('MarketV1'); + + var addresses: {[key: string]: {[key: string]: string}} = {}; + if(!noLog) { + console.log("Chain Id:", chainId); + + if(fs.existsSync('address.json')) { + addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); + } + + if(addresses[chainId] === undefined) { + addresses[chainId] = {}; + } + + if(addresses[chainId]['MarketV1'] !== undefined) { + console.log("Existing deployment:", addresses[chainId]['MarketV1']); + return MarketV1.attach(addresses[chainId]['MarketV1']); + } + } + + // TODO: Add RATE_LOCK to config + let marketV1 = await upgrades.deployProxy(MarketV1, [ + addresses[chainId]['Pond'], + [ethers.utils.id('RATE_LOCK')], + [300], + ], { kind: "uups" }); + + if(!noLog) { + console.log("Deployed addr:", marketV1.address); + + addresses[chainId]['MarketV1'] = marketV1.address; + + fs.writeFileSync('address.json', JSON.stringify(addresses, null, 2), 'utf8'); + } + + return marketV1; +} + +export async function upgrade() { + await upgradeUtil('MarketV1', 'MarketV1', []); +} + +export async function verify() { + let chainId = (await ethers.provider.getNetwork()).chainId; + console.log("Chain Id:", chainId); + + var addresses: {[key: string]: {[key: string]: string}} = {}; + if(fs.existsSync('address.json')) { + addresses = JSON.parse(fs.readFileSync('address.json', 'utf8')); + } + + if(addresses[chainId] === undefined || addresses[chainId]['MarketV1'] === undefined) { + throw new Error("MarketV1 not deployed"); + } + + const implAddress = await upgrades.erc1967.getImplementationAddress(addresses[chainId]['MarketV1']); + + await run("verify:verify", { + address: implAddress, + constructorArguments: [] + }); + + console.log("MarketV1 verified"); +} \ No newline at end of file diff --git a/deployments/run.ts b/deployments/run.ts new file mode 100644 index 00000000..6c4d3c57 --- /dev/null +++ b/deployments/run.ts @@ -0,0 +1,19 @@ +import { ethers } from 'hardhat'; +import { deploy, upgrade, verify } from './oyster/MarketV1'; +import { upgrade as upgradeClusterRewards, verify as verifyClusterRewards } from './staking/ClusterRewards'; +import { upgrade as upgradeRewardDelegators, verify as verifyRewardDelegators } from './staking/RewardDelegators'; + +async function run() { + let chainId = (await ethers.provider.getNetwork()).chainId; + console.log("Chain Id:", chainId); + + if(chainId !== 1) { + await upgradeClusterRewards(); + await upgradeRewardDelegators(); + + await verifyClusterRewards(); + await verifyRewardDelegators(); + } +} + +run(); \ No newline at end of file diff --git a/deployments/staking/ClusterRegistry.ts b/deployments/staking/ClusterRegistry.ts index d54924b4..d03e3c9e 100644 --- a/deployments/staking/ClusterRegistry.ts +++ b/deployments/staking/ClusterRegistry.ts @@ -1,7 +1,7 @@ import { ethers, upgrades, run } from 'hardhat'; import { Contract } from 'ethers'; import * as fs from 'fs'; -import { upgrade as upgradeUtil } from './Upgrade'; +import { upgrade as upgradeUtil } from '../utils/Upgrade'; const config = require('./config'); export async function deploy(rewardDelegatorsAddress: string, noLog?: boolean): Promise { diff --git a/deployments/staking/ClusterRewards.ts b/deployments/staking/ClusterRewards.ts index 68b1e8db..2293502d 100644 --- a/deployments/staking/ClusterRewards.ts +++ b/deployments/staking/ClusterRewards.ts @@ -1,7 +1,7 @@ import { ethers, run, upgrades } from 'hardhat'; import { Contract } from 'ethers'; import * as fs from 'fs'; -import { upgrade as upgradeUtil } from './Upgrade'; +import { upgrade as upgradeUtil } from '../utils/Upgrade'; const config = require('./config'); export async function deploy(rewardDelegators: string, receiverStaking: string, clusterSelectorMap: any, admin?: string, noLog?: boolean): Promise { diff --git a/deployments/staking/ClusterSelector.ts b/deployments/staking/ClusterSelector.ts index 21010f70..83f562fa 100644 --- a/deployments/staking/ClusterSelector.ts +++ b/deployments/staking/ClusterSelector.ts @@ -1,7 +1,7 @@ import { ethers, run, upgrades } from "hardhat"; import { Contract } from "ethers"; import * as fs from "fs"; -import { upgrade as upgradeUtil } from './Upgrade'; +import { upgrade as upgradeUtil } from '../utils/Upgrade'; const config = require('./config'); export async function deploy(network: string, rewardDelegators: string, arbGasInfo?: string, admin?: string, startTime?: number, epochLength?: number, gasRefund?: string, maxReward?: string, noLog?: boolean): Promise { diff --git a/deployments/staking/ReceiverStaking.ts b/deployments/staking/ReceiverStaking.ts index c499cde7..e741e892 100644 --- a/deployments/staking/ReceiverStaking.ts +++ b/deployments/staking/ReceiverStaking.ts @@ -1,7 +1,7 @@ import { ethers, run, upgrades } from 'hardhat'; import { Contract } from 'ethers'; import * as fs from 'fs'; -import { upgrade as upgradeUtil } from './Upgrade'; +import { upgrade as upgradeUtil } from '../utils/Upgrade'; const config = require('./config'); diff --git a/deployments/staking/RewardDelegators.ts b/deployments/staking/RewardDelegators.ts index 7becd77c..849391dd 100644 --- a/deployments/staking/RewardDelegators.ts +++ b/deployments/staking/RewardDelegators.ts @@ -1,7 +1,7 @@ import { ethers, run, upgrades } from 'hardhat'; import { Contract } from 'ethers'; import * as fs from 'fs'; -import { upgrade as upgradeUtil } from './Upgrade'; +import { upgrade as upgradeUtil } from '../utils/Upgrade'; export async function deployNoInit(): Promise { let chainId = (await ethers.provider.getNetwork()).chainId; diff --git a/deployments/staking/StakeManager.ts b/deployments/staking/StakeManager.ts index 024cf00d..5e7975a2 100644 --- a/deployments/staking/StakeManager.ts +++ b/deployments/staking/StakeManager.ts @@ -1,7 +1,7 @@ import { ethers, run, upgrades } from 'hardhat'; import { Contract } from 'ethers'; import * as fs from 'fs'; -import { upgrade as upgradeUtil } from './Upgrade'; +import { upgrade as upgradeUtil } from '../utils/Upgrade'; const config = require('./config'); export async function deploy(rewardDelegators: string): Promise { diff --git a/deployments/staking/Upgrade.ts b/deployments/utils/Upgrade.ts similarity index 86% rename from deployments/staking/Upgrade.ts rename to deployments/utils/Upgrade.ts index 2ec60dbb..1241d621 100644 --- a/deployments/staking/Upgrade.ts +++ b/deployments/utils/Upgrade.ts @@ -25,8 +25,8 @@ export async function upgrade(contractName: string, contractId: string, construc const CF = await ethers.getContractFactory(contractName); let c = await upgrades.upgradeProxy(addresses[chainId][contractId], CF, { kind: "uups", - constructorArgs + constructorArgs, }); - console.log("Deployed addr:", c.address); + console.log("Upgraded implementation:", await upgrades.erc1967.getImplementationAddress(addresses[chainId][contractId])); } \ No newline at end of file diff --git a/test/staking/ClusterRewards.ts b/test/staking/ClusterRewards.ts index 8923a75b..e749710e 100644 --- a/test/staking/ClusterRewards.ts +++ b/test/staking/ClusterRewards.ts @@ -12,6 +12,7 @@ import { FuzzedNumber } from "../../utils/fuzzer"; import { takeSnapshotBeforeAndAfterEveryTest } from "../../utils/testSuite"; import { getClusterRewards } from "../../utils/typechainConvertor"; import { randomlyDivideInXPieces } from "../../benchmarks/helpers/util"; +import { skipToTimestamp } from "../helpers/common"; // const e9 = BN.from(10).pow(9); @@ -29,7 +30,7 @@ const DOTWEIGHT = 150; const NEARWEIGHT = 250; const WEIGHTS = [ETHWEIGHT, DOTWEIGHT, NEARWEIGHT]; const TOTALWEIGHT = ETHWEIGHT + DOTWEIGHT + NEARWEIGHT; -const MAX_TICKETS = BN.from(2).pow(16); +const MAX_TICKETS = BN.from(2).pow(16).sub(1); const MAX_REWARD = BN.from("242352345636745756867986"); const MAX_TICKETS_1_pc = MAX_TICKETS.div(100); // const MAX_REWARD_1_pc = MAX_REWARD.div(100); @@ -579,157 +580,6 @@ describe("ClusterRewards update global vars", function() { it("non admin cannot update reward per epoch", async function() { await expect(clusterRewards.connect(signers[1]).changeRewardPerEpoch(30000)).to.be.reverted; }); - - it("admin can update reward wait time", async function() { - await clusterRewards.updateRewardWaitTime(30000); - expect(await clusterRewards.rewardDistributionWaitTime()).to.equal(30000); - }); - - it("non admin cannot update reward wait time", async function() { - await expect(clusterRewards.connect(signers[1]).updateRewardWaitTime(30000)).to.be.reverted; - }); -}); - -describe("ClusterRewards feed rewards", function() { - let signers: Signer[]; - let addrs: string[]; - let clusterRewards: ClusterRewards; - const FEED_REWARD = BN.from("200000").mul(e18); - const FEEDER_REWARD_1_pc = FEED_REWARD.mul(ETHWEIGHT).div(TOTALWEIGHT).div(100).mul(24 * 60 * 60).div(900); - - before(async function() { - signers = await ethers.getSigners(); - addrs = await Promise.all(signers.map((a) => a.getAddress())); - - const ReceiverStaking = await ethers.getContractFactory("ReceiverStaking"); - let receiverStaking = await deployMockContract(signers[0], ReceiverStaking.interface.format()); - await receiverStaking.mock.START_TIME.returns(startTime); - await receiverStaking.mock.EPOCH_LENGTH.returns(900); - - const ClusterRewards = await ethers.getContractFactory("ClusterRewards"); - let clusterRewardsContract = await upgrades.deployProxy( - ClusterRewards, - [ - addrs[0], - addrs[1], - receiverStaking.address, - NETWORK_IDS, - WEIGHTS, - [addrs[11], addrs[12], addrs[13]], - FEED_REWARD, - ], - { kind: "uups" } - ); - clusterRewards = getClusterRewards(clusterRewardsContract.address, signers[0]); - - await clusterRewards.grantRole(await clusterRewards.FEEDER_ROLE(), addrs[2]); - await clusterRewards.updateRewardWaitTime(43200); - }); - - takeSnapshotBeforeAndAfterEveryTest(async () => { }); - - it("feeder can feed rewards before start time", async function() { - await clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21], addrs[22]], [e16.mul(10), e16.mul(50)], 1); - - expect(await clusterRewards.clusterRewards(addrs[21])).to.equal(FEEDER_REWARD_1_pc.mul(10)); - expect(await clusterRewards.clusterRewards(addrs[22])).to.equal(FEEDER_REWARD_1_pc.mul(50)); - expect(await clusterRewards.rewardDistributedPerEpoch(1)).to.equal(FEEDER_REWARD_1_pc.mul(60)); - }); - - it("feeder can feed rewards after start time", async function() { - await time.increaseTo(startTime + 10); - - await clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21], addrs[22]], [e16.mul(10), e16.mul(50)], 1); - - expect(await clusterRewards.clusterRewards(addrs[21])).to.equal(FEEDER_REWARD_1_pc.mul(10)); - expect(await clusterRewards.clusterRewards(addrs[22])).to.equal(FEEDER_REWARD_1_pc.mul(50)); - expect(await clusterRewards.rewardDistributedPerEpoch(1)).to.equal(FEEDER_REWARD_1_pc.mul(60)); - }); - - it("feeder can feed rewards till 1 day after switching time", async function() { - await time.increaseTo(startTime + 33 * 86400 + 85000); - - await clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21], addrs[22]], [e16.mul(10), e16.mul(50)], 1); - - expect(await clusterRewards.clusterRewards(addrs[21])).to.equal(FEEDER_REWARD_1_pc.mul(10)); - expect(await clusterRewards.clusterRewards(addrs[22])).to.equal(FEEDER_REWARD_1_pc.mul(50)); - expect(await clusterRewards.rewardDistributedPerEpoch(1)).to.equal(FEEDER_REWARD_1_pc.mul(60)); - }); - - it("feeder cannot feed rewards after 1 day after switching time", async function() { - await time.increaseTo(startTime + 33 * 86400 + 90000); - - await expect(clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21], addrs[22]], [e16.mul(10), e16.mul(50)], 1)).to.be.revertedWith("CRW:F-Invalid method"); - }); - - it("feeder cannot feed rewards exceeding total", async function() { - await expect(clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21], addrs[22], addrs[23]], [e16.mul(10), e16.mul(50), (e16.mul(40)).add(e16.div(100))], 1)).to.be.revertedWith("CRW:F-Reward Distributed cant be more than totalRewardPerEpoch"); - }); - - it("feeder can feed rewards in multiple parts", async function() { - await clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21]], [e16.mul(10)], 1); - - expect(await clusterRewards.clusterRewards(addrs[21])).to.equal(FEEDER_REWARD_1_pc.mul(10)); - expect(await clusterRewards.clusterRewards(addrs[22])).to.equal(0); - expect(await clusterRewards.rewardDistributedPerEpoch(1)).to.equal(FEEDER_REWARD_1_pc.mul(10)); - - await clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[22]], [e16.mul(50)], 1); - - expect(await clusterRewards.clusterRewards(addrs[21])).to.equal(FEEDER_REWARD_1_pc.mul(10)); - expect(await clusterRewards.clusterRewards(addrs[22])).to.equal(FEEDER_REWARD_1_pc.mul(50)); - expect(await clusterRewards.rewardDistributedPerEpoch(1)).to.equal(FEEDER_REWARD_1_pc.mul(60)); - }); - - it("feeder cannot feed rewards in multiple parts exceeding total", async function() { - await clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21]], [e16.mul(10)], 1); - - expect(await clusterRewards.clusterRewards(addrs[21])).to.equal(FEEDER_REWARD_1_pc.mul(10)); - expect(await clusterRewards.clusterRewards(addrs[22])).to.equal(0); - expect(await clusterRewards.rewardDistributedPerEpoch(1)).to.equal(FEEDER_REWARD_1_pc.mul(10)); - - await clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[22]], [e16.mul(50)], 1); - - expect(await clusterRewards.clusterRewards(addrs[21])).to.equal(FEEDER_REWARD_1_pc.mul(10)); - expect(await clusterRewards.clusterRewards(addrs[22])).to.equal(FEEDER_REWARD_1_pc.mul(50)); - expect(await clusterRewards.rewardDistributedPerEpoch(1)).to.equal(FEEDER_REWARD_1_pc.mul(60)); - - await expect(clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[23]], [e16.mul(41)], 1)).to.be.revertedWith("CRW:F-Reward Distributed cant be more than totalRewardPerEpoch"); - }); - - it("feeder cannot feed rewards again before wait time", async function() { - await clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21], addrs[22]], [e16.mul(10), e16.mul(50)], 1); - - expect(await clusterRewards.clusterRewards(addrs[21])).to.equal(FEEDER_REWARD_1_pc.mul(10)); - expect(await clusterRewards.clusterRewards(addrs[22])).to.equal(FEEDER_REWARD_1_pc.mul(50)); - expect(await clusterRewards.rewardDistributedPerEpoch(1)).to.equal(FEEDER_REWARD_1_pc.mul(60)); - - await expect(clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21], addrs[22]], [e16.mul(10), e16.mul(10)], 2)).to.be.revertedWith("CRW:F-Cant distribute reward for new epoch within such short interval"); - await time.increase(40000); - await expect(clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21], addrs[22]], [e16.mul(10), e16.mul(10)], 2)).to.be.revertedWith("CRW:F-Cant distribute reward for new epoch within such short interval"); - }); - - it("feeder can feed rewards again after wait time", async function() { - await clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21], addrs[22]], [e16.mul(10), e16.mul(50)], 1); - - expect(await clusterRewards.clusterRewards(addrs[21])).to.equal(FEEDER_REWARD_1_pc.mul(10)); - expect(await clusterRewards.clusterRewards(addrs[22])).to.equal(FEEDER_REWARD_1_pc.mul(50)); - expect(await clusterRewards.rewardDistributedPerEpoch(1)).to.equal(FEEDER_REWARD_1_pc.mul(60)); - - await expect(clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21], addrs[22]], [e16.mul(10), e16.mul(10)], 2)).to.be.revertedWith("CRW:F-Cant distribute reward for new epoch within such short interval"); - await time.increase(40000); - await expect(clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21], addrs[22]], [e16.mul(10), e16.mul(10)], 2)).to.be.revertedWith("CRW:F-Cant distribute reward for new epoch within such short interval"); - await time.increase(5000); - await clusterRewards.connect(signers[2]).feed(ETHHASH, [addrs[21], addrs[22]], [e16.mul(10), e16.mul(10)], 2); - - expect(await clusterRewards.clusterRewards(addrs[21])).to.equal(FEEDER_REWARD_1_pc.mul(20)); - expect(await clusterRewards.clusterRewards(addrs[22])).to.equal(FEEDER_REWARD_1_pc.mul(60)); - expect(await clusterRewards.rewardDistributedPerEpoch(1)).to.equal(FEEDER_REWARD_1_pc.mul(60)); - expect(await clusterRewards.rewardDistributedPerEpoch(2)).to.equal(FEEDER_REWARD_1_pc.mul(20)); - }); - - it("non feeder cannot feed rewards", async function() { - await expect(clusterRewards.feed(ETHHASH, [addrs[21], addrs[22]], [e16.mul(10), e16.mul(50)], 1)).to.be.revertedWith("only feeder"); - }); }); describe("ClusterRewards submit tickets", function() { @@ -766,47 +616,51 @@ describe("ClusterRewards submit tickets", function() { { kind: "uups" } ); clusterRewards = getClusterRewards(clusterRewardsContract.address, signers[0]); - - await clusterRewards.grantRole(await clusterRewards.FEEDER_ROLE(), addrs[2]); - await clusterRewards.updateRewardWaitTime(43200); }); takeSnapshotBeforeAndAfterEveryTest(async () => { }); it("staker can submit tickets before switch with zero rewards", async function() { + const totalEpochStake = 500; + const epochToUse = 2; + const receiverStakeAmount = 50; await receiverStaking.mock.balanceOfSignerAt.reverts(); - await receiverStaking.mock.balanceOfSignerAt.withArgs(addrs[5], 2).returns(50, addrs[4]); + await receiverStaking.mock.balanceOfSignerAt.withArgs(addrs[5], epochToUse).returns(receiverStakeAmount, addrs[4]); await receiverStaking.mock.getEpochInfo.reverts(); - await receiverStaking.mock.getEpochInfo.withArgs(2).returns(500, 5); + await receiverStaking.mock.getEpochInfo.withArgs(2).returns(totalEpochStake, 5); await ethSelector.mock.getClusters.returns([addrs[31], addrs[32], addrs[33], addrs[34], addrs[35]]); await clusterRewards.connect(signers[5])["issueTickets(bytes32,uint24,uint16[])"](ETHHASH, 2, tickets.slice(0, -1)); - expect(await clusterRewards.isTicketsIssued(addrs[4], 2)).to.be.true; - expect(await clusterRewards.isTicketsIssued(addrs[5], 2)).to.be.false; - expect(await clusterRewards.clusterRewards(addrs[31])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[32])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[33])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[34])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[35])).to.equal(0); + let receiverRewards1 = tickets.map((e) => ETH_REWARD.mul(receiverStakeAmount).mul(e).div(totalEpochStake).div(MAX_TICKETS)); - await time.increaseTo(startTime + 34 * 86400); + expect(await clusterRewards.isTicketsIssued(addrs[4], epochToUse)).to.be.true; + expect(await clusterRewards.isTicketsIssued(addrs[5], epochToUse)).to.be.false; + expect(await clusterRewards.clusterRewards(addrs[31])).to.closeTo(receiverRewards1[0], 1); + expect(await clusterRewards.clusterRewards(addrs[32])).to.closeTo(receiverRewards1[1], 1); + expect(await clusterRewards.clusterRewards(addrs[33])).to.closeTo(receiverRewards1[2], 1); + expect(await clusterRewards.clusterRewards(addrs[34])).to.closeTo(receiverRewards1[3], 1); + expect(await clusterRewards.clusterRewards(addrs[35])).to.closeTo(receiverRewards1[4], 1); - await receiverStaking.mock.balanceOfSignerAt.withArgs(addrs[6], 2).returns(50, addrs[7]); + await skipToTimestamp(ethers, startTime + 34 * 86400); + const anotherReceiverStake = 78; + await receiverStaking.mock.balanceOfSignerAt.withArgs(addrs[6], 2).returns(anotherReceiverStake, addrs[7]); await clusterRewards.connect(signers[6])["issueTickets(bytes32,uint24,uint16[])"](ETHHASH, 2, tickets.slice(0, -1)); + let receiverRewards2 = tickets.map((e) => ETH_REWARD.mul(anotherReceiverStake).mul(e).div(totalEpochStake).div(MAX_TICKETS)); + expect(await clusterRewards.isTicketsIssued(addrs[7], 2)).to.be.true; expect(await clusterRewards.isTicketsIssued(addrs[6], 2)).to.be.false; - expect(await clusterRewards.clusterRewards(addrs[31])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[32])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[33])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[34])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[35])).to.equal(0); + expect(await clusterRewards.clusterRewards(addrs[31])).to.closeTo(receiverRewards1[0].add(receiverRewards2[0]), 1); + expect(await clusterRewards.clusterRewards(addrs[32])).to.closeTo(receiverRewards1[1].add(receiverRewards2[1]), 1); + expect(await clusterRewards.clusterRewards(addrs[33])).to.closeTo(receiverRewards1[2].add(receiverRewards2[2]), 1); + expect(await clusterRewards.clusterRewards(addrs[34])).to.closeTo(receiverRewards1[3].add(receiverRewards2[3]), 1); + expect(await clusterRewards.clusterRewards(addrs[35])).to.closeTo(receiverRewards1[4].add(receiverRewards2[4]), 1); }); it("staker can submit tickets after switch with non zero rewards", async function() { - const epochWithRewards = 33 * 86400 / 900 + 2; + const epochWithRewards = (33 * 86400) / 900 + 2; await receiverStaking.mock.balanceOfSignerAt.reverts(); await receiverStaking.mock.balanceOfSignerAt.withArgs(addrs[5], epochWithRewards).returns(50, addrs[4]); await receiverStaking.mock.getEpochInfo.reverts(); @@ -814,7 +668,7 @@ describe("ClusterRewards submit tickets", function() { await ethSelector.mock.getClusters.returns([addrs[31], addrs[32], addrs[33], addrs[34], addrs[35]]); let receiverRewards1 = tickets.map((e) => ETH_REWARD.mul(50).mul(e).div(500).div(MAX_TICKETS)); - await time.increaseTo(startTime + 34 * 86400); + await skipToTimestamp(ethers, startTime + 34 * 86400); await clusterRewards.connect(signers[5])["issueTickets(bytes32,uint24,uint16[])"](ETHHASH, epochWithRewards, tickets.slice(0, -1)); @@ -837,11 +691,11 @@ describe("ClusterRewards submit tickets", function() { expect(await clusterRewards.isTicketsIssued(addrs[4], epochWithRewards + 2)).to.be.true; expect(await clusterRewards.isTicketsIssued(addrs[5], epochWithRewards + 2)).to.be.false; - expect((await clusterRewards.clusterRewards(addrs[31]))).to.be.closeTo((receiverRewards1[0]).add(receiverRewards2[4]), 2); - expect((await clusterRewards.clusterRewards(addrs[32]))).to.be.closeTo((receiverRewards1[1]).add(receiverRewards2[3]), 2); - expect((await clusterRewards.clusterRewards(addrs[33]))).to.be.closeTo((receiverRewards1[2]).add(receiverRewards2[2]), 2); - expect((await clusterRewards.clusterRewards(addrs[34]))).to.be.closeTo((receiverRewards1[3]).add(receiverRewards2[1]), 2); - expect((await clusterRewards.clusterRewards(addrs[35]))).to.be.closeTo((receiverRewards1[4]).add(receiverRewards2[0]), 2); + expect(await clusterRewards.clusterRewards(addrs[31])).to.be.closeTo(receiverRewards1[0].add(receiverRewards2[4]), 2); + expect(await clusterRewards.clusterRewards(addrs[32])).to.be.closeTo(receiverRewards1[1].add(receiverRewards2[3]), 2); + expect(await clusterRewards.clusterRewards(addrs[33])).to.be.closeTo(receiverRewards1[2].add(receiverRewards2[2]), 2); + expect(await clusterRewards.clusterRewards(addrs[34])).to.be.closeTo(receiverRewards1[3].add(receiverRewards2[1]), 2); + expect(await clusterRewards.clusterRewards(addrs[35])).to.be.closeTo(receiverRewards1[4].add(receiverRewards2[0]), 2); }); it("staker cannot submit tickets multiple times for the same epoch", async function() { @@ -888,17 +742,44 @@ describe("ClusterRewards submit tickets", function() { }); it("staker cannot submit excess tickets", async function() { - const epochWithRewards = 33 * 86400 / 900 + 2; + const epochWithRewards = (33 * 86400) / 900 + 2; await receiverStaking.mock.balanceOfSignerAt.reverts(); - await receiverStaking.mock.balanceOfSignerAt.withArgs(addrs[5], epochWithRewards).returns(50, addrs[4]); + + await receiverStaking.mock.signerToStaker.withArgs(addrs[5]).returns(addrs[4]); + await receiverStaking.mock.balanceOfAt.withArgs(addrs[4], epochWithRewards).returns(50); + await receiverStaking.mock.getEpochInfo.reverts(); await receiverStaking.mock.getEpochInfo.withArgs(epochWithRewards).returns(500, epochWithRewards + 3); await ethSelector.mock.getClusters.returns([addrs[31], addrs[32], addrs[33], addrs[34], addrs[35]]); - await time.increaseTo(startTime + 34 * 86400); + await skipToTimestamp(ethers, startTime + 34 * 86400); - await expect(clusterRewards.connect(signers[5])["issueTickets(bytes32,uint24[],uint16[][])"](ETHHASH, [epochWithRewards], [[MAX_TICKETS_1_pc.mul(10), MAX_TICKETS_1_pc.mul(20), MAX_TICKETS_1_pc.mul(30), MAX_TICKETS_1_pc.mul(41)]])).to.be.revertedWith("CRW:IPRT-Total ticket count invalid"); - await expect(clusterRewards.connect(signers[5])["issueTickets(bytes32,uint24[],uint16[][])"](ETHHASH, [], [[MAX_TICKETS_1_pc.mul(10), MAX_TICKETS_1_pc.mul(20), MAX_TICKETS_1_pc.mul(30), MAX_TICKETS_1_pc.mul(15), MAX_TICKETS_1_pc.mul(26)]])).to.be.revertedWith("CRW:MIT-invalid inputs"); + await expect( + clusterRewards + .connect(signers[5]) + ["issueTickets(bytes32,uint24[],uint16[][])"]( + ETHHASH, + [epochWithRewards], + [[MAX_TICKETS_1_pc.mul(10), MAX_TICKETS_1_pc.mul(20), MAX_TICKETS_1_pc.mul(30), MAX_TICKETS_1_pc.mul(41)]] + ) + ).to.be.revertedWith("CRW:IPRT-Total ticket count invalid"); + await expect( + clusterRewards + .connect(signers[5]) + ["issueTickets(bytes32,uint24[],uint16[][])"]( + ETHHASH, + [], + [ + [ + MAX_TICKETS_1_pc.mul(10), + MAX_TICKETS_1_pc.mul(20), + MAX_TICKETS_1_pc.mul(30), + MAX_TICKETS_1_pc.mul(15), + MAX_TICKETS_1_pc.mul(26), + ], + ] + ) + ).to.be.revertedWith("CRW:MIT-invalid inputs"); }); it("staker cannot submit tickets over maximum", async function() { @@ -959,44 +840,11 @@ describe("ClusterRewards submit compressed tickets", function() { { kind: "uups" } ); clusterRewards = getClusterRewards(clusterRewardsContract.address, signers[0]); - - await clusterRewards.grantRole(await clusterRewards.FEEDER_ROLE(), addrs[2]); - await clusterRewards.updateRewardWaitTime(43200); }); takeSnapshotBeforeAndAfterEveryTest(async () => { }); - it("staker can submit compressed tickets for 1 epoch before switch with zero rewards", async function() { - await receiverStaking.mock.getCurrentEpoch.returns(5); - await receiverStaking.mock.totalSupplyAtRanged.revertsWithReason("unexpected query for total balance"); - await receiverStaking.mock.totalSupplyAtRanged.withArgs(2, 1).returns([500]); - await receiverStaking.mock.balanceOfSignerAtRanged.revertsWithReason("unexpected query for signer balance"); - await receiverStaking.mock.balanceOfSignerAtRanged.withArgs(addrs[5], 2, 1).returns([50], addrs[4]); - await ethSelector.mock.getClustersRanged.returns([[addrs[31], addrs[32], addrs[33], addrs[34], addrs[35]]]); - - const tickets: number[][] = []; - let rawTicketInfo = ETHHASH + (2).toString(16).padStart(8, '0'); - for (let i = 0; i < 1 * ticketsLength; i++) { - let j: number = parseInt((i / ticketsLength) + ""); - let k: number = i % ticketsLength; - if (!tickets[j]) tickets[j] = []; - tickets[j][k] = parseInt((Math.random() * 13000) + ""); - rawTicketInfo = rawTicketInfo + tickets[j][k].toString(16).padStart(4, '0'); - } - - await clusterRewards.connect(signers[5])["issueTickets(bytes)"](rawTicketInfo); - - expect(await clusterRewards.isTicketsIssued(addrs[4], 2)).to.be.true; - expect(await clusterRewards.isTicketsIssued(addrs[4], 3)).to.be.false; - expect(await clusterRewards.isTicketsIssued(addrs[5], 2)).to.be.false; - expect(await clusterRewards.clusterRewards(addrs[31])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[32])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[33])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[34])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[35])).to.equal(0); - }); - - it("staker can submit compressed tickets for 1 epoch before switch with zero rewards, tx submitted after rewards open", async function() { + it("staker can submit compressed tickets for 1 epoch", async function() { await receiverStaking.mock.getCurrentEpoch.returns(5); await receiverStaking.mock.totalSupplyAtRanged.revertsWithReason("unexpected query for total balance"); await receiverStaking.mock.totalSupplyAtRanged.withArgs(2, 1).returns([500]); @@ -1005,27 +853,29 @@ describe("ClusterRewards submit compressed tickets", function() { await ethSelector.mock.getClustersRanged.returns([[addrs[31], addrs[32], addrs[33], addrs[34], addrs[35]]]); const tickets: number[][] = []; - let rawTicketInfo = ETHHASH + (2).toString(16).padStart(8, '0'); + let rawTicketInfo = ETHHASH + (2).toString(16).padStart(8, "0"); for (let i = 0; i < 1 * ticketsLength; i++) { - let j: number = parseInt((i / ticketsLength) + ""); + let j: number = parseInt(i / ticketsLength + ""); let k: number = i % ticketsLength; if (!tickets[j]) tickets[j] = []; - tickets[j][k] = parseInt((Math.random() * 13000) + ""); - rawTicketInfo = rawTicketInfo + tickets[j][k].toString(16).padStart(4, '0'); + tickets[j][k] = parseInt(Math.random() * 13000 + ""); + rawTicketInfo = rawTicketInfo + tickets[j][k].toString(16).padStart(4, "0"); } + let lastTicket = MAX_TICKETS.sub(tickets[0].reduce((prev, curr) => prev.add(curr), BN.from(0))); + let receiverRewards1 = tickets[0].map((e) => ETH_REWARD.mul(50).mul(e).div(500).div(MAX_TICKETS)); + let lastReward = ETH_REWARD.mul(50).mul(lastTicket).div(500).div(MAX_TICKETS); - await time.increaseTo(startTime + 34 * 86400); - - await clusterRewards.connect(signers[5])["issueTickets(bytes)"](rawTicketInfo); + // check the events arguments emitted + await expect(clusterRewards.connect(signers[5])["issueTickets(bytes)"](rawTicketInfo)).to.emit(clusterRewards, "TicketsIssued"); expect(await clusterRewards.isTicketsIssued(addrs[4], 2)).to.be.true; expect(await clusterRewards.isTicketsIssued(addrs[4], 3)).to.be.false; expect(await clusterRewards.isTicketsIssued(addrs[5], 2)).to.be.false; - expect(await clusterRewards.clusterRewards(addrs[31])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[32])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[33])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[34])).to.equal(0); - expect(await clusterRewards.clusterRewards(addrs[35])).to.equal(0); + expect(await clusterRewards.clusterRewards(addrs[31])).to.equal(receiverRewards1[0]); + expect(await clusterRewards.clusterRewards(addrs[32])).to.equal(receiverRewards1[1]); + expect(await clusterRewards.clusterRewards(addrs[33])).to.equal(receiverRewards1[2]); + expect(await clusterRewards.clusterRewards(addrs[34])).to.equal(receiverRewards1[3]); + expect(await clusterRewards.clusterRewards(addrs[35])).to.closeTo(lastReward, 1); }); it("staker can submit compressed tickets after switch with non zero rewards", async function() { @@ -1430,9 +1280,6 @@ describe("ClusterRewards claim rewards", function() { { kind: "uups" } ); clusterRewards = getClusterRewards(clusterRewardsContract.address, signers[0]); - - await clusterRewards.grantRole(await clusterRewards.FEEDER_ROLE(), addrs[2]); - await clusterRewards.updateRewardWaitTime(43200); }); takeSnapshotBeforeAndAfterEveryTest(async () => { }); @@ -1499,5 +1346,132 @@ describe("ClusterRewards claim rewards", function() { await expect(clusterRewards.claimReward(addrs[33])).to.be.revertedWith(`AccessControl: account ${addrs[0].toLowerCase()} is missing role ${await clusterRewards.CLAIMER_ROLE()}`); }); -}); + describe("ClusterRewards: Add Receiver extra payment", function () { + let signers: Signer[]; + let addrs: string[]; + let receiverStaking: Contract; + let ethSelector: Contract; + let clusterRewards: ClusterRewards; + let mockToken: Contract; + + let staker: Signer; + let signer: Signer; + + before(async function () { + signers = await ethers.getSigners(); + addrs = await Promise.all(signers.map((a) => a.getAddress())); + + staker = signers[10]; + signer = signers[11]; + + const Pond = await ethers.getContractFactory("Pond"); + mockToken = await upgrades.deployProxy(Pond, ["Marlin", "POND"], { kind: "uups" }); + + const ReceiverStaking = await ethers.getContractFactory("ReceiverStaking"); + receiverStaking = await deployMockContract(signers[0], ReceiverStaking.interface.format()); + await receiverStaking.mock.START_TIME.returns(startTime); + await receiverStaking.mock.EPOCH_LENGTH.returns(900); + + const ClusterSelector = await ethers.getContractFactory("ClusterSelector"); + ethSelector = await deployMockContract(signers[0], ClusterSelector.interface.format()); + + const ClusterRewards = await ethers.getContractFactory("ClusterRewards"); + let clusterRewardsContract = await upgrades.deployProxy( + ClusterRewards, + [addrs[0], addrs[1], receiverStaking.address, NETWORK_IDS, WEIGHTS, [ethSelector.address, addrs[12], addrs[13]], MAX_REWARD], + { kind: "uups", constructorArgs: [] } + ); + clusterRewards = getClusterRewards(clusterRewardsContract.address, signers[0]); + + await receiverStaking.mock.STAKING_TOKEN.returns(mockToken.address); + await receiverStaking.mock.signerToStaker.withArgs(await signer.getAddress()).returns(await staker.getAddress()); + await receiverStaking.mock.stakerToSigner.withArgs(await staker.getAddress()).returns(await signer.getAddress()); + + await mockToken.transfer(await signer.getAddress(), 100000); + }); + + takeSnapshotBeforeAndAfterEveryTest(async () => {}); + + it("_increaseReceiverBalance: can be only called by specific role", async () => { + await expect(clusterRewards.connect(signer)._increaseReceiverBalance(await signer.getAddress(), 50)).to.be.revertedWith( + `AccessControl: account ${( + await signer.getAddress() + ).toLowerCase()} is missing role ${await clusterRewards.RECEIVER_PAYMENTS_MANAGER()}` + ); + }); + + it("_setReceiverRewardPerEpoch: can be only called by specific role", async () => { + await expect(clusterRewards.connect(signer)._setReceiverRewardPerEpoch(await signer.getAddress(), 50)).to.be.revertedWith( + `AccessControl: account ${( + await signer.getAddress() + ).toLowerCase()} is missing role ${await clusterRewards.RECEIVER_PAYMENTS_MANAGER()}` + ); + }); + + it("only admin can grant RECEIVER_PAYMENTS_MANAGER", async () => { + await expect( + clusterRewards.connect(signer).grantRole(await clusterRewards.RECEIVER_PAYMENTS_MANAGER(), await signer.getAddress()) + ).to.be.revertedWith( + `AccessControl: account ${(await signer.getAddress()).toLowerCase()} is missing role ${await clusterRewards.DEFAULT_ADMIN_ROLE()}` + ); + + await clusterRewards.connect(signers[0]).grantRole(await clusterRewards.RECEIVER_PAYMENTS_MANAGER(), await signer.getAddress()); + }); + + it("Check increase balance", async () => { + const increaseByAmount = 100; + await clusterRewards.connect(signers[0]).grantRole(await clusterRewards.RECEIVER_PAYMENTS_MANAGER(), await signer.getAddress()); + + const balanceBefore = (await clusterRewards.receiverRewardPayment(await signer.getAddress()))[0]; + await clusterRewards.connect(signer)._increaseReceiverBalance(await signer.getAddress(), increaseByAmount); + const balanceAfter = (await clusterRewards.receiverRewardPayment(await signer.getAddress()))[0]; + + expect(balanceAfter).eq(balanceBefore.add(increaseByAmount)); + }); + + it("Check reward per epoch setting", async () => { + const receiverExtraRewardPerEpoch = 109; + await clusterRewards.connect(signers[0]).grantRole(await clusterRewards.RECEIVER_PAYMENTS_MANAGER(), await signer.getAddress()); + + await clusterRewards.connect(signer)._setReceiverRewardPerEpoch(await staker.getAddress(), receiverExtraRewardPerEpoch); + expect((await clusterRewards.receiverRewardPayment(await staker.getAddress()))[1]).eq(receiverExtraRewardPerEpoch); + }); + + it("Reward Checking after receiver adds extra tokens", async () => { + const mockRewardDelegators = signers[49]; // any random address can be used + const receiverRewardPerEpoch = BN.from(3000); + const receiverBalance = BN.from(100000); + + await clusterRewards.connect(signers[0]).grantRole(await clusterRewards.RECEIVER_PAYMENTS_MANAGER(), await mockRewardDelegators.getAddress()); + // balance can be added by any address, hence we are using staker address directly + await clusterRewards.connect(mockRewardDelegators)._increaseReceiverBalance(await staker.getAddress(), receiverBalance); + + // reward will set only by signer, hence we are using signer address here + await clusterRewards.connect(mockRewardDelegators)._setReceiverRewardPerEpoch(await staker.getAddress(), receiverRewardPerEpoch); + + const epochWithRewards = (33 * 86400) / 900 + 2; + await ethSelector.mock.getClusters.returns([addrs[31], addrs[32], addrs[33], addrs[34], addrs[35]]); + // is this 500 inflation rewards + let receiverRewards1 = tickets.map((e) => ETH_REWARD.mul(50).mul(e).div(500).div(MAX_TICKETS)); + let extraRewards1 = tickets.map((e) => receiverRewardPerEpoch.mul(e).div(MAX_TICKETS)); + + await receiverStaking.mock.balanceOfSignerAt.reverts(); + await receiverStaking.mock.balanceOfSignerAt + .withArgs(await signer.getAddress(), epochWithRewards) + .returns(50, await staker.getAddress()); + + // is this 500 inflation rewards + await receiverStaking.mock.getEpochInfo.withArgs(epochWithRewards).returns(500, epochWithRewards + 3); + + await skipToTimestamp(ethers, startTime + 34 * 86400); + await clusterRewards.connect(signer)["issueTickets(bytes32,uint24,uint16[])"](ETHHASH, epochWithRewards, tickets.slice(0, -1)); + + expect(await clusterRewards.clusterRewards(addrs[31])).to.be.closeTo(receiverRewards1[0].add(extraRewards1[0]), 1); + expect(await clusterRewards.clusterRewards(addrs[32])).to.be.closeTo(receiverRewards1[1].add(extraRewards1[1]), 1); + expect(await clusterRewards.clusterRewards(addrs[33])).to.be.closeTo(receiverRewards1[2].add(extraRewards1[2]), 1); + expect(await clusterRewards.clusterRewards(addrs[34])).to.be.closeTo(receiverRewards1[3].add(extraRewards1[3]), 1); + expect(await clusterRewards.clusterRewards(addrs[35])).to.be.closeTo(receiverRewards1[4].add(extraRewards1[4]), 1); + }); + }) +}); \ No newline at end of file diff --git a/test/staking/Integration.ts b/test/staking/Integration.ts index 9eaced1e..0b5e552e 100644 --- a/test/staking/Integration.ts +++ b/test/staking/Integration.ts @@ -31,7 +31,7 @@ const stakingConfig = require("../config/staking.json"); const UNDELEGATION_WAIT_TIME = 604800; const REDELEGATION_WAIT_TIME = 21600; const REWARD_PER_EPOCH = BN.from(10).pow(21).mul(35); -const MAX_TICKETS = BN.from(2).pow(16) +const MAX_TICKETS = BN.from(2).pow(16).sub(1) describe("Integration", function () { let signers: Signer[]; @@ -389,7 +389,7 @@ describe("Integration", function () { it("Only 1 receiver, all cluster should get equal reward", async () => { const receiver = receivers[0]; - const totalWeight = MAX_TICKETS.sub(1); + const totalWeight = MAX_TICKETS; let equiWeightedTickets = totalWeight.div(totalClusters); const weights = [equiWeightedTickets, equiWeightedTickets, equiWeightedTickets, equiWeightedTickets, totalWeight.sub(equiWeightedTickets.mul(4))]; // let missingWeights = totalWeight.sub(weights.reduce((prev, curr) => prev.add(curr), BN.from(0))) @@ -432,7 +432,7 @@ describe("Integration", function () { it("Only 1 receiver, tickets ratio 1:2:3:4:5", async () => { const receiver = receivers[0]; - const totalWeight = MAX_TICKETS.sub(1); + const totalWeight = MAX_TICKETS; let fration = totalWeight.div(15); const weights = [fration.mul(1), fration.mul(2), fration.mul(3), fration.mul(4), totalWeight.sub(fration.mul(10))]; await receiverDeposit(pond, receiverStaking, receiver, minPondToUseByReceiver); @@ -474,7 +474,7 @@ describe("Integration", function () { const receiver = receivers[0]; let fration = MAX_TICKETS.div(4); const zero = BN.from(0); - const weights = [fration, fration, fration, fration, zero]; + const weights = [fration, fration, fration, MAX_TICKETS.sub(fration.mul(3)), zero]; await receiverDeposit(pond, receiverStaking, receiver, minPondToUseByReceiver); await skipEpoch(); let currentEpoch = (await clusterSelectors[0].getCurrentEpoch()).toString(); @@ -498,28 +498,29 @@ describe("Integration", function () { ); expect(pondRewards[0]).gt(0) - expect(pondRewards[0]).eq(pondRewards[1]) - expect(pondRewards[0]).eq(pondRewards[2]) - expect(pondRewards[0]).eq(pondRewards[3]) + expect(pondRewards[0]).closeTo(pondRewards[1], 1) + expect(pondRewards[0]).closeTo(pondRewards[2], 1) + // compare by fractions for better accuracy + expect(MAX_TICKETS.sub(fration.mul(3)).div(fration)).to.eq(pondRewards[3].div(pondRewards[0])); expect(pondRewards[4]).eq(0) expect(mpondRewards[0]).gt(0) - expect(mpondRewards[0]).eq(mpondRewards[1]) - expect(mpondRewards[0]).eq(mpondRewards[2]) - expect(mpondRewards[0]).eq(mpondRewards[3]) + expect(mpondRewards[0]).closeTo(mpondRewards[1], 1) + expect(mpondRewards[0]).closeTo(mpondRewards[2], 1) + expect(mpondRewards[0]).closeTo(mpondRewards[3], mpondRewards[0].div(2000)) expect(mpondRewards[4]).eq(0) expect(commision[0]).gt(0) - expect(commision[0]).eq(commision[1]) - expect(commision[0]).eq(commision[2]) - expect(commision[0]).eq(commision[3]) + expect(commision[0]).closeTo(commision[1], 1) + expect(commision[0]).closeTo(commision[2], 1) + expect(commision[0]).closeTo(commision[3], commision[0].div(2000)) expect(commision[4]).eq(0) }); it("Only 1 receiver, tickets ratio 0:0:0:0:1", async () => { const receiver = receivers[0]; - let fration = MAX_TICKETS.sub(1); + let fration = MAX_TICKETS; const zero = BN.from(0); const weights = [zero, zero, zero, zero, fration]; await receiverDeposit(pond, receiverStaking, receiver, minPondToUseByReceiver); @@ -612,7 +613,7 @@ describe("Integration", function () { await receiverDeposit(pond, receiverStaking, receiver1, minPondToUseByReceiver.div(2)); await receiverDeposit(pond, receiverStaking, receiver2, minPondToUseByReceiver); - const fraction = MAX_TICKETS.sub(1); + const fraction = MAX_TICKETS; await skipEpoch(); let currentEpoch = (await clusterSelectors[0].getCurrentEpoch()).toString(); @@ -695,7 +696,7 @@ describe("Integration", function () { it("Should Fail: Invalid Receiver can't submit tickets", async () => { const receiver = invalidReceivers[0]; - const fraction = MAX_TICKETS.sub(1); + const fraction = MAX_TICKETS; // move some epoch/s ahead await skipEpoch(); @@ -967,7 +968,7 @@ describe("Integration", function () { ): Promise => { const epoch = BN.from(clusterSelectedAtEpoch).sub(1).toString(); const weights: BN[] = []; - let pendingFraction = MAX_TICKETS.sub(1); + let pendingFraction = MAX_TICKETS; for (let index = 0; index < randomClustersSelected.length - 1; index++) { const fraction = FuzzedNumber.randomInRange("1", pendingFraction); @@ -1064,7 +1065,7 @@ const mineTillGivenClustersAreSelected = async ( return [currentEpoch, clusters]; } else { await whenNotFound(); - await clusterSelector.selectClusters(); + await clusterSelector.selectClusters({gasLimit: "2000000"}); currentEpoch = BN.from(currentEpoch.toString()).add(1).toString(); } } diff --git a/test/staking/RewardDelegators.ts b/test/staking/RewardDelegators.ts index 4a82cd7a..421e377d 100644 --- a/test/staking/RewardDelegators.ts +++ b/test/staking/RewardDelegators.ts @@ -1319,4 +1319,81 @@ describe("RewardDelegators ", function () { await rewardDelegators.refreshClusterDelegation(networkId, [cluster, cluster1, cluster2]); await fakeClusterSelectors["ETH"].mock.upsertMultiple.withArgs([cluster2], [10000]).reverts(); }); -}); \ No newline at end of file +}); + +describe("RewardDelegators 5", function() { + let signers: Signer[]; + let addrs: string[]; + let mockClusterRewards: MockContract; + let receiver: Signer + const receiverAmountToTest: BN = BN.from(10).pow(20) + const rewardPerEpochToTest: BN = BN.from(10).pow(18).mul(5) + let pond: Pond; + let rewardDelegators: RewardDelegators + + + before(async function () { + signers = await ethers.getSigners(); + receiver = signers[10] + addrs = await Promise.all(signers.map((a) => a.getAddress())); + + const ClusterRewards = await ethers.getContractFactory("ClusterRewards"); + mockClusterRewards = await deployMockContract(signers[0], ClusterRewards.interface.format()); + + await mockClusterRewards.mock._increaseReceiverBalance.withArgs(await receiver.getAddress(), receiverAmountToTest).returns(); + await mockClusterRewards.mock._setReceiverRewardPerEpoch.withArgs(await receiver.getAddress(), rewardPerEpochToTest).returns(); + + const Pond = await ethers.getContractFactory("Pond"); + let pondContract = await upgrades.deployProxy(Pond, ["Marlin", "POND"], { kind: "uups" }); + pond = getPond(pondContract.address, signers[0]); + await pond.transfer(await receiver.getAddress(), receiverAmountToTest) + + const RewardDelegators = await ethers.getContractFactory("RewardDelegators"); + const rewardDelegatorsUntyped: Contract = await upgrades.deployProxy(RewardDelegators, { + kind: "uups", + initializer: false, + }); + rewardDelegators = getRewardDelegators(rewardDelegatorsUntyped.address, signers[0]); + + const fakeStakeManagerAddr = addrs[20]; + const fakeClusterRegistryAddr = addrs[18]; + const fakeMPond = addrs[6]; + const pondTokenId = ethers.utils.id(pond.address); + const mpondTokenId = ethers.utils.id(fakeMPond); + + await rewardDelegators.initialize( + fakeStakeManagerAddr, + mockClusterRewards.address, + fakeClusterRegistryAddr, + pond.address, + [pondTokenId, mpondTokenId], + [stakingConfig.PondRewardFactor, stakingConfig.MPondRewardFactor], + [stakingConfig.PondWeightForThreshold, stakingConfig.MPondWeightForThreshold], + [stakingConfig.PondWeightForDelegation, stakingConfig.MPondWeightForDelegation], + [], + [] + ); + }); + + takeSnapshotBeforeAndAfterEveryTest(async () => {}); + + it("Check receiver deposit", async() => { + await pond.connect(receiver).approve(rewardDelegators.address, receiverAmountToTest) + const balanceBefore = await pond.balanceOf(rewardDelegators.address) + await expect(rewardDelegators.connect(receiver).addReceiverBalance(await receiver.getAddress(), receiverAmountToTest)).to.emit(rewardDelegators, "AddReceiverBalance").withArgs(await receiver.getAddress(), receiverAmountToTest); + const balanceAfter = await pond.balanceOf(rewardDelegators.address) + + expect(balanceAfter.sub(balanceBefore)).eq(receiverAmountToTest); + }) + + it("Check receiver receiver reward per epoch", async() => { + await expect(rewardDelegators.connect(receiver).setReceiverRewardPerEpoch(rewardPerEpochToTest)).to.emit(rewardDelegators, "UpdateReceiverRewardPerEpoch").withArgs(await receiver.getAddress(), rewardPerEpochToTest) + }) + + it("Receiver can deposit and set reward per epoch in same tranasction", async() => { + await pond.connect(receiver).approve(rewardDelegators.address, receiverAmountToTest) + const tx = rewardDelegators.connect(receiver).setupReceiverReward(receiverAmountToTest, rewardPerEpochToTest) + await expect(tx).to.emit(rewardDelegators, "AddReceiverBalance").withArgs(await receiver.getAddress(), receiverAmountToTest) + .to.emit(rewardDelegators, "UpdateReceiverRewardPerEpoch").withArgs(await receiver.getAddress(), rewardPerEpochToTest); + }) +}) \ No newline at end of file