511 lines
22 KiB
Solidity
511 lines
22 KiB
Solidity
// SPDX-License-Identifier: CC0-1.0
|
|
pragma solidity ^0.7.1;
|
|
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
|
|
import { ABDKMath64x64 } from "abdk-libraries-solidity/ABDKMath64x64.sol";
|
|
import { ERC1155WithTotals } from "./ERC1155/ERC1155WithTotals.sol";
|
|
import { ERC1155Holder } from "@openzeppelin/contracts/token/ERC1155/ERC1155Holder.sol";
|
|
import { IERC1155 } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
|
|
import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/ERC721Holder.sol";
|
|
|
|
/// @title A base class to lock collaterals and distribute them proportional to an oracle result.
|
|
/// @author Victor Porton
|
|
/// @notice Not audited, not enough tested.
|
|
///
|
|
/// One can also donate/bequest a smart wallet (explain how).
|
|
///
|
|
/// We have two kinds of ERC-1155 token IDs:
|
|
/// - conditional tokens: numbers < 2**64
|
|
/// - a combination of a collateral contract address and collateral token ID
|
|
/// (a counter of donated amount of collateral tokens, don't confuse with collateral tokens themselves)
|
|
///
|
|
/// Inheriting from here don't forget to create `createOracle()` external method.
|
|
abstract contract BaseLock is
|
|
ERC1155WithTotals,
|
|
ERC1155Holder, // You are recommended to use `donate()` function instead.
|
|
ERC721Holder // It can be used through an ERC-1155 wrapper.
|
|
{
|
|
using ABDKMath64x64 for int128;
|
|
using SafeMath for uint256;
|
|
|
|
/// Emitted when an oracle is created.
|
|
/// @param oracleId The ID of the created oracle.
|
|
event OracleCreated(uint64 oracleId);
|
|
|
|
/// Emitted when an oracle owner is set.
|
|
/// @param oracleOwner Who created an oracle
|
|
/// @param oracleId The ID of the oracle.
|
|
event OracleOwnerChanged(address indexed oracleOwner, uint64 indexed oracleId);
|
|
|
|
/// Emitted when an oracle owner is set.
|
|
/// @param sender Who created the condition
|
|
/// @param customer The owner of the condition.
|
|
/// @param condition The created condition ID.
|
|
event ConditionCreated(address indexed sender, address indexed customer, uint256 indexed condition);
|
|
|
|
/// Emitted when a collateral is donated.
|
|
/// @param collateralContractAddress The ERC-1155 contract of the donated token.
|
|
/// @param collateralTokenId The ERC-1155 ID of the donated token.
|
|
/// @param sender Who donated.
|
|
/// @param amount The amount donated.
|
|
/// @param to Whose account the donation is assigned to.
|
|
/// @param data Additional transaction data.
|
|
event DonateCollateral(
|
|
IERC1155 indexed collateralContractAddress,
|
|
uint256 indexed collateralTokenId,
|
|
address indexed sender,
|
|
uint256 amount,
|
|
address to,
|
|
bytes data
|
|
);
|
|
|
|
/// Emitted when an oracle is marked as having finished its work.
|
|
/// @param oracleId The oracle ID.
|
|
event OracleFinished(uint64 indexed oracleId);
|
|
|
|
/// Emitted when collateral is withdrawn.
|
|
/// @param contractAddress The ERC-1155 contract of the collateral token.
|
|
/// @param collateralTokenId The ERC-1155 token ID of the collateral.
|
|
/// @param oracleId The oracle ID for which withdrawal is done.
|
|
/// @param user Who has withdrawn.
|
|
/// @param amount The amount withdrawn.
|
|
event CollateralWithdrawn(
|
|
IERC1155 indexed contractAddress,
|
|
uint256 indexed collateralTokenId,
|
|
uint64 indexed oracleId,
|
|
address user,
|
|
uint256 amount
|
|
);
|
|
|
|
// Next ID.
|
|
uint64 public maxOracleId; // It doesn't really need to be public.
|
|
uint64 public maxConditionId; // It doesn't really need to be public.
|
|
|
|
// Mapping (oracleId => oracle owner).
|
|
mapping(uint64 => address) private oracleOwnersMap;
|
|
// Mapping (oracleId => time) the max time for first withdrawal.
|
|
mapping(uint64 => uint) private gracePeriodEnds;
|
|
// The user lost the right to transfer conditional tokens: (user => (conditionalToken => bool)).
|
|
mapping(address => mapping(uint256 => bool)) private userUsedRedeemMap;
|
|
// Mapping (token => (original user => amount)) used to calculate withdrawal of collateral amounts.
|
|
mapping(uint256 => mapping(address => uint256)) public lastCollateralBalanceFirstRoundMap;
|
|
// Mapping (token => (original user => amount)) used to calculate withdrawal of collateral amounts.
|
|
mapping(uint256 => mapping(address => uint256)) public lastCollateralBalanceSecondRoundMap;
|
|
/// Mapping (oracleId => amount user withdrew in first round) (see `docs/Calculations.md`).
|
|
mapping(uint64 => uint256) public usersWithdrewInFirstRound;
|
|
|
|
// Mapping (condition ID => original account)
|
|
mapping(uint256 => address) public conditionOwners;
|
|
|
|
/// Constructor.
|
|
/// @param _uri Our ERC-1155 tokens description URI.
|
|
constructor(string memory _uri) ERC1155WithTotals(_uri) {
|
|
_registerInterface(
|
|
BaseLock(0).onERC1155Received.selector ^
|
|
BaseLock(0).onERC1155BatchReceived.selector ^
|
|
BaseLock(0).onERC721Received.selector
|
|
);
|
|
}
|
|
|
|
/// This function makes no sense, because it would produce a condition with zero tokens.
|
|
// function createCondition() public returns (uint64) {
|
|
// return _createCondition();
|
|
// }
|
|
|
|
/// Modify the owner of an oracle.
|
|
/// @param _newOracleOwner New owner.
|
|
/// @param _oracleId The oracle whose owner to change.
|
|
function changeOracleOwner(address _newOracleOwner, uint64 _oracleId) public _isOracle(_oracleId) {
|
|
oracleOwnersMap[_oracleId] = _newOracleOwner;
|
|
emit OracleOwnerChanged(_newOracleOwner, _oracleId);
|
|
}
|
|
|
|
/// Set the end time of the grace period.
|
|
///
|
|
/// The first withdrawal can be done *only* during the grace period.
|
|
/// The second withdrawal can be done after the end of the grace period and only if the first withdrawal was done.
|
|
///
|
|
/// The intention of the grace period is to check which of users are active ("alive").
|
|
function updateGracePeriodEnds(uint64 _oracleId, uint _time) public _isOracle(_oracleId) {
|
|
gracePeriodEnds[_oracleId] = _time;
|
|
}
|
|
|
|
/// Donate funds in an ERC-1155 token.
|
|
///
|
|
/// First, the collateral token need to be approved to be spent by this contract from the address `_from`.
|
|
///
|
|
/// It also mints a token (with a different ID), that counts donations in that token.
|
|
///
|
|
/// @param _collateralContractAddress The collateral ERC-1155 contract address.
|
|
/// @param _collateralTokenId The collateral ERC-1155 token ID.
|
|
/// @param _oracleId The oracle ID to whose ecosystem to donate to.
|
|
/// @param _amount The amount to donate.
|
|
/// @param _from From whom to take the donation.
|
|
/// @param _to On whose account the donation amount is assigned.
|
|
/// @param _data Additional transaction data.
|
|
function donate(
|
|
IERC1155 _collateralContractAddress,
|
|
uint256 _collateralTokenId,
|
|
uint64 _oracleId,
|
|
uint256 _amount,
|
|
address _from,
|
|
address _to,
|
|
bytes calldata _data) public
|
|
{
|
|
uint _donatedPerOracleCollateralTokenId = _collateralDonatedPerOracleTokenId(_collateralContractAddress, _collateralTokenId, _oracleId);
|
|
_mint(_to, _donatedPerOracleCollateralTokenId, _amount, _data);
|
|
uint _donatedCollateralTokenId = _collateralDonatedTokenId(_collateralContractAddress, _collateralTokenId);
|
|
_mint(_to, _donatedCollateralTokenId, _amount, _data);
|
|
emit DonateCollateral(_collateralContractAddress, _collateralTokenId, _from, _amount, _to, _data);
|
|
_collateralContractAddress.safeTransferFrom(_from, address(this), _collateralTokenId, _amount, _data); // last against reentrancy attack
|
|
}
|
|
|
|
/// Gather a DeFi profit of a token previous donated to this contact.
|
|
/// @param _collateralContractAddress The collateral ERC-1155 contract address.
|
|
/// @param _collateralTokenId The collateral ERC-1155 token ID.
|
|
/// @param _oracleId The oracle ID to whose ecosystem to donate to.
|
|
/// @param _data Additional transaction data.
|
|
/// TODO: Batch calls in several tokens and/or to several oracles for less gas usage?
|
|
function gatherDeFiProfit(
|
|
IERC1155 _collateralContractAddress,
|
|
uint256 _collateralTokenId,
|
|
uint64 _oracleId,
|
|
bytes calldata _data) external
|
|
{
|
|
uint _donatedPerOracleCollateralTokenId = _collateralDonatedPerOracleTokenId(_collateralContractAddress, _collateralTokenId, _oracleId);
|
|
uint _donatedCollateralTokenId = _collateralDonatedTokenId(_collateralContractAddress, _collateralTokenId);
|
|
|
|
// We consider an overflow an error and just revert:
|
|
// FIXME: Impossible due to reentrancy vulnerability? (Really? It's a view!)
|
|
uint256 _difference =
|
|
_collateralContractAddress.balanceOf(address(this), _collateralTokenId).sub(
|
|
balanceOf(address(this), _donatedCollateralTokenId));
|
|
uint256 _amount = // rounding down to prevent overflows
|
|
_difference *
|
|
balanceOf(address(this), _donatedPerOracleCollateralTokenId) /
|
|
balanceOf(address(this), _donatedCollateralTokenId);
|
|
|
|
// Last to avoid reentrancy vulnerability.
|
|
donate(
|
|
_collateralContractAddress,
|
|
_collateralTokenId,
|
|
_oracleId,
|
|
_amount,
|
|
address(this),
|
|
address(this),
|
|
_data);
|
|
}
|
|
|
|
/// Calculate how much collateral is owed to a user.
|
|
/// @param _collateralContractAddress The ERC-1155 collateral token contract.
|
|
/// @param _collateralTokenId The ERC-1155 collateral token ID.
|
|
/// @param _oracleId From which oracle's "account" to withdraw.
|
|
/// @param _condition The condition (the original receiver of a conditional token).
|
|
/// @param _user The user to which we may owe.
|
|
function collateralOwing(
|
|
IERC1155 _collateralContractAddress,
|
|
uint256 _collateralTokenId,
|
|
uint64 _oracleId,
|
|
uint256 _condition,
|
|
address _user
|
|
) external view returns(uint256) {
|
|
bool _inFirstRound = _isInFirstRound(_oracleId);
|
|
(, uint256 _donated) =
|
|
_collateralOwingBase(_collateralContractAddress, _collateralTokenId, _oracleId, _condition, _user, _inFirstRound);
|
|
return _donated;
|
|
}
|
|
|
|
/// Transfer to `msg.sender` the collateral ERC-1155 token.
|
|
///
|
|
/// The amount transferred is proportional to the score of `_condition` by the oracle.
|
|
/// @param _collateralContractAddress The ERC-1155 collateral token contract.
|
|
/// @param _collateralTokenId The ERC-1155 collateral token ID.
|
|
/// @param _oracleId From which oracle's "account" to withdraw.
|
|
/// @param _condition The condition.
|
|
/// @param _data Additional data.
|
|
///
|
|
/// Notes:
|
|
/// - It is made impossible to withdraw somebody's other collateral, as otherwise we can't mark non-active
|
|
/// accounts in grace period.
|
|
/// - We can't transfer to somebody other than `msg.sender` because anybody can transfer
|
|
/// (needed for multi-level transfers).
|
|
/// - After this function is called, it becomes impossible to transfer the corresponding conditional token
|
|
/// of `msg.sender` (to prevent its repeated withdrawal).
|
|
function withdrawCollateral(
|
|
IERC1155 _collateralContractAddress,
|
|
uint256 _collateralTokenId,
|
|
uint64 _oracleId,
|
|
uint256 _condition,
|
|
bytes calldata _data) external
|
|
{
|
|
require(isOracleFinished(_oracleId), "too early"); // to prevent the denominator or the numerators change meantime
|
|
bool _inFirstRound = _isInFirstRound(_oracleId);
|
|
userUsedRedeemMap[msg.sender][_condition] = true;
|
|
// _burn(msg.sender, _condition, conditionalBalance); // Burning it would break using the same token for multiple markets.
|
|
(uint _donatedPerOracleCollateralTokenId, uint256 _owingDonated) =
|
|
_collateralOwingBase(_collateralContractAddress, _collateralTokenId, _oracleId, _condition, msg.sender, _inFirstRound);
|
|
|
|
// Against rounding errors. Not necessary because of rounding down.
|
|
// if(_owing > balanceOf(address(this), _collateralTokenId)) _owing = balanceOf(address(this), _collateralTokenId);
|
|
|
|
if (_owingDonated != 0) {
|
|
uint256 _newTotal = totalSupply(_donatedPerOracleCollateralTokenId);
|
|
if (_inFirstRound) {
|
|
lastCollateralBalanceFirstRoundMap[_donatedPerOracleCollateralTokenId][msg.sender] = _newTotal;
|
|
} else {
|
|
lastCollateralBalanceSecondRoundMap[_donatedPerOracleCollateralTokenId][msg.sender] = _newTotal;
|
|
}
|
|
}
|
|
if (!_inFirstRound) {
|
|
usersWithdrewInFirstRound[_oracleId] = usersWithdrewInFirstRound[_oracleId].add(_owingDonated);
|
|
}
|
|
// Last to prevent reentrancy attack:
|
|
_collateralContractAddress.safeTransferFrom(address(this), msg.sender, _collateralTokenId, _owingDonated, _data);
|
|
emit CollateralWithdrawn(
|
|
_collateralContractAddress,
|
|
_collateralTokenId,
|
|
_oracleId,
|
|
msg.sender,
|
|
_owingDonated
|
|
);
|
|
}
|
|
|
|
/// An ERC-1155 function.
|
|
///
|
|
/// We disallow transfers of conditional tokens after redeem `_to` prevent "gathering" them before redeeming
|
|
/// each oracle.
|
|
function safeTransferFrom(
|
|
address _from,
|
|
address _to,
|
|
uint256 _id,
|
|
uint256 _value,
|
|
bytes calldata _data
|
|
)
|
|
public override
|
|
{
|
|
_checkTransferAllowed(_id, _from);
|
|
_baseSafeTransferFrom(_from, _to, _id, _value, _data);
|
|
}
|
|
|
|
/// An ERC-1155 function.
|
|
///
|
|
/// We disallow transfers of conditional tokens after redeem `_to` prevent "gathering" them before redeeming
|
|
/// each oracle.
|
|
function safeBatchTransferFrom(
|
|
address _from,
|
|
address _to,
|
|
uint256[] calldata _ids,
|
|
uint256[] calldata _values,
|
|
bytes calldata _data
|
|
)
|
|
public override
|
|
{
|
|
for(uint _i = 0; _i < _ids.length; ++_i) {
|
|
_checkTransferAllowed(_ids[_i], _from);
|
|
}
|
|
_baseSafeBatchTransferFrom(_from, _to, _ids, _values, _data);
|
|
}
|
|
|
|
// Getters //
|
|
|
|
/// Get the oracle owner.
|
|
/// @param _oracleId The oracle ID.
|
|
function oracleOwner(uint64 _oracleId) public view returns (address) {
|
|
return oracleOwnersMap[_oracleId];
|
|
}
|
|
|
|
/// Is the oracle marked as having finished its work?
|
|
///
|
|
/// `oracleId` is the oracle ID.
|
|
function isOracleFinished(uint64 /*oracleId*/) public virtual view returns (bool) {
|
|
return true;
|
|
}
|
|
|
|
/// Are transfers of a conditinal token locked?
|
|
///
|
|
/// This is used to prevent its repeated withdrawal.
|
|
/// @param _user Querying if locked for this user.
|
|
/// @param _condition The condition (the original receiver of a conditional token).
|
|
function isConditionalLocked(address _user, uint256 _condition) public view returns (bool) {
|
|
return userUsedRedeemMap[_user][_condition];
|
|
}
|
|
|
|
/// Retrieve the end of the grace period.
|
|
/// @param _oracleId For which oracle.
|
|
function gracePeriodEnd(uint64 _oracleId) public view returns (uint) {
|
|
return gracePeriodEnds[_oracleId];
|
|
}
|
|
|
|
// Virtual functions //
|
|
|
|
/// Current address of a user.
|
|
/// @param _originalAddress The original address of the user.
|
|
function originalToCurrentAddress(address _originalAddress) internal virtual returns (address) {
|
|
return _originalAddress;
|
|
}
|
|
|
|
/// Mint a conditional to a customer.
|
|
function _mintToCustomer(address _customer, uint256 _condition, uint256 _amount, bytes calldata _data)
|
|
internal virtual
|
|
{
|
|
require(conditionOwners[_condition] == _customer, "Other's salary get attempt.");
|
|
_mint(originalToCurrentAddress(_customer), _condition, _amount, _data);
|
|
}
|
|
|
|
/// Calculate the share of a condition in an oracle's market.
|
|
/// @param _oracleId The oracle ID.
|
|
/// @return Uses `ABDKMath64x64` number ID.
|
|
function _calcRewardShare(uint64 _oracleId, uint256 _condition) internal virtual view returns (int128);
|
|
|
|
function _calcMultiplier(uint64 _oracleId, uint256 _condition, int128 _oracleShare) internal virtual view returns (int128) {
|
|
int128 _rewardShare = _calcRewardShare(_oracleId, _condition);
|
|
return _oracleShare.mul(_rewardShare);
|
|
}
|
|
|
|
function _doTransfer(uint256 _id, address _from, address _to, uint256 _value) internal virtual {
|
|
_balances[_id][_from] = _balances[_id][_from].sub(_value);
|
|
_balances[_id][_to] = _value.add(_balances[_id][_to]);
|
|
}
|
|
|
|
// Internal //
|
|
|
|
/// Generate the ERC-1155 token ID that counts amount of donations per oracle for a ERC-1155 collateral token.
|
|
/// @param _collateralContractAddress The ERC-1155 contract of the collateral token.
|
|
/// @param _collateralTokenId The ERC-1155 ID of the collateral token.
|
|
/// @param _oracleId The oracle ID.
|
|
/// Note: It does not conflict with other tokens kinds, because the only other one is the uint64 conditional.
|
|
function _collateralDonatedPerOracleTokenId(IERC1155 _collateralContractAddress, uint256 _collateralTokenId, uint64 _oracleId)
|
|
internal pure returns (uint256)
|
|
{
|
|
return uint256(keccak256(abi.encodePacked(_collateralContractAddress, _collateralTokenId, _oracleId)));
|
|
}
|
|
|
|
/// Generate the ERC-1155 token ID that counts amount of donations for a ERC-1155 collateral token.
|
|
/// @param _collateralContractAddress The ERC-1155 contract of the collateral token.
|
|
/// @param _collateralTokenId The ERC-1155 ID of the collateral token.
|
|
/// Note: It does not conflict with other tokens kinds, because the only other one is the uint64 conditional.
|
|
function _collateralDonatedTokenId(IERC1155 _collateralContractAddress, uint256 _collateralTokenId)
|
|
internal pure returns (uint256)
|
|
{
|
|
return uint256(keccak256(abi.encodePacked(_collateralContractAddress, _collateralTokenId)));
|
|
}
|
|
|
|
function _checkTransferAllowed(uint256 _id, address _from) internal view {
|
|
require(!userUsedRedeemMap[_from][_id], "You can't trade conditional tokens after redeem.");
|
|
}
|
|
|
|
function _baseSafeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes memory _data) private {
|
|
require(_to != address(0), "ERC1155: target address must be non-zero");
|
|
require(
|
|
_from == msg.sender || _operatorApprovals[_from][msg.sender] == true,
|
|
"ERC1155: need operator approval for 3rd party transfers."
|
|
);
|
|
|
|
_doTransfer(_id, _from, _to, _value);
|
|
|
|
emit TransferSingle(msg.sender, _from, _to, _id, _value);
|
|
|
|
_doSafeTransferAcceptanceCheck(msg.sender, _from, _to, _id, _value, _data);
|
|
}
|
|
|
|
function _baseSafeBatchTransferFrom(
|
|
address _from,
|
|
address _to,
|
|
uint256[] memory _ids,
|
|
uint256[] memory _values,
|
|
bytes memory _data
|
|
)
|
|
private
|
|
{
|
|
require(_ids.length == _values.length, "ERC1155: IDs and _values must have same lengths");
|
|
require(_to != address(0), "ERC1155: target address must be non-zero");
|
|
require(
|
|
_from == msg.sender || _operatorApprovals[_from][msg.sender] == true,
|
|
"ERC1155: need operator approval for 3rd party transfers."
|
|
);
|
|
|
|
for (uint256 _i = 0; _i < _ids.length; ++_i) {
|
|
uint256 _id = _ids[_i];
|
|
uint256 _value = _values[_i];
|
|
|
|
_doTransfer(_id, _from, _to, _value);
|
|
}
|
|
|
|
emit TransferBatch(msg.sender, _from, _to, _ids, _values);
|
|
|
|
_doSafeBatchTransferAcceptanceCheck(msg.sender, _from, _to, _ids, _values, _data);
|
|
}
|
|
|
|
function _createOracle() internal returns (uint64) {
|
|
uint64 _oracleId = ++maxOracleId;
|
|
oracleOwnersMap[_oracleId] = msg.sender;
|
|
emit OracleCreated(_oracleId);
|
|
emit OracleOwnerChanged(msg.sender, _oracleId);
|
|
return _oracleId;
|
|
}
|
|
|
|
/// Start with 1, not 0, to avoid glitch with `conditionalTokens` variable.
|
|
///
|
|
/// TODO: Use uint64 variables instead?
|
|
function _createCondition(address _customer) internal returns (uint256) {
|
|
return _doCreateCondition(_customer);
|
|
}
|
|
|
|
/// Start with 1, not 0, to avoid glitch with `conditionalTokens` variable.
|
|
///
|
|
/// TODO: Use uint64 variables instead?
|
|
function _doCreateCondition(address _customer) internal virtual returns (uint256) {
|
|
uint64 _condition = ++maxConditionId;
|
|
|
|
conditionOwners[_condition] = _customer;
|
|
|
|
emit ConditionCreated(msg.sender, _customer, _condition);
|
|
|
|
return _condition;
|
|
}
|
|
|
|
function _collateralOwingBase(
|
|
IERC1155 _collateralContractAddress,
|
|
uint256 _collateralTokenId,
|
|
uint64 _oracleId,
|
|
uint256 _condition,
|
|
address _user,
|
|
bool _inFirstRound
|
|
)
|
|
private view returns (uint _donatedPerOracleCollateralTokenId, uint256 _donated)
|
|
{
|
|
uint256 _conditionalBalance = balanceOf(_user, _condition);
|
|
uint256 _totalConditionalBalance =
|
|
_inFirstRound ? totalSupply(_condition) : usersWithdrewInFirstRound[_oracleId];
|
|
_donatedPerOracleCollateralTokenId = _collateralDonatedPerOracleTokenId(_collateralContractAddress, _collateralTokenId, _oracleId);
|
|
// Rounded to below for no out-of-funds:
|
|
int128 _oracleShare = ABDKMath64x64.divu(_conditionalBalance, _totalConditionalBalance);
|
|
uint256 _newDividendsDonated =
|
|
totalSupply(_donatedPerOracleCollateralTokenId) -
|
|
(_inFirstRound
|
|
? lastCollateralBalanceFirstRoundMap[_donatedPerOracleCollateralTokenId][_user]
|
|
: lastCollateralBalanceSecondRoundMap[_donatedPerOracleCollateralTokenId][_user]);
|
|
int128 _multiplier = _calcMultiplier(_oracleId, _condition, _oracleShare);
|
|
_donated = _multiplier.mulu(_newDividendsDonated);
|
|
}
|
|
|
|
function _isInFirstRound(uint64 _oracleId) internal view returns (bool) {
|
|
return block.timestamp <= gracePeriodEnds[_oracleId];
|
|
}
|
|
|
|
function _isConditional(uint256 _tokenId) internal pure returns (bool) {
|
|
// Zero 2**-192 probability that tokenId < (1<<64) if it's not a conditional.
|
|
// Note to auditor: It's a hack, check for no errors carefully.
|
|
return _tokenId < (1<<64);
|
|
}
|
|
|
|
modifier _isOracle(uint64 _oracleId) {
|
|
require(oracleOwnersMap[_oracleId] == msg.sender, "Not the oracle owner.");
|
|
_;
|
|
}
|
|
|
|
modifier checkIsConditional(uint256 _tokenId) {
|
|
require(_isConditional(_tokenId), "It's not your conditional.");
|
|
_;
|
|
}
|
|
}
|