Contract Name:
OrionVoting
Contract Source Code:
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
//import "hardhat/console.sol";
abstract contract Staking {
uint256 internal constant RATE_PRECISION = 1e40;
uint256 public rewardRate; //скорость наград, например 1 ORN в секунду на всех (точность 10**48)
struct UserState {
uint256 stake;
uint256 rateCumulative;
uint256 reward;
uint256 rewardWithdraw;
}
uint256 public allStake; //сумма всех стейков
mapping(address => UserState) public poolStake; //стейки по пользователям
uint256 public rateCumulative;
uint256 public rateTime;
uint256 private rewardCumulativeTotal; //сумма всех наград
event SetRewards(
uint64 rewards,
uint64 duration,
uint256 rewardCumulativeTotal,
uint256 rateCumulative,
uint256 timestamp
);
event Stake(
address indexed account,
uint256 amount,
uint256 rewardCumulativeTotal,
uint256 rateCumulative,
uint256 reward,
uint256 timestamp
);
event Unstake(
address indexed account,
uint256 amount,
uint256 rewardCumulativeTotal,
uint256 rateCumulative,
uint256 reward,
uint256 timestamp
);
event ClaimReward(
address indexed account,
uint256 amount,
uint256 rewardCumulativeTotal,
uint256 rateCumulative,
uint256 reward,
uint256 timestamp
);
// Set the overall reward
function _setRewards(uint64 rewards, uint64 duration) internal {
require(duration > 0, "_setRewards: zero duration");
_writeCumulative();
// ORN / sec
rewardRate = (RATE_PRECISION * rewards) / duration;
emit SetRewards(
rewards,
duration,
rewardCumulativeTotal,
rateCumulative,
block.timestamp
);
}
//Расчет нового курса награды
function calcNewRate() public view virtual returns (uint256) {
uint256 Rate = 0;
if (allStake > 0) {
Rate = rewardRate / allStake;
}
return Rate * (block.timestamp - rateTime);
}
function _writeCumulative() internal virtual {
uint256 newRate = calcNewRate();
rewardCumulativeTotal += (newRate * allStake) / RATE_PRECISION;
rateCumulative += newRate;
rateTime = block.timestamp;
}
function _stake(address account, uint256 amount) internal {
require(amount > 0, "_stake: zero stake amount");
_writeCumulative();
UserState memory item = poolStake[account];
item.reward = _calcReward(item, rateCumulative);
item.stake += amount;
item.rateCumulative = rateCumulative;
poolStake[account] = item;
allStake += amount;
emit Stake(
account,
amount,
rewardCumulativeTotal,
rateCumulative,
item.reward,
block.timestamp
);
}
function _claimReward(address account, uint256 amount) internal {
_writeCumulative();
UserState memory item = poolStake[account];
item.reward = _calcReward(item, rateCumulative);
require(
item.reward - item.rewardWithdraw >= amount,
"Error claim amount"
);
item.rewardWithdraw += amount;
item.rateCumulative = rateCumulative;
poolStake[account] = item;
emit ClaimReward(
account,
amount,
rewardCumulativeTotal,
rateCumulative,
item.reward,
block.timestamp
);
}
function _unstake(address account, uint256 amount) internal {
_writeCumulative();
UserState memory item = poolStake[account];
require(item.stake >= amount, "Error unstake amount");
item.reward = _calcReward(item, rateCumulative);
item.stake -= amount;
item.rateCumulative = rateCumulative;
poolStake[account] = item;
allStake -= amount;
emit Unstake(
account,
amount,
rewardCumulativeTotal,
rateCumulative,
item.reward,
block.timestamp
);
}
function _calcReward(
UserState memory item,
uint256 _rateCumulative
) internal pure returns (uint256) {
return
item.reward +
((_rateCumulative - item.rateCumulative) * item.stake) /
RATE_PRECISION;
}
function getReward(address account) public view virtual returns (uint256) {
UserState memory item = poolStake[account];
uint256 _rateCumulative = rateCumulative + calcNewRate();
return _calcReward(item, _rateCumulative) - item.rewardWithdraw;
}
function getStake(address account) public view returns (uint256) {
return poolStake[account].stake;
}
function getRewardWithdraw(
address account
) external view returns (uint256) {
return poolStake[account].rewardWithdraw;
}
function getRewardCumulative(
address account
) external view returns (uint256) {
return getReward(account) + poolStake[account].rewardWithdraw;
}
function getRewardCumulativeAll() public view returns (uint256) {
uint256 newRate = calcNewRate();
return rewardCumulativeTotal + (newRate * allStake) / RATE_PRECISION;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
interface IOrionFarmV2
{
//Action
function create_lock_period(address pool, uint256 amount, uint256 lock_period) external;
function increase_amount(address pool, uint256 amount) external;
function increase_lock_period(address pool, uint256 new_lock_period) external;
function withdraw(address pool) external;
function claimReward(address pool) external;
function createSmartReward(address pool) external;
//View
function getReward(address pool, address account) external view returns (uint256);
function getBoost(address pool, address account) external view returns (uint256);
function getStake(address pool, address account) external view returns (uint256);
function allStake(address pool) external view returns (uint256);
function lockTimeStart(address pool, address account) external view returns (uint48);
function lockTimePeriod(address pool, address account) external view returns (uint48);
function libStakingReward() external view returns(address);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "./IStaking.sol";
interface IOrionVoting is IStaking
{
//admin
function setSmart(address addr, bool bUse) external;
function setRewards(uint64 rewards, uint64 duration) external;
function addPool(address pool) external;
function deletePool(address pool) external;
//user
function vote(address pool, uint256 amount) external;
function voteArr(address[] calldata pools, uint256[] calldata amounts) external;
function unvote(address pool, uint256 amount) external;
function unvoteAll(address account) external;
//smart
function claimReward(address pool, address to, uint256 amount) external;
//vew
function countPool() external view returns (uint256);//number of pools
function poolList(uint256) external view returns (address);//list of pools
function poolIndex(address pool) external view returns (uint256);//whether there is a pool in the list (index numbers starting from 1)
function users(address user) external view returns (uint256);//user votes across all pools
function usersPool(address user,address pool) external view returns (uint256);//user votes by pool
function smarts(address smart) external view returns (bool);//white list of trusted farm contracts
function veORN() external view returns (address);
function ORN() external view returns (address);
function havePool(address account) external view returns (bool);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
interface IStaking
{
//staking
function getReward(address account) external view returns (uint256);
function getStake(address account) external view returns (uint256);
function allStake() external view returns (uint256);
function rewardRate() external view returns (uint256);
function rateCumulative() external view returns (uint256);
function rateTime() external view returns (uint256);
function getRewardWithdraw(address account) external view returns (uint256);
function getRewardCumulative(address account) external view returns (uint256);
function getRewardCumulativeAll() external view returns (uint256);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
interface ITWBalance
{
struct TWItem
{
// the block timestamp
uint48 timestamp;
// the amount accumulator, i.e. amount * time elapsed
uint208 amountTW;
}
/// @notice Returns the time weight (TW) amount of tokens in existence.
function totalSupplyTW() external view returns (TWItem memory);
/// @notice Calculates the average aamount of tokens in existence from the specified TW period
function totalSupplyAvg(TWItem memory itemStart) view external returns (uint256);
/// @notice Returns the time weight (TW) balance of a token
/// @param user The account for which to look up the number of tokens it has, i.e. its balance
/// @return The number of tokens held by the account
function balanceOfTW(address user) external view returns (TWItem memory);
/// @notice Calculates the average address balance from the specified TW period
function balanceOfAvg(address user, TWItem memory itemStart) view external returns (uint256);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "./ITWBalance.sol";
import "./IStaking.sol";
interface IveORN is ITWBalance,IStaking
{
struct UserInfo {
uint48 time_lock;
uint128 balance;
uint128 amount_token;
}
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
function totalSupply(uint256 ts) external view returns (uint256);
function totalSupply0() external view returns (uint256);//balance on start timestamp
/// @notice Returns the balance of a token
/// @param account The account for which to look up the number of tokens it has, i.e. its balance
/// @return The number of tokens held by the account
function balanceOf(address account) external view returns (uint256);
function balanceOf(address account, uint256 ts) external view returns (uint256);
function balanceOf0(address account) external view returns (uint256);//balance on start timestamp
function balanceTokenOf(address account) external view returns (uint256);
/// @notice Returns the number of decimals used to get its user representation.
function decimals() external view returns (uint8);
function name() pure external returns(string memory);
function symbol() pure external returns(string memory);
function ORN() external view returns (address);
function lockTime(address account) external view returns (uint48);
//staking ORN
function create_lock(uint256 _value, uint256 _unlock_time) external;
//function deposit_for(address _addr, uint256 _value) external;
function increase_amount(uint256 _value) external;
function increase_unlock_time(uint256 _unlock_time) external;
function increase_unlock_period(uint256 unlock_period) external;
function create_lock_period(uint256 _value, uint256 unlock_period) external;
function withdraw() external;
function claimReward() external;
function tokenMap(address user) external returns(UserInfo memory);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.7;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
library TransferHelper {
/// @notice Transfers tokens from the targeted address to the given destination
/// @notice Errors with 'STF' if transfer fails
/// @param token The contract address of the token to be transferred
/// @param from The originating address from which the tokens will be transferred
/// @param to The destination address of the transfer
/// @param value The amount to be transferred
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
}
/// @notice Transfers tokens from msg.sender to a recipient
/// @dev Errors with ST if transfer fails
/// @param token The contract address of the token which will be transferred
/// @param to The recipient of the transfer
/// @param value The value of the transfer
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
}
/// @notice Approves the stipulated contract to spend the given allowance in the given token
/// @dev Errors with 'SA' if transfer fails
/// @param token The contract address of the token to be approved
/// @param to The target of the approval
/// @param value The amount of the given token the target will be allowed to spend
function safeApprove(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
}
/// @notice Transfers ETH to the recipient address
/// @dev Fails with `STE`
/// @param to The destination of the transfer
/// @param value The value to be transferred
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'STE');
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "./base/Staking.sol";
import "./libraries/TransferHelper.sol";
import "./interfaces/IveORN.sol";
import "./interfaces/IOrionVoting.sol";
import "./interfaces/IOrionFarmV2.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
uint256 constant MAX_PERCENT = 10000;
contract OrionVoting is Staking {
using EnumerableSet for EnumerableSet.Bytes32Set;
struct Proposal {
bytes32 name;
EnumerableSet.Bytes32Set choices;
uint256 timestampFinish;
}
uint256 public countPool; //number of pools
mapping(uint256 => address) public poolList; //list of pools
mapping(address => uint256) public poolIndex; //whether there is a pool in the list (index numbers starting from 1)
mapping(address => uint256) public users; //user votes across all pools
mapping(address => mapping(address => uint256)) public usersPool; //user votes by pool
mapping(address => bool) public smarts; //white list of trusted farm contracts
address public immutable veORN;
address public immutable ORN;
address public immutable smartOwner;
mapping(bytes32 => Proposal) private proposals;
mapping(bytes32 => uint256) public proposalVotes;
mapping(bytes32 => mapping(address => uint256)) public voted;
mapping(address => mapping(bytes32 => bytes32)) public userVotedChoice;
modifier onlyOwner() {
require(msg.sender == smartOwner, "Caller is not the owner");
_;
}
event UsePool(address indexed pool, bool bUse);
event Vote(address indexed pool, address indexed account, uint256 amount);
event Unvote(address indexed pool, address indexed account, uint256 amount);
event UnvoteAll(address indexed account);
event ProposalCreated(
bytes32 name,
uint256 timestampFinish,
bytes32[] choices
);
event ProposalVoted(
address user,
bytes32 name,
bytes32 choice,
uint256 votePower
);
event ProposalUnvoted(
address user,
bytes32 name,
bytes32 choice,
uint256 votePower
);
error MAX_PROPOSALS_EXCEED();
error PROPOSAL_NOT_EXIST();
error ALREADY_VOTED();
error EMPTY_NAME();
error EMPTY_CHOICES();
error PROPOSAL_FINISHED();
error CHOICE_NOT_EXIST();
error DUPLICATED_CHOICES();
error PROPOSAL_ALREADY_EXIST();
error LOCK_WILL_EXPIRE();
error ZERO_PREVIOUS_VOTE();
error CANT_CHANGE_CHOICE();
constructor(address veORN_, address owner) {
veORN = veORN_;
ORN = IveORN(veORN).ORN();
smartOwner = owner;
}
//admin caller
function setSmart(address addr, bool bUse) external onlyOwner {
smarts[addr] = bUse;
}
function setRewards(uint64 rewards, uint64 duration) external onlyOwner {
_setRewards(rewards, duration);
}
function addPool(address pool) public onlyOwner {
countPool++;
poolIndex[pool] = countPool;
poolList[countPool] = pool;
emit UsePool(pool, true);
}
function deletePool(address pool) external onlyOwner {
uint256 index = poolIndex[pool];
require(index > 0, "Pool not found");
delete poolIndex[pool];
//we move the last element to the place of the deleted one and delete the last element
poolList[index] = poolList[countPool];
poolIndex[poolList[countPool]] = index;
delete poolList[countPool];
countPool--;
emit UsePool(pool, false);
}
//smart caller
function claimReward(address pool, address to, uint256 amount) external {
require(
smarts[msg.sender],
"claimReward: caller not found in white list"
);
_claimReward(pool, amount);
TransferHelper.safeTransfer(ORN, to, amount);
}
//user caller
function votePercent(address pool, uint256 percent) external {
require(percent <= MAX_PERCENT, "Error percent");
uint256 balanceVeORN = IveORN(veORN).balanceOf0(msg.sender);
vote(pool, (balanceVeORN * percent) / MAX_PERCENT);
}
function vote(address pool, uint256 amount) public {
require(poolIndex[pool] > 0, "Pool not found");
//check balance
uint256 balanceVeORN = IveORN(veORN).balanceOf0(msg.sender);
uint256 balanceVotes = users[msg.sender];
//require(balanceVeORN >= balanceVotes+amount,"Error user veORN balance");// and revert if overflow
uint256 balanceRemained;
if (balanceVeORN > balanceVotes)
balanceRemained = balanceVeORN - balanceVotes;
if (amount > balanceRemained) amount = balanceRemained;
users[msg.sender] += amount;
usersPool[msg.sender][pool] += amount;
_stake(pool, amount);
emit Vote(pool, msg.sender, amount);
}
function unvotePercent(address pool, uint256 percent) external {
require(percent <= MAX_PERCENT, "Error percent");
uint256 balanceVeORN = IveORN(veORN).balanceOf0(msg.sender);
unvote(pool, (balanceVeORN * percent) / MAX_PERCENT);
}
function unvote(address pool, uint256 amount) public {
if (usersPool[msg.sender][pool] > amount) {
usersPool[msg.sender][pool] -= amount;
} else {
amount = usersPool[msg.sender][pool];
delete usersPool[msg.sender][pool];
}
if (users[msg.sender] > amount) {
users[msg.sender] -= amount;
} else {
amount = users[msg.sender];
delete users[msg.sender];
}
_unstake(pool, amount);
emit Unvote(pool, msg.sender, amount);
}
//array call support
function voteArr(
address[] calldata pools,
uint256[] calldata amounts
) external {
require(pools.length == amounts.length, "Pool not found");
for (uint256 i = 0; i < pools.length; i++) vote(pools[i], amounts[i]);
}
//user or smart caller
function unvoteAll(address account) external {
require(
msg.sender == veORN || msg.sender == account,
"unvoteAll: caller is not the veORN contract"
);
uint256 balanceVotes = users[account];
if (balanceVotes > 0) {
uint256 _countPool = countPool;
for (uint256 i = 1; i <= _countPool; i++) {
address pool = poolList[i];
uint256 amount = usersPool[account][pool];
if (amount > 0) {
usersPool[account][pool] = 0;
_unstake(pool, amount);
balanceVotes -= amount;
if (balanceVotes == 0) break;
}
}
users[account] = 0;
emit UnvoteAll(account);
}
}
function addPool2(address pool, address farmv2) external {
//добавление пула и создание смарт-контракта наград для пулов v2
addPool(pool); //check owner
IOrionFarmV2(farmv2).createSmartReward(pool);
}
function createProposal(
bytes32 name,
bytes32[] memory choices,
uint256 timestampFinish
) external onlyOwner {
if (name == 0x0) revert EMPTY_NAME();
if (choices.length > 10) revert MAX_PROPOSALS_EXCEED();
if (choices.length == 0) revert EMPTY_CHOICES();
if (timestampFinish <= block.timestamp) revert PROPOSAL_FINISHED();
Proposal storage proposal = proposals[name];
if (proposal.name != 0x0) revert PROPOSAL_ALREADY_EXIST();
proposal.name = name;
proposal.timestampFinish = timestampFinish;
for (uint i = 0; i < choices.length; ++i) {
bytes32 choice = choices[i];
if (choice == 0x0) revert EMPTY_NAME();
if (!proposal.choices.add(choice)) revert DUPLICATED_CHOICES();
}
emit ProposalCreated(name, timestampFinish, choices);
}
function voteProposal(bytes32 name, bytes32 choice) external {
Proposal storage proposal = proposals[name];
if (proposal.name == 0x0) revert PROPOSAL_NOT_EXIST();
if (proposal.timestampFinish <= block.timestamp)
revert PROPOSAL_FINISHED();
if (!proposal.choices.contains(choice)) revert CHOICE_NOT_EXIST();
uint256 lockFinish = IveORN(veORN).lockTime(msg.sender);
if (lockFinish < proposal.timestampFinish)
revert LOCK_WILL_EXPIRE();
bytes32 currentChoice = userVotedChoice[msg.sender][name];
if (currentChoice != bytes32(0) && currentChoice != choice) revert CANT_CHANGE_CHOICE();
uint256 votePower = IveORN(veORN).balanceOf0(msg.sender); // votePower != 0 due to userInfo.time_lock != 0
uint256 votedAmount = voted[name][msg.sender];
if (votePower <= votedAmount) revert ALREADY_VOTED();
bytes32 id = keccak256(abi.encode(name, choice));
proposalVotes[id] += votePower - votedAmount;
voted[name][msg.sender] = votePower;
userVotedChoice[msg.sender][name] = choice;
emit ProposalVoted(msg.sender, name, choice, votePower);
}
function changeVoteProposal(
bytes32 name,
bytes32 previousChoice,
bytes32 newChoice
) external {
Proposal storage proposal = proposals[name];
if (proposal.name == 0x0) revert PROPOSAL_NOT_EXIST();
if (proposal.timestampFinish <= block.timestamp)
revert PROPOSAL_FINISHED();
if (!proposal.choices.contains(previousChoice)) revert CHOICE_NOT_EXIST();
if (!proposal.choices.contains(newChoice)) revert CHOICE_NOT_EXIST();
uint256 votedAmount = voted[name][msg.sender];
if (votedAmount == 0) revert ZERO_PREVIOUS_VOTE();
uint256 votePower = IveORN(veORN).balanceOf0(msg.sender); // votePower >= votedAmount
bytes32 previousId = keccak256(abi.encode(name, previousChoice));
proposalVotes[previousId] -= votedAmount;
bytes32 newId = keccak256(abi.encode(name, newChoice));
proposalVotes[newId] += votePower;
voted[name][msg.sender] = votePower;
userVotedChoice[msg.sender][name] = newChoice;
emit ProposalUnvoted(msg.sender, name, previousChoice, votedAmount);
emit ProposalVoted(msg.sender, name, newChoice, votePower);
}
function proposalInfo(
bytes32 name
)
external
view
returns (bytes32, uint256, bytes32[] memory, uint256[] memory)
{
Proposal storage proposal = proposals[name];
uint256 choicesLength = proposal.choices.length();
uint256[] memory votes = new uint256[](choicesLength);
for (uint i = 0; i < choicesLength; ++i) {
bytes32 id = keccak256(abi.encode(name, proposal.choices.at(i)));
votes[i] = proposalVotes[id];
}
return (
name,
proposal.timestampFinish,
proposal.choices.values(),
votes
);
}
//view
function havePool(address account) external view returns (bool) {
return poolIndex[account] > 0;
}
}