编译器
0.8.10+commit.fc410830
文件 1 的 10:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 10:AggregatorV3Interface.sol
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
文件 3 的 10: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;
}
}
文件 4 的 10:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, 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 from, address to, uint256 amount) external returns (bool);
}
文件 5 的 10:IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 6 的 10:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 7 的 10:Pausable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Pausable is Context {
event Paused(address account);
event Unpaused(address account);
bool private _paused;
constructor() {
_paused = false;
}
modifier whenNotPaused() {
_requireNotPaused();
_;
}
modifier whenPaused() {
_requirePaused();
_;
}
function paused() public view virtual returns (bool) {
return _paused;
}
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
文件 8 的 10:PredictionETH.sol
pragma solidity ^0.8.0;
pragma abicoder v2;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract PredictionETH is Ownable, Pausable, ReentrancyGuard {
using SafeERC20 for IERC20;
AggregatorV3Interface public oracle;
bool public genesisLockOnce = false;
bool public genesisStartOnce = false;
address public adminAddress;
address public operatorAddress;
uint256 public bufferSeconds;
uint256 public intervalSeconds;
uint256 public minBetAmount;
uint256 public treasuryFee;
uint256 public treasuryAmount;
uint256 public currentEpoch;
uint256 public oracleLatestRoundId;
uint256 public oracleUpdateAllowance;
uint256 public constant MAX_TREASURY_FEE = 1000;
mapping(uint256 => mapping(address => BetInfo)) public ledger;
mapping(uint256 => Round) public rounds;
mapping(address => uint256[]) public userRounds;
enum Position {
Bull,
Bear
}
struct Round {
uint256 epoch;
uint256 startTimestamp;
uint256 lockTimestamp;
uint256 closeTimestamp;
int256 lockPrice;
int256 closePrice;
uint256 lockOracleId;
uint256 closeOracleId;
uint256 totalAmount;
uint256 bullAmount;
uint256 bearAmount;
uint256 rewardBaseCalAmount;
uint256 rewardAmount;
bool oracleCalled;
}
struct BetInfo {
Position position;
uint256 amount;
bool claimed;
}
event BetBear(address indexed sender, uint256 indexed epoch, uint256 amount);
event BetBull(address indexed sender, uint256 indexed epoch, uint256 amount);
event Claim(address indexed sender, uint256 indexed epoch, uint256 amount);
event EndRound(uint256 indexed epoch, uint256 indexed roundId, int256 price);
event LockRound(uint256 indexed epoch, uint256 indexed roundId, int256 price);
event NewAdminAddress(address admin);
event NewBufferAndIntervalSeconds(uint256 bufferSeconds, uint256 intervalSeconds);
event NewMinBetAmount(uint256 indexed epoch, uint256 minBetAmount);
event NewTreasuryFee(uint256 indexed epoch, uint256 treasuryFee);
event NewOperatorAddress(address operator);
event NewOracle(address oracle);
event NewOracleUpdateAllowance(uint256 oracleUpdateAllowance);
event Pause(uint256 indexed epoch);
event RewardsCalculated(
uint256 indexed epoch,
uint256 rewardBaseCalAmount,
uint256 rewardAmount,
uint256 treasuryAmount
);
event StartRound(uint256 indexed epoch);
event TokenRecovery(address indexed token, uint256 amount);
event TreasuryClaim(uint256 amount);
event Unpause(uint256 indexed epoch);
modifier onlyAdmin() {
require(msg.sender == adminAddress, "Not admin");
_;
}
modifier onlyAdminOrOperator() {
require(msg.sender == adminAddress || msg.sender == operatorAddress, "Not operator/admin");
_;
}
modifier onlyOperator() {
require(msg.sender == operatorAddress, "Not operator");
_;
}
modifier notContract() {
require(!_isContract(msg.sender), "Contract not allowed");
require(msg.sender == tx.origin, "Proxy contract not allowed");
_;
}
constructor(
address _oracleAddress,
address _adminAddress,
address _operatorAddress,
uint256 _intervalSeconds,
uint256 _bufferSeconds,
uint256 _minBetAmount,
uint256 _oracleUpdateAllowance,
uint256 _treasuryFee
) {
require(_treasuryFee <= MAX_TREASURY_FEE, "Treasury fee too high");
oracle = AggregatorV3Interface(_oracleAddress);
adminAddress = _adminAddress;
operatorAddress = _operatorAddress;
intervalSeconds = _intervalSeconds;
bufferSeconds = _bufferSeconds;
minBetAmount = _minBetAmount;
oracleUpdateAllowance = _oracleUpdateAllowance;
treasuryFee = _treasuryFee;
}
function betBear(uint256 epoch) external payable whenNotPaused nonReentrant notContract {
require(epoch == currentEpoch, "Bet is too early/late");
require(_bettable(epoch), "Round not bettable");
require(msg.value >= minBetAmount, "Bet amount must be greater than minBetAmount");
require(ledger[epoch][msg.sender].amount == 0, "Can only bet once per round");
uint256 amount = msg.value;
Round storage round = rounds[epoch];
round.totalAmount = round.totalAmount + amount;
round.bearAmount = round.bearAmount + amount;
BetInfo storage betInfo = ledger[epoch][msg.sender];
betInfo.position = Position.Bear;
betInfo.amount = amount;
userRounds[msg.sender].push(epoch);
emit BetBear(msg.sender, epoch, amount);
}
function betBull(uint256 epoch) external payable whenNotPaused nonReentrant notContract {
require(epoch == currentEpoch, "Bet is too early/late");
require(_bettable(epoch), "Round not bettable");
require(msg.value >= minBetAmount, "Bet amount must be greater than minBetAmount");
require(ledger[epoch][msg.sender].amount == 0, "Can only bet once per round");
uint256 amount = msg.value;
Round storage round = rounds[epoch];
round.totalAmount = round.totalAmount + amount;
round.bullAmount = round.bullAmount + amount;
BetInfo storage betInfo = ledger[epoch][msg.sender];
betInfo.position = Position.Bull;
betInfo.amount = amount;
userRounds[msg.sender].push(epoch);
emit BetBull(msg.sender, epoch, amount);
}
function claim(uint256[] calldata epochs) external nonReentrant notContract {
uint256 reward;
for (uint256 i = 0; i < epochs.length; i++) {
require(rounds[epochs[i]].startTimestamp != 0, "Round has not started");
require(block.timestamp > rounds[epochs[i]].closeTimestamp, "Round has not ended");
uint256 addedReward = 0;
if (rounds[epochs[i]].oracleCalled) {
require(claimable(epochs[i], msg.sender), "Not eligible for claim");
Round memory round = rounds[epochs[i]];
addedReward = (ledger[epochs[i]][msg.sender].amount * round.rewardAmount) / round.rewardBaseCalAmount;
}
else {
require(refundable(epochs[i], msg.sender), "Not eligible for refund");
addedReward = ledger[epochs[i]][msg.sender].amount;
}
ledger[epochs[i]][msg.sender].claimed = true;
reward += addedReward;
emit Claim(msg.sender, epochs[i], addedReward);
}
if (reward > 0) {
_safeTransferETH(address(msg.sender), reward);
}
}
function executeRound() external whenNotPaused onlyOperator {
require(
genesisStartOnce && genesisLockOnce,
"Can only run after genesisStartRound and genesisLockRound is triggered"
);
(uint80 currentRoundId, int256 currentPrice) = _getPriceFromOracle();
oracleLatestRoundId = uint256(currentRoundId);
_safeLockRound(currentEpoch, currentRoundId, currentPrice);
_safeEndRound(currentEpoch - 1, currentRoundId, currentPrice);
_calculateRewards(currentEpoch - 1);
currentEpoch = currentEpoch + 1;
_safeStartRound(currentEpoch);
}
function genesisLockRound() external whenNotPaused onlyOperator {
require(genesisStartOnce, "Can only run after genesisStartRound is triggered");
require(!genesisLockOnce, "Can only run genesisLockRound once");
(uint80 currentRoundId, int256 currentPrice) = _getPriceFromOracle();
oracleLatestRoundId = uint256(currentRoundId);
_safeLockRound(currentEpoch, currentRoundId, currentPrice);
currentEpoch = currentEpoch + 1;
_startRound(currentEpoch);
genesisLockOnce = true;
}
function genesisStartRound() external whenNotPaused onlyOperator {
require(!genesisStartOnce, "Can only run genesisStartRound once");
currentEpoch = currentEpoch + 1;
_startRound(currentEpoch);
genesisStartOnce = true;
}
function pause() external whenNotPaused onlyAdminOrOperator {
_pause();
emit Pause(currentEpoch);
}
function claimTreasury() external nonReentrant onlyAdmin {
uint256 currentTreasuryAmount = treasuryAmount;
treasuryAmount = 0;
_safeTransferETH(adminAddress, currentTreasuryAmount);
emit TreasuryClaim(currentTreasuryAmount);
}
function unpause() external whenPaused onlyAdmin {
genesisStartOnce = false;
genesisLockOnce = false;
_unpause();
emit Unpause(currentEpoch);
}
function setBufferAndIntervalSeconds(uint256 _bufferSeconds, uint256 _intervalSeconds)
external
whenPaused
onlyAdmin
{
require(_bufferSeconds < _intervalSeconds, "bufferSeconds must be inferior to intervalSeconds");
bufferSeconds = _bufferSeconds;
intervalSeconds = _intervalSeconds;
emit NewBufferAndIntervalSeconds(_bufferSeconds, _intervalSeconds);
}
function setMinBetAmount(uint256 _minBetAmount) external whenPaused onlyAdmin {
require(_minBetAmount != 0, "Must be superior to 0");
minBetAmount = _minBetAmount;
emit NewMinBetAmount(currentEpoch, minBetAmount);
}
function setOperator(address _operatorAddress) external onlyAdmin {
require(_operatorAddress != address(0), "Cannot be zero address");
operatorAddress = _operatorAddress;
emit NewOperatorAddress(_operatorAddress);
}
function setOracle(address _oracle) external whenPaused onlyAdmin {
require(_oracle != address(0), "Cannot be zero address");
oracleLatestRoundId = 0;
oracle = AggregatorV3Interface(_oracle);
oracle.latestRoundData();
emit NewOracle(_oracle);
}
function setOracleUpdateAllowance(uint256 _oracleUpdateAllowance) external whenPaused onlyAdmin {
oracleUpdateAllowance = _oracleUpdateAllowance;
emit NewOracleUpdateAllowance(_oracleUpdateAllowance);
}
function setTreasuryFee(uint256 _treasuryFee) external whenPaused onlyAdmin {
require(_treasuryFee <= MAX_TREASURY_FEE, "Treasury fee too high");
treasuryFee = _treasuryFee;
emit NewTreasuryFee(currentEpoch, treasuryFee);
}
function recoverToken(address _token, uint256 _amount) external onlyOwner {
IERC20(_token).safeTransfer(address(msg.sender), _amount);
emit TokenRecovery(_token, _amount);
}
function setAdmin(address _adminAddress) external onlyOwner {
require(_adminAddress != address(0), "Cannot be zero address");
adminAddress = _adminAddress;
emit NewAdminAddress(_adminAddress);
}
function getUserRounds(
address user,
uint256 cursor,
uint256 size
)
external
view
returns (
uint256[] memory,
BetInfo[] memory,
uint256
)
{
uint256 length = size;
if (length > userRounds[user].length - cursor) {
length = userRounds[user].length - cursor;
}
uint256[] memory values = new uint256[](length);
BetInfo[] memory betInfo = new BetInfo[](length);
for (uint256 i = 0; i < length; i++) {
values[i] = userRounds[user][cursor + i];
betInfo[i] = ledger[values[i]][user];
}
return (values, betInfo, cursor + length);
}
function getUserRoundsLength(address user) external view returns (uint256) {
return userRounds[user].length;
}
function claimable(uint256 epoch, address user) public view returns (bool) {
BetInfo memory betInfo = ledger[epoch][user];
Round memory round = rounds[epoch];
if (round.lockPrice == round.closePrice) {
return false;
}
return
round.oracleCalled &&
betInfo.amount != 0 &&
!betInfo.claimed &&
((round.closePrice > round.lockPrice && betInfo.position == Position.Bull) ||
(round.closePrice < round.lockPrice && betInfo.position == Position.Bear));
}
function refundable(uint256 epoch, address user) public view returns (bool) {
BetInfo memory betInfo = ledger[epoch][user];
Round memory round = rounds[epoch];
return
!round.oracleCalled &&
!betInfo.claimed &&
block.timestamp > round.closeTimestamp + bufferSeconds &&
betInfo.amount != 0;
}
function _calculateRewards(uint256 epoch) internal {
require(rounds[epoch].rewardBaseCalAmount == 0 && rounds[epoch].rewardAmount == 0, "Rewards calculated");
Round storage round = rounds[epoch];
uint256 rewardBaseCalAmount;
uint256 treasuryAmt;
uint256 rewardAmount;
if (round.closePrice > round.lockPrice) {
rewardBaseCalAmount = round.bullAmount;
treasuryAmt = (round.totalAmount * treasuryFee) / 10000;
rewardAmount = round.totalAmount - treasuryAmt;
}
else if (round.closePrice < round.lockPrice) {
rewardBaseCalAmount = round.bearAmount;
treasuryAmt = (round.totalAmount * treasuryFee) / 10000;
rewardAmount = round.totalAmount - treasuryAmt;
}
else {
rewardBaseCalAmount = 0;
rewardAmount = 0;
treasuryAmt = round.totalAmount;
}
round.rewardBaseCalAmount = rewardBaseCalAmount;
round.rewardAmount = rewardAmount;
treasuryAmount += treasuryAmt;
emit RewardsCalculated(epoch, rewardBaseCalAmount, rewardAmount, treasuryAmt);
}
function _safeEndRound(
uint256 epoch,
uint256 roundId,
int256 price
) internal {
require(rounds[epoch].lockTimestamp != 0, "Can only end round after round has locked");
require(block.timestamp >= rounds[epoch].closeTimestamp, "Can only end round after closeTimestamp");
require(
block.timestamp <= rounds[epoch].closeTimestamp + bufferSeconds,
"Can only end round within bufferSeconds"
);
Round storage round = rounds[epoch];
round.closePrice = price;
round.closeOracleId = roundId;
round.oracleCalled = true;
emit EndRound(epoch, roundId, round.closePrice);
}
function _safeLockRound(
uint256 epoch,
uint256 roundId,
int256 price
) internal {
require(rounds[epoch].startTimestamp != 0, "Can only lock round after round has started");
require(block.timestamp >= rounds[epoch].lockTimestamp, "Can only lock round after lockTimestamp");
require(
block.timestamp <= rounds[epoch].lockTimestamp + bufferSeconds,
"Can only lock round within bufferSeconds"
);
Round storage round = rounds[epoch];
round.closeTimestamp = block.timestamp + intervalSeconds;
round.lockPrice = price;
round.lockOracleId = roundId;
emit LockRound(epoch, roundId, round.lockPrice);
}
function _safeStartRound(uint256 epoch) internal {
require(genesisStartOnce, "Can only run after genesisStartRound is triggered");
require(rounds[epoch - 2].closeTimestamp != 0, "Can only start round after round n-2 has ended");
require(
block.timestamp >= rounds[epoch - 2].closeTimestamp,
"Can only start new round after round n-2 closeTimestamp"
);
_startRound(epoch);
}
function _safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}("");
require(success, "TransferHelper: ETH_TRANSFER_FAILED");
}
function _startRound(uint256 epoch) internal {
Round storage round = rounds[epoch];
round.startTimestamp = block.timestamp;
round.lockTimestamp = block.timestamp + intervalSeconds;
round.closeTimestamp = block.timestamp + (2 * intervalSeconds);
round.epoch = epoch;
round.totalAmount = 0;
emit StartRound(epoch);
}
function _bettable(uint256 epoch) internal view returns (bool) {
return
rounds[epoch].startTimestamp != 0 &&
rounds[epoch].lockTimestamp != 0 &&
block.timestamp > rounds[epoch].startTimestamp &&
block.timestamp < rounds[epoch].lockTimestamp;
}
function _getPriceFromOracle() internal view returns (uint80, int256) {
uint256 leastAllowedTimestamp = block.timestamp + oracleUpdateAllowance;
(uint80 roundId, int256 price, , uint256 timestamp, ) = oracle.latestRoundData();
require(timestamp <= leastAllowedTimestamp, "Oracle update exceeded max timestamp allowance");
require(
uint256(roundId) > oracleLatestRoundId,
"Oracle update roundId must be larger than oracleLatestRoundId"
);
return (roundId, price);
}
function _isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
}
文件 9 的 10: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() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
文件 10 的 10:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.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 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
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");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
{
"compilationTarget": {
"contracts/predictions/PredictionETH.sol": "PredictionETH"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 999999
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_oracleAddress","type":"address"},{"internalType":"address","name":"_adminAddress","type":"address"},{"internalType":"address","name":"_operatorAddress","type":"address"},{"internalType":"uint256","name":"_intervalSeconds","type":"uint256"},{"internalType":"uint256","name":"_bufferSeconds","type":"uint256"},{"internalType":"uint256","name":"_minBetAmount","type":"uint256"},{"internalType":"uint256","name":"_oracleUpdateAllowance","type":"uint256"},{"internalType":"uint256","name":"_treasuryFee","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BetBear","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BetBull","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"int256","name":"price","type":"int256"}],"name":"EndRound","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":false,"internalType":"int256","name":"price","type":"int256"}],"name":"LockRound","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"NewAdminAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"bufferSeconds","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"intervalSeconds","type":"uint256"}],"name":"NewBufferAndIntervalSeconds","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minBetAmount","type":"uint256"}],"name":"NewMinBetAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"NewOperatorAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oracle","type":"address"}],"name":"NewOracle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oracleUpdateAllowance","type":"uint256"}],"name":"NewOracleUpdateAllowance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasuryFee","type":"uint256"}],"name":"NewTreasuryFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardBaseCalAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"treasuryAmount","type":"uint256"}],"name":"RewardsCalculated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"StartRound","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenRecovery","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TreasuryClaim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"MAX_TREASURY_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adminAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"betBear","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"betBull","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"bufferSeconds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"epochs","type":"uint256[]"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"claimable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"executeRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"genesisLockOnce","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"genesisLockRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"genesisStartOnce","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"genesisStartRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"cursor","type":"uint256"},{"internalType":"uint256","name":"size","type":"uint256"}],"name":"getUserRounds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"components":[{"internalType":"enum PredictionETH.Position","name":"position","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"claimed","type":"bool"}],"internalType":"struct PredictionETH.BetInfo[]","name":"","type":"tuple[]"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserRoundsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"intervalSeconds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"ledger","outputs":[{"internalType":"enum PredictionETH.Position","name":"position","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"claimed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minBetAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operatorAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleLatestRoundId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleUpdateAllowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"recoverToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"refundable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rounds","outputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"startTimestamp","type":"uint256"},{"internalType":"uint256","name":"lockTimestamp","type":"uint256"},{"internalType":"uint256","name":"closeTimestamp","type":"uint256"},{"internalType":"int256","name":"lockPrice","type":"int256"},{"internalType":"int256","name":"closePrice","type":"int256"},{"internalType":"uint256","name":"lockOracleId","type":"uint256"},{"internalType":"uint256","name":"closeOracleId","type":"uint256"},{"internalType":"uint256","name":"totalAmount","type":"uint256"},{"internalType":"uint256","name":"bullAmount","type":"uint256"},{"internalType":"uint256","name":"bearAmount","type":"uint256"},{"internalType":"uint256","name":"rewardBaseCalAmount","type":"uint256"},{"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"internalType":"bool","name":"oracleCalled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_adminAddress","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_bufferSeconds","type":"uint256"},{"internalType":"uint256","name":"_intervalSeconds","type":"uint256"}],"name":"setBufferAndIntervalSeconds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minBetAmount","type":"uint256"}],"name":"setMinBetAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operatorAddress","type":"address"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oracle","type":"address"}],"name":"setOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_oracleUpdateAllowance","type":"uint256"}],"name":"setOracleUpdateAllowance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_treasuryFee","type":"uint256"}],"name":"setTreasuryFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasuryAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"treasuryFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userRounds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]