Source Code
Overview
S Balance
S Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
RandomnessBeacon
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 1000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {SkillLibrary} from "./libraries/SkillLibrary.sol";
import {IOracleCB} from "./interfaces/IOracleCB.sol";
import {ISamWitchVRF} from "./interfaces/ISamWitchVRF.sol";
import {IWorldActions} from "./interfaces/IWorldActions.sol";
// solhint-disable-next-line no-global-import
import "./globals/all.sol";
contract RandomnessBeacon is UUPSUpgradeable, OwnableUpgradeable {
using SkillLibrary for uint8;
using SkillLibrary for Skill;
event RequestSent(uint256 requestId, uint256 numWords, uint256 lastRandomWordsUpdatedTime);
event RequestFulfilled(uint256 requestId, uint256 randomWord);
error RandomWordsCannotBeUpdatedYet();
error CanOnlyRequestAfterTheNextCheckpoint(uint256 currentTime, uint256 checkpoint);
error RequestAlreadyFulfilled();
error NoValidRandomWord();
error LengthMismatch();
error CallbackGasLimitTooHigh();
error CallerNotSamWitchVRF();
error RandomWordsAlreadyInitialized();
uint256 private constant NUM_WORDS = 1;
uint256 public constant MIN_RANDOM_WORDS_UPDATE_TIME = 1 days;
uint256 public constant NUM_DAYS_RANDOM_WORDS_INITIALIZED = 3;
uint40 private _lastRandomWordsUpdatedTime;
uint40 private _startTime;
uint24 private _expectedGasLimitFulfill;
ISamWitchVRF private _samWitchVRF;
IOracleCB private _wishingWell;
IOracleCB private _dailyRewardsScheduler;
uint256 private _isRandomWordsInitialized; // Doesn't need to be packed with anything, only called on initialization
uint256[] private _requestIds; // Each one is a set of random words for 1 day
mapping(uint256 requestId => uint256 randomWord) private _randomWords;
/// @dev Reverts if the caller is not the SamWitchVRF contract.
modifier onlySamWitchVRF() {
require(_msgSender() == address(_samWitchVRF), CallerNotSamWitchVRF());
_;
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(address vrf) external initializer {
__Ownable_init(_msgSender());
__UUPSUpgradeable_init();
uint40 startTime = uint40(
(block.timestamp / MIN_RANDOM_WORDS_UPDATE_TIME) *
MIN_RANDOM_WORDS_UPDATE_TIME -
(NUM_DAYS_RANDOM_WORDS_INITIALIZED + 1) *
1 days
);
_startTime = startTime; // Floor to the nearest day 00:00 UTC
_lastRandomWordsUpdatedTime = uint40(startTime + NUM_DAYS_RANDOM_WORDS_INITIALIZED * 1 days);
_expectedGasLimitFulfill = 600_000;
_samWitchVRF = ISamWitchVRF(vrf);
}
function requestIds(uint256 requestId) external view returns (uint256) {
return _requestIds[requestId];
}
function randomWords(uint256 requestId) external view returns (uint256) {
return _randomWords[requestId];
}
function lastRandomWordsUpdatedTime() external view returns (uint256) {
return _lastRandomWordsUpdatedTime;
}
function requestRandomWords() external returns (uint256 requestId) {
// Last one has not been fulfilled yet
require(
_requestIds.length == 0 || _randomWords[_requestIds[_requestIds.length - 1]] != 0,
RandomWordsCannotBeUpdatedYet()
);
uint40 newLastRandomWordsUpdatedTime = uint40(_lastRandomWordsUpdatedTime + MIN_RANDOM_WORDS_UPDATE_TIME);
require(
newLastRandomWordsUpdatedTime <= block.timestamp,
CanOnlyRequestAfterTheNextCheckpoint(block.timestamp, newLastRandomWordsUpdatedTime)
);
requestId = uint256(_samWitchVRF.requestRandomWords(NUM_WORDS, _expectedGasLimitFulfill));
_requestIds.push(requestId);
_lastRandomWordsUpdatedTime = newLastRandomWordsUpdatedTime;
emit RequestSent(requestId, NUM_WORDS, newLastRandomWordsUpdatedTime);
return requestId;
}
function fulfillRandomWords(bytes32 requestId, uint256[] memory words) external onlySamWitchVRF {
_fulfillRandomWords(uint256(requestId), words);
}
function _getRandomWordOffset(uint256 timestamp) private view returns (int) {
if (timestamp < _startTime) {
return -1;
}
return int((timestamp - _startTime) / MIN_RANDOM_WORDS_UPDATE_TIME);
}
function _getRandomWord(uint256 timestamp) private view returns (uint256) {
int _offset = _getRandomWordOffset(timestamp);
if (_offset < 0 || _requestIds.length <= uint256(_offset)) {
return 0;
}
return _randomWords[_requestIds[uint256(_offset)]];
}
function _getRandomComponent(
bytes32 word,
uint256 startTimestamp,
uint256 endTimestamp,
uint256 id
) private pure returns (bytes32) {
return keccak256(abi.encodePacked(word, startTimestamp, endTimestamp, id));
}
function _fulfillRandomWords(uint256 requestId, uint256[] memory fulfilledRandomWords) internal {
require(_randomWords[requestId] == 0, RequestAlreadyFulfilled());
require(fulfilledRandomWords.length == NUM_WORDS, LengthMismatch());
uint256 randomWord = fulfilledRandomWords[0];
_randomWords[requestId] = randomWord;
_wishingWell.newOracleRandomWords(randomWord);
_dailyRewardsScheduler.newOracleRandomWords(randomWord);
emit RequestFulfilled(requestId, randomWord);
}
function initializeAddresses(IOracleCB wishingWell, IOracleCB dailyRewardsScheduler) external onlyOwner {
_wishingWell = IOracleCB(wishingWell);
_dailyRewardsScheduler = IOracleCB(dailyRewardsScheduler);
}
function hasRandomWord(uint256 timestamp) external view returns (bool) {
return _getRandomWord(timestamp) != 0;
}
function getRandomWord(uint256 timestamp) public view returns (uint256 randomWord) {
randomWord = _getRandomWord(timestamp);
require(randomWord != 0, NoValidRandomWord());
}
function getMultipleWords(uint256 timestamp) public view returns (uint256[4] memory words) {
for (uint256 i; i < 4; ++i) {
words[i] = getRandomWord(timestamp - (i * 1 days));
}
}
function getRandomBytes(
uint256 numTickets,
uint256 startTimestamp,
uint256 endTimestamp,
uint256 id // player or pet id for instance
) external view returns (bytes memory randomBytes) {
if (numTickets <= 16) {
// 32 bytes
bytes32 word = bytes32(getRandomWord(endTimestamp));
randomBytes = abi.encodePacked(_getRandomComponent(word, startTimestamp, endTimestamp, id));
} else if (numTickets <= MAX_UNIQUE_TICKETS) {
// 4 * 32 bytes
uint256[4] memory multipleWords = getMultipleWords(endTimestamp);
for (uint256 i; i < 4; ++i) {
multipleWords[i] = uint256(_getRandomComponent(bytes32(multipleWords[i]), startTimestamp, endTimestamp, id));
// XOR all the words with the first fresh random number to give more randomness to the existing random words
if (i != 0) {
multipleWords[i] = uint256(keccak256(abi.encodePacked(multipleWords[i] ^ multipleWords[0])));
}
}
randomBytes = abi.encodePacked(multipleWords);
} else {
assert(false);
}
}
function getStartTime() external view returns (uint256) {
return _startTime;
}
function initializeRandomWords() external onlyOwner {
// Initialize a few days worth of random words so that we have enough data to fetch the first day
require(_isRandomWordsInitialized == 0, RandomWordsAlreadyInitialized());
_isRandomWordsInitialized = 1;
for (uint256 i; i < NUM_DAYS_RANDOM_WORDS_INITIALIZED; ++i) {
uint256 requestId = 200 + i;
_requestIds.push(requestId);
emit RequestSent(requestId, NUM_WORDS, _startTime + (i * 1 days) + 1 days);
uint256[] memory words = new uint256[](1);
words[0] = uint256(keccak256(abi.encodePacked(block.chainid == 31337 ? address(31337) : address(this), i)));
_fulfillRandomWords(requestId, words);
}
}
function setExpectedGasLimitFulfill(uint256 gasLimit) external onlyOwner {
require(gasLimit <= 3_000_000, CallbackGasLimitTooHigh());
_expectedGasLimitFulfill = uint24(gasLimit);
}
// solhint-disable-next-line no-empty-blocks
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly ("memory-safe") {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @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]
* ```solidity
* 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 Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 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 in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._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 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._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() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @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 {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly ("memory-safe") {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.20;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC-1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
*/
interface IERC1967 {
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.21;
import {IBeacon} from "../beacon/IBeacon.sol";
import {IERC1967} from "../../interfaces/IERC1967.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This library provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.
*/
library ERC1967Utils {
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the ERC-1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit IERC1967.Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the ERC-1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit IERC1967.AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the ERC-1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit IERC1967.BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert Errors.FailedCall();
}
}
/**
* @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 or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* 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.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @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`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) 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
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC-1967 implementation slot:
* ```solidity
* contract ERC1967 {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct Int256Slot {
int256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Int256Slot` with member `value` located at `slot`.
*/
function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
/**
* @dev Returns a `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {Skill, Attire, CombatStyle, CombatStats} from "./misc.sol";
import {GuaranteedReward, RandomReward} from "./rewards.sol";
enum ActionQueueStrategy {
OVERWRITE,
APPEND,
KEEP_LAST_IN_PROGRESS
}
struct QueuedActionInput {
Attire attire;
uint16 actionId;
uint16 regenerateId; // Food (combat), maybe something for non-combat later
uint16 choiceId; // Melee/Ranged/Magic (combat), logs, ore (non-combat)
uint16 rightHandEquipmentTokenId; // Axe/Sword/bow, can be empty
uint16 leftHandEquipmentTokenId; // Shield, can be empty
uint24 timespan; // How long to queue the action for
uint8 combatStyle; // CombatStyle specific style of combat
uint40 petId; // id of the pet (can be empty)
}
struct QueuedAction {
uint16 actionId;
uint16 regenerateId; // Food (combat), maybe something for non-combat later
uint16 choiceId; // Melee/Ranged/Magic (combat), logs, ore (non-combat)
uint16 rightHandEquipmentTokenId; // Axe/Sword/bow, can be empty
uint16 leftHandEquipmentTokenId; // Shield, can be empty
uint24 timespan; // How long to queue the action for
uint24 prevProcessedTime; // How long the action has been processed for previously
uint24 prevProcessedXPTime; // How much XP has been gained for this action so far
uint64 queueId; // id of this queued action
bytes1 packed; // 1st bit is isValid (not used yet), 2nd bit is for hasPet (decides if the 2nd storage slot is read)
uint8 combatStyle;
uint24 reserved;
// Next storage slot
uint40 petId; // id of the pet (can be empty)
}
// This is only used as an input arg (and events)
struct ActionInput {
uint16 actionId;
ActionInfo info;
GuaranteedReward[] guaranteedRewards;
RandomReward[] randomRewards;
CombatStats combatStats;
}
struct ActionInfo {
uint8 skill;
bool actionChoiceRequired; // If true, then the user must choose an action choice
uint24 xpPerHour;
uint32 minXP;
uint24 numSpawned; // Mostly for combat, capped respawn rate for xp/drops. Per hour, base 10000
uint16 handItemTokenIdRangeMin; // Inclusive
uint16 handItemTokenIdRangeMax; // Inclusive
uint8 successPercent; // 0-100
uint8 worldLocation; // 0 is the main starting world
bool isFullModeOnly;
bool isAvailable;
uint16 questPrerequisiteId;
}
uint16 constant ACTIONCHOICE_MELEE_BASIC_SWORD = 1500;
uint16 constant ACTIONCHOICE_MAGIC_SHADOW_BLAST = 2000;
uint16 constant ACTIONCHOICE_RANGED_BASIC_BOW = 3000;
// Allows for 2, 4 or 8 hour respawn time
uint256 constant SPAWN_MUL = 1000;
uint256 constant RATE_MUL = 1000;
uint256 constant GUAR_MUL = 10; // Guaranteeded reward multiplier (1 decimal, allows for 2 hour action times)
uint256 constant MAX_QUEUEABLE_ACTIONS = 3; // Available slots to queue actions// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import "./actions.sol"; import "./items.sol"; import "./misc.sol"; import "./players.sol"; import "./rewards.sol"; import "./quests.sol"; import "./promotions.sol"; import "./clans.sol"; import "./pets.sol";
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {IBank} from "../interfaces/IBank.sol";
enum ClanRank {
NONE, // Not in a clan
COMMONER, // Member of the clan
SCOUT, // Invite and kick commoners
COLONEL, // Can launch attacks and assign combatants
TREASURER, // Can withdraw from bank
LEADER, // Can edit clan details
OWNER // Can do everything and transfer ownership
}
enum BattleResultEnum {
DRAW,
WIN,
LOSE
}
struct ClanBattleInfo {
uint40 lastClanIdAttackOtherClanIdCooldownTimestamp;
uint8 numReattacks;
uint40 lastOtherClanIdAttackClanIdCooldownTimestamp;
uint8 numReattacksOtherClan;
}
// Packed for gas efficiency
struct Vault {
bool claimed; // Only applies to the first one, if it's claimed without the second one being claimed
uint40 timestamp;
uint80 amount;
uint40 timestamp1;
uint80 amount1;
}
struct VaultClanInfo {
IBank bank;
uint96 totalBrushLocked;
// New storage slot
uint40 attackingCooldownTimestamp;
uint40 assignCombatantsCooldownTimestamp;
bool currentlyAttacking;
uint24 defendingVaultsOffset;
uint40 blockAttacksTimestamp;
uint8 blockAttacksCooldownHours;
bool isInMMRArray;
uint40 superAttackCooldownTimestamp;
uint64[] playerIds;
Vault[] defendingVaults; // Append only, and use defendingVaultsOffset to decide where the real start is
}
uint256 constant MAX_CLAN_COMBATANTS = 20;
uint256 constant CLAN_WARS_GAS_PRICE_WINDOW_SIZE = 4;
bool constant XP_EMITTED_ELSEWHERE = true;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
uint16 constant NONE = 0;
uint16 constant COMBAT_BASE = 2048;
// Melee
uint16 constant SWORD_BASE = COMBAT_BASE;
uint16 constant BRONZE_SWORD = SWORD_BASE;
// Woodcutting (2816 - 3071)
uint16 constant WOODCUTTING_BASE = 2816;
uint16 constant BRONZE_AXE = WOODCUTTING_BASE;
// Firemaking (3328 - 3583)
uint16 constant FIRE_BASE = 3328;
uint16 constant MAGIC_FIRE_STARTER = FIRE_BASE;
uint16 constant FIRE_MAX = FIRE_BASE + 255;
// Fishing (3072 - 3327)
uint16 constant FISHING_BASE = 3072;
uint16 constant NET_STICK = FISHING_BASE;
// Mining (2560 - 2815)
uint16 constant MINING_BASE = 2560;
uint16 constant BRONZE_PICKAXE = MINING_BASE;
// Magic
uint16 constant STAFF_BASE = COMBAT_BASE + 50;
uint16 constant TOTEM_STAFF = STAFF_BASE;
// Ranged
uint16 constant BOW_BASE = COMBAT_BASE + 100;
uint16 constant BASIC_BOW = BOW_BASE;
// Cooked fish
uint16 constant COOKED_FISH_BASE = 11008;
uint16 constant COOKED_FEOLA = COOKED_FISH_BASE + 3;
// Scrolls
uint16 constant SCROLL_BASE = 12032;
uint16 constant SHADOW_SCROLL = SCROLL_BASE;
// Boosts
uint16 constant BOOST_BASE = 12800;
uint16 constant COMBAT_BOOST = BOOST_BASE;
uint16 constant XP_BOOST = BOOST_BASE + 1;
uint16 constant GATHERING_BOOST = BOOST_BASE + 2;
uint16 constant SKILL_BOOST = BOOST_BASE + 3;
uint16 constant ABSENCE_BOOST = BOOST_BASE + 4;
uint16 constant LUCKY_POTION = BOOST_BASE + 5;
uint16 constant LUCK_OF_THE_DRAW = BOOST_BASE + 6;
uint16 constant PRAY_TO_THE_BEARDIE = BOOST_BASE + 7;
uint16 constant PRAY_TO_THE_BEARDIE_2 = BOOST_BASE + 8;
uint16 constant PRAY_TO_THE_BEARDIE_3 = BOOST_BASE + 9;
uint16 constant BOOST_RESERVED_1 = BOOST_BASE + 10;
uint16 constant BOOST_RESERVED_2 = BOOST_BASE + 11;
uint16 constant BOOST_RESERVED_3 = BOOST_BASE + 12;
uint16 constant GO_OUTSIDE = BOOST_BASE + 13;
uint16 constant RAINING_RARES = BOOST_BASE + 14;
uint16 constant CLAN_BOOSTER = BOOST_BASE + 15;
uint16 constant CLAN_BOOSTER_2 = BOOST_BASE + 16;
uint16 constant CLAN_BOOSTER_3 = BOOST_BASE + 17;
uint16 constant BOOST_RESERVED_4 = BOOST_BASE + 18;
uint16 constant BOOST_RESERVED_5 = BOOST_BASE + 19;
uint16 constant BOOST_RESERVED_6 = BOOST_BASE + 20;
uint16 constant BOOST_MAX = 13055;
// Eggs
uint16 constant EGG_BASE = 12544;
uint16 constant SECRET_EGG_1_TIER1 = EGG_BASE;
uint16 constant SECRET_EGG_2_TIER1 = EGG_BASE + 1;
uint16 constant EGG_MAX = 12799;
// Miscs
uint16 constant MISC_BASE = 65535;
uint16 constant RAID_PASS = MISC_BASE - 1;
struct BulkTransferInfo {
uint256[] tokenIds;
uint256[] amounts;
address to;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
enum BoostType {
NONE,
ANY_XP,
COMBAT_XP,
NON_COMBAT_XP,
GATHERING,
ABSENCE,
PASSIVE_SKIP_CHANCE,
// Clan wars
PVP_BLOCK,
PVP_REATTACK,
PVP_SUPER_ATTACK,
// Combat stats
COMBAT_FIXED
}
struct Equipment {
uint16 itemTokenId;
uint24 amount;
}
enum Skill {
NONE,
COMBAT, // This is a helper which incorporates all combat skills, attack <-> magic, defence, health etc
MELEE,
RANGED,
MAGIC,
DEFENCE,
HEALTH,
RESERVED_COMBAT,
MINING,
WOODCUTTING,
FISHING,
SMITHING,
THIEVING,
CRAFTING,
COOKING,
FIREMAKING,
FARMING,
ALCHEMY,
FLETCHING,
FORGING,
RESERVED2,
RESERVED3,
RESERVED4,
RESERVED5,
RESERVED6,
RESERVED7,
RESERVED8,
RESERVED9,
RESERVED10,
RESERVED11,
RESERVED12,
RESERVED13,
RESERVED14,
RESERVED15,
RESERVED16,
RESERVED17,
RESERVED18,
RESERVED19,
RESERVED20,
TRAVELING // Helper Skill for travelling
}
struct Attire {
uint16 head;
uint16 neck;
uint16 body;
uint16 arms;
uint16 legs;
uint16 feet;
uint16 ring;
uint16 reserved1;
}
struct CombatStats {
// From skill points
int16 meleeAttack;
int16 magicAttack;
int16 rangedAttack;
int16 health;
// These include equipment
int16 meleeDefence;
int16 magicDefence;
int16 rangedDefence;
}
enum CombatStyle {
NONE,
ATTACK,
DEFENCE
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {Skill} from "./misc.sol";
enum PetSkin {
NONE,
DEFAULT,
OG,
ONEKIN,
FROST,
CRYSTAL,
ANNIV1,
KRAGSTYR
}
enum PetEnhancementType {
NONE,
MELEE,
MAGIC,
RANGED,
DEFENCE,
HEALTH,
MELEE_AND_DEFENCE,
MAGIC_AND_DEFENCE,
RANGED_AND_DEFENCE
}
struct Pet {
Skill skillEnhancement1;
uint8 skillFixedEnhancement1;
uint8 skillPercentageEnhancement1;
Skill skillEnhancement2;
uint8 skillFixedEnhancement2;
uint8 skillPercentageEnhancement2;
uint40 lastAssignmentTimestamp;
address owner; // Will be used as an optimization to avoid having to look up the owner of the pet in another storage slot
bool isTransferable;
// New storage slot
uint24 baseId;
// These are used when training a pet
uint40 lastTrainedTimestamp;
uint8 skillFixedEnhancementMax1; // The maximum possible value for skillFixedEnhancement1 when training
uint8 skillFixedEnhancementMax2;
uint8 skillPercentageEnhancementMax1;
uint8 skillPercentageEnhancementMax2;
uint64 xp;
}
struct BasePetMetadata {
string description;
uint8 tier;
PetSkin skin;
PetEnhancementType enhancementType;
Skill skillEnhancement1;
uint8 skillFixedMin1;
uint8 skillFixedMax1;
uint8 skillFixedIncrement1;
uint8 skillPercentageMin1;
uint8 skillPercentageMax1;
uint8 skillPercentageIncrement1;
uint8 skillMinLevel1;
Skill skillEnhancement2;
uint8 skillFixedMin2;
uint8 skillFixedMax2;
uint8 skillFixedIncrement2;
uint8 skillPercentageMin2;
uint8 skillPercentageMax2;
uint8 skillPercentageIncrement2;
uint8 skillMinLevel2;
uint16 fixedStarThreshold;
uint16 percentageStarThreshold;
bool isTransferable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {QueuedAction} from "./actions.sol";
import {Skill, BoostType, CombatStats, Equipment} from "./misc.sol";
import {PlayerQuest} from "./quests.sol";
// 4 bytes for each level. 0x00000000 is the first level, 0x00000054 is the second, etc.
bytes constant XP_BYTES = hex"0000000000000054000000AE0000010E00000176000001E60000025E000002DE00000368000003FD0000049B00000546000005FC000006C000000792000008730000096400000A6600000B7B00000CA400000DE100000F36000010A200001229000013CB0000158B0000176B0000196E00001B9400001DE20000205A000022FF000025D5000028DD00002C1E00002F99000033540000375200003B9A000040300000451900004A5C00004FFF0000560900005C810000637000006ADD000072D100007B570000847900008E42000098BE0000A3F90000B0020000BCE70000CAB80000D9860000E9630000FA6200010C990001201D0001350600014B6F0001637300017D2E000198C10001B64E0001D5F80001F7E600021C430002433B00026CFD000299BE0002C9B30002FD180003342B00036F320003AE730003F23D00043AE3000488BE0004DC2F0005359B000595700005FC2400066A360006E02D00075E990007E6160008774C000912EB0009B9B4000A6C74000B2C06000BF956000CD561000DC134000EBDF3000FCCD40010EF2400122648001373BF0014D9230016582C0017F2B00019AAA9001B8234001D7B95001F99390021DDBC00244BE60026E6B60029B15F002CAF51002FE43A0033540D00370303003AF5A4003F30CC0043B9B0004895E3004DCB600053609100595C53005FC6030066A585006E034D0075E86C007E5E980087703B0091287D009B935300A6BD8F00B2B4EE00BF882800CD470500DC026F00EBCC8500FCB8B7010EDBD5";
uint256 constant MAX_LEVEL = 140; // Original max level
uint256 constant MAX_LEVEL_1 = 160; // TODO: Update later
uint256 constant MAX_LEVEL_2 = 190; // TODO: Update later
enum EquipPosition {
NONE,
HEAD,
NECK,
BODY,
ARMS,
LEGS,
FEET,
RING,
SPARE2,
LEFT_HAND,
RIGHT_HAND,
BOTH_HANDS,
QUIVER,
MAGIC_BAG,
FOOD,
AUX, // wood, seeds etc..
BOOST_VIAL,
EXTRA_BOOST_VIAL,
GLOBAL_BOOST_VIAL,
CLAN_BOOST_VIAL,
PASSIVE_BOOST_VIAL,
LOCKED_VAULT,
TERRITORY
}
struct Player {
uint40 currentActionStartTimestamp; // The in-progress start time of the first queued action
Skill currentActionProcessedSkill1; // The skill that the queued action has already gained XP in
uint24 currentActionProcessedXPGained1; // The amount of XP that the queued action has already gained
Skill currentActionProcessedSkill2;
uint24 currentActionProcessedXPGained2;
Skill currentActionProcessedSkill3;
uint24 currentActionProcessedXPGained3;
uint16 currentActionProcessedFoodConsumed;
uint16 currentActionProcessedBaseInputItemsConsumedNum; // e.g scrolls, crafting materials etc
Skill skillBoosted1; // The first skill that is boosted
Skill skillBoosted2; // The second skill that is boosted (if applicable)
uint48 totalXP;
uint16 totalLevel; // Doesn't not automatically add new skills to it
bytes1 packedData; // Contains worldLocation in first 6 bits (0 is the main starting randomnessBeacon), and full mode unlocked in the upper most bit
// TODO: Can be up to 7
QueuedAction[] actionQueue;
string name; // Raw name
}
struct Item {
EquipPosition equipPosition;
bytes1 packedData; // 0x1 exists, upper most bit is full mode
uint16 questPrerequisiteId;
// Can it be transferred?
bool isTransferable; // TODO: Move into packedData
// Food
uint16 healthRestored;
// Boost vial
BoostType boostType;
uint16 boostValue; // Varies, could be the % increase
uint24 boostDuration; // How long the effect of the boost last
// Combat stats
int16 meleeAttack;
int16 magicAttack;
int16 rangedAttack;
int16 meleeDefence;
int16 magicDefence;
int16 rangedDefence;
int16 health;
// Minimum requirements in this skill to use this item (can be NONE)
Skill skill;
uint32 minXP;
}
// Used for events
struct BoostInfo {
uint40 startTime;
uint24 duration;
uint16 value;
uint16 itemTokenId; // Get the effect of it
BoostType boostType;
}
struct PlayerBoostInfo {
uint40 startTime;
uint24 duration;
uint16 value;
uint16 itemTokenId; // Get the effect of it
BoostType boostType;
// Another boost slot (for global/clan boosts this is the "last", for users it is the "extra")
uint40 extraOrLastStartTime;
uint24 extraOrLastDuration;
uint16 extraOrLastValue;
uint16 extraOrLastItemTokenId;
BoostType extraOrLastBoostType;
uint40 cooldown; // Just put here for packing
}
// This is effectively a ratio to produce 1 of outputTokenId.
// Available choices that can be undertaken for an action
struct ActionChoiceInput {
uint8 skill; // Skill that this action choice is related to
uint24 rate; // Rate of output produced per hour (base 1000) 3 decimals
uint24 xpPerHour;
uint16[] inputTokenIds;
uint24[] inputAmounts;
uint16 outputTokenId;
uint8 outputAmount;
uint8 successPercent; // 0-100
uint16 handItemTokenIdRangeMin; // Inclusive
uint16 handItemTokenIdRangeMax; // Inclusive
bool isFullModeOnly;
bool isAvailable;
uint16 questPrerequisiteId;
uint8[] skills; // Skills required to do this action choice
uint32[] skillMinXPs; // Min XP in the corresponding skills to be able to do this action choice
int16[] skillDiffs; // How much the skill is increased/decreased by this action choice
}
struct ActionChoice {
uint8 skill; // Skill that this action choice is related to
uint24 rate; // Rate of output produced per hour (base 1000) 3 decimals
uint24 xpPerHour;
uint16 inputTokenId1;
uint24 inputAmount1;
uint16 inputTokenId2;
uint24 inputAmount2;
uint16 inputTokenId3;
uint24 inputAmount3;
uint16 outputTokenId;
uint8 outputAmount;
uint8 successPercent; // 0-100
uint8 skill1; // Skills required to do this action choice, commonly the same as skill
uint32 skillMinXP1; // Min XP in the skill to be able to do this action choice
int16 skillDiff1; // How much the skill is increased/decreased by this action choice
uint8 skill2;
uint32 skillMinXP2;
int16 skillDiff2;
uint8 skill3;
uint32 skillMinXP3;
int16 skillDiff3;
uint16 handItemTokenIdRangeMin; // Inclusive
uint16 handItemTokenIdRangeMax; // Inclusive
uint16 questPrerequisiteId;
// FullMode is last bit, first 6 bits is worldLocation,
// 2nd last bit is if there are other skills in next storage slot to check,
// 3rd last bit if the input amounts should be used
bytes1 packedData;
}
// Must be in the same order as Skill enum
struct PackedXP {
uint40 melee;
uint40 ranged;
uint40 magic;
uint40 defence;
uint40 health;
uint40 reservedCombat;
bytes2 packedDataIsMaxed; // 2 bits per skill to indicate whether the maxed skill is reached. I think this was added in case we added a new max level which a user had already passed so old & new levels are the same and it would not trigger a level up event.
// Next slot
uint40 mining;
uint40 woodcutting;
uint40 fishing;
uint40 smithing;
uint40 thieving;
uint40 crafting;
bytes2 packedDataIsMaxed1; // 2 bits per skill to indicate whether the maxed skill is reached
// Next slot
uint40 cooking;
uint40 firemaking;
uint40 farming;
uint40 alchemy;
uint40 fletching;
uint40 forging;
bytes2 packedDataIsMaxed2; // 2 bits per skill to indicate whether the maxed skill is reached
}
struct AvatarInfo {
string name;
string description;
string imageURI;
Skill[2] startSkills; // Can be NONE
}
struct PastRandomRewardInfo {
uint16 itemTokenId;
uint24 amount;
uint64 queueId;
}
struct PendingQueuedActionEquipmentState {
uint256[] consumedItemTokenIds;
uint256[] consumedAmounts;
uint256[] producedItemTokenIds;
uint256[] producedAmounts;
}
struct PendingQueuedActionMetadata {
uint32 xpGained; // total xp gained
uint32 rolls;
bool died;
uint16 actionId;
uint64 queueId;
uint24 elapsedTime;
uint24 xpElapsedTime;
uint8 checkpoint;
}
struct PendingQueuedActionData {
// The amount of XP that the queued action has already gained
Skill skill1;
uint24 xpGained1;
Skill skill2; // Most likely health
uint24 xpGained2;
Skill skill3; // Could come
uint24 xpGained3;
// How much food is consumed in the current action so far
uint16 foodConsumed;
// How many base consumables are consumed in the current action so far
uint16 baseInputItemsConsumedNum;
}
struct PendingQueuedActionProcessed {
// XP gained during this session
Skill[] skills;
uint32[] xpGainedSkills;
// Data for the current action which has been previously processed, this is used to store on the Player
PendingQueuedActionData currentAction;
}
struct QuestState {
uint256[] consumedItemTokenIds;
uint256[] consumedAmounts;
uint256[] rewardItemTokenIds;
uint256[] rewardAmounts;
PlayerQuest[] activeQuestInfo;
uint256[] questsCompleted;
Skill[] skills; // Skills gained XP in
uint32[] xpGainedSkills; // XP gained in these skills
}
struct LotteryWinnerInfo {
uint16 lotteryId;
uint24 raffleId;
uint16 itemTokenId;
uint16 amount;
bool instantConsume;
uint64 playerId;
}
struct PendingQueuedActionState {
// These 2 are in sync. Separated to reduce gas/deployment costs as these are passed down many layers.
PendingQueuedActionEquipmentState[] equipmentStates;
PendingQueuedActionMetadata[] actionMetadatas;
QueuedAction[] remainingQueuedActions;
PastRandomRewardInfo[] producedPastRandomRewards;
uint256[] xpRewardItemTokenIds;
uint256[] xpRewardAmounts;
uint256[] dailyRewardItemTokenIds;
uint256[] dailyRewardAmounts;
PendingQueuedActionProcessed processedData;
bytes32 dailyRewardMask;
QuestState quests;
uint256 numPastRandomRewardInstancesToRemove;
uint8 worldLocation;
LotteryWinnerInfo lotteryWinner;
}
struct FullAttireBonusInput {
Skill skill;
uint8 bonusXPPercent;
uint8 bonusRewardsPercent; // 3 = 3%
uint16[5] itemTokenIds; // 0 = head, 1 = body, 2 arms, 3 body, 4 = feet
}
// Contains everything you need to create an item
struct ItemInput {
CombatStats combatStats;
uint16 tokenId;
EquipPosition equipPosition;
bool isTransferable;
bool isFullModeOnly;
bool isAvailable;
uint16 questPrerequisiteId;
// Minimum requirements in this skill
Skill skill;
uint32 minXP;
// Food
uint16 healthRestored;
// Boost
BoostType boostType;
uint16 boostValue; // Varies, could be the % increase
uint24 boostDuration; // How long the effect of the boost vial last
// uri
string metadataURI;
string name;
}
/* Order head, neck, body, arms, legs, feet, ring, reserved1,
leftHandEquipment, rightHandEquipment,
Not used yet: input1, input2,input3, regenerate, reserved2, reserved3 */
struct CheckpointEquipments {
uint16[16] itemTokenIds;
uint16[16] balances;
}
struct ActivePlayerInfo {
uint64 playerId;
uint40 checkpoint;
uint24 timespan;
uint24 timespan1;
uint24 timespan2;
}
uint8 constant START_LEVEL = 17; // Needs updating when there is a new skill. Only useful for new heroes.
uint256 constant MAX_UNIQUE_TICKETS = 64;
// Used in a bunch of places
uint256 constant IS_FULL_MODE_BIT = 7;
// Passive/Instant/InstantVRF/Actions/ActionChoices/Item action
uint256 constant IS_AVAILABLE_BIT = 6;
// Passive actions
uint256 constant HAS_RANDOM_REWARDS_BIT = 5;
// The rest use world location for first 4 bits
// Queued action
uint256 constant HAS_PET_BIT = 2;
uint256 constant IS_VALID_BIT = 1;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
enum Promotion {
NONE,
STARTER,
HALLOWEEN_2023,
XMAS_2023,
HALLOWEEN_2024,
HOLIDAY4, // Just have placeholders for now
HOLIDAY5,
HOLIDAY6,
HOLIDAY7,
HOLIDAY8,
HOLIDAY9,
HOLIDAY10
}
enum PromotionMintStatus {
NONE,
SUCCESS,
PROMOTION_ALREADY_CLAIMED,
ORACLE_NOT_CALLED,
MINTING_OUTSIDE_AVAILABLE_DATE,
PLAYER_DOES_NOT_QUALIFY,
PLAYER_NOT_HIT_ENOUGH_CLAIMS_FOR_STREAK_BONUS,
DEPENDENT_QUEST_NOT_COMPLETED
}
struct PromotionInfoInput {
Promotion promotion;
uint40 startTime;
uint40 endTime; // Exclusive
uint8 numDailyRandomItemsToPick; // Number of items to pick
uint40 minTotalXP; // Minimum xp required to claim
uint256 tokenCost; // Cost in brush to start the promotion, max 16mil
// Special promotion specific (like 1kin)
uint8 redeemCodeLength; // Length of the redeem code
bool adminOnly; // Only admins can mint the promotion, like for 1kin (Not used yet)
bool promotionTiedToUser; // If the promotion is tied to a user
bool promotionTiedToPlayer; // If the promotion is tied to the player
bool promotionMustOwnPlayer; // Must own the player to get the promotion
// Evolution specific
bool evolvedHeroOnly; // Only allow evolved heroes to claim
// Multiday specific
bool isMultiday; // The promotion is multi-day
uint256 brushCostMissedDay; // Cost in brush to mint the promotion if they miss a day (in ether), max 25.6 (base 100)
uint8 numDaysHitNeededForStreakBonus; // How many days to hit for the streak bonus
uint8 numDaysClaimablePeriodStreakBonus; // If there is a streak bonus, how many days to claim it after the promotion ends. If no final day bonus, set to 0
uint8 numRandomStreakBonusItemsToPick1; // Number of items to pick for the streak bonus
uint8 numRandomStreakBonusItemsToPick2; // Number of random items to pick for the streak bonus
uint16[] randomStreakBonusItemTokenIds1;
uint32[] randomStreakBonusAmounts1;
uint16[] randomStreakBonusItemTokenIds2;
uint32[] randomStreakBonusAmounts2;
uint16[] guaranteedStreakBonusItemTokenIds;
uint16[] guaranteedStreakBonusAmounts;
// Single and multiday
uint16[] guaranteedItemTokenIds; // Guaranteed items for the promotions each day, if empty then they are handled in a specific way for the promotion like daily rewards
uint32[] guaranteedAmounts; // Corresponding amounts to the itemTokenIds
uint16[] randomItemTokenIds; // Possible items for the promotions each day, if empty then they are handled in a specific way for the promotion like daily rewards
uint32[] randomAmounts; // Corresponding amounts to the randomItemTokenIds
// Quests
uint16 questPrerequisiteId;
}
struct PromotionInfo {
Promotion promotion;
uint40 startTime;
uint8 numDays;
uint8 numDailyRandomItemsToPick; // Number of items to pick
uint40 minTotalXP; // Minimum xp required to claim
uint24 tokenCost; // Cost in brush to mint the promotion (in ether), max 16mil
// Quests
uint16 questPrerequisiteId;
// Special promotion specific (like 1kin), could pack these these later
uint8 redeemCodeLength; // Length of the redeem code
bool adminOnly; // Only admins can mint the promotion, like for 1kin
bool promotionTiedToUser; // If the promotion is tied to a user
bool promotionTiedToPlayer; // If the promotion is tied to the player
bool promotionMustOwnPlayer; // Must own the player to get the promotion
// Evolution specific
bool evolvedHeroOnly; // Only allow evolved heroes to claim
// Multiday specific
bool isMultiday; // The promotion is multi-day
uint8 brushCostMissedDay; // Cost in brush to mint the promotion if they miss a day (in ether), max 25.5, base 100
uint8 numDaysHitNeededForStreakBonus; // How many days to hit for the streak bonus
uint8 numDaysClaimablePeriodStreakBonus; // If there is a streak bonus, how many days to claim it after the promotion ends. If no final day bonus, set to 0
uint8 numRandomStreakBonusItemsToPick1; // Number of items to pick for the streak bonus
uint8 numRandomStreakBonusItemsToPick2; // Number of random items to pick for the streak bonus
// Misc
uint16[] randomStreakBonusItemTokenIds1;
uint32[] randomStreakBonusAmounts1;
uint16[] randomStreakBonusItemTokenIds2; // Not used yet
uint32[] randomStreakBonusAmounts2; // Not used yet
uint16[] guaranteedStreakBonusItemTokenIds; // Not used yet
uint16[] guaranteedStreakBonusAmounts; // Not used yet
// Single and multiday
uint16[] guaranteedItemTokenIds; // Guaranteed items for the promotions each day, if empty then they are handled in a specific way for the promotion like daily rewards
uint32[] guaranteedAmounts; // Corresponding amounts to the itemTokenIds
uint16[] randomItemTokenIds; // Possible items for the promotions each day, if empty then they are handled in a specific way for the promotion like daily rewards
uint32[] randomAmounts; // Corresponding amounts to the randomItemTokenIds
}
uint256 constant BRUSH_COST_MISSED_DAY_MUL = 10;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {Skill} from "./misc.sol";
struct QuestInput {
uint16 dependentQuestId; // The quest that must be completed before this one can be started
uint16 actionId1; // action to do
uint16 actionNum1; // how many (up to 65535)
uint16 actionId2; // another action to do
uint16 actionNum2; // how many (up to 65535)
uint16 actionChoiceId; // actionChoice to perform
uint16 actionChoiceNum; // how many to do (base number), (up to 65535)
Skill skillReward; // The skill to reward XP to
uint24 skillXPGained; // The amount of XP to give (up to 65535)
uint16 rewardItemTokenId1; // Reward an item
uint16 rewardAmount1; // amount of the reward (up to 65535)
uint16 rewardItemTokenId2; // Reward another item
uint16 rewardAmount2; // amount of the reward (up to 65535)
uint16 burnItemTokenId; // Burn an item
uint16 burnAmount; // amount of the burn (up to 65535)
uint16 questId; // Unique id for this quest
bool isFullModeOnly; // If true this quest requires the user be evolved
uint8 worldLocation; // 0 is the main starting world
}
struct Quest {
uint16 dependentQuestId; // The quest that must be completed before this one can be started
uint16 actionId1; // action to do
uint16 actionNum1; // how many (up to 65535)
uint16 actionId2; // another action to do
uint16 actionNum2; // how many (up to 65535)
uint16 actionChoiceId; // actionChoice to perform
uint16 actionChoiceNum; // how many to do (base number), (up to 65535)
Skill skillReward; // The skill to reward XP to
uint24 skillXPGained; // The amount of XP to give (up to 65535)
uint16 rewardItemTokenId1; // Reward an item
uint16 rewardAmount1; // amount of the reward (up to 65535)
uint16 rewardItemTokenId2; // Reward another item
uint16 rewardAmount2; // amount of the reward (up to 65535)
uint16 burnItemTokenId; // Burn an item
uint16 burnAmount; // amount of the burn (up to 65535)
uint16 reserved; // Reserved for future use (previously was questId and cleared)
bytes1 packedData; // FullMode is last bit, first 6 bits is worldLocation
}
struct PlayerQuest {
uint32 questId;
uint16 actionCompletedNum1;
uint16 actionCompletedNum2;
uint16 actionChoiceCompletedNum;
uint16 burnCompletedAmount;
}
uint256 constant QUEST_PURSE_STRINGS = 5; // MAKE SURE THIS MATCHES definitions// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {BoostType, Equipment} from "./misc.sol";
struct GuaranteedReward {
uint16 itemTokenId;
uint16 rate; // num per hour (base 10, 1 decimal) for actions and num per duration for passive actions
}
struct RandomReward {
uint16 itemTokenId;
uint16 chance; // out of 65535
uint8 amount; // out of 255
}
struct PendingRandomReward {
uint16 actionId;
uint40 startTime;
uint24 xpElapsedTime;
uint16 boostItemTokenId;
uint24 elapsedTime;
uint40 boostStartTime; // When the boost was started
uint24 sentinelElapsedTime;
// Full equipment at the time this was generated
uint8 fullAttireBonusRewardsPercent;
uint64 queueId; // TODO: Could reduce this if more stuff is needed
}
struct ActionRewards {
uint16 guaranteedRewardTokenId1;
uint16 guaranteedRewardRate1; // Num per hour base 10 (1 decimal) for actions (Max 6553.5 per hour), num per duration for passive actions
uint16 guaranteedRewardTokenId2;
uint16 guaranteedRewardRate2;
uint16 guaranteedRewardTokenId3;
uint16 guaranteedRewardRate3;
// Random chance rewards
uint16 randomRewardTokenId1;
uint16 randomRewardChance1; // out of 65535
uint8 randomRewardAmount1; // out of 255
uint16 randomRewardTokenId2;
uint16 randomRewardChance2;
uint8 randomRewardAmount2;
uint16 randomRewardTokenId3;
uint16 randomRewardChance3;
uint8 randomRewardAmount3;
uint16 randomRewardTokenId4;
uint16 randomRewardChance4;
uint8 randomRewardAmount4;
// No more room in this storage slot!
}
struct XPThresholdReward {
uint32 xpThreshold;
Equipment[] rewards;
}
enum InstantVRFActionType {
NONE,
GENERIC,
FORGING,
EGG
}
struct InstantVRFActionInput {
uint16 actionId;
uint16[] inputTokenIds;
uint24[] inputAmounts;
bytes data;
InstantVRFActionType actionType;
bool isFullModeOnly;
bool isAvailable;
uint16 questPrerequisiteId;
}
struct InstantVRFRandomReward {
uint16 itemTokenId;
uint16 chance; // out of 65535
uint16 amount; // out of 65535
}
uint256 constant MAX_GUARANTEED_REWARDS_PER_ACTION = 3;
uint256 constant MAX_RANDOM_REWARDS_PER_ACTION = 4;
uint256 constant MAX_REWARDS_PER_ACTION = MAX_GUARANTEED_REWARDS_PER_ACTION + MAX_RANDOM_REWARDS_PER_ACTION;
uint256 constant MAX_CONSUMED_PER_ACTION = 3;
uint256 constant MAX_QUEST_REWARDS = 2;
uint256 constant TIER_1_DAILY_REWARD_START_XP = 0;
uint256 constant TIER_2_DAILY_REWARD_START_XP = 7_650;
uint256 constant TIER_3_DAILY_REWARD_START_XP = 33_913;
uint256 constant TIER_4_DAILY_REWARD_START_XP = 195_864;
uint256 constant TIER_5_DAILY_REWARD_START_XP = 784_726;
uint256 constant TIER_6_DAILY_REWARD_START_XP = 2_219_451;
// 4 bytes for each threshold, starts at 500 xp in decimal
bytes constant XP_THRESHOLD_REWARDS = hex"00000000000001F4000003E8000009C40000138800002710000075300000C350000186A00001D4C0000493E0000557300007A120000927C0000B71B0000DBBA0000F424000124F800016E360001B7740001E8480002625A0002932E0002DC6C0003567E0003D0900004C4B40005B8D80006ACFC0007A1200008954400098968000A7D8C000B71B0000C65D4000D59F8000E4E1C0";// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
interface IBank {
function initialize() external;
function initializeAddresses(
uint256 clanId,
address bankRegistry,
address bankRelay,
address playerNFT,
address itemNFT,
address clans,
address players,
address lockedBankVaults,
address raids
) external;
function depositToken(address sender, address from, uint256 playerId, address token, uint256 amount) external;
function setAllowBreachedCapacity(bool allow) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
interface IOracleCB {
function newOracleRandomWords(uint256 randomWord) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
interface ISamWitchVRF {
function requestRandomWords(uint256 numWords, uint256 callbackGasLimit) external returns (bytes32 requestId);
function fulfillRandomWords(
bytes32 requestId,
address oracle,
address fulfillAddress,
uint256 callbackGasLimit,
uint256 numWords,
uint256[2] calldata publicKey,
uint256[4] calldata proof,
uint256[2] calldata uPoint,
uint256[4] calldata vComponents
) external returns (bool callSuccess);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {CombatStats, Skill} from "../globals/misc.sol";
import {ActionChoice} from "../globals/players.sol";
import {ActionRewards} from "../globals/rewards.sol";
import {ActionInfo} from "../globals/actions.sol";
interface IWorldActions {
function getXPPerHour(uint16 actionId, uint16 actionChoiceId) external view returns (uint24 xpPerHour);
function getNumSpawn(uint16 actionId) external view returns (uint256 numSpawned);
function getActionSuccessPercentAndMinXP(uint16 actionId) external view returns (uint8 successPercent, uint32 minXP);
function getCombatStats(uint16 actionId) external view returns (CombatStats memory stats);
function getActionChoice(uint16 actionId, uint16 choiceId) external view returns (ActionChoice memory choice);
function getRewardsHelper(
uint16 actionId
) external view returns (ActionRewards memory, Skill skill, uint256 numSpawned); // , uint8 worldLocation);
function getSkill(uint256 actionId) external view returns (Skill skill);
function getActionRewards(uint256 actionId) external view returns (ActionRewards memory);
function getActionInfo(uint256 actionId) external view returns (ActionInfo memory info);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {Skill} from "../globals/misc.sol";
library SkillLibrary {
error InvalidSkillId(uint8 skill);
function _asSkill(uint8 skill) internal pure returns (Skill) {
require(skill >= uint8(type(Skill).min) && skill <= uint8(type(Skill).max), InvalidSkillId(skill));
return Skill(skill);
}
function _isSkill(uint8 skill) internal pure returns (bool) {
return _isSkill(_asSkill(skill));
}
function _isSkill(uint8 skill, Skill check) internal pure returns (bool) {
return _isSkill(_asSkill(skill), check);
}
function _isSkillCombat(uint8 skill) internal pure returns (bool) {
return _isSkillCombat(_asSkill(skill));
}
function _isSkillNone(uint8 skill) internal pure returns (bool) {
return _isSkillNone(_asSkill(skill));
}
function _asUint8(Skill skill) internal pure returns (uint8) {
return uint8(skill);
}
function _isSkill(Skill skill) internal pure returns (bool) {
return !_isSkill(skill, Skill.NONE);
}
function _isSkill(Skill skill, Skill check) internal pure returns (bool) {
return skill == check;
}
function _isSkillCombat(Skill skill) internal pure returns (bool) {
return _isSkill(skill, Skill.COMBAT);
}
function _isSkillNone(Skill skill) internal pure returns (bool) {
return _isSkill(skill, Skill.NONE);
}
}{
"evmVersion": "cancun",
"optimizer": {
"enabled": true,
"runs": 1000,
"details": {
"yul": true
}
},
"viaIR": true,
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"CallbackGasLimitTooHigh","type":"error"},{"inputs":[],"name":"CallerNotSamWitchVRF","type":"error"},{"inputs":[{"internalType":"uint256","name":"currentTime","type":"uint256"},{"internalType":"uint256","name":"checkpoint","type":"uint256"}],"name":"CanOnlyRequestAfterTheNextCheckpoint","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[],"name":"NoValidRandomWord","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"RandomWordsAlreadyInitialized","type":"error"},{"inputs":[],"name":"RandomWordsCannotBeUpdatedYet","type":"error"},{"inputs":[],"name":"RequestAlreadyFulfilled","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","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":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"randomWord","type":"uint256"}],"name":"RequestFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"numWords","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lastRandomWordsUpdatedTime","type":"uint256"}],"name":"RequestSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"MIN_RANDOM_WORDS_UPDATE_TIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NUM_DAYS_RANDOM_WORDS_INITIALIZED","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"uint256[]","name":"words","type":"uint256[]"}],"name":"fulfillRandomWords","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getMultipleWords","outputs":[{"internalType":"uint256[4]","name":"words","type":"uint256[4]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"numTickets","type":"uint256"},{"internalType":"uint256","name":"startTimestamp","type":"uint256"},{"internalType":"uint256","name":"endTimestamp","type":"uint256"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getRandomBytes","outputs":[{"internalType":"bytes","name":"randomBytes","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getRandomWord","outputs":[{"internalType":"uint256","name":"randomWord","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"hasRandomWord","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vrf","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOracleCB","name":"wishingWell","type":"address"},{"internalType":"contract IOracleCB","name":"dailyRewardsScheduler","type":"address"}],"name":"initializeAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initializeRandomWords","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastRandomWordsUpdatedTime","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":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"name":"randomWords","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"name":"requestIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"requestRandomWords","outputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gasLimit","type":"uint256"}],"name":"setExpectedGasLimitFulfill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"}]Contract Creation Code
60a080604052346100c257306080525f5160206116e05f395f51905f525460ff8160401c166100b3576002600160401b03196001600160401b03821601610060575b60405161161990816100c78239608051818181610c420152610d360152f35b6001600160401b0319166001600160401b039081175f5160206116e05f395f51905f525581527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80610041565b63f92ee8a960e01b5f5260045ffd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80631929f7b314610ff357806330b7bdad14610f8d5780634f1ef28614610cba57806352d1902d14610c28578063604fefb714610ace5780636528f09a14610ab1578063715018a614610a1a578063770d0441146109ff5780637fe4cf6b146108b25780638796ba8c146108855780638da5cb5b14610840578063ad3cb1cc146107dd578063b8e719a714610762578063be52e34f1461067e578063beff730f14610654578063c4d66de8146103eb578063c828371e146103c5578063cc76b9111461039f578063debde2841461037c578063e0c8628914610180578063e5df71fc1461013a5763f2fde38b1461010b575f80fd5b346101365760203660031901126101365761013461012761101b565b61012f6112d5565b6111d3565b005b5f80fd5b3461013657602036600319011261013657610156600435611172565b604051905f825b6004821061016a57608084f35b602080600192855181520193019101909161015d565b34610136575f36600319011261013657600554801590811561034c575b5015610324575f5464ffffffffff81166201518081018091116103105764ffffffffff164281116102e157602062ffffff60446001600160a01b0360015416945f60405196879485937f1b739ef10000000000000000000000000000000000000000000000000000000085526001600486015260501c1660248401525af19182156102d6575f92610282575b6020837ff98e18bb2828ff6fd29ca6214d585469dd4c2f311d2b765ab247985ba08b111b606085610259846110bf565b8064ffffffffff195f5416175f55604051908482526001868301526040820152a1604051908152f35b91506020823d6020116102ce575b8161029d60209383611031565b81010312610136579051907ff98e18bb2828ff6fd29ca6214d585469dd4c2f311d2b765ab247985ba08b111b610229565b3d9150610290565b6040513d5f823e3d90fd5b7f1351c940000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b634e487b7160e01b5f52601160045260245ffd5b7f974b4f8a000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f198101915081116103105761036190611093565b90549060031b1c5f52600660205260405f205415158161019d565b34610136575f36600319011261013657602064ffffffffff5f5416604051908152f35b346101365760203660031901126101365760206103bd600435611138565b604051908152f35b34610136575f36600319011261013657602064ffffffffff5f5460281c16604051908152f35b346101365760203660031901126101365761040461101b565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549060ff8260401c16159167ffffffffffffffff81168015908161064c575b6001149081610642575b159081610639575b506106115767ffffffffffffffff1981166001177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055826105d2575b5061049b611500565b6104a3611500565b6104ac336111d3565b6104b4611500565b620151804204906201518082029180830462015180149015171561031057620545ff1982019182116103105764ffffffffff82165f54906203f48081018091116103105764ffffffffff6c0927c0000000000000000000009269ffffffffff00000000006001600160a01b039660281b16906cffffffffffffffffffffffffff191617911617175f55166001600160a01b0319600154161760015561055557005b68ff0000000000000000197ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b68ffffffffffffffffff191668010000000000000001177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005582610492565b7ff92ee8a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b90501584610456565b303b15915061044e565b849150610444565b34610136576020366003190112610136576004355f526006602052602060405f2054604051908152f35b346101365760403660031901126101365760243567ffffffffffffffff811161013657366023820112156101365780600401359067ffffffffffffffff821161074e578160051b60208101926106d76040519485611031565b8352602460208401918301019136831161013657602401905b82821061073e57836001600160a01b036001541633036107165761013490600435611334565b7f580ae425000000000000000000000000000000000000000000000000000000005f5260045ffd5b81358152602091820191016106f0565b634e487b7160e01b5f52604160045260245ffd5b346101365760203660031901126101365760043561077e6112d5565b622dc6c081116107b5576cffffff000000000000000000005f549160501b16906cffffff000000000000000000001916175f555f80f35b7fbfbc293b000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610136575f3660031901126101365761083c6040516107fe604082611031565b600581527f352e302e30000000000000000000000000000000000000000000000000000000602082015260405191829160208352602083019061106f565b0390f35b34610136575f3660031901126101365760206001600160a01b037f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005416604051908152f35b346101365760203660031901126101365760206108a3600435611093565b90549060031b1c604051908152f35b34610136576080366003190112610136576044356064356024356004356010811161091b575090826108ef926108ea61083c95611138565b6114a1565b60405190602082015260208152610907604082611031565b60405191829160208352602083019061106f565b9091906040106109eb5761092e83611172565b925f5b60048110610981575050505060405190602082015f905b6004821061096b5750505080608061083c925261096660a082611031565b610907565b6020806001928551815201930191019091610948565b8061099a8484876109946001968b61111a565b516114a1565b6109a4828861111a565b52806109b1575b01610931565b6109bb818761111a565b5186511860405160208101918252602081526109d8604082611031565b5190206109e5828861111a565b526109ab565b634e487b7160e01b5f52600160045260245ffd5b34610136575f36600319011261013657602060405160038152f35b34610136575f36600319011261013657610a326112d5565b5f6001600160a01b037f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031981167f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b34610136575f366003190112610136576020604051620151808152f35b34610136575f36600319011261013657610ae66112d5565b600454610c0057600160045546617a6914602060405f5b60038110610b0757005b8060c801908160c81161031057610b1d826110bf565b64ffffffffff5f5460281c1662015180820282810462015180148315171561031057810180911161031057620151808101809111610310576001927ff98e18bb2828ff6fd29ca6214d585469dd4c2f311d2b765ab247985ba08b111b6060610bf49387519084825287602083015288820152a1845190610b9d8683611031565b848252863660208401378715610bfa57617a695b865160208101916bffffffffffffffffffffffff199060601b16825285603482015260348152610be2605482611031565b519020610bee8361110d565b52611334565b01610afd565b30610bb1565b7fbb4ae23d000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610136575f366003190112610136576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163003610c925760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b7fe07c8dba000000000000000000000000000000000000000000000000000000005f5260045ffd5b604036600319011261013657610cce61101b565b6024359067ffffffffffffffff8211610136573660238301121561013657816004013590610cfb82611053565b91610d096040519384611031565b8083526020830193366024838301011161013657815f926024602093018737840101526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016803014908115610f58575b50610c9257610d6e6112d5565b6001600160a01b038116926040517f52d1902d000000000000000000000000000000000000000000000000000000008152602081600481885afa5f9181610f24575b50610dc85784634c9c8ce360e01b5f5260045260245ffd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc869203610ef95750823b15610ee757806001600160a01b03197f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416177f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2825115610eb5575f8091610134945190845af43d15610ead573d91610e9183611053565b92610e9f6040519485611031565b83523d5f602085013e611557565b606091611557565b50505034610ebf57005b7fb398979f000000000000000000000000000000000000000000000000000000005f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b7faa1d49a4000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9091506020813d602011610f50575b81610f4060209383611031565b8101031261013657519086610db0565b3d9150610f33565b90506001600160a01b037f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416141584610d61565b3461013657604036600319011261013657610fa661101b565b602435906001600160a01b038216809203610136576001600160a01b0390610fcc6112d5565b166001600160a01b031960025416176002556001600160a01b031960035416176003555f80f35b3461013657602036600319011261013657602061101160043561128e565b1515604051908152f35b600435906001600160a01b038216820361013657565b90601f8019910116810190811067ffffffffffffffff82111761074e57604052565b67ffffffffffffffff811161074e57601f01601f191660200190565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b6005548110156110ab5760055f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b6005546801000000000000000081101561074e57600181016005556005548110156110ab5760055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00155565b8051156110ab5760200190565b9060048110156110ab5760051b0190565b9190820391821161031057565b6111419061128e565b90811561114a57565b7fb4190460000000000000000000000000000000000000000000000000000000005f5260045ffd5b9060405191611182608084611031565b6080368437825f5b6004811061119757505050565b62015180810290808204620151801481151715610310576111c26111bd6001938661112b565b611138565b6111cc828561111a565b520161118a565b6001600160a01b03168015611262576001600160a01b037f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930054826001600160a01b03198216177f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b7f1e4fbdf7000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b611297906114d3565b5f811280156112c9575b6112c4576112ae90611093565b90549060031b1c5f52600660205260405f205490565b505f90565b508060055411156112a1565b6001600160a01b037f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005416330361130857565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b905f90825f52600660205260405f20546114795760018151036114515761135a9061110d565b5190825f5260066020528160405f20556001600160a01b0360025416803b15610136575f80916024604051809481936327fe556d60e21b83528860048401525af180156102d65761143e575b506001600160a01b0360035416803b1561143a578180916024604051809481936327fe556d60e21b83528860048401525af1801561142f57611417575b50507f5c69e7026b653d8606b5613bb00fd8c4b0504b1cbe8db600c406faac180924d59160409182519182526020820152a1565b611422828092611031565b61142c57806113e3565b80fd5b6040513d84823e3d90fd5b5080fd5b61144a91505f90611031565b5f5f6113a6565b7fff633a38000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f533d99dd000000000000000000000000000000000000000000000000000000005f5260045ffd5b9290916040519260208401948552604084015260608301526080820152608081526114cd60a082611031565b51902090565b64ffffffffff5f5460281c16908181106114f95762015180916114f59161112b565b0490565b50505f1990565b60ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c161561152f57565b7fd7e6bcf8000000000000000000000000000000000000000000000000000000005f5260045ffd5b90611594575080511561156c57805190602001fd5b7fd6bda275000000000000000000000000000000000000000000000000000000005f5260045ffd5b815115806115da575b6115a5575090565b6001600160a01b03907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b1561159d56fea2646970667358221220b9ce35991d2db9cf3f4c8239582d4e7d7b416b59700c3ea6e86f326132a53f8e64736f6c634300081c0033f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c80631929f7b314610ff357806330b7bdad14610f8d5780634f1ef28614610cba57806352d1902d14610c28578063604fefb714610ace5780636528f09a14610ab1578063715018a614610a1a578063770d0441146109ff5780637fe4cf6b146108b25780638796ba8c146108855780638da5cb5b14610840578063ad3cb1cc146107dd578063b8e719a714610762578063be52e34f1461067e578063beff730f14610654578063c4d66de8146103eb578063c828371e146103c5578063cc76b9111461039f578063debde2841461037c578063e0c8628914610180578063e5df71fc1461013a5763f2fde38b1461010b575f80fd5b346101365760203660031901126101365761013461012761101b565b61012f6112d5565b6111d3565b005b5f80fd5b3461013657602036600319011261013657610156600435611172565b604051905f825b6004821061016a57608084f35b602080600192855181520193019101909161015d565b34610136575f36600319011261013657600554801590811561034c575b5015610324575f5464ffffffffff81166201518081018091116103105764ffffffffff164281116102e157602062ffffff60446001600160a01b0360015416945f60405196879485937f1b739ef10000000000000000000000000000000000000000000000000000000085526001600486015260501c1660248401525af19182156102d6575f92610282575b6020837ff98e18bb2828ff6fd29ca6214d585469dd4c2f311d2b765ab247985ba08b111b606085610259846110bf565b8064ffffffffff195f5416175f55604051908482526001868301526040820152a1604051908152f35b91506020823d6020116102ce575b8161029d60209383611031565b81010312610136579051907ff98e18bb2828ff6fd29ca6214d585469dd4c2f311d2b765ab247985ba08b111b610229565b3d9150610290565b6040513d5f823e3d90fd5b7f1351c940000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b634e487b7160e01b5f52601160045260245ffd5b7f974b4f8a000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f198101915081116103105761036190611093565b90549060031b1c5f52600660205260405f205415158161019d565b34610136575f36600319011261013657602064ffffffffff5f5416604051908152f35b346101365760203660031901126101365760206103bd600435611138565b604051908152f35b34610136575f36600319011261013657602064ffffffffff5f5460281c16604051908152f35b346101365760203660031901126101365761040461101b565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549060ff8260401c16159167ffffffffffffffff81168015908161064c575b6001149081610642575b159081610639575b506106115767ffffffffffffffff1981166001177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055826105d2575b5061049b611500565b6104a3611500565b6104ac336111d3565b6104b4611500565b620151804204906201518082029180830462015180149015171561031057620545ff1982019182116103105764ffffffffff82165f54906203f48081018091116103105764ffffffffff6c0927c0000000000000000000009269ffffffffff00000000006001600160a01b039660281b16906cffffffffffffffffffffffffff191617911617175f55166001600160a01b0319600154161760015561055557005b68ff0000000000000000197ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a1005b68ffffffffffffffffff191668010000000000000001177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005582610492565b7ff92ee8a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b90501584610456565b303b15915061044e565b849150610444565b34610136576020366003190112610136576004355f526006602052602060405f2054604051908152f35b346101365760403660031901126101365760243567ffffffffffffffff811161013657366023820112156101365780600401359067ffffffffffffffff821161074e578160051b60208101926106d76040519485611031565b8352602460208401918301019136831161013657602401905b82821061073e57836001600160a01b036001541633036107165761013490600435611334565b7f580ae425000000000000000000000000000000000000000000000000000000005f5260045ffd5b81358152602091820191016106f0565b634e487b7160e01b5f52604160045260245ffd5b346101365760203660031901126101365760043561077e6112d5565b622dc6c081116107b5576cffffff000000000000000000005f549160501b16906cffffff000000000000000000001916175f555f80f35b7fbfbc293b000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610136575f3660031901126101365761083c6040516107fe604082611031565b600581527f352e302e30000000000000000000000000000000000000000000000000000000602082015260405191829160208352602083019061106f565b0390f35b34610136575f3660031901126101365760206001600160a01b037f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005416604051908152f35b346101365760203660031901126101365760206108a3600435611093565b90549060031b1c604051908152f35b34610136576080366003190112610136576044356064356024356004356010811161091b575090826108ef926108ea61083c95611138565b6114a1565b60405190602082015260208152610907604082611031565b60405191829160208352602083019061106f565b9091906040106109eb5761092e83611172565b925f5b60048110610981575050505060405190602082015f905b6004821061096b5750505080608061083c925261096660a082611031565b610907565b6020806001928551815201930191019091610948565b8061099a8484876109946001968b61111a565b516114a1565b6109a4828861111a565b52806109b1575b01610931565b6109bb818761111a565b5186511860405160208101918252602081526109d8604082611031565b5190206109e5828861111a565b526109ab565b634e487b7160e01b5f52600160045260245ffd5b34610136575f36600319011261013657602060405160038152f35b34610136575f36600319011261013657610a326112d5565b5f6001600160a01b037f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031981167f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b34610136575f366003190112610136576020604051620151808152f35b34610136575f36600319011261013657610ae66112d5565b600454610c0057600160045546617a6914602060405f5b60038110610b0757005b8060c801908160c81161031057610b1d826110bf565b64ffffffffff5f5460281c1662015180820282810462015180148315171561031057810180911161031057620151808101809111610310576001927ff98e18bb2828ff6fd29ca6214d585469dd4c2f311d2b765ab247985ba08b111b6060610bf49387519084825287602083015288820152a1845190610b9d8683611031565b848252863660208401378715610bfa57617a695b865160208101916bffffffffffffffffffffffff199060601b16825285603482015260348152610be2605482611031565b519020610bee8361110d565b52611334565b01610afd565b30610bb1565b7fbb4ae23d000000000000000000000000000000000000000000000000000000005f5260045ffd5b34610136575f366003190112610136576001600160a01b037f00000000000000000000000050590a161d5c11b157b2a11097b32117622939c6163003610c925760206040517f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8152f35b7fe07c8dba000000000000000000000000000000000000000000000000000000005f5260045ffd5b604036600319011261013657610cce61101b565b6024359067ffffffffffffffff8211610136573660238301121561013657816004013590610cfb82611053565b91610d096040519384611031565b8083526020830193366024838301011161013657815f926024602093018737840101526001600160a01b037f00000000000000000000000050590a161d5c11b157b2a11097b32117622939c616803014908115610f58575b50610c9257610d6e6112d5565b6001600160a01b038116926040517f52d1902d000000000000000000000000000000000000000000000000000000008152602081600481885afa5f9181610f24575b50610dc85784634c9c8ce360e01b5f5260045260245ffd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc869203610ef95750823b15610ee757806001600160a01b03197f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416177f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2825115610eb5575f8091610134945190845af43d15610ead573d91610e9183611053565b92610e9f6040519485611031565b83523d5f602085013e611557565b606091611557565b50505034610ebf57005b7fb398979f000000000000000000000000000000000000000000000000000000005f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b7faa1d49a4000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9091506020813d602011610f50575b81610f4060209383611031565b8101031261013657519086610db0565b3d9150610f33565b90506001600160a01b037f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5416141584610d61565b3461013657604036600319011261013657610fa661101b565b602435906001600160a01b038216809203610136576001600160a01b0390610fcc6112d5565b166001600160a01b031960025416176002556001600160a01b031960035416176003555f80f35b3461013657602036600319011261013657602061101160043561128e565b1515604051908152f35b600435906001600160a01b038216820361013657565b90601f8019910116810190811067ffffffffffffffff82111761074e57604052565b67ffffffffffffffff811161074e57601f01601f191660200190565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b6005548110156110ab5760055f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b6005546801000000000000000081101561074e57600181016005556005548110156110ab5760055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00155565b8051156110ab5760200190565b9060048110156110ab5760051b0190565b9190820391821161031057565b6111419061128e565b90811561114a57565b7fb4190460000000000000000000000000000000000000000000000000000000005f5260045ffd5b9060405191611182608084611031565b6080368437825f5b6004811061119757505050565b62015180810290808204620151801481151715610310576111c26111bd6001938661112b565b611138565b6111cc828561111a565b520161118a565b6001600160a01b03168015611262576001600160a01b037f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930054826001600160a01b03198216177f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b7f1e4fbdf7000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b611297906114d3565b5f811280156112c9575b6112c4576112ae90611093565b90549060031b1c5f52600660205260405f205490565b505f90565b508060055411156112a1565b6001600160a01b037f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005416330361130857565b7f118cdaa7000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b905f90825f52600660205260405f20546114795760018151036114515761135a9061110d565b5190825f5260066020528160405f20556001600160a01b0360025416803b15610136575f80916024604051809481936327fe556d60e21b83528860048401525af180156102d65761143e575b506001600160a01b0360035416803b1561143a578180916024604051809481936327fe556d60e21b83528860048401525af1801561142f57611417575b50507f5c69e7026b653d8606b5613bb00fd8c4b0504b1cbe8db600c406faac180924d59160409182519182526020820152a1565b611422828092611031565b61142c57806113e3565b80fd5b6040513d84823e3d90fd5b5080fd5b61144a91505f90611031565b5f5f6113a6565b7fff633a38000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f533d99dd000000000000000000000000000000000000000000000000000000005f5260045ffd5b9290916040519260208401948552604084015260608301526080820152608081526114cd60a082611031565b51902090565b64ffffffffff5f5460281c16908181106114f95762015180916114f59161112b565b0490565b50505f1990565b60ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c161561152f57565b7fd7e6bcf8000000000000000000000000000000000000000000000000000000005f5260045ffd5b90611594575080511561156c57805190602001fd5b7fd6bda275000000000000000000000000000000000000000000000000000000005f5260045ffd5b815115806115da575b6115a5575090565b6001600160a01b03907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b1561159d56fea2646970667358221220b9ce35991d2db9cf3f4c8239582d4e7d7b416b59700c3ea6e86f326132a53f8e64736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.