文件 1 的 15:AccessControl.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
interface IAccessControl {
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address account) external;
}
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
modifier onlyRole(bytes32 role) {
_checkRole(role, _msgSender());
_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
function hasRole(bytes32 role, address account) public view override returns (bool) {
return _roles[role].members[account];
}
function _checkRole(bytes32 role, address account) internal view {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(uint160(account), 20),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
emit RoleAdminChanged(role, getRoleAdmin(role), adminRole);
_roles[role].adminRole = adminRole;
}
function _grantRole(bytes32 role, address account) private {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) private {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
文件 2 的 15:Address.sol
pragma solidity ^0.8.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) private pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 3 的 15:CloneFactory.sol
pragma solidity 0.8.0;
contract CloneFactory {
function createClone(address target) internal returns (address result) {
bytes20 targetBytes = bytes20(target);
assembly {
let clone := mload(0x40)
mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(clone, 0x14), targetBytes)
mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
result := create(0, clone, 0x37)
}
}
}
文件 4 的 15:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 5 的 15:CudosAccessControls.sol
pragma solidity 0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
contract CudosAccessControls is AccessControl {
bytes32 public constant WHITELISTED_ROLE = keccak256("WHITELISTED_ROLE");
bytes32 public constant SMART_CONTRACT_ROLE = keccak256("SMART_CONTRACT_ROLE");
event AdminRoleGranted(
address indexed beneficiary,
address indexed caller
);
event AdminRoleRemoved(
address indexed beneficiary,
address indexed caller
);
event WhitelistRoleGranted(
address indexed beneficiary,
address indexed caller
);
event WhitelistRoleRemoved(
address indexed beneficiary,
address indexed caller
);
event SmartContractRoleGranted(
address indexed beneficiary,
address indexed caller
);
event SmartContractRoleRemoved(
address indexed beneficiary,
address indexed caller
);
modifier onlyAdminRole() {
require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "CudosAccessControls: sender must be an admin");
_;
}
constructor() {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
function hasAdminRole(address _address) external view returns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, _address);
}
function hasWhitelistRole(address _address) external view returns (bool) {
return hasRole(WHITELISTED_ROLE, _address);
}
function hasSmartContractRole(address _address) external view returns (bool) {
return hasRole(SMART_CONTRACT_ROLE, _address);
}
function addAdminRole(address _address) external onlyAdminRole {
_setupRole(DEFAULT_ADMIN_ROLE, _address);
emit AdminRoleGranted(_address, _msgSender());
}
function removeAdminRole(address _address) external onlyAdminRole {
revokeRole(DEFAULT_ADMIN_ROLE, _address);
emit AdminRoleRemoved(_address, _msgSender());
}
function addWhitelistRole(address _address) external onlyAdminRole {
_setupRole(WHITELISTED_ROLE, _address);
emit WhitelistRoleGranted(_address, _msgSender());
}
function removeWhitelistRole(address _address) external onlyAdminRole {
revokeRole(WHITELISTED_ROLE, _address);
emit WhitelistRoleRemoved(_address, _msgSender());
}
function addSmartContractRole(address _address) external onlyAdminRole {
_setupRole(SMART_CONTRACT_ROLE, _address);
emit SmartContractRoleGranted(_address, _msgSender());
}
function removeSmartContractRole(address _address) external onlyAdminRole {
revokeRole(SMART_CONTRACT_ROLE, _address);
emit SmartContractRoleRemoved(_address, _msgSender());
}
}
文件 6 的 15:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 7 的 15:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 8 的 15:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 9 的 15:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
文件 10 的 15:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 11 的 15:SafeMath.sol
pragma solidity ^0.8.0;
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return a % b;
}
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}
文件 12 的 15:ServiceProvider.sol
pragma solidity 0.8.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import {StakingRewards} from "./StakingRewards.sol";
contract ServiceProvider is ReentrancyGuard, Context {
using SafeMath for uint256;
using SafeERC20 for IERC20;
struct WithdrawalRequest {
uint256 withdrawalPermittedFrom;
uint256 amount;
uint256 lastStakedBlock;
}
mapping(address => WithdrawalRequest) public withdrawalRequest;
address public controller;
address public serviceProvider;
address public serviceProviderManager;
IERC20 public cudosToken;
uint256 public constant PERCENTAGE_MODULO = 100_00;
bool public isServiceProviderFullySetup;
bool public exited;
uint256 public rewardsFeePercentage;
event StakedServiceProviderBond(address indexed serviceProvider, address indexed serviceProviderManager, uint256 indexed pid, uint256 rewardsFeePercentage);
event IncreasedServiceProviderBond(address indexed serviceProvider, uint256 indexed pid, uint256 amount, uint256 totalAmount);
event DecreasedServiceProviderBond(address indexed serviceProvider, uint256 indexed pid, uint256 amount, uint256 totalAmount);
event ExitedServiceProviderBond(address indexed serviceProvider, uint256 indexed pid);
event WithdrewServiceProviderStake(address indexed serviceProvider, uint256 amount, uint256 totalAmount);
event AddDelegatedStake(address indexed user, uint256 amount, uint256 totalAmount);
event WithdrawDelegatedStakeRequested(address indexed user, uint256 amount, uint256 totalAmount);
event WithdrewDelegatedStake(address indexed user, uint256 amount, uint256 totalAmount);
event ExitDelegatedStake(address indexed user, uint256 amount);
event CalibratedServiceProviderFee(address indexed user, uint256 newFee);
mapping(address => uint256) public delegatedStake;
mapping(address => uint256) public rewardDebt;
uint256 public totalDelegatedStake;
uint256 public rewardsProgrammeId;
uint256 public minStakingLength;
uint256 public accTokensPerShare;
modifier notSetupSP() {
require(isServiceProviderFullySetup, "SPC2");
_;
}
modifier onlySP() {
require(_msgSender() == serviceProvider, "SPC1");
_;
}
modifier onlySPM() {
require(_msgSender() == serviceProviderManager, "SPC3");
_;
}
modifier allowedSP() {
require(_msgSender() != serviceProviderManager && _msgSender() != serviceProvider, "SPC4");
_;
}
modifier isExitedSP() {
require(!exited, "SPHL");
_;
}
modifier notZero(uint256 _amount) {
require(_amount > 0, "SPC6");
_;
}
function init(address _serviceProvider, IERC20 _cudosToken) external {
require(serviceProvider == address(0), "SPI1");
require(_serviceProvider != address(0), "SPI2");
require(address(_cudosToken) != address(0), "SPI3");
serviceProvider = _serviceProvider;
cudosToken = _cudosToken;
controller = _msgSender();
}
function stakeServiceProviderBond(uint256 _rewardsProgrammeId, uint256 _rewardsFeePercentage) nonReentrant external onlySP {
serviceProviderManager = serviceProvider;
_stakeServiceProviderBond(_rewardsProgrammeId, _rewardsFeePercentage);
}
function adminStakeServiceProviderBond(uint256 _rewardsProgrammeId, uint256 _rewardsFeePercentage) nonReentrant external {
require(
StakingRewards(controller).hasAdminRole(_msgSender()),
"OA"
);
serviceProviderManager = _msgSender();
_stakeServiceProviderBond(_rewardsProgrammeId, _rewardsFeePercentage);
}
function increaseServiceProviderStake(uint256 _amount) nonReentrant external notSetupSP onlySPM notZero(_amount) {
StakingRewards rewards = StakingRewards(controller);
uint256 maxStakingAmountForServiceProviders = rewards.maxStakingAmountForServiceProviders();
uint256 amountStakedSoFar = rewards.amountStakedByUserInRewardProgramme(rewardsProgrammeId, address(this));
require(amountStakedSoFar.add(_amount) <= maxStakingAmountForServiceProviders, "SPS1");
_getAndDistributeRewardsWithMassUpdate();
StakingRewards(controller).stake(rewardsProgrammeId, serviceProviderManager, _amount);
delegatedStake[serviceProvider] = delegatedStake[serviceProvider].add(_amount);
totalDelegatedStake = totalDelegatedStake.add(_amount);
WithdrawalRequest storage withdrawalReq = withdrawalRequest[_msgSender()];
withdrawalReq.lastStakedBlock = rewards._getBlock();
emit IncreasedServiceProviderBond(serviceProvider, rewardsProgrammeId, _amount, delegatedStake[serviceProvider]);
}
function requestExcessServiceProviderStakeWithdrawal(uint256 _amount) nonReentrant external notSetupSP onlySPM notZero(_amount) {
StakingRewards rewards = StakingRewards(controller);
WithdrawalRequest storage withdrawalReq = withdrawalRequest[_msgSender()];
uint256 stakeStart = withdrawalReq.lastStakedBlock;
require(rewards._getBlock() >= stakeStart.add(minStakingLength), "SPW5");
uint256 amountLeftAfterWithdrawal = delegatedStake[serviceProvider].sub(_amount);
require(
amountLeftAfterWithdrawal >= rewards.minRequiredStakingAmountForServiceProviders(),
"SPW7"
);
_getAndDistributeRewardsWithMassUpdate();
uint256 unbondingPeriod = rewards.unbondingPeriod();
withdrawalReq.withdrawalPermittedFrom = rewards._getBlock().add(unbondingPeriod);
withdrawalReq.amount = withdrawalReq.amount.add(_amount);
delegatedStake[serviceProvider] = amountLeftAfterWithdrawal;
totalDelegatedStake = totalDelegatedStake.sub(_amount);
rewards.withdraw(rewardsProgrammeId, address(this), _amount);
emit DecreasedServiceProviderBond(serviceProvider, rewardsProgrammeId, _amount, delegatedStake[serviceProvider]);
}
function exitAsServiceProvider() nonReentrant external onlySPM {
StakingRewards rewards = StakingRewards(controller);
WithdrawalRequest storage withdrawalReq = withdrawalRequest[_msgSender()];
uint256 stakeStart = withdrawalReq.lastStakedBlock;
require(rewards._getBlock() >= stakeStart.add(minStakingLength), "SPW5");
_getAndDistributeRewardsWithMassUpdate();
uint256 unbondingPeriod = rewards.unbondingPeriod();
withdrawalReq.withdrawalPermittedFrom = rewards._getBlock().add(unbondingPeriod);
withdrawalReq.amount = withdrawalReq.amount.add(delegatedStake[serviceProvider]);
StakingRewards(controller).exit(rewardsProgrammeId);
uint256 serviceProviderDelegatedStake = delegatedStake[serviceProvider];
delegatedStake[serviceProvider] = 0;
totalDelegatedStake = totalDelegatedStake.sub(serviceProviderDelegatedStake);
isServiceProviderFullySetup = false;
exited = true;
emit ExitedServiceProviderBond(serviceProvider, rewardsProgrammeId);
}
function withdrawServiceProviderStake() nonReentrant external onlySPM {
WithdrawalRequest storage withdrawalReq = withdrawalRequest[_msgSender()];
require(withdrawalReq.amount > 0, "SPW5");
require(
StakingRewards(controller)._getBlock() >= withdrawalReq.withdrawalPermittedFrom,
"SPW3"
);
uint256 withdrawalRequestAmount = withdrawalReq.amount;
withdrawalReq.amount = 0;
cudosToken.transfer(_msgSender(), withdrawalRequestAmount);
emit WithdrewServiceProviderStake(_msgSender(), withdrawalRequestAmount, delegatedStake[serviceProvider]);
}
function delegateStake(uint256 _amount) nonReentrant external notSetupSP allowedSP notZero(_amount) {
_getAndDistributeRewardsWithMassUpdate();
StakingRewards(controller).stake(rewardsProgrammeId, _msgSender(), _amount);
uint256 previousDelegatedStake = delegatedStake[_msgSender()];
delegatedStake[_msgSender()] = previousDelegatedStake.add(_amount);
totalDelegatedStake = totalDelegatedStake.add(_amount);
rewardDebt[_msgSender()] = delegatedStake[_msgSender()].mul(accTokensPerShare).div(1e18);
WithdrawalRequest storage withdrawalReq = withdrawalRequest[_msgSender()];
withdrawalReq.lastStakedBlock = StakingRewards(controller)._getBlock();
emit AddDelegatedStake(_msgSender(), _amount, delegatedStake[_msgSender()]);
}
function requestDelegatedStakeWithdrawal(uint256 _amount) nonReentrant external isExitedSP notSetupSP notZero(_amount) allowedSP {
require(delegatedStake[_msgSender()] >= _amount, "SPW4");
StakingRewards rewards = StakingRewards(controller);
WithdrawalRequest storage withdrawalReq = withdrawalRequest[_msgSender()];
uint256 stakeStart = withdrawalReq.lastStakedBlock;
require(rewards._getBlock() >= stakeStart.add(minStakingLength), "SPW5");
_getAndDistributeRewardsWithMassUpdate();
uint256 unbondingPeriod = rewards.unbondingPeriod();
withdrawalReq.withdrawalPermittedFrom = rewards._getBlock().add(unbondingPeriod);
withdrawalReq.amount = withdrawalReq.amount.add(_amount);
delegatedStake[_msgSender()] = delegatedStake[_msgSender()].sub(_amount);
totalDelegatedStake = totalDelegatedStake.sub(_amount);
rewards.withdraw(rewardsProgrammeId, address(this), _amount);
rewardDebt[_msgSender()] = delegatedStake[_msgSender()].mul(accTokensPerShare).div(1e18);
emit WithdrawDelegatedStakeRequested(_msgSender(), _amount, delegatedStake[_msgSender()]);
}
function withdrawDelegatedStake() nonReentrant external isExitedSP notSetupSP allowedSP {
WithdrawalRequest storage withdrawalReq = withdrawalRequest[_msgSender()];
require(withdrawalReq.amount > 0, "SPW2");
require(
StakingRewards(controller)._getBlock() >= withdrawalReq.withdrawalPermittedFrom,
"SPW3"
);
uint256 withdrawalRequestAmount = withdrawalReq.amount;
withdrawalReq.amount = 0;
cudosToken.transfer(_msgSender(), withdrawalRequestAmount);
emit WithdrewDelegatedStake(_msgSender(), withdrawalRequestAmount, delegatedStake[_msgSender()]);
}
function exitAsDelegator() nonReentrant external {
require(exited, "SPE1");
WithdrawalRequest storage withdrawalReq = withdrawalRequest[_msgSender()];
uint256 withdrawalRequestAmount = withdrawalReq.amount;
uint256 userDelegatedStake = delegatedStake[_msgSender()];
uint256 totalPendingWithdrawal = withdrawalRequestAmount.add(userDelegatedStake);
require(totalPendingWithdrawal > 0, "SPW1");
if (userDelegatedStake > 0) {
_sendDelegatorAnyPendingRewards();
}
withdrawalReq.amount = 0;
delegatedStake[_msgSender()] = 0;
totalDelegatedStake = totalDelegatedStake.sub(userDelegatedStake);
cudosToken.transfer(_msgSender(), totalPendingWithdrawal);
rewardDebt[_msgSender()] = 0;
emit ExitDelegatedStake(_msgSender(), totalPendingWithdrawal);
}
function getReward() external isExitedSP notSetupSP {
_getAndDistributeRewards();
}
function callibrateServiceProviderFee() external {
StakingRewards rewards = StakingRewards(controller);
uint256 minServiceProviderFee = rewards.minServiceProviderFee();
if (rewardsFeePercentage < minServiceProviderFee) {
rewardsFeePercentage = minServiceProviderFee;
emit CalibratedServiceProviderFee(_msgSender(), rewardsFeePercentage);
}
}
function pendingRewards(address _user) public view returns (uint256) {
uint256 pendingRewardsServiceProviderAndDelegators = StakingRewards(controller).pendingCudoRewards(
rewardsProgrammeId,
address(this)
);
(
uint256 stakeDelegatedToServiceProvider,
uint256 rewardsFee,
uint256 baseRewardsDueToServiceProvider,
uint256 netRewardsDueToDelegators
) = _workOutHowMuchDueToServiceProviderAndDelegators(pendingRewardsServiceProviderAndDelegators);
if (_user == serviceProvider && _user == serviceProviderManager) {
return baseRewardsDueToServiceProvider.add(rewardsFee);
} else if (_user == serviceProvider){
return rewardsFee;
} else if (_user == serviceProviderManager){
return baseRewardsDueToServiceProvider;
}
uint256 _accTokensPerShare = accTokensPerShare;
if (stakeDelegatedToServiceProvider > 0) {
_accTokensPerShare = _accTokensPerShare.add(
netRewardsDueToDelegators.mul(1e18).div(stakeDelegatedToServiceProvider)
);
}
return delegatedStake[_user].mul(_accTokensPerShare).div(1e18).sub(rewardDebt[_user]);
}
function _getAndDistributeRewards() private {
uint256 cudosBalanceBeforeGetReward = cudosToken.balanceOf(address(this));
StakingRewards(controller).getReward(rewardsProgrammeId);
uint256 cudosBalanceAfterGetReward = cudosToken.balanceOf(address(this));
uint256 rewardDelta = cudosBalanceAfterGetReward.sub(cudosBalanceBeforeGetReward);
if (rewardDelta > 0) {
_distributeRewards(rewardDelta);
}
if (_msgSender() != serviceProviderManager && _msgSender() != serviceProvider) {
if (delegatedStake[_msgSender()] > 0) {
_sendDelegatorAnyPendingRewards();
}
}
}
function _getAndDistributeRewardsWithMassUpdate() private {
uint256 cudosBalanceBeforeGetReward = cudosToken.balanceOf(address(this));
StakingRewards(controller).getRewardWithMassUpdate(rewardsProgrammeId);
uint256 cudosBalanceAfterGetReward = cudosToken.balanceOf(address(this));
uint256 rewardDelta = cudosBalanceAfterGetReward.sub(cudosBalanceBeforeGetReward);
if (rewardDelta > 0) {
_distributeRewards(rewardDelta);
}
if (_msgSender() != serviceProviderManager && _msgSender() != serviceProvider) {
if (delegatedStake[_msgSender()] > 0) {
_sendDelegatorAnyPendingRewards();
}
}
}
function _distributeRewards(uint256 _amount) private {
(
uint256 stakeDelegatedToServiceProvider,
uint256 rewardsFee,
uint256 baseRewardsDueToServiceProvider,
uint256 netRewardsDueToDelegators
) = _workOutHowMuchDueToServiceProviderAndDelegators(_amount);
if (stakeDelegatedToServiceProvider > 0) {
accTokensPerShare = accTokensPerShare.add(
netRewardsDueToDelegators.mul(1e18).div(stakeDelegatedToServiceProvider)
);
}
if (serviceProvider == serviceProviderManager){
cudosToken.transfer(serviceProvider, baseRewardsDueToServiceProvider.add(rewardsFee));
} else {
cudosToken.transfer(serviceProviderManager, baseRewardsDueToServiceProvider);
cudosToken.transfer(serviceProvider, rewardsFee);
}
}
function _workOutHowMuchDueToServiceProviderAndDelegators(uint256 _amount) private view returns (uint256, uint256, uint256, uint256) {
if (totalDelegatedStake == 0) {
return (0, 0, 0, 0);
}
uint256 stakeDelegatedToServiceProvider = totalDelegatedStake.sub(delegatedStake[serviceProvider]);
uint256 percentageOfStakeThatIsDelegatedToServiceProvider = stakeDelegatedToServiceProvider.mul(PERCENTAGE_MODULO).div(totalDelegatedStake);
uint256 grossRewardsDueToDelegators = _amount.mul(percentageOfStakeThatIsDelegatedToServiceProvider).div(PERCENTAGE_MODULO);
uint256 baseRewardsDueToServiceProvider = _amount.sub(grossRewardsDueToDelegators);
uint256 rewardsFee = grossRewardsDueToDelegators.mul(rewardsFeePercentage).div(PERCENTAGE_MODULO);
uint256 netRewardsDueToDelegators = grossRewardsDueToDelegators.sub(rewardsFee);
return (stakeDelegatedToServiceProvider, rewardsFee, baseRewardsDueToServiceProvider, netRewardsDueToDelegators);
}
function _sendDelegatorAnyPendingRewards() private {
uint256 pending = delegatedStake[_msgSender()].mul(accTokensPerShare).div(1e18).sub(rewardDebt[_msgSender()]);
if (pending > 0) {
rewardDebt[_msgSender()] = delegatedStake[_msgSender()].mul(accTokensPerShare).div(1e18);
cudosToken.transfer(_msgSender(), pending);
}
}
function _stakeServiceProviderBond(uint256 _rewardsProgrammeId, uint256 _rewardsFeePercentage) private {
require(!isServiceProviderFullySetup, "SPC7");
require(!exited, "ECR1");
require(_rewardsFeePercentage > 0 && _rewardsFeePercentage < PERCENTAGE_MODULO, "FP2");
StakingRewards rewards = StakingRewards(controller);
uint256 minRequiredStakingAmountForServiceProviders = rewards.minRequiredStakingAmountForServiceProviders();
uint256 minServiceProviderFee = rewards.minServiceProviderFee();
require(_rewardsFeePercentage >= minServiceProviderFee, "SPF1");
rewardsFeePercentage = _rewardsFeePercentage;
rewardsProgrammeId = _rewardsProgrammeId;
minStakingLength = rewards._findMinStakingLength(_rewardsProgrammeId);
isServiceProviderFullySetup = true;
delegatedStake[serviceProvider] = minRequiredStakingAmountForServiceProviders;
totalDelegatedStake = totalDelegatedStake.add(minRequiredStakingAmountForServiceProviders);
_getAndDistributeRewardsWithMassUpdate();
rewards.stake(
_rewardsProgrammeId,
_msgSender(),
minRequiredStakingAmountForServiceProviders
);
WithdrawalRequest storage withdrawalReq = withdrawalRequest[_msgSender()];
withdrawalReq.lastStakedBlock = rewards._getBlock();
emit StakedServiceProviderBond(serviceProvider, serviceProviderManager, _rewardsProgrammeId, rewardsFeePercentage);
}
function recoverERC20(address _erc20, address _recipient, uint256 _amount) external {
require(StakingRewards(controller).hasAdminRole(_msgSender()), "OA");
IERC20(_erc20).transfer(_recipient, _amount);
}
}
文件 13 的 15:StakingRewards.sol
pragma solidity 0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import { ServiceProvider } from "./ServiceProvider.sol";
import { CloneFactory } from "./CloneFactory.sol";
import { CudosAccessControls } from "../CudosAccessControls.sol";
import { StakingRewardsGuild } from "./StakingRewardsGuild.sol";
contract StakingRewards is CloneFactory, ReentrancyGuard, Context {
using SafeMath for uint256;
using SafeERC20 for IERC20;
struct UserInfo {
uint256 amount;
uint256 rewardDebt;
}
struct RewardProgramme {
uint256 minStakingLengthInBlocks;
uint256 allocPoint;
uint256 lastRewardBlock;
uint256 accTokensPerShare;
uint256 totalStaked;
}
bool public userActionsPaused;
IERC20 public token;
CudosAccessControls public accessControls;
StakingRewardsGuild public rewardsGuildBank;
uint256 public tokenRewardPerBlock;
RewardProgramme[] public rewardProgrammes;
mapping(uint256 => bool) public isActiveRewardProgramme;
mapping (uint256 => mapping (address => UserInfo)) public userInfo;
uint256 public totalCudosStaked;
uint256 public weightedTotalCudosStaked;
uint256 public startBlock;
mapping(address => address) public serviceProviderToWhitelistedProxyContracts;
mapping(address => address) public serviceProviderContractToServiceProvider;
address public cloneableServiceProviderContract;
uint256 public minRequiredStakingAmountForServiceProviders = 2_000_000 * 10 ** 18;
uint256 public maxStakingAmountForServiceProviders = 1_000_000_000 * 10 ** 18;
uint256 public constant PERCENTAGE_MODULO = 100_00;
uint256 public minServiceProviderFee = 2_00;
uint256 public constant numOfBlocksInADay = 6500;
uint256 public unbondingPeriod = numOfBlocksInADay.mul(21);
event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);
event MinRequiredStakingAmountForServiceProvidersUpdated(uint256 oldValue, uint256 newValue);
event MaxStakingAmountForServiceProvidersUpdated(uint256 oldValue, uint256 newValue);
event MinServiceProviderFeeUpdated(uint256 oldValue, uint256 newValue);
event ServiceProviderWhitelisted(address indexed serviceProvider, address indexed serviceProviderContract);
event RewardPerBlockUpdated(uint256 oldValue, uint256 newValue);
event RewardProgrammeAdded(uint256 allocPoint, uint256 minStakingLengthInBlocks);
event RewardProgrammeAllocPointUpdated(uint256 oldValue, uint256 newValue);
event UserActionsPausedToggled(bool isPaused);
modifier paused() {
require(userActionsPaused == false, "PSD");
_;
}
modifier notZero(uint256 _amount) {
require(_amount > 0, "SPC6");
_;
}
modifier unkSP() {
require(serviceProviderContractToServiceProvider[_msgSender()] != address(0), "SPU1");
_;
}
modifier whitelisted() {
require(accessControls.hasWhitelistRole(_msgSender()), "OWL");
_;
}
constructor(
IERC20 _token,
CudosAccessControls _accessControls,
StakingRewardsGuild _rewardsGuildBank,
uint256 _tokenRewardPerBlock,
uint256 _startBlock,
address _cloneableServiceProviderContract
) {
require(address(_accessControls) != address(0), "StakingRewards.constructor: Invalid access controls");
require(address(_token) != address(0), "StakingRewards.constructor: Invalid token address");
require(_cloneableServiceProviderContract != address(0), "StakingRewards.constructor: Invalid cloneable service provider");
token = _token;
accessControls = _accessControls;
rewardsGuildBank = _rewardsGuildBank;
tokenRewardPerBlock = _tokenRewardPerBlock;
startBlock = _startBlock;
cloneableServiceProviderContract = _cloneableServiceProviderContract;
}
function updateRewardProgramme(uint256 _programmeId) public {
RewardProgramme storage rewardProgramme = rewardProgrammes[_programmeId];
if (_getBlock() <= rewardProgramme.lastRewardBlock) {
return;
}
uint256 totalStaked = rewardProgramme.totalStaked;
if (totalStaked == 0) {
rewardProgramme.lastRewardBlock = _getBlock();
return;
}
uint256 blocksSinceLastReward = _getBlock().sub(rewardProgramme.lastRewardBlock);
uint256 rewardPerShare = blocksSinceLastReward.mul(tokenRewardPerBlock).mul(rewardProgramme.allocPoint).mul(1e18).div(weightedTotalCudosStaked);
rewardProgramme.accTokensPerShare = rewardProgramme.accTokensPerShare.add(rewardPerShare);
rewardProgramme.lastRewardBlock = _getBlock();
}
function getReward(uint256 _programmeId) external nonReentrant {
updateRewardProgramme(_programmeId);
_getReward(_programmeId);
}
function massUpdateRewardProgrammes() public {
uint256 programmeLength = rewardProgrammes.length;
for(uint256 i = 0; i < programmeLength; i++) {
updateRewardProgramme(i);
}
}
function getRewardWithMassUpdate(uint256 _programmeId) external nonReentrant {
massUpdateRewardProgrammes();
_getReward(_programmeId);
}
function stake(uint256 _programmeId, address _from, uint256 _amount) external nonReentrant paused notZero(_amount) unkSP {
RewardProgramme storage rewardProgramme = rewardProgrammes[_programmeId];
UserInfo storage user = userInfo[_programmeId][_msgSender()];
user.amount = user.amount.add(_amount);
rewardProgramme.totalStaked = rewardProgramme.totalStaked.add(_amount);
totalCudosStaked = totalCudosStaked.add(_amount);
weightedTotalCudosStaked = weightedTotalCudosStaked.add(_amount.mul(rewardProgramme.allocPoint));
user.rewardDebt = user.amount.mul(rewardProgramme.accTokensPerShare).div(1e18);
token.safeTransferFrom(address(_from), address(rewardsGuildBank), _amount);
emit Deposit(_from, _programmeId, _amount);
}
function withdraw(uint256 _programmeId, address _to, uint256 _amount) public nonReentrant paused notZero(_amount) unkSP {
RewardProgramme storage rewardProgramme = rewardProgrammes[_programmeId];
UserInfo storage user = userInfo[_programmeId][_msgSender()];
require(user.amount >= _amount, "SRW1");
user.amount = user.amount.sub(_amount);
rewardProgramme.totalStaked = rewardProgramme.totalStaked.sub(_amount);
totalCudosStaked = totalCudosStaked.sub(_amount);
weightedTotalCudosStaked = weightedTotalCudosStaked.sub(_amount.mul(rewardProgramme.allocPoint));
user.rewardDebt = user.amount.mul(rewardProgramme.accTokensPerShare).div(1e18);
rewardsGuildBank.withdrawTo(_to, _amount);
emit Withdraw(_msgSender(), _programmeId, _amount);
}
function exit(uint256 _programmeId) external unkSP {
withdraw(_programmeId, _msgSender(), userInfo[_programmeId][_msgSender()].amount);
}
function numberOfRewardProgrammes() external view returns (uint256) {
return rewardProgrammes.length;
}
function getRewardProgrammeInfo(uint256 _programmeId) external view returns (
uint256 minStakingLengthInBlocks,
uint256 allocPoint,
uint256 lastRewardBlock,
uint256 accTokensPerShare,
uint256 totalStaked
) {
RewardProgramme storage rewardProgramme = rewardProgrammes[_programmeId];
return (
rewardProgramme.minStakingLengthInBlocks,
rewardProgramme.allocPoint,
rewardProgramme.lastRewardBlock,
rewardProgramme.accTokensPerShare,
rewardProgramme.totalStaked
);
}
function amountStakedByUserInRewardProgramme(uint256 _programmeId, address _user) external view returns (uint256) {
return userInfo[_programmeId][_user].amount;
}
function totalStakedInRewardProgramme(uint256 _programmeId) external view returns (uint256) {
return rewardProgrammes[_programmeId].totalStaked;
}
function totalStakedAcrossAllRewardProgrammes() external view returns (uint256) {
return totalCudosStaked;
}
function pendingCudoRewards(uint256 _programmeId, address _user) external view returns (uint256) {
RewardProgramme storage rewardProgramme = rewardProgrammes[_programmeId];
UserInfo storage user = userInfo[_programmeId][_user];
uint256 accTokensPerShare = rewardProgramme.accTokensPerShare;
uint256 totalStaked = rewardProgramme.totalStaked;
if (_getBlock() > rewardProgramme.lastRewardBlock && totalStaked != 0) {
uint256 blocksSinceLastReward = _getBlock().sub(rewardProgramme.lastRewardBlock);
uint256 rewardPerShare = blocksSinceLastReward.mul(tokenRewardPerBlock).mul(rewardProgramme.allocPoint).mul(1e18).div(weightedTotalCudosStaked);
accTokensPerShare = accTokensPerShare.add(rewardPerShare);
}
return user.amount.mul(accTokensPerShare).div(1e18).sub(user.rewardDebt);
}
function hasAdminRole(address _caller) external view returns (bool) {
return accessControls.hasAdminRole(_caller);
}
function updateMinRequiredStakingAmountForServiceProviders(uint256 _newValue) external whitelisted {
require(_newValue < maxStakingAmountForServiceProviders, "StakingRewards.updateMinRequiredStakingAmountForServiceProviders: Min staking must be less than max staking amount");
emit MinRequiredStakingAmountForServiceProvidersUpdated(minRequiredStakingAmountForServiceProviders, _newValue);
minRequiredStakingAmountForServiceProviders = _newValue;
}
function updateMaxStakingAmountForServiceProviders(uint256 _newValue) external whitelisted {
require(_newValue > minRequiredStakingAmountForServiceProviders, "StakingRewards.updateMaxStakingAmountForServiceProviders: Max staking must be greater than min staking amount");
emit MaxStakingAmountForServiceProvidersUpdated(maxStakingAmountForServiceProviders, _newValue);
maxStakingAmountForServiceProviders = _newValue;
}
function updateMinServiceProviderFee(uint256 _newValue) external whitelisted {
require(_newValue > 0 && _newValue < PERCENTAGE_MODULO, "StakingRewards.updateMinServiceProviderFee: Fee percentage must be between zero and one");
emit MinServiceProviderFeeUpdated(minServiceProviderFee, _newValue);
minServiceProviderFee = _newValue;
}
function recoverERC20(address _erc20, address _recipient, uint256 _amount) external {
require(accessControls.hasAdminRole(_msgSender()), "OA");
IERC20(_erc20).safeTransfer(_recipient, _amount);
}
function whitelistServiceProvider(address _serviceProvider) external {
require(accessControls.hasAdminRole(_msgSender()), "OA");
require(serviceProviderToWhitelistedProxyContracts[_serviceProvider] == address(0), "StakingRewards.whitelistServiceProvider: Already whitelisted service provider");
address serviceProviderContract = createClone(cloneableServiceProviderContract);
serviceProviderToWhitelistedProxyContracts[_serviceProvider] = serviceProviderContract;
serviceProviderContractToServiceProvider[serviceProviderContract] = _serviceProvider;
ServiceProvider(serviceProviderContract).init(_serviceProvider, token);
emit ServiceProviderWhitelisted(_serviceProvider, serviceProviderContract);
}
function updateTokenRewardPerBlock(uint256 _tokenRewardPerBlock) external {
require(
accessControls.hasAdminRole(_msgSender()),
"StakingRewards.updateTokenRewardPerBlock: Only admin"
);
massUpdateRewardProgrammes();
emit RewardPerBlockUpdated(tokenRewardPerBlock, _tokenRewardPerBlock);
tokenRewardPerBlock = _tokenRewardPerBlock;
}
function addRewardsProgramme(uint256 _allocPoint, uint256 _minStakingLengthInBlocks, bool _withUpdate) external {
require(
accessControls.hasAdminRole(_msgSender()),
"OA"
);
require(
isActiveRewardProgramme[_minStakingLengthInBlocks] == false,
"PAA"
);
require(_allocPoint > 0, "IAP");
if (_withUpdate) {
massUpdateRewardProgrammes();
}
uint256 lastRewardBlock = _getBlock() > startBlock ? _getBlock() : startBlock;
rewardProgrammes.push(
RewardProgramme({
minStakingLengthInBlocks: _minStakingLengthInBlocks,
allocPoint: _allocPoint,
lastRewardBlock: lastRewardBlock,
accTokensPerShare: 0,
totalStaked: 0
})
);
isActiveRewardProgramme[_minStakingLengthInBlocks] = true;
emit RewardProgrammeAdded(_allocPoint, _minStakingLengthInBlocks);
}
function updateAllocPointForRewardProgramme(uint256 _programmeId, uint256 _allocPoint, bool _withUpdate) external {
require(
accessControls.hasAdminRole(_msgSender()),
"OA"
);
if (_withUpdate) {
massUpdateRewardProgrammes();
}
weightedTotalCudosStaked = weightedTotalCudosStaked.sub(rewardProgrammes[_programmeId].totalStaked.mul(rewardProgrammes[_programmeId].allocPoint));
emit RewardProgrammeAllocPointUpdated(rewardProgrammes[_programmeId].allocPoint, _allocPoint);
rewardProgrammes[_programmeId].allocPoint = _allocPoint;
weightedTotalCudosStaked = weightedTotalCudosStaked.add(rewardProgrammes[_programmeId].totalStaked.mul(rewardProgrammes[_programmeId].allocPoint));
}
function updateUserActionsPaused(bool _isPaused) external {
require(
accessControls.hasAdminRole(_msgSender()),
"OA"
);
userActionsPaused = _isPaused;
emit UserActionsPausedToggled(_isPaused);
}
function _getReward(uint256 _programmeId) internal {
RewardProgramme storage rewardProgramme = rewardProgrammes[_programmeId];
UserInfo storage user = userInfo[_programmeId][_msgSender()];
if (user.amount > 0) {
uint256 pending = user.amount.mul(rewardProgramme.accTokensPerShare).div(1e18).sub(user.rewardDebt);
if (pending > 0) {
user.rewardDebt = user.amount.mul(rewardProgramme.accTokensPerShare).div(1e18);
rewardsGuildBank.withdrawTo(_msgSender(), pending);
}
}
}
function _getBlock() public virtual view returns (uint256) {
return block.number;
}
function _findMinStakingLength(uint256 _programmeId) external view returns (uint256) {
RewardProgramme storage rewardProgramme = rewardProgrammes[_programmeId];
uint256 minStakingLength = rewardProgramme.minStakingLengthInBlocks;
return minStakingLength;
}
}
文件 14 的 15:StakingRewardsGuild.sol
pragma solidity 0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { CudosAccessControls } from "../CudosAccessControls.sol";
contract StakingRewardsGuild {
using SafeERC20 for IERC20;
IERC20 public token;
CudosAccessControls public accessControls;
constructor(IERC20 _token, CudosAccessControls _accessControls) {
token = _token;
accessControls = _accessControls;
}
function withdrawTo(address _recipient, uint256 _amount) external {
require(
accessControls.hasSmartContractRole(msg.sender),
"OASM"
);
require(_recipient != address(0), "SRG1");
token.transfer(_recipient, _amount);
}
function recoverERC20(address _erc20, address _recipient, uint256 _amount) external {
require(accessControls.hasAdminRole(msg.sender), "OA");
IERC20(_erc20).safeTransfer(_recipient, _amount);
}
}
文件 15 的 15:Strings.sol
pragma solidity ^0.8.0;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
function toString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
{
"compilationTarget": {
"contracts/staking/StakingRewards.sol": "StakingRewards"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"contract CudosAccessControls","name":"_accessControls","type":"address"},{"internalType":"contract StakingRewardsGuild","name":"_rewardsGuildBank","type":"address"},{"internalType":"uint256","name":"_tokenRewardPerBlock","type":"uint256"},{"internalType":"uint256","name":"_startBlock","type":"uint256"},{"internalType":"address","name":"_cloneableServiceProviderContract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"MaxStakingAmountForServiceProvidersUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"MinRequiredStakingAmountForServiceProvidersUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"MinServiceProviderFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"RewardPerBlockUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"allocPoint","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minStakingLengthInBlocks","type":"uint256"}],"name":"RewardProgrammeAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"RewardProgrammeAllocPointUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"serviceProvider","type":"address"},{"indexed":true,"internalType":"address","name":"serviceProviderContract","type":"address"}],"name":"ServiceProviderWhitelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"UserActionsPausedToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"pid","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"PERCENTAGE_MODULO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_programmeId","type":"uint256"}],"name":"_findMinStakingLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_getBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accessControls","outputs":[{"internalType":"contract CudosAccessControls","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_allocPoint","type":"uint256"},{"internalType":"uint256","name":"_minStakingLengthInBlocks","type":"uint256"},{"internalType":"bool","name":"_withUpdate","type":"bool"}],"name":"addRewardsProgramme","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_programmeId","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"amountStakedByUserInRewardProgramme","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cloneableServiceProviderContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_programmeId","type":"uint256"}],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_programmeId","type":"uint256"}],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_programmeId","type":"uint256"}],"name":"getRewardProgrammeInfo","outputs":[{"internalType":"uint256","name":"minStakingLengthInBlocks","type":"uint256"},{"internalType":"uint256","name":"allocPoint","type":"uint256"},{"internalType":"uint256","name":"lastRewardBlock","type":"uint256"},{"internalType":"uint256","name":"accTokensPerShare","type":"uint256"},{"internalType":"uint256","name":"totalStaked","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_programmeId","type":"uint256"}],"name":"getRewardWithMassUpdate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"}],"name":"hasAdminRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"isActiveRewardProgramme","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"massUpdateRewardProgrammes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxStakingAmountForServiceProviders","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minRequiredStakingAmountForServiceProviders","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minServiceProviderFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numOfBlocksInADay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfRewardProgrammes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_programmeId","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"pendingCudoRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_erc20","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardProgrammes","outputs":[{"internalType":"uint256","name":"minStakingLengthInBlocks","type":"uint256"},{"internalType":"uint256","name":"allocPoint","type":"uint256"},{"internalType":"uint256","name":"lastRewardBlock","type":"uint256"},{"internalType":"uint256","name":"accTokensPerShare","type":"uint256"},{"internalType":"uint256","name":"totalStaked","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsGuildBank","outputs":[{"internalType":"contract StakingRewardsGuild","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"serviceProviderContractToServiceProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"serviceProviderToWhitelistedProxyContracts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_programmeId","type":"uint256"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenRewardPerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCudosStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakedAcrossAllRewardProgrammes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_programmeId","type":"uint256"}],"name":"totalStakedInRewardProgramme","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unbondingPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_programmeId","type":"uint256"},{"internalType":"uint256","name":"_allocPoint","type":"uint256"},{"internalType":"bool","name":"_withUpdate","type":"bool"}],"name":"updateAllocPointForRewardProgramme","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newValue","type":"uint256"}],"name":"updateMaxStakingAmountForServiceProviders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newValue","type":"uint256"}],"name":"updateMinRequiredStakingAmountForServiceProviders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newValue","type":"uint256"}],"name":"updateMinServiceProviderFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_programmeId","type":"uint256"}],"name":"updateRewardProgramme","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenRewardPerBlock","type":"uint256"}],"name":"updateTokenRewardPerBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isPaused","type":"bool"}],"name":"updateUserActionsPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"userActionsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"rewardDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weightedTotalCudosStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_serviceProvider","type":"address"}],"name":"whitelistServiceProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_programmeId","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]