Contract Name:
ConsumerSystem
Contract Source Code:
// SPDX-License-Identifier: MIT LICENSE
pragma solidity ^0.8.26;
import {ReentrancyGuardUpgradeable} from '@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol';
import {GAME_LOGIC_CONTRACT_ROLE, VRF_SYSTEM_ROLE} from '../../Constants.sol';
import {GameRegistryConsumerUpgradeable} from '../../GameRegistryConsumerUpgradeable.sol';
import {IVRFSystem} from '../vrf/IVRFSystem.sol';
import {IVRFSystemCallback} from '../vrf/IVRFSystemCallback.sol';
error InvalidCaller();
error InvalidAddress();
event ReceivedRandomNumber(uint256 requestId, uint256 randomNumber);
uint256 constant ID = uint256(keccak256('com.proofofplay.consumersystem.v1'));
/**
* Example contract that uses the VRF system to request random numbers
*/
contract ConsumerSystem is GameRegistryConsumerUpgradeable {
// request Id => random number
mapping(uint256 => uint256) public requestIdToRandomNumber;
/** SETUP **/
function initialize(address gameRegistryAddress) public initializer {
__GameRegistryConsumer_init(gameRegistryAddress, ID);
}
// this is an example contract with no role. anyone can call this
// @returns requestId
function getRandomNumber(uint256 traceId) external returns (uint256) {
return _requestRandomNumber(traceId);
}
// this is an example contract with no role. anyone can call this
function randomNumberCallback(uint256 requestId, uint256 randomNumber) external override {
// if (msg.sender != address(_vrf())) {
// revert InvalidCaller();
// }
requestIdToRandomNumber[requestId] = randomNumber;
emit ReceivedRandomNumber(requestId, randomNumber);
}
}
// SPDX-License-Identifier: MIT LICENSE
pragma solidity ^0.8.13;
import {ReentrancyGuardUpgradeable} from '@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol';
import {IERC2771Recipient} from '@opengsn/contracts/src/interfaces/IERC2771Recipient.sol';
import {PERCENTAGE_RANGE, TRUSTED_FORWARDER_ROLE, PAUSER_ROLE, MANAGER_ROLE} from './Constants.sol';
import {ISystem} from './core/ISystem.sol';
import {IVRFSystem, ID as VRF_SYSTEM_ID} from './systems/vrf/IVRFSystem.sol';
import {IVRFSystemCallback} from './systems/vrf/IVRFSystemCallback.sol';
import {IGameRegistry, IERC165} from './core/IGameRegistry.sol';
/** @title Contract that lets a child contract access the GameRegistry contract */
abstract contract GameRegistryConsumerUpgradeable is
ReentrancyGuardUpgradeable,
IERC2771Recipient,
IVRFSystemCallback,
ISystem
{
/// @notice Whether or not the contract is paused
bool private _paused;
/// @notice Reference to the game registry that this contract belongs to
IGameRegistry internal _gameRegistry;
/// @notice Id for the system/component
uint256 private _id;
/** EVENTS **/
/// @dev Emitted when the pause is triggered by `account`.
event Paused(address account);
/// @dev Emitted when the pause is lifted by `account`.
event Unpaused(address account);
/** ERRORS **/
/// @notice Not authorized to perform action
error MissingRole(address account, bytes32 expectedRole);
/** MODIFIERS **/
/// @notice Modifier to verify a user has the appropriate role to call a given function
modifier onlyRole(bytes32 role) {
_checkRole(role, _msgSender());
_;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/** ERRORS **/
/// @notice Error if the game registry specified is invalid
error InvalidGameRegistry(address gameRegistry);
/** SETUP **/
/**
* Initializer for this upgradeable contract
*
* @param gameRegistryAddress Address of the GameRegistry contract
* @param id Id of the system/component
*/
function __GameRegistryConsumer_init(address gameRegistryAddress, uint256 id) internal onlyInitializing {
__ReentrancyGuard_init();
_gameRegistry = IGameRegistry(gameRegistryAddress);
if (gameRegistryAddress == address(0)) {
revert InvalidGameRegistry(gameRegistryAddress);
}
_paused = true;
_id = id;
}
/** @return ID for this system */
function getId() public view override returns (uint256) {
return _id;
}
/**
* Pause/Unpause the contract
*
* @param shouldPause Whether or pause or unpause
*/
function setPaused(bool shouldPause) external onlyRole(PAUSER_ROLE) {
if (shouldPause) {
_pause();
} else {
_unpause();
}
}
/**
* @dev Returns true if the contract OR the GameRegistry is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused || _gameRegistry.paused();
}
/**
* Sets the GameRegistry contract address for this contract
*
* @param gameRegistryAddress Address for the GameRegistry contract
*/
function setGameRegistry(address gameRegistryAddress) external onlyRole(MANAGER_ROLE) {
_gameRegistry = IGameRegistry(gameRegistryAddress);
if (gameRegistryAddress == address(0)) {
revert InvalidGameRegistry(gameRegistryAddress);
}
}
/** @return GameRegistry contract for this contract */
function getGameRegistry() external view returns (IGameRegistry) {
return _gameRegistry;
}
/// @inheritdoc IERC2771Recipient
function isTrustedForwarder(address forwarder) public view virtual override(IERC2771Recipient) returns (bool) {
return address(_gameRegistry) != address(0) && _hasAccessRole(TRUSTED_FORWARDER_ROLE, forwarder);
}
/**
* Callback for when a random number request has returned with a random number
*
* @param requestId Id of the request
* @param randomNumber Random number
*/
function randomNumberCallback(uint256 requestId, uint256 randomNumber) external virtual override {
// Do nothing by default
}
/** INTERNAL **/
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function _hasAccessRole(bytes32 role, address account) internal view returns (bool) {
return _gameRegistry.hasAccessRole(role, account);
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!_gameRegistry.hasAccessRole(role, account)) {
revert MissingRole(account, role);
}
}
/** @return Interface to the VRF */
function _vrf() internal view returns (IVRFSystem) {
return IVRFSystem(_gameRegistry.getSystem(VRF_SYSTEM_ID));
}
/** @return Address for a given system */
function _getSystem(uint256 systemId) internal view returns (address) {
return _gameRegistry.getSystem(systemId);
}
/**
* Requests randomness from the game's VRF
*
* @param traceId A traceId to use that joins many x-chain requests together
*
* @return requestId of the randomness request
*/
function _requestRandomNumber(uint256 traceId) internal returns (uint256) {
return _vrf().requestRandomNumberWithTraceId(traceId);
}
/**
* Returns the Player address for the Operator account
* @param operatorAccount address of the Operator account to retrieve the player for
*/
function _getPlayerAccount(address operatorAccount) internal view returns (address playerAccount) {
return _gameRegistry.getPlayerAccount(operatorAccount);
}
/// @inheritdoc IERC2771Recipient
function _msgSender() internal view virtual override(IERC2771Recipient) returns (address ret) {
if (msg.data.length >= 20 && isTrustedForwarder(msg.sender)) {
assembly {
ret := shr(96, calldataload(sub(calldatasize(), 20)))
}
} else {
ret = msg.sender;
}
}
/// @inheritdoc IERC2771Recipient
function _msgData() internal view virtual override(IERC2771Recipient) returns (bytes calldata ret) {
if (msg.data.length >= 20 && isTrustedForwarder(msg.sender)) {
return msg.data[0:msg.data.length - 20];
} else {
return msg.data;
}
}
/** PAUSABLE **/
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), 'Pausable: paused');
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), 'Pausable: not paused');
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual {
require(_paused == false, 'Pausable: not paused');
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual {
require(_paused == true, 'Pausable: not paused');
_paused = false;
emit Unpaused(_msgSender());
}
uint256[47] private __gap;
}
// SPDX-License-Identifier: MIT LICENSE
pragma solidity ^0.8.9;
// Used for calculating decimal-point percentages (10000 = 100%)
uint256 constant PERCENTAGE_RANGE = 10000;
// Pauser Role - Can pause the game
bytes32 constant PAUSER_ROLE = keccak256('PAUSER_ROLE');
// Minter Role - Can mint items, NFTs, and ERC20 currency
bytes32 constant MINTER_ROLE = keccak256('MINTER_ROLE');
// Manager Role - Can manage the shop, loot tables, and other game data
bytes32 constant MANAGER_ROLE = keccak256('MANAGER_ROLE');
// Depoloyer Role - Can Deploy new Systems
bytes32 constant DEPLOYER_ROLE = keccak256('DEPLOYER_ROLE');
// Game Logic Contract - Contract that executes game logic and accesses other systems
bytes32 constant GAME_LOGIC_CONTRACT_ROLE = keccak256('GAME_LOGIC_CONTRACT_ROLE');
// Game Currency Contract - Allowlisted currency ERC20 contract
bytes32 constant GAME_CURRENCY_CONTRACT_ROLE = keccak256('GAME_CURRENCY_CONTRACT_ROLE');
// Game NFT Contract - Allowlisted game NFT ERC721 contract
bytes32 constant GAME_NFT_CONTRACT_ROLE = keccak256('GAME_NFT_CONTRACT_ROLE');
// Game Items Contract - Allowlist game items ERC1155 contract
bytes32 constant GAME_ITEMS_CONTRACT_ROLE = keccak256('GAME_ITEMS_CONTRACT_ROLE');
// Depositor role - used by Polygon bridge to mint on child chain
bytes32 constant DEPOSITOR_ROLE = keccak256('DEPOSITOR_ROLE');
// Randomizer role - Used by the randomizer contract to callback
bytes32 constant VRF_SYSTEM_ROLE = keccak256('VRF_SYSTEM_ROLE');
// VRF Consumer Contract - Contract that uses VRF to demonstrate randomness
bytes32 constant VRF_CONSUMER_ROLE = keccak256('VRF_CONSUMER_ROLE');
// Trusted forwarder role - Used by meta transactions to verify trusted forwader(s)
bytes32 constant TRUSTED_FORWARDER_ROLE = keccak256('TRUSTED_FORWARDER_ROLE');
// Trusted mirror role - Used by pirate mirroring
bytes32 constant TRUSTED_MIRROR_ROLE = keccak256('TRUSTED_MIRROR_ROLE');
// Trusted multichain oracle role - Used by multichain contracts
bytes32 constant TRUSTED_MULTICHAIN_ORACLE_ROLE = keccak256('TRUSTED_MULTICHAIN_ORACLE_ROLE');
// =====
// All of the possible traits in the system
// =====
/// @dev Trait that points to another token/template id
uint256 constant TEMPLATE_ID_TRAIT_ID = uint256(keccak256('template_id'));
// Generation of a token
uint256 constant GENERATION_TRAIT_ID = uint256(keccak256('generation'));
// XP for a token
uint256 constant XP_TRAIT_ID = uint256(keccak256('xp'));
// Current level of a token
uint256 constant LEVEL_TRAIT_ID = uint256(keccak256('level'));
// Whether or not a token is a pirate
uint256 constant IS_PIRATE_TRAIT_ID = uint256(keccak256('is_pirate'));
// Whether or not a token is a ship
uint256 constant IS_SHIP_TRAIT_ID = uint256(keccak256('is_ship'));
// Whether or not an item is equippable on ships
uint256 constant EQUIPMENT_TYPE_TRAIT_ID = uint256(keccak256('equipment_type'));
// Combat modifiers for items and tokens
uint256 constant COMBAT_MODIFIERS_TRAIT_ID = uint256(keccak256('combat_modifiers'));
// Animation URL for the token
uint256 constant ANIMATION_URL_TRAIT_ID = uint256(keccak256('animation_url'));
// Item slots
uint256 constant ITEM_SLOTS_TRAIT_ID = uint256(keccak256('item_slots'));
// Rank of the ship
uint256 constant SHIP_RANK_TRAIT_ID = uint256(keccak256('ship_rank'));
// Current Health trait
uint256 constant CURRENT_HEALTH_TRAIT_ID = uint256(keccak256('current_health'));
// Health trait
uint256 constant HEALTH_TRAIT_ID = uint256(keccak256('health'));
// Damage trait
uint256 constant DAMAGE_TRAIT_ID = uint256(keccak256('damage'));
// Speed trait
uint256 constant SPEED_TRAIT_ID = uint256(keccak256('speed'));
// Accuracy trait
uint256 constant ACCURACY_TRAIT_ID = uint256(keccak256('accuracy'));
// Evasion trait
uint256 constant EVASION_TRAIT_ID = uint256(keccak256('evasion'));
// Image hash of token's image, used for verifiable / fair drops
uint256 constant IMAGE_HASH_TRAIT_ID = uint256(keccak256('image_hash'));
// Name of a token
uint256 constant NAME_TRAIT_ID = uint256(keccak256('name_trait'));
// Description of a token
uint256 constant DESCRIPTION_TRAIT_ID = uint256(keccak256('description_trait'));
// General rarity for a token (corresponds to IGameRarity)
uint256 constant RARITY_TRAIT_ID = uint256(keccak256('rarity'));
// The character's affinity for a specific element
uint256 constant ELEMENTAL_AFFINITY_TRAIT_ID = uint256(keccak256('affinity_id'));
// The character's expertise value
uint256 constant EXPERTISE_TRAIT_ID = uint256(keccak256('expertise_id'));
// Expertise damage mod ID from SoT
uint256 constant EXPERTISE_DAMAGE_ID = uint256(keccak256('expertise.levelmultiplier.damage'));
// Expertise evasion mod ID from SoT
uint256 constant EXPERTISE_EVASION_ID = uint256(keccak256('expertise.levelmultiplier.evasion'));
// Expertise speed mod ID from SoT
uint256 constant EXPERTISE_SPEED_ID = uint256(keccak256('expertise.levelmultiplier.speed'));
// Expertise accuracy mod ID from SoT
uint256 constant EXPERTISE_ACCURACY_ID = uint256(keccak256('expertise.levelmultiplier.accuracy'));
// Expertise health mod ID from SoT
uint256 constant EXPERTISE_HEALTH_ID = uint256(keccak256('expertise.levelmultiplier.health'));
// Boss start time trait
uint256 constant BOSS_START_TIME_TRAIT_ID = uint256(keccak256('boss_start_time'));
// Boss end time trait
uint256 constant BOSS_END_TIME_TRAIT_ID = uint256(keccak256('boss_end_time'));
// Boss type trait
uint256 constant BOSS_TYPE_TRAIT_ID = uint256(keccak256('boss_type'));
// The character's dice rolls
uint256 constant DICE_ROLL_1_TRAIT_ID = uint256(keccak256('dice_roll_1'));
uint256 constant DICE_ROLL_2_TRAIT_ID = uint256(keccak256('dice_roll_2'));
// The character's star sign (astrology)
uint256 constant STAR_SIGN_TRAIT_ID = uint256(keccak256('star_sign'));
// Image for the token
uint256 constant IMAGE_TRAIT_ID = uint256(keccak256('image_trait'));
// How much energy the token provides if used
uint256 constant ENERGY_PROVIDED_TRAIT_ID = uint256(keccak256('energy_provided'));
// Whether a given token is soulbound, meaning it is unable to be transferred
uint256 constant SOULBOUND_TRAIT_ID = uint256(keccak256('soulbound'));
// ------
// Avatar Profile Picture related traits
// If an avatar is a 1 of 1, this is their only trait
uint256 constant PROFILE_IS_LEGENDARY_TRAIT_ID = uint256(keccak256('profile_is_legendary'));
// Avatar's archetype -- possible values: Human (including Druid, Mage, Berserker, Crusty), Robot, Animal, Zombie, Vampire, Ghost
uint256 constant PROFILE_CHARACTER_TYPE = uint256(keccak256('profile_character_type'));
// Avatar's profile picture's background image
uint256 constant PROFILE_BACKGROUND_TRAIT_ID = uint256(keccak256('profile_background'));
// Avatar's eye style
uint256 constant PROFILE_EYES_TRAIT_ID = uint256(keccak256('profile_eyes'));
// Avatar's facial hair type
uint256 constant PROFILE_FACIAL_HAIR_TRAIT_ID = uint256(keccak256('profile_facial_hair'));
// Avatar's hair style
uint256 constant PROFILE_HAIR_TRAIT_ID = uint256(keccak256('profile_hair'));
// Avatar's skin color
uint256 constant PROFILE_SKIN_TRAIT_ID = uint256(keccak256('profile_skin'));
// Avatar's coat color
uint256 constant PROFILE_COAT_TRAIT_ID = uint256(keccak256('profile_coat'));
// Avatar's earring(s) type
uint256 constant PROFILE_EARRING_TRAIT_ID = uint256(keccak256('profile_facial_hair'));
// Avatar's eye covering
uint256 constant PROFILE_EYE_COVERING_TRAIT_ID = uint256(keccak256('profile_eye_covering'));
// Avatar's headwear
uint256 constant PROFILE_HEADWEAR_TRAIT_ID = uint256(keccak256('profile_headwear'));
// Avatar's (Mages only) gem color
uint256 constant PROFILE_MAGE_GEM_TRAIT_ID = uint256(keccak256('profile_mage_gem'));
// ------
// Dungeon traits
// Whether this token template is a dungeon trigger
uint256 constant IS_DUNGEON_TRIGGER_TRAIT_ID = uint256(keccak256('is_dungeon_trigger'));
// Dungeon start time trait
uint256 constant DUNGEON_START_TIME_TRAIT_ID = uint256(keccak256('dungeon.start_time'));
// Dungeon end time trait
uint256 constant DUNGEON_END_TIME_TRAIT_ID = uint256(keccak256('dungeon.end_time'));
// Dungeon SoT map id trait
uint256 constant DUNGEON_MAP_TRAIT_ID = uint256(keccak256('dungeon.map_id'));
// Whether this token template is a mob
uint256 constant IS_MOB_TRAIT_ID = uint256(keccak256('is_mob'));
// ------
// Island traits
// Whether a game item is placeable on an island
uint256 constant IS_PLACEABLE_TRAIT_ID = uint256(keccak256('is_placeable'));
// ------
// Extra traits for component migration
// NOTE: CURRENTLY NOT USED IN CONTRACTS CODE
uint256 constant MODEL_GLTF_URL_TRAIT_ID = uint256(keccak256('model_gltf_url'));
uint256 constant PLACEABLE_CATEGORY_TRAIT_ID = uint256(keccak256('placeable_category'));
uint256 constant PLACEABLE_IS_BOTTOM_STACKABLE_TRAIT_ID = uint256(keccak256('placeable.is_bottom_stackable'));
uint256 constant PLACEABLE_IS_TOP_STACKABLE_TRAIT_ID = uint256(keccak256('placeable.is_top_stackable'));
uint256 constant PLACEABLE_TERRAIN_TRAIT_ID = uint256(keccak256('placeable.terrain'));
uint256 constant GLTF_SCALING_FACTOR_TRAIT_ID = uint256(keccak256('gltf_scaling_factor'));
uint256 constant SIZE_TRAIT_ID = uint256(keccak256('size'));
// SPDX-License-Identifier: MIT LICENSE
pragma solidity ^0.8.26;
interface IVRFSystemCallback {
/**
* Callback for when a Random Number is delivered
*
* @param requestId Id of the VRF request
* @param randomNumber Random number that was generated by the VRF
*/
function randomNumberCallback(uint256 requestId, uint256 randomNumber) external;
}
// SPDX-License-Identifier: MIT LICENSE
pragma solidity ^0.8.26;
uint256 constant ID = uint256(keccak256('com.proofofplay.vrfsystem.v1'));
interface IVRFSystem {
/**
* Starts a VRF random number request
*
* @param traceId Optional Id to use when tracing the request
* @return requestId for the random number, will be passed to the callback contract
*/
function requestRandomNumberWithTraceId(uint256 traceId) external returns (uint256);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
// SPDX-License-Identifier: MIT LICENSE
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/**
* Defines a system the game engine
*/
interface ISystem {
/** @return The ID for the system. Ex: a uint256 casted keccak256 hash */
function getId() external view returns (uint256);
}
// SPDX-License-Identifier: MIT LICENSE
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
// @title Interface the game's ACL / Management Layer
interface IGameRegistry is IERC165 {
/**
* @dev Returns `true` if `account` has been granted `role`.
* @param role The role to query
* @param account The address to query
*/
function hasAccessRole(
bytes32 role,
address account
) external view returns (bool);
/**
* @return Whether or not the registry is paused
*/
function paused() external view returns (bool);
/**
* Registers a system by id
*
* @param systemId Id of the system
* @param systemAddress Address of the system contract
*/
function registerSystem(uint256 systemId, address systemAddress) external;
/**
* @param systemId Id of the system
* @return System based on an id
*/
function getSystem(uint256 systemId) external view returns (address);
/**
* Registers a component using an id and contract address
* @param componentId Id of the component to register
* @param componentAddress Address of the component contract
*/
function registerComponent(
uint256 componentId,
address componentAddress
) external;
/**
* @param componentId Id of the component
* @return A component's contract address given its ID
*/
function getComponent(uint256 componentId) external view returns (address);
/**
* @param componentAddr Address of the component contract
* @return A component's id given its contract address
*/
function getComponentIdFromAddress(
address componentAddr
) external view returns (uint256);
/**
* @param entity Entity to check
* @param componentId Component to check
* @return Boolean indicating if entity belongs to component
*/
function getEntityHasComponent(
uint256 entity,
uint256 componentId
) external view returns (bool);
/**
* @return Boolean array indicating if entity belongs to component
* @param entities Entities to check
* @param componentIds Components to check
*/
function batchGetEntitiesHasComponents(
uint256[] calldata entities,
uint256[] calldata componentIds
) external view returns (bool[] memory);
/**
* Sets multiple component values at once
* @param entities Entities to set values for
* @param componentIds Component to set value on
* @param values Values to set
*/
function batchSetComponentValue(
uint256[] calldata entities,
uint256[] calldata componentIds,
bytes[] calldata values
) external;
/**
* Sets multiple component values at once and emits a publish event (for cross-chain)
* @param entities Entities to set values for
* @param componentIds Component to set value on
* @param values Values to set
*/
function batchPublishSetComponentValue(
uint256[] calldata entities,
uint256[] calldata componentIds,
bytes[] calldata values
) external returns (uint256 requestId);
/**
* @param componentId Id of the component
* @return Entire array of components belonging an entity
*
* 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 getEntityComponents(
uint256 componentId
) external view returns (uint256[] memory);
/**
* @param componentId Id of the component
* @return Number of components belonging to an entity
*/
function getEntityComponentCount(
uint256 componentId
) external view returns (uint256);
/**
* Gets multiple component values at once
* @param entities Entities to get values for
* @param componentIds Component to get value from
*/
function batchGetComponentValues(
uint256[] calldata entities,
uint256[] calldata componentIds
) external view returns (bytes[] memory values);
/**
* Register a component value update.
* Emits the `ComponentValueSet` event for clients to reconstruct the state.
* @param entity Entity to update
* @param data Data to update
*/
function registerComponentValueSet(
uint256 entity,
bytes calldata data
) external;
/**
* Emit a component value update across chains.
* Emits the `PublishComponentValueSet` event for cross-chain clients to reconstruct the state.
* @param entity Entity to update
* @param data Data to update
*/
function publishComponentValueSet(
uint256 componentId,
uint256 entity,
bytes calldata data
) external returns (uint256);
/**
* Register a component batch value update.
* Emits the `ComponentBatchValueSet` event for clients to reconstruct the state.
* @param entities Entities to update
* @param data Data to update
*/
function batchRegisterComponentValueSet(
uint256[] calldata entities,
bytes[] calldata data
) external;
/**
* Emit a component batch value update across chains.
* Emits the `PublishComponentBatchValueSet` event for cross-chain clients to reconstruct the state.
* @param entities Entities to update
* @param data Data to update
*/
function batchPublishComponentValueSet(
uint256 componentId,
uint256[] calldata entities,
bytes[] calldata data
) external returns (uint256);
/**
* Register a component value removal.
* Emits the `ComponentValueRemoved` event for clients to reconstruct the state.
*/
function registerComponentValueRemoved(uint256 entity) external;
/**
* Emit a component value removal across chains.
* Emits the `PublishComponentValueRemoved` event for cross-chain clients to reconstruct the state.
*/
// TODO: Reenable when we're ready to support cross-chain removal
// function publishComponentValueRemoved(
// uint256 componentId,
// uint256 entity
// ) external returns (uint256);
/**
* Register a component batch value removal.
* Emits the `ComponentBatchValueRemoved` event for clients to reconstruct the state.
* @param entities Entities to update
*/
function batchRegisterComponentValueRemoved(
uint256[] calldata entities
) external;
/**
* Emit a component batch value removal across chains.
* Emits the `PublishComponentBatchValueRemoved` event for cross-chain clients to reconstruct the state.
* @param entities Entities to update
*/
// TODO: Reenable when we're ready to support cross-chain removal
// function batchPublishComponentValueRemoved(
// uint256 componentId,
// uint256[] calldata entities
// ) external returns (uint256);
/**
* DEPRECATED: Generate a new general-purpose entity GUID
*/
function generateGUIDDeprecated() external returns (uint256);
/**
*
* @param operatorAddress Address of the Operator account
* @return Authorized Player account for an address
*/
function getPlayerAccount(
address operatorAddress
) external view returns (address);
/**
* @notice Sends a transfer to another chain in the multichain
* @param systemId Id of the 1155 System (Must implement IMultichain1155)
* @param from From address of the user sending the token
* @param to To address of the user receiving the token
* @param toChainId Chain ID of the receiving chain
* @param id Array of token IDs to send
* @param amount Array of token amounts to send
*/
function sendMultichain1155TransferSingle(
uint256 systemId,
address from,
address to,
uint256 toChainId,
uint256 id,
uint256 amount
) external;
/**
* @notice Sends a transfer to another chain in the multichain
* @param systemId Id of the 1155 System (Must implement IMultichain1155)
* @param from From address of the user sending the token
* @param to To address of the user receiving the token
* @param toChainId Chain ID of the receiving chain
* @param ids Array of token IDs to send
* @param amounts Array of token amounts to send
*/
function sendMultichain1155TransferBatch(
uint256 systemId,
address from,
address to,
uint256 toChainId,
uint256[] calldata ids,
uint256[] calldata amounts
) external;
/**
* @notice Sends a transfer to another chain in the multichain
* @param systemId Id of the 1155 System (Must implement Multichain721)
* @param from From address of the user sending the token
* @param to To address of the user receiving the token
* @param tokenId the tokenId being transferred
* @param toChainId Chain ID of the receiving chain
*/
function sendMultichain721Transfer(
uint256 systemId,
address from,
address to,
uint256 tokenId,
uint256 toChainId
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0;
/**
* @title The ERC-2771 Recipient Base Abstract Class - Declarations
*
* @notice A contract must implement this interface in order to support relayed transaction.
*
* @notice It is recommended that your contract inherits from the ERC2771Recipient contract.
*/
abstract contract IERC2771Recipient {
/**
* :warning: **Warning** :warning: The Forwarder can have a full control over your Recipient. Only trust verified Forwarder.
* @param forwarder The address of the Forwarder contract that is being used.
* @return isTrustedForwarder `true` if the Forwarder is trusted to forward relayed transactions by this Recipient.
*/
function isTrustedForwarder(address forwarder) public virtual view returns(bool);
/**
* @notice Use this method the contract anywhere instead of msg.sender to support relayed transactions.
* @return sender The real sender of this call.
* For a call that came through the Forwarder the real sender is extracted from the last 20 bytes of the `msg.data`.
* Otherwise simply returns `msg.sender`.
*/
function _msgSender() internal virtual view returns (address);
/**
* @notice Use this method in the contract instead of `msg.data` when difference matters (hashing, signature, etc.)
* @return data The real `msg.data` of this call.
* For a call that came through the Forwarder, the real sender address was appended as the last 20 bytes
* of the `msg.data` - so this method will strip those 20 bytes off.
* Otherwise (if the call was made directly and not through the forwarder) simply returns `msg.data`.
*/
function _msgData() internal virtual view returns (bytes calldata);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Internal function that returns the initialized version. Returns `_initialized`
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Internal function that returns the initialized version. Returns `_initializing`
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
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");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
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");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
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);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
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);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
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 {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}