Overview
ETH Balance
0 ETH
ETH Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 1 from a total of 1 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Mint | 21489814 | 415 days ago | IN | 0 ETH | 0.00000053 |
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 Source Code Verified (Exact Match)
Contract Name:
FlightNft
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IPolicy} from "../../instance/module/IPolicy.sol";
import {IRegistry} from "../../registry/IRegistry.sol";
import {Amount} from "../../type/Amount.sol";
import {ChainNft} from "../../registry/ChainNft.sol";
import {FlightProduct} from "./FlightProduct.sol";
import {InstanceReader} from "../../instance/InstanceReader.sol";
import {NftId, NftIdLib} from "../../type/NftId.sol";
import {RiskId} from "../../type/RiskId.sol";
/**
* @title FlightNft
* @dev FlightNft provides NFT data for flight delay policies.
*/
// contract FlightNft is IERC721Metadata {
contract FlightNft is
ERC721,
Ownable
{
error ErrorFlightNftNotMinter();
error ErrorFlightNftAlreadyMinted(uint256 tokenId);
error ErrorFlightNftNotAvailable(uint256 tokenId);
error ErrorFlightNftNotFlightPolicy(uint256 tokenId);
ChainNft public immutable chainNft;
FlightProduct public immutable flightProduct;
InstanceReader public immutable instanceReader;
IRegistry public registry;
address public minter;
string private _baseUri;
modifier onlyMinter() {
if(msg.sender != minter) {
revert ErrorFlightNftNotMinter();
}
_;
}
constructor(
address flightProductAddress,
string memory nftName,
string memory nftSymbol,
string memory baseUri
)
ERC721(nftName, nftSymbol)
Ownable(msg.sender)
{
flightProduct = FlightProduct(flightProductAddress);
registry = flightProduct.getRegistry();
chainNft = ChainNft(registry.getChainNftAddress());
instanceReader = flightProduct.getInstance().getInstanceReader();
minter = msg.sender;
_baseUri = baseUri;
}
/**
* Set the base URI to the specified value.
* Once set, this results in tokenURI() to return <baseUri><tokenId>.
*/
function setBaseURI(string memory baseUri)
public
onlyOwner()
{
_baseUri = baseUri;
}
/**
* Set the minter address.
*/
function setMinter(address minterAddress)
public
onlyOwner()
{
minter = minterAddress;
}
/**
* Mints a metadata NFT for the specified chainNft NFT.
* Only the minter can mint such NFTs.
*/
function mint(uint256 tokenId)
external
onlyMinter()
{
// verify nft does not yet exist
if (_ownerOf(tokenId) != address(0)) {
revert ErrorFlightNftAlreadyMinted(tokenId);
}
// verify nft on chainNft exists
// also checks if nft exists (ERC721NonexistentToken)
address nftOwner = chainNft.ownerOf(tokenId);
// verify nft is flight delay policy
if (registry.getParentNftId(NftIdLib.toNftId(tokenId)) != registry.getNftIdForAddress(address(flightProduct))) {
revert ErrorFlightNftNotFlightPolicy(tokenId);
}
_mint(nftOwner, tokenId);
}
/**
* @dev Return the NFT token URI for the specified token.
* No check is performed to ensure the token exists.
*/
function tokenURI(uint256 tokenId) public view override returns (string memory) {
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string.concat(baseURI, toString(tokenId)) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal override view returns (string memory) {
return _baseUri;
}
function approve(address to, uint256 tokenId) public override { _revert(); }
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public override { _revert(); }
function setApprovalForAll(address operator, bool approved) public override { _revert(); }
function transferFrom(address from, address to, uint256 tokenId) public override { _revert(); }
function balanceOf(address owner) public override view returns (uint256 balance) {
return chainNft.balanceOf(owner);
}
function getApproved(uint256 tokenId) public override view returns (address operator) {
return chainNft.getApproved(tokenId);
}
function isApprovedForAll(address owner, address operator) public override view returns (bool) {
return chainNft.isApprovedForAll(owner, operator);
}
function ownerOf(uint256 tokenId) public override view returns (address owner) {
return chainNft.ownerOf(tokenId);
}
function supportsInterface(bytes4 interfaceId) public override view returns (bool) {
return chainNft.supportsInterface(interfaceId);
}
function getPolicyData(NftId policyNftId)
public
view
returns (
RiskId riskId,
string memory flightData,
string memory departureTimeLocal,
string memory arrivalTimeLocal,
Amount premiumAmount,
Amount[5] memory payoutAmounts,
bytes1 status,
int256 delayMinutes
)
{
IPolicy.PolicyInfo memory info = instanceReader.getPolicyInfo(policyNftId);
// get financial data
premiumAmount = info.premiumAmount;
if (info.applicationData.length > 0) {
(, payoutAmounts) = abi.decode(info.applicationData, (Amount, Amount[5]));
}
// get risk data
riskId = info.riskId;
bytes memory data = instanceReader.getRiskInfo(riskId).data;
if (data.length > 0) {
FlightProduct.FlightRisk memory flightRisk = flightProduct.decodeFlightRiskData(data);
flightData = flightRisk.flightData.toString();
departureTimeLocal = flightRisk.departureTimeLocal;
arrivalTimeLocal = flightRisk.arrivalTimeLocal;
status = flightRisk.status;
delayMinutes = flightRisk.delayMinutes;
}
}
function toString(uint256 value) public pure returns (string memory) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits = 0;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
uint index = digits - 1;
temp = value;
while (temp != 0) {
buffer[index] = bytes1(uint8(48 + temp % 10));
temp /= 10;
if (index > 0) {
index--;
}
}
return string(buffer);
}
function _revert() private pure {
revert("FlightNft: Use GIF Chain NFT contract to interact with NFTs. See function getChainNft()");
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AccessManaged.sol)
pragma solidity ^0.8.20;
import {IAuthority} from "@openzeppelin/contracts/access/manager/IAuthority.sol";
import {AuthorityUtils} from "@openzeppelin/contracts/access/manager/AuthorityUtils.sol";
import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol";
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev This contract module makes available a {restricted} modifier. Functions decorated with this modifier will be
* permissioned according to an "authority": a contract like {AccessManager} that follows the {IAuthority} interface,
* implementing a policy that allows certain callers to access certain functions.
*
* IMPORTANT: The `restricted` modifier should never be used on `internal` functions, judiciously used in `public`
* functions, and ideally only used in `external` functions. See {restricted}.
*/
abstract contract AccessManagedUpgradeable is Initializable, ContextUpgradeable, IAccessManaged {
/// @custom:storage-location erc7201:openzeppelin.storage.AccessManaged
struct AccessManagedStorage {
address _authority;
bool _consumingSchedule;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessManaged")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessManagedStorageLocation = 0xf3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00;
function _getAccessManagedStorage() private pure returns (AccessManagedStorage storage $) {
assembly {
$.slot := AccessManagedStorageLocation
}
}
/**
* @dev Initializes the contract connected to an initial authority.
*/
function __AccessManaged_init(address initialAuthority) internal onlyInitializing {
__AccessManaged_init_unchained(initialAuthority);
}
function __AccessManaged_init_unchained(address initialAuthority) internal onlyInitializing {
_setAuthority(initialAuthority);
}
/**
* @dev Restricts access to a function as defined by the connected Authority for this contract and the
* caller and selector of the function that entered the contract.
*
* [IMPORTANT]
* ====
* In general, this modifier should only be used on `external` functions. It is okay to use it on `public`
* functions that are used as external entry points and are not called internally. Unless you know what you're
* doing, it should never be used on `internal` functions. Failure to follow these rules can have critical security
* implications! This is because the permissions are determined by the function that entered the contract, i.e. the
* function at the bottom of the call stack, and not the function where the modifier is visible in the source code.
* ====
*
* [WARNING]
* ====
* Avoid adding this modifier to the https://docs.soliditylang.org/en/v0.8.20/contracts.html#receive-ether-function[`receive()`]
* function or the https://docs.soliditylang.org/en/v0.8.20/contracts.html#fallback-function[`fallback()`]. These
* functions are the only execution paths where a function selector cannot be unambiguosly determined from the calldata
* since the selector defaults to `0x00000000` in the `receive()` function and similarly in the `fallback()` function
* if no calldata is provided. (See {_checkCanCall}).
*
* The `receive()` function will always panic whereas the `fallback()` may panic depending on the calldata length.
* ====
*/
modifier restricted() {
_checkCanCall(_msgSender(), _msgData());
_;
}
/// @inheritdoc IAccessManaged
function authority() public view virtual returns (address) {
AccessManagedStorage storage $ = _getAccessManagedStorage();
return $._authority;
}
/// @inheritdoc IAccessManaged
function setAuthority(address newAuthority) public virtual {
address caller = _msgSender();
if (caller != authority()) {
revert AccessManagedUnauthorized(caller);
}
if (newAuthority.code.length == 0) {
revert AccessManagedInvalidAuthority(newAuthority);
}
_setAuthority(newAuthority);
}
/// @inheritdoc IAccessManaged
function isConsumingScheduledOp() public view returns (bytes4) {
AccessManagedStorage storage $ = _getAccessManagedStorage();
return $._consumingSchedule ? this.isConsumingScheduledOp.selector : bytes4(0);
}
/**
* @dev Transfers control to a new authority. Internal function with no access restriction. Allows bypassing the
* permissions set by the current authority.
*/
function _setAuthority(address newAuthority) internal virtual {
AccessManagedStorage storage $ = _getAccessManagedStorage();
$._authority = newAuthority;
emit AuthorityUpdated(newAuthority);
}
/**
* @dev Reverts if the caller is not allowed to call the function identified by a selector. Panics if the calldata
* is less than 4 bytes long.
*/
function _checkCanCall(address caller, bytes calldata data) internal virtual {
AccessManagedStorage storage $ = _getAccessManagedStorage();
(bool immediate, uint32 delay) = AuthorityUtils.canCallWithDelay(
authority(),
caller,
address(this),
bytes4(data[0:4])
);
if (!immediate) {
if (delay > 0) {
$._consumingSchedule = true;
IAccessManager(authority()).consumeScheduledOp(caller, data);
$._consumingSchedule = false;
} else {
revert AccessManagedUnauthorized(caller);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AccessManager.sol)
pragma solidity ^0.8.20;
import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol";
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {MulticallUpgradeable} from "../../utils/MulticallUpgradeable.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev AccessManager is a central contract to store the permissions of a system.
*
* A smart contract under the control of an AccessManager instance is known as a target, and will inherit from the
* {AccessManaged} contract, be connected to this contract as its manager and implement the {AccessManaged-restricted}
* modifier on a set of functions selected to be permissioned. Note that any function without this setup won't be
* effectively restricted.
*
* The restriction rules for such functions are defined in terms of "roles" identified by an `uint64` and scoped
* by target (`address`) and function selectors (`bytes4`). These roles are stored in this contract and can be
* configured by admins (`ADMIN_ROLE` members) after a delay (see {getTargetAdminDelay}).
*
* For each target contract, admins can configure the following without any delay:
*
* * The target's {AccessManaged-authority} via {updateAuthority}.
* * Close or open a target via {setTargetClosed} keeping the permissions intact.
* * The roles that are allowed (or disallowed) to call a given function (identified by its selector) through {setTargetFunctionRole}.
*
* By default every address is member of the `PUBLIC_ROLE` and every target function is restricted to the `ADMIN_ROLE` until configured otherwise.
* Additionally, each role has the following configuration options restricted to this manager's admins:
*
* * A role's admin role via {setRoleAdmin} who can grant or revoke roles.
* * A role's guardian role via {setRoleGuardian} who's allowed to cancel operations.
* * A delay in which a role takes effect after being granted through {setGrantDelay}.
* * A delay of any target's admin action via {setTargetAdminDelay}.
* * A role label for discoverability purposes with {labelRole}.
*
* Any account can be added and removed into any number of these roles by using the {grantRole} and {revokeRole} functions
* restricted to each role's admin (see {getRoleAdmin}).
*
* Since all the permissions of the managed system can be modified by the admins of this instance, it is expected that
* they will be highly secured (e.g., a multisig or a well-configured DAO).
*
* NOTE: This contract implements a form of the {IAuthority} interface, but {canCall} has additional return data so it
* doesn't inherit `IAuthority`. It is however compatible with the `IAuthority` interface since the first 32 bytes of
* the return data are a boolean as expected by that interface.
*
* NOTE: Systems that implement other access control mechanisms (for example using {Ownable}) can be paired with an
* {AccessManager} by transferring permissions (ownership in the case of {Ownable}) directly to the {AccessManager}.
* Users will be able to interact with these contracts through the {execute} function, following the access rules
* registered in the {AccessManager}. Keep in mind that in that context, the msg.sender seen by restricted functions
* will be {AccessManager} itself.
*
* WARNING: When granting permissions over an {Ownable} or {AccessControl} contract to an {AccessManager}, be very
* mindful of the danger associated with functions such as {{Ownable-renounceOwnership}} or
* {{AccessControl-renounceRole}}.
*/
contract AccessManagerUpgradeable is Initializable, ContextUpgradeable, MulticallUpgradeable, IAccessManager {
using Time for *;
// Structure that stores the details for a target contract.
struct TargetConfig {
mapping(bytes4 selector => uint64 roleId) allowedRoles;
Time.Delay adminDelay;
bool closed;
}
// Structure that stores the details for a role/account pair. This structures fit into a single slot.
struct Access {
// Timepoint at which the user gets the permission.
// If this is either 0 or in the future, then the role permission is not available.
uint48 since;
// Delay for execution. Only applies to restricted() / execute() calls.
Time.Delay delay;
}
// Structure that stores the details of a role.
struct Role {
// Members of the role.
mapping(address user => Access access) members;
// Admin who can grant or revoke permissions.
uint64 admin;
// Guardian who can cancel operations targeting functions that need this role.
uint64 guardian;
// Delay in which the role takes effect after being granted.
Time.Delay grantDelay;
}
// Structure that stores the details for a scheduled operation. This structure fits into a single slot.
struct Schedule {
// Moment at which the operation can be executed.
uint48 timepoint;
// Operation nonce to allow third-party contracts to identify the operation.
uint32 nonce;
}
uint64 public constant ADMIN_ROLE = type(uint64).min; // 0
uint64 public constant PUBLIC_ROLE = type(uint64).max; // 2**64-1
/// @custom:storage-location erc7201:openzeppelin.storage.AccessManager
struct AccessManagerStorage {
mapping(address target => TargetConfig mode) _targets;
mapping(uint64 roleId => Role) _roles;
mapping(bytes32 operationId => Schedule) _schedules;
// Used to identify operations that are currently being executed via {execute}.
// This should be transient storage when supported by the EVM.
bytes32 _executionId;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessManager")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessManagerStorageLocation = 0x40c6c8c28789853c7efd823ab20824bbd71718a8a5915e855f6f288c9a26ad00;
function _getAccessManagerStorage() private pure returns (AccessManagerStorage storage $) {
assembly {
$.slot := AccessManagerStorageLocation
}
}
/**
* @dev Check that the caller is authorized to perform the operation, following the restrictions encoded in
* {_getAdminRestrictions}.
*/
modifier onlyAuthorized() {
_checkAuthorized();
_;
}
function __AccessManager_init(address initialAdmin) internal onlyInitializing {
__AccessManager_init_unchained(initialAdmin);
}
function __AccessManager_init_unchained(address initialAdmin) internal onlyInitializing {
if (initialAdmin == address(0)) {
revert AccessManagerInvalidInitialAdmin(address(0));
}
// admin is active immediately and without any execution delay.
_grantRole(ADMIN_ROLE, initialAdmin, 0, 0);
}
// =================================================== GETTERS ====================================================
/// @inheritdoc IAccessManager
function canCall(
address caller,
address target,
bytes4 selector
) public view virtual returns (bool immediate, uint32 delay) {
if (isTargetClosed(target)) {
return (false, 0);
} else if (caller == address(this)) {
// Caller is AccessManager, this means the call was sent through {execute} and it already checked
// permissions. We verify that the call "identifier", which is set during {execute}, is correct.
return (_isExecuting(target, selector), 0);
} else {
uint64 roleId = getTargetFunctionRole(target, selector);
(bool isMember, uint32 currentDelay) = hasRole(roleId, caller);
return isMember ? (currentDelay == 0, currentDelay) : (false, 0);
}
}
/// @inheritdoc IAccessManager
function expiration() public view virtual returns (uint32) {
return 1 weeks;
}
/// @inheritdoc IAccessManager
function minSetback() public view virtual returns (uint32) {
return 5 days;
}
/// @inheritdoc IAccessManager
function isTargetClosed(address target) public view virtual returns (bool) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
return $._targets[target].closed;
}
/// @inheritdoc IAccessManager
function getTargetFunctionRole(address target, bytes4 selector) public view virtual returns (uint64) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
return $._targets[target].allowedRoles[selector];
}
/// @inheritdoc IAccessManager
function getTargetAdminDelay(address target) public view virtual returns (uint32) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
return $._targets[target].adminDelay.get();
}
/// @inheritdoc IAccessManager
function getRoleAdmin(uint64 roleId) public view virtual returns (uint64) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
return $._roles[roleId].admin;
}
/// @inheritdoc IAccessManager
function getRoleGuardian(uint64 roleId) public view virtual returns (uint64) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
return $._roles[roleId].guardian;
}
/// @inheritdoc IAccessManager
function getRoleGrantDelay(uint64 roleId) public view virtual returns (uint32) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
return $._roles[roleId].grantDelay.get();
}
/// @inheritdoc IAccessManager
function getAccess(
uint64 roleId,
address account
) public view virtual returns (uint48 since, uint32 currentDelay, uint32 pendingDelay, uint48 effect) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
Access storage access = $._roles[roleId].members[account];
since = access.since;
(currentDelay, pendingDelay, effect) = access.delay.getFull();
return (since, currentDelay, pendingDelay, effect);
}
/// @inheritdoc IAccessManager
function hasRole(
uint64 roleId,
address account
) public view virtual returns (bool isMember, uint32 executionDelay) {
if (roleId == PUBLIC_ROLE) {
return (true, 0);
} else {
(uint48 hasRoleSince, uint32 currentDelay, , ) = getAccess(roleId, account);
return (hasRoleSince != 0 && hasRoleSince <= Time.timestamp(), currentDelay);
}
}
// =============================================== ROLE MANAGEMENT ===============================================
/// @inheritdoc IAccessManager
function labelRole(uint64 roleId, string calldata label) public virtual onlyAuthorized {
if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
revert AccessManagerLockedRole(roleId);
}
emit RoleLabel(roleId, label);
}
/// @inheritdoc IAccessManager
function grantRole(uint64 roleId, address account, uint32 executionDelay) public virtual onlyAuthorized {
_grantRole(roleId, account, getRoleGrantDelay(roleId), executionDelay);
}
/// @inheritdoc IAccessManager
function revokeRole(uint64 roleId, address account) public virtual onlyAuthorized {
_revokeRole(roleId, account);
}
/// @inheritdoc IAccessManager
function renounceRole(uint64 roleId, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessManagerBadConfirmation();
}
_revokeRole(roleId, callerConfirmation);
}
/// @inheritdoc IAccessManager
function setRoleAdmin(uint64 roleId, uint64 admin) public virtual onlyAuthorized {
_setRoleAdmin(roleId, admin);
}
/// @inheritdoc IAccessManager
function setRoleGuardian(uint64 roleId, uint64 guardian) public virtual onlyAuthorized {
_setRoleGuardian(roleId, guardian);
}
/// @inheritdoc IAccessManager
function setGrantDelay(uint64 roleId, uint32 newDelay) public virtual onlyAuthorized {
_setGrantDelay(roleId, newDelay);
}
/**
* @dev Internal version of {grantRole} without access control. Returns true if the role was newly granted.
*
* Emits a {RoleGranted} event.
*/
function _grantRole(
uint64 roleId,
address account,
uint32 grantDelay,
uint32 executionDelay
) internal virtual returns (bool) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
if (roleId == PUBLIC_ROLE) {
revert AccessManagerLockedRole(roleId);
}
bool newMember = $._roles[roleId].members[account].since == 0;
uint48 since;
if (newMember) {
since = Time.timestamp() + grantDelay;
$._roles[roleId].members[account] = Access({since: since, delay: executionDelay.toDelay()});
} else {
// No setback here. Value can be reset by doing revoke + grant, effectively allowing the admin to perform
// any change to the execution delay within the duration of the role admin delay.
($._roles[roleId].members[account].delay, since) = $._roles[roleId].members[account].delay.withUpdate(
executionDelay,
0
);
}
emit RoleGranted(roleId, account, executionDelay, since, newMember);
return newMember;
}
/**
* @dev Internal version of {revokeRole} without access control. This logic is also used by {renounceRole}.
* Returns true if the role was previously granted.
*
* Emits a {RoleRevoked} event if the account had the role.
*/
function _revokeRole(uint64 roleId, address account) internal virtual returns (bool) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
if (roleId == PUBLIC_ROLE) {
revert AccessManagerLockedRole(roleId);
}
if ($._roles[roleId].members[account].since == 0) {
return false;
}
delete $._roles[roleId].members[account];
emit RoleRevoked(roleId, account);
return true;
}
/**
* @dev Internal version of {setRoleAdmin} without access control.
*
* Emits a {RoleAdminChanged} event.
*
* NOTE: Setting the admin role as the `PUBLIC_ROLE` is allowed, but it will effectively allow
* anyone to set grant or revoke such role.
*/
function _setRoleAdmin(uint64 roleId, uint64 admin) internal virtual {
AccessManagerStorage storage $ = _getAccessManagerStorage();
if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
revert AccessManagerLockedRole(roleId);
}
$._roles[roleId].admin = admin;
emit RoleAdminChanged(roleId, admin);
}
/**
* @dev Internal version of {setRoleGuardian} without access control.
*
* Emits a {RoleGuardianChanged} event.
*
* NOTE: Setting the guardian role as the `PUBLIC_ROLE` is allowed, but it will effectively allow
* anyone to cancel any scheduled operation for such role.
*/
function _setRoleGuardian(uint64 roleId, uint64 guardian) internal virtual {
AccessManagerStorage storage $ = _getAccessManagerStorage();
if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
revert AccessManagerLockedRole(roleId);
}
$._roles[roleId].guardian = guardian;
emit RoleGuardianChanged(roleId, guardian);
}
/**
* @dev Internal version of {setGrantDelay} without access control.
*
* Emits a {RoleGrantDelayChanged} event.
*/
function _setGrantDelay(uint64 roleId, uint32 newDelay) internal virtual {
AccessManagerStorage storage $ = _getAccessManagerStorage();
if (roleId == PUBLIC_ROLE) {
revert AccessManagerLockedRole(roleId);
}
uint48 effect;
($._roles[roleId].grantDelay, effect) = $._roles[roleId].grantDelay.withUpdate(newDelay, minSetback());
emit RoleGrantDelayChanged(roleId, newDelay, effect);
}
// ============================================= FUNCTION MANAGEMENT ==============================================
/// @inheritdoc IAccessManager
function setTargetFunctionRole(
address target,
bytes4[] calldata selectors,
uint64 roleId
) public virtual onlyAuthorized {
for (uint256 i = 0; i < selectors.length; ++i) {
_setTargetFunctionRole(target, selectors[i], roleId);
}
}
/**
* @dev Internal version of {setTargetFunctionRole} without access control.
*
* Emits a {TargetFunctionRoleUpdated} event.
*/
function _setTargetFunctionRole(address target, bytes4 selector, uint64 roleId) internal virtual {
AccessManagerStorage storage $ = _getAccessManagerStorage();
$._targets[target].allowedRoles[selector] = roleId;
emit TargetFunctionRoleUpdated(target, selector, roleId);
}
/// @inheritdoc IAccessManager
function setTargetAdminDelay(address target, uint32 newDelay) public virtual onlyAuthorized {
_setTargetAdminDelay(target, newDelay);
}
/**
* @dev Internal version of {setTargetAdminDelay} without access control.
*
* Emits a {TargetAdminDelayUpdated} event.
*/
function _setTargetAdminDelay(address target, uint32 newDelay) internal virtual {
AccessManagerStorage storage $ = _getAccessManagerStorage();
uint48 effect;
($._targets[target].adminDelay, effect) = $._targets[target].adminDelay.withUpdate(newDelay, minSetback());
emit TargetAdminDelayUpdated(target, newDelay, effect);
}
// =============================================== MODE MANAGEMENT ================================================
/// @inheritdoc IAccessManager
function setTargetClosed(address target, bool closed) public virtual onlyAuthorized {
_setTargetClosed(target, closed);
}
/**
* @dev Set the closed flag for a contract. This is an internal setter with no access restrictions.
*
* Emits a {TargetClosed} event.
*/
function _setTargetClosed(address target, bool closed) internal virtual {
AccessManagerStorage storage $ = _getAccessManagerStorage();
if (target == address(this)) {
revert AccessManagerLockedAccount(target);
}
$._targets[target].closed = closed;
emit TargetClosed(target, closed);
}
// ============================================== DELAYED OPERATIONS ==============================================
/// @inheritdoc IAccessManager
function getSchedule(bytes32 id) public view virtual returns (uint48) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
uint48 timepoint = $._schedules[id].timepoint;
return _isExpired(timepoint) ? 0 : timepoint;
}
/// @inheritdoc IAccessManager
function getNonce(bytes32 id) public view virtual returns (uint32) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
return $._schedules[id].nonce;
}
/// @inheritdoc IAccessManager
function schedule(
address target,
bytes calldata data,
uint48 when
) public virtual returns (bytes32 operationId, uint32 nonce) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
address caller = _msgSender();
// Fetch restrictions that apply to the caller on the targeted function
(, uint32 setback) = _canCallExtended(caller, target, data);
uint48 minWhen = Time.timestamp() + setback;
// if call with delay is not authorized, or if requested timing is too soon
if (setback == 0 || (when > 0 && when < minWhen)) {
revert AccessManagerUnauthorizedCall(caller, target, _checkSelector(data));
}
// Reuse variable due to stack too deep
when = uint48(Math.max(when, minWhen)); // cast is safe: both inputs are uint48
// If caller is authorised, schedule operation
operationId = hashOperation(caller, target, data);
_checkNotScheduled(operationId);
unchecked {
// It's not feasible to overflow the nonce in less than 1000 years
nonce = $._schedules[operationId].nonce + 1;
}
$._schedules[operationId].timepoint = when;
$._schedules[operationId].nonce = nonce;
emit OperationScheduled(operationId, nonce, when, caller, target, data);
// Using named return values because otherwise we get stack too deep
}
/**
* @dev Reverts if the operation is currently scheduled and has not expired.
* (Note: This function was introduced due to stack too deep errors in schedule.)
*/
function _checkNotScheduled(bytes32 operationId) private view {
AccessManagerStorage storage $ = _getAccessManagerStorage();
uint48 prevTimepoint = $._schedules[operationId].timepoint;
if (prevTimepoint != 0 && !_isExpired(prevTimepoint)) {
revert AccessManagerAlreadyScheduled(operationId);
}
}
/// @inheritdoc IAccessManager
// Reentrancy is not an issue because permissions are checked on msg.sender. Additionally,
// _consumeScheduledOp guarantees a scheduled operation is only executed once.
// slither-disable-next-line reentrancy-no-eth
function execute(address target, bytes calldata data) public payable virtual returns (uint32) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
address caller = _msgSender();
// Fetch restrictions that apply to the caller on the targeted function
(bool immediate, uint32 setback) = _canCallExtended(caller, target, data);
// If caller is not authorised, revert
if (!immediate && setback == 0) {
revert AccessManagerUnauthorizedCall(caller, target, _checkSelector(data));
}
bytes32 operationId = hashOperation(caller, target, data);
uint32 nonce;
// If caller is authorised, check operation was scheduled early enough
// Consume an available schedule even if there is no currently enforced delay
if (setback != 0 || getSchedule(operationId) != 0) {
nonce = _consumeScheduledOp(operationId);
}
// Mark the target and selector as authorised
bytes32 executionIdBefore = $._executionId;
$._executionId = _hashExecutionId(target, _checkSelector(data));
// Perform call
Address.functionCallWithValue(target, data, msg.value);
// Reset execute identifier
$._executionId = executionIdBefore;
return nonce;
}
/// @inheritdoc IAccessManager
function cancel(address caller, address target, bytes calldata data) public virtual returns (uint32) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
address msgsender = _msgSender();
bytes4 selector = _checkSelector(data);
bytes32 operationId = hashOperation(caller, target, data);
if ($._schedules[operationId].timepoint == 0) {
revert AccessManagerNotScheduled(operationId);
} else if (caller != msgsender) {
// calls can only be canceled by the account that scheduled them, a global admin, or by a guardian of the required role.
(bool isAdmin, ) = hasRole(ADMIN_ROLE, msgsender);
(bool isGuardian, ) = hasRole(getRoleGuardian(getTargetFunctionRole(target, selector)), msgsender);
if (!isAdmin && !isGuardian) {
revert AccessManagerUnauthorizedCancel(msgsender, caller, target, selector);
}
}
delete $._schedules[operationId].timepoint; // reset the timepoint, keep the nonce
uint32 nonce = $._schedules[operationId].nonce;
emit OperationCanceled(operationId, nonce);
return nonce;
}
/// @inheritdoc IAccessManager
function consumeScheduledOp(address caller, bytes calldata data) public virtual {
address target = _msgSender();
if (IAccessManaged(target).isConsumingScheduledOp() != IAccessManaged.isConsumingScheduledOp.selector) {
revert AccessManagerUnauthorizedConsume(target);
}
_consumeScheduledOp(hashOperation(caller, target, data));
}
/**
* @dev Internal variant of {consumeScheduledOp} that operates on bytes32 operationId.
*
* Returns the nonce of the scheduled operation that is consumed.
*/
function _consumeScheduledOp(bytes32 operationId) internal virtual returns (uint32) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
uint48 timepoint = $._schedules[operationId].timepoint;
uint32 nonce = $._schedules[operationId].nonce;
if (timepoint == 0) {
revert AccessManagerNotScheduled(operationId);
} else if (timepoint > Time.timestamp()) {
revert AccessManagerNotReady(operationId);
} else if (_isExpired(timepoint)) {
revert AccessManagerExpired(operationId);
}
delete $._schedules[operationId].timepoint; // reset the timepoint, keep the nonce
emit OperationExecuted(operationId, nonce);
return nonce;
}
/// @inheritdoc IAccessManager
function hashOperation(address caller, address target, bytes calldata data) public view virtual returns (bytes32) {
return keccak256(abi.encode(caller, target, data));
}
// ==================================================== OTHERS ====================================================
/// @inheritdoc IAccessManager
function updateAuthority(address target, address newAuthority) public virtual onlyAuthorized {
IAccessManaged(target).setAuthority(newAuthority);
}
// ================================================= ADMIN LOGIC ==================================================
/**
* @dev Check if the current call is authorized according to admin logic.
*/
function _checkAuthorized() private {
address caller = _msgSender();
(bool immediate, uint32 delay) = _canCallSelf(caller, _msgData());
if (!immediate) {
if (delay == 0) {
(, uint64 requiredRole, ) = _getAdminRestrictions(_msgData());
revert AccessManagerUnauthorizedAccount(caller, requiredRole);
} else {
_consumeScheduledOp(hashOperation(caller, address(this), _msgData()));
}
}
}
/**
* @dev Get the admin restrictions of a given function call based on the function and arguments involved.
*
* Returns:
* - bool restricted: does this data match a restricted operation
* - uint64: which role is this operation restricted to
* - uint32: minimum delay to enforce for that operation (max between operation's delay and admin's execution delay)
*/
function _getAdminRestrictions(
bytes calldata data
) private view returns (bool restricted, uint64 roleAdminId, uint32 executionDelay) {
if (data.length < 4) {
return (false, 0, 0);
}
bytes4 selector = _checkSelector(data);
// Restricted to ADMIN with no delay beside any execution delay the caller may have
if (
selector == this.labelRole.selector ||
selector == this.setRoleAdmin.selector ||
selector == this.setRoleGuardian.selector ||
selector == this.setGrantDelay.selector ||
selector == this.setTargetAdminDelay.selector
) {
return (true, ADMIN_ROLE, 0);
}
// Restricted to ADMIN with the admin delay corresponding to the target
if (
selector == this.updateAuthority.selector ||
selector == this.setTargetClosed.selector ||
selector == this.setTargetFunctionRole.selector
) {
// First argument is a target.
address target = abi.decode(data[0x04:0x24], (address));
uint32 delay = getTargetAdminDelay(target);
return (true, ADMIN_ROLE, delay);
}
// Restricted to that role's admin with no delay beside any execution delay the caller may have.
if (selector == this.grantRole.selector || selector == this.revokeRole.selector) {
// First argument is a roleId.
uint64 roleId = abi.decode(data[0x04:0x24], (uint64));
return (true, getRoleAdmin(roleId), 0);
}
return (false, 0, 0);
}
// =================================================== HELPERS ====================================================
/**
* @dev An extended version of {canCall} for internal usage that checks {_canCallSelf}
* when the target is this contract.
*
* Returns:
* - bool immediate: whether the operation can be executed immediately (with no delay)
* - uint32 delay: the execution delay
*/
function _canCallExtended(
address caller,
address target,
bytes calldata data
) private view returns (bool immediate, uint32 delay) {
if (target == address(this)) {
return _canCallSelf(caller, data);
} else {
return data.length < 4 ? (false, 0) : canCall(caller, target, _checkSelector(data));
}
}
/**
* @dev A version of {canCall} that checks for admin restrictions in this contract.
*/
function _canCallSelf(address caller, bytes calldata data) private view returns (bool immediate, uint32 delay) {
if (data.length < 4) {
return (false, 0);
}
if (caller == address(this)) {
// Caller is AccessManager, this means the call was sent through {execute} and it already checked
// permissions. We verify that the call "identifier", which is set during {execute}, is correct.
return (_isExecuting(address(this), _checkSelector(data)), 0);
}
(bool enabled, uint64 roleId, uint32 operationDelay) = _getAdminRestrictions(data);
if (!enabled) {
return (false, 0);
}
(bool inRole, uint32 executionDelay) = hasRole(roleId, caller);
if (!inRole) {
return (false, 0);
}
// downcast is safe because both options are uint32
delay = uint32(Math.max(operationDelay, executionDelay));
return (delay == 0, delay);
}
/**
* @dev Returns true if a call with `target` and `selector` is being executed via {executed}.
*/
function _isExecuting(address target, bytes4 selector) private view returns (bool) {
AccessManagerStorage storage $ = _getAccessManagerStorage();
return $._executionId == _hashExecutionId(target, selector);
}
/**
* @dev Returns true if a schedule timepoint is past its expiration deadline.
*/
function _isExpired(uint48 timepoint) private view returns (bool) {
return timepoint + expiration() <= Time.timestamp();
}
/**
* @dev Extracts the selector from calldata. Panics if data is not at least 4 bytes
*/
function _checkSelector(bytes calldata data) private pure returns (bytes4) {
return bytes4(data[0:4]);
}
/**
* @dev Hashing function for execute protection
*/
function _hashExecutionId(address target, bytes4 selector) private pure returns (bytes32) {
return keccak256(abi.encode(target, selector));
}
}// 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 {
$.slot := INITIALIZABLE_STORAGE
}
}
}// 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.0.1) (utils/Multicall.sol)
pragma solidity ^0.8.20;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ContextUpgradeable} from "./ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* Consider any assumption about calldata validation performed by the sender may be violated if it's not especially
* careful about sending transactions invoking {multicall}. For example, a relay address that filters function
* selectors won't filter calls nested within a {multicall} operation.
*
* NOTE: Since 5.0.1 and 4.9.4, this contract identifies non-canonical contexts (i.e. `msg.sender` is not {_msgSender}).
* If a non-canonical context is identified, the following self `delegatecall` appends the last bytes of `msg.data`
* to the subcall. This makes it safe to use with {ERC2771Context}. Contexts that don't affect the resolution of
* {_msgSender} are not propagated to subcalls.
*/
abstract contract MulticallUpgradeable is Initializable, ContextUpgradeable {
function __Multicall_init() internal onlyInitializing {
}
function __Multicall_init_unchained() internal onlyInitializing {
}
/**
* @dev Receives and executes a batch of function calls on this contract.
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
bytes memory context = msg.sender == _msgSender()
? new bytes(0)
: msg.data[msg.data.length - _contextSuffixLength():];
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), bytes.concat(data[i], context));
}
return results;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AccessManaged.sol)
pragma solidity ^0.8.20;
import {IAuthority} from "./IAuthority.sol";
import {AuthorityUtils} from "./AuthorityUtils.sol";
import {IAccessManager} from "./IAccessManager.sol";
import {IAccessManaged} from "./IAccessManaged.sol";
import {Context} from "../../utils/Context.sol";
/**
* @dev This contract module makes available a {restricted} modifier. Functions decorated with this modifier will be
* permissioned according to an "authority": a contract like {AccessManager} that follows the {IAuthority} interface,
* implementing a policy that allows certain callers to access certain functions.
*
* IMPORTANT: The `restricted` modifier should never be used on `internal` functions, judiciously used in `public`
* functions, and ideally only used in `external` functions. See {restricted}.
*/
abstract contract AccessManaged is Context, IAccessManaged {
address private _authority;
bool private _consumingSchedule;
/**
* @dev Initializes the contract connected to an initial authority.
*/
constructor(address initialAuthority) {
_setAuthority(initialAuthority);
}
/**
* @dev Restricts access to a function as defined by the connected Authority for this contract and the
* caller and selector of the function that entered the contract.
*
* [IMPORTANT]
* ====
* In general, this modifier should only be used on `external` functions. It is okay to use it on `public`
* functions that are used as external entry points and are not called internally. Unless you know what you're
* doing, it should never be used on `internal` functions. Failure to follow these rules can have critical security
* implications! This is because the permissions are determined by the function that entered the contract, i.e. the
* function at the bottom of the call stack, and not the function where the modifier is visible in the source code.
* ====
*
* [WARNING]
* ====
* Avoid adding this modifier to the https://docs.soliditylang.org/en/v0.8.20/contracts.html#receive-ether-function[`receive()`]
* function or the https://docs.soliditylang.org/en/v0.8.20/contracts.html#fallback-function[`fallback()`]. These
* functions are the only execution paths where a function selector cannot be unambiguosly determined from the calldata
* since the selector defaults to `0x00000000` in the `receive()` function and similarly in the `fallback()` function
* if no calldata is provided. (See {_checkCanCall}).
*
* The `receive()` function will always panic whereas the `fallback()` may panic depending on the calldata length.
* ====
*/
modifier restricted() {
_checkCanCall(_msgSender(), _msgData());
_;
}
/// @inheritdoc IAccessManaged
function authority() public view virtual returns (address) {
return _authority;
}
/// @inheritdoc IAccessManaged
function setAuthority(address newAuthority) public virtual {
address caller = _msgSender();
if (caller != authority()) {
revert AccessManagedUnauthorized(caller);
}
if (newAuthority.code.length == 0) {
revert AccessManagedInvalidAuthority(newAuthority);
}
_setAuthority(newAuthority);
}
/// @inheritdoc IAccessManaged
function isConsumingScheduledOp() public view returns (bytes4) {
return _consumingSchedule ? this.isConsumingScheduledOp.selector : bytes4(0);
}
/**
* @dev Transfers control to a new authority. Internal function with no access restriction. Allows bypassing the
* permissions set by the current authority.
*/
function _setAuthority(address newAuthority) internal virtual {
_authority = newAuthority;
emit AuthorityUpdated(newAuthority);
}
/**
* @dev Reverts if the caller is not allowed to call the function identified by a selector. Panics if the calldata
* is less than 4 bytes long.
*/
function _checkCanCall(address caller, bytes calldata data) internal virtual {
(bool immediate, uint32 delay) = AuthorityUtils.canCallWithDelay(
authority(),
caller,
address(this),
bytes4(data[0:4])
);
if (!immediate) {
if (delay > 0) {
_consumingSchedule = true;
IAccessManager(authority()).consumeScheduledOp(caller, data);
_consumingSchedule = false;
} else {
revert AccessManagedUnauthorized(caller);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AuthorityUtils.sol)
pragma solidity ^0.8.20;
import {IAuthority} from "./IAuthority.sol";
library AuthorityUtils {
/**
* @dev Since `AccessManager` implements an extended IAuthority interface, invoking `canCall` with backwards compatibility
* for the preexisting `IAuthority` interface requires special care to avoid reverting on insufficient return data.
* This helper function takes care of invoking `canCall` in a backwards compatible way without reverting.
*/
function canCallWithDelay(
address authority,
address caller,
address target,
bytes4 selector
) internal view returns (bool immediate, uint32 delay) {
(bool success, bytes memory data) = authority.staticcall(
abi.encodeCall(IAuthority.canCall, (caller, target, selector))
);
if (success) {
if (data.length >= 0x40) {
(immediate, delay) = abi.decode(data, (bool, uint32));
} else if (data.length >= 0x20) {
immediate = abi.decode(data, (bool));
}
}
return (immediate, delay);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAccessManaged.sol)
pragma solidity ^0.8.20;
interface IAccessManaged {
/**
* @dev Authority that manages this contract was updated.
*/
event AuthorityUpdated(address authority);
error AccessManagedUnauthorized(address caller);
error AccessManagedRequiredDelay(address caller, uint32 delay);
error AccessManagedInvalidAuthority(address authority);
/**
* @dev Returns the current authority.
*/
function authority() external view returns (address);
/**
* @dev Transfers control to a new authority. The caller must be the current authority.
*/
function setAuthority(address) external;
/**
* @dev Returns true only in the context of a delayed restricted call, at the moment that the scheduled operation is
* being consumed. Prevents denial of service for delayed restricted calls in the case that the contract performs
* attacker controlled calls.
*/
function isConsumingScheduledOp() external view returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAccessManager.sol)
pragma solidity ^0.8.20;
import {IAccessManaged} from "./IAccessManaged.sol";
import {Time} from "../../utils/types/Time.sol";
interface IAccessManager {
/**
* @dev A delayed operation was scheduled.
*/
event OperationScheduled(
bytes32 indexed operationId,
uint32 indexed nonce,
uint48 schedule,
address caller,
address target,
bytes data
);
/**
* @dev A scheduled operation was executed.
*/
event OperationExecuted(bytes32 indexed operationId, uint32 indexed nonce);
/**
* @dev A scheduled operation was canceled.
*/
event OperationCanceled(bytes32 indexed operationId, uint32 indexed nonce);
/**
* @dev Informational labelling for a roleId.
*/
event RoleLabel(uint64 indexed roleId, string label);
/**
* @dev Emitted when `account` is granted `roleId`.
*
* NOTE: The meaning of the `since` argument depends on the `newMember` argument.
* If the role is granted to a new member, the `since` argument indicates when the account becomes a member of the role,
* otherwise it indicates the execution delay for this account and roleId is updated.
*/
event RoleGranted(uint64 indexed roleId, address indexed account, uint32 delay, uint48 since, bool newMember);
/**
* @dev Emitted when `account` membership or `roleId` is revoked. Unlike granting, revoking is instantaneous.
*/
event RoleRevoked(uint64 indexed roleId, address indexed account);
/**
* @dev Role acting as admin over a given `roleId` is updated.
*/
event RoleAdminChanged(uint64 indexed roleId, uint64 indexed admin);
/**
* @dev Role acting as guardian over a given `roleId` is updated.
*/
event RoleGuardianChanged(uint64 indexed roleId, uint64 indexed guardian);
/**
* @dev Grant delay for a given `roleId` will be updated to `delay` when `since` is reached.
*/
event RoleGrantDelayChanged(uint64 indexed roleId, uint32 delay, uint48 since);
/**
* @dev Target mode is updated (true = closed, false = open).
*/
event TargetClosed(address indexed target, bool closed);
/**
* @dev Role required to invoke `selector` on `target` is updated to `roleId`.
*/
event TargetFunctionRoleUpdated(address indexed target, bytes4 selector, uint64 indexed roleId);
/**
* @dev Admin delay for a given `target` will be updated to `delay` when `since` is reached.
*/
event TargetAdminDelayUpdated(address indexed target, uint32 delay, uint48 since);
error AccessManagerAlreadyScheduled(bytes32 operationId);
error AccessManagerNotScheduled(bytes32 operationId);
error AccessManagerNotReady(bytes32 operationId);
error AccessManagerExpired(bytes32 operationId);
error AccessManagerLockedAccount(address account);
error AccessManagerLockedRole(uint64 roleId);
error AccessManagerBadConfirmation();
error AccessManagerUnauthorizedAccount(address msgsender, uint64 roleId);
error AccessManagerUnauthorizedCall(address caller, address target, bytes4 selector);
error AccessManagerUnauthorizedConsume(address target);
error AccessManagerUnauthorizedCancel(address msgsender, address caller, address target, bytes4 selector);
error AccessManagerInvalidInitialAdmin(address initialAdmin);
/**
* @dev Check if an address (`caller`) is authorised to call a given function on a given contract directly (with
* no restriction). Additionally, it returns the delay needed to perform the call indirectly through the {schedule}
* & {execute} workflow.
*
* This function is usually called by the targeted contract to control immediate execution of restricted functions.
* Therefore we only return true if the call can be performed without any delay. If the call is subject to a
* previously set delay (not zero), then the function should return false and the caller should schedule the operation
* for future execution.
*
* If `immediate` is true, the delay can be disregarded and the operation can be immediately executed, otherwise
* the operation can be executed if and only if delay is greater than 0.
*
* NOTE: The IAuthority interface does not include the `uint32` delay. This is an extension of that interface that
* is backward compatible. Some contracts may thus ignore the second return argument. In that case they will fail
* to identify the indirect workflow, and will consider calls that require a delay to be forbidden.
*
* NOTE: This function does not report the permissions of this manager itself. These are defined by the
* {_canCallSelf} function instead.
*/
function canCall(
address caller,
address target,
bytes4 selector
) external view returns (bool allowed, uint32 delay);
/**
* @dev Expiration delay for scheduled proposals. Defaults to 1 week.
*
* IMPORTANT: Avoid overriding the expiration with 0. Otherwise every contract proposal will be expired immediately,
* disabling any scheduling usage.
*/
function expiration() external view returns (uint32);
/**
* @dev Minimum setback for all delay updates, with the exception of execution delays. It
* can be increased without setback (and reset via {revokeRole} in the case event of an
* accidental increase). Defaults to 5 days.
*/
function minSetback() external view returns (uint32);
/**
* @dev Get whether the contract is closed disabling any access. Otherwise role permissions are applied.
*/
function isTargetClosed(address target) external view returns (bool);
/**
* @dev Get the role required to call a function.
*/
function getTargetFunctionRole(address target, bytes4 selector) external view returns (uint64);
/**
* @dev Get the admin delay for a target contract. Changes to contract configuration are subject to this delay.
*/
function getTargetAdminDelay(address target) external view returns (uint32);
/**
* @dev Get the id of the role that acts as an admin for the given role.
*
* The admin permission is required to grant the role, revoke the role and update the execution delay to execute
* an operation that is restricted to this role.
*/
function getRoleAdmin(uint64 roleId) external view returns (uint64);
/**
* @dev Get the role that acts as a guardian for a given role.
*
* The guardian permission allows canceling operations that have been scheduled under the role.
*/
function getRoleGuardian(uint64 roleId) external view returns (uint64);
/**
* @dev Get the role current grant delay.
*
* Its value may change at any point without an event emitted following a call to {setGrantDelay}.
* Changes to this value, including effect timepoint are notified in advance by the {RoleGrantDelayChanged} event.
*/
function getRoleGrantDelay(uint64 roleId) external view returns (uint32);
/**
* @dev Get the access details for a given account for a given role. These details include the timepoint at which
* membership becomes active, and the delay applied to all operation by this user that requires this permission
* level.
*
* Returns:
* [0] Timestamp at which the account membership becomes valid. 0 means role is not granted.
* [1] Current execution delay for the account.
* [2] Pending execution delay for the account.
* [3] Timestamp at which the pending execution delay will become active. 0 means no delay update is scheduled.
*/
function getAccess(uint64 roleId, address account) external view returns (uint48, uint32, uint32, uint48);
/**
* @dev Check if a given account currently has the permission level corresponding to a given role. Note that this
* permission might be associated with an execution delay. {getAccess} can provide more details.
*/
function hasRole(uint64 roleId, address account) external view returns (bool, uint32);
/**
* @dev Give a label to a role, for improved role discoverability by UIs.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleLabel} event.
*/
function labelRole(uint64 roleId, string calldata label) external;
/**
* @dev Add `account` to `roleId`, or change its execution delay.
*
* This gives the account the authorization to call any function that is restricted to this role. An optional
* execution delay (in seconds) can be set. If that delay is non 0, the user is required to schedule any operation
* that is restricted to members of this role. The user will only be able to execute the operation after the delay has
* passed, before it has expired. During this period, admin and guardians can cancel the operation (see {cancel}).
*
* If the account has already been granted this role, the execution delay will be updated. This update is not
* immediate and follows the delay rules. For example, if a user currently has a delay of 3 hours, and this is
* called to reduce that delay to 1 hour, the new delay will take some time to take effect, enforcing that any
* operation executed in the 3 hours that follows this update was indeed scheduled before this update.
*
* Requirements:
*
* - the caller must be an admin for the role (see {getRoleAdmin})
* - granted role must not be the `PUBLIC_ROLE`
*
* Emits a {RoleGranted} event.
*/
function grantRole(uint64 roleId, address account, uint32 executionDelay) external;
/**
* @dev Remove an account from a role, with immediate effect. If the account does not have the role, this call has
* no effect.
*
* Requirements:
*
* - the caller must be an admin for the role (see {getRoleAdmin})
* - revoked role must not be the `PUBLIC_ROLE`
*
* Emits a {RoleRevoked} event if the account had the role.
*/
function revokeRole(uint64 roleId, address account) external;
/**
* @dev Renounce role permissions for the calling account with immediate effect. If the sender is not in
* the role this call has no effect.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* Emits a {RoleRevoked} event if the account had the role.
*/
function renounceRole(uint64 roleId, address callerConfirmation) external;
/**
* @dev Change admin role for a given role.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleAdminChanged} event
*/
function setRoleAdmin(uint64 roleId, uint64 admin) external;
/**
* @dev Change guardian role for a given role.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleGuardianChanged} event
*/
function setRoleGuardian(uint64 roleId, uint64 guardian) external;
/**
* @dev Update the delay for granting a `roleId`.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleGrantDelayChanged} event.
*/
function setGrantDelay(uint64 roleId, uint32 newDelay) external;
/**
* @dev Set the role required to call functions identified by the `selectors` in the `target` contract.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {TargetFunctionRoleUpdated} event per selector.
*/
function setTargetFunctionRole(address target, bytes4[] calldata selectors, uint64 roleId) external;
/**
* @dev Set the delay for changing the configuration of a given target contract.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {TargetAdminDelayUpdated} event.
*/
function setTargetAdminDelay(address target, uint32 newDelay) external;
/**
* @dev Set the closed flag for a contract.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {TargetClosed} event.
*/
function setTargetClosed(address target, bool closed) external;
/**
* @dev Return the timepoint at which a scheduled operation will be ready for execution. This returns 0 if the
* operation is not yet scheduled, has expired, was executed, or was canceled.
*/
function getSchedule(bytes32 id) external view returns (uint48);
/**
* @dev Return the nonce for the latest scheduled operation with a given id. Returns 0 if the operation has never
* been scheduled.
*/
function getNonce(bytes32 id) external view returns (uint32);
/**
* @dev Schedule a delayed operation for future execution, and return the operation identifier. It is possible to
* choose the timestamp at which the operation becomes executable as long as it satisfies the execution delays
* required for the caller. The special value zero will automatically set the earliest possible time.
*
* Returns the `operationId` that was scheduled. Since this value is a hash of the parameters, it can reoccur when
* the same parameters are used; if this is relevant, the returned `nonce` can be used to uniquely identify this
* scheduled operation from other occurrences of the same `operationId` in invocations of {execute} and {cancel}.
*
* Emits a {OperationScheduled} event.
*
* NOTE: It is not possible to concurrently schedule more than one operation with the same `target` and `data`. If
* this is necessary, a random byte can be appended to `data` to act as a salt that will be ignored by the target
* contract if it is using standard Solidity ABI encoding.
*/
function schedule(address target, bytes calldata data, uint48 when) external returns (bytes32, uint32);
/**
* @dev Execute a function that is delay restricted, provided it was properly scheduled beforehand, or the
* execution delay is 0.
*
* Returns the nonce that identifies the previously scheduled operation that is executed, or 0 if the
* operation wasn't previously scheduled (if the caller doesn't have an execution delay).
*
* Emits an {OperationExecuted} event only if the call was scheduled and delayed.
*/
function execute(address target, bytes calldata data) external payable returns (uint32);
/**
* @dev Cancel a scheduled (delayed) operation. Returns the nonce that identifies the previously scheduled
* operation that is cancelled.
*
* Requirements:
*
* - the caller must be the proposer, a guardian of the targeted function, or a global admin
*
* Emits a {OperationCanceled} event.
*/
function cancel(address caller, address target, bytes calldata data) external returns (uint32);
/**
* @dev Consume a scheduled operation targeting the caller. If such an operation exists, mark it as consumed
* (emit an {OperationExecuted} event and clean the state). Otherwise, throw an error.
*
* This is useful for contract that want to enforce that calls targeting them were scheduled on the manager,
* with all the verifications that it implies.
*
* Emit a {OperationExecuted} event.
*/
function consumeScheduledOp(address caller, bytes calldata data) external;
/**
* @dev Hashing function for delayed operations.
*/
function hashOperation(address caller, address target, bytes calldata data) external view returns (bytes32);
/**
* @dev Changes the authority of a target managed by this manager instance.
*
* Requirements:
*
* - the caller must be a global admin
*/
function updateAuthority(address target, address newAuthority) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAuthority.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard interface for permissioning originally defined in Dappsys.
*/
interface IAuthority {
/**
* @dev Returns true if the caller can invoke on a target the function identified by a function selector.
*/
function canCall(address caller, address target, bytes4 selector) external view returns (bool allowed);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.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 Ownable is Context {
address private _owner;
/**
* @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.
*/
constructor(address initialOwner) {
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) {
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 {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.20;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Permit.sol)
pragma solidity ^0.8.20;
import {IERC20Permit} from "./IERC20Permit.sol";
import {ERC20} from "../ERC20.sol";
import {ECDSA} from "../../../utils/cryptography/ECDSA.sol";
import {EIP712} from "../../../utils/cryptography/EIP712.sol";
import {Nonces} from "../../../utils/Nonces.sol";
/**
* @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712, Nonces {
bytes32 private constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
/**
* @dev Permit deadline has expired.
*/
error ERC2612ExpiredSignature(uint256 deadline);
/**
* @dev Mismatched signature.
*/
error ERC2612InvalidSigner(address signer, address owner);
/**
* @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
*
* It's a good idea to use the same `name` that is defined as the ERC20 token name.
*/
constructor(string memory name) EIP712(name, "1") {}
/**
* @inheritdoc IERC20Permit
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
if (block.timestamp > deadline) {
revert ERC2612ExpiredSignature(deadline);
}
bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
bytes32 hash = _hashTypedDataV4(structHash);
address signer = ECDSA.recover(hash, v, r, s);
if (signer != owner) {
revert ERC2612InvalidSigner(signer, owner);
}
_approve(owner, spender, value);
}
/**
* @inheritdoc IERC20Permit
*/
function nonces(address owner) public view virtual override(IERC20Permit, Nonces) returns (uint256) {
return super.nonces(owner);
}
/**
* @inheritdoc IERC20Permit
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {
return _domainSeparatorV4();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol)
pragma solidity ^0.8.20;
import {IERC721} from "./IERC721.sol";
import {IERC721Receiver} from "./IERC721Receiver.sol";
import {IERC721Metadata} from "./extensions/IERC721Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {Strings} from "../../utils/Strings.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {IERC721Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/
abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
using Strings for uint256;
// Token name
string private _name;
// Token symbol
string private _symbol;
mapping(uint256 tokenId => address) private _owners;
mapping(address owner => uint256) private _balances;
mapping(uint256 tokenId => address) private _tokenApprovals;
mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual returns (uint256) {
if (owner == address(0)) {
revert ERC721InvalidOwner(address(0));
}
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual returns (address) {
return _requireOwned(tokenId);
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
_requireOwned(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual {
_approve(to, tokenId, _msgSender());
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual returns (address) {
_requireOwned(tokenId);
return _getApproved(tokenId);
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual {
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
// Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
// (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
address previousOwner = _update(to, tokenId, _msgSender());
if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
transferFrom(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
*
* IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
* core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances
* consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
* `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
*/
function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
return _owners[tokenId];
}
/**
* @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
*/
function _getApproved(uint256 tokenId) internal view virtual returns (address) {
return _tokenApprovals[tokenId];
}
/**
* @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
* particular (ignoring whether it is owned by `owner`).
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
return
spender != address(0) &&
(owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
}
/**
* @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
* Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets
* the `spender` for the specific `tokenId`.
*
* WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
* assumption.
*/
function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
if (!_isAuthorized(owner, spender, tokenId)) {
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else {
revert ERC721InsufficientApproval(spender, tokenId);
}
}
}
/**
* @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
*
* NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
* a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
*
* WARNING: Increasing an account's balance using this function tends to be paired with an override of the
* {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
* remain consistent with one another.
*/
function _increaseBalance(address account, uint128 value) internal virtual {
unchecked {
_balances[account] += value;
}
}
/**
* @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
* (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that
* `auth` is either the owner of the token, or approved to operate on the token (by the owner).
*
* Emits a {Transfer} event.
*
* NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
*/
function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
address from = _ownerOf(tokenId);
// Perform (optional) operator check
if (auth != address(0)) {
_checkAuthorized(from, auth, tokenId);
}
// Execute the update
if (from != address(0)) {
// Clear approval. No need to re-authorize or emit the Approval event
_approve(address(0), tokenId, address(0), false);
unchecked {
_balances[from] -= 1;
}
}
if (to != address(0)) {
unchecked {
_balances[to] += 1;
}
}
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
return from;
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner != address(0)) {
revert ERC721InvalidSender(address(0));
}
}
/**
* @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
_checkOnERC721Received(address(0), to, tokenId, data);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
* This is an internal function that does not check if the sender is authorized to operate on the token.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal {
address previousOwner = _update(address(0), tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal {
if (to == address(0)) {
revert ERC721InvalidReceiver(address(0));
}
address previousOwner = _update(to, tokenId, address(0));
if (previousOwner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else if (previousOwner != from) {
revert ERC721IncorrectOwner(from, tokenId, previousOwner);
}
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
* are aware of the ERC721 standard to prevent tokens from being forever locked.
*
* `data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is like {safeTransferFrom} in the sense that it invokes
* {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `tokenId` token must exist and be owned by `from`.
* - `to` cannot be the zero address.
* - `from` cannot be the zero address.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId) internal {
_safeTransfer(from, to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
* either the owner of the token, or approved to operate on all tokens held by this owner.
*
* Emits an {Approval} event.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address to, uint256 tokenId, address auth) internal {
_approve(to, tokenId, auth, true);
}
/**
* @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
* emitted in the context of transfers.
*/
function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
// Avoid reading the owner unless necessary
if (emitEvent || auth != address(0)) {
address owner = _requireOwned(tokenId);
// We do not use _isAuthorized because single-token approvals should not be able to call approve
if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
revert ERC721InvalidApprover(auth);
}
if (emitEvent) {
emit Approval(owner, to, tokenId);
}
}
_tokenApprovals[tokenId] = to;
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Requirements:
* - operator can't be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
if (operator == address(0)) {
revert ERC721InvalidOperator(operator);
}
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
* Returns the owner.
*
* Overrides to ownership logic should be done to {_ownerOf}.
*/
function _requireOwned(uint256 tokenId) internal view returns (address) {
address owner = _ownerOf(tokenId);
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
}
return owner;
}
/**
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
* recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param data bytes optional data to send along with the call
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
if (to.code.length > 0) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
revert ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC721InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/ERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {ERC721} from "../ERC721.sol";
import {IERC721Enumerable} from "./IERC721Enumerable.sol";
import {IERC165} from "../../../utils/introspection/ERC165.sol";
/**
* @dev This implements an optional extension of {ERC721} defined in the EIP that adds enumerability
* of all the token ids in the contract as well as all token ids owned by each account.
*
* CAUTION: `ERC721` extensions that implement custom `balanceOf` logic, such as `ERC721Consecutive`,
* interfere with enumerability and should not be used together with `ERC721Enumerable`.
*/
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
mapping(address owner => mapping(uint256 index => uint256)) private _ownedTokens;
mapping(uint256 tokenId => uint256) private _ownedTokensIndex;
uint256[] private _allTokens;
mapping(uint256 tokenId => uint256) private _allTokensIndex;
/**
* @dev An `owner`'s token query was out of bounds for `index`.
*
* NOTE: The owner being `address(0)` indicates a global out of bounds index.
*/
error ERC721OutOfBoundsIndex(address owner, uint256 index);
/**
* @dev Batch mint is not allowed.
*/
error ERC721EnumerableForbiddenBatchMint();
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual returns (uint256) {
if (index >= balanceOf(owner)) {
revert ERC721OutOfBoundsIndex(owner, index);
}
return _ownedTokens[owner][index];
}
/**
* @dev See {IERC721Enumerable-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _allTokens.length;
}
/**
* @dev See {IERC721Enumerable-tokenByIndex}.
*/
function tokenByIndex(uint256 index) public view virtual returns (uint256) {
if (index >= totalSupply()) {
revert ERC721OutOfBoundsIndex(address(0), index);
}
return _allTokens[index];
}
/**
* @dev See {ERC721-_update}.
*/
function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) {
address previousOwner = super._update(to, tokenId, auth);
if (previousOwner == address(0)) {
_addTokenToAllTokensEnumeration(tokenId);
} else if (previousOwner != to) {
_removeTokenFromOwnerEnumeration(previousOwner, tokenId);
}
if (to == address(0)) {
_removeTokenFromAllTokensEnumeration(tokenId);
} else if (previousOwner != to) {
_addTokenToOwnerEnumeration(to, tokenId);
}
return previousOwner;
}
/**
* @dev Private function to add a token to this extension's ownership-tracking data structures.
* @param to address representing the new owner of the given token ID
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
*/
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
uint256 length = balanceOf(to) - 1;
_ownedTokens[to][length] = tokenId;
_ownedTokensIndex[tokenId] = length;
}
/**
* @dev Private function to add a token to this extension's token tracking data structures.
* @param tokenId uint256 ID of the token to be added to the tokens list
*/
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
/**
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
* while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
* gas optimizations e.g. when performing a transfer operation (avoiding double writes).
* This has O(1) time complexity, but alters the order of the _ownedTokens array.
* @param from address representing the previous owner of the given token ID
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = balanceOf(from);
uint256 tokenIndex = _ownedTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
}
// This also deletes the contents at the last position of the array
delete _ownedTokensIndex[tokenId];
delete _ownedTokens[from][lastTokenIndex];
}
/**
* @dev Private function to remove a token from this extension's token tracking data structures.
* This has O(1) time complexity, but alters the order of the _allTokens array.
* @param tokenId uint256 ID of the token to be removed from the tokens list
*/
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
// To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = _allTokens.length - 1;
uint256 tokenIndex = _allTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
// rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
// an 'if' statement (like in _removeTokenFromOwnerEnumeration)
uint256 lastTokenId = _allTokens[lastTokenIndex];
_allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
// This also deletes the contents at the last position of the array
delete _allTokensIndex[tokenId];
_allTokens.pop();
}
/**
* See {ERC721-_increaseBalance}. We need that to account tokens that were minted in batch
*/
function _increaseBalance(address account, uint128 amount) internal virtual override {
if (amount > 0) {
revert ERC721EnumerableForbiddenBatchMint();
}
super._increaseBalance(account, amount);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @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 AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @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
* {FailedInnerCall} 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 AddressInsufficientBalance(address(this));
}
(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 {FailedInnerCall}) 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 {FailedInnerCall} 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 {FailedInnerCall}.
*/
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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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 Context {
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.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.20;
import {MessageHashUtils} from "./MessageHashUtils.sol";
import {ShortStrings, ShortString} from "../ShortStrings.sol";
import {IERC5267} from "../../interfaces/IERC5267.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
* encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
* does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
* produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
* separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
*
* @custom:oz-upgrades-unsafe-allow state-variable-immutable
*/
abstract contract EIP712 is IERC5267 {
using ShortStrings for *;
bytes32 private constant TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _cachedDomainSeparator;
uint256 private immutable _cachedChainId;
address private immutable _cachedThis;
bytes32 private immutable _hashedName;
bytes32 private immutable _hashedVersion;
ShortString private immutable _name;
ShortString private immutable _version;
string private _nameFallback;
string private _versionFallback;
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
_name = name.toShortStringWithFallback(_nameFallback);
_version = version.toShortStringWithFallback(_versionFallback);
_hashedName = keccak256(bytes(name));
_hashedVersion = keccak256(bytes(version));
_cachedChainId = block.chainid;
_cachedDomainSeparator = _buildDomainSeparator();
_cachedThis = address(this);
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
return _cachedDomainSeparator;
} else {
return _buildDomainSeparator();
}
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev See {IERC-5267}.
*/
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
return (
hex"0f", // 01111
_EIP712Name(),
_EIP712Version(),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/**
* @dev The name parameter for the EIP712 domain.
*
* NOTE: By default this function reads _name which is an immutable value.
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Name() internal view returns (string memory) {
return _name.toStringWithFallback(_nameFallback);
}
/**
* @dev The version parameter for the EIP712 domain.
*
* NOTE: By default this function reads _version which is an immutable value.
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Version() internal view returns (string memory) {
return _version.toStringWithFallback(_versionFallback);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165Checker.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Library used to query support of an interface declared via {IERC165}.
*
* Note that these functions return the actual result of the query: they do not
* `revert` if an interface is not supported. It is up to the caller to decide
* what to do in these cases.
*/
library ERC165Checker {
// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 private constant INTERFACE_ID_INVALID = 0xffffffff;
/**
* @dev Returns true if `account` supports the {IERC165} interface.
*/
function supportsERC165(address account) internal view returns (bool) {
// Any contract that implements ERC165 must explicitly indicate support of
// InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
return
supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
!supportsERC165InterfaceUnchecked(account, INTERFACE_ID_INVALID);
}
/**
* @dev Returns true if `account` supports the interface defined by
* `interfaceId`. Support for {IERC165} itself is queried automatically.
*
* See {IERC165-supportsInterface}.
*/
function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
// query support of both ERC165 as per the spec and support of _interfaceId
return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
}
/**
* @dev Returns a boolean array where each value corresponds to the
* interfaces passed in and whether they're supported or not. This allows
* you to batch check interfaces for a contract where your expectation
* is that some interfaces may not be supported.
*
* See {IERC165-supportsInterface}.
*/
function getSupportedInterfaces(
address account,
bytes4[] memory interfaceIds
) internal view returns (bool[] memory) {
// an array of booleans corresponding to interfaceIds and whether they're supported or not
bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);
// query support of ERC165 itself
if (supportsERC165(account)) {
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
}
}
return interfaceIdsSupported;
}
/**
* @dev Returns true if `account` supports all the interfaces defined in
* `interfaceIds`. Support for {IERC165} itself is queried automatically.
*
* Batch-querying can lead to gas savings by skipping repeated checks for
* {IERC165} support.
*
* See {IERC165-supportsInterface}.
*/
function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
// query support of ERC165 itself
if (!supportsERC165(account)) {
return false;
}
// query support of each interface in interfaceIds
for (uint256 i = 0; i < interfaceIds.length; i++) {
if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
return false;
}
}
// all interfaces supported
return true;
}
/**
* @notice Query if a contract implements an interface, does not check ERC165 support
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return true if the contract at account indicates support of the interface with
* identifier interfaceId, false otherwise
* @dev Assumes that account contains a contract that supports ERC165, otherwise
* the behavior of this method is undefined. This precondition can be checked
* with {supportsERC165}.
*
* Some precompiled contracts will falsely indicate support for a given interface, so caution
* should be exercised when using this function.
*
* Interface identification is specified in ERC-165.
*/
function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
// prepare call
bytes memory encodedParams = abi.encodeCall(IERC165.supportsInterface, (interfaceId));
// perform static call
bool success;
uint256 returnSize;
uint256 returnValue;
assembly {
success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
returnSize := returndatasize()
returnValue := mload(0x00)
}
return success && returnSize >= 0x20 && returnValue > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Nonces.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides tracking nonces for addresses. Nonces will only increment.
*/
abstract contract Nonces {
/**
* @dev The nonce used for an `account` is not the expected current nonce.
*/
error InvalidAccountNonce(address account, uint256 currentNonce);
mapping(address account => uint256) private _nonces;
/**
* @dev Returns the next unused nonce for an address.
*/
function nonces(address owner) public view virtual returns (uint256) {
return _nonces[owner];
}
/**
* @dev Consumes a nonce.
*
* Returns the current value and increments nonce.
*/
function _useNonce(address owner) internal virtual returns (uint256) {
// For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be
// decremented or reset. This guarantees that the nonce never overflows.
unchecked {
// It is important to do x++ and not ++x here.
return _nonces[owner]++;
}
}
/**
* @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`.
*/
function _useCheckedNonce(address owner, uint256 nonce) internal virtual {
uint256 current = _useNonce(owner);
if (nonce != current) {
revert InvalidAccountNonce(owner, current);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ShortStrings.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
/// @solidity memory-safe-assembly
assembly {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using
* {setWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* 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;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 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) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
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) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
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) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/types/Time.sol)
pragma solidity ^0.8.20;
import {Math} from "../math/Math.sol";
import {SafeCast} from "../math/SafeCast.sol";
/**
* @dev This library provides helpers for manipulating time-related objects.
*
* It uses the following types:
* - `uint48` for timepoints
* - `uint32` for durations
*
* While the library doesn't provide specific types for timepoints and duration, it does provide:
* - a `Delay` type to represent duration that can be programmed to change value automatically at a given point
* - additional helper functions
*/
library Time {
using Time for *;
/**
* @dev Get the block timestamp as a Timepoint.
*/
function timestamp() internal view returns (uint48) {
return SafeCast.toUint48(block.timestamp);
}
/**
* @dev Get the block number as a Timepoint.
*/
function blockNumber() internal view returns (uint48) {
return SafeCast.toUint48(block.number);
}
// ==================================================== Delay =====================================================
/**
* @dev A `Delay` is a uint32 duration that can be programmed to change value automatically at a given point in the
* future. The "effect" timepoint describes when the transitions happens from the "old" value to the "new" value.
* This allows updating the delay applied to some operation while keeping some guarantees.
*
* In particular, the {update} function guarantees that if the delay is reduced, the old delay still applies for
* some time. For example if the delay is currently 7 days to do an upgrade, the admin should not be able to set
* the delay to 0 and upgrade immediately. If the admin wants to reduce the delay, the old delay (7 days) should
* still apply for some time.
*
*
* The `Delay` type is 112 bits long, and packs the following:
*
* ```
* | [uint48]: effect date (timepoint)
* | | [uint32]: value before (duration)
* ↓ ↓ ↓ [uint32]: value after (duration)
* 0xAAAAAAAAAAAABBBBBBBBCCCCCCCC
* ```
*
* NOTE: The {get} and {withUpdate} functions operate using timestamps. Block number based delays are not currently
* supported.
*/
type Delay is uint112;
/**
* @dev Wrap a duration into a Delay to add the one-step "update in the future" feature
*/
function toDelay(uint32 duration) internal pure returns (Delay) {
return Delay.wrap(duration);
}
/**
* @dev Get the value at a given timepoint plus the pending value and effect timepoint if there is a scheduled
* change after this timepoint. If the effect timepoint is 0, then the pending value should not be considered.
*/
function _getFullAt(Delay self, uint48 timepoint) private pure returns (uint32, uint32, uint48) {
(uint32 valueBefore, uint32 valueAfter, uint48 effect) = self.unpack();
return effect <= timepoint ? (valueAfter, 0, 0) : (valueBefore, valueAfter, effect);
}
/**
* @dev Get the current value plus the pending value and effect timepoint if there is a scheduled change. If the
* effect timepoint is 0, then the pending value should not be considered.
*/
function getFull(Delay self) internal view returns (uint32, uint32, uint48) {
return _getFullAt(self, timestamp());
}
/**
* @dev Get the current value.
*/
function get(Delay self) internal view returns (uint32) {
(uint32 delay, , ) = self.getFull();
return delay;
}
/**
* @dev Update a Delay object so that it takes a new duration after a timepoint that is automatically computed to
* enforce the old delay at the moment of the update. Returns the updated Delay object and the timestamp when the
* new delay becomes effective.
*/
function withUpdate(
Delay self,
uint32 newValue,
uint32 minSetback
) internal view returns (Delay updatedDelay, uint48 effect) {
uint32 value = self.get();
uint32 setback = uint32(Math.max(minSetback, value > newValue ? value - newValue : 0));
effect = timestamp() + setback;
return (pack(value, newValue, effect), effect);
}
/**
* @dev Split a delay into its components: valueBefore, valueAfter and effect (transition timepoint).
*/
function unpack(Delay self) internal pure returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) {
uint112 raw = Delay.unwrap(self);
valueAfter = uint32(raw);
valueBefore = uint32(raw >> 32);
effect = uint48(raw >> 64);
return (valueBefore, valueAfter, effect);
}
/**
* @dev pack the components into a Delay object.
*/
function pack(uint32 valueBefore, uint32 valueAfter, uint48 effect) internal pure returns (Delay) {
return Delay.wrap((uint112(effect) << 64) | (uint112(valueBefore) << 32) | uint112(valueAfter));
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IAccess} from "./IAccess.sol";
import {IAccessAdmin} from "./IAccessAdmin.sol";
import {IAuthorization} from "./IAuthorization.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IServiceAuthorization} from "./IServiceAuthorization.sol";
import {AccessAdminLib} from "./AccessAdminLib.sol";
import {AccessManagerCloneable} from "./AccessManagerCloneable.sol";
import {Blocknumber, BlocknumberLib} from "../type/Blocknumber.sol";
import {ContractLib} from "../shared/ContractLib.sol";
import {NftId, NftIdLib} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RoleId, RoleIdLib, ADMIN_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol";
import {Selector, SelectorSetLib} from "../type/Selector.sol";
import {Str, StrLib} from "../type/String.sol";
import {TimestampLib} from "../type/Timestamp.sol";
import {VersionPart} from "../type/Version.sol";
/**
* @dev A generic access amin contract that implements role based access control based on OpenZeppelin's AccessManager contract.
* The contract provides read functions to query all available roles, targets and access rights.
* This contract works for both a constructor based deployment or a deployment based on cloning and initialization.
*/
contract AccessAdmin is
AccessManagedUpgradeable,
IAccessAdmin
{
using EnumerableSet for EnumerableSet.AddressSet;
/// @dev admin name used for logging only
string internal _adminName;
/// @dev the access manager driving the access admin contract
/// hold link to registry and release version
AccessManagerCloneable internal _authority;
/// @dev the authorization contract used for initial access control
IAuthorization internal _authorization;
// /// @dev stores the deployer address and allows to create initializers
// /// that are restricted to the deployer address.
// address internal _deployer;
/// @dev the linked NFT ID
NftId internal _linkedNftId;
/// @dev store role info per role id
mapping(RoleId roleId => RoleInfo info) internal _roleInfo;
/// @dev store role name info per role name
mapping(Str roleName => RoleNameInfo nameInfo) internal _roleForName;
/// @dev store array with all created roles
RoleId [] internal _roleIds;
// @dev target type specific role id counters
mapping(TargetType => uint64) internal _nextRoleId;
/// @dev store set of current role members for given role
mapping(RoleId roleId => EnumerableSet.AddressSet roleMembers) internal _roleMembers;
/// @dev store target info per target address
mapping(address target => TargetInfo info) internal _targetInfo;
/// @dev store role name info per role name
mapping(Str targetName => address target) internal _targetForName;
/// @dev store array with all created targets
address [] internal _targets;
/// @dev store all managed functions per target
mapping(address target => SelectorSetLib.Set selectors) internal _targetFunctions;
/// @dev function infos array
mapping(address target => mapping(Selector selector => FunctionInfo)) internal _functionInfo;
/// @dev temporary dynamic functions array
bytes4[] private _functions;
//-------------- initialization functions ------------------------------//
/// @dev Initializes this admin with the provided accessManager (and authorization specification).
/// Internally initializes access manager with this admin and creates basic role setup.
function initialize(
address authority,
string memory adminName
)
public
initializer()
{
__AccessAdmin_init(authority, adminName);
}
/// @dev Initializes this admin with the provided accessManager and name.
/// IMPORTANT
/// - cloning of an access admin and initialization MUST be done in the same tx.
/// - this function as well as any completeSetup functions MUST be called in the same tx.
function __AccessAdmin_init(
address authority,
string memory adminName
)
internal
onlyInitializing()
{
AccessAdminLib.checkInitParameters(authority, adminName);
_authority = AccessManagerCloneable(authority);
_authority.initialize(address(this));
// delayed additional check for authority after its initialization
if (!ContractLib.isAuthority(authority)) {
revert ErrorAccessAdminAccessManagerNotAccessManager(authority);
}
// effects
// set and initialize this access manager contract as
// the admin (ADMIN_ROLE) of the provided authority
__AccessManaged_init(authority);
// set name for logging
_adminName = adminName;
// set initial linked NFT ID to zero
_linkedNftId = NftIdLib.zero();
// setup admin role
_createRoleUnchecked(
ADMIN_ROLE(), AccessAdminLib.adminRoleInfo());
// add this contract as admin role member, as contract roles cannot be revoked
// and max member count is 1 for admin role this access admin contract will
// always be the only admin of the access manager.
_roleMembers[
RoleIdLib.toRoleId(_authority.ADMIN_ROLE())].add(address(this));
// setup public role
_createRoleUnchecked(
PUBLIC_ROLE(), AccessAdminLib.publicRoleInfo());
}
//--- view functions for access admin ---------------------------------------//
function getRelease() public view virtual returns (VersionPart release) {
return _authority.getRelease();
}
function getRegistry() public view returns (IRegistry registry) {
return _authority.getRegistry();
}
function getLinkedNftId() external view returns (NftId linkedNftId) {
return _linkedNftId;
}
function getAuthorization() public view returns (IAuthorization authorization) {
return _authorization;
}
function isLocked() public view returns (bool locked) {
return _authority.isLocked();
}
//--- view functions for roles ------------------------------------------//
function roles() external view returns (uint256 numberOfRoles) {
return _roleIds.length;
}
function getRoleId(uint256 idx) external view returns (RoleId roleId) {
return _roleIds[idx];
}
function getAdminRole() public view returns (RoleId roleId) {
return RoleId.wrap(_authority.ADMIN_ROLE());
}
function getPublicRole() public view returns (RoleId roleId) {
return RoleId.wrap(_authority.PUBLIC_ROLE());
}
function roleExists(RoleId roleId) public view returns (bool exists) {
return _roleInfo[roleId].targetType != TargetType.Undefined;
}
function getRoleForName(string memory name) public view returns (RoleId roleId, bool exists) {
roleId = _roleForName[StrLib.toStr(name)].roleId;
exists = false;
if (roleId.gtz() || AccessAdminLib.isAdminRoleName(name)) {
exists = true;
}
}
function getRoleInfo(RoleId roleId) public view returns (RoleInfo memory) {
return _roleInfo[roleId];
}
function isRoleActive(RoleId roleId) external view returns (bool isActive) {
return _roleInfo[roleId].pausedAt > TimestampLib.current();
}
function isRoleCustom(RoleId roleId) external view returns (bool isActive) {
return _roleInfo[roleId].targetType == TargetType.Custom;
}
function roleMembers(RoleId roleId) external view returns (uint256 numberOfMembers) {
return _roleMembers[roleId].length();
}
function getRoleMember(RoleId roleId, uint256 idx) external view returns (address account) {
return _roleMembers[roleId].at(idx);
}
function isRoleMember(RoleId roleId, address account) public view returns (bool) {
(bool isMember, ) = _authority.hasRole(
RoleId.unwrap(roleId),
account);
return isMember;
}
function isRoleAdmin(RoleId roleId, address account) public virtual view returns (bool) {
return isRoleMember(_roleInfo[roleId].adminRoleId, account);
}
//--- view functions for targets ----------------------------------------//
function targetExists(address target) public view returns (bool exists) {
return _targetInfo[target].createdAt.gtz();
}
function targets() external view returns (uint256 numberOfTargets) {
return _targets.length;
}
function getTargetAddress(uint256 idx) external view returns (address target) {
return _targets[idx];
}
function getTargetInfo(address target) public view returns (TargetInfo memory targetInfo) {
return _targetInfo[target];
}
function getTargetForName(Str name) public view returns (address target) {
return _targetForName[name];
}
function isTargetLocked(address target) public view returns (bool locked) {
return _authority.isLocked() || _authority.isTargetClosed(target);
}
//--- view functions for target functions -------------------------------//
function authorizedFunctions(address target) external view returns (uint256 numberOfFunctions) {
return SelectorSetLib.size(_targetFunctions[target]);
}
function getAuthorizedFunction(
address target,
uint256 idx
)
external
view
returns (
FunctionInfo memory func,
RoleId roleId
)
{
Selector selector = SelectorSetLib.at(_targetFunctions[target], idx);
func = _functionInfo[target][selector];
roleId = AccessAdminLib.getFunctionRoleId(_authority, target, selector);
// roleId = RoleIdLib.toRoleId(
// _authority.getTargetFunctionRole(
// target,
// selector.toBytes4()));
}
function getFunctionInfo(address target, Selector selector)
external
view
returns (FunctionInfo memory functionInfo)
{
return _functionInfo[target][selector];
}
//--- internal/private functions -------------------------------------------------//
function _linkToNftOwnable(address registerable) internal {
if (!getRegistry().isRegistered(registerable)) {
revert ErrorAccessAdminNotRegistered(registerable);
}
_linkedNftId = getRegistry().getNftIdForAddress(registerable);
}
function _createRoles(
IServiceAuthorization authorization
)
internal
{
RoleId[] memory roles = authorization.getRoles();
for(uint256 i = 0; i < roles.length; i++) {
RoleId authzRoleId = roles[i];
IAccess.RoleInfo memory roleInfo = authorization.getRoleInfo(authzRoleId);
(RoleId roleId, bool exists) = getRoleForName(roleInfo.name.toString());
if (!exists) {
if (!AccessAdminLib.isDynamicRoleId(authzRoleId) || roleInfo.targetType == TargetType.Custom) {
roleId = authzRoleId;
}
_createRole(
roleId,
roleInfo,
true);
}
}
}
/// @dev Creates a role based on the provided parameters.
/// Checks that the provided role and role id and role name not already used.
function _createRole(
RoleId roleId,
RoleInfo memory info,
bool revertOnExistingRole
)
internal
{
bool isAdminOrPublicRole = AccessAdminLib.checkRoleCreation(this, roleId, info, revertOnExistingRole);
if (!isAdminOrPublicRole) {
_createRoleUnchecked(roleId, info);
}
}
function _createRoleUnchecked(
RoleId roleId,
RoleInfo memory info
)
private
{
// create role info
info.createdAt = TimestampLib.current();
info.pausedAt = TimestampLib.max();
_roleInfo[roleId] = info;
// create role name info
_roleForName[info.name] = RoleNameInfo({
roleId: roleId,
exists: true});
// add role to list of roles
_roleIds.push(roleId);
emit LogAccessAdminRoleCreated(_adminName, roleId, info.targetType, info.adminRoleId, info.name.toString());
}
/// @dev Activates or deactivates role.
/// The role activ property is indirectly controlled over the pausedAt timestamp.
function _setRoleActive(RoleId roleId, bool active)
internal
{
AccessAdminLib.checkRoleExists(this, roleId, false, false);
if (active) {
_roleInfo[roleId].pausedAt = TimestampLib.max();
} else {
_roleInfo[roleId].pausedAt = TimestampLib.current();
}
Blocknumber lastUpdateIn = _roleInfo[roleId].lastUpdateIn;
_roleInfo[roleId].lastUpdateIn = BlocknumberLib.current();
emit LogAccessAdminRoleActivatedSet(_adminName, roleId, active, lastUpdateIn);
}
/// @dev grant the specified role to the provided account
function _grantRoleToAccount(RoleId roleId, address account)
internal
{
AccessAdminLib.checkRoleExists(this, roleId, true, false);
// check max role members will not be exceeded
if (_roleMembers[roleId].length() >= _roleInfo[roleId].maxMemberCount) {
revert ErrorAccessAdminRoleMembersLimitReached(roleId, _roleInfo[roleId].maxMemberCount);
}
// check account is contract for contract role
if (
_roleInfo[roleId].targetType != TargetType.Custom &&
!ContractLib.isContract(account) // will fail in account's constructor
) {
revert ErrorAccessAdminRoleMemberNotContract(roleId, account);
}
// effects
_roleMembers[roleId].add(account);
_authority.grantRole(
RoleId.unwrap(roleId),
account,
0);
emit LogAccessAdminRoleGranted(
_adminName,
account,
AccessAdminLib.getRoleName(this, roleId));
}
/// @dev revoke the specified role from the provided account
function _revokeRoleFromAccount(RoleId roleId, address account)
internal
{
AccessAdminLib.checkRoleExists(this, roleId, false, false);
// check for attempt to revoke contract role
if (_roleInfo[roleId].targetType != TargetType.Custom) {
revert ErrorAccessAdminRoleMemberRemovalDisabled(roleId, account);
}
// effects
_roleMembers[roleId].remove(account);
_authority.revokeRole(
RoleId.unwrap(roleId),
account);
emit LogAccessAdminRoleRevoked(_adminName, account, _roleInfo[roleId].name.toString());
}
function _getOrCreateTargetRoleIdAndName(
address target,
string memory targetName,
TargetType targetType
)
internal
returns (
RoleId roleId,
string memory roleName,
bool exists
)
{
roleName = AccessAdminLib.toRoleName(targetName);
(roleId, exists) = getRoleForName(roleName);
if (exists) {
return (roleId, roleName, true);
}
// get roleId
if (targetType == TargetType.Service || targetType == TargetType.GenericService) {
roleId = AccessAdminLib.getServiceRoleId(target, targetType);
} else {
uint64 nextRoleId = _nextRoleId[targetType];
roleId = AccessAdminLib.getTargetRoleId(target, targetType, nextRoleId);
// increment target type specific role id counter
_nextRoleId[targetType]++;
}
}
function _createTarget(
address target,
string memory targetName,
TargetType targetType,
bool checkAuthority
)
internal
returns (RoleId contractRoleId)
{
// checks
AccessAdminLib.checkTargetCreation(this, target, targetName, checkAuthority);
// effects
contractRoleId = _createTargetUnchecked(
target,
targetName,
targetType,
checkAuthority);
// deal with token handler, if applicable
(
address tokenHandler,
string memory tokenHandlerName
) = AccessAdminLib.getTokenHandler(target, targetName, targetType);
if (tokenHandler != address(0)) {
_createTargetUnchecked(
tokenHandler,
tokenHandlerName,
targetType,
checkAuthority);
}
}
/// @dev Creates a new target and a corresponding contract role.
/// The function assigns the role to the target and logs the creation.
function _createTargetUnchecked(
address target,
string memory targetName,
TargetType targetType,
bool managed
)
internal
returns (RoleId targetRoleId)
{
// create target role (if not existing)
string memory roleName;
bool roleExists;
(targetRoleId, roleName, roleExists) = _getOrCreateTargetRoleIdAndName(target, targetName, targetType);
if (!roleExists) {
_createRole(
targetRoleId,
AccessAdminLib.roleInfo(
ADMIN_ROLE(),
targetType,
1,
roleName),
true); // revert on existing role
}
// create target info
Str name = StrLib.toStr(targetName);
_targetInfo[target] = TargetInfo({
name: name,
targetType: targetType,
roleId: targetRoleId,
createdAt: TimestampLib.current(),
lastUpdateIn: BlocknumberLib.current()});
// create name to target mapping
_targetForName[name] = target;
// add target to list of targets
_targets.push(target);
// grant contract role to target
_grantRoleToAccount(targetRoleId, target);
emit LogAccessAdminTargetCreated(_adminName, targetName, managed, target, targetRoleId);
}
function _setTargetLocked(address target, bool locked)
internal
{
AccessAdminLib.checkTargetExists(this, target);
_authority.setTargetClosed(target, locked);
// logging
Blocknumber lastUpdateIn = _targetInfo[target].lastUpdateIn;
_targetInfo[target].lastUpdateIn = BlocknumberLib.current();
emit LogAccessAdminTargetLockedSet(_adminName, target, locked, lastUpdateIn);
}
/// @dev Authorize the functions of the target for the specified role.
function _authorizeFunctions(IAuthorization authorization, Str target, RoleId roleId)
internal
{
_authorizeTargetFunctions(
getTargetForName(target),
AccessAdminLib.getAuthorizedRole(
this,
authorization,
roleId),
authorization.getAuthorizedFunctions(
target,
roleId),
false,
true);
}
/// @dev Authorize the functions of the target for the specified role.
/// Flag addFunctions determines if functions are added or removed.
function _authorizeTargetFunctions(
address target,
RoleId roleId,
FunctionInfo[] memory functions,
bool onlyComponentOrContractTargets,
bool addFunctions
)
internal
{
// checks
AccessAdminLib.checkTargetAndRoleForFunctions(
this,
target,
roleId,
onlyComponentOrContractTargets);
if (addFunctions && roleId == getAdminRole()) {
revert ErrorAccessAdminAuthorizeForAdminRoleInvalid(target);
}
_authority.setTargetFunctionRole(
target,
AccessAdminLib.getSelectors(functions),
RoleId.unwrap(roleId));
// update function set and log function grantings
for (uint256 i = 0; i < functions.length; i++) {
_updateFunctionAccess(
target,
roleId,
functions[i],
addFunctions);
}
}
function _updateFunctionAccess(
address target,
RoleId roleId,
FunctionInfo memory func,
bool addFunction
)
internal
{
// update functions info
Selector selector = func.selector;
Blocknumber lastUpdateIn = _functionInfo[target][selector].lastUpdateIn;
// update function sets
if (addFunction) { SelectorSetLib.add(_targetFunctions[target], selector); }
else { SelectorSetLib.remove(_targetFunctions[target], selector); }
_functionInfo[target][selector] = func;
_functionInfo[target][selector].lastUpdateIn = BlocknumberLib.current();
// logging
emit LogAccessAdminFunctionGranted(
_adminName,
target,
AccessAdminLib.toFunctionGrantingString(this, func.name, roleId),
lastUpdateIn);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import {IAccess} from "./IAccess.sol";
import {IAccessAdmin} from "./IAccessAdmin.sol";
import {IAuthorization} from "./IAuthorization.sol";
import {IComponent} from "../shared/IComponent.sol";
import {IAuthorizedComponent} from "../shared/IAuthorizedComponent.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IService} from "../shared/IService.sol";
import {IServiceAuthorization} from "./IServiceAuthorization.sol";
import {AccessManagerCloneable} from "./AccessManagerCloneable.sol";
import {BlocknumberLib} from "../type/Blocknumber.sol";
import {ContractLib} from "../shared/ContractLib.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RoleId, RoleIdLib, ADMIN_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol";
import {Selector, SelectorLib} from "../type/Selector.sol";
import {Str, StrLib} from "../type/String.sol";
import {TimestampLib} from "../type/Timestamp.sol";
import {VersionPart, VersionPartLib} from "../type/Version.sol";
library AccessAdminLib { // ACCESS_ADMIN_LIB
string public constant TOKEN_HANDLER_SUFFIX = "Th";
string public constant ROLE_SUFFIX = "_Role";
uint64 public constant SERVICE_DOMAIN_ROLE_FACTOR = 100;
uint64 public constant COMPONENT_ROLE_FACTOR = 1000;
uint64 public constant COMPONENT_ROLE_MAX = 19000;
uint64 public constant CORE_ROLE_MIN = 100;
uint64 public constant SERVICE_ROLE_MIN = 1000; // + service domain * SERVICE_ROLE_FACTOR + release
uint64 public constant SERVICE_ROLE_FACTOR = 1000;
uint64 public constant INSTANCE_ROLE_MIN = 100000;
// MUST match with Authorization.COMPONENT_ROLE_MIN
uint64 public constant COMPONENT_ROLE_MIN = 110000;
uint64 public constant CUSTOM_ROLE_MIN = 1000000;
function ADMIN_ROLE_NAME() public pure returns (string memory) {
return "AdminRole";
}
function isAdminRoleName(string memory name) public pure returns (bool) {
return StrLib.eq(name, ADMIN_ROLE_NAME());
}
function PUBLIC_ROLE_NAME() public pure returns (string memory) {
return "PublicRole";
}
function getAdminRole() public pure returns (RoleId adminRoleId) {
// see oz AccessManagerUpgradeable
return RoleId.wrap(type(uint64).min);
}
function getPublicRole() public pure returns (RoleId publicRoleId) {
// see oz AccessManagerUpgradeable
return RoleId.wrap(type(uint64).max);
}
function isAdminOrPublicRole(string memory name)
public
view
returns (bool)
{
return StrLib.eq(name, ADMIN_ROLE_NAME())
|| StrLib.eq(name, PUBLIC_ROLE_NAME());
}
function isDynamicRoleId(RoleId roleId)
public
pure
returns (bool)
{
return roleId.toInt() >= COMPONENT_ROLE_MIN;
}
function adminRoleInfo()
public
view
returns (IAccess.RoleInfo memory)
{
return roleInfo(
getAdminRole(),
IAccess.TargetType.Core,
1,
ADMIN_ROLE_NAME());
}
function publicRoleInfo()
public
view
returns (IAccess.RoleInfo memory)
{
return roleInfo(
getAdminRole(),
IAccess.TargetType.Custom,
type(uint32).max,
PUBLIC_ROLE_NAME());
}
function coreRoleInfo(string memory name)
public
view
returns (IAccess.RoleInfo memory)
{
return roleInfo(
getAdminRole(),
IAccess.TargetType.Core,
1,
name);
}
function serviceRoleInfo(string memory serviceName)
public
view
returns (IAccess.RoleInfo memory)
{
return roleInfo(
getAdminRole(),
IAccess.TargetType.Service,
1,
serviceName);
}
/// @dev Creates a role info object from the provided parameters
function roleInfo(
RoleId adminRoleId,
IAccess.TargetType targetType,
uint32 maxMemberCount,
string memory roleName
)
public
view
returns (IAccess.RoleInfo memory info)
{
return IAccess.RoleInfo({
name: StrLib.toStr(roleName),
adminRoleId: adminRoleId,
targetType: targetType,
maxMemberCount: maxMemberCount,
createdAt: TimestampLib.current(),
pausedAt: TimestampLib.max(),
lastUpdateIn: BlocknumberLib.current()});
}
function getSelectors(
IAccess.FunctionInfo[] memory functions
)
public
pure
returns (
bytes4[] memory selectors
)
{
uint256 n = functions.length;
selectors = new bytes4[](n);
for (uint256 i = 0; i < n; i++) {
selectors[i] = functions[i].selector.toBytes4();
}
}
function checkInitParameters(
address authority,
string memory adminName
)
public
view
{
// only contract check (authority might not yet be initialized at this time)
if (!ContractLib.isContract(authority)) {
revert IAccessAdmin.ErrorAccessAdminAuthorityNotContract(authority);
}
// check name not empty
if (bytes(adminName).length == 0) {
revert IAccessAdmin.ErrorAccessAdminAccessManagerEmptyName();
}
}
function checkRoleCreation(
IAccessAdmin accessAdmin,
RoleId roleId,
IAccess.RoleInfo memory info,
bool revertOnExistingRole
)
public
view
returns (bool isAdminOrPublicRole)
{
// check
if (roleId == ADMIN_ROLE() || roleId == PUBLIC_ROLE()) {
return true;
}
// check role does not yet exist
if(revertOnExistingRole && accessAdmin.roleExists(roleId)) {
revert IAccessAdmin.ErrorAccessAdminRoleAlreadyCreated(
roleId,
accessAdmin.getRoleInfo(roleId).name.toString());
}
// check admin role exists
if(!accessAdmin.roleExists(info.adminRoleId)) {
revert IAccessAdmin.ErrorAccessAdminRoleAdminNotExisting(info.adminRoleId);
}
// check role name is not empty
if(info.name.length() == 0) {
revert IAccessAdmin.ErrorAccessAdminRoleNameEmpty(roleId);
}
// check role name is not used for another role
(RoleId roleIdForName, bool exists) = accessAdmin.getRoleForName(StrLib.toString(info.name));
if(revertOnExistingRole && exists) {
revert IAccessAdmin.ErrorAccessAdminRoleNameAlreadyExists(
roleId,
info.name.toString(),
roleIdForName);
}
return false;
}
function checkRoleExists(
IAccessAdmin accessAdmin,
RoleId roleId,
bool onlyActiveRole,
bool allowAdminAndPublicRoles
)
internal
view
{
// check role exists
if (!accessAdmin.roleExists(roleId)) {
revert IAccessAdmin.ErrorAccessAdminRoleUnknown(roleId);
}
// if onlyActiveRoles: check if role is disabled
if (onlyActiveRole && accessAdmin.getRoleInfo(roleId).pausedAt <= TimestampLib.current()) {
revert IAccessAdmin.ErrorAccessAdminRoleIsPaused(roleId);
}
// if not allowAdminAndPublicRoles, check if role is admin or public role
if (!allowAdminAndPublicRoles) {
checkNotAdminOrPublicRole(roleId);
}
}
function checkNotAdminOrPublicRole(RoleId roleId) public pure {
if (roleId == ADMIN_ROLE()) {
revert IAccessAdmin.ErrorAccessAdminInvalidUseOfAdminRole();
}
if (roleId == PUBLIC_ROLE()) {
revert IAccessAdmin.ErrorAccessAdminInvalidUseOfPublicRole();
}
}
function checkTargetCreation(
IAccessAdmin accessAdmin,
address target,
string memory targetName,
bool checkAuthority
)
public
view
{
// check target does not yet exist
if(accessAdmin.targetExists(target)) {
revert IAccessAdmin.ErrorAccessAdminTargetAlreadyCreated(
target,
accessAdmin.getTargetInfo(target).name.toString());
}
// check target name is not empty
Str name = StrLib.toStr(targetName);
if(name.length() == 0) {
revert IAccessAdmin.ErrorAccessAdminTargetNameEmpty(target);
}
// check target name is not used for another target
address targetForName = accessAdmin.getTargetForName(name);
if(targetForName != address(0)) {
revert IAccessAdmin.ErrorAccessAdminTargetNameAlreadyExists(
target,
targetName,
targetForName);
}
// check target is an access managed contract
if (!ContractLib.isAccessManaged(target)) {
revert IAccessAdmin.ErrorAccessAdminTargetNotAccessManaged(target);
}
// check target shares authority with this contract
if (checkAuthority) {
address targetAuthority = AccessManagedUpgradeable(target).authority();
if (targetAuthority != accessAdmin.authority()) {
revert IAccessAdmin.ErrorAccessAdminTargetAuthorityMismatch(accessAdmin.authority(), targetAuthority);
}
}
}
function checkComponentInitialization(
IAccessAdmin accessAdmin,
IAuthorization instanceAuthorization,
address componentAddress,
ObjectType expectedType
)
public
view
returns (IAuthorization componentAuthorization)
{
checkIsRegistered(address(accessAdmin.getRegistry()), componentAddress, expectedType);
VersionPart expecteRelease = accessAdmin.getRelease();
IAuthorizedComponent component = IAuthorizedComponent(componentAddress);
componentAuthorization = component.getAuthorization();
checkAuthorization(
address(instanceAuthorization),
address(componentAuthorization),
expectedType,
expecteRelease,
false, // expectServiceAuthorization,
false); // checkAlreadyInitialized
}
function checkTargetAndRoleForFunctions(
IAccessAdmin accessAdmin,
address target,
RoleId roleId,
bool onlyComponentOrContractTargets
)
public
view
{
// check target exists
IAccess.TargetType targetType = accessAdmin.getTargetInfo(target).targetType;
if (targetType == IAccess.TargetType.Undefined) {
revert IAccessAdmin.ErrorAccessAdminTargetNotCreated(target);
}
// check target type
if (onlyComponentOrContractTargets) {
if (targetType != IAccess.TargetType.Component && targetType != IAccess.TargetType.Contract) {
revert IAccessAdmin.ErrorAccessAdminNotComponentOrCustomTarget(target);
}
}
// check role exist
checkRoleExists(accessAdmin, roleId, true, true);
}
function checkTargetExists(
IAccessAdmin accessAdmin,
address target
)
public
view
{
// check not yet created
if (!accessAdmin.targetExists(target)) {
revert IAccessAdmin.ErrorAccessAdminTargetNotCreated(target);
}
}
function checkAuthorization(
address authorizationOld,
address authorization,
ObjectType expectedDomain,
VersionPart expectedRelease,
bool expectServiceAuthorization,
bool checkAlreadyInitialized
)
public
view
{
// checks
// check not yet initialized
if (checkAlreadyInitialized && authorizationOld != address(0)) {
revert IAccessAdmin.ErrorAccessAdminAlreadyInitialized(authorizationOld);
}
// check contract type matches
if (expectServiceAuthorization) {
if (!ContractLib.supportsInterface(authorization, type(IServiceAuthorization).interfaceId)) {
revert IAccessAdmin.ErrorAccessAdminNotServiceAuthorization(authorization);
}
} else {
if (!ContractLib.supportsInterface(authorization, type(IAuthorization).interfaceId)) {
revert IAccessAdmin.ErrorAccessAdminNotAuthorization(authorization);
}
}
// check domain matches
ObjectType domain = IAuthorization(authorization).getDomain();
if (domain != expectedDomain) {
revert IAccessAdmin.ErrorAccessAdminDomainMismatch(authorization, expectedDomain, domain);
}
// check release matches
VersionPart authorizationRelease = IAuthorization(authorization).getRelease();
if (authorizationRelease != expectedRelease) {
revert IAccessAdmin.ErrorAccessAdminReleaseMismatch(authorization, expectedRelease, authorizationRelease);
}
}
function checkIsRegistered(
address registry,
address target,
ObjectType expectedType
)
public
view
{
checkRegistry(registry);
ObjectType tagetType = IRegistry(registry).getObjectInfo(target).objectType;
if (tagetType.eqz()) {
revert IAccessAdmin.ErrorAccessAdminNotRegistered(target);
}
if (tagetType != expectedType) {
revert IAccessAdmin.ErrorAccessAdminTargetTypeMismatch(target, expectedType, tagetType);
}
}
function checkRegistry(
address registry
)
public
view
{
if (!ContractLib.isRegistry(registry)) {
revert IAccessAdmin.ErrorAccessAdminNotRegistry(registry);
}
}
function getAuthorizedRole(
IAccessAdmin accessAdmin,
IAuthorization authorization,
RoleId roleId
)
public
view
returns (RoleId authorizedRoleId)
{
string memory roleName = authorization.getRoleInfo(roleId).name.toString();
(authorizedRoleId, ) = accessAdmin.getRoleForName(roleName);
}
function getFunctionRoleId(
AccessManagerCloneable authority,
address target,
Selector selector
)
public
view
returns (RoleId functionRoleId)
{
return RoleIdLib.toRoleId(
authority.getTargetFunctionRole(
target,
selector.toBytes4()));
}
function getServiceRoleId(
address serviceAddress,
IAccess.TargetType serviceTargetType
)
public
view
returns (RoleId serviceRoleId)
{
IService service = IService(serviceAddress);
if (serviceTargetType == IAccess.TargetType.Service) {
return RoleIdLib.toServiceRoleId(service.getDomain(), service.getRelease());
} else if (serviceTargetType == IAccess.TargetType.GenericService) {
return RoleIdLib.toGenericServiceRoleId(service.getDomain());
}
revert IAccessAdmin.ErrorAccessAdminInvalidServiceType(serviceAddress, serviceTargetType);
}
function getVersionedServiceRoleId(
ObjectType serviceDomain,
VersionPart release
)
public
pure
returns (RoleId serviceRoleId)
{
return RoleIdLib.toRoleId(
SERVICE_ROLE_MIN + SERVICE_ROLE_FACTOR * serviceDomain.toInt() + release.toInt());
}
function getGenericServiceRoleId(
ObjectType serviceDomain
)
public
pure
returns (RoleId serviceRoleId)
{
return RoleIdLib.toRoleId(
SERVICE_ROLE_MIN + SERVICE_ROLE_FACTOR * serviceDomain.toInt() + VersionPartLib.releaseMax().toInt());
}
function getCustomRoleId(uint64 index)
public
pure
returns (RoleId customRoleId)
{
return RoleIdLib.toRoleId(CUSTOM_ROLE_MIN + index);
}
function isCustomRole(RoleId roleId)
public
pure
returns (bool)
{
return roleId.toInt() >= CUSTOM_ROLE_MIN;
}
function getTargetRoleId(
address target,
IAccess.TargetType targetType,
uint64 index
)
public
view
returns (RoleId targetRoleId)
{
if (targetType == IAccess.TargetType.Core) {
return RoleIdLib.toRoleId(CORE_ROLE_MIN + index);
}
if (targetType == IAccess.TargetType.Service || targetType == IAccess.TargetType.GenericService ) {
return getServiceRoleId(target, targetType);
}
if (targetType == IAccess.TargetType.Instance) {
return RoleIdLib.toRoleId(INSTANCE_ROLE_MIN + index);
}
if (targetType == IAccess.TargetType.Component) {
return RoleIdLib.toRoleId(COMPONENT_ROLE_MIN + index);
}
if (targetType == IAccess.TargetType.Custom
|| targetType == IAccess.TargetType.Contract)
{
return RoleIdLib.toRoleId(CUSTOM_ROLE_MIN + index);
}
revert IAccessAdmin.ErrorAccessAdminInvalidTargetType(target, targetType);
}
function getTokenHandler(
address target,
string memory targetName,
IAccess.TargetType targetType
)
public
view
returns (
address tokenHandler,
string memory tokenHandlerName
)
{
// not component or core (we need to check core because of staking)
if (targetType != IAccess.TargetType.Component && targetType != IAccess.TargetType.Core) {
return (address(0), "");
}
// not contract
if (!ContractLib.isContract(target)) {
return (address(0), "");
}
// not component
if (!ContractLib.supportsInterface(target, type(IComponent).interfaceId)) {
return (address(0), "");
}
tokenHandler = address(IComponent(target).getTokenHandler());
tokenHandlerName = string(abi.encodePacked(targetName, TOKEN_HANDLER_SUFFIX));
}
function toFunctionGrantingString(
IAccessAdmin accessAdmin,
Str functionName,
RoleId roleId
)
public
view
returns (string memory)
{
return string(
abi.encodePacked(
functionName.toString(),
"(): ",
getRoleName(accessAdmin, roleId)));
}
function getRoleName(
IAccessAdmin accessAdmin,
RoleId roleId
)
public
view
returns (string memory)
{
if (accessAdmin.roleExists(roleId)) {
return accessAdmin.getRoleInfo(roleId).name.toString();
}
return "<unknown-role>";
}
function toRoleName(string memory name) public pure returns (string memory) {
return string(
abi.encodePacked(
name,
ROLE_SUFFIX));
}
function toFunction(
bytes4 selector,
string memory name
)
public
view
returns (IAccess.FunctionInfo memory)
{
if(selector == bytes4(0)) {
revert IAccessAdmin.ErrorAccessAdminSelectorZero();
}
if(bytes(name).length == 0) {
revert IAccessAdmin.ErrorAccessAdminFunctionNameEmpty();
}
return IAccess.FunctionInfo({
name: StrLib.toStr(name),
selector: SelectorLib.toSelector(selector),
createdAt: TimestampLib.current(),
lastUpdateIn: BlocknumberLib.current()});
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import {AccessManagerUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagerUpgradeable.sol";
import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol";
import {InitializableERC165} from "../shared/InitializableERC165.sol";
import {RegistryLinked} from "../shared/RegistryLinked.sol";
import {VersionPart} from "../type/Version.sol";
/// @dev An AccessManager based on OpenZeppelin that is cloneable and has a central lock property.
/// The lock property allows to lock all services of a release in a central place.
/// Cloned by upon release preparation and instance cloning.
contract AccessManagerCloneable is
AccessManagerUpgradeable,
InitializableERC165,
RegistryLinked
{
error ErrorAccessManagerCallerNotAdmin(address caller);
error ErrorAccessManagerRegistryAlreadySet(address registry);
error ErrorAccessManagerInvalidRelease(VersionPart release);
error ErrorAccessManagerTargetAdminLocked(address target);
error ErrorAccessManagerCallerAdminLocked(address caller);
VersionPart private _release;
bool private _isLocked;
modifier onlyAdminRole() {
(bool isMember, ) = hasRole(ADMIN_ROLE, msg.sender);
if(!isMember) {
revert ErrorAccessManagerCallerNotAdmin(msg.sender);
}
_;
}
function initialize(address admin)
public
initializer()
{
__ERC165_init();
__AccessManager_init(admin);
_registerInterface(type(IAccessManager).interfaceId);
}
/// @dev Completes the setup of the access manager.
/// Links the access manager to the registry and sets the release version.
function completeSetup(
address registry,
VersionPart release
)
external
onlyAdminRole
reinitializer(uint64(release.toInt()))
{
_checkAndSetRegistry(registry);
_checkAndSetRelease(release);
}
/// @dev Returns true if the caller is authorized to call the target with the given selector and the manager lock is not set to locked.
/// Feturn values as in OpenZeppelin AccessManager.
/// For a locked manager the function reverts with ErrorAccessManagerTargetAdminLocked.
function canCall(
address caller,
address target,
bytes4 selector
)
public
view
virtual override
returns (
bool immediate,
uint32 delay
)
{
// locking of all contracts under control of this access manager
if (_isLocked) {
revert ErrorAccessManagerTargetAdminLocked(target);
}
(immediate, delay) = super.canCall(caller, target, selector);
}
/// @dev Locks/unlocks all services of this access manager.
/// Only the corresponding access admin can lock/unlock the services.
function setLocked(bool locked)
external
onlyAdminRole()
{
_isLocked = locked;
}
/// @dev Returns the release version of this access manager.
/// For the registry admin release 3 is returned.
/// For the release admin and the instance admin the actual release version is returned.
function getRelease() external view returns (VersionPart release) {
return _release;
}
/// @dev Returns true iff all contracts of this access manager are locked.
function isLocked()
public
view
returns (bool)
{
return _isLocked;
}
function _checkAndSetRelease(VersionPart release)
internal
{
if (!release.isValidRelease()) {
revert ErrorAccessManagerInvalidRelease(release);
}
_release = release;
}
function _checkAndSetRegistry(address registry)
internal
{
// checks
if(address(getRegistry()) != address(0)) {
revert ErrorAccessManagerRegistryAlreadySet(address(getRegistry()) );
}
// effects
__RegistryLinked_init(registry);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import {Blocknumber} from "../type/Blocknumber.sol";
import {RoleId} from "../type/RoleId.sol";
import {Selector} from "../type/Selector.sol";
import {Str} from "../type/String.sol";
import {Timestamp} from "../type/Timestamp.sol";
interface IAccess {
enum TargetType {
Undefined, // no target must have this type
Core, // GIF core contracts
GenericService, // release independent service contracts
Service, // service contracts
Instance, // instance contracts
Component, // instance contracts
Contract, // normal contracts
Custom // use case specific rules for contracts or normal accounts
}
struct RoleInfo {
// slot 0
RoleId adminRoleId; // 64
TargetType targetType; // ?
uint32 maxMemberCount; // 32
Timestamp createdAt; // 40
Timestamp pausedAt; // 40
Blocknumber lastUpdateIn; // 40
// slot 1
Str name; // 256
}
// TODO recalc slot allocation
struct TargetInfo {
Str name;
TargetType targetType;
RoleId roleId;
Timestamp createdAt;
Blocknumber lastUpdateIn;
}
struct FunctionInfo {
// slot 0
Str name; // function name
// slot 1
Selector selector; // function selector
Timestamp createdAt;
Blocknumber lastUpdateIn;
}
struct RoleNameInfo {
// slot 0
RoleId roleId;
bool exists;
}
struct TargeNameInfo {
// slot 0
address target;
bool exists;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {IAccess} from "./IAccess.sol";
import {IAuthorization} from "./IAuthorization.sol";
import {IRegistryLinked} from "../shared/IRegistryLinked.sol";
import {IRelease} from "../registry/IRelease.sol";
import {Blocknumber} from "../type/Blocknumber.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RoleId} from "../type/RoleId.sol";
import {Str} from "../type/String.sol";
import {VersionPart} from "../type/Version.sol";
/// @dev Base interface for registry admin, release admin, and instance admin
interface IAccessAdmin is
IAccessManaged,
IAccess,
IRegistryLinked,
IRelease
{
// roles, targets and functions
event LogAccessAdminRoleCreated(string admin, RoleId roleId, TargetType targetType, RoleId roleAdminId, string name);
event LogAccessAdminTargetCreated(string admin, string name, bool managed, address target, RoleId roleId);
event LogAccessAdminRoleActivatedSet(string admin, RoleId roleId, bool active, Blocknumber lastUpdateIn);
event LogAccessAdminRoleGranted(string admin, address account, string roleName);
event LogAccessAdminRoleRevoked(string admin, address account, string roleName);
event LogAccessAdminTargetLockedSet(string admin, address target, bool locked, Blocknumber lastUpdateIn);
event LogAccessAdminFunctionGranted(string admin, address target, string func, Blocknumber lastUpdateIn);
// only deployer modifier
error ErrorAccessAdminNotDeployer();
// only role admin modifier
error ErrorAccessAdminNotAdminOfRole(RoleId adminRoleId, address account);
// only role owner modifier
error ErrorAccessAdminNotRoleOwner(RoleId roleId, address account);
// role management
error ErrorAccessAdminInvalidUseOfAdminRole();
error ErrorAccessAdminInvalidUseOfPublicRole();
error ErrorAccessAdminRoleNotCustom(RoleId roleId);
// initialization
error ErrorAccessAdminNotRegistry(address registry);
error ErrorAccessAdminAuthorityNotContract(address authority);
error ErrorAccessAdminAccessManagerNotAccessManager(address authority);
error ErrorAccessAdminAccessManagerEmptyName();
// check target
error ErrorAccessAdminInvalidTargetType(address target, TargetType targetType);
error ErrorAccessAdminInvalidServiceType(address target, TargetType serviceTargetType);
error ErrorAccessAdminTargetNotCreated(address target);
error ErrorAccessAdminTargetNotRegistered(address target);
error ErrorAccessAdminTargetTypeMismatch(address target, ObjectType expectedType, ObjectType actualType);
// check authorization
error ErrorAccessAdminAlreadyInitialized(address authorization);
error ErrorAccessAdminNotAuthorization(address authorization);
error ErrorAccessAdminNotServiceAuthorization(address serviceAuthorization);
error ErrorAccessAdminDomainMismatch(address authorization, ObjectType expectedDomain, ObjectType actualDomain);
error ErrorAccessAdminReleaseMismatch(address authorization, VersionPart expectedRelease, VersionPart actualRelease);
// link to nft
error ErrorAccessAdminNotRegistered(address registerable);
// initialize authority
error ErrorAccessAdminAdminRoleMissing();
// create role
error ErrorAccessAdminRoleAlreadyCreated(RoleId roleId, string name);
error ErrorAccessAdminRoleAdminNotExisting(RoleId adminRoleId);
error ErrorAccessAdminRoleNameEmpty(RoleId roleId);
error ErrorAccessAdminRoleNameAlreadyExists(RoleId roleId, string name, RoleId existingRoleId);
// grant/revoke/renounce role
error ErrorAccessAdminRoleUnknown(RoleId roleId);
error ErrorAccessAdminRoleIsLocked(RoleId roleId);
error ErrorAccessAdminRoleIsPaused(RoleId roleId);
error ErrorAccessAdminRoleMembersLimitReached(RoleId roleId, uint256 memberCountLimit);
error ErrorAccessAdminRoleMemberNotContract(RoleId roleId, address notContract);
error ErrorAccessAdminRoleMemberRemovalDisabled(RoleId roleId, address expectedMember);
// create target
error ErrorAccessAdminTargetAlreadyCreated(address target, string name);
error ErrorAccessAdminTargetNameEmpty(address target);
error ErrorAccessAdminTargetNameAlreadyExists(address target, string name, address existingTarget);
error ErrorAccessAdminTargetNotAccessManaged(address target);
error ErrorAccessAdminTargetAuthorityMismatch(address expectedAuthority, address actualAuthority);
// lock target
error ErrorAccessAdminTagetNotLockable();
error ErrorAccessAdminTargetAlreadyLocked(address target, bool isLocked);
// authorize target functions
error ErrorAccessAdminNotComponentOrCustomTarget(address target);
error ErrorAccessAdminAuthorizeForAdminRoleInvalid(address target);
// toFunction
error ErrorAccessAdminSelectorZero();
error ErrorAccessAdminFunctionNameEmpty();
// check target
error ErrorAccessAdminTargetUnknown(address target);
//--- view functions ----------------------------------------------------//
function getAuthorization() external view returns (IAuthorization authorization);
function getLinkedNftId() external view returns (NftId linkedNftId);
function isLocked() external view returns (bool locked);
function roles() external view returns (uint256 numberOfRoles);
function getRoleId(uint256 idx) external view returns (RoleId roleId);
function roleExists(RoleId roleId) external view returns (bool exists);
function getRoleForName(string memory name) external view returns (RoleId roleId, bool exists);
function getRoleInfo(RoleId roleId) external view returns (RoleInfo memory roleInfo);
function isRoleActive(RoleId roleId) external view returns (bool isActive);
function isRoleCustom(RoleId roleId) external view returns (bool isCustom);
function isRoleMember(RoleId roleId, address account) external view returns (bool);
function isRoleAdmin(RoleId roleId, address account) external view returns (bool);
function roleMembers(RoleId roleId) external view returns (uint256 numberOfMembers);
function getRoleMember(RoleId roleId, uint256 idx) external view returns (address account);
function targetExists(address target) external view returns (bool exists);
function getTargetForName(Str name) external view returns (address target);
function targets() external view returns (uint256 numberOfTargets);
function getTargetAddress(uint256 idx) external view returns (address target);
function getTargetInfo(address target) external view returns (TargetInfo memory targetInfo);
function isTargetLocked(address target) external view returns (bool locked);
function authorizedFunctions(address target) external view returns (uint256 numberOfFunctions);
function getAuthorizedFunction(address target, uint256 idx) external view returns (FunctionInfo memory func, RoleId roleId);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IServiceAuthorization} from "./IServiceAuthorization.sol";
import {Str} from "../type/String.sol";
interface IAuthorization is
IServiceAuthorization
{
/// @dev Returns the token hander name.
/// Only components have a token handler.
function getTokenHandlerName() external view returns(string memory name);
/// @dev Returns the token hander target.
/// Only components have a token handler.
function getTokenHandlerTarget() external view returns(Str target);
/// @dev Returns the complete list of targets.
function getTargets() external view returns(Str[] memory targets);
/// @dev Returns true iff the specified target exists.
function targetExists(Str target) external view returns(bool exists);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IAccess} from "../authorization/IAccess.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RoleId} from "../type/RoleId.sol";
import {Str} from "../type/String.sol";
import {VersionPart} from "../type/Version.sol";
interface IServiceAuthorization is
IERC165,
IAccess
{
error ErrorAuthorizationMainTargetNameEmpty();
error ErrorAuthorizationTargetDomainZero();
error ErrorAuthorizationReleaseInvalid(uint8 release);
error ErrorAuthorizationCommitHashInvalid(string commitHash);
/// @dev Returns the main domain of the authorization.
function getDomain() external view returns(ObjectType targetDomain);
/// @dev Returns the release (VersionPart) for which the authorizations are defined by this contract.
/// Matches with the release returned by the linked service authorization.
function getRelease() external view returns(VersionPart release);
/// @dev Returns the commit hash for the related GIF release.
function getCommitHash() external view returns(string memory commitHash);
/// @dev Returns the main target id name as string.
/// This name is used to derive the target id and a corresponding target role name
/// Overwrite this function to change the basic pool target name.
function getMainTargetName() external view returns (string memory name);
/// @dev Returns the main target.
function getMainTarget() external view returns(Str target);
/// @dev Returns the full list of service domains for this release.
/// Services need to be registered for the release in revers order of this list.
function getServiceDomains() external view returns(ObjectType[] memory serviceDomains);
/// @dev Returns the service domain for the provided index.
function getServiceDomain(uint256 idx) external view returns(ObjectType serviceDomain);
/// @dev Returns the service target for the specified domain.
function getServiceTarget(ObjectType serviceDomain) external view returns(Str serviceTarget);
/// @dev Returns the service target for the specified domain.
function getServiceRole(ObjectType serviceDomain) external view returns(RoleId serviceRoleId);
/// @dev Returns the expected service address for the provided domain.
function getServiceAddress(ObjectType serviceDomain) external view returns(address service);
/// @dev Returns the role id associated with the target.
/// If no role is associated with the target the zero role id is returned.
function getTargetRole(Str target) external view returns(RoleId roleId);
/// @dev Returns true iff the role exists.
function roleExists(RoleId roleId) external view returns(bool exists);
/// @dev Returns the list of involved roles.
function getRoles() external view returns(RoleId[] memory roles);
/// @dev Returns the role info for the provided role id.
function getRoleInfo(RoleId roleId) external view returns (RoleInfo memory roleInfo);
/// @dev Returns the name for the provided role id.
function getRoleName(RoleId roleId) external view returns (string memory roleName);
/// @dev For the given target the list of authorized role ids is returned
function getAuthorizedRoles(Str target) external view returns(RoleId[] memory roleIds);
/// @dev For the given target and role id the list of authorized functions is returned
function getAuthorizedFunctions(Str target, RoleId roleId) external view returns(FunctionInfo[] memory authorizatedFunctions);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IPolicy} from "../instance/module/IPolicy.sol";
import {IService} from "../shared/IService.sol";
import {Amount} from "../type/Amount.sol";
import {DistributorType} from "../type/DistributorType.sol";
import {InstanceReader} from "../instance/InstanceReader.sol";
import {NftId} from "../type/NftId.sol";
import {ReferralId, ReferralStatus} from "../type/Referral.sol";
import {Seconds} from "../type/Seconds.sol";
import {Timestamp} from "../type/Timestamp.sol";
import {UFixed} from "../type/UFixed.sol";
interface IDistributionService is IService {
error ErrorDistributionServiceCallerNotRegistered(address caller);
error ErrorDistributionServiceParentNftIdNotInstance(NftId nftId, NftId parentNftId);
error ErrorDistributionServiceCallerNotDistributor(address caller);
error ErrorDistributionServiceInvalidReferralId(ReferralId referralId);
error ErrorDistributionServiceMaxReferralsExceeded(uint256 limit, uint256 maxReferrals);
error ErrorDistributionServiceDiscountTooLow(UFixed minDiscountPercentage, UFixed discountPercentage);
error ErrorDistributionServiceDiscountTooHigh(UFixed maxDiscountPercentage, UFixed discountPercentage);
error ErrorDistributionServiceExpiryTooLong(Seconds maxReferralLifetime, Timestamp expiryAt);
error ErrorDistributionServiceInvalidReferral();
error ErrorDistributionServiceExpirationInvalid(Timestamp expiryAt);
error ErrorDistributionServiceCommissionTooHigh(uint256 commissionPercentage, uint256 maxCommissionPercentage);
error ErrorDistributionServiceMinFeeTooHigh(uint256 minFee, uint256 limit);
error ErrorDistributionServiceDistributorTypeDistributionMismatch(DistributorType distributorType, NftId distributorTypeDistributionNftId, NftId distributionNftId);
error ErrorDistributionServiceDistributorDistributionMismatch(NftId distributorNftId, NftId distributorDistributionNftId, NftId distributionNftId);
error ErrorDistributionServiceCommissionWithdrawAmountExceedsLimit(Amount amount, Amount limit);
error ErrorDistributionServiceVariableFeesTooHight(uint256 maxDiscountPercentage, uint256 limit);
error ErrorDistributionServiceMaxDiscountTooHigh(uint256 maxDiscountPercentage, uint256 limit);
error ErrorDistributionServiceReferralInvalid(NftId distributionNftId, ReferralId referralId);
error ErrorDistributionServiceInvalidFeeTransferred(Amount transferredDistributionFeeAmount, Amount expectedDistributionFeeAmount);
error ErrorDistributionServiceReferralDistributionMismatch(ReferralId referralId, NftId referralDistributionNft, NftId distributionNftId);
event LogDistributionServiceCommissionWithdrawn(NftId distributorNftId, address recipient, address tokenAddress, Amount amount);
event LogDistributionServiceDistributorTypeCreated(NftId distributionNftId, string name);
event LogDistributionServiceDistributorCreated(NftId distributionNftId, NftId distributorNftId, DistributorType distributorType, address distributor);
event LogDistributionServiceDistributorTypeChanged(NftId distributorNftId, DistributorType oldDistributorType, DistributorType newDistributorType);
event LogDistributionServiceReferralCreated(NftId distributionNftId, NftId distributorNftId, ReferralId referralId, string code);
event LogDistributionServiceReferralProcessed(NftId distributionNftId, NftId distributorNftId, ReferralId referralId, uint32 usedReferrals);
event LogDistributionServiceSaleProcessed(NftId distributionNftId, ReferralId referralId);
function createDistributorType(
string memory name,
UFixed minDiscountPercentage,
UFixed maxDiscountPercentage,
UFixed commissionPercentage,
uint32 maxReferralCount,
Seconds maxReferralLifetime,
bool allowSelfReferrals,
bool allowRenewals,
bytes memory data
)
external
returns (DistributorType distributorType);
function createDistributor(
address distributor,
DistributorType distributorType,
bytes memory data
) external returns (NftId distributorNftId);
function changeDistributorType(
NftId distributorNftId,
DistributorType newDistributorType,
bytes memory data
) external;
function createReferral(
NftId distributorNftId,
string memory code,
UFixed discountPercentage,
uint32 maxReferrals,
Timestamp expiryAt,
bytes memory data
)
external
returns (ReferralId referralId);
/// @dev callback from product service when a referral is used.
/// Calling this will increment the referral usage counter.
function processReferral(
NftId distributionNftId,
ReferralId referralId
) external;
/// @dev callback from product service when selling a policy for a specific referralId
function processSale(
NftId distributionNftId,
ReferralId referralId,
IPolicy.PremiumInfo memory premium
) external;
function referralIsValid(
NftId distributorNftId,
ReferralId referralId
) external view returns (bool isValid);
/// @dev Withdraw commission for the distributor
/// @param distributorNftId the distributor Nft Id
/// @param amount the amount to withdraw. If set to AMOUNT_MAX, the full commission available is withdrawn
/// @return withdrawnAmount the effective withdrawn amount
function withdrawCommission(NftId distributorNftId, Amount amount) external returns (Amount withdrawnAmount);
/// @dev Returns the discount percentage for the provided referral code.
/// The function retuns both the percentage and the status of the referral code.
function getDiscountPercentage(InstanceReader instanceReader, ReferralId referralId) external view returns (UFixed discountPercentage, ReferralStatus status);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IPolicy} from "../../instance/module/IPolicy.sol";
import {IPolicyService} from "../../product/IPolicyService.sol";
import {Amount, AmountLib} from "../../type/Amount.sol";
import {FlightMessageVerifier} from "./FlightMessageVerifier.sol";
import {FlightProduct} from "./FlightProduct.sol";
import {InstanceReader} from "../../instance/InstanceReader.sol";
import {NftId} from "../../type/NftId.sol";
import {RequestId} from "../../type/RequestId.sol";
import {RiskId, RiskIdLib} from "../../type/RiskId.sol";
import {Seconds} from "../../type/Seconds.sol";
import {StateId} from "../../type/StateId.sol";
import {Str} from "../../type/String.sol";
import {Timestamp, TimestampLib} from "../../type/Timestamp.sol";
library FlightLib {
event LogFlightProductErrorUnprocessableStatus(RequestId requestId, RiskId riskId, bytes1 status);
event LogFlightProductErrorUnexpectedStatus(RequestId requestId, RiskId riskId, bytes1 status, int256 delayMinutes);
error ErrorFlightProductRiskInvalid(RiskId riskId);
error ErrorFlightProductPremiumAmountTooSmall(Amount premiumAmount, Amount minPremium);
error ErrorFlightProductPremiumAmountTooLarge(Amount premiumAmount, Amount maxPremium);
error ErrorFlightProductArrivalBeforeDepartureTime(Timestamp departureTime, Timestamp arrivalTime);
error ErrorFlightProductArrivalAfterMaxFlightDuration(Timestamp arrivalTime, Timestamp maxArrivalTime, Seconds maxDuration);
error ErrorFlightProductDepartureBeforeMinTimeBeforeDeparture(Timestamp departureTime, Timestamp now, Seconds minTimeBeforeDeparture);
error ErrorFlightProductDepartureAfterMaxTimeBeforeDeparture(Timestamp departureTime, Timestamp now, Seconds maxTimeBeforeDeparture);
error ErrorFlightProductNotEnoughObservations(uint256 observations, uint256 minObservations);
error ErrorFlightProductClusterRisk(Amount totalSumInsured, Amount maxTotalPayout);
function checkApplicationData(
FlightProduct flightProduct,
Str flightData,
Timestamp departureTime,
Timestamp arrivalTime,
Amount premiumAmount
)
public
view
{
_checkApplicationData(flightProduct, premiumAmount, arrivalTime, departureTime);
}
function _checkApplicationData(
FlightProduct flightProduct,
Amount premiumAmount,
Timestamp arrivalTime,
Timestamp departureTime
)
internal
view
{
bool testMode = flightProduct.isTestMode();
// solhint-disable
if (premiumAmount < flightProduct.MIN_PREMIUM()) {
revert ErrorFlightProductPremiumAmountTooSmall(premiumAmount, flightProduct.MIN_PREMIUM());
}
if (premiumAmount > flightProduct.MAX_PREMIUM()) {
revert ErrorFlightProductPremiumAmountTooLarge(premiumAmount, flightProduct.MAX_PREMIUM());
}
if (arrivalTime <= departureTime) {
revert ErrorFlightProductArrivalBeforeDepartureTime(departureTime, arrivalTime);
}
if (arrivalTime > departureTime.addSeconds(flightProduct.MAX_FLIGHT_DURATION())) {
revert ErrorFlightProductArrivalAfterMaxFlightDuration(arrivalTime, departureTime, flightProduct.MAX_FLIGHT_DURATION());
}
// test mode allows the creation for policies that are outside restricted policy creation times
if (!testMode && departureTime < TimestampLib.current().addSeconds(flightProduct.MIN_TIME_BEFORE_DEPARTURE())) {
revert ErrorFlightProductDepartureBeforeMinTimeBeforeDeparture(departureTime, TimestampLib.current(), flightProduct.MIN_TIME_BEFORE_DEPARTURE());
}
if (!testMode && departureTime > TimestampLib.current().addSeconds(flightProduct.MAX_TIME_BEFORE_DEPARTURE())) {
revert ErrorFlightProductDepartureAfterMaxTimeBeforeDeparture(departureTime, TimestampLib.current(), flightProduct.MAX_TIME_BEFORE_DEPARTURE());
}
// solhint-enable
}
function checkClusterRisk(
Amount sumOfSumInsuredAmounts,
Amount sumInsuredAmount,
Amount maxTotalPayout
)
public
pure
{
if (sumOfSumInsuredAmounts + sumInsuredAmount > maxTotalPayout) {
revert ErrorFlightProductClusterRisk(sumOfSumInsuredAmounts + sumInsuredAmount, maxTotalPayout);
}
}
/// @dev calculates payout option based on flight status and delay minutes.
/// Is not a view function as it emits log evens in case of unexpected status.
function checkAndGetPayoutOption(
RequestId requestId,
RiskId riskId,
bytes1 status,
int256 delayMinutes
)
public
returns (uint8 payoutOption)
{
// default: no payout
payoutOption = type(uint8).max;
// check status
if (status != "L" && status != "A" && status != "C" && status != "D") {
emit LogFlightProductErrorUnprocessableStatus(requestId, riskId, status);
return payoutOption;
}
if (status == "A") {
// todo: active, reschedule oracle call + 45 min
emit LogFlightProductErrorUnexpectedStatus(requestId, riskId, status, delayMinutes);
return payoutOption;
}
// trigger payout if applicable
if (status == "C") { payoutOption = 3; }
else if (status == "D") { payoutOption = 4; }
else if (delayMinutes >= 15 && delayMinutes < 30) { payoutOption = 0; }
else if (delayMinutes >= 30 && delayMinutes < 45) { payoutOption = 1; }
else if (delayMinutes >= 45) { payoutOption = 2; }
}
function calculateWeight(
FlightProduct flightProduct,
uint256[6] memory statistics
)
public
view
returns (uint256 weight)
{
// check we have enough observations
if (statistics[0] < flightProduct.MIN_OBSERVATIONS()) {
revert ErrorFlightProductNotEnoughObservations(statistics[0], flightProduct.MIN_OBSERVATIONS());
}
weight = 0;
for (uint256 i = 1; i < 6; i++) {
weight += flightProduct.WEIGHT_PATTERN(i) * statistics[i] * 10000 / statistics[0];
}
// To avoid div0 in the payout section, we have to make a minimal assumption on weight
if (weight == 0) {
weight = 100000 / statistics[0];
}
// TODO comment on intended effect
weight = (weight * (100 + flightProduct.MARGIN_PERCENT())) / 100;
}
// REMARK: each flight may get different payouts depending on the latest statics
function calculatePayoutAmounts(
FlightProduct flightProduct,
Amount premium,
uint256[6] memory statistics
)
public
view
returns (
uint256 weight,
Amount[5] memory payoutAmounts,
Amount sumInsuredAmount // simply the max of payoutAmounts
)
{
if (premium < flightProduct.MIN_PREMIUM()) {
revert ErrorFlightProductPremiumAmountTooSmall(premium, flightProduct.MIN_PREMIUM());
}
if (premium > flightProduct.MAX_PREMIUM()) {
revert ErrorFlightProductPremiumAmountTooLarge(premium, flightProduct.MAX_PREMIUM());
}
sumInsuredAmount = AmountLib.zero();
weight = calculateWeight(flightProduct, statistics);
for (uint256 i = 0; i < 5; i++) {
Amount payoutAmount = AmountLib.toAmount(
premium.toInt() * flightProduct.WEIGHT_PATTERN(i + 1) * 10000 / weight);
// cap payout and update sum insured if applicable
if (payoutAmount > flightProduct.MAX_PAYOUT()) { payoutAmount = flightProduct.MAX_PAYOUT(); }
if (payoutAmount > sumInsuredAmount) { sumInsuredAmount = payoutAmount; }
payoutAmounts[i] = payoutAmount;
}
}
function getPayoutOption(
InstanceReader reader,
NftId productNftId,
RiskId riskId
)
public
view
returns (
bool exists,
bool statusAvailable,
uint8 payoutOption
)
{
FlightProduct.FlightRisk memory flightRisk;
(exists, flightRisk) = getFlightRisk(
reader,
productNftId,
riskId,
false);
statusAvailable = flightRisk.statusUpdatedAt.gtz();
payoutOption = flightRisk.payoutOption;
}
function getPayoutAmount(
bytes memory applicationData,
uint8 payoutOption
)
public
pure
returns (Amount payoutAmount)
{
if (payoutOption == type(uint8).max) {
return AmountLib.zero();
}
// retrieve payout amounts from application data
(, Amount[5] memory payoutAmounts) = abi.decode(
applicationData, (Amount, Amount[5]));
// get payout amount for selected option
payoutAmount = payoutAmounts[payoutOption];
}
function getFlightRisk(
InstanceReader reader,
NftId productNftId,
Str flightData,
Timestamp departureTime,
string memory departureTimeLocal,
Timestamp arrivalTime,
string memory arrivalTimeLocal
)
public
view
returns (
RiskId riskId,
bool exists,
FlightProduct.FlightRisk memory flightRisk
)
{
riskId = getRiskId(productNftId, flightData);
(exists, flightRisk) = getFlightRisk(reader, productNftId, riskId, false);
// create new risk if not existing
if (!exists) {
flightRisk = FlightProduct.FlightRisk({
flightData: flightData,
departureTime: departureTime,
departureTimeLocal: departureTimeLocal,
arrivalTime: arrivalTime,
arrivalTimeLocal: arrivalTimeLocal,
sumOfSumInsuredAmounts: AmountLib.toAmount(0),
status: bytes1(0),
delayMinutes: 0,
payoutOption: uint8(0),
statusUpdatedAt: TimestampLib.zero()});
}
}
function getFlightRisk(
InstanceReader reader,
NftId productNftId,
RiskId riskId,
bool requireRiskExists
)
public
view
returns (
bool exists,
FlightProduct.FlightRisk memory flightRisk
)
{
// check if risk exists
exists = reader.isProductRisk(productNftId, riskId);
if (!exists && requireRiskExists) {
revert ErrorFlightProductRiskInvalid(riskId);
}
// get risk data if risk exists
if (exists) {
flightRisk = abi.decode(
reader.getRiskInfo(riskId).data, (FlightProduct.FlightRisk));
}
}
function getRiskId(
NftId productNftId,
Str flightData
)
public
view
returns (RiskId riskId)
{
bytes32 riskKey = getRiskKey(flightData);
riskId = getRiskId(productNftId, riskKey);
}
function getRiskKey(
Str flightData
)
internal
pure
returns (bytes32 riskKey)
{
return keccak256(abi.encode(flightData));
}
function getRiskId(NftId productNftId, bytes32 riskKey) internal view returns (RiskId riskId) {
return RiskIdLib.toRiskId(productNftId, riskKey);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {Amount} from "../../type/Amount.sol";
import {Str} from "../../type/String.sol";
import {Timestamp} from "../../type/Timestamp.sol";
contract FlightMessageVerifier is
Ownable
{
error ErrorFlightMessageVerifierSignerZero();
error ErrorFlightMessageVerifierContractSignerNotSupported();
address private _expectedSigner;
constructor() Ownable(msg.sender) { }
function setExpectedSigner(address signer) external onlyOwner {
if (signer == address(0)) { revert ErrorFlightMessageVerifierSignerZero(); }
if (signer.code.length > 0) { revert ErrorFlightMessageVerifierContractSignerNotSupported(); }
_expectedSigner = signer;
}
function getExpectedSigner() external view returns(address) {
return _expectedSigner;
}
/// @dev creates digest hash based on application parameters
/// proposal:
/// use "LX 180 ZRH BKK 20241104" (23 chars, should be enough for all flights)
/// carriers, airports: https://www.iata.org/en/publications/directories/code-search/
/// flight numbers: https://en.wikipedia.org/wiki/Flight_number
/// instead of separate strings, coding/decoding done anyway off-chain
function getRatingsHash(
Str flightData,
Timestamp departureTime,
Timestamp arrivalTime,
Amount premiumAmount,
uint256[6] memory statistics
)
public
view
returns(bytes32)
{
return MessageHashUtils.toEthSignedMessageHash(
abi.encode(
flightData,
departureTime,
arrivalTime,
premiumAmount,
statistics));
}
function verifyRatingsHash(
Str flightData,
Timestamp departureTime,
Timestamp arrivalTime,
Amount premiumAmount,
uint256[6] memory statistics,
// bytes memory signature,
uint8 v,
bytes32 r,
bytes32 s
)
public
view
returns (
address actualSigner,
ECDSA.RecoverError errorStatus,
bool success
)
{
bytes32 messageHash = getRatingsHash(
flightData,
departureTime,
arrivalTime,
premiumAmount,
statistics);
(
actualSigner,
errorStatus,
) = ECDSA.tryRecover(messageHash, v, r, s);
success = (
errorStatus == ECDSA.RecoverError.NoError
&& actualSigner == _expectedSigner);
}
// TODO re-enable or cleanup
// function checkAndRegisterSignature (
// address policyHolder,
// address protectedWallet,
// uint256 protectedBalance,
// uint256 duration,
// uint256 bundleId,
// bytes32 signatureId,
// bytes calldata signature
// )
// external
// {
// bytes32 signatureHash = keccak256(abi.encode(signature));
// require(!_signatureIsUsed[signatureHash], "ERROR:DMH-001:SIGNATURE_USED");
// address signer = getSignerFromDigestAndSignature(
// protectedWallet,
// protectedBalance,
// duration,
// bundleId,
// signatureId,
// signature);
// require(policyHolder == signer, "ERROR:DMH-002:SIGNATURE_INVALID");
// _signatureIsUsed[signatureHash] = true;
// }
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IAuthorization} from "../../authorization/IAuthorization.sol";
import {ACTIVE, FULFILLED, FAILED} from "../../type/StateId.sol";
import {NftId} from "../../type/NftId.sol";
import {BasicOracle} from "../../oracle/BasicOracle.sol";
import {RequestId} from "../../type/RequestId.sol";
import {LibRequestIdSet} from "../../type/RequestIdSet.sol";
import {RiskId} from "../../type/RiskId.sol";
import {StateId} from "../../type/StateId.sol";
import {Str} from "../../type/String.sol";
import {Timestamp, TimestampLib} from "../../type/Timestamp.sol";
contract FlightOracle is
BasicOracle
{
struct FlightStatusRequest {
RiskId riskId;
Str flightData; // "LX 180 ZRH BKK 20241104"
Timestamp departureTime; // is this needed or is flight number and date unique aready?
}
struct FlightStatusResponse {
RiskId riskId;
bytes1 status;
int256 delayMinutes;
}
event LogFlightOracleRequestReceived(RequestId requestId, NftId requesterId);
event LogFlightOracleResponseSent(RequestId requestId, bytes1 status, int256 delay);
event LogFlightOracleRequestCancelled(RequestId requestId);
// TODO decide if this variable should be moved to instance store
// if so it need to manage active requests by requestor nft id
LibRequestIdSet.Set internal _activeRequests;
constructor(
address registry,
NftId productNftId,
string memory componentName,
IAuthorization authorization
)
{
address initialOwner = msg.sender;
initialize(
registry,
productNftId,
authorization,
initialOwner,
componentName
);
}
function initialize(
address registry,
NftId productNftId,
IAuthorization authorization,
address initialOwner,
string memory name
)
public
virtual
initializer()
{
_initializeBasicOracle(
registry,
productNftId,
authorization,
initialOwner,
name);
}
function respondWithFlightStatus(
RequestId requestId,
bytes1 status,
int256 delayMinutes
)
external
restricted()
{
// obtain riskId for request
bytes memory requestData = _getInstanceReader().getRequestInfo(requestId).requestData;
(RiskId riskId,,) = abi.decode(requestData, (RiskId, Str, Timestamp));
// assemble response data
bytes memory responseData = abi.encode(
FlightStatusResponse ({
riskId: riskId,
status: status,
delayMinutes: delayMinutes}));
// logging
emit LogFlightOracleResponseSent(requestId, status, delayMinutes);
// effects + interaction (via framework to receiving component)
_respond(requestId, responseData);
// TODO decide if the code below should be moved to GIF
_updateRequestState(requestId);
}
function updateRequestState(
RequestId requestId
)
external
restricted()
{
_updateRequestState(requestId);
}
//--- view functions ----------------------------------------------------//
// TODO decide if the code below should be moved to GIF
function activeRequests()
external
view
returns(uint256 numberOfRequests)
{
return LibRequestIdSet.size(_activeRequests);
}
// TODO decide if the code below should be moved to GIF
function getActiveRequest(uint256 idx)
external
view
returns(RequestId requestId)
{
return LibRequestIdSet.getElementAt(_activeRequests, idx);
}
function isActiveRequest(RequestId requestId)
external
view
returns(bool isActive)
{
return LibRequestIdSet.contains(_activeRequests, requestId);
}
function getRequestState(RequestId requestId)
external
view
returns (
RiskId riskId,
string memory flightData,
StateId requestState,
bool readyForResponse,
bool waitingForResend
)
{
bytes memory requestData = _getInstanceReader().getRequestInfo(requestId).requestData;
Str fltData;
Timestamp departureTime;
(riskId, fltData, departureTime) = abi.decode(requestData, (RiskId, Str, Timestamp));
flightData = fltData.toString();
requestState = _getInstanceReader().getRequestState(requestId);
readyForResponse = requestState == ACTIVE() && TimestampLib.current() >= departureTime;
waitingForResend = requestState == FAILED();
}
function decodeFlightStatusRequestData(bytes memory data) external pure returns (FlightStatusRequest memory) {
return abi.decode(data, (FlightStatusRequest));
}
//--- internal functions ------------------------------------------------//
// TODO decide if the code below should be moved to GIF
// check callback result
function _updateRequestState(
RequestId requestId
)
internal
{
bool requestFulfilled = _getInstanceReader().getRequestState(
requestId) == FULFILLED();
// remove from active requests when successful
if (requestFulfilled && LibRequestIdSet.contains(_activeRequests, requestId)) {
LibRequestIdSet.remove(_activeRequests, requestId);
}
}
/// @dev use case specific handling of oracle requests
/// for now only log is emitted to verify that request has been received by oracle component
function _request(
RequestId requestId,
NftId requesterId,
bytes calldata requestData,
Timestamp expiryAt
)
internal
virtual override
{
FlightStatusRequest memory request = abi.decode(requestData, (FlightStatusRequest));
// TODO decide if the line below should be moved to GIF
LibRequestIdSet.add(_activeRequests, requestId);
emit LogFlightOracleRequestReceived(requestId, requesterId);
}
/// @dev use case specific handling of oracle requests
/// for now only log is emitted to verify that cancelling has been received by oracle component
function _cancel(
RequestId requestId
)
internal
virtual override
{
// TODO decide if the line below should be moved to GIF
LibRequestIdSet.remove(_activeRequests, requestId);
emit LogFlightOracleRequestCancelled(requestId);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IAuthorization} from "../../authorization/IAuthorization.sol";
import {IComponents} from "../../instance/module/IComponents.sol";
import {IPolicy} from "../../instance/module/IPolicy.sol";
import {Amount, AmountLib} from "../../type/Amount.sol";
import {ClaimId} from "../../type/ClaimId.sol";
import {Component} from "../../shared/Component.sol";
import {FeeLib} from "../../type/Fee.sol";
import {FlightLib} from "./FlightLib.sol";
import {FlightMessageVerifier} from "./FlightMessageVerifier.sol";
import {FlightOracle} from "./FlightOracle.sol";
import {InstanceReader} from "../../instance/InstanceReader.sol";
import {NftId, NftIdLib} from "../../type/NftId.sol";
import {PayoutId} from "../../type/PayoutId.sol";
import {Product} from "../../product/Product.sol";
import {ReferralLib} from "../../type/Referral.sol";
import {RiskId, RiskIdLib} from "../../type/RiskId.sol";
import {RequestId} from "../../type/RequestId.sol";
import {Seconds, SecondsLib} from "../../type/Seconds.sol";
import {Str, StrLib} from "../../type/String.sol";
import {Timestamp, TimestampLib} from "../../type/Timestamp.sol";
/// @dev FlightProduct implements the flight delay product.
contract FlightProduct is
Product
{
event LogFlightPolicyPurchased(NftId policyNftId, string flightData, Amount premiumAmount);
event LogFlightPolicyClosed(NftId policyNftId, Amount payoutAmount);
event LogFlightStatusProcessed(RequestId requestId, RiskId riskId, bytes1 status, int256 delayMinutes, uint8 payoutOption);
event LogFlightPoliciesProcessed(RiskId riskId, uint8 payoutOption, uint256 policiesProcessed, uint256 policiesRemaining);
// solhint-disable
Amount public MIN_PREMIUM;
Amount public MAX_PREMIUM;
Amount public MAX_PAYOUT;
Amount public MAX_TOTAL_PAYOUT; // Maximum risk per flight/risk
// Minimum time before departure for applying
Seconds public MIN_TIME_BEFORE_DEPARTURE;
// Maximum time before departure for applying
Seconds public MAX_TIME_BEFORE_DEPARTURE;
// Maximum duration of flight
Seconds public MAX_FLIGHT_DURATION;
// Max time to process claims after departure
Seconds public LIFETIME;
// ['observations','late15','late30','late45','cancelled','diverted']
// no payouts for delays of 30' or less
uint8[6] public WEIGHT_PATTERN;
// Minimum number of observations for valid prediction/premium calculation
uint256 public MIN_OBSERVATIONS;
// Maximum cumulated weighted premium per risk
uint256 public MARGIN_PERCENT;
// Maximum number of policies to process in one callback
uint8 public MAX_POLICIES_TO_PROCESS;
// solhint-enable
bool internal _testMode;
mapping(RiskId riskId => RequestId requestId) internal _requests;
// GIF V3 specifics
NftId internal _defaultBundleNftId;
NftId internal _oracleNftId;
struct FlightRisk {
Str flightData; // example: "LX 180 ZRH BKK 20241104"
Timestamp departureTime;
// this field contains static data required by the frontend and is not directly used by the product
string departureTimeLocal; // example "2024-10-14T10:10:00.000 Asia/Seoul"
Timestamp arrivalTime;
// this field contains static data required by the frontend and is not directly used by the product
string arrivalTimeLocal; // example "2024-10-14T10:10:00.000 Asia/Seoul"
Amount sumOfSumInsuredAmounts;
bytes1 status; // 'L'ate, 'C'ancelled, 'D'iverted, ...
int256 delayMinutes;
uint8 payoutOption;
Timestamp statusUpdatedAt;
}
struct ApplicationData {
string flightData;
Timestamp departureTime;
string departureTimeLocal;
Timestamp arrivalTime;
string arrivalTimeLocal;
Amount premiumAmount;
uint256[6] statistics;
}
struct PermitData {
address owner;
address spender;
uint256 value;
uint256 deadline;
uint8 v;
bytes32 r;
bytes32 s;
}
constructor(
address registry,
NftId instanceNftId,
string memory componentName,
IAuthorization authorization
)
{
address initialOwner = msg.sender;
_initialize(
registry,
instanceNftId,
componentName,
authorization,
initialOwner);
}
//--- external functions ------------------------------------------------//
//--- unpermissioned functions ------------------------------------------//
function setOracleNftId()
external
{
_oracleNftId = _getInstanceReader().getProductInfo(
getNftId()).oracleNftId[0];
}
function calculatePayoutAmounts(
FlightProduct flightProduct,
Amount premium,
uint256[6] memory statistics
)
external
view
returns (
uint256 weight,
Amount[5] memory payoutAmounts,
Amount sumInsuredAmount // simply the max of payoutAmounts
)
{
return FlightLib.calculatePayoutAmounts(
flightProduct,
premium,
statistics);
}
/// @dev Creates a policy using a permit for the policy holder.
/// The policy holder is defined as the owner parameter of the permit data.
/// NOTE: This function makes the assumption that the product token
/// supports permits. This assumption is not verfied.
function createPolicyWithPermit(
PermitData memory permit,
ApplicationData memory application
)
external
virtual
restricted()
returns (
RiskId riskId,
NftId policyNftId
)
{
// process permit data
_processPermit(permit);
// create policy
address policyHolder = permit.owner;
(
riskId,
policyNftId
) = _createPolicy(
policyHolder,
StrLib.toStr(application.flightData),
application.departureTime,
application.departureTimeLocal,
application.arrivalTime,
application.arrivalTimeLocal,
application.premiumAmount,
application.statistics);
}
/// @dev Callback for flight status oracle.
/// Function may only be alled by oracle service.
function flightStatusCallback(
RequestId requestId,
bytes memory responseData
)
external
virtual
restricted()
{
FlightOracle.FlightStatusResponse memory response = abi.decode(
responseData, (FlightOracle.FlightStatusResponse));
_processFlightStatus(
requestId,
response.riskId,
response.status,
response.delayMinutes);
}
function resendRequest(RequestId requestId)
external
virtual
restricted()
{
_resendRequest(requestId);
}
/// @dev Manual fallback function for product owner.
function processPayoutsAndClosePolicies(
RiskId riskId,
uint8 maxPoliciesToProcess
)
external
virtual
restricted()
onlyOwner()
{
_processPayoutsAndClosePolicies(
riskId,
maxPoliciesToProcess);
}
//--- owner functions ---------------------------------------------------//
/// @dev Call after product registration with the instance
/// when the product token/tokenhandler is available
function setConstants(
Amount minPremium,
Amount maxPremium,
Amount maxPayout,
Amount maxTotalPayout,
Seconds minTimeBeforeDeparture,
Seconds maxTimeBeforeDeparture,
uint8 maxPoliciesToProcess
)
external
virtual
restricted()
onlyOwner()
{
MIN_PREMIUM = minPremium;
MAX_PREMIUM = maxPremium;
MAX_PAYOUT = maxPayout;
MAX_TOTAL_PAYOUT = maxTotalPayout;
MIN_TIME_BEFORE_DEPARTURE = minTimeBeforeDeparture;
MAX_TIME_BEFORE_DEPARTURE = maxTimeBeforeDeparture;
MAX_POLICIES_TO_PROCESS = maxPoliciesToProcess;
}
function setDefaultBundle(NftId bundleNftId) external restricted() onlyOwner() { _defaultBundleNftId = bundleNftId; }
function setTestMode(bool testMode) external restricted() onlyOwner() { _testMode = testMode; }
function approveTokenHandler(IERC20Metadata token, Amount amount) external restricted() onlyOwner() { _approveTokenHandler(token, amount); }
function setLocked(bool locked) external onlyOwner() { _setLocked(locked); }
function setWallet(address newWallet) external restricted() onlyOwner() { _setWallet(newWallet); }
//--- view functions ----------------------------------------------------//
function calculateNetPremium(
Amount, // sumInsuredAmount: not used in this product
RiskId, // riskId: not used in this product
Seconds, // lifetime: not used in this product, a flight is a one time risk
bytes memory applicationData // holds the premium amount the customer is willing to pay
)
external
virtual override
view
returns (Amount netPremiumAmount)
{
(netPremiumAmount, ) = abi.decode(applicationData, (Amount, Amount[5]));
}
function getOracleNftId() public view returns (NftId oracleNftId) { return _oracleNftId; }
function isTestMode() public view returns (bool) { return _testMode; }
function getFlightRisk(
RiskId riskId,
bool requireRiskExists
)
public
view
returns (
bool exists,
FlightRisk memory flightRisk
)
{
(exists, flightRisk) = FlightLib.getFlightRisk(
_getInstanceReader(),
getNftId(),
riskId,
requireRiskExists);
}
function getRequestForRisk(RiskId riskId) public view returns (RequestId requestId) { return _requests[riskId]; }
function decodeFlightRiskData(bytes memory data) public pure returns (FlightRisk memory) { return abi.decode(data, (FlightRisk)); }
//--- internal functions ------------------------------------------------//
function _processPermit(
PermitData memory permit
)
internal
virtual
restricted()
{
address tokenAddress = address(getToken());
// process permit data
ERC20Permit(tokenAddress).permit(
permit.owner,
permit.spender,
permit.value,
permit.deadline,
permit.v,
permit.r,
permit.s);
}
function _createPolicy(
address policyHolder,
Str flightData,
Timestamp departureTime,
string memory departureTimeLocal,
Timestamp arrivalTime,
string memory arrivalTimeLocal,
Amount premiumAmount,
uint256[6] memory statistics
)
internal
virtual
returns (
RiskId riskId,
NftId policyNftId
)
{
// checks
// disabled for now - using rbac for security
FlightLib.checkApplicationData(
this,
flightData,
departureTime,
arrivalTime,
premiumAmount);
(riskId, policyNftId) = _prepareApplication(
policyHolder,
flightData,
departureTime,
departureTimeLocal,
arrivalTime,
arrivalTimeLocal,
premiumAmount,
statistics);
_createPolicy(
policyNftId,
TimestampLib.zero(), // do not ativate yet
premiumAmount); // max premium amount
// interactions (token transfer + callback to token holder, if contract)
_collectPremium(
policyNftId,
departureTime); // activate at scheduled departure time of flight
// send oracle request for for new risk to obtain flight status (interacts with flight oracle contract)
if (_requests[riskId].eqz()) {
_requests[riskId] = _sendRequest(
_oracleNftId,
abi.encode(
FlightOracle.FlightStatusRequest(
riskId,
flightData,
departureTime)),
// allow up to 30 days to process the claim
arrivalTime.addSeconds(SecondsLib.fromDays(30)),
"flightStatusCallback");
}
emit LogFlightPolicyPurchased(policyNftId, flightData.toString(), premiumAmount);
}
function _prepareApplication(
address policyHolder,
Str flightData,
Timestamp departureTime,
string memory departureTimeLocal,
Timestamp arrivalTime,
string memory arrivalTimeLocal,
Amount premiumAmount,
uint256[6] memory statistics
)
internal
virtual
returns (
RiskId riskId,
NftId policyNftId
)
{
Amount[5] memory payoutAmounts;
Amount sumInsuredAmount;
(
riskId,
payoutAmounts,
sumInsuredAmount
) = _createRiskAndPayoutAmounts(
flightData,
departureTime,
departureTimeLocal,
arrivalTime,
arrivalTimeLocal,
premiumAmount,
statistics);
policyNftId = _createApplication(
policyHolder,
riskId,
sumInsuredAmount,
premiumAmount,
LIFETIME,
_defaultBundleNftId,
ReferralLib.zero(),
abi.encode(
premiumAmount,
payoutAmounts)); // application data
}
function _createRiskAndPayoutAmounts(
Str flightData,
Timestamp departureTime,
string memory departureTimeLocal,
Timestamp arrivalTime,
string memory arrivalTimeLocal,
Amount premiumAmount,
uint256[6] memory statistics
)
internal
virtual
returns (
RiskId riskId,
Amount[5] memory payoutAmounts,
Amount sumInsuredAmount
)
{
uint256 weight;
(
weight,
payoutAmounts,
sumInsuredAmount
) = FlightLib.calculatePayoutAmounts(
this,
premiumAmount,
statistics);
riskId = _checkAndUpdateFlightRisk(
flightData,
departureTime,
departureTimeLocal,
arrivalTime,
arrivalTimeLocal,
sumInsuredAmount,
weight);
}
function _checkAndUpdateFlightRisk(
Str flightData,
Timestamp departureTime,
string memory departureTimeLocal,
Timestamp arrivalTime,
string memory arrivalTimeLocal,
Amount sumInsuredAmount,
uint256 weight
)
internal
virtual
returns (RiskId riskId)
{
bool exists;
FlightRisk memory flightRisk;
(riskId, exists, flightRisk) = FlightLib.getFlightRisk(
_getInstanceReader(),
getNftId(),
flightData,
departureTime,
departureTimeLocal,
arrivalTime,
arrivalTimeLocal);
// create risk, if new
if (!exists) {
bytes32 riskKey = FlightLib.getRiskKey(flightData);
_createRisk(riskKey, abi.encode(flightRisk));
}
FlightLib.checkClusterRisk(
flightRisk.sumOfSumInsuredAmounts,
sumInsuredAmount,
MAX_TOTAL_PAYOUT);
// update existing risk with additional sum insured amount
flightRisk.sumOfSumInsuredAmounts = flightRisk.sumOfSumInsuredAmounts + sumInsuredAmount;
_updateRisk(riskId, abi.encode(flightRisk));
}
function _processFlightStatus(
RequestId requestId,
RiskId riskId,
bytes1 status,
int256 delayMinutes
)
internal
virtual
{
// check risk exists
(, FlightRisk memory flightRisk) = getFlightRisk(riskId, true);
// update status, if not yet set
if (flightRisk.statusUpdatedAt.eqz()) {
flightRisk.statusUpdatedAt = TimestampLib.current();
flightRisk.status = status;
flightRisk.delayMinutes = delayMinutes;
flightRisk.payoutOption = FlightLib.checkAndGetPayoutOption(
requestId, riskId, status, delayMinutes);
_updateRisk(riskId, abi.encode(flightRisk));
}
(,, uint8 payoutOption) = _processPayoutsAndClosePolicies(
riskId,
MAX_POLICIES_TO_PROCESS);
// logging
emit LogFlightStatusProcessed(requestId, riskId, status, delayMinutes, payoutOption);
}
function _processPayoutsAndClosePolicies(
RiskId riskId,
uint8 maxPoliciesToProcess
)
internal
virtual
returns (
bool riskExists,
bool statusAvailable,
uint8 payoutOption
)
{
// determine numbers of policies to process
InstanceReader reader = _getInstanceReader();
(riskExists, statusAvailable, payoutOption) = FlightLib.getPayoutOption(reader, getNftId(), riskId);
// return with default values if risk does not exist or status is not yet available
if (!riskExists || !statusAvailable) {
return (riskExists, statusAvailable, payoutOption);
}
uint256 policiesToProcess = reader.policiesForRisk(riskId);
uint256 policiesProcessed = policiesToProcess < maxPoliciesToProcess ? policiesToProcess : maxPoliciesToProcess;
// assemble array with policies to process
NftId [] memory policies = new NftId[](policiesProcessed);
for (uint256 i = 0; i < policiesProcessed; i++) {
policies[i] = reader.getPolicyForRisk(riskId, i);
}
// go through policies
for (uint256 i = 0; i < policiesProcessed; i++) {
NftId policyNftId = policies[i];
Amount payoutAmount = FlightLib.getPayoutAmount(
reader.getPolicyInfo(policyNftId).applicationData,
payoutOption);
// create claim/payout (if applicable)
_resolvePayout(
policyNftId,
payoutAmount);
// expire and close policy
_expire(policyNftId, TimestampLib.current());
_close(policyNftId);
emit LogFlightPolicyClosed(policyNftId, payoutAmount);
}
// logging
emit LogFlightPoliciesProcessed(riskId, payoutOption, policiesProcessed, policiesToProcess - policiesProcessed);
}
function _resolvePayout(
NftId policyNftId,
Amount payoutAmount
)
internal
virtual
{
// no action if no payout
if (payoutAmount.eqz()) {
return;
}
// create confirmed claim
ClaimId claimId = _submitClaim(policyNftId, payoutAmount, "");
_confirmClaim(policyNftId, claimId, payoutAmount, "");
// create and execute payout
PayoutId payoutId = _createPayout(policyNftId, claimId, payoutAmount, "");
_processPayout(policyNftId, payoutId);
}
function _initialize(
address registry,
NftId instanceNftId,
string memory componentName,
IAuthorization authorization,
address initialOwner
)
internal
initializer()
{
__Product_init(
registry,
instanceNftId,
componentName,
IComponents.ProductInfo({
isProcessingFundedClaims: false,
isInterceptingPolicyTransfers: false,
hasDistribution: false,
expectedNumberOfOracles: 1,
numberOfOracles: 0,
poolNftId: NftIdLib.zero(),
distributionNftId: NftIdLib.zero(),
oracleNftId: new NftId[](0)
}),
IComponents.FeeInfo({
productFee: FeeLib.zero(),
processingFee: FeeLib.zero(),
distributionFee: FeeLib.zero(),
minDistributionOwnerFee: FeeLib.zero(),
poolFee: FeeLib.zero(),
stakingFee: FeeLib.zero(),
performanceFee: FeeLib.zero()
}),
authorization,
initialOwner); // number of oracles
MAX_FLIGHT_DURATION = SecondsLib.fromDays(2);
LIFETIME = SecondsLib.fromDays(30);
WEIGHT_PATTERN = [0, 0, 0, 30, 50, 50];
MIN_OBSERVATIONS = 10;
MARGIN_PERCENT = 30;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Blocknumber, BlocknumberLib} from "../../type/Blocknumber.sol";
import {NftId} from "../../type/NftId.sol";
import {Amount} from "../../type/Amount.sol";
contract BalanceStore {
error ErrorBalanceStoreTargetAlreadyRegistered(NftId targetNftId);
error ErrorBalanceStoreTargetNotRegistered(NftId targetNftId);
event LogBalanceStoreTargetRegistered(NftId targetNftId);
event LogBalanceStoreFeesIncreased(NftId targetNftId, Amount addedAmount, Amount newBalance, Blocknumber lastUpdatedIn);
event LogBalanceStoreFeesDecreased(NftId targetNftId, Amount addedAmount, Amount newBalance, Blocknumber lastUpdatedIn);
event LogBalanceStoreLockedIncreased(NftId targetNftId, Amount addedAmount, Amount newBalance, Blocknumber lastUpdatedIn);
event LogBalanceStoreLockedDecreased(NftId targetNftId, Amount addedAmount, Amount newBalance, Blocknumber lastUpdatedIn);
event LogBalanceStoreBalanceIncreased(NftId targetNftId, Amount addedAmount, Amount newBalance, Blocknumber lastUpdatedIn);
event LogBalanceStoreBalanceDecreased(NftId targetNftId, Amount addedAmount, Amount newBalance, Blocknumber lastUpdatedIn);
mapping(NftId nftId => Amount balance) private _balanceAmount;
mapping(NftId nftId => Amount locked) private _lockedAmount;
mapping(NftId nftId => Amount fees) private _feeAmount;
// used to indicate if the target has been registered as well as when it was last updated (not used externally atm)
mapping(NftId nftId => Blocknumber lastUpdatedIn) private _lastUpdatedIn;
modifier onlyRegisteredTarget(NftId targetNftId) {
if (!_lastUpdatedIn[targetNftId].gtz()) {
revert ErrorBalanceStoreTargetNotRegistered(targetNftId);
}
_;
}
function getBalanceAmount(NftId targetNftId) external view returns (Amount balanceAmount) { return _balanceAmount[targetNftId]; }
function getLockedAmount(NftId targetNftId) external view returns (Amount lockedAmount) { return _lockedAmount[targetNftId]; }
function getFeeAmount(NftId targetNftId) external view returns (Amount feeAmount) { return _feeAmount[targetNftId]; }
function getAmounts(NftId targetNftId)
external
view
returns (
Amount balanceAmount,
Amount lockedAmount,
Amount feeAmount
)
{
balanceAmount = _balanceAmount[targetNftId];
lockedAmount = _lockedAmount[targetNftId];
feeAmount = _feeAmount[targetNftId];
}
function _registerBalanceTarget(NftId targetNftId) internal {
if (_lastUpdatedIn[targetNftId].gtz()) {
revert ErrorBalanceStoreTargetAlreadyRegistered(targetNftId);
}
_setLastUpdatedIn(targetNftId);
emit LogBalanceStoreTargetRegistered(targetNftId);
}
//--- fee management ----------------------------------------------------//
function _increaseFees(NftId targetNftId, Amount amount) internal onlyRegisteredTarget(targetNftId) returns (Amount newBalance) {
newBalance = _feeAmount[targetNftId] + amount;
_feeAmount[targetNftId] = newBalance;
emit LogBalanceStoreFeesIncreased(targetNftId, amount, newBalance, _lastUpdatedIn[targetNftId]);
_setLastUpdatedIn(targetNftId);
}
function _decreaseFees(NftId targetNftId, Amount amount) internal onlyRegisteredTarget(targetNftId) returns (Amount newBalance) {
newBalance = _feeAmount[targetNftId] - amount;
_feeAmount[targetNftId] = newBalance;
emit LogBalanceStoreFeesDecreased(targetNftId, amount, newBalance, _lastUpdatedIn[targetNftId]);
_setLastUpdatedIn(targetNftId);
}
//--- locked management -------------------------------------------------//
function _increaseLocked(NftId targetNftId, Amount amount) internal onlyRegisteredTarget(targetNftId) returns (Amount newBalance) {
newBalance = _lockedAmount[targetNftId] + amount;
_lockedAmount[targetNftId] = newBalance;
emit LogBalanceStoreLockedIncreased(targetNftId, amount, newBalance, _lastUpdatedIn[targetNftId]);
_setLastUpdatedIn(targetNftId);
}
function _decreaseLocked(NftId targetNftId, Amount amount) internal onlyRegisteredTarget(targetNftId) returns (Amount newBalance) {
newBalance = _lockedAmount[targetNftId] - amount;
_lockedAmount[targetNftId] = newBalance;
emit LogBalanceStoreLockedDecreased(targetNftId, amount, newBalance, _lastUpdatedIn[targetNftId]);
_setLastUpdatedIn(targetNftId);
}
//--- balance management ------------------------------------------------//
function _increaseBalance(NftId targetNftId, Amount amount) internal onlyRegisteredTarget(targetNftId) returns (Amount newBalance) {
newBalance = _balanceAmount[targetNftId] + amount;
_balanceAmount[targetNftId] = newBalance;
emit LogBalanceStoreBalanceIncreased(targetNftId, amount, newBalance, _lastUpdatedIn[targetNftId]);
_setLastUpdatedIn(targetNftId);
}
function _decreaseBalance(NftId targetNftId, Amount amount) internal onlyRegisteredTarget(targetNftId) returns (Amount newBalance) {
newBalance = _balanceAmount[targetNftId] - amount;
_balanceAmount[targetNftId] = newBalance;
emit LogBalanceStoreBalanceDecreased(targetNftId, amount, newBalance, _lastUpdatedIn[targetNftId]);
_setLastUpdatedIn(targetNftId);
}
//--- internal/private functions ----------------------------------------//
function _setLastUpdatedIn(NftId targetNftId) internal {
_lastUpdatedIn[targetNftId] = BlocknumberLib.current();
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import {IRegistry} from "../../registry/IRegistry.sol";
abstract contract Cloneable is
AccessManagedUpgradeable
{
IRegistry internal _registry;
/// @dev call to initialize MUST be made in the same transaction as cloning of the contract
function __Cloneable_init(
address authority,
address registry
)
internal
onlyInitializing
{
__AccessManaged_init(authority);
_registry = IRegistry(registry);
}
function getRegistry() external view returns (IRegistry) {
return _registry;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Blocknumber, BlocknumberLib} from "../../type/Blocknumber.sol";
import {NftId} from "../../type/NftId.sol";
import {Amount} from "../../type/Amount.sol";
import {RequestId, RequestIdLib} from "../../type/RequestId.sol";
contract ObjectCounter {
// TODO refactor risk id
// mapping(NftId productNftId => uint64 risks) private _riskCounter;
uint256 private _requestCounter = 0;
function _createNextRequestId() internal returns (RequestId requestId) {
_requestCounter++;
requestId = RequestIdLib.toRequestId(_requestCounter);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {COMPONENT, BUNDLE, POLICY, REQUEST, RISK, CLAIM, PAYOUT, POOL, PREMIUM, PRODUCT, DISTRIBUTION, DISTRIBUTOR, DISTRIBUTOR_TYPE, REFERRAL, FEE} from "../../type/ObjectType.sol";
import {ACTIVE, PAUSED, ARCHIVED, CLOSED, APPLIED, COLLATERALIZED, REVOKED, SUBMITTED, CONFIRMED, DECLINED, EXPECTED, PAID, FULFILLED, FAILED, CANCELLED} from "../../type/StateId.sol";
import {Lifecycle} from "../../shared/Lifecycle.sol";
contract ObjectLifecycle is
Lifecycle,
Initializable
{
function _initializeLifecycle() internal onlyInitializing
{
_setupLifecycle();
}
function _setupLifecycle()
internal
override
{
_setupBundleLifecycle();
_setupComponentLifecycle();
_setupPolicyLifecycle();
_setupPremiumLifecycle();
_setupClaimLifecycle();
_setupPayoutLifecycle();
_setupRiskLifecycle();
_setupRequestLifecycle();
// setup dummy lifecycles to manage with key value store
_setUpPoolLifecycle();
_setUpProductLifecycle();
_setUpDistributionLifecycle();
}
function _setupComponentLifecycle() private {
setInitialState(COMPONENT(), ACTIVE());
setStateTransition(COMPONENT(), ACTIVE(), PAUSED());
setStateTransition(COMPONENT(), PAUSED(), ACTIVE());
setStateTransition(COMPONENT(), PAUSED(), ARCHIVED());
}
function _setupBundleLifecycle() private {
setInitialState(BUNDLE(), ACTIVE());
setStateTransition(BUNDLE(), ACTIVE(), CLOSED());
}
function _setupPolicyLifecycle() private {
setInitialState(POLICY(), APPLIED());
setStateTransition(POLICY(), APPLIED(), REVOKED());
setStateTransition(POLICY(), APPLIED(), DECLINED());
setStateTransition(POLICY(), APPLIED(), COLLATERALIZED());
setStateTransition(POLICY(), COLLATERALIZED(), CLOSED());
}
function _setupPremiumLifecycle() private {
setInitialState(PREMIUM(), EXPECTED());
setStateTransition(PREMIUM(), EXPECTED(), PAID());
}
function _setupClaimLifecycle() private {
setInitialState(CLAIM(), SUBMITTED());
setStateTransition(CLAIM(), SUBMITTED(), REVOKED());
setStateTransition(CLAIM(), SUBMITTED(), CONFIRMED());
setStateTransition(CLAIM(), SUBMITTED(), DECLINED());
setStateTransition(CLAIM(), CONFIRMED(), CLOSED());
setStateTransition(CLAIM(), CONFIRMED(), CANCELLED());
}
function _setupPayoutLifecycle() private {
setInitialState(PAYOUT(), EXPECTED());
setStateTransition(PAYOUT(), EXPECTED(), PAID());
setStateTransition(PAYOUT(), EXPECTED(), CANCELLED());
}
function _setupRiskLifecycle() private {
setInitialState(RISK(), ACTIVE());
setStateTransition(RISK(), ACTIVE(), CLOSED());
}
function _setupRequestLifecycle() private {
setInitialState(REQUEST(), ACTIVE());
setStateTransition(REQUEST(), ACTIVE(), FULFILLED());
setStateTransition(REQUEST(), ACTIVE(), FAILED());
setStateTransition(REQUEST(), FAILED(), FULFILLED());
setStateTransition(REQUEST(), ACTIVE(), CANCELLED());
}
// dummy lifecycle only
function _setUpPoolLifecycle() private {
setInitialState(POOL(), ACTIVE());
}
// dummy lifecycle only
function _setUpProductLifecycle() private {
setInitialState(PRODUCT(), ACTIVE());
setInitialState(FEE(), ACTIVE());
}
// dummy lifecycles only
function _setUpDistributionLifecycle() private {
setInitialState(DISTRIBUTION(), ACTIVE());
setInitialState(DISTRIBUTOR(), ACTIVE());
setInitialState(DISTRIBUTOR_TYPE(), ACTIVE());
setInitialState(REFERRAL(), ACTIVE());
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Cloneable} from "./Cloneable.sol";
import {LibKey32Set} from "../../type/Key32Set.sol";
import {NftId} from "../../type/NftId.sol";
import {Key32} from "../../type/Key32.sol";
contract ObjectSet is
Cloneable
{
using LibKey32Set for LibKey32Set.Set;
event LogObjectSetInitialized(address instance);
error ErrorObjectSetNftIdInvalid(NftId instanceNftId);
mapping(NftId compnentNftId => LibKey32Set.Set objects) internal _activeObjects;
mapping(NftId compnentNftId => LibKey32Set.Set objects) internal _allObjects;
address internal _instanceAddress;
/// @dev This initializer needs to be called from the instance itself.
function initialize(address authority, address registry)
external
initializer()
{
_instanceAddress = msg.sender;
__Cloneable_init(authority, registry);
emit LogObjectSetInitialized(address(_instanceAddress));
}
function getInstanceAddress() external view returns (address) {
return _instanceAddress;
}
function _add(NftId componentNftId, Key32 key) internal {
LibKey32Set.Set storage allSet = _allObjects[componentNftId];
LibKey32Set.Set storage activeSet = _activeObjects[componentNftId];
allSet.add(key);
activeSet.add(key);
}
function _activate(NftId componentNftId, Key32 key) internal {
_activeObjects[componentNftId].add(key);
}
function _deactivate(NftId componentNftId, Key32 key) internal {
_activeObjects[componentNftId].remove(key);
}
function _objects(NftId componentNftId) internal view returns (uint256) {
return _allObjects[componentNftId].size();
}
function _contains(NftId componentNftId, Key32 key) internal view returns (bool) {
return _allObjects[componentNftId].contains(key);
}
function _getObject(NftId componentNftId, uint256 idx) internal view returns (Key32) {
return _allObjects[componentNftId].getElementAt(idx);
}
function _activeObjs(NftId componentNftId) internal view returns (uint256) {
return _activeObjects[componentNftId].size();
}
function _isActive(NftId componentNftId, Key32 key) internal view returns (bool) {
return _activeObjects[componentNftId].contains(key);
}
function _getActiveObject(NftId componentNftId, uint256 idx) internal view returns (Key32) {
return _activeObjects[componentNftId].getElementAt(idx);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IBundle} from "../module/IBundle.sol";
import {IInstance} from "../IInstance.sol";
import {IRisk} from "../module/IRisk.sol";
import {NftId} from "../../type/NftId.sol";
import {RiskId} from "../../type/RiskId.sol";
library ObjectSetHelperLib {
function getRiskInfo(address instanceAddress, RiskId riskId) public view returns (IRisk.RiskInfo memory) {
return IInstance(instanceAddress).getInstanceReader().getRiskInfo(riskId);
}
function getProductNftId(address instanceAddress, RiskId riskId) public view returns (NftId) {
return getRiskInfo(instanceAddress, riskId).productNftId;
}
function getBundleInfo(address instanceAddress, NftId bundleNftId) public view returns (IBundle.BundleInfo memory) {
return IInstance(instanceAddress).getInstanceReader().getBundleInfo(bundleNftId);
}
function getPoolNftId(address instanceAddress, NftId bundleNftId) public view returns (NftId) {
return getBundleInfo(instanceAddress, bundleNftId).poolNftId;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IBaseStore} from "./IBaseStore.sol";
import {Blocknumber, BlocknumberLib} from "../type/Blocknumber.sol";
import {Key32, KeyId, Key32Lib} from "../type/Key32.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {StateId, KEEP_STATE} from "../type/StateId.sol";
import {Lifecycle} from "../shared/Lifecycle.sol";
abstract contract BaseStore is
Lifecycle,
IBaseStore
{
mapping(Key32 key32 => IBaseStore.Metadata metadata) private _metadata;
function _createMetadata(
Key32 key32
)
internal
{
ObjectType objectType = key32.toObjectType();
if (objectType.eqz()) {
revert ErrorBaseStoreTypeUndefined(objectType);
}
Metadata storage metadata = _metadata[key32];
if (metadata.updatedIn.gtz()) {
revert ErrorBaseStoreAlreadyCreated(key32, objectType);
}
if(!hasLifecycle(objectType)) {
revert ErrorBaseStoreNoLifecycle(objectType);
}
Blocknumber blocknumber = BlocknumberLib.current();
StateId initialState = getInitialState(objectType);
// set metadata
metadata.objectType = objectType;
metadata.state = initialState;
metadata.updatedIn = blocknumber;
}
function _updateState(
Key32 key32,
StateId state
)
internal
returns (Blocknumber lastUpdatedIn, StateId oldState)
{
if (state.eqz()) {
revert ErrorBaseStoreStateZero(key32);
}
Metadata storage metadata = _metadata[key32];
oldState = metadata.state;
lastUpdatedIn = metadata.updatedIn;
if (oldState.eqz()) {
revert ErrorBaseStoreNotExisting(key32);
}
// update state
if(state != KEEP_STATE()) {
checkTransition(oldState, metadata.objectType, oldState, state);
metadata.state = state;
// solhint-disable-next-line avoid-tx-origin
}
// update metadata
metadata.updatedIn = BlocknumberLib.current();
}
function exists(Key32 key32) public view returns (bool) {
return _metadata[key32].state.gtz();
}
function getMetadata(Key32 key32) public view returns (Metadata memory metadata) {
return _metadata[key32];
}
function getState(Key32 key32) public view returns (StateId state) {
return _metadata[key32].state;
}
function toKey32(ObjectType objectType, KeyId id) external pure override returns(Key32) {
return Key32Lib.toKey32(objectType, id);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {LibNftIdSet} from "../type/NftIdSet.sol";
import {NftId, NftIdLib} from "../type/NftId.sol";
import {Key32} from "../type/Key32.sol";
import {BUNDLE} from "../type/ObjectType.sol";
import {ObjectSet} from "./base/ObjectSet.sol";
import {ObjectSetHelperLib} from "./base/ObjectSetHelperLib.sol";
contract BundleSet is
ObjectSet
{
using LibNftIdSet for LibNftIdSet.Set;
event LogBundleSetPolicyLinked(NftId bundleNftId, NftId policyNftId);
event LogBundleSetPolicyUnlinked(NftId bundleNftId, NftId policyNftId);
event LogBundleSetBundleAdded(NftId poolNftId, NftId bundleNftId);
event LogBundleSetBundleUnlocked(NftId poolNftId, NftId bundleNftId);
event LogBundleSetBundleLocked(NftId poolNftId, NftId bundleNftId);
event LogBundleSetBundleClosed(NftId poolNftId, NftId bundleNftId);
error ErrorBundleSetPolicyAlreadyActivated(NftId policyNftId);
error ErrorBundleSetBundleLocked(NftId bundleNftId, NftId policyNftId);
error ErrorBundleSetPolicyWithOpenClaims(NftId policyNftId, uint256 openClaimsCount);
error ErrorBundleSetPolicyNotCloseable(NftId policyNftId);
error ErrorBundleSetBundleUnknown(NftId bundleNftId);
error ErrorBundleSetBundleNotRegistered(NftId bundleNftId);
mapping(NftId bundleNftId => LibNftIdSet.Set policies) internal _activePolicies;
/// @dev links a policy to its bundle
function linkPolicy(NftId poolNftId, NftId bundleNftId, NftId policyNftId) external restricted() {
// ensure bundle is unlocked (in active set) and registered with this instance
if (!_isActive(poolNftId, _toBundleKey32(bundleNftId))) {
revert ErrorBundleSetBundleLocked(bundleNftId, policyNftId);
}
_activePolicies[bundleNftId].add(policyNftId);
emit LogBundleSetPolicyLinked(bundleNftId, policyNftId);
}
/// @dev unlinks a policy from its bundle
function unlinkPolicy(NftId poolNftId, NftId bundleNftId, NftId policyNftId) external restricted() {
// ensure bundle is registered with this instance
if (!_contains(poolNftId, _toBundleKey32(bundleNftId))) {
revert ErrorBundleSetBundleUnknown(bundleNftId);
}
_activePolicies[bundleNftId].remove(policyNftId);
emit LogBundleSetPolicyUnlinked(bundleNftId, policyNftId);
}
/// @dev add a new bundle to a pool registerd with this instance
// the corresponding pool is fetched via instance reader
function add(NftId bundleNftId) external restricted() {
NftId poolNftId = ObjectSetHelperLib.getPoolNftId(_instanceAddress, bundleNftId);
// ensure pool is registered with instance
if(poolNftId.eqz()) {
revert ErrorBundleSetBundleNotRegistered(bundleNftId);
}
_add(poolNftId, _toBundleKey32(bundleNftId));
emit LogBundleSetBundleAdded(poolNftId, bundleNftId);
}
/// @dev unlocked (active) bundles are available to collateralize new policies
function unlock(NftId bundleNftId) external restricted() {
NftId poolNftId = ObjectSetHelperLib.getPoolNftId(_instanceAddress, bundleNftId);
_activate(poolNftId, _toBundleKey32(bundleNftId));
emit LogBundleSetBundleUnlocked(poolNftId, bundleNftId);
}
/// @dev locked (deactivated) bundles may not collateralize any new policies
function lock(NftId bundleNftId) external restricted() {
NftId poolNftId = ObjectSetHelperLib.getPoolNftId(_instanceAddress, bundleNftId);
_deactivate(poolNftId, _toBundleKey32(bundleNftId));
emit LogBundleSetBundleLocked(poolNftId, bundleNftId);
}
function checkBundle(NftId productNftId, NftId bundleId)
public
view
returns (bool exists, bool active)
{
Key32 bundleKey32 = bundleId.toKey32(BUNDLE());
exists = _contains(productNftId, bundleKey32);
if (exists) {
active = _isActive(productNftId, bundleKey32);
}
}
function bundles(NftId poolNftId) external view returns(uint256) {
return _objects(poolNftId);
}
function getBundleNftId(NftId poolNftId, uint256 idx) external view returns(NftId) {
return NftIdLib.toNftId(_getObject(poolNftId, idx).toKeyId());
}
function activeBundles(NftId poolNftId) external view returns(uint256) {
return _activeObjs(poolNftId);
}
function getActiveBundleNftId(NftId poolNftId, uint256 idx) external view returns(NftId) {
return NftIdLib.toNftId(_getActiveObject(poolNftId, idx).toKeyId());
}
function activePolicies(NftId bundleNftId) external view returns(uint256) {
return _activePolicies[bundleNftId].size();
}
function getActivePolicy(NftId bundleNftId, uint256 idx) external view returns(NftId policyNftId) {
return _activePolicies[bundleNftId].getElementAt(idx);
}
function _toBundleKey32(NftId nftId) private pure returns (Key32) {
return nftId.toKey32(BUNDLE());
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {ILifecycle} from "../shared/ILifecycle.sol";
import {Blocknumber} from "../type/Blocknumber.sol";
import {Key32, KeyId} from "../type/Key32.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {StateId} from "../type/StateId.sol";
interface IBaseStore is ILifecycle {
error ErrorBaseStoreTypeUndefined(ObjectType objectType);
error ErrorBaseStoreAlreadyCreated(Key32 key, ObjectType objectType);
error ErrorBaseStoreNoLifecycle(ObjectType objectType);
error ErrorBaseStoreStateZero(Key32 key);
error ErrorBaseStoreNotExisting(Key32 key);
struct Metadata {
// slot 0
ObjectType objectType;
StateId state;
Blocknumber updatedIn;
}
/// @dev check if a metadata entry with the key exists
function exists(Key32 key) external view returns (bool);
/// @dev retrieve the metadata for a given key
function getMetadata(Key32 key) external view returns (Metadata memory metadata);
/// @dev retrieve the state for a given key
function getState(Key32 key) external view returns (StateId state);
/// @dev convert an object type and an id to a key32
function toKey32(ObjectType objectType, KeyId id) external pure returns(Key32);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IAccess} from "../authorization/IAccess.sol";
import {IRegisterable} from "../shared/IRegisterable.sol";
import {Amount} from "../type/Amount.sol";
import {BundleSet} from "./BundleSet.sol";
import {RiskSet} from "./RiskSet.sol";
import {InstanceAdmin} from "./InstanceAdmin.sol";
import {InstanceReader} from "./InstanceReader.sol";
import {InstanceStore} from "./InstanceStore.sol";
import {NftId} from "../type/NftId.sol";
import {ProductStore} from "./ProductStore.sol";
import {RoleId} from "../type/RoleId.sol";
import {Seconds} from "../type/Seconds.sol";
import {UFixed} from "../type/UFixed.sol";
interface IInstance is
IRegisterable
{
// role handling
event LogInstanceCustomRoleCreated(RoleId roleId, string roleName, RoleId adminRoleId, uint32 maxMemberCount);
event LogInstanceCustomRoleActiveSet(RoleId roleId, bool active, address caller);
event LogInstanceCustomRoleGranted(RoleId roleId, address account, address caller);
event LogInstanceCustomRoleRevoked(RoleId roleId, address account, address caller);
// target handling
event LogInstanceCustomTargetCreated(address target, RoleId targetRoleId, string name);
event LogInstanceTargetLocked(address target, bool locked);
event LogInstanceCustomTargetFunctionRoleSet(address target, bytes4[] selectors, RoleId roleId);
// modifier is onlyRoleAdmin
error ErrorInstanceNotCustomRole(RoleId roleId);
error ErrorInstanceNotRoleAdmin(RoleId roleId, address account);
error ErrorInstanceInstanceAdminZero();
error ErrorInstanceInstanceAdminAlreadySet(address instanceAdmin);
error ErrorInstanceInstanceAdminAuthorityMismatch(address instanceAuthority);
error ErrorInstanceBundleSetAlreadySet(address instanceBundleSet);
error ErrorInstanceBundleSetInstanceMismatch(address instance);
error ErrorInstanceBundleSetAuthorityMismatch(address instanceAuthority);
error ErrorInstanceRiskSetAlreadySet(address instanceRiskSet);
error ErrorInstanceRiskSetInstanceMismatch(address instance);
error ErrorInstanceRiskSetAuthorityMismatch(address instanceAuthority);
error ErrorInstanceInstanceReaderInstanceMismatch(address instanceAuthority);
error ErrorInstanceInstanceStoreAlreadySet(address instanceStore);
error ErrorInstanceInstanceStoreAuthorityMismatch(address instanceAuthority);
struct InstanceContracts {
InstanceAdmin instanceAdmin;
InstanceStore instanceStore;
ProductStore productStore;
BundleSet bundleSet;
RiskSet riskSet;
InstanceReader instanceReader;
}
struct InstanceInfo {
uint64 requestsCount;
}
///--- instance ---------------------------------------------------------//
/// @dev Locks/unlocks the complete instance, including all its components.
function setInstanceLocked(bool locked) external;
/// @dev Upgrades the instance reader to the specified target.
function upgradeInstanceReader() external;
/// @dev Sets the instance reader for the instance.
/// Permissioned: only the instance service may call this function.
function setInstanceReader(InstanceReader instanceReader) external;
///--- staking ----------------------------------------------------------//
/// @dev Sets the duration for locking new stakes on this instance..
function setStakingLockingPeriod(Seconds stakeLockingPeriod) external;
/// @dev Sets the staking reward rate [apr] for this instance.
function setStakingRewardRate(UFixed rewardRate) external;
/// @dev Sets the maximum staked amount for this instance.
function setStakingMaxAmount(Amount maxStakedAmount) external;
/// @dev Refills the staking reward reserves for the specified target.
function refillStakingRewardReserves(Amount dipAmount) external returns (Amount newBalance);
/// @dev Defunds the staking reward reserves for the specified target.
function withdrawStakingRewardReserves(Amount dipAmount) external returns (Amount newBalance);
///--- product/component ------------------------------------------------//
/// @dev Locks/unlocks the specified target.
function setTargetLocked(address target, bool locked) external;
/// @dev Register a product with the instance.
function registerProduct(address product, address token) external returns (NftId productNftId);
///--- authz ------------------------------------------------------------//
/// @dev Creates a new custom role for the calling instance.
/// Custom roles are intended to be used for access control of custom components and its helper contracts.
/// Custom roles are not intended to be used as target roles for custom contracts.
function createRole(string memory roleName, RoleId adminRoleId, uint32 maxMemberCount) external returns (RoleId roleId);
/// @dev Activates/deactivates the specified role.
/// Only instance owner or account with role admin role can call this function.
function setRoleActive(RoleId roleId, bool active) external;
/// @dev Grants the specified role to the account.
/// Only active roles can be granted.
/// Only instance owner or account with role admin role can call this function.
function grantRole(RoleId roleId, address account) external;
/// @dev Revokes the specified role from the account.
/// Only instance owner or account with role admin role can call this function.
function revokeRole(RoleId roleId, address account) external;
/// @dev Creates a new custom target.
/// Custom targets are intended to be used for access control helper contracts of components.
/// Custom targets are not intended to be used for components.
function createTarget(address target, string memory name) external returns (RoleId contractRoleId);
/// @dev Authorizes the specified functions for the target and provided role.
function authorizeFunctions(address target, RoleId roleId, IAccess.FunctionInfo[] memory functions) external;
/// @dev Removes any role authorization for the specified functions.
function unauthorizeFunctions(address target, IAccess.FunctionInfo[] memory functions) external;
//--- getters -----------------------------------------------------------//
/// @dev returns the overall locking state of the instance (including all components)
function isInstanceLocked() external view returns (bool isLocked);
/// @dev returns the locking state of the specified target
function isTargetLocked(address target) external view returns (bool isLocked);
// get products
function products() external view returns (uint256 productCount);
function getProduct(uint256 idx) external view returns (NftId productNftId);
// get supporting contracts
function getInstanceReader() external view returns (InstanceReader);
function getBundleSet() external view returns (BundleSet);
function getRiskSet() external view returns (RiskSet);
function getInstanceAdmin() external view returns (InstanceAdmin);
function getInstanceStore() external view returns (InstanceStore);
function getProductStore() external view returns (ProductStore);
function isTokenRegistryDisabled() external view returns (bool);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IAccess} from "../authorization/IAccess.sol";
import {IInstance} from "./IInstance.sol";
import {IService} from "../shared/IService.sol";
import {Amount} from "../type/Amount.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RoleId} from "../type/RoleId.sol";
import {Seconds} from "../type/Seconds.sol";
import {UFixed} from "../type/UFixed.sol";
import {VersionPart} from "../type/Version.sol";
interface IInstanceService is IService {
// onlyInstance
error ErrorInstanceServiceNotRegistered(address instance);
error ErrorInstanceServiceNotInstance(address instance, ObjectType objectType);
error ErrorInstanceServiceInstanceVersionMismatch(NftId instanceNftId, VersionPart expectedRelease, VersionPart instanceRelease);
error ErrorInstanceServiceComponentNotInstanceLinked(address component);
error ErrorInstanceServiceMasterInstanceAlreadySet();
error ErrorInstanceServiceMasterInstanceAdminAlreadySet();
error ErrorInstanceServiceMasterBundleSetAlreadySet();
error ErrorInstanceServiceMasterRiskSetAlreadySet();
error ErrorInstanceServiceInstanceAddressZero();
error ErrorInstanceServiceMasterInstanceReaderNotSet();
error ErrorInstanceServiceInstanceReaderAddressZero();
error ErrorInstanceServiceInstanceReaderSameAsMasterInstanceReader();
error ErrorInstanceServiceInstanceReaderInstanceMismatch();
error ErrorInstanceServiceAccessManagerZero();
error ErrorInstanceServiceInstanceAdminZero();
error ErrorInstanceServiceInstanceReaderZero();
error ErrorInstanceServiceBundleSetZero();
error ErrorInstanceServiceRiskSetZero();
error ErrorInstanceServiceInstanceStoreZero();
error ErrorInstanceServiceProductStoreZero();
error ErrorInstanceServiceInstanceAuthorityMismatch();
error ErrorInstanceServiceBundleSetAuthorityMismatch();
error ErrorInstanceServiceRiskSetAuthorityMismatch();
error ErrorInstanceServiceInstanceReaderInstanceMismatch2();
error ErrorInstanceServiceBundleSetInstanceMismatch();
error ErrorInstanceServiceRiskSetInstanceMismatch();
error ErrorInstanceServiceInstanceStoreAuthorityMismatch();
error ErrorInstanceServiceProductStoreAuthorityMismatch();
error ErrorInstanceServiceRequestUnauhorized(address caller);
error ErrorInstanceServiceNotInstanceNftId(NftId nftId);
error ErrorInstanceServiceComponentNotRegistered(address componentAddress);
error ErrorInstanceServiceInstanceComponentMismatch(NftId instanceNftId, NftId componentNftId);
error ErrorInstanceServiceInvalidComponentType(address componentAddress, ObjectType expectedType, ObjectType componentType);
event LogInstanceServiceInstanceLocked(NftId instanceNftId, bool locked);
event LogInstanceServiceInstanceCreated(NftId instanceNftId, address instance);
event LogInstanceServiceMasterInstanceReaderUpgraded(NftId instanceNfId, address newInstanceReader);
event LogInstanceServiceInstanceReaderUpgraded(NftId instanceNfId, address newInstanceReader);
/// @dev Creates a new custom role for the calling instance.
function createRole(string memory roleName, RoleId adminRoleId, uint32 maxMemberCount) external returns (RoleId roleId);
/// @dev Sets the specified custom role as active or inactive for the calling instance.
function setRoleActive(RoleId roleId, bool active) external;
/// @dev Grants the specified custom role to the specified account for the calling instance.
function grantRole(RoleId roleId, address account) external;
/// @dev Revokes the specified custom role from the specified account for the calling instance.
function revokeRole(RoleId roleId, address account) external;
/// @dev Creates a new custom target for the calling instance.
/// All custom trargets are created with a corresponding contract role.
function createTarget(address target, string memory name) external returns (RoleId contractRoleId);
/// @dev Authorizes the specified functions for the specified target.
function authorizeFunctions(address target, RoleId roleId, IAccess.FunctionInfo[] memory functions) external;
/// @dev Removes any role authorization for the specified functions.
function unauthorizeFunctions(address target, IAccess.FunctionInfo[] memory functions) external;
/// @dev Locks/unlocks the specified target constrolled by the corresponding instance admin.
function setTargetLocked(address target, bool locked) external;
/// @dev Locks the complete instance, including all its components.
function setInstanceLocked(bool locked) external;
/// @dev Creates a new instance.
/// The caller becomes the owner of the new instance.
/// Creation of a new instance is achieved by this service through the creation and registration
/// of a new clone of the master instance and then setting up the initial wiring and authorization
/// of the necessary components.
/// @param allowAnyToken specifies whether the new instance is allowed to use any token or
/// only tokens that are registered in the `TokenRegistry`. Use of tokens **not** registered in the
/// `TokenRegistry` may result in problems with token handline and is therefor not recommended. The
/// use of such instances **is not supported** by Etherisc.
function createInstance(bool allowAnyToken)
external
returns (
IInstance instance,
NftId instanceNftId
);
function upgradeInstanceReader() external;
function upgradeMasterInstanceReader(address instanceReaderAddress) external;
function setStakingLockingPeriod(Seconds stakeLockingPeriod) external;
function setStakingRewardRate(UFixed rewardRate) external;
function setStakingMaxAmount(Amount maxStakedAmount) external;
function refillInstanceRewardReserves(address rewardProvider, Amount dipAmount) external returns (Amount newBalance);
/// @dev Defunds the staking reward reserves for the specified target.
function withdrawInstanceRewardReserves(Amount dipAmount) external returns (Amount newBalance);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import {IAccess} from "../authorization/IAccess.sol";
import {IAuthorization} from "../authorization/IAuthorization.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IInstance} from "./IInstance.sol";
import {AccessAdmin} from "../authorization/AccessAdmin.sol";
import {AccessAdminLib} from "../authorization/AccessAdminLib.sol";
import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol";
import {ObjectType, INSTANCE} from "../type/ObjectType.sol";
import {RoleId, ADMIN_ROLE} from "../type/RoleId.sol";
import {Str} from "../type/String.sol";
import {VersionPart} from "../type/Version.sol";
import {INSTANCE_TARGET_NAME, INSTANCE_ADMIN_TARGET_NAME, INSTANCE_STORE_TARGET_NAME, PRODUCT_STORE_TARGET_NAME, BUNDLE_SET_TARGET_NAME, RISK_SET_TARGET_NAME} from "./TargetNames.sol";
contract InstanceAdmin is
AccessAdmin
{
// onlyInstanceService
error ErrorInstanceAdminNotInstanceService(address caller);
// authorizeFunctions
error ErrorInstanceAdminNotComponentOrCustomTarget(address target);
IInstance internal _instance;
IRegistry internal _registry;
uint64 internal _customRoleIdNext;
modifier onlyInstanceService() {
if (msg.sender != _registry.getServiceAddress(INSTANCE(), getRelease())) {
revert ErrorInstanceAdminNotInstanceService(msg.sender);
}
_;
}
/// @dev Only used for master instance admin.
constructor(address accessManager) {
initialize(
accessManager,
"MasterInstanceAdmin");
}
/// @dev Completes the initialization of this instance admin using the provided instance, registry and version.
/// Important: Initialization of instance admin is only complete after calling this function.
/// Important: The instance MUST be registered and all instance supporting contracts must be wired to this instance.
function completeSetup(
address registry,
address authorization,
VersionPart release,
address instance
)
external
reinitializer(uint64(release.toInt()))
{
// checks
AccessAdminLib.checkIsRegistered(registry, instance, INSTANCE());
// effects
AccessManagerCloneable(
authority()).completeSetup(
registry,
release);
AccessAdminLib.checkAuthorization(
address(_authorization),
authorization,
INSTANCE(), // expectedDomain
release, // expectedRelease
false, // expectServiceAuthorization
true); // checkAlreadyInitialized
_registry = IRegistry(registry);
_instance = IInstance(instance);
_authorization = IAuthorization(authorization);
_customRoleIdNext = 0;
// link nft ownability to instance
_linkToNftOwnable(instance);
// setup roles and services
_createRoles(_authorization);
_setupServices(_authorization);
// setup instance targets
_createInstanceTargets(_authorization.getMainTargetName());
// authorize functions of instance contracts
_createTargetAuthorizations(_authorization);
}
/// @dev grants the service roles to the service addresses based on the authorization specification.
/// Service addresses used for the granting are determined by the registry and the release of this instance.
function _setupServices(IAuthorization authorization)
internal
{
ObjectType[] memory serviceDomains = authorization.getServiceDomains();
for(uint256 i = 0; i < serviceDomains.length; i++) {
ObjectType serviceDomain = serviceDomains[i];
RoleId serviceRoleId = authorization.getServiceRole(serviceDomain);
address service = _registry.getServiceAddress(serviceDomain, getRelease());
_grantRoleToAccount(
serviceRoleId,
service);
}
}
function _createInstanceTargets(string memory instanceTargetName)
internal
{
_createInstanceTarget(address(_instance), instanceTargetName);
_createInstanceTarget(address(this), INSTANCE_ADMIN_TARGET_NAME);
_createInstanceTarget(address(_instance.getInstanceStore()), INSTANCE_STORE_TARGET_NAME);
_createInstanceTarget(address(_instance.getProductStore()), PRODUCT_STORE_TARGET_NAME);
_createInstanceTarget(address(_instance.getBundleSet()), BUNDLE_SET_TARGET_NAME);
_createInstanceTarget(address(_instance.getRiskSet()), RISK_SET_TARGET_NAME);
}
function _createInstanceTarget(address target, string memory name) internal {
_createTarget(target, name, TargetType.Instance, true);
}
/// @dev Initializes the authorization for the specified component.
/// Important: The component MUST be registered.
function initializeComponentAuthorization(
address componentAddress,
ObjectType expectedType
)
external
restricted()
{
IAuthorization authorization = AccessAdminLib.checkComponentInitialization(
this, _authorization, componentAddress, expectedType);
// effects
_createRoles(authorization);
_createTarget(componentAddress, authorization.getMainTargetName(), TargetType.Component, true);
_createTargetAuthorizations(authorization);
}
/// @dev Creates a custom role.
function createRole(
string memory name,
RoleId adminRoleId,
uint32 maxMemberCount
)
external
restricted()
returns (RoleId roleId)
{
// create roleId
roleId = AccessAdminLib.getCustomRoleId(_customRoleIdNext++);
// create role
_createRole(
roleId,
AccessAdminLib.roleInfo(
adminRoleId,
IAccess.TargetType.Custom,
maxMemberCount,
name),
true); // revert on existing role
}
/// @dev Activtes/pauses the specified role.
function setRoleActive(RoleId roleId, bool active)
external
restricted()
{
_setRoleActive(roleId, active);
}
/// @dev Grants the provided role to the specified account
function grantRole(
RoleId roleId,
address account)
external
restricted()
{
_grantRoleToAccount(roleId, account);
}
/// @dev Revokes the provided role from the specified account
function revokeRole(
RoleId roleId,
address account)
external
restricted()
{
_revokeRoleFromAccount(roleId, account);
}
/// @dev Create a new contract target.
/// The target needs to be an access managed contract.
function createTarget(
address target,
string memory name
)
external
restricted()
returns (RoleId contractRoleId)
{
return _createTarget(
target,
name,
TargetType.Contract,
true); // check authority matches
}
/// @dev Add function authorizations for the specified component or custom target.
function authorizeFunctions(
address target,
RoleId roleId,
IAccess.FunctionInfo[] memory functions
)
external
restricted()
{
_authorizeTargetFunctions(target, roleId, functions, true, true);
}
/// @dev Removes function authorizations for the specified component or custom target.
function unauthorizeFunctions(
address target,
IAccess.FunctionInfo[] memory functions
)
external
restricted()
{
_authorizeTargetFunctions(target, ADMIN_ROLE(), functions, true, false);
}
/// @dev locks the instance and all its releated targets including component and custom targets.
function setInstanceLocked(bool locked)
external
// not restricted(): need to operate on locked instances to unlock instance
onlyInstanceService()
{
AccessManagerCloneable accessManager = AccessManagerCloneable(authority());
accessManager.setLocked(locked);
}
function setTargetLocked(address target, bool locked)
external
// not restricted(): need to operate on locked instances to unlock instance
onlyInstanceService()
{
_setTargetLocked(target, locked);
}
function setContractLocked(address target, bool locked)
external
restricted() // component service
{
_setTargetLocked(target, locked);
}
/// @dev Returns the instance authorization specification used to set up this instance admin.
function getInstanceAuthorization()
external
view
returns (IAuthorization instanceAuthorizaion)
{
return _authorization;
}
// ------------------- Internal functions ------------------- //
function _createTargetAuthorizations(IAuthorization authorization)
internal
{
Str[] memory targets = authorization.getTargets();
Str target;
for(uint256 i = 0; i < targets.length; i++) {
target = targets[i];
RoleId[] memory authorizedRoles = authorization.getAuthorizedRoles(target);
for(uint256 j = 0; j < authorizedRoles.length; j++) {
_authorizeFunctions(authorization, target, authorizedRoles[j]);
}
}
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IAccess} from "../authorization/IAccess.sol";
import {IBaseStore} from "./IBaseStore.sol";
import {IBundle} from "../instance/module/IBundle.sol";
import {IComponents} from "../instance/module/IComponents.sol";
import {IDistribution} from "../instance/module/IDistribution.sol";
import {IDistributionService} from "../distribution/IDistributionService.sol";
import {IInstance} from "./IInstance.sol";
import {IOracle} from "../oracle/IOracle.sol";
import {IPolicy} from "../instance/module/IPolicy.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IRisk} from "../instance/module/IRisk.sol";
import {AccessAdminLib} from "../authorization/AccessAdminLib.sol";
import {Amount} from "../type/Amount.sol";
import {BundleSet} from "./BundleSet.sol";
import {BUNDLE, COMPONENT, DISTRIBUTION, PREMIUM, POLICY} from "../type/ObjectType.sol";
import {ClaimId, ClaimIdLib} from "../type/ClaimId.sol";
import {DistributorType} from "../type/DistributorType.sol";
import {InstanceAdmin} from "./InstanceAdmin.sol";
import {InstanceStore} from "./InstanceStore.sol";
import {Key32} from "../type/Key32.sol";
import {NftId} from "../type/NftId.sol";
import {PayoutId, PayoutIdLib} from "../type/PayoutId.sol";
import {PolicyServiceLib} from "../product/PolicyServiceLib.sol";
import {ProductStore} from "./ProductStore.sol";
import {ReferralId, ReferralStatus, ReferralLib} from "../type/Referral.sol";
import {RequestId} from "../type/RequestId.sol";
import {RiskId} from "../type/RiskId.sol";
import {RiskSet} from "./RiskSet.sol";
import {RoleId, INSTANCE_OWNER_ROLE} from "../type/RoleId.sol";
import {StateId} from "../type/StateId.sol";
import {Str, StrLib} from "../type/String.sol";
import {TokenHandler} from "../shared/TokenHandler.sol";
import {UFixed, UFixedLib} from "../type/UFixed.sol";
/// @dev Central reader contract for a specific instance.
/// Provides reading functions for all instance data and related component data.
contract InstanceReader {
error ErrorInstanceReaderAlreadyInitialized();
error ErrorInstanceReaderInstanceAddressZero();
bool private _initialized = false;
IRegistry internal _registry;
IInstance internal _instance;
InstanceAdmin internal _instanceAdmin;
InstanceStore internal _store;
ProductStore internal _productStore;
BundleSet internal _bundleSet;
RiskSet internal _riskSet;
IDistributionService internal _distributionService;
/// @dev This initializer needs to be called from the instance itself.
function initialize() public {
if(_initialized) {
revert ErrorInstanceReaderAlreadyInitialized();
}
initializeWithInstance(msg.sender);
}
/// @dev Initializer to upgrade instance reader via instance service
function initializeWithInstance(address instanceAddress)
public
{
if(_initialized) {
revert ErrorInstanceReaderAlreadyInitialized();
}
_initialized = true;
_instance = IInstance(instanceAddress);
_instanceAdmin = _instance.getInstanceAdmin();
_registry = _instance.getRegistry();
_store = _instance.getInstanceStore();
_productStore = _instance.getProductStore();
_bundleSet = _instance.getBundleSet();
_riskSet = _instance.getRiskSet();
_distributionService = IDistributionService(_registry.getServiceAddress(DISTRIBUTION(), _instance.getRelease()));
}
//--- instance functions ---------------------------------------------------------//
/// @dev Returns the registry this instance is registered in.
function getRegistry() public view returns (IRegistry registry) {
return _registry;
}
/// @dev Returns the instance NFT ID.
function getInstanceNftId() public view returns (NftId instanceNftid) {
return _registry.getNftIdForAddress(address(_instance));
}
/// @dev Returns the instance contract.
function getInstance() public view returns (IInstance instance) {
return _instance;
}
//--- component functions ------------------------------------------------------//
/// @dev Returns the component info for the given component NFT ID.
function getComponentInfo(NftId componentNftId) public view returns (IComponents.ComponentInfo memory info) {
return _store.getComponentInfo(componentNftId);
}
/// @dev Returns the registered token for the given component NFT ID.
function getToken(NftId componentNftId) public view returns (IERC20Metadata token) {
TokenHandler tokenHandler = getTokenHandler(componentNftId);
if (address(tokenHandler) != address(0)) { return tokenHandler.TOKEN(); }
}
/// @dev Returns the current wallet address for the given component NFT ID.
/// The wallet address is either the component's own address or any other wallet address specified by the component owner.
/// The wallet holds the component's funds. Tokens collected by the component are transferred to the wallet and
/// Tokens distributed from the component are transferred from this wallet.
function getWallet(NftId componentNftId) public view returns (address wallet) {
IComponents.ComponentInfo memory info = getComponentInfo(componentNftId);
if (address(info.tokenHandler) != address(0)) {
return info.tokenHandler.getWallet();
}
}
/// @dev Returns the token handler for the given component NFT ID.
/// The token handler manages all transfers from/to the component's wallet.
/// To allow a component to collect funds from an account, it has to create a corresponding allowance from the
/// account to the address of the component's token handler.
function getTokenHandler(NftId componentNftId) public view returns (TokenHandler tokenHandler) {
IComponents.ComponentInfo memory info = _store.getComponentInfo(componentNftId);
if(address(info.tokenHandler) != address(0)) {
return info.tokenHandler;
}
}
/// @dev Returns the current token balance amount for the given component NFT ID.
/// The balance amount includes the fee amount.
function getBalanceAmount(NftId targetNftId) external view returns (Amount) {
return _store.getBalanceAmount(targetNftId);
}
/// @dev Returns the current fee amount for the given NFT ID.
/// The target NFT ID may reference a component, a distributor or a bundle.
function getFeeAmount(NftId targetNftId) external view returns (Amount) {
return _store.getFeeAmount(targetNftId);
}
/// @dev Returns the currently locked amount for the given NFT ID.
/// The target NFT ID may reference a pool or a bundle.
function getLockedAmount(NftId targetNftId) external view returns (Amount) {
return _store.getLockedAmount(targetNftId);
}
//--- product functions ------------------------------------------------------//
/// @dev Returns the number of registered products.
function products() public view returns (uint256 productCount) {
return _instance.products();
}
/// @dev Returns th product NFT ID for the given index.
function getProduct(uint256 idx) public view returns (NftId productNftId) {
return _instance.getProduct(idx);
}
/// @dev Returns the product info for the given product NFT ID.
function getProductInfo(NftId productNftId) public view returns (IComponents.ProductInfo memory info) {
return _productStore.getProductInfo(productNftId);
}
/// @dev Returns the current fee settings for the given product NFT ID.
function getFeeInfo(NftId productNftId) public view returns (IComponents.FeeInfo memory feeInfo) {
return _productStore.getFeeInfo(productNftId);
}
//--- risk functions ---------------------------------------------------------//
/// @dev Returns the total number of registered risks for the specified product.
function risks(NftId productNftId) public view returns (uint256 riskCount) {
return _riskSet.risks(productNftId);
}
/// @dev Returns the number of active risks for the specified product.
function activeRisks(NftId productNftId) public view returns (uint256 activeRiskCount) {
return _riskSet.activeRisks(productNftId);
}
/// @dev Returns the risk ID for the given product NFT ID and (registered) risk index.
function getRiskId(NftId productNftId, uint256 idx) public view returns (RiskId riskId) {
return _riskSet.getRiskId(productNftId, idx);
}
/// @dev Returns the active risk ID for the given product NFT ID and (active) risk index.
function getActiveRiskId(NftId productNftId, uint256 idx) public view returns (RiskId riskId) {
return _riskSet.getActiveRiskId(productNftId, idx);
}
/// @dev Returns true if the specified risk exists for the given product NFT ID.
function isProductRisk(NftId productNftId, RiskId riskId) public view returns (bool exists) {
return _riskSet.hasRisk(productNftId, riskId);
}
/// @dev Returns the risk info for the given risk ID.
function getRiskInfo(RiskId riskId) public view returns (IRisk.RiskInfo memory info) {
return _productStore.getRiskInfo(riskId);
}
/// @dev Returns the risk state for the given risk ID.
function getRiskState(RiskId riskId) public view returns (StateId stateId) {
return _productStore.getState(riskId.toKey32());
}
//--- policy functions -------------------------------------------------------//
/// @dev Returns the number of linked policies for the given risk ID.
function policiesForRisk(RiskId riskId) public view returns (uint256 linkedPolicies) {
return _riskSet.linkedPolicies(riskId);
}
/// @dev Returns the linked policy NFT ID for the given risk ID and index.
function getPolicyForRisk(RiskId riskId, uint256 idx) public view returns (NftId linkedPolicyNftId) {
return _riskSet.getLinkedPolicyNftId(riskId, idx);
}
/// @dev Returns the number of linked policies for the given bundle NFT ID.
function policiesForBundle(NftId bundleNftId) public view returns (uint256 linkedPolicies) {
return _bundleSet.activePolicies(bundleNftId);
}
/// @dev Returns the linked policy NFT ID for the given risk ID and index.
function getPolicyForBundle(NftId bundleNftId, uint256 idx) public view returns (NftId linkedPolicyNftId) {
return _bundleSet.getActivePolicy(bundleNftId, idx);
}
/// @dev Returns the info for the given policy NFT ID.
function getPolicyInfo(NftId policyNftId) public view returns (IPolicy.PolicyInfo memory info) {
return _productStore.getPolicyInfo(policyNftId);
}
/// @dev Returns the state for the given policy NFT ID.
function getPolicyState(NftId policyNftId) public view returns (StateId state) {
return _productStore.getState(_toPolicyKey(policyNftId));
}
/// @dev Returns true iff policy is active.
function policyIsActive(NftId policyNftId) public view returns (bool isCloseable) {
return PolicyServiceLib.policyIsActive(this, policyNftId);
}
//--- claim functions -------------------------------------------------------//
/// @dev Returns the number of claims for the given policy NFT ID.
function claims(NftId policyNftId) public view returns (uint16 claimCount) {
return getPolicyInfo(policyNftId).claimsCount;
}
/// @dev Returns the claim ID for the given policy NFT ID and index.
function getClaimId(uint256 idx) public pure returns (ClaimId claimId) {
return ClaimIdLib.toClaimId(idx + 1);
}
/// @dev Returns the claim info for the given policy NFT ID and claim ID.
function getClaimInfo(NftId policyNftId, ClaimId claimId) public view returns (IPolicy.ClaimInfo memory info) {
return _productStore.getClaimInfo(policyNftId, claimId);
}
/// @dev Returns the current claim state for the given policy NFT ID and claim ID.
function getClaimState(NftId policyNftId, ClaimId claimId) public view returns (StateId state) {
return _productStore.getState(claimId.toKey32(policyNftId));
}
/// @dev Returns the remaining claimable amount for the given policy NFT ID.
/// The remaining claimable amount is the difference between the sum insured amount and total approved claim amounts so far.
function getRemainingClaimableAmount(NftId policyNftId)
public view returns (Amount remainingClaimableAmount) {
IPolicy.PolicyInfo memory info = getPolicyInfo(policyNftId);
return info.sumInsuredAmount - info.claimAmount;
}
//--- payout functions -------------------------------------------------------//
/// @dev Returns the number of payouts for the given policy NFT ID and claim ID.
function payouts(NftId policyNftId, ClaimId claimId) public view returns (uint24 payoutCount) {
return getClaimInfo(policyNftId, claimId).payoutsCount;
}
/// @dev Returns the payout ID for the given claim ID and index.
function getPayoutId(ClaimId claimId, uint24 idx) public pure returns (PayoutId payoutId) {
return PayoutIdLib.toPayoutId(claimId, idx + 1);
}
/// @dev Returns the payout info for the given policy NFT ID and payout ID.
function getPayoutInfo(NftId policyNftId, PayoutId payoutId) public view returns (IPolicy.PayoutInfo memory info) {
return _productStore.getPayoutInfo(policyNftId, payoutId);
}
/// @dev Returns the payout state for the given policy NFT ID and payout ID.
function getPayoutState(NftId policyNftId, PayoutId payoutId) public view returns (StateId state) {
return _productStore.getState(payoutId.toKey32(policyNftId));
}
//--- premium functions -------------------------------------------------------//
/// @dev Returns the premium info for the given policy NFT ID.
function getPremiumInfo(NftId policyNftId) public view returns (IPolicy.PremiumInfo memory info) {
return _productStore.getPremiumInfo(policyNftId);
}
/// @dev Returns the premium state for the given policy NFT ID.
function getPremiumState(NftId policyNftId) public view returns (StateId state) {
return _productStore.getState(_toPremiumKey(policyNftId));
}
//--- oracle functions ---------------------------------------------------------//
/// @dev Returns the request info for the given oracle request ID.
function getRequestInfo(RequestId requestId) public view returns (IOracle.RequestInfo memory requestInfo) {
return _store.getRequestInfo(requestId);
}
/// @dev Returns the request info for the given oracle request ID.
function getRequestState(RequestId requestId) public view returns (StateId state) {
return getState(requestId.toKey32());
}
//--- pool functions -----------------------------------------------------------//
/// @dev Returns the pool info for the given pool NFT ID.
function getPoolInfo(NftId poolNftId) public view returns (IComponents.PoolInfo memory info) {
return _store.getPoolInfo(poolNftId);
}
//--- bundle functions -------------------------------------------------------//
/// @dev Returns the total number of registered bundles for the given pool.
function bundles(NftId poolNftId) public view returns (uint256 bundleCount) {
return _bundleSet.bundles(poolNftId);
}
/// @dev Returns the number of active bundles for the given pool.
function activeBundles(NftId poolNftId) public view returns (uint256 bundleCount) {
return _bundleSet.activeBundles(poolNftId);
}
/// @dev Returns the bunde NFT ID for the given pool and index.
function getBundleNftId(NftId poolNftId, uint256 idx) public view returns (NftId bundleNftId) {
return _bundleSet.getBundleNftId(poolNftId, idx);
}
/// @dev Returns the active bunde NFT ID for the given pool and index.
function getActiveBundleNftId(NftId poolNftId, uint256 idx) public view returns (NftId bundleNftId) {
return _bundleSet.getActiveBundleNftId(poolNftId, idx);
}
/// @dev Returns the bundle info for the given bundle NFT ID.
function getBundleInfo(NftId bundleNftId) public view returns (IBundle.BundleInfo memory info) {
return _store.getBundleInfo(bundleNftId);
}
/// @dev Returns the bundle state for the given bundle NFT ID.
function getBundleState(NftId bundleNftId) public view returns (StateId state) {
return getState(_toBundleKey(bundleNftId));
}
//--- distribution functions -------------------------------------------------------//
function getDistributorTypeInfo(DistributorType distributorType) public view returns (IDistribution.DistributorTypeInfo memory info) {
return _store.getDistributorTypeInfo(distributorType);
}
function getDistributorInfo(NftId distributorNftId) public view returns (IDistribution.DistributorInfo memory info) {
return _store.getDistributorInfo(distributorNftId);
}
//--- referral functions -------------------------------------------------------//
function toReferralId(NftId distributionNftId, string memory referralCode) public pure returns (ReferralId referralId) {
return ReferralLib.toReferralId(distributionNftId, referralCode);
}
function isReferralValid(NftId distributionNftId, ReferralId referralId) external view returns (bool isValid) {
return _distributionService.referralIsValid(distributionNftId, referralId);
}
function getReferralInfo(ReferralId referralId) public view returns (IDistribution.ReferralInfo memory info) {
return _store.getReferralInfo(referralId);
}
function getDiscountPercentage(ReferralId referralId)
public
view
returns (
UFixed discountPercentage,
ReferralStatus status
)
{
return IDistributionService(
_registry.getServiceAddress(
DISTRIBUTION(),
_instance.getRelease())).getDiscountPercentage(
this, // instance reader
referralId);
}
//--- authorization functions -------------------------------------------------------//
/// @dev Returns the number of defined roles.
function roles() public view returns (uint256) {
return _instanceAdmin.roles();
}
/// @dev Returns the role ID for the given index.
function getRoleId(uint256 idx) public view returns (RoleId roleId) {
return _instanceAdmin.getRoleId(uint64(idx));
}
/// @dev Returns the role ID for the given index.
function getRoleForName(string memory name) public view returns (RoleId roleId, bool exists) {
return _instanceAdmin.getRoleForName(name);
}
/// @dev Returns the role ID for the instance owner role.
/// This role may be used as a "root" admin role for other custom roles defined for this instance.
function getInstanceOwnerRole() public pure returns (RoleId roleId) {
return INSTANCE_OWNER_ROLE();
}
/// @dev Returns the role info for the given role ID.
function getRoleInfo(RoleId roleId) public view returns (IAccess.RoleInfo memory roleInfo) {
return _instanceAdmin.getRoleInfo(roleId);
}
/// @dev Returns true iff the provided role ID is defined for this instance.
function roleExists(RoleId roleId) public view returns (bool exists) {
return _instanceAdmin.roleExists(roleId);
}
/// @dev Returns true iff the provided role ID represents a custom role ID.
function isRoleCustom(RoleId roleId) public view returns (bool isCustom) {
return _instanceAdmin.isRoleCustom(roleId);
}
/// @dev Returns true iff the provided role ID is active.
function isRoleActive(RoleId roleId) public view returns (bool isActive) {
return _instanceAdmin.isRoleActive(roleId);
}
/// @dev Returns the number of members (accounts) for the given role ID.
function roleMembers(RoleId roleId) public view returns (uint256 numberOfMembers) {
return _instanceAdmin.roleMembers(roleId);
}
/// @dev Returns the member (account address) for the given role ID and index.
function getRoleMember(RoleId roleId, uint256 idx) public view returns (address account) {
return _instanceAdmin.getRoleMember(roleId, idx);
}
/// @dev Returns true iff the given account is a member of the specified role ID.
function isRoleMember(RoleId roleId, address account) public view returns (bool isMember) {
return _instanceAdmin.isRoleMember(roleId, account);
}
/// @dev Returns true iff the given account is an admin of the specified role ID.
/// Role admins may grant and revoke the role to other accounts.
function isRoleAdmin(RoleId roleId, address account) public view returns (bool isMember) {
return _instanceAdmin.isRoleAdmin(roleId, account);
}
/// @dev Returns the number of targets (contracts) defined for this instance.
function targets() public view returns (uint256 targetCount) {
return _instanceAdmin.targets();
}
/// @dev Returns the target address for the given index.
function getTargetAddress(uint256 idx) public view returns (address target) {
return _instanceAdmin.getTargetAddress(idx);
}
/// @dev Returns the target info for the given target address.
function getTargetInfo(address target) public view returns (IAccess.TargetInfo memory targetInfo) {
return _instanceAdmin.getTargetInfo(target);
}
/// @dev Returns true iff the given target is defined for this instance.
function targetExists(address target) public view returns (bool exists) {
return _instanceAdmin.targetExists(target);
}
/// @dev Returns true iff the given target is locked.
function isLocked(address target) public view returns (bool) {
return _instanceAdmin.isTargetLocked(target);
}
/// @dev Returns the number of authorized functions for the given target.
function authorizedFunctions(address target) external view returns (uint256 numberOfFunctions) {
return _instanceAdmin.authorizedFunctions(target);
}
/// @dev Returns the authorized function info for the given target and index.
function getAuthorizedFunction(address target, uint256 idx) external view returns (IAccess.FunctionInfo memory func, RoleId roleId) {
return _instanceAdmin.getAuthorizedFunction(target, idx);
}
/// @dev Returns a function info for the given function signature and function name.
/// The function signature must not be zero and the function name must not be empty.
function toFunction(bytes4 signature, string memory name) public view returns (IAccess.FunctionInfo memory) {
return AccessAdminLib.toFunction(signature, name);
}
//--- low level function ----------------------------------------------------//
function getInstanceAdmin() external view returns (InstanceAdmin instanceAdmin) {
return _instanceAdmin;
}
function getBundleSet() external view returns (BundleSet bundleSet) {
return _bundleSet;
}
function getRiskSet() external view returns (RiskSet riskSet) {
return _riskSet;
}
function getMetadata(Key32 key) public view returns (IBaseStore.Metadata memory metadata) {
return _store.getMetadata(key);
}
function getState(Key32 key) public view returns (StateId state) {
return _store.getState(key);
}
function toInt(UFixed value) public pure returns (uint256) {
return UFixedLib.toInt(value);
}
function toString(Str str) public pure returns (string memory) {
return StrLib.toString(str);
}
function toUFixed(uint256 value, int8 exp) public pure returns (UFixed) {
return UFixedLib.toUFixed(value, exp);
}
//--- internal functions ----------------------------------------------------//
function _toPolicyKey(NftId policyNftId) internal pure returns (Key32) {
return policyNftId.toKey32(POLICY());
}
function _toPremiumKey(NftId policyNftId) internal pure returns (Key32) {
return policyNftId.toKey32(PREMIUM());
}
function _toBundleKey(NftId poolNftId) internal pure returns (Key32) {
return poolNftId.toKey32(BUNDLE());
}
function _toComponentKey(NftId componentNftId) internal pure returns (Key32) {
return componentNftId.toKey32(COMPONENT());
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import {IBundle} from "./module/IBundle.sol";
import {IComponents} from "./module/IComponents.sol";
import {IDistribution} from "./module/IDistribution.sol";
import {IInstance} from "./IInstance.sol";
import {IOracle} from "../oracle/IOracle.sol";
import {Amount} from "../type/Amount.sol";
import {BaseStore} from "./BaseStore.sol";
import {Blocknumber} from "../type/Blocknumber.sol";
import {Key32} from "../type/Key32.sol";
import {NftId} from "../type/NftId.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {ObjectType, BUNDLE, POOL, COMPONENT, DISTRIBUTOR} from "../type/ObjectType.sol";
import {RequestId} from "../type/RequestId.sol";
import {StateId} from "../type/StateId.sol";
import {ReferralId} from "../type/Referral.sol";
import {DistributorType} from "../type/DistributorType.sol";
import {PayoutId} from "../type/PayoutId.sol";
import {BalanceStore} from "./base/BalanceStore.sol";
import {ObjectCounter} from "./base/ObjectCounter.sol";
import {ObjectLifecycle} from "./base/ObjectLifecycle.sol";
contract InstanceStore is
AccessManagedUpgradeable,
BalanceStore,
ObjectCounter,
ObjectLifecycle,
BaseStore
{
event LogProductStoreComponentInfoCreated(NftId componentNftId, StateId state, address createdby, address txOrigin);
event LogProductStoreComponentInfoUpdated(NftId componentNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
event LogProductStorePoolInfoCreated(NftId poolNftId, StateId state, address createdBy, address txOrigin);
event LogProductStorePoolInfoUpdated(NftId poolNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
event LogProductStoreDistributorTypeInfoCreated(DistributorType distributorType, StateId state, address createdBy, address txOrigin);
event LogProductStoreDistributorTypeInfoUpdated(DistributorType distributorType, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
event LogProductStoreDistributorInfoCreated(NftId distributorNftId, StateId state, address createdBy, address txOrigin);
event LogProductStoreDistributorInfoUpdated(NftId distributorNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
event LogProductStoreReferralInfoCreated(ReferralId referralId, StateId state, address createdBy, address txOrigin);
event LogProductStoreReferralInfoUpdated(ReferralId referralId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
event LogProductStoreBundleInfoCreated(NftId bundleNftId, StateId state, address createdBy, address txOrigin);
event LogProductStoreBundleInfoUpdated(NftId bundleNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
event LogProductStoreRequestInfoCreated(RequestId requestId, StateId state, address createdBy, address txOrigin);
event LogProductStoreRequestInfoUpdated(RequestId requestId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
mapping(Key32 => IComponents.ComponentInfo) private _components;
mapping(Key32 => IComponents.PoolInfo) private _pools;
mapping(Key32 => IDistribution.DistributorTypeInfo) private _distributorTypes;
mapping(Key32 => IDistribution.DistributorInfo) private _distributors;
mapping(Key32 => IDistribution.ReferralInfo) private _referrals;
mapping(Key32 => IBundle.BundleInfo) private _bundles;
mapping(Key32 => IOracle.RequestInfo) private _requests;
/// @dev This initializer needs to be called from the instance itself.
function initialize()
public
initializer()
{
address instance = msg.sender;
address authority = IInstance(instance).authority();
__AccessManaged_init(authority);
// double initialization, safe
_initializeLifecycle();
}
//--- Component ---------------------------------------------------------//
function createComponent(
NftId componentNftId,
IComponents.ComponentInfo memory componentInfo
)
external
restricted()
{
_registerBalanceTarget(componentNftId);
// _create(_toNftKey32(componentNftId, COMPONENT()), abi.encode(componentInfo));
Key32 key = _toNftKey32(componentNftId, COMPONENT());
_createMetadata(key);
_components[key] = componentInfo;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreComponentInfoCreated(componentNftId, getState(key), msg.sender, tx.origin);
}
function updateComponent(
NftId componentNftId,
IComponents.ComponentInfo memory componentInfo,
StateId newState
)
external
restricted()
{
// _update(_toNftKey32(componentNftId, COMPONENT()), abi.encode(componentInfo), newState);
Key32 key = _toNftKey32(componentNftId, COMPONENT());
(Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
_components[key] = componentInfo;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreComponentInfoUpdated(componentNftId, oldState, newState, msg.sender, tx.origin, updatedIn);
}
function getComponentInfo(NftId componentNftId) external view returns (IComponents.ComponentInfo memory componentInfo) {
return _components[_toNftKey32(componentNftId, COMPONENT())];
}
//--- Pool --------------------------------------------------------------//
function createPool(
NftId poolNftId,
IComponents.PoolInfo memory info
)
external
restricted()
{
Key32 key = _toNftKey32(poolNftId, POOL());
_createMetadata(key);
_pools[key] = info;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePoolInfoCreated(poolNftId, getState(key), msg.sender, tx.origin);
}
function updatePool(NftId poolNftId, IComponents.PoolInfo memory info, StateId newState) external restricted() {
Key32 key = _toNftKey32(poolNftId, POOL());
(Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
_pools[key] = info;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePoolInfoUpdated(poolNftId, oldState, newState, msg.sender, tx.origin, updatedIn);
}
function getPoolInfo(NftId poolNftId) external view returns (IComponents.PoolInfo memory info) {
return _pools[_toNftKey32(poolNftId, POOL())];
}
//--- DistributorType ---------------------------------------------------//
function createDistributorType(DistributorType distributorType, IDistribution.DistributorTypeInfo memory info) external restricted() {
Key32 key = distributorType.toKey32();
_createMetadata(key);
_distributorTypes[key] = info;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreDistributorTypeInfoCreated(distributorType, getState(key), msg.sender, tx.origin);
}
function updateDistributorType(DistributorType distributorType, IDistribution.DistributorTypeInfo memory info, StateId newState) external restricted() {
Key32 key = distributorType.toKey32();
(Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
_distributorTypes[key] = info;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreDistributorTypeInfoUpdated(distributorType, oldState, newState, msg.sender, tx.origin, updatedIn);
}
function updateDistributorTypeState(DistributorType distributorType, StateId newState) external restricted() {
Key32 key = distributorType.toKey32();
(Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreDistributorTypeInfoUpdated(distributorType, oldState, newState, msg.sender, tx.origin, updatedIn);
}
function getDistributorTypeInfo(DistributorType distributorType) external view returns (IDistribution.DistributorTypeInfo memory info) {
return _distributorTypes[distributorType.toKey32()];
}
//--- Distributor -------------------------------------------------------//
function createDistributor(NftId distributorNftId, IDistribution.DistributorInfo memory info) external restricted() {
_registerBalanceTarget(distributorNftId);
Key32 key = _toNftKey32(distributorNftId, DISTRIBUTOR());
_createMetadata(key);
_distributors[key] = info;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreDistributorInfoCreated(distributorNftId, getState(key), msg.sender, tx.origin);
}
function updateDistributor(NftId distributorNftId, IDistribution.DistributorInfo memory info, StateId newState) external restricted() {
Key32 key = _toNftKey32(distributorNftId, DISTRIBUTOR());
(Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
_distributors[key] = info;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreDistributorInfoUpdated(distributorNftId, oldState, newState, msg.sender, tx.origin, updatedIn);
}
function updateDistributorState(NftId distributorNftId, StateId newState) external restricted() {
Key32 key = _toNftKey32(distributorNftId, DISTRIBUTOR());
(Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreDistributorInfoUpdated(distributorNftId, oldState, newState, msg.sender, tx.origin, updatedIn);
}
function getDistributorInfo(NftId distributorNftId) external view returns (IDistribution.DistributorInfo memory info) {
return _distributors[_toNftKey32(distributorNftId, DISTRIBUTOR())];
}
//--- Referral ----------------------------------------------------------//
function createReferral(ReferralId referralId, IDistribution.ReferralInfo memory referralInfo) external restricted() {
Key32 key = referralId.toKey32();
_createMetadata(key);
_referrals[key] = referralInfo;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreReferralInfoCreated(referralId, getState(key), msg.sender, tx.origin);
}
function updateReferral(ReferralId referralId, IDistribution.ReferralInfo memory referralInfo, StateId newState) external restricted() {
Key32 key = referralId.toKey32();
(Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
_referrals[key] = referralInfo;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreReferralInfoUpdated(referralId, oldState, newState, msg.sender, tx.origin, updatedIn);
}
function updateReferralState(ReferralId referralId, StateId newState) external restricted() {
Key32 key = referralId.toKey32();
(Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreReferralInfoUpdated(referralId, oldState, newState, msg.sender, tx.origin, updatedIn);
}
function getReferralInfo(ReferralId referralId) external view returns (IDistribution.ReferralInfo memory referralInfo) {
return _referrals[referralId.toKey32()];
}
//--- Bundle ------------------------------------------------------------//
function createBundle(NftId bundleNftId, IBundle.BundleInfo memory bundle) external restricted() {
_registerBalanceTarget(bundleNftId);
Key32 key = _toNftKey32(bundleNftId, BUNDLE());
_createMetadata(key);
_bundles[key] = bundle;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreBundleInfoCreated(bundleNftId, getState(key), msg.sender, tx.origin);
}
function updateBundle(NftId bundleNftId, IBundle.BundleInfo memory bundle, StateId newState) external restricted() {
Key32 key = _toNftKey32(bundleNftId, BUNDLE());
(Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
_bundles[key] = bundle;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreBundleInfoUpdated(bundleNftId, oldState, newState, msg.sender, tx.origin, updatedIn);
}
function updateBundleState(NftId bundleNftId, StateId newState) external restricted() {
Key32 key = _toNftKey32(bundleNftId, BUNDLE());
(Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreBundleInfoUpdated(bundleNftId, oldState, newState, msg.sender, tx.origin, updatedIn);
}
function getBundleInfo(NftId bundleNftId) external view returns (IBundle.BundleInfo memory bundle) {
return _bundles[_toNftKey32(bundleNftId, BUNDLE())];
}
//--- Request -----------------------------------------------------------//
function createRequest(IOracle.RequestInfo memory request) external restricted() returns (RequestId requestId) {
requestId = _createNextRequestId();
Key32 key = requestId.toKey32();
_createMetadata(key);
_requests[key] = request;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreRequestInfoCreated(requestId, getState(key), msg.sender, tx.origin);
}
function updateRequest(RequestId requestId, IOracle.RequestInfo memory request, StateId newState) external restricted() {
Key32 key = requestId.toKey32();
(Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
_requests[key] = request;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreRequestInfoUpdated(requestId, oldState, newState, msg.sender, tx.origin, updatedIn);
}
function updateRequestState(RequestId requestId, StateId newState) external restricted() {
Key32 key = requestId.toKey32();
(Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreRequestInfoUpdated(requestId, oldState, newState, msg.sender, tx.origin, updatedIn);
}
function getRequestInfo(RequestId requestId) external view returns (IOracle.RequestInfo memory request) {
return _requests[requestId.toKey32()];
}
//--- balance and fee management functions ------------------------------//
function increaseBalance(NftId targetNftId, Amount amount) external restricted() returns (Amount newBalance) {
return _increaseBalance(targetNftId, amount);
}
function decreaseBalance(NftId targetNftId, Amount amount) external restricted() returns (Amount newBalance) {
return _decreaseBalance(targetNftId, amount);
}
function increaseFees(NftId targetNftId, Amount amount) external restricted() returns (Amount newFeeBalance) {
return _increaseFees(targetNftId, amount);
}
function decreaseFees(NftId targetNftId, Amount amount) external restricted() returns (Amount newFeeBalance) {
return _decreaseFees(targetNftId, amount);
}
function increaseLocked(NftId targetNftId, Amount amount) external restricted() returns (Amount newBalance) {
return _increaseLocked(targetNftId, amount);
}
function decreaseLocked(NftId targetNftId, Amount amount) external restricted() returns (Amount newBalance) {
return _decreaseLocked(targetNftId, amount);
}
//--- internal view/pure functions --------------------------------------//
function _toNftKey32(NftId nftId, ObjectType objectType) private pure returns (Key32) {
return nftId.toKey32(objectType);
}
function _toClaimKey32(NftId policyNftId, ClaimId claimId) private pure returns (Key32) {
return claimId.toKey32(policyNftId);
}
function _toPayoutKey32(NftId policyNftId, PayoutId payoutId) private pure returns (Key32) {
return payoutId.toKey32(policyNftId);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {NftId} from "../../type/NftId.sol";
import {Fee} from "../../type/Fee.sol";
import {Timestamp} from "../../type/Timestamp.sol";
interface IBundle {
struct BundleInfo {
// slot 0
Timestamp activatedAt;
Timestamp expiredAt; // no new policies starting with this timestamp
Timestamp closedAt; // no open policies, locked amount = 0
NftId poolNftId;
// slot 1
Fee fee; // bundle fee on net premium amounts
// slot 2
bytes filter; // required conditions for applications to be considered for collateralization by this bundle
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Amount} from "../../type/Amount.sol";
import {Fee} from "../../type/Fee.sol";
import {NftId} from "../../type/NftId.sol";
import {TokenHandler} from "../../shared/TokenHandler.sol";
import {UFixed} from "../../type/UFixed.sol";
interface IComponents {
struct ComponentInfo {
// slot 0
string name; // component name (needs to be unique per instance)
// slot 1
TokenHandler tokenHandler;
}
struct ProductInfo {
// slot 0
bool isProcessingFundedClaims; // custom logic to react to pool events for funded claims
bool isInterceptingPolicyTransfers; // custom logic for policy nft transfers
bool hasDistribution; // flag to indicate if distribution is enabled
uint8 expectedNumberOfOracles; // expected number of oracles
uint8 numberOfOracles; // actual number of oracles
NftId poolNftId; // mandatory
NftId distributionNftId; // 0..1 (optional)
// slot 1
NftId [] oracleNftId; // 0..n (optional)
}
struct FeeInfo {
// slot 0
Fee productFee; // product fee on net premium
// slot 1
Fee processingFee; // product fee on payout amounts
// slot 2
Fee distributionFee; // distribution fee for sales that do not include commissions
// slot 3
Fee minDistributionOwnerFee; // min fee required by distribution owner (not including commissions for distributors)
// slot 4
Fee poolFee; // pool fee on net premium
// slot 5
Fee stakingFee; // pool fee on staked capital from investor
// slot 6
Fee performanceFee; // pool fee on profits from capital investors
}
struct PoolInfo {
// slot 0
Amount maxBalanceAmount; // max balance amount allowed for pool
UFixed collateralizationLevel; // factor to calculate collateral for sum insurance (default 100%)
// slot 1
UFixed retentionLevel; // amount of collateral held in pool (default 100%)
bool isInterceptingBundleTransfers; // custom logic for bundle nft transfers
bool isProcessingConfirmedClaims; // custom logic for claims confirmation
bool isExternallyManaged; // funding bundles is restricted to book keeping, actual funds may be provided as needed to support payouts
bool isVerifyingApplications; // underwriting requires the pool component checks/confirms the applications
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {DistributorType} from "../../type/DistributorType.sol";
import {NftId} from "../../type/NftId.sol";
import {Seconds} from "../../type/Seconds.sol";
import {Timestamp} from "../../type/Timestamp.sol";
import {UFixed} from "../../type/UFixed.sol";
interface IDistribution {
struct DistributorTypeInfo {
// slot 0
UFixed minDiscountPercentage;
NftId distributionNftId;
// slot 1
UFixed maxDiscountPercentage;
uint32 maxReferralCount;
Seconds maxReferralLifetime;
bool allowSelfReferrals;
bool allowRenewals;
// slot 2
UFixed commissionPercentage;
// slot 3
string name;
// slot 4
bytes data;
}
struct DistributorInfo {
// slot 0
DistributorType distributorType;
bool active;
uint32 numPoliciesSold;
// slot 1
bytes data;
}
struct ReferralInfo {
// slot 0
NftId distributionNftId;
NftId distributorNftId;
uint32 maxReferrals;
uint32 usedReferrals;
// slot 1
UFixed discountPercentage;
Timestamp expiryAt;
// slot 2
string referralCode;
// slot 3
bytes data;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Amount} from "../../type/Amount.sol";
import {NftId} from "../../type/NftId.sol";
import {ClaimId} from "../../type/ClaimId.sol";
import {ReferralId} from "../../type/Referral.sol";
import {RiskId} from "../../type/RiskId.sol";
import {Seconds} from "../../type/Seconds.sol";
import {Timestamp} from "../../type/Timestamp.sol";
interface IPolicy {
struct PremiumInfo {
// slot 0
// premium splitting per target wallet
Amount productFeeAmount;
Amount distributionFeeAndCommissionAmount;
// slot 1
Amount poolPremiumAndFeeAmount;
// detailed positions
// this is the net premium calculated by the product
Amount netPremiumAmount;
// slot 2
// fullPremium = netPremium + all fixed amounts + all variable amounts (excl commission and minDistribtuionOwnerFee variable part)
Amount fullPremiumAmount;
// effective premium = fullPremium - discount
Amount premiumAmount;
// slot 3
Amount productFeeFixAmount;
Amount poolFeeFixAmount;
// slot 4
Amount bundleFeeFixAmount;
Amount distributionFeeFixAmount;
// slot 5
Amount productFeeVarAmount;
Amount poolFeeVarAmount;
// slot 6
Amount bundleFeeVarAmount;
Amount distributionFeeVarAmount;
// slot 7
Amount distributionOwnerFeeFixAmount;
// this is the remaining amount when the commission and discount are subtracted from the distribution fee variable part (must be at least the minDistributionOwnerFee)
Amount distributionOwnerFeeVarAmount;
// slot 8
// this value is based on distributor type referenced in the referral
Amount commissionAmount;
// this is based on referral used
Amount discountAmount;
}
/// @dev policy data for the full policy lifecycle
struct PolicyInfo {
// slot 0
NftId productNftId;
NftId bundleNftId;
RiskId riskId;
// slot 1
Amount sumInsuredAmount;
Amount premiumAmount; // expected premium at application time
ReferralId referralId;
// slot 2
uint16 claimsCount;
uint16 openClaimsCount;
Amount claimAmount; // sum of confirmed claim amounts (max = sum insured amount)
Amount payoutAmount; // sum of payouts (max = sum confirmed claim amountst)
// slot 3
Timestamp activatedAt; // time of underwriting
Seconds lifetime;
Timestamp expiredAt; // no new claims (activatedAt + lifetime)
Timestamp closedAt; // no locked capital (or declinedAt)
// slot 4
bytes applicationData;
// slot 5
bytes processData;
}
// claimId neeeds to be encoded policyNftId:claimId combination
struct ClaimInfo {
// slot 0
Amount claimAmount;
Amount paidAmount;
Timestamp closedAt; // payment of confirmed claim amount (or declinedAt)
uint24 payoutsCount;
// slot 1
uint24 openPayoutsCount;
// slot 2
bytes submissionData; // use case specific claim submission data, no changes after submitting the claim
// slot 3
bytes processData; // use case specific data that may include information supporting confirm or decline
}
// claimId neeeds to be encoded policyNftId:claimId combination
struct PayoutInfo {
// slot 0
ClaimId claimId;
Amount amount;
Timestamp paidAt; // timestamp for actual payout
// slot 1
address beneficiary; // for address(0) beneficiary is policy nft owner
// slot 2
bytes data; // use case specific supporting data
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {NftId} from "../../type/NftId.sol";
import {Timestamp} from "../../type/Timestamp.sol";
interface IRisk {
struct RiskInfo {
// slot 0
NftId productNftId;
Timestamp createdAt;
// slot 1
bytes data;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import {IComponents} from "./module/IComponents.sol";
import {IInstance} from "./IInstance.sol";
import {IPolicy} from "./module/IPolicy.sol";
import {IRisk} from "./module/IRisk.sol";
import {BalanceStore} from "./base/BalanceStore.sol";
import {BaseStore} from "./BaseStore.sol";
import {Blocknumber} from "../type/Blocknumber.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {Key32} from "../type/Key32.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectCounter} from "./base/ObjectCounter.sol";
import {ObjectLifecycle} from "./base/ObjectLifecycle.sol";
import {ObjectType, FEE, POLICY, PREMIUM, PRODUCT} from "../type/ObjectType.sol";
import {PayoutId} from "../type/PayoutId.sol";
import {RiskId} from "../type/RiskId.sol";
import {StateId, KEEP_STATE} from "../type/StateId.sol";
contract ProductStore is
AccessManagedUpgradeable,
BalanceStore,
BaseStore,
ObjectCounter,
ObjectLifecycle
{
event LogProductStoreProductInfoCreated(NftId productNftId, StateId state, address createdBy, address txOrigin);
event LogProductStoreProductInfoUpdated(NftId productNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
event LogProductStoreFeeInfoCreated(NftId productNftId, StateId state, address createdBy, address txOrigin);
event LogProductStoreFeeInfoUpdated(NftId productNftId, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
event LogProductStoreRiskInfoCreated(RiskId riskId, StateId state, address createdBy, address txOrigin);
event LogProductStoreRiskInfoUpdated(RiskId riskId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
event LogProductStorePolicyInfoCreated(NftId policyNftId, StateId state, address createdBy, address txOrigin);
event LogProductStorePolicyInfoUpdated(NftId policyNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
event LogProductStorePremiumInfoCreated(NftId policyNftId, StateId state, address createdBy, address txOrigin);
event LogProductStorePremiumInfoUpdated(NftId policyNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
event LogProductStoreClaimInfoCreated(NftId policyNftId, ClaimId claimId, StateId state, address createdBy, address txOrigin);
event LogProductStoreClaimInfoUpdated(NftId policyNftId, ClaimId claimId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
event LogProductStorePayoutInfoCreated(NftId policyNftId, PayoutId payoutId, StateId state, address createdBy, address txOrigin);
event LogProductStorePayoutInfoUpdated(NftId policyNftId, PayoutId payoutId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
mapping(Key32 key32 => IComponents.ProductInfo) private _products;
mapping(Key32 key32 => IComponents.FeeInfo) private _fees;
mapping(Key32 key32 => IRisk.RiskInfo) private _risks;
mapping(Key32 key32 => IPolicy.PolicyInfo) private _policies;
mapping(Key32 key32 => IPolicy.PremiumInfo) private _premiums;
mapping(Key32 key32 => IPolicy.ClaimInfo) private _claims;
mapping(Key32 key32 => IPolicy.PayoutInfo) private _payouts;
/// @dev This initializer needs to be called from the instance itself.
function initialize()
public
initializer()
{
address instance = msg.sender;
address authority = IInstance(instance).authority();
__AccessManaged_init(authority);
// double initialization, safe
_initializeLifecycle();
}
//--- Product -----------------------------------------------------------//
function createProduct(NftId productNftId, IComponents.ProductInfo memory info) external restricted() {
Key32 key = _toNftKey32(productNftId, PRODUCT());
_createMetadata(key);
_products[key] = info;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreProductInfoCreated(productNftId, getState(key), msg.sender, tx.origin);
}
function updateProduct(NftId productNftId, IComponents.ProductInfo memory info, StateId newState) external restricted() {
Key32 key = _toNftKey32(productNftId, PRODUCT());
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
_products[key] = info;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreProductInfoUpdated(productNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
function getProductInfo(NftId productNftId) external view returns (IComponents.ProductInfo memory info) {
return _products[_toNftKey32(productNftId, PRODUCT())];
}
//--- Fee -----------------------------------------------------------//
function createFee(NftId productNftId, IComponents.FeeInfo memory info) external restricted() {
Key32 key = _toNftKey32(productNftId, FEE());
_createMetadata(key);
_fees[key] = info;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreFeeInfoCreated(productNftId, getState(key), msg.sender, tx.origin);
}
// Fee only has one state, so no change change possible
function updateFee(NftId productNftId, IComponents.FeeInfo memory info) external restricted() {
Key32 key = _toNftKey32(productNftId, FEE());
(Blocknumber lastUpdatedIn,) = _updateState(key, KEEP_STATE());
_fees[key] = info;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreFeeInfoUpdated(productNftId, msg.sender, tx.origin, lastUpdatedIn);
}
function getFeeInfo(NftId productNftId) external view returns (IComponents.FeeInfo memory info) {
return _fees[_toNftKey32(productNftId, FEE())];
}
//--- Risk --------------------------------------------------------------//
function createRisk(RiskId riskId, IRisk.RiskInfo memory info) external restricted() {
Key32 key = riskId.toKey32();
_createMetadata(key);
_risks[key] = info;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreRiskInfoCreated(riskId, getState(key), msg.sender, tx.origin);
}
function updateRisk(RiskId riskId, IRisk.RiskInfo memory info, StateId newState) external restricted() {
Key32 key = riskId.toKey32();
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
_risks[key] = info;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreRiskInfoUpdated(riskId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
function updateRiskState(RiskId riskId, StateId newState) external restricted() {
// _updateState(riskId.toKey32(), newState);
Key32 key = riskId.toKey32();
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreRiskInfoUpdated(riskId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
function getRiskInfo(RiskId riskId) external view returns (IRisk.RiskInfo memory info) {
return _risks[riskId.toKey32()];
}
//--- Application (Policy) ----------------------------------------------//
function createApplication(NftId applicationNftId, IPolicy.PolicyInfo memory policy) external restricted() {
_registerBalanceTarget(applicationNftId);
Key32 key = _toNftKey32(applicationNftId, POLICY());
_createMetadata(key);
_policies[key] = policy;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePolicyInfoCreated(applicationNftId, getState(key), msg.sender, tx.origin);
}
function updateApplication(NftId applicationNftId, IPolicy.PolicyInfo memory policy, StateId newState) external restricted() {
Key32 key = _toNftKey32(applicationNftId, POLICY());
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
_policies[key] = policy;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePolicyInfoUpdated(applicationNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
function updateApplicationState(NftId applicationNftId, StateId newState) external restricted() {
Key32 key = _toNftKey32(applicationNftId, POLICY());
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePolicyInfoUpdated(applicationNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
//--- Policy ------------------------------------------------------------//
function updatePolicy(NftId policyNftId, IPolicy.PolicyInfo memory policy, StateId newState) external restricted() {
Key32 key = _toNftKey32(policyNftId, POLICY());
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
_policies[key] = policy;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePolicyInfoUpdated(policyNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
function updatePolicyClaims(NftId policyNftId, IPolicy.PolicyInfo memory policy, StateId newState) external restricted() {
Key32 key = _toNftKey32(policyNftId, POLICY());
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
_policies[key] = policy;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePolicyInfoUpdated(policyNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
function updatePolicyState(NftId policyNftId, StateId newState) external restricted() {
Key32 key = _toNftKey32(policyNftId, POLICY());
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePolicyInfoUpdated(policyNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
function getPolicyInfo(NftId policyNftId) external view returns (IPolicy.PolicyInfo memory policy) {
return _policies[_toNftKey32(policyNftId, POLICY())];
}
//--- Premium (Policy) ----------------------------------------------//
function createPremium(NftId policyNftId, IPolicy.PremiumInfo memory premium) external restricted() {
Key32 key = _toNftKey32(policyNftId, PREMIUM());
_createMetadata(key);
_premiums[key] = premium;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePremiumInfoCreated(policyNftId, getState(key), msg.sender, tx.origin);
}
function updatePremiumState(NftId policyNftId, StateId newState) external restricted() {
Key32 key = _toNftKey32(policyNftId, PREMIUM());
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePremiumInfoUpdated(policyNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
function getPremiumInfo(NftId policyNftId) external view returns (IPolicy.PremiumInfo memory premium) {
return _premiums[_toNftKey32(policyNftId, PREMIUM())];
}
//--- Claim -------------------------------------------------------------//
function createClaim(NftId policyNftId, ClaimId claimId, IPolicy.ClaimInfo memory claim) external restricted() {
Key32 key = _toClaimKey32(policyNftId, claimId);
_createMetadata(key);
_claims[key] = claim;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreClaimInfoCreated(policyNftId, claimId, getState(key), msg.sender, tx.origin);
}
function updateClaim(NftId policyNftId, ClaimId claimId, IPolicy.ClaimInfo memory claim, StateId newState) external restricted() {
Key32 key = _toClaimKey32(policyNftId, claimId);
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
_claims[key] = claim;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreClaimInfoUpdated(policyNftId, claimId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
function updateClaimState(NftId policyNftId, ClaimId claimId, StateId newState) external restricted() {
Key32 key = _toClaimKey32(policyNftId, claimId);
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
// solhint-disable-next-line avoid-tx-origin
emit LogProductStoreClaimInfoUpdated(policyNftId, claimId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
function getClaimInfo(NftId policyNftId, ClaimId claimId) external view returns (IPolicy.ClaimInfo memory claim) {
return _claims[_toClaimKey32(policyNftId, claimId)];
}
//--- Payout ------------------------------------------------------------//
function createPayout(NftId policyNftId, PayoutId payoutId, IPolicy.PayoutInfo memory payout) external restricted() {
Key32 key = _toPayoutKey32(policyNftId, payoutId);
_createMetadata(key);
_payouts[key] = payout;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePayoutInfoCreated(policyNftId, payoutId, getState(key), msg.sender, tx.origin);
}
function updatePayout(NftId policyNftId, PayoutId payoutId, IPolicy.PayoutInfo memory payout, StateId newState) external restricted() {
Key32 key = _toPayoutKey32(policyNftId, payoutId);
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
_payouts[key] = payout;
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePayoutInfoUpdated(policyNftId, payoutId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
function updatePayoutState(NftId policyNftId, PayoutId payoutId, StateId newState) external restricted() {
Key32 key = _toPayoutKey32(policyNftId, payoutId);
(Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
// solhint-disable-next-line avoid-tx-origin
emit LogProductStorePayoutInfoUpdated(policyNftId, payoutId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
}
function getPayoutInfo(NftId policyNftId, PayoutId payoutId) external view returns (IPolicy.PayoutInfo memory payout) {
return _payouts[_toPayoutKey32(policyNftId, payoutId)];
}
//--- internal view/pure functions --------------------------------------//
function _toNftKey32(NftId nftId, ObjectType objectType) private pure returns (Key32) {
return nftId.toKey32(objectType);
}
function _toClaimKey32(NftId policyNftId, ClaimId claimId) private pure returns (Key32) {
return claimId.toKey32(policyNftId);
}
function _toPayoutKey32(NftId policyNftId, PayoutId payoutId) private pure returns (Key32) {
return payoutId.toKey32(policyNftId);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Key32} from "../type/Key32.sol";
import {LibNftIdSet} from "../type/NftIdSet.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectSet} from "./base/ObjectSet.sol";
import {ObjectSetHelperLib} from "./base/ObjectSetHelperLib.sol";
import {RiskIdLib, RiskId} from "../type/RiskId.sol";
/// @dev RiskSet manages the risks and its active policies per product.
contract RiskSet is
ObjectSet
{
event LogRiskSetPolicyLinked(RiskId riskId, NftId policyNftId);
event LogRiskSetPolicyUnlinked(RiskId riskId, NftId policyNftId);
event LogRiskSetRiskAdded(NftId productNftId, RiskId riskId);
event LogRiskSetRiskActive(NftId poolNftId, RiskId riskId);
event LogRiskSetRiskPaused(NftId poolNftId, RiskId riskId);
error ErrorRiskSetRiskLocked(RiskId riskId, NftId policyNftId);
error ErrorRiskSetRiskUnknown(RiskId riskId);
error ErrorRiskSetRiskNotRegistered(RiskId riskId);
mapping(RiskId riskId => LibNftIdSet.Set policies) internal _activePolicies;
/// @dev links a policy to its bundle
function linkPolicy(NftId productNftId, RiskId riskId, NftId policyNftId) external restricted() {
// ensure risk is active (in active set) and registered with this instance
if (!_isActive(productNftId, riskId.toKey32())) {
revert ErrorRiskSetRiskLocked(riskId, policyNftId);
}
LibNftIdSet.add(_activePolicies[riskId], policyNftId);
emit LogRiskSetPolicyLinked(riskId, policyNftId);
}
/// @dev Unlinks a policy from its risk.
function unlinkPolicy(NftId productNftId, RiskId riskId, NftId policyNftId) external restricted() {
// ensure risk is registered with this instance
if (!_contains(productNftId, riskId.toKey32())) {
revert ErrorRiskSetRiskUnknown(riskId);
}
LibNftIdSet.remove(_activePolicies[riskId], policyNftId);
emit LogRiskSetPolicyUnlinked(riskId, policyNftId);
}
/// @dev add a new risk to a product registered with this instance
// the corresponding product is fetched via instance reader
function add(RiskId riskId) external restricted() {
NftId productNftId = ObjectSetHelperLib.getProductNftId(_instanceAddress, riskId);
// ensure product is registered with instance
if(productNftId.eqz()) {
revert ErrorRiskSetRiskNotRegistered(riskId);
}
_add(productNftId, riskId.toKey32());
emit LogRiskSetRiskAdded(productNftId, riskId);
}
/// @dev Applications linked to active risks may be underwritten
function activate(RiskId riskId) external restricted() {
NftId productNftId = ObjectSetHelperLib.getProductNftId(_instanceAddress, riskId);
_activate(productNftId, riskId.toKey32());
emit LogRiskSetRiskActive(productNftId, riskId);
}
/// @dev Applications linked to paused/archived risks may not be underwritten
function deactivate(RiskId riskId) external restricted() {
NftId productNftId = ObjectSetHelperLib.getProductNftId(_instanceAddress, riskId);
_deactivate(productNftId, riskId.toKey32());
emit LogRiskSetRiskPaused(productNftId, riskId);
}
function checkRisk(NftId productNftId, RiskId riskId)
public
view
returns (bool exists, bool active)
{
Key32 riskKey32 = riskId.toKey32();
exists = _contains(productNftId, riskKey32);
if (exists) {
active = _isActive(productNftId, riskKey32);
}
}
function hasRisk(NftId productNftId, RiskId riskId)
public
view
returns (bool)
{
Key32 riskKey32 = riskId.toKey32();
return _contains(productNftId, riskKey32);
}
function risks(NftId productNftId) external view returns(uint256) {
return _objects(productNftId);
}
function getRiskId(NftId productNftId, uint256 idx) external view returns(RiskId riskId) {
return RiskIdLib.toRiskId(_getObject(productNftId, idx).toKeyId());
}
function activeRisks(NftId productNftId) external view returns(uint256) {
return _activeObjs(productNftId);
}
function getActiveRiskId(NftId productNftId, uint256 idx) external view returns(RiskId riskId) {
return RiskIdLib.toRiskId(_getActiveObject(productNftId, idx).toKeyId());
}
function linkedPolicies(RiskId riskId) external view returns(uint256) {
return LibNftIdSet.size(_activePolicies[riskId]);
}
function getLinkedPolicyNftId(RiskId riskId, uint256 idx) external view returns(NftId policyNftId) {
return LibNftIdSet.getElementAt(_activePolicies[riskId], idx);
}
}// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; string constant INSTANCE_TARGET_NAME = "Instance"; string constant INSTANCE_ADMIN_TARGET_NAME = "InstanceAdmin"; string constant INSTANCE_STORE_TARGET_NAME = "InstanceStore"; string constant PRODUCT_STORE_TARGET_NAME = "ProductStore"; string constant BUNDLE_SET_TARGET_NAME = "BundleSet"; string constant RISK_SET_TARGET_NAME = "RiskSet";
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IAuthorization} from "../../contracts/authorization/IAuthorization.sol";
import {NftId} from "../../contracts/type/NftId.sol";
import {Oracle} from "../../contracts/oracle/Oracle.sol";
import {RequestId} from "../../contracts/type/RequestId.sol";
contract BasicOracle is
Oracle
{
/// Function to provide reponse data releated to request id.
function respond(
RequestId requestId,
bytes memory responseData
)
external
virtual
restricted()
{
_respond(requestId, responseData);
}
function _initializeBasicOracle(
address registry,
NftId instanceNftId,
IAuthorization authorization,
address initialOwner,
string memory name
)
internal
virtual
onlyInitializing()
{
__Oracle_init(
registry,
instanceNftId,
authorization,
initialOwner,
name);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {NftId} from "../type/NftId.sol";
import {RequestId} from "../type/RequestId.sol";
import {Timestamp} from "../type/Timestamp.sol";
interface IOracle {
struct RequestInfo {
// slot 0
NftId requesterNftId; // originator of the request
NftId oracleNftId; // responsible oracle component
bool isCancelled;
Timestamp respondedAt; // response timestamp
// slot 1
Timestamp expiredAt; // expiry timestamp
// slot 2
string callbackMethodName; // callback function of the requestor to call to provide response data
// slot 3
bytes requestData;
// slot 4
bytes responseData;
}
/// @dev Callback function for oracle service to notify this oracle component to retreive some oracle data.
function request(
RequestId requestId,
NftId requesterId,
bytes calldata requestData,
Timestamp expiryAt
) external;
/// @dev Callback function for oracle service to notify this oracle component that the specified oracle request has ben cancelled by the requestor.
function cancel(
RequestId requestId
) external;
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Fee} from "../type/Fee.sol";
import {IInstanceLinkedComponent} from "../shared/IInstanceLinkedComponent.sol";
import {ReferralId, ReferralStatus} from "../type/Referral.sol";
import {NftId} from "../type/NftId.sol";
import {DistributorType} from "../type/DistributorType.sol";
import {RequestId} from "../type/RequestId.sol";
import {UFixed} from "../type/UFixed.sol";
import {Timestamp} from "../type/Timestamp.sol";
interface IOracleComponent is IInstanceLinkedComponent {
error ErrorOracleNotImplemented(string methodName);
/// @dev callback method for requesting some data from the oracle
function request(
RequestId requestId,
NftId requesterNftId,
bytes calldata requestData,
Timestamp expiryAt
) external;
/// @dev callback method for cancelling the specified oracle request
function cancel(
RequestId requestId
) external;
/// @dev returns true iff the component needs to be called when selling/renewing policis
function isVerifying() external view returns (bool verifying);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IService} from "../shared/IService.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RequestId} from "../type/RequestId.sol";
import {StateId} from "../type/StateId.sol";
import {Timestamp} from "../type/Timestamp.sol";
interface IOracleService is IService {
event LogOracleServiceRequestCreated(RequestId requestId, NftId requesterNftId, NftId oracleNftId, Timestamp expiryAt);
event LogOracleServiceResponseProcessed(RequestId requestId, NftId oracleNftId);
event LogOracleServiceDeliveryFailed(RequestId requestId, address requesterAddress, string functionSignature);
event LogOracleServiceResponseResent(RequestId requestId, NftId requesterNftId);
event LogOracleServiceRequestCancelled(RequestId requestId, NftId requesterNftId);
// create request
error ErrorOracleServiceProductMismatch(ObjectType callerObjectType, NftId productNft, NftId oracleParentNftId);
error ErrorOracleServiceExpiryInThePast(Timestamp blockTimestamp, Timestamp expiryAt);
error ErrorOracleServiceCallbackMethodNameEmpty();
// respond
error ErrorOracleServiceNotResponsibleOracle(RequestId requestId, NftId expectedOracleNftId, NftId oracleNftId);
// get request info
error ErrorOracleServiceRequestStateNotActive(RequestId requestId, StateId state);
error ErrorOracleServiceCallerNotResponsibleOracle(RequestId requestId, NftId oracleNftId, NftId callerNftId);
error ErrorOracleServiceCallerNotRequester(RequestId requestId, NftId requesterNftId, NftId callerNftId);
error ErrorOracleServiceRequestExpired(RequestId requestId, Timestamp expiredAt);
/// @dev send an oracle request to the specified oracle component.
/// the function returns the id of the newly created request.
/// permissioned: only registered components may send requests to oracles.
function request(
NftId oracleNftId,
bytes calldata requestData,
Timestamp expiryAt,
string calldata callbackMethodName
) external returns (RequestId requestId);
/// @dev Respond to oracle request by oracle compnent.
/// The response data is amended in the request info stored with the instance.
/// The request state changes to FULFILLED (when calling the callback method of the requester is successful)
/// or to FAILED when calling the requester is not succesful.
/// The function returns true iff the state changes to FULFILLED.
/// Permissioned: only the receiving oracle component may call this method
function respond(
RequestId requestId,
bytes calldata responseData
) external returns (bool success);
/// @dev Resend a failed response to the requester.
/// Only requests in state FAILED may be resent.
/// The request state changes to FULFILLED when calling the callback method of the requester is successful.
/// Permissioned: only the receiving oracle may resend a request
function resend(RequestId requestId) external;
/// @dev Notify the oracle component that the specified request has become invalid.
/// Only requests in state ACTIVE may be cancelled.
/// Permissioned: only the requester may cancel a request
function cancel(RequestId requestId) external;
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Amount} from "../type/Amount.sol";
import {COMPONENT, PRODUCT, ORACLE} from "../type/ObjectType.sol";
import {IAuthorization} from "../authorization/IAuthorization.sol";
import {IComponentService} from "../shared/IComponentService.sol";
import {IInstanceLinkedComponent} from "../shared/IInstanceLinkedComponent.sol";
import {IOracleComponent} from "./IOracleComponent.sol";
import {IOracleService} from "./IOracleService.sol";
import {NftId} from "../type/NftId.sol";
import {InstanceLinkedComponent} from "../shared/InstanceLinkedComponent.sol";
import {RequestId} from "../type/RequestId.sol";
import {Timestamp} from "../type/Timestamp.sol";
abstract contract Oracle is
InstanceLinkedComponent,
IOracleComponent
{
// keccak256(abi.encode(uint256(keccak256("etherisc.storage.Oracle")) - 1)) & ~bytes32(uint256(0xff));
bytes32 public constant ORACLE_STORAGE_LOCATION_V1 = 0xaab7c0ea03d290e56d6c060e0733d3ebcbe647f7694616a2ec52738a64b2f900;
struct OracleStorage {
IComponentService _componentService;
IOracleService _oracleService;
}
function request(
RequestId requestId,
NftId requesterId,
bytes calldata requestData,
Timestamp expiryAt
)
external
virtual
restricted()
{
_request(requestId, requesterId, requestData, expiryAt);
}
function cancel(
RequestId requestId
)
external
virtual
restricted()
{
_cancel(requestId);
}
/// @dev Not relevant for oracle components, always returns false.
function isVerifying()
external
virtual
view
returns (bool verifying)
{
return false;
}
/// @dev Not relevant for oracle components
function withdrawFees(Amount amount)
external
virtual
override(IInstanceLinkedComponent, InstanceLinkedComponent)
onlyOwner()
restricted()
returns (Amount)
{
revert ErrorOracleNotImplemented("withdrawFees");
}
function __Oracle_init(
address registry,
NftId productNftId,
IAuthorization authorization,
address initialOwner,
string memory name
)
internal
virtual
onlyInitializing()
{
__InstanceLinkedComponent_init(
registry,
productNftId,
name,
ORACLE(),
authorization,
true,
initialOwner);
OracleStorage storage $ = _getOracleStorage();
$._componentService = IComponentService(_getServiceAddress(COMPONENT()));
$._oracleService = IOracleService(_getServiceAddress(ORACLE()));
_registerInterface(type(IOracleComponent).interfaceId);
}
/// @dev Internal function for handling requests.
/// Empty implementation.
/// Overwrite this function to implement use case specific handling for oracle calls.
function _request(
RequestId requestId,
NftId requesterId,
bytes calldata requestData,
Timestamp expiryAt
)
internal
virtual
{
}
/// @dev Internal function for cancelling requests.
/// Empty implementation.
/// Overwrite this function to implement use case specific cancelling.
function _cancel(
RequestId requestId
)
internal
virtual
{
}
/// @dev Internal function for handling oracle responses.
/// Default implementation sends response back to oracle service.
/// Use this function in use case specific external/public functions to handle use case specific response handling.
function _respond(
RequestId requestId,
bytes memory responseData
)
internal
virtual
{
_getOracleStorage()._oracleService.respond(requestId, responseData);
}
function _getOracleStorage() private pure returns (OracleStorage storage $) {
assembly {
$.slot := ORACLE_STORAGE_LOCATION_V1
}
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IPolicy} from "../instance/module/IPolicy.sol";
import {IService} from "../shared/IService.sol";
import {Amount} from "../type/Amount.sol";
import {NftId} from "../type/NftId.sol";
import {ReferralId} from "../type/Referral.sol";
import {RiskId} from "../type/RiskId.sol";
import {Seconds} from "../type/Seconds.sol";
/// @dev gif service responsible for creating applications
/// only product components may call transaction functions
interface IApplicationService is IService {
event LogApplicationServiceApplicationCreated(
NftId applicationNftId,
NftId productNftId,
NftId bundleNftId,
RiskId riskId,
ReferralId referralId,
address applicationOwner,
Amount sumInsuredAmount,
Amount premiumAmount,
Seconds lifetime);
event LogApplicationServiceApplicationRenewed(NftId policyNftId, NftId bundleNftId);
event LogApplicationServiceApplicationAdjusted(
NftId applicationNftId,
NftId bundleNftId,
RiskId riskId,
ReferralId referralId,
Amount sumInsuredAmount,
Seconds lifetime);
event LogApplicationServiceApplicationRevoked(NftId applicationNftId);
// _checkLinkedApplicationParameters
error ErrorApplicationServiceRiskProductMismatch(RiskId riskId, NftId riskProductNftId, NftId productNftId);
error ErrorApplicationServiceRiskUnknown(RiskId riskId, NftId productNftId);
error ErrorApplicationServiceRiskLocked(RiskId riskId, NftId productNftId);
error ErrorApplicationServiceBundleUnknown(NftId bundleNftId, NftId poolNftId);
error ErrorApplicationServiceBundleLocked(NftId bundleNftId, NftId poolNftId);
error ErrorApplicationServiceReferralInvalid(NftId productNftId, NftId distributionNftId, ReferralId referralId);
/// @dev creates a new application based on the specified attributes
/// may only be called by a product component
function create(
address applicationOwner,
RiskId riskId,
Amount sumInsuredAmount,
Amount premiumAmount,
Seconds lifetime,
NftId bundleNftId,
ReferralId referralId,
bytes memory applicationData
) external returns (NftId applicationNftId);
/// @dev updates application attributes
/// may only be called while the application is in applied state
/// may only be called by the referenced product related to applicationNftId
function adjust(
NftId applicationNftId,
RiskId riskId,
NftId bundleNftId,
ReferralId referralId,
Amount sumInsuredAmount,
Seconds lifetime,
bytes memory applicationData
) external;
/// @dev creates a new application that extends the provided policy
/// lifetime will seamlessly extend referenced policy, for closed policies
/// lifetime will start at underwriting time
/// product will need to limit the time window for renewal as underwriting
/// will lock the collateral at underwriting time which might be earlier than activation time
/// policyNftId needs to refer to an underwritten (or active or closed) policy
/// may only be called by the referenced product related to policyNftId
function renew(
NftId policyNftId, // policy to be renewd (renewal inherits policy attributes)
NftId bundleNftId // will likely need a newer bundle for underwriting
) external returns (NftId applicationNftId);
/// @dev revokes the application represented by {policyNftId}
/// an application can only be revoked in applied state
/// only the application holder may revoke an application
function revoke(NftId policyNftId) external;
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IInstance} from "../instance/IInstance.sol";
import {InstanceReader} from "../instance/InstanceReader.sol";
import {IService} from "../shared/IService.sol";
import {Amount} from "../type/Amount.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {PayoutId} from "../type/PayoutId.sol";
import {NftId} from "../type/NftId.sol";
import {StateId} from "../type/StateId.sol";
import {Timestamp} from "../type/Timestamp.sol";
import {UFixed} from "../type/UFixed.sol";
import {Fee} from "../type/Fee.sol";
/// @dev gif service responsible for creating claims and payouts
/// only product components may call transaction functions
interface IClaimService is
IService
{
event LogClaimServiceClaimSubmitted(NftId policyNftId, ClaimId claimId, Amount claimAmount);
event LogClaimServiceClaimConfirmed(NftId policyNftId, ClaimId claimId, Amount confirmedAmount);
event LogClaimServiceClaimDeclined(NftId policyNftId, ClaimId claimId);
event LogClaimServiceClaimRevoked(NftId policyNftId, ClaimId claimId);
event LogClaimServiceClaimCancelled(NftId policyNftId, ClaimId claimId);
event LogClaimServicePayoutCreated(NftId policyNftId, PayoutId payoutId, Amount amount, address beneficiary);
event LogClaimServicePayoutProcessed(NftId policyNftId, PayoutId payoutId, Amount amount);
event LogClaimServicePayoutCancelled(NftId policyNftId, PayoutId payoutId);
error ErrorClaimServiceBeneficiarySet(NftId policyNftId, PayoutId payoutId, address beneficiary);
error ErrorClaimServicePolicyProductMismatch(NftId policyNftId, NftId expectedProduct, NftId actualProduct);
error ErrorClaimServicePolicyNotOpen(NftId policyNftId);
error ErrorClaimServiceClaimAmountIsZero(NftId policyNftId);
error ErrorClaimServiceClaimExceedsSumInsured(NftId policyNftId, Amount sumInsured, Amount payoutsIncludingClaimAmount);
error ErrorClaimServiceBeneficiaryIsZero(NftId policyNftId, ClaimId claimId);
error ErrorClaimsServicePayoutAmountIsZero(NftId policyNftId, PayoutId payoutId);
error ErrorClaimServiceClaimWithOpenPayouts(NftId policyNftId, ClaimId claimId, uint24 openPayouts);
error ErrorClaimServiceClaimWithMissingPayouts(NftId policyNftId, ClaimId claimId, Amount claimAmount, Amount paidAmount);
error ErrorClaimServiceClaimNotInExpectedState(NftId policyNftId, ClaimId claimId, StateId expectedState, StateId actualState);
error ErrorClaimServiceClaimNotConfirmed(NftId policyNftId, ClaimId claimId, StateId actualState);
error ErrorClaimServicePayoutExceedsClaimAmount(NftId policyNftId, ClaimId claimId, Amount claimAmount, Amount totalPayoutAmount);
error ErrorClaimServicePayoutNotExpected(NftId policyNftId, PayoutId payoutId, StateId actualState);
/// @dev create a new claim for the specified policy
/// returns the id of the newly created claim
/// function can only be called by product, policy needs to match with calling product
function submit(
NftId policyNftId,
Amount claimAmount,
bytes memory claimData
) external returns (ClaimId claimId);
/// @dev declines the specified claim
/// function can only be called by product, policy needs to match with calling product
function decline(
NftId policyNftId,
ClaimId claimId,
bytes memory data // claim processing data
) external;
/// @dev revokes the specified claim
/// function can only be called by product, policy needs to match with calling product
function revoke(
NftId policyNftId,
ClaimId claimId
) external;
/// @dev confirms the specified claim and specifies the payout amount
/// function can only be called by product, policy needs to match with calling product
function confirm(
NftId policyNftId,
ClaimId claimId,
Amount confirmedAmount,
bytes memory data // claim processing data
) external;
/// @dev cancels a confirmed claim before it has been fully paid out.
/// Can only be called when there are not pending payouts
/// function can only be called by product, policy needs to match with calling product
function cancelConfirmedClaim(
NftId policyNftId,
ClaimId claimId
) external;
/// @dev Creates a new payout for the specified claim.
/// The beneficiary is the holder of the policy NFT
/// returns the id of the newly created payout, this id is unique for the specified policy
/// function can only be called by product, policy needs to match with calling product
function createPayout(
NftId policyNftId,
ClaimId claimId,
Amount amount,
bytes memory data
)
external
returns (PayoutId payoutId);
/// @dev Creates a new payout for the specified claim and beneficiary.
/// returns the id of the newly created payout, this id is unique for the specified policy
/// function can only be called by product, policy needs to match with calling product
function createPayoutForBeneficiary(
NftId policyNftId,
ClaimId claimId,
Amount amount,
address beneficiary,
bytes memory data
)
external
returns (PayoutId payoutId);
/// @dev processes the specified payout
/// this includes moving the payout token to the beneficiary (default: policy holder)
/// function can only be called by product, policy needs to match with calling product
function processPayout(
NftId policyNftId,
PayoutId payoutId
) external returns (Amount netPayoutAmount, Amount processingFeeAmount);
/// @dev cancels the specified payout. no tokens are moved, payout is set to cancelled.
function cancelPayout(
NftId policyNftId,
PayoutId payoutId
) external;
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IService} from "../shared/IService.sol";
import {Amount} from "../type/Amount.sol";
import {IInstance} from "../instance/IInstance.sol";
import {NftId} from "../type/NftId.sol";
import {StateId} from "../type/StateId.sol";
import {Timestamp} from "../type/Timestamp.sol";
interface IPolicyService is IService {
event LogPolicyServicePolicyCreated(NftId policyNftId, Amount premiumAmount, Timestamp activatedAt);
event LogPolicyServicePolicyDeclined(NftId policyNftId);
event LogPolicyServicePolicyPremiumCollected(NftId policyNftId, Amount premiumAmount);
event LogPolicyServicePolicyActivated(NftId policyNftId, Timestamp activatedAt);
event LogPolicyServicePolicyActivatedUpdated(NftId policyNftId, Timestamp activatedAt);
event LogPolicyServicePolicyExpirationUpdated(NftId policyNftId, Timestamp expiredAt);
event LogPolicyServicePolicyClosed(NftId policyNftId);
error LogPolicyServiceMaxPremiumAmountExceeded(NftId policyNftId, Amount maxPremiumAmount, Amount premiumAmount);
error ErrorPolicyServicePolicyProductMismatch(NftId applicationNftId, NftId expectedProductNftId, NftId actualProductNftId);
error ErrorPolicyServicePolicyStateNotApplied(NftId applicationNftId);
error ErrorPolicyServicePolicyStateNotCollateralized(NftId applicationNftId);
error ErrorPolicyServicePolicyAlreadyActivated(NftId policyNftId);
error ErrorPolicyServicePolicyNotActivated(NftId policyNftId);
error ErrorPolicyServicePolicyActivationTooEarly(NftId policyNftId, Timestamp lowerLimit, Timestamp activatedAt);
error ErrorPolicyServicePolicyActivationTooLate(NftId policyNftId, Timestamp upperLimit, Timestamp activatedAt);
error ErrorPolicyServiceInsufficientAllowance(address customer, address tokenHandlerAddress, uint256 amount);
error ErrorPolicyServicePremiumAlreadyPaid(NftId policyNftId);
error ErrorPolicyServicePolicyNotCloseable(NftId policyNftId);
error ErrorPolicyServicePolicyNotActive(NftId policyNftId, StateId state);
error ErrorPolicyServicePremiumNotPaid(NftId policyNftId, Amount premiumAmount);
error ErrorPolicyServiceOpenClaims(NftId policyNftId, uint16 openClaimsCount);
error ErrorPolicyServicePolicyHasNotExpired(NftId policyNftId, Timestamp expiredAt);
error ErrorPolicyServicePolicyExpirationTooLate(NftId policyNftId, Timestamp upperLimit, Timestamp expiredAt);
error ErrorPolicyServicePolicyExpirationTooEarly(NftId policyNftId, Timestamp lowerLimit, Timestamp expiredAt);
error ErrorPolicyServicePremiumMismatch(NftId policyNftId, Amount expectedPremiumAmount, Amount recalculatedPremiumAmount);
error ErrorPolicyServiceTransferredPremiumMismatch(NftId policyNftId, Amount expectedPremiumAmount, Amount transferredPremiumAmount);
/// @dev creates the policy from {applicationNftId}.
/// @param applicationNftId the application NftId
/// @param activateAt the timestamp when the policy should be activated
/// @param maxPremiumAmount the maximum premium amount that the policy holder is willing to pay
/// During policy creation, the effective premium amount is calculated based on the provided parameters. If this
/// amount is higher than the maxPremiumAmount, the function will revert.
/// After successful completion of the function the policy can be referenced using the application NftId.
/// Locks the sum insured amount in the pool, but does not transfer tokens. Call collectPremium to transfer tokens.
/// Sets the policy state to collateralized.
/// Optionally activates the policy if activateAt is a non-zero timestamp.
/// only the related product may create a policy from an application
/// @return premiumAmount the effective premium amount
function createPolicy(
NftId applicationNftId,
Timestamp activateAt,
Amount maxPremiumAmount
)
external
returns (Amount premiumAmount);
/// @dev declines an application represented by {policyNftId}
/// an application can only be declined in applied state
/// only the related product may decline an application
function decline(NftId policyNftId) external;
/// @dev collects the premium token for the specified policy (must be in COLLATERALIZED state)
function collectPremium(NftId policyNftId, Timestamp activateAt) external;
/// @dev activates the specified policy and sets the activation date in the policy metadata
/// to activate a policy it needs to be in underwritten state
function activate(NftId policyNftId, Timestamp activateAt) external;
/// @dev adjusts the activation date of the specified policy and sets the new activation date in the policy metadata
/// to adjust the activation date of a policy it needs to have an activation date set.
/// the new activation date must not be before the current block timestamp or after the expiry date
function adjustActivation(NftId policyNftId, Timestamp newActivateAt) external;
/// @dev Expires the specified policy and sets the expiry date in the policy metadata.
/// Function consumers are products.
/// If expiry date is set to 0, then the earliest possible expiry date (current blocktime) is set
/// to expire a policy it must be in active state, policies may be expired even when the predefined expiry date is still in the future
/// a policy can only be closed when it has been expired. in addition, it must not have any open claims
/// this function can only be called by a product. the policy needs to match with the calling product
/// @return expiredAt the effective expiry date
function expire(NftId policyNftId, Timestamp expireAt) external returns (Timestamp expiredAt);
/// @dev Closes the specified policy and sets the closed data in the policy metadata
/// a policy can only be closed when it has been expired. in addition, it must not have any open claims
/// this function can only be called by a product. the policy needs to match with the calling product
function close(NftId policyNftId) external;
/// @dev Expires the specified policy and sets the expiry date in the policy metadata.
/// Function consumers is claim service.
function expirePolicy(IInstance instance, NftId policyNftId, Timestamp expireAt) external returns (Timestamp expiredAt);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Seconds} from "../type/Seconds.sol";
import {NftId} from "../type/NftId.sol";
import {ReferralId} from "../type/Referral.sol";
import {RiskId} from "../type/RiskId.sol";
import {Amount} from "../type/Amount.sol";
import {IService} from "./IApplicationService.sol";
import {IPolicy} from "../instance/module/IPolicy.sol";
interface IPricingService is IService
{
error ErrorPricingServiceTargetWalletAmountsMismatch();
error ErrorPricingServiceRiskProductMismatch(RiskId riskId, NftId riskProductNftId, NftId productNftId);
error ErrorPricingServiceBundlePoolMismatch(NftId bundleNftId, NftId bundlePoolNftId, NftId poolNftId);
error ErrorPricingServiceFeeCalculationMismatch(
Amount distributionFeeFixAmount,
Amount distributionFeeVarAmount,
Amount distributionOwnerFeeFixAmount,
Amount distributionOwnerFeeVarAmount,
Amount commissionAmount,
Amount discountAmount
);
function calculatePremium(
NftId productNftId,
RiskId riskId,
Amount sumInsuredAmount,
Seconds lifetime,
bytes memory applicationData,
NftId bundleNftId,
ReferralId referralId
)
external
view
returns (IPolicy.PremiumInfo memory premium);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Amount} from "../type/Amount.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {IInstanceLinkedComponent} from "../shared/IInstanceLinkedComponent.sol";
import {IComponents} from "../instance/module/IComponents.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {ReferralId} from "../type/Referral.sol";
import {RiskId} from "../type/RiskId.sol";
import {Seconds} from "../type/Seconds.sol";
interface IProductComponent is
IInstanceLinkedComponent
{
// @dev register a new component for this product cluster.
function registerComponent(address component)
external
returns (NftId componentNftId);
/// @dev Callback function to inform product compnent about arrival of funding for a claim.
/// The callback is called by the pool service after the corresponding pool triggers this function.
/// The callback is only called when the product's property isProcessingFundedClaims is set.
function processFundedClaim(
NftId policyNftId,
ClaimId claimId,
Amount availableAmount
) external;
/// @dev Calculates the premium amount for the provided application data.
/// The returned premium amounts takes into account potential discounts and fees.
function calculatePremium(
Amount sumInsuredAmount,
RiskId riskId,
Seconds lifetime,
bytes memory applicationData,
NftId bundleNftId,
ReferralId referralId
) external view returns (Amount premiumAmount);
/// @dev Calculates the net premium amount for the provided application data.
/// The returned net premium amounts only covers the cost of collateralizing the application.
/// This amount purely depends on the use case specific risk and does not include any fees/commission.
function calculateNetPremium(
Amount sumInsuredAmount,
RiskId riskId,
Seconds lifetime,
bytes memory applicationData
) external view returns (Amount netPremiumAmount);
/// @dev returns initial product specific infos
function getInitialProductInfo() external view returns (IComponents.ProductInfo memory info);
/// @dev returns initial fee infos
function getInitialFeeInfo() external view returns (IComponents.FeeInfo memory info);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IService} from "../shared/IService.sol";
import {NftId} from "../type/NftId.sol";
import {RiskId} from "../type/RiskId.sol";
import {StateId} from "../type/StateId.sol";
interface IRiskService is IService {
event LogRiskServiceRiskCreated(NftId productNftId, RiskId riskId);
event LogRiskServiceRiskUpdated(NftId productNftId, RiskId riskId);
event LogRiskServiceRiskLocked(NftId productNftId, RiskId riskId);
event LogRiskServiceRiskUnlocked(NftId productNftId, RiskId riskId);
event LogRiskServiceRiskClosed(NftId productNftId, RiskId riskId);
error ErrorRiskServiceRiskProductMismatch(RiskId riskId, NftId riskProductNftId, NftId productNftId);
error ErrorRiskServiceRiskNotActive(NftId productNftId, RiskId riskId);
error ErrorRiskServiceUnknownRisk(NftId productNftId, RiskId riskId);
error ErrorRiskServiceRiskNotLocked(NftId productNftId, RiskId riskId);
/// @dev Create a new risk with the given id and provided data.
/// The key of the risk derived from the risk id in comination with the product NftId.
/// Risk data is stored in the instance store.
function createRisk(
bytes32 id,
bytes memory data
) external returns (RiskId riskId);
function updateRisk(
RiskId riskId,
bytes memory data
) external;
/// @dev Locks/unlocks the risk with the given id.
/// No new policies can be underwritten for a locked risk.
function setRiskLocked(
RiskId riskId,
bool locked
) external;
/// @dev Close the risk with the given id.
function closeRisk(
RiskId riskId
) external;
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {InstanceReader} from "../instance/InstanceReader.sol";
import {IPolicy} from "../instance/module/IPolicy.sol";
import {IPolicyService} from "../product/IPolicyService.sol";
import {NftId} from "../type/NftId.sol";
import {StateId, CLOSED, COLLATERALIZED} from "../type/StateId.sol";
import {Timestamp, TimestampLib} from "../type/Timestamp.sol";
library PolicyServiceLib {
function policyIsActive(InstanceReader instanceReader, NftId policyNftId)
external
view
returns (bool isActive)
{
// policy not collateralized
if (instanceReader.getPolicyState(policyNftId) != COLLATERALIZED()) {
return false;
}
IPolicy.PolicyInfo memory info = instanceReader.getPolicyInfo(policyNftId);
if (info.productNftId.eqz()) { return false; } // not closeable: policy does not exist (or does not belong to this instance)
if (info.activatedAt.eqz()) { return false; } // not closeable: not yet activated
if (info.activatedAt > TimestampLib.current()) { return false; } // not yet active
if (info.expiredAt <= TimestampLib.current()) { return false; } // already expired
return true;
}
function activate(
NftId policyNftId,
IPolicy.PolicyInfo memory policyInfo,
Timestamp activateAt
)
external
pure
returns (IPolicy.PolicyInfo memory)
{
// fail if policy has already been activated and activateAt is different
if(! policyInfo.activatedAt.eqz() && activateAt != policyInfo.activatedAt) {
revert IPolicyService.ErrorPolicyServicePolicyAlreadyActivated(policyNftId);
}
// ignore if policy has already been activated and activateAt is the same
if (policyInfo.activatedAt == activateAt) {
return policyInfo;
}
policyInfo.activatedAt = activateAt;
policyInfo.expiredAt = activateAt.addSeconds(policyInfo.lifetime);
return policyInfo;
}
function expire(
InstanceReader instanceReader,
NftId policyNftId,
IPolicy.PolicyInfo memory policyInfo,
Timestamp expireAt
)
external
view
returns (IPolicy.PolicyInfo memory)
{
StateId policyState = instanceReader.getPolicyState(policyNftId);
checkExpiration(
expireAt,
policyNftId,
policyState,
policyInfo);
// effects
// update policyInfo with new expiredAt timestamp
if (expireAt.gtz()) {
policyInfo.expiredAt = expireAt;
} else {
policyInfo.expiredAt = TimestampLib.current();
}
return policyInfo;
}
function checkExpiration(
Timestamp newExpiredAt,
NftId policyNftId,
StateId policyState,
IPolicy.PolicyInfo memory policyInfo
)
public
view
{
if (policyState != COLLATERALIZED()) {
revert IPolicyService.ErrorPolicyServicePolicyNotActive(policyNftId, policyState);
}
if (policyInfo.activatedAt.eqz() || TimestampLib.current() < policyInfo.activatedAt) {
revert IPolicyService.ErrorPolicyServicePolicyNotActive(policyNftId, policyState);
}
// check expiredAt represents a valid expiry time
if (newExpiredAt > policyInfo.expiredAt) {
revert IPolicyService.ErrorPolicyServicePolicyExpirationTooLate(policyNftId, policyInfo.expiredAt, newExpiredAt);
}
if (newExpiredAt.gtz() && newExpiredAt < TimestampLib.current()) {
revert IPolicyService.ErrorPolicyServicePolicyExpirationTooEarly(policyNftId, TimestampLib.current(), newExpiredAt);
}
}
function policyIsCloseable(InstanceReader instanceReader, NftId policyNftId)
external
view
returns (bool isCloseable)
{
// policy already closed
if (instanceReader.getPolicyState(policyNftId) == CLOSED()) {
return false;
}
IPolicy.PolicyInfo memory info = instanceReader.getPolicyInfo(policyNftId);
if (info.productNftId.eqz()) { return false; } // not closeable: policy does not exist (or does not belong to this instance)
if (info.activatedAt.eqz()) { return false; } // not closeable: not yet activated
if (info.openClaimsCount > 0) { return false; } // not closeable: has open claims
// closeable: if sum of claims matches sum insured a policy may be closed prior to the expiry date
if (info.claimAmount == info.sumInsuredAmount) { return true; }
// not closeable: not yet expired
if (TimestampLib.current() < info.expiredAt) { return false; }
// all conditions to close the policy are met
return true;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Amount, AmountLib} from "../type/Amount.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {Fee} from "../type/Fee.sol";
import {IApplicationService} from "./IApplicationService.sol";
import {IAuthorization} from "../authorization/IAuthorization.sol";
import {IComponentService} from "../shared/IComponentService.sol";
import {InstanceLinkedComponent} from "../shared/InstanceLinkedComponent.sol";
import {IPolicyService} from "./IPolicyService.sol";
import {IRiskService} from "./IRiskService.sol";
import {IClaimService} from "./IClaimService.sol";
import {IPricingService} from "./IPricingService.sol";
import {IProductComponent} from "./IProductComponent.sol";
import {NftId} from "../type/NftId.sol";
import {COMPONENT, PRODUCT, BUNDLE, APPLICATION, POLICY, CLAIM, PRICE } from "../type/ObjectType.sol";
import {PayoutId} from "../type/PayoutId.sol";
import {COMPONENT, PRODUCT, APPLICATION, POLICY, CLAIM, PRICE, BUNDLE, RISK } from "../type/ObjectType.sol";
import {ReferralId} from "../type/Referral.sol";
import {RiskId} from "../type/RiskId.sol";
import {Seconds} from "../type/Seconds.sol";
import {StateId} from "../type/StateId.sol";
import {Timestamp} from "../type/Timestamp.sol";
import {IPolicy} from "../instance/module/IPolicy.sol";
import {IComponents} from "../instance/module/IComponents.sol";
abstract contract Product is
InstanceLinkedComponent,
IProductComponent
{
// keccak256(abi.encode(uint256(keccak256("etherisc.storage.Product")) - 1)) & ~bytes32(uint256(0xff));
bytes32 public constant PRODUCT_STORAGE_LOCATION_V1 = 0x0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d6800;
struct ProductStorage {
IComponents.ProductInfo _productInfo;
IComponents.FeeInfo _feeInfo;
IComponentService _componentService;
IRiskService _riskService;
IApplicationService _applicationService;
IPolicyService _policyService;
IClaimService _claimService;
IPricingService _pricingService;
}
function registerComponent(address component)
external
virtual
restricted()
onlyOwner()
returns (NftId componentNftId)
{
return _getProductStorage()._componentService.registerComponent(component);
}
function processFundedClaim(
NftId policyNftId,
ClaimId claimId,
Amount availableAmount
)
external
virtual
restricted() // pool service role
onlyNftOfType(policyNftId, POLICY())
{
// default implementation does nothing
}
function calculatePremium(
Amount sumInsuredAmount,
RiskId riskId,
Seconds lifetime,
bytes memory applicationData,
NftId bundleNftId,
ReferralId referralId
)
public
view
virtual
override
onlyNftOfType(bundleNftId, BUNDLE())
returns (Amount premiumAmount)
{
IPolicy.PremiumInfo memory premium = _getProductStorage()._pricingService.calculatePremium(
getNftId(),
riskId,
sumInsuredAmount,
lifetime,
applicationData,
bundleNftId,
referralId
);
return premium.premiumAmount;
}
function calculateNetPremium(
Amount sumInsuredAmount,
RiskId,
Seconds,
bytes memory
)
external
view
virtual override
returns (Amount netPremiumAmount)
{
// default 10% of sum insured
return AmountLib.toAmount(sumInsuredAmount.toInt() / 10);
}
function getInitialProductInfo()
public
virtual
view
returns (IComponents.ProductInfo memory poolInfo)
{
return _getProductStorage()._productInfo;
}
function getInitialFeeInfo()
public
virtual
view
returns (IComponents.FeeInfo memory feeInfo)
{
return _getProductStorage()._feeInfo;
}
function __Product_init(
address registry,
NftId instanceNftId,
string memory name,
IComponents.ProductInfo memory productInfo,
IComponents.FeeInfo memory feeInfo,
IAuthorization authorization,
address initialOwner
)
internal
virtual
onlyInitializing()
{
__InstanceLinkedComponent_init(
registry,
instanceNftId,
name,
PRODUCT(),
authorization,
productInfo.isInterceptingPolicyTransfers,
initialOwner);
ProductStorage storage $ = _getProductStorage();
$._productInfo = productInfo;
$._feeInfo = feeInfo;
$._riskService = IRiskService(_getServiceAddress(RISK()));
$._applicationService = IApplicationService(_getServiceAddress(APPLICATION()));
$._policyService = IPolicyService(_getServiceAddress(POLICY()));
$._claimService = IClaimService(_getServiceAddress(CLAIM()));
$._pricingService = IPricingService(_getServiceAddress(PRICE()));
$._componentService = IComponentService(_getServiceAddress(COMPONENT()));
_registerInterface(type(IProductComponent).interfaceId);
}
function _setFees(
Fee memory productFee,
Fee memory processingFee
)
internal
virtual
{
_getProductStorage()._componentService.setProductFees(productFee, processingFee);
}
function _createRisk(
bytes32 id,
bytes memory data
)
internal
virtual
returns (RiskId riskId)
{
return _getProductStorage()._riskService.createRisk(
id,
data
);
}
function _updateRisk(
RiskId id,
bytes memory data
)
internal
virtual
{
_getProductStorage()._riskService.updateRisk(
id,
data
);
}
function _setRiskLocked(
RiskId id,
bool locked
)
internal
virtual
{
_getProductStorage()._riskService.setRiskLocked(
id,
locked
);
}
function _closeRisk(
RiskId id
)
internal
virtual
{
_getProductStorage()._riskService.closeRisk(
id
);
}
function _createApplication(
address applicationOwner,
RiskId riskId,
Amount sumInsuredAmount,
Amount premiumAmount,
Seconds lifetime,
NftId bundleNftId,
ReferralId referralId,
bytes memory applicationData
)
internal
virtual
returns (NftId applicationNftId)
{
return _getProductStorage()._applicationService.create(
applicationOwner,
riskId,
sumInsuredAmount,
premiumAmount,
lifetime,
bundleNftId,
referralId,
applicationData
);
}
function _revoke(
NftId applicationNftId
)
internal
virtual
{
_getProductStorage()._applicationService.revoke(
applicationNftId);
}
function _createPolicy(
NftId applicationNftId,
Timestamp activateAt,
Amount maxPremiumAmount
)
internal
virtual
returns (Amount premiumAmount)
{
premiumAmount = _getProductStorage()._policyService.createPolicy(
applicationNftId,
activateAt,
maxPremiumAmount);
}
function _decline(
NftId policyNftId
)
internal
virtual
{
_getProductStorage()._policyService.decline(
policyNftId);
}
function _expire(
NftId policyNftId,
Timestamp expireAt
)
internal
virtual
returns (Timestamp expiredAt)
{
expiredAt = _getProductStorage()._policyService.expire(policyNftId, expireAt);
}
/// @dev adjust the activation date of the policy.
/// The policy must already have an activation date set.
/// Allowed values are from the current blocktime to the expiration date of the policy.
function _adjustActivation(
NftId policyNftId,
Timestamp activateAt
)
internal
virtual
{
_getProductStorage()._policyService.adjustActivation(
policyNftId,
activateAt);
}
function _collectPremium(
NftId policyNftId,
Timestamp activateAt
)
internal
virtual
{
_getProductStorage()._policyService.collectPremium(
policyNftId,
activateAt);
}
function _activate(
NftId policyNftId,
Timestamp activateAt
)
internal
virtual
{
_getProductStorage()._policyService.activate(
policyNftId,
activateAt);
}
function _close(
NftId policyNftId
)
internal
virtual
{
_getProductStorage()._policyService.close(
policyNftId);
}
function _submitClaim(
NftId policyNftId,
Amount claimAmount,
bytes memory claimData
)
internal
virtual
returns(ClaimId)
{
return _getProductStorage()._claimService.submit(
policyNftId,
claimAmount,
claimData);
}
function _revokeClaim(
NftId policyNftId,
ClaimId claimId
)
internal
virtual
{
_getProductStorage()._claimService.revoke(
policyNftId,
claimId);
}
function _confirmClaim(
NftId policyNftId,
ClaimId claimId,
Amount confirmedAmount,
bytes memory data
)
internal
virtual
{
_getProductStorage()._claimService.confirm(
policyNftId,
claimId,
confirmedAmount,
data);
}
function _declineClaim(
NftId policyNftId,
ClaimId claimId,
bytes memory data
)
internal
virtual
{
_getProductStorage()._claimService.decline(
policyNftId,
claimId,
data);
}
function _cancelConfirmedClaim(
NftId policyNftId,
ClaimId claimId
)
internal
virtual
{
_getProductStorage()._claimService.cancelConfirmedClaim(
policyNftId,
claimId);
}
function _createPayout(
NftId policyNftId,
ClaimId claimId,
Amount amount,
bytes memory data
)
internal
virtual
returns (PayoutId)
{
return _getProductStorage()._claimService.createPayout(
policyNftId,
claimId,
amount,
data);
}
function _createPayoutForBeneficiary(
NftId policyNftId,
ClaimId claimId,
Amount amount,
address beneficiary,
bytes memory data
)
internal
virtual
returns (PayoutId)
{
return _getProductStorage()._claimService.createPayoutForBeneficiary(
policyNftId,
claimId,
amount,
beneficiary,
data);
}
function _processPayout(
NftId policyNftId,
PayoutId payoutId
)
internal
virtual
returns (Amount netPayoutAmount, Amount processingFeeAmount)
{
(netPayoutAmount, processingFeeAmount) = _getProductStorage()._claimService.processPayout(
policyNftId,
payoutId);
}
function _cancelPayout(
NftId policyNftId,
PayoutId payoutId
)
internal
virtual
{
_getProductStorage()._claimService.cancelPayout(
policyNftId,
payoutId);
}
function _getProductStorage() internal virtual pure returns (ProductStorage storage $) {
assembly {
$.slot := PRODUCT_STORAGE_LOCATION_V1
}
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {ERC721, ERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {ITransferInterceptor} from "./ITransferInterceptor.sol";
contract ChainNft is ERC721Enumerable {
event LogTokenInterceptorAddress(uint256 tokenId, address interceptor);
// constants
string public constant NAME = "Dezentralized Insurance Protocol NFT";
string public constant SYMBOL = "DIPNFT";
uint256 public constant PROTOCOL_NFT_ID = 1101;
uint256 public constant GLOBAL_REGISTRY_ID = 2101;
// custom errors
error ErrorChainNftCallerNotRegistry(address caller);
error ErrorChainNftRegistryAddressZero();
error ErrorChainNftUriEmpty();
error ErrorChainNftUriAlreadySet();
// contract state
// remember interceptors
mapping(uint256 tokenId => address interceptor) private _interceptor;
// remember token uri
mapping(uint256 tokenId => string uri) private _uri;
// remember registry
address private _registry;
// only used for _getNextTokenId
uint256 internal _chainIdDigits;
uint256 internal _chainIdMultiplier;
uint256 internal _idNext;
uint256 internal _totalMinted;
modifier onlyRegistry() {
if (msg.sender != _registry) { revert ErrorChainNftCallerNotRegistry(msg.sender); }
_;
}
constructor(address registry)
ERC721(NAME, SYMBOL)
{
if (registry == address(0)) { revert ErrorChainNftRegistryAddressZero(); }
// NFT contract is deployed by the registry
_registry = registry;
_chainIdDigits = _calculateChainIdDigits(block.chainid);
_chainIdMultiplier = 10 ** _chainIdDigits;
// the first object registered through normal registration starts with id 4
// 1 -> protocol
// 2 -> registry
// 3 -> staking
// >= 4 -> all other objects
_idNext = 4;
_totalMinted = 0;
}
/**
* @dev mints a token for a specified token id
* not part of the IRegistry interface only needed for
* initial registry setup (protocol and global registry objects)
*/
function mint(
address to,
uint256 tokenId
)
external
onlyRegistry()
{
_safeMintWithInterceptorAddress(to, tokenId, address(0));
}
/**
* @dev mints the next token to register new objects
* non-zero transferInterceptors are recorded and called during nft token transfers.
* the contract receiving such a notification may decides to revert or record the transfer
*/
function mint(
address to,
address interceptor,
string memory uri
)
public
onlyRegistry()
returns (uint256 tokenId)
{
tokenId = _getNextTokenId();
if (interceptor != address(0)) {
_interceptor[tokenId] = interceptor;
}
if (bytes(uri).length > 0) {
_uri[tokenId] = uri;
}
_safeMintWithInterceptorAddress(to, tokenId, interceptor);
}
/**
* @dev Amend the open zeppelin transferFrom function by an interceptor call if such an interceptor is defined for the nft token id.
* This allows distribution, product and pool components to be notified when distributors, policies and bundles are transferred.
*/
function transferFrom(address from, address to, uint256 tokenId) public override (IERC721, ERC721) {
// default open zeppelin implementation of transferFrom
super.transferFrom(from, to, tokenId);
if (_interceptor[tokenId] != address(0)) {
address operator = msg.sender;
ITransferInterceptor(_interceptor[tokenId]).nftTransferFrom(from, to, tokenId, operator);
}
}
function burn(uint256 tokenId) external onlyRegistry {
_requireOwned(tokenId);
_burn(tokenId);
delete _uri[tokenId];
}
function setURI(
uint256 tokenId,
string memory uri
) external onlyRegistry {
if (bytes(uri).length == 0) { revert ErrorChainNftUriEmpty(); }
if (bytes(_uri[tokenId]).length > 0) { revert ErrorChainNftUriAlreadySet(); }
_requireOwned(tokenId);
_uri[tokenId] = uri;
}
function exists(uint256 tokenId) external view returns (bool) {
return _ownerOf(tokenId) != address(0);
}
function tokenURI(
uint256 tokenId
) public view override returns (string memory) {
// gif generally does not revert for view functions
// this is an exception to keep the openzeppelin nft semantics
_requireOwned(tokenId);
return _uri[tokenId];
}
function getInterceptor(uint256 tokenId) external view returns (address) {
return _interceptor[tokenId];
}
function getRegistryAddress() external view returns (address) {
return _registry;
}
function totalMinted() external view returns (uint256) {
return _totalMinted;
}
/**
* @dev token id calculation based on an index value that is supposed
* to increase with every minted token
*
* requirement: each chain registry produces token ids that
* are guaranteed to not collide with any token id genereated
* on a different chain
*
* format concat(counter,chainid,2 digits for len-of-chain-id)
* restriction chainid up to 99 digits
* decode: from right to left:
* - 2 right most digits encode length of chainid
* - move number of digits to left as determined above (-> chainid)
* - the reminder to the left is the counter
*
* special cases
* 1101 -> decentralized insurance protocol
* 2102 -> global registry
* 2xxxxx -> chain registry, where xxxxx = <chain-part>
*
* examples
* 1101
* ^^ ^
* || +- 1-digit chain id
* |+-- chain id = 1 (mainnet)
* +-- 1st token id on mainnet
* (1 * 10 ** 1 + 1) * 100 + 1
* 42987654321010
* ^ ^ ^
* | | +- 10-digit chain id
* | +-- chain id = 9876543210 (hypothetical chainid)
* +-- 42nd token id on this chain
* (42 * 10 ** 10 + 9876543210) * 100 + 10
* (index * 10 ** digits + chainid) * 100 + digits (1 < digits < 100)
*/
function calculateTokenId(uint256 idIndex, uint256 chainId) public view returns (uint256 id) {
if(chainId == block.chainid) {
return 100 * (idIndex * _chainIdMultiplier + chainId) + _chainIdDigits;
} else {
uint256 chainIdDigits = _calculateChainIdDigits(chainId);
return 100 * (idIndex * (10 ** chainIdDigits) + chainId) + chainIdDigits;
}
}
function calculateTokenId(uint256 idIndex) public view returns (uint256) {
return 100 * (idIndex * _chainIdMultiplier + block.chainid) + _chainIdDigits;
}
function getNextTokenId() external view returns (uint256) {
return calculateTokenId(_idNext);
}
// ---------------- private functions ----------------- //
function _getNextTokenId() private returns (uint256 id) {
id = calculateTokenId(_idNext);
_idNext++;
}
function _calculateChainIdDigits(uint256 chainId) private pure returns (uint256) {
uint256 num = chainId;
uint256 digits = 0;
while (num != 0) {
digits++;
num /= 10;
}
return digits;
}
function _safeMintWithInterceptorAddress(
address to,
uint256 tokenId,
address interceptor
)
private
{
_totalMinted++;
_safeMint(to, tokenId);
emit LogTokenInterceptorAddress(tokenId, interceptor);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IRelease} from "./IRelease.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {VersionPart} from "../type/Version.sol";
/// @title Chain Registry interface.
/// A chain registry holds all protocol relevant objects with basic metadata.
/// Registered objects include services, instances, products, pools, policies, bundles, stakes and more.
/// Registered objects are represented by NFTs.
/// When on mainnet registry is global and keeps arbitrary number of chain registries residing on different chain ids.
/// When not on mainnet registry keeps the only object residing on different chain id (on mainnet) - global registry.
interface IRegistry is
IERC165
{
event LogRegistryObjectRegistered(NftId nftId, NftId parentNftId, ObjectType objectType, bool isInterceptor, address objectAddress, address initialOwner);
event LogRegistryServiceRegistered(VersionPart majorVersion, ObjectType domain);
event LogRegistryChainRegistryRegistered(NftId nftId, uint256 chainId, address chainRegistryAddress);
// initialize
error ErrorRegistryCallerNotDeployer();
// register()
error ErrorRegistryObjectTypeNotSupported(ObjectType objectType);
// registerRegistry()
error ErrorRegistryNotOnMainnet(uint256 chainId);
error ErrorRegistryChainRegistryChainIdZero(NftId nftId);
error ErrorRegistryChainRegistryAddressZero(NftId nftId, uint256 chainId);
error ErrorRegistryChainRegistryNftIdInvalid(NftId nftId, uint256 chainId);
error ErrorRegistryChainRegistryAlreadyRegistered(NftId nftId, uint256 chainId);
// registerService()
error ErrorRegistryServiceAddressZero();
error ErrorRegistryServiceVersionZero(address service);
error ErrorRegistryServiceDomainZero(address service, VersionPart version);
error ErrorRegistryNotService(address service, ObjectType objectType);
error ErrorRegistryServiceParentNotRegistry(address service, VersionPart version, NftId parentNftId);
error ErrorRegistryServiceDomainAlreadyRegistered(address service, VersionPart version, ObjectType domain);
// registerWithCustomTypes()
error ErrorRegistryCoreTypeRegistration();
// _register()
error ErrorRegistryGlobalRegistryAsParent(address objectAddress, ObjectType objectType);
error ErrorRegistryTypeCombinationInvalid(address objectAddress, ObjectType objectType, ObjectType parentType);
error ErrorRegistryContractAlreadyRegistered(address objectAddress);
struct ObjectInfo {
// slot 0
NftId nftId;
NftId parentNftId;
ObjectType objectType;
bool isInterceptor;
// slot 1
address objectAddress;
// slot 2
address initialOwner;
// slot 3
bytes data;
}
/// @dev Registers a registry contract for a specified chain.
/// Only one chain registry may be registered per chain
function registerRegistry(
NftId nftId,
uint256 chainId,
address chainRegistryAddress
) external;
/// @dev Register a service with using the provided domain and version.
/// The function returns a newly minted service NFT ID.
/// May only be used to register services.
function registerService(
ObjectInfo memory serviceInfo,
VersionPart serviceVersion,
ObjectType serviceDomain
) external returns(NftId nftId);
/// @dev Register an object with a known core type.
/// The function returns a newly minted object NFT ID.
/// May not be used to register services.
function register(ObjectInfo memory info) external returns (NftId nftId);
/// @dev Register an object with a custom type.
/// The function returns a newly minted object NFT ID.
/// This function is reserved for GIF releases > 3.
/// May not be used to register known core types.
function registerWithCustomType(ObjectInfo memory info) external returns (NftId nftId);
function getInitialRelease() external view returns (VersionPart);
function getNextRelease() external view returns (VersionPart);
function getLatestRelease() external view returns (VersionPart);
function getReleaseInfo(VersionPart release) external view returns (IRelease.ReleaseInfo memory);
/// @dev Returns the number of supported chains.
function chainIds() external view returns (uint256);
/// @dev Returns the chain id at the specified index.
function getChainId(uint256 idx) external view returns (uint256);
/// @dev Returns the NFT ID of the registry for the specified chain.
function getRegistryNftId(uint256 chainId) external returns (NftId nftId);
function getObjectCount() external view returns (uint256);
function getNftIdForAddress(address objectAddress) external view returns (NftId nftId);
function ownerOf(NftId nftId) external view returns (address);
function isOwnerOf(NftId nftId, address expectedOwner) external view returns (bool);
function ownerOf(address contractAddress) external view returns (address);
function getObjectInfo(NftId nftId) external view returns (ObjectInfo memory info);
function getParentNftId(NftId nftId) external view returns (NftId parentNftId);
function isObjectType(NftId nftId, ObjectType expectedObjectType) external view returns (bool);
function isObjectType(address contractAddress, ObjectType expectedObjectType) external view returns (bool);
function getObjectAddress(NftId nftId) external view returns (address objectAddress);
/// @dev Returns the object info for the specified object address.
// MUST not be used with chain registry address (resides on different chan id)
function getObjectInfo(address object) external view returns (ObjectInfo memory info);
function isRegistered(NftId nftId) external view returns (bool);
function isRegistered(address contractAddress) external view returns (bool);
function isRegisteredService(address contractAddress) external view returns (bool);
function isRegisteredComponent(address object) external view returns (bool);
function isActiveRelease(VersionPart version) external view returns (bool);
function getServiceAddress(
ObjectType serviceDomain,
VersionPart releaseVersion
) external view returns (address serviceAddress);
function getProtocolNftId() external view returns (NftId protocolNftId);
function getNftId() external view returns (NftId nftId);
function getOwner() external view returns (address);
// TODO refactor the address getters below to contract getters
function getChainNftAddress() external view returns (address);
function getReleaseRegistryAddress() external view returns (address);
function getStakingAddress() external view returns (address);
function getTokenRegistryAddress() external view returns (address);
function getRegistryAdminAddress() external view returns (address);
function getAuthority() external view returns (address);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IServiceAuthorization} from "../authorization/IServiceAuthorization.sol";
import {StateId} from "../type/StateId.sol";
import {Timestamp} from "../type/Timestamp.sol";
import {VersionPart} from "../type/Version.sol";
/// @title Marks contracts that are linked to a specific GIF release version.
interface IRelease {
struct ReleaseInfo {
// slot 0
address releaseAdmin;
StateId state;
VersionPart version;
Timestamp activatedAt;
Timestamp disabledAt;
// slot 1
IServiceAuthorization auth;
// slot 2
bytes32 salt;
}
/// @dev Registers a registry contract for a specified chain.
/// Only one chain registry may be registered per chain
function getRelease() external view returns (VersionPart release);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
interface ITransferInterceptor {
function nftTransferFrom(address from, address to, uint256 tokenId, address operator) external;
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Amount, AmountLib} from "../type/Amount.sol";
import {IComponent} from "./IComponent.sol";
import {IComponents} from "../instance/module/IComponents.sol";
import {IComponentService} from "./IComponentService.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType, COMPONENT} from "../type/ObjectType.sol";
import {Registerable} from "../shared/Registerable.sol";
import {TokenHandler} from "../shared/TokenHandler.sol";
import {Version, VersionLib} from "../type/Version.sol";
abstract contract Component is
Registerable,
IComponent
{
// keccak256(abi.encode(uint256(keccak256("gif-next.contracts.component.Component.sol")) - 1)) & ~bytes32(uint256(0xff));
bytes32 public constant COMPONENT_LOCATION_V1 = 0xffe8d4462baed26a47154f4b8f6db497d2f772496965791d25bd456e342b7f00;
struct ComponentStorage {
string _name; // unique (per instance) component name
bool _isInterceptor;
IComponentService _componentService;
}
modifier onlyChainNft() {
if(msg.sender != getRegistry().getChainNftAddress()) {
revert ErrorComponentNotChainNft(msg.sender);
}
_;
}
function _getComponentStorage() private pure returns (ComponentStorage storage $) {
// solhint-disable-next-line no-inline-assembly
assembly {
$.slot := COMPONENT_LOCATION_V1
}
}
function __Component_init(
address authority,
address registry,
NftId parentNftId,
string memory name,
ObjectType componentType,
bool isInterceptor,
address initialOwner,
bytes memory registryData // writeonly data that will saved in the object info record of the registry
)
internal
virtual
onlyInitializing()
{
if (bytes(name).length == 0) {
revert ErrorComponentNameLengthZero();
}
__Registerable_init(
authority,
registry,
parentNftId,
componentType,
isInterceptor,
initialOwner,
registryData);
// set component state
ComponentStorage storage $ = _getComponentStorage();
$._name = name;
$._isInterceptor = isInterceptor;
$._componentService = IComponentService(_getServiceAddress(COMPONENT()));
_registerInterface(type(IAccessManaged).interfaceId);
_registerInterface(type(IComponent).interfaceId);
}
/// @dev callback function for nft transfers
/// may only be called by chain nft contract.
/// override internal function _nftTransferFrom to implement custom behaviour
function nftTransferFrom(address from, address to, uint256 tokenId, address operator)
external
onlyChainNft
{
_nftTransferFrom(from, to, tokenId, operator);
}
function getWallet() public view virtual returns (address walletAddress) {
return getTokenHandler().getWallet();
}
function getTokenHandler() public virtual view returns (TokenHandler tokenHandler) {
return getComponentInfo().tokenHandler;
}
function getToken() public view virtual returns (IERC20Metadata token) {
return getTokenHandler().TOKEN();
}
function getName() public view override returns(string memory name) {
return getComponentInfo().name;
}
function getVersion() public view virtual returns (Version version) {
return VersionLib.toVersion(1, 0, 0);
}
function getComponentInfo() public virtual view returns (IComponents.ComponentInfo memory info) {
if (isRegistered()) {
return _getComponentInfo();
} else {
return getInitialComponentInfo();
}
}
/// @dev defines initial component specification
/// overwrite this function according to your use case
function getInitialComponentInfo() public virtual view returns (IComponents.ComponentInfo memory info) {
return _getComponentInfo();
}
function isNftInterceptor() public virtual view returns(bool isInterceptor) {
if (isRegistered()) {
return getRegistry().getObjectInfo(address(this)).isInterceptor;
} else {
return _getComponentStorage()._isInterceptor;
}
}
function isRegistered() public virtual view returns (bool) {
return getRegistry().getNftIdForAddress(address(this)).gtz();
}
/// @dev Approves token hanlder to spend up to the specified amount of tokens.
/// Reverts if component wallet is not token handler itself.
/// Only component owner (nft holder) is authorizes to call this function.
function _approveTokenHandler(IERC20Metadata token, Amount amount)
internal
virtual
returns (Amount oldAllowanceAmount)
{
oldAllowanceAmount = AmountLib.toAmount(
token.allowance(address(getTokenHandler()), address(this)));
_getComponentStorage()._componentService.approveTokenHandler(
token,
amount);
}
/// @dev internal function for nft transfers.
/// handling logic that deals with nft transfers need to overwrite this function
function _nftTransferFrom(address from, address to, uint256 tokenId, address operator)
internal
virtual
// solhint-disable-next-line no-empty-blocks
{
// empty default implementation
}
/// @dev Sets the components wallet to the specified address.
/// Depending on the source of the component information this function needs to be overwritten.
/// eg for instance linked components that externally store this information with the instance store contract
function _setWallet(
address newWallet
)
internal
virtual
{
_getComponentStorage()._componentService.setWallet(newWallet);
}
function _setLocked(bool locked)
internal
virtual
{
_getComponentStorage()._componentService.setLocked(locked);
}
/// @dev depending on the source of the component information this function needs to be overwritten.
/// eg for instance linked components that externally store this information with the instance store contract
function _getComponentInfo() internal virtual view returns (IComponents.ComponentInfo memory info) {
ComponentStorage storage $ = _getComponentStorage();
return IComponents.ComponentInfo({
name: $._name,
tokenHandler: TokenHandler(address(0))
});
}
/// @dev returns the service address for the specified domain
/// gets address via lookup from registry using the major version form the linked instance
function _getServiceAddress(ObjectType domain) internal view returns (address) {
return getRegistry().getServiceAddress(domain, getRelease());
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol";
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {IPolicyHolder} from "../shared/IPolicyHolder.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IService} from "../shared/IService.sol";
import {ChainId} from "../type/ChainId.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType, INSTANCE, PRODUCT, DISTRIBUTION, ORACLE, POOL} from "../type/ObjectType.sol";
import {VersionPart} from "../type/Version.sol";
interface ITargetHelper {
function isTargetLocked(address target) external view returns (bool);
}
interface IInstanceAdminHelper {
function getInstanceAdmin() external view returns (ITargetHelper);
}
interface ITokenRegistryHelper {
function isActive(ChainId chainId, address token, VersionPart release) external view returns (bool);
}
library ContractLib {
error ErrorContractLibCallerNotRegistered(address target);
error ErrorContractLibCallerNotComponent(NftId componentNftId, ObjectType objectType);
error ErrorContractLibParentNotInstance(NftId componentNftId, NftId parentNftId);
error ErrorContractLibParentNotProduct(NftId componentNftId, NftId parentNftId);
error ErrorContractLibComponentTypeMismatch(NftId componentNftId, ObjectType expectedType, ObjectType actualType);
error ErrorContractLibComponentInactive(NftId componentNftId);
function getInfoAndInstance(
IRegistry registry,
NftId componentNftId,
bool onlyActive
)
external
view
returns (
IRegistry.ObjectInfo memory info,
address instance
)
{
info = registry.getObjectInfo(componentNftId);
return _getAndVerifyComponentAndInstance(registry, info, info.objectType, onlyActive);
}
function getAndVerifyAnyComponent(
IRegistry registry,
address caller,
bool onlyActive
)
external
view
returns (
IRegistry.ObjectInfo memory callerInfo,
address instance
)
{
// check caller is component
callerInfo = _getAndVerifyObjectInfo(registry, caller);
if(!(callerInfo.objectType == PRODUCT()
|| callerInfo.objectType == POOL()
|| callerInfo.objectType == DISTRIBUTION()
|| callerInfo.objectType == ORACLE())
) {
revert ErrorContractLibCallerNotComponent(
callerInfo.nftId,
callerInfo.objectType);
}
return _getAndVerifyComponentAndInstance(registry, callerInfo, callerInfo.objectType, onlyActive);
}
function getAndVerifyComponent(
IRegistry registry,
address caller,
ObjectType expectedType,
bool onlyActive
)
external
view
returns (
IRegistry.ObjectInfo memory info,
address instance
)
{
info = _getAndVerifyObjectInfo(registry, caller);
return _getAndVerifyComponentAndInstance(registry, info, expectedType, onlyActive);
}
function getInstanceForComponent(
IRegistry registry,
NftId componentNftId
)
public
view
returns (address instance)
{
NftId productNftId = registry.getParentNftId(componentNftId);
NftId instanceNftId = registry.getParentNftId(productNftId);
return registry.getObjectInfo(instanceNftId).objectAddress;
}
function isActiveToken(
address tokenRegistryAddress,
ChainId chainId,
address token,
VersionPart release
)
external
view
returns (bool)
{
return ITokenRegistryHelper(
tokenRegistryAddress).isActive(
chainId, token, release);
}
function isPolicyHolder(address target) external view returns (bool) {
return ERC165Checker.supportsInterface(target, type(IPolicyHolder).interfaceId);
}
function isAuthority(address authority) public view returns (bool) {
if (!isContract(authority)) {
return false;
}
return supportsInterface(authority, type(IAccessManager).interfaceId);
}
function isAccessManaged(address target)
public
view
returns (bool)
{
if (!isContract(target)) {
return false;
}
(bool success, ) = target.staticcall(
abi.encodeWithSelector(
IAccessManaged.authority.selector));
return success;
}
function isRegistered(address registry, address caller, ObjectType expectedType) public view returns (bool) {
NftId nftId = IRegistry(registry).getNftIdForAddress(caller);
if (nftId.eqz()) {
return false;
}
return IRegistry(registry).getObjectInfo(nftId).objectType == expectedType;
}
function isService(address service) public view returns (bool) {
if (!isContract(service)) {
return false;
}
return supportsInterface(service, type(IService).interfaceId);
}
function isRegistry(address registry) public view returns (bool) {
if (!isContract(registry)) {
return false;
}
return supportsInterface(registry, type(IRegistry).interfaceId);
}
function isContract(address target) public view returns (bool) {
if (target == address(0)) {
return false;
}
uint256 size;
assembly {
size := extcodesize(target)
}
return size > 0;
}
function supportsInterface(address target, bytes4 interfaceId) public view returns (bool) {
return ERC165Checker.supportsInterface(target, interfaceId);
}
function _getAndVerifyComponentAndInstance(
IRegistry registry,
IRegistry.ObjectInfo memory info,
ObjectType expectedType,
bool onlyActive
)
internal
view
returns (
IRegistry.ObjectInfo memory,
address instance
)
{
if(info.objectType != expectedType) {
revert ErrorContractLibComponentTypeMismatch(
info.nftId,
expectedType,
info.objectType);
}
// get instance and check component is active
instance = getAndVerifyInstance(registry, info);
_checkComponentActive(instance, info.objectAddress, info.nftId, onlyActive);
return (
info,
instance
);
}
function _checkComponentActive(
address instance,
address target,
NftId componentNftId,
bool onlyActive
)
internal
view
{
if (onlyActive) {
if (IInstanceAdminHelper(
instance).getInstanceAdmin().isTargetLocked(
target)
) {
revert ErrorContractLibComponentInactive(componentNftId);
}
}
}
/// @dev Given an object info the function returns the instance address.
/// The info may represent a product or any other component.
/// If the parent of the provided info is not registered with the correct type, the function reverts.
function getAndVerifyInstance(
IRegistry registry,
IRegistry.ObjectInfo memory info
)
public
view
returns (address instance)
{
// get instance for product case
if (info.objectType == PRODUCT()) {
// verify that parent of product is registered instance
IRegistry.ObjectInfo memory instanceInfo = registry.getObjectInfo(info.parentNftId);
if (instanceInfo.objectType != INSTANCE()) {
revert ErrorContractLibParentNotInstance(
info.nftId,
info.parentNftId);
}
// we have verified that parent object is a registerd instance -> we return the instance address
return instanceInfo.objectAddress;
}
// not product: verify parent is registered product
info = registry.getObjectInfo(info.parentNftId);
if (info.objectType != PRODUCT()) {
revert ErrorContractLibParentNotProduct(
info.nftId,
info.parentNftId);
}
// we have verified that parent is registerd product -> we can rely on registry that its parent is an instance
return registry.getObjectAddress(info.parentNftId);
}
function _getAndVerifyObjectInfo(
IRegistry registry,
address caller
)
internal
view
returns (IRegistry.ObjectInfo memory info)
{
NftId componentNftId = registry.getNftIdForAddress(caller);
if (componentNftId.eqz()) {
revert ErrorContractLibCallerNotRegistered(caller);
}
info = registry.getObjectInfo(componentNftId);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IComponent} from "../shared/IComponent.sol";
import {IAuthorization} from "../authorization/IAuthorization.sol";
/// @dev component base class
/// component examples are product, distribution, pool and oracle
interface IAuthorizedComponent is
IComponent
{
/// @dev returns the initial component authorization specification.
function getAuthorization() external view returns (IAuthorization authorization);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Amount} from "../type/Amount.sol";
import {IComponents} from "../instance/module/IComponents.sol";
import {IRegisterable} from "../shared/IRegisterable.sol";
import {ITransferInterceptor} from "../registry/ITransferInterceptor.sol";
import {TokenHandler} from "../shared/TokenHandler.sol";
/// @dev component base class
/// component examples are staking, product, distribution, pool and oracle
interface IComponent is
IRegisterable,
ITransferInterceptor
{
error ErrorComponentProductNftIdZero();
error ErrorComponentProductNftIdNonzero();
error ErrorComponentNameLengthZero();
error ErrorComponentNotChainNft(address caller);
error ErrorComponentWalletAddressZero();
error ErrorComponentWalletAddressIsSameAsCurrent();
error ErrorComponentWalletNotComponent();
event LogComponentWalletAddressChanged(address oldWallet, address newWallet);
event LogComponentWalletTokensTransferred(address from, address to, uint256 amount);
event LogComponentTokenHandlerApproved(address tokenHandler, address token, Amount limit, bool isMaxAmount);
/// @dev returns the name of this component
/// to successfully register the component with an instance the name MUST be unique in the linked instance
function getName() external view returns (string memory name);
/// @dev defines which ERC20 token is used by this component
function getToken() external view returns (IERC20Metadata token);
/// @dev returns token handler for this component
function getTokenHandler() external view returns (TokenHandler tokenHandler);
/// @dev defines the wallet address used to hold the ERC20 tokens related to this component
/// the default address is the token handler address
function getWallet() external view returns (address walletAddress);
/// @dev returns true iff this compoent intercepts nft minting and transfers for objects registered by this component
function isNftInterceptor() external view returns(bool isInterceptor);
/// @dev returns true iff this component is registered with the registry
function isRegistered() external view returns (bool);
/// @dev returns the component infos for this component
/// for a non registered component the function returns getInitialComponentInfo()
function getComponentInfo() external view returns (IComponents.ComponentInfo memory info);
/// @dev returns the iniital component infos for this component
function getInitialComponentInfo() external view returns (IComponents.ComponentInfo memory info);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Amount} from "../type/Amount.sol";
import {Fee} from "../type/Fee.sol";
import {IService} from "../shared/IService.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {UFixed} from "../type/UFixed.sol";
import {VersionPart} from "../type/Version.sol";
/// @dev component base class
/// component examples are staking, product, distribution, pool and oracle
interface IComponentService is
IService
{
// registerProduct
error ErrorComponentServiceCallerNotInstance(address caller);
error ErrorComponentServiceNotProduct(address product);
error ErrorComponentServiceTokenInvalid(address token);
// registerComponent
error ErrorComponentServiceCallerNotProduct(address caller);
error ErrorComponentServiceNotComponent(address component);
error ErrorComponentServiceNotInstanceLinkedComponent(address component);
error ErrorComponentServiceComponentTypeNotSupported(address component, ObjectType invalid);
error ErrorComponentServiceComponentParentInvalid(address component, NftId required, NftId actual);
error ErrorComponentServiceComponentReleaseMismatch(address component, VersionPart serviceRelease, VersionPart componentRelease);
error ErrorComponentServiceComponentAlreadyRegistered(address component);
error ErrorProductServiceNoDistributionExpected(NftId productNftId);
error ErrorProductServiceDistributionAlreadyRegistered(NftId productNftId, NftId distributionNftId);
error ErrorProductServiceNoOraclesExpected(NftId productNftId);
error ErrorProductServiceOraclesAlreadyRegistered(NftId productNftId, uint8 expectedOracles);
error ErrorProductServicePoolAlreadyRegistered(NftId productNftId, NftId poolNftId);
error ErrorComponentServiceNewWalletAddressZero();
error ErrorComponentServiceWalletAddressZero();
error ErrorComponentServiceWalletAddressIsSameAsCurrent();
error ErrorComponentServiceWithdrawAmountIsZero();
error ErrorComponentServiceWithdrawAmountExceedsLimit(Amount withdrawnAmount, Amount withdrawLimit);
error ErrorComponentServiceWalletAllowanceTooSmall(address wallet, address spender, uint256 allowance, uint256 amount);
event LogComponentServiceComponentLocked(address component, bool locked);
event LogComponentServiceRegistered(NftId instanceNftId, NftId componentNftId, ObjectType componentType, address component, address token, address initialOwner);
event LogComponentServiceWalletAddressChanged(NftId componentNftId, address currentWallet, address newWallet);
event LogComponentServiceWalletTokensTransferred(NftId componentNftId, address currentWallet, address newWallet, uint256 currentBalance);
event LogComponentServiceComponentFeesWithdrawn(NftId componentNftId, address recipient, address token, Amount withdrawnAmount);
event LogComponentServiceProductFeesUpdated(NftId productNftId);
event LogComponentServiceDistributionFeesUpdated(NftId distributionNftId);
event LogComponentServicePoolFeesUpdated(NftId poolNftId);
event LogComponentServiceUpdateFee(
NftId nftId,
string feeName,
UFixed previousFractionalFee,
Amount previousFixedFee,
UFixed newFractionalFee,
Amount newFixedFee
);
//-------- component ----------------------------------------------------//
/// @dev Approves the callers token handler to spend up to the specified amount of tokens.
/// Reverts if the component's token handler wallet is not the token handler itself.
function approveTokenHandler(IERC20Metadata token, Amount amount) external;
/// @dev Sets the components associated wallet address.
/// To set the wallet to the token handler contract, use address(0) as the new wallet adress.
function setWallet(address newWallet) external;
/// @dev Locks/Unlocks the calling component.
function setLocked(bool locked) external;
/// @dev Withdraw fees from the distribution component. Only component owner is allowed to withdraw fees.
/// @param withdrawAmount the amount to withdraw
/// @return withdrawnAmount the amount that was actually withdrawn
function withdrawFees(Amount withdrawAmount) external returns (Amount withdrawnAmount);
/// @dev Registers the provided component with the product (sender)
function registerComponent(address component) external returns (NftId componentNftId);
//-------- product ------------------------------------------------------//
/// @dev Registers the specified product component for the instance (sender)
function registerProduct(address product, address token) external returns (NftId productNftId);
function setProductFees(
Fee memory productFee, // product fee on net premium
Fee memory processingFee // product fee on payout amounts
) external;
//-------- distribution -------------------------------------------------//
function setDistributionFees(
Fee memory distributionFee, // distribution fee for sales that do not include commissions
Fee memory minDistributionOwnerFee // min fee required by distribution owner (not including commissions for distributors)
) external;
//-------- pool ---------------------------------------------------------//
function setPoolFees(
Fee memory poolFee, // pool fee on net premium
Fee memory stakingFee, // pool fee on staked capital from investor
Fee memory performanceFee // pool fee on profits from capital investors
) external;
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Amount} from "../type/Amount.sol";
import {IAuthorizedComponent} from "../shared/IAuthorizedComponent.sol";
import {IAuthorization} from "../authorization/IAuthorization.sol";
import {IInstance} from "../instance/IInstance.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
/// @dev component base class
/// component examples are product, distribution, pool and oracle
interface IInstanceLinkedComponent is
IAuthorizedComponent
{
error ErrorInstanceLinkedComponentTypeMismatch(ObjectType requiredType, ObjectType objectType);
error ErrorInstanceLinkedComponentNotProduct(NftId nftId, ObjectType objectType);
/// @dev Withdraw fees from the distribution component. Only component owner is allowed to withdraw fees.
/// @param amount the amount to withdraw
/// @return withdrawnAmount the amount that was actually withdrawn
function withdrawFees(Amount amount) external returns (Amount withdrawnAmount);
/// @dev defines the instance to which this component is linked to
function getInstance() external view returns (IInstance instance);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {ObjectType} from "../type/ObjectType.sol";
import {StateId} from "../type/StateId.sol";
interface ILifecycle {
error ErrorNoLifecycle(address contractAddress, ObjectType objectType);
error ErrorFromStateMissmatch(address contractAddress, ObjectType objectType, StateId actual, StateId required);
error ErrorInvalidStateTransition(
address contractAddress,
ObjectType objectType,
StateId fromStateId,
StateId toStateId
);
function hasLifecycle(
ObjectType objectType
) external view returns (bool);
function getInitialState(
ObjectType objectType
) external view returns (StateId);
function isValidTransition(
ObjectType objectType,
StateId fromId,
StateId toId
) external view returns (bool);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IRegistryLinked} from "./IRegistryLinked.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
interface INftOwnable is
IERC165,
IRegistryLinked
{
event LogNftOwnableNftLinkedToAddress(NftId nftId, address owner);
error ErrorNftOwnableInitialOwnerZero();
error ErrorNftOwnableNotOwner(address account);
error ErrorNftOwnableInvalidType(NftId nftId, ObjectType expectedObjectType);
error ErrorNftOwnableAlreadyLinked(NftId nftId);
error ErrorNftOwnableContractNotRegistered(address contractAddress);
function linkToRegisteredNftId() external returns (NftId);
function getNftId() external view returns (NftId);
function getOwner() external view returns (address);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract InitializableERC165 is
Initializable,
IERC165
{
mapping(bytes4 => bool) private _isSupported;
// @dev initializes with support for ERC165
function __ERC165_init() internal onlyInitializing() {
_initializeERC165();
}
function _initializeERC165() internal {
_isSupported[type(IERC165).interfaceId] = true;
}
// @dev register support for provided interfaceId
// includes initialization for ERC165_ID if not yet done
function _registerInterface(bytes4 interfaceId) internal onlyInitializing() {
_registerInterfaceNotInitializing(interfaceId);
}
function _registerInterfaceNotInitializing(bytes4 interfaceId) internal{
_isSupported[interfaceId] = true;
}
function supportsInterface(bytes4 interfaceId) external view override returns (bool) {
return _isSupported[interfaceId];
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IAuthorization} from "../authorization/IAuthorization.sol";
import {IAuthorizedComponent} from "../shared/IAuthorizedComponent.sol";
import {IComponents} from "../instance/module/IComponents.sol";
import {IComponentService} from "./IComponentService.sol";
import {IInstance} from "../instance/IInstance.sol";
import {IInstanceLinkedComponent} from "./IInstanceLinkedComponent.sol";
import {IInstanceService} from "../instance/IInstanceService.sol";
import {IOracleService} from "../oracle/IOracleService.sol";
import {Amount} from "../type/Amount.sol";
import {Component} from "./Component.sol";
import {InstanceReader} from "../instance/InstanceReader.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType, COMPONENT, INSTANCE, ORACLE, PRODUCT} from "../type/ObjectType.sol";
import {RequestId} from "../type/RequestId.sol";
import {RoleId, RoleIdLib} from "../type/RoleId.sol";
import {TokenHandler} from "../shared/TokenHandler.sol";
import {Timestamp} from "../type/Timestamp.sol";
import {VersionPart} from "../type/Version.sol";
// then add (Distribution|Pool|Product)Upradeable that also intherit from Versionable
// same pattern as for Service which is also upgradeable
abstract contract InstanceLinkedComponent is
Component,
IInstanceLinkedComponent
{
// keccak256(abi.encode(uint256(keccak256("gif-next.contracts.component.Component.sol")) - 1)) & ~bytes32(uint256(0xff));
bytes32 public constant INSTANCE_LINKED_COMPONENT_LOCATION_V1 = 0xffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f00;
struct InstanceLinkedComponentStorage {
IInstance _instance; // instance for this component
InstanceReader _instanceReader; // instance reader for this component
IAuthorization _initialAuthorization;
IComponentService _componentService;
IOracleService _oracleService;
}
//--- view functions ----------------------------------------------------//
/// @inheritdoc IInstanceLinkedComponent
function getInstance() public view virtual override returns (IInstance instance) {
return _getInstanceLinkedComponentStorage()._instance;
}
/// @inheritdoc IAuthorizedComponent
function getAuthorization() external view virtual returns (IAuthorization authorization) {
return _getInstanceLinkedComponentStorage()._initialAuthorization;
}
/// @inheritdoc IInstanceLinkedComponent
function withdrawFees(Amount amount)
external
virtual
restricted()
onlyOwner()
returns (Amount withdrawnAmount)
{
return _withdrawFees(amount);
}
//--- internal functions ------------------------------------------------//
function _sendRequest(
NftId oracleNftId,
bytes memory requestData,
Timestamp expiryAt,
string memory callbackMethod
)
internal
virtual
returns (RequestId requestId)
{
return _getInstanceLinkedComponentStorage()._oracleService.request(
oracleNftId,
requestData,
expiryAt,
callbackMethod);
}
function _cancelRequest(RequestId requestId)
internal
virtual
{
_getInstanceLinkedComponentStorage()._oracleService.cancel(requestId);
}
function _resendRequest(RequestId requestId)
internal
virtual
{
_getInstanceLinkedComponentStorage()._oracleService.resend(requestId);
}
function _getInstanceLinkedComponentStorage() private pure returns (InstanceLinkedComponentStorage storage $) {
assembly {
$.slot := INSTANCE_LINKED_COMPONENT_LOCATION_V1
}
}
function __InstanceLinkedComponent_init(
address registry,
NftId parentNftId,
string memory name,
ObjectType componentType,
IAuthorization authorization,
bool isInterceptor,
address initialOwner
)
internal
virtual
onlyInitializing()
{
// validate registry, nft ids and get parent nft id
NftId instanceNftId = _checkAndGetInstanceNftId(
registry,
parentNftId,
componentType);
// set and check linked instance
InstanceLinkedComponentStorage storage $ = _getInstanceLinkedComponentStorage();
$._instance = IInstance(
IRegistry(registry).getObjectAddress(instanceNftId));
// set component specific parameters
__Component_init(
$._instance.authority(), // instance linked components need to point to instance admin
registry,
parentNftId,
name,
componentType,
isInterceptor,
initialOwner,
""); // registry data
// set instance linked specific parameters
$._instanceReader = $._instance.getInstanceReader();
$._initialAuthorization = authorization;
$._componentService = IComponentService(_getServiceAddress(COMPONENT()));
$._oracleService = IOracleService(_getServiceAddress(ORACLE()));
// register interfaces
_registerInterface(type(IInstanceLinkedComponent).interfaceId);
}
function _checkAndGetInstanceNftId(
address registryAddress,
NftId parentNftId,
ObjectType componentType
)
internal
view
returns (NftId instanceNftId)
{
// if product, then parent is already instance
if (componentType == PRODUCT()) {
_checkAndGetRegistry(registryAddress, parentNftId, INSTANCE());
return parentNftId;
}
// if not product parent is product, and parent of product is instance
IRegistry registry = _checkAndGetRegistry(registryAddress, parentNftId, PRODUCT());
return registry.getParentNftId(parentNftId);
}
/// @dev checks the and gets registry.
/// validates registry using a provided nft id and expected object type.
function _checkAndGetRegistry(
address registryAddress,
NftId objectNftId,
ObjectType requiredType
)
internal
view
returns (IRegistry registry)
{
registry = IRegistry(registryAddress);
IRegistry.ObjectInfo memory info = registry.getObjectInfo(objectNftId);
if (info.objectType != requiredType) {
revert ErrorInstanceLinkedComponentTypeMismatch(requiredType, info.objectType);
}
}
/// @dev for instance linked components the wallet address stored in the instance store.
/// updating needs to go throug component service
function _setWallet(address newWallet) internal virtual override onlyOwner {
IComponentService(_getServiceAddress(COMPONENT())).setWallet(newWallet);
}
function _getComponentInfo() internal virtual override view returns (IComponents.ComponentInfo memory info) {
NftId componentNftId = getRegistry().getNftIdForAddress(address(this));
// if registered, attempt to return component info via instance reader
if (componentNftId.gtz()) {
// component registerd with registry
info = _getInstanceReader().getComponentInfo(getNftId());
// check if also registered with instance
if (address(info.tokenHandler) != address(0)) {
return info;
}
}
// return data from component contract if not yet registered
return super._getComponentInfo();
}
/// @dev returns reader for linked instance
function _getInstanceReader() internal view returns (InstanceReader reader) {
return _getInstanceLinkedComponentStorage()._instanceReader;
}
function _withdrawFees(Amount amount)
internal
returns (Amount withdrawnAmount)
{
return _getInstanceLinkedComponentStorage()._componentService.withdrawFees(amount);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {Amount} from "../type/Amount.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {IRegistryLinked} from "../shared/IRegistryLinked.sol";
import {NftId} from "../type/NftId.sol";
import {PayoutId} from "../type/PayoutId.sol";
import {Timestamp} from "../type/Timestamp.sol";
/// @dev Generic interface for contracts that need to hold policies and receive payouts.
/// The framework notifies policy holder contracts for policy creation/expiry, claim confirmation and payout execution
interface IPolicyHolder is
IERC165,
IERC721Receiver,
IRegistryLinked
{
/// @dev Callback function that will be called after successful policy activation.
/// Active policies may open claims under the activated policy.
function policyActivated(NftId policyNftId, Timestamp activatedAt) external;
/// @dev Callback function to indicate the specified policy has expired.
/// expired policies no longer accept new claims.
function policyExpired(NftId policyNftId, Timestamp expiredAt) external;
/// @dev Callback function to notify the confirmation of the specified claim.
function claimConfirmed(NftId policyNftId, ClaimId claimId, Amount amount) external;
/// @dev Callback function to notify the successful payout.
function payoutExecuted(NftId policyNftId, PayoutId payoutId, Amount amount, address beneficiary) external;
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {INftOwnable} from "./INftOwnable.sol";
import {IRelease} from "../registry/IRelease.sol";
import {IRegistry} from "../registry/IRegistry.sol";
/// @title IRegisterable
/// @dev Marks contracts that are intended to be registered in the registry.
///
interface IRegisterable is
IAccessManaged,
INftOwnable,
IRelease
{
// __Registerable_init
error ErrorAuthorityInvalid(address authority);
// onlyActive()
error ErrorRegisterableNotActive();
/// @dev Returns true iff this contract managed by its authority is active.
/// Queries the IAccessManaged.authority().
function isActive() external view returns (bool active);
/// @dev retuns the object info relevant for registering for this contract
/// IMPORTANT information returned by this function may only be used
/// before the contract is registered in the registry.
/// Once registered this information MUST only be accessed via the registry.
function getInitialInfo()
external
view
returns (IRegistry.ObjectInfo memory);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IRegistry} from "../registry/IRegistry.sol";
interface IRegistryLinked {
error ErrorNotRegistry(address registryAddress);
function getRegistry() external view returns (IRegistry);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {IRegisterable} from "./IRegisterable.sol";
import {IVersionable} from "../upgradeability/IVersionable.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RoleId} from "../type/RoleId.sol";
interface IService is
IAccessManaged,
IRegisterable,
IVersionable
{
/// @dev returns the domain for this service.
/// In any GIF release only one service for any given domain may be deployed.
function getDomain() external pure returns(ObjectType serviceDomain);
/// @dev returns the GIF release specific role id.
/// These role ids are used to authorize service to service communication.
function getRoleId() external view returns(RoleId serviceRoleId);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {ObjectType} from "../type/ObjectType.sol";
import {StateId, zeroStateId} from "../type/StateId.sol";
import {ILifecycle} from "./ILifecycle.sol";
abstract contract Lifecycle is
ILifecycle
{
mapping(ObjectType objectType => StateId initialState)
private _initialState;
mapping(ObjectType objectType => mapping(StateId stateFrom => mapping(StateId stateTo => bool isValid)))
private _isValidTransition;
/// @dev child class must implement and CALL setup func at deployment/initializaton time
function _setupLifecycle() internal virtual;
function setInitialState(ObjectType ttype, StateId state) internal virtual {
assert(_initialState[ttype] == zeroStateId());
_initialState[ttype] = state;
}
function setStateTransition(ObjectType ttype, StateId oldState, StateId newState) internal virtual {
assert(_isValidTransition[ttype][oldState][newState] == false);
_isValidTransition[ttype][oldState][newState] = true;
}
function hasLifecycle(
ObjectType objectType
)
public
view
override
returns (bool)
{
return _initialState[objectType].gtz();
}
function getInitialState(
ObjectType objectType
)
public
view
returns (StateId)
{
return _initialState[objectType];
}
function checkTransition(
StateId stateId,
ObjectType objectType,
StateId expectedFromId,
StateId toId
)
public
view
{
// revert if no life cycle support
if (_initialState[objectType].eqz()) {
revert ErrorNoLifecycle(address(this), objectType);
}
// revert if current state is not expected `from` state
if(stateId != expectedFromId) {
revert ErrorFromStateMissmatch(address(this), objectType, stateId, expectedFromId);
}
// enforce valid state transition
if (!_isValidTransition[objectType][stateId][toId]) {
revert ErrorInvalidStateTransition(
address(this),
objectType,
stateId,
toId
);
}
}
function isValidTransition(
ObjectType objectType,
StateId fromId,
StateId toId
) public view returns (bool) {
return _isValidTransition[objectType][fromId][toId];
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {InitializableERC165} from "./InitializableERC165.sol";
import {INftOwnable} from "./INftOwnable.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RegistryLinked} from "./RegistryLinked.sol";
contract NftOwnable is
InitializableERC165,
RegistryLinked,
INftOwnable
{
// keccak256(abi.encode(uint256(keccak256("etherisc.storage.NftOwnable")) - 1)) & ~bytes32(uint256(0xff));
bytes32 public constant NFT_OWNABLE_STORAGE_LOCATION_V1 = 0x07ebcf49758b6ed3af50fa146bec0abe157c0218fe65dc0874c286e9d5da4f00;
struct NftOwnableStorage {
NftId _nftId;
address _initialOwner;
}
/// @dev enforces msg.sender is owner of nft (or initial owner of nft ownable)
modifier onlyOwner() {
if (msg.sender != getOwner()) {
revert ErrorNftOwnableNotOwner(msg.sender);
}
_;
}
modifier onlyNftOwner(NftId nftId) {
if(!getRegistry().isOwnerOf(nftId, msg.sender)) {
revert ErrorNftOwnableNotOwner(msg.sender);
}
_;
}
modifier onlyNftOfType(NftId nftId, ObjectType expectedObjectType) {
_checkNftType(nftId, expectedObjectType);
_;
}
function _checkNftType(NftId nftId, ObjectType expectedObjectType) internal view {
if(expectedObjectType.eqz() || !getRegistry().isObjectType(nftId, expectedObjectType)) {
revert ErrorNftOwnableInvalidType(nftId, expectedObjectType);
}
}
/// @dev Initialization for upgradable contracts.
// used in __Registerable_init, ProxyManager._preDeployChecksAndSetup
function __NftOwnable_init(
address registry,
address initialOwner
)
internal
virtual
onlyInitializing()
{
__ERC165_init();
__RegistryLinked_init(registry);
if(initialOwner == address(0)) {
revert ErrorNftOwnableInitialOwnerZero();
}
_getNftOwnableStorage()._initialOwner = initialOwner;
}
/// @dev links this contract to nft after registration
// needs to be done once per registered contract and
// reduces registry calls to check ownership
// does not need any protection as function can only do the "right thing"
function linkToRegisteredNftId()
public
virtual
returns (NftId nftId)
{
return _linkToNftOwnable(address(this));
}
function getNftId() public view virtual override returns (NftId) {
return _getNftOwnableStorage()._nftId;
}
function getOwner() public view virtual override returns (address) {
NftOwnableStorage storage $ = _getNftOwnableStorage();
if ($._nftId.gtz()) {
return getRegistry().ownerOf($._nftId);
}
return $._initialOwner;
}
/// @dev used in constructor of registry service manager
// links ownership of registry service manager ot nft owner of registry service
function _linkToNftOwnable(
address nftOwnableAddress
)
internal
returns (NftId)
{
NftOwnableStorage storage $ = _getNftOwnableStorage();
if ($._nftId.gtz()) {
revert ErrorNftOwnableAlreadyLinked($._nftId);
}
if (!getRegistry().isRegistered(nftOwnableAddress)) {
revert ErrorNftOwnableContractNotRegistered(nftOwnableAddress);
}
$._nftId = getRegistry().getNftIdForAddress(nftOwnableAddress);
emit LogNftOwnableNftLinkedToAddress($._nftId, getOwner());
return $._nftId;
}
function _getNftOwnableStorage() private pure returns (NftOwnableStorage storage $) {
// solhint-disable-next-line no-inline-assembly
assembly {
$.slot := NFT_OWNABLE_STORAGE_LOCATION_V1
}
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol";
import {ContractLib} from "../shared/ContractLib.sol";
import {NftId, NftIdLib} from "../type/NftId.sol";
import {NftOwnable} from "../shared/NftOwnable.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {VersionPart, VersionPartLib} from "../type/Version.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IRegisterable} from "./IRegisterable.sol";
import {IRelease} from "../registry/IRelease.sol";
abstract contract Registerable is
AccessManagedUpgradeable,
NftOwnable,
IRegisterable
{
// keccak256(abi.encode(uint256(keccak256("gif-next.contracts.shared.Registerable.sol")) - 1)) & ~bytes32(uint256(0xff));
bytes32 public constant REGISTERABLE_LOCATION_V1 = 0x6548007c3f4340f82f348c576c0ff69f4f529cadd5ad41f96aae61abceeaa300;
struct RegisterableStorage {
NftId _parentNftId;
ObjectType _objectType;
bool _isInterceptor;
bytes _data;
}
modifier onlyActive() {
if (!isActive()) {
revert ErrorRegisterableNotActive();
}
_;
}
function __Registerable_init(
address authority,
address registry,
NftId parentNftId,
ObjectType objectType,
bool isInterceptor,
address initialOwner,
bytes memory data // writeonly data that will saved in the object info record of the registry
)
internal
virtual
onlyInitializing()
{
if (!ContractLib.isAuthority(authority)) {
revert ErrorAuthorityInvalid(authority);
}
__AccessManaged_init(authority);
__NftOwnable_init(registry, initialOwner);
RegisterableStorage storage $ = _getRegisterableStorage();
$._parentNftId = parentNftId;
$._objectType = objectType;
$._isInterceptor = isInterceptor;
$._data = data;
_registerInterface(type(IAccessManaged).interfaceId);
}
/// @inheritdoc IRegisterable
function isActive() public virtual view returns (bool active) {
return !AccessManagerCloneable(authority()).isTargetClosed(address(this));
}
/// @inheritdoc IRelease
function getRelease() public virtual view returns (VersionPart release) {
return AccessManagerCloneable(authority()).getRelease();
}
/// @inheritdoc IRegisterable
function getInitialInfo()
public
view
virtual
returns (IRegistry.ObjectInfo memory info)
{
RegisterableStorage storage $ = _getRegisterableStorage();
return IRegistry.ObjectInfo(
NftIdLib.zero(),
$._parentNftId,
$._objectType,
$._isInterceptor,
address(this),
getOwner(),
$._data);
}
function _getRegisterableStorage() private pure returns (RegisterableStorage storage $) {
assembly {
$.slot := REGISTERABLE_LOCATION_V1
}
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {ContractLib} from "../shared/ContractLib.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IRegistryLinked} from "./IRegistryLinked.sol";
contract RegistryLinked is
Initializable,
IRegistryLinked
{
// priorize simplicity and size over using standard upgradeability structs
IRegistry private _registry;
/// @dev initialization for upgradable contracts
// used in _initializeRegisterable
function __RegistryLinked_init(
address registry
)
internal
virtual
onlyInitializing()
{
if (!ContractLib.isRegistry(registry)) {
revert ErrorNotRegistry(registry);
}
_registry = IRegistry(registry);
}
function getRegistry() public view returns (IRegistry) {
return _registry;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Amount, AmountLib} from "../type/Amount.sol";
import {ContractLib} from "../shared/ContractLib.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {NftId} from "../type/NftId.sol";
import {SERVICE} from "../type/ObjectType.sol";
/// @dev Token specific transfer helper base contract.
/// A default token contract is provided via contract constructor.
/// Relies internally on OpenZeppelin SafeERC20.safeTransferFrom.
/// This base contract simplifies writing tests.
contract TokenHandlerBase {
// _setWallet
event LogTokenHandlerWalletAddressChanged(NftId componentNftId, address oldWallet, address newWallet);
event LogTokenHandlerWalletTokensTransferred(NftId componentNftId, address oldWallet, address newWallet, Amount amount);
// _approveTokenHandler
event LogTokenHandlerTokenApproved(NftId nftId, address tokenHandler, address token, Amount amount, bool isMaxAmount);
// _transfer
event LogTokenHandlerTokenTransfer(address token, address from, address to, Amount amount);
// constructor
error ErrorTokenHandlerNotRegistry(address registry);
error ErrorTokenHandlerComponentNotRegistered(address component);
error ErrorTokenHandlerTokenAddressZero();
// _setWallet
error ErrorTokenHandlerNewWalletAddressZero();
error ErrorTokenHandlerAddressIsSameAsCurrent();
// _approveTokenHandler
error ErrorTokenHandlerNotWallet(NftId nftId, address tokenHandler, address wallet);
// _checkPreconditions
error ErrorTokenHandlerBalanceTooLow(address token, address from, uint256 balance, uint256 expectedBalance);
error ErrorTokenHandlerAllowanceTooSmall(address token, address from, address spender, uint256 allowance, uint256 expectedAllowance);
error ErrorTokenHandlerAmountIsZero();
IRegistry public immutable REGISTRY;
IERC20Metadata public immutable TOKEN;
address public immutable COMPONENT;
NftId public immutable NFT_ID;
address internal _wallet;
constructor(
address registry,
address component,
address token
)
{
if (!ContractLib.isRegistry(registry)) {
revert ErrorTokenHandlerNotRegistry(registry);
}
if (token == address(0)) {
revert ErrorTokenHandlerTokenAddressZero();
}
REGISTRY = IRegistry(registry);
COMPONENT = component;
NFT_ID = REGISTRY.getNftIdForAddress(component);
if (NFT_ID.eqz()) {
revert ErrorTokenHandlerComponentNotRegistered(component);
}
TOKEN = IERC20Metadata(token);
// self approval of token handler to max amount
_approve(TOKEN, AmountLib.max());
}
/// @dev Checks the balance and allowance for the from address and amount.
/// When requiring amount > 0 set checkAmount to true.
function checkBalanceAndAllowance(
address from,
Amount amount,
bool checkAmount
)
external
view
{
_checkBalanceAndAllowance(from, amount, checkAmount);
}
/// @dev Returns the wallet linked to this TokenHandler.
function getWallet()
public
view
returns (address wallet)
{
if (_wallet == address(0)) {
return address(this);
}
return _wallet;
}
/// @dev Approves token handler to spend up to the specified amount of tokens.
/// Sets spending limit to type(uint256).max for AmountLib.max().
/// Reverts if wallet is not token handler itself.
/// Sets approvel using SareERC20.forceApprove internally.
function _approve(
IERC20Metadata token,
Amount amount
)
internal
{
// check that wallet is token handler contract itself
if(_wallet != address(0)) {
revert ErrorTokenHandlerNotWallet(NFT_ID, address(this), _wallet);
}
// update spending limit for AmountLib.max() to type(uint256).max
uint256 amountInt = amount.toInt();
bool isMaxAmount = false;
if (amount == AmountLib.max()) {
amountInt = type(uint256).max;
isMaxAmount = true;
}
emit LogTokenHandlerTokenApproved(NFT_ID, address(this), address(token), amount, isMaxAmount);
// execute approval
SafeERC20.forceApprove(
token,
address(this),
amountInt);
}
function _setWallet(address newWallet)
internal
{
address oldWallet = _wallet;
if (newWallet == oldWallet) {
revert ErrorTokenHandlerAddressIsSameAsCurrent();
}
// effects
address oldWalletForBalance = getWallet();
_wallet = newWallet;
emit LogTokenHandlerWalletAddressChanged(NFT_ID, oldWallet, newWallet);
// interactions
Amount balanceAmount = AmountLib.toAmount(
TOKEN.balanceOf(oldWalletForBalance));
if (balanceAmount.gtz()) {
// move tokens from old to new wallet
emit LogTokenHandlerWalletTokensTransferred(NFT_ID, oldWallet, newWallet, balanceAmount);
if (oldWallet == address(0)) {
// transfer from the component requires an allowance
_transfer(address(this), newWallet, balanceAmount, true);
} else if (newWallet == address(0)) {
_transfer(oldWallet, address(this), balanceAmount, true);
} else {
_transfer(oldWallet, newWallet, balanceAmount, true);
}
}
}
function _pullToken(address from, Amount amount)
internal
{
_transfer(from, getWallet(), amount, true);
}
function _pushToken(address to, Amount amount)
internal
{
_transfer(getWallet(), to, amount, true);
}
function _transfer(
address from,
address to,
Amount amount,
bool checkPreconditions
)
internal
{
if (checkPreconditions) {
bool checkAmount = true;
_checkBalanceAndAllowance(from, amount, checkAmount);
}
// transfer the tokens
emit LogTokenHandlerTokenTransfer(address(TOKEN), from, to, amount);
SafeERC20.safeTransferFrom(
TOKEN,
from,
to,
amount.toInt());
}
function _checkBalanceAndAllowance(
address from,
Amount amount,
bool checkAmount
)
internal
view
{
// amount must be greater than zero
if (checkAmount && amount.eqz()) {
revert ErrorTokenHandlerAmountIsZero();
}
// allowance must be >= amount
uint256 allowance = TOKEN.allowance(from, address(this));
if (allowance < amount.toInt()) {
revert ErrorTokenHandlerAllowanceTooSmall(address(TOKEN), from, address(this), allowance, amount.toInt());
}
// balance must be >= amount
uint256 balance = TOKEN.balanceOf(from);
if (balance < amount.toInt()) {
revert ErrorTokenHandlerBalanceTooLow(address(TOKEN), from, balance, amount.toInt());
}
}
}
/// @dev Token specific transfer helper.
/// Contract is derived from TokenHandlerBase and adds
/// authorization based on OpenZeppelin AccessManaged.
contract TokenHandler is
AccessManaged,
TokenHandlerBase
{
// onlyService
error ErrorTokenHandlerNotService(address service);
modifier onlyService() {
if (!REGISTRY.isObjectType(msg.sender, SERVICE())) {
revert ErrorTokenHandlerNotService(msg.sender);
}
_;
}
constructor(
address registry,
address component,
address token,
address authority
)
TokenHandlerBase(registry, component, token)
AccessManaged(authority)
{ }
/// @dev Sets the wallet address for the component.
/// Seeting the new wallet address to address(0) will set the wallet to the tokenHandler contract itself.
/// If the current wallet has tokens, these will be transferred.
/// If the new wallet address is externally owned, an approval from the
/// owner of the external wallet to the tokenhandler of the component that
/// covers the current component balance must exist.
function setWallet(address newWallet)
external
restricted()
onlyService()
{
_setWallet(newWallet);
}
/// @dev Approves token handler to spend up to the specified amount of tokens.
/// Sets spending limit to type(uint256).max for AmountLib.max().
/// Reverts if component wallet is not component itself.
/// Sets approvel using SareERC20.forceApprove internally.
function approve(
IERC20Metadata token,
Amount amount
)
external
restricted()
onlyService()
{
_approve(token, amount);
}
/// @dev Collect tokens from outside of GIF and transfer them to the wallet.
/// This method also checks balance and allowance and makes sure the amount is greater than zero.
function pullToken(
address from,
Amount amount
)
external
restricted()
onlyService()
{
_pullToken(from, amount);
}
/// @dev Distribute tokens from a wallet within the scope of gif to some address.
function pushToken(
address to,
Amount amount
)
external
restricted()
onlyService()
{
_pushToken(to, amount);
}
/// @dev Distribute fee tokens from a wallet within the scope of gif to some address.
/// Separate push function for component service.
function pushFeeToken(
address to,
Amount amount
)
external
restricted()
onlyService()
{
_pushToken(to, amount);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {UFixed, UFixedLib} from "./UFixed.sol";
/// @dev Targets: 100 trillion (worlds GDP) with 6 decimal places
/// 3 trillion USD (total crypto market cap) with 12 decimal places.
/// 2023 100 trillion USD => 100e12 * 1e6 = 1e20
/// 2024 2 trillion crypto market cap => 2e12 * 1e18 = 2e30
type Amount is uint96;
using {
addAmount as +,
subAmount as -,
eqAmount as ==,
nqAmount as !=,
ltAmount as <,
ltAmount as <=,
gtAmount as >,
gteAmount as >=,
AmountLib.add,
AmountLib.eq,
AmountLib.eqz,
AmountLib.gtz,
AmountLib.gt,
AmountLib.gte,
AmountLib.multiplyWith,
AmountLib.toInt,
AmountLib.toUFixed
} for Amount global;
function addAmount(Amount a, Amount b) pure returns (Amount) {
return AmountLib.add(a, b);
}
function subAmount(Amount a, Amount b) pure returns (Amount) {
return AmountLib.sub(a, b);
}
function eqAmount(Amount a, Amount b) pure returns (bool) {
return AmountLib.eq(a, b);
}
function nqAmount(Amount a, Amount b) pure returns (bool) {
return !AmountLib.eq(a, b);
}
function ltAmount(Amount a, Amount b) pure returns (bool) {
return AmountLib.lt(a, b);
}
function lteAmount(Amount a, Amount b) pure returns (bool) {
return AmountLib.lte(a, b);
}
function gtAmount(Amount a, Amount b) pure returns (bool) {
return AmountLib.gt(a, b);
}
function gteAmount(Amount a, Amount b) pure returns (bool) {
return AmountLib.gte(a, b);
}
library AmountLib {
error ErrorAmountLibValueTooBig(uint256 amount);
function zero() public pure returns (Amount) {
return Amount.wrap(0);
}
function max() public pure returns (Amount) {
return Amount.wrap(_max());
}
/// @dev converts the uint amount into Amount
/// function reverts if value is exceeding max Amount value
function toAmount(uint256 amount) public pure returns (Amount) {
if(amount > _max()) {
revert ErrorAmountLibValueTooBig(amount);
}
return Amount.wrap(uint96(amount));
}
/// @dev return true if amount equals 0
function eqz(Amount amount) public pure returns (bool) {
return Amount.unwrap(amount) == 0;
}
/// @dev return true if amount1 equals amount2
function eq(Amount amount1, Amount amount2) public pure returns (bool) {
return Amount.unwrap(amount1) == Amount.unwrap(amount2);
}
/// @dev return true if amount a1 is less than a2
function lt(Amount a1, Amount a2) public pure returns (bool) {
return Amount.unwrap(a1) < Amount.unwrap(a2);
}
/// @dev return true if amount a1 is less or equal than a2
function lte(Amount a1, Amount a2) public pure returns (bool) {
return Amount.unwrap(a1) <= Amount.unwrap(a2);
}
/// @dev return true if amount a1 is greater than a2
function gt(Amount a1, Amount a2) public pure returns (bool) {
return Amount.unwrap(a1) > Amount.unwrap(a2);
}
/// @dev return true if amount a1 is greater or equal than a2
function gte(Amount a1, Amount a2) public pure returns (bool) {
return Amount.unwrap(a1) >= Amount.unwrap(a2);
}
/// @dev return minimum of a1 and a2.
function min(Amount a1, Amount a2) public pure returns (Amount) {
if (Amount.unwrap(a1) < Amount.unwrap(a2)) {
return a1;
}
return a2;
}
/// @dev return true if amount is larger than 0
function gtz(Amount amount) public pure returns (bool) {
return Amount.unwrap(amount) > 0;
}
function add(Amount a1, Amount a2) public pure returns (Amount) {
return Amount.wrap(Amount.unwrap(a1) + Amount.unwrap(a2));
}
function sub(Amount a1, Amount a2) public pure returns (Amount) {
return Amount.wrap(Amount.unwrap(a1) - Amount.unwrap(a2));
}
function toInt(Amount amount) public pure returns (uint256) {
return uint256(uint96(Amount.unwrap(amount)));
}
function toUFixed(Amount amount) public pure returns (UFixed) {
return UFixedLib.toUFixed(Amount.unwrap(amount));
}
function multiplyWith(Amount amount, UFixed factor) public pure returns (Amount) {
return toAmount((factor * UFixedLib.toUFixed(Amount.unwrap(amount))).toInt());
}
function _max() internal pure returns (uint96) {
// IMPORTANT: type nees to match with actual definition for Amount
return type(uint96).max;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
/// @dev Target: Cover 10 years with 1 ms block times.
/// Typical block times are a few seconds.
type Blocknumber is uint40;
using {
gtBlocknumber as >,
gteBlocknumber as >=,
ltBlocknumber as <,
lteBlocknumber as <=,
eqBlocknumber as ==,
neBlocknumber as !=,
BlocknumberLib.toInt,
BlocknumberLib.eq,
BlocknumberLib.ne,
BlocknumberLib.eqz,
BlocknumberLib.gtz,
BlocknumberLib.gt,
BlocknumberLib.gte,
BlocknumberLib.lt,
BlocknumberLib.lte
} for Blocknumber global;
/// @dev return true if Blocknumber a is greater than Blocknumber b
function gtBlocknumber(Blocknumber a, Blocknumber b) pure returns (bool) {
return Blocknumber.unwrap(a) > Blocknumber.unwrap(b);
}
/// @dev return true if Blocknumber a is greater than or equal to Blocknumber b
function gteBlocknumber(Blocknumber a, Blocknumber b) pure returns (bool) {
return Blocknumber.unwrap(a) >= Blocknumber.unwrap(b);
}
/// @dev return true if Blocknumber a is less than Blocknumber b
function ltBlocknumber(Blocknumber a, Blocknumber b) pure returns (bool) {
return Blocknumber.unwrap(a) < Blocknumber.unwrap(b);
}
/// @dev return true if Blocknumber a is less than or equal to Blocknumber b
function lteBlocknumber(Blocknumber a, Blocknumber b) pure returns (bool) {
return Blocknumber.unwrap(a) <= Blocknumber.unwrap(b);
}
/// @dev return true if Blocknumber a is equal to Blocknumber b
function eqBlocknumber(Blocknumber a, Blocknumber b) pure returns (bool) {
return Blocknumber.unwrap(a) == Blocknumber.unwrap(b);
}
/// @dev return true if Blocknumber a is not equal to Blocknumber b
function neBlocknumber(Blocknumber a, Blocknumber b) pure returns (bool) {
return Blocknumber.unwrap(a) != Blocknumber.unwrap(b);
}
library BlocknumberLib {
function zero() public pure returns (Blocknumber) {
return Blocknumber.wrap(0);
}
function max() public pure returns (Blocknumber) {
return Blocknumber.wrap(type(uint40).max);
}
function current() public view returns (Blocknumber) {
return Blocknumber.wrap(uint40(block.number));
}
function toBlocknumber(uint256 blocknum) public pure returns (Blocknumber) {
return Blocknumber.wrap(uint32(blocknum));
}
/// @dev return true iff blocknumber is 0
function eqz(Blocknumber blocknumber) public pure returns (bool) {
return Blocknumber.unwrap(blocknumber) == 0;
}
/// @dev return true iff blocknumber is > 0
function gtz(Blocknumber blocknumber) public pure returns (bool) {
return Blocknumber.unwrap(blocknumber) > 0;
}
/// @dev return true if Blocknumber a is greater than Blocknumber b
function gt(
Blocknumber a,
Blocknumber b
) public pure returns (bool isAfter) {
return gtBlocknumber(a, b);
}
/// @dev return true if Blocknumber a is greater than or equal to Blocknumber b
function gte(
Blocknumber a,
Blocknumber b
) public pure returns (bool isAfterOrSame) {
return gteBlocknumber(a, b);
}
/// @dev return true if Blocknumber a is less than Blocknumber b
function lt(
Blocknumber a,
Blocknumber b
) public pure returns (bool isBefore) {
return ltBlocknumber(a, b);
}
/// @dev return true if Blocknumber a is less than or equal to Blocknumber b
function lte(
Blocknumber a,
Blocknumber b
) public pure returns (bool isBeforeOrSame) {
return lteBlocknumber(a, b);
}
/// @dev return true if Blocknumber a is equal to Blocknumber b
function eq(
Blocknumber a,
Blocknumber b
) public pure returns (bool isSame) {
return eqBlocknumber(a, b);
}
/// @dev return true if Blocknumber a is not equal to Blocknumber b
function ne(
Blocknumber a,
Blocknumber b
) public pure returns (bool isDifferent) {
return neBlocknumber(a, b);
}
/// @dev converts the Blocknumber to a uint256
function toInt(Blocknumber blocknumber) public pure returns (uint256) {
return uint256(uint32(Blocknumber.unwrap(blocknumber)));
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {NftId} from "./NftId.sol";
/// @dev Target: Cover chain IDs up to 26 decimal places.
/// Current longest chain ID seems to be DCHAIN Testnet: 2713017997578000 with 16 decimal places
type ChainId is uint96;
using {
eqChainId as ==,
neChainId as !=,
ChainIdLib.toInt,
ChainIdLib.eqz,
ChainIdLib.gtz
} for ChainId global;
/// @dev return true if ChainId a is equal to ChainId b
function eqChainId(ChainId a, ChainId b) pure returns (bool) {
return ChainId.unwrap(a) == ChainId.unwrap(b);
}
/// @dev return true if ChainId a is not equal to ChainId b
function neChainId(ChainId a, ChainId b) pure returns (bool) {
return ChainId.unwrap(a) != ChainId.unwrap(b);
}
library ChainIdLib {
error ErrorChainIdLibValueTooBig(uint256 chainId);
function zero() public pure returns (ChainId) {
return ChainId.wrap(0);
}
function max() public pure returns (ChainId) {
return ChainId.wrap(_max());
}
function current() public view returns (ChainId) {
return toChainId(block.chainid);
}
/// @dev return true iff chainId is 0
function eqz(ChainId chainId) public pure returns (bool) {
return ChainId.unwrap(chainId) == 0;
}
/// @dev return true iff chainId is > 0
function gtz(ChainId chainId) public pure returns (bool) {
return ChainId.unwrap(chainId) > 0;
}
/// @dev converts the uint into ChainId
/// function reverts if value is exceeding max ChainId value
function toChainId(uint256 chainId) public pure returns (ChainId) {
if(chainId > _max()) {
revert ErrorChainIdLibValueTooBig(chainId);
}
return ChainId.wrap(uint96(chainId));
}
/// @dev returns true iff NFT ID is from the current chain.
function isCurrentChain(NftId nftId) public view returns (bool) {
return _fromNftId(nftId) == block.chainid;
}
function fromNftId(NftId nftId) public pure returns (ChainId) {
return toChainId(_fromNftId(nftId));
}
/// @dev converts the ChainId to a uint256
function toInt(ChainId chainId) public pure returns (uint256) {
return uint256(uint96(ChainId.unwrap(chainId)));
}
function _fromNftId(NftId nftId) internal pure returns (uint256 chainIdInt) {
uint256 nftIdInt = nftId.toInt();
uint256 chainIdDigits = nftIdInt % 100; // Extract the last two digits
chainIdInt = nftIdInt % 10**(chainIdDigits + 2) / 100; // Extract the chainId
}
function _max() internal pure returns (uint96) {
// IMPORTANT: type nees to match with actual definition for Amount
return type(uint96).max;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
// uint16 allows for 65'535 claims per policy
type ClaimId is uint16;
import {CLAIM} from "./ObjectType.sol";
import {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {NftId} from "./NftId.sol";
// type bindings
using {
eqClaimId as ==,
neClaimId as !=,
ClaimIdLib.eq,
ClaimIdLib.eqz,
ClaimIdLib.gtz,
ClaimIdLib.toInt,
ClaimIdLib.toKey32
} for ClaimId global;
// pure free functions for operators
function eqClaimId(ClaimId a, ClaimId b) pure returns (bool isSame) {
return ClaimIdLib.eq(a, b);
}
function neClaimId(ClaimId a, ClaimId b) pure returns (bool isDifferent) {
return ClaimId.unwrap(a) != ClaimId.unwrap(b);
}
// library functions that operate on user defined type
library ClaimIdLib {
/// @dev claim id min value (0), use only for non-initialized values
function zero() public pure returns (ClaimId) {
return ClaimId.wrap(0);
}
/// @dev claim id max value (2**16-1), use only for non-initialized values
function max() public pure returns (ClaimId) {
return ClaimId.wrap(type(uint16).max);
}
/// @dev Converts an uint into a ClaimId.
function toClaimId(uint256 a) public pure returns (ClaimId) {
return ClaimId.wrap(uint16(a));
}
/// @dev Converts the ClaimId to a uint.
function toInt(ClaimId a) public pure returns (uint16) {
return uint16(ClaimId.unwrap(a));
}
/// @dev Converts the ClaimId and NftId to a Key32.
function toKey32(ClaimId claimId, NftId policyNftId) public pure returns (Key32) {
return Key32Lib.toKey32(CLAIM(), toKeyId(claimId, policyNftId));
}
/// @dev Converts the ClaimId and NftId to a Key32.
function toKeyId(ClaimId claimId, NftId policyNftId) public pure returns (KeyId) {
return KeyId.wrap(
bytes31(
bytes14(
uint112(
NftId.unwrap(policyNftId) << 16 + ClaimId.unwrap(claimId)))));
}
/// @dev Returns true if the value is non-zero (> 0).
function gtz(ClaimId a) public pure returns (bool) {
return ClaimId.unwrap(a) > 0;
}
function eq(ClaimId a, ClaimId b) public pure returns (bool) {
return ClaimId.unwrap(a) == ClaimId.unwrap(b);
}
/// @dev Returns true if the value is zero (== 0).
function eqz(ClaimId a) public pure returns (bool) {
return ClaimId.unwrap(a) == 0;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {NftId} from "./NftId.sol";
import {DISTRIBUTOR_TYPE} from "./ObjectType.sol";
type DistributorType is bytes8;
// type bindings
using {
eqDistributorType as ==,
neDistributorType as !=,
DistributorTypeLib.toKey32
} for DistributorType global;
// general pure free functions
// pure free functions for operators
function eqDistributorType(
DistributorType a,
DistributorType b
) pure returns (bool isSame) {
return DistributorType.unwrap(a) == DistributorType.unwrap(b);
}
function neDistributorType(
DistributorType a,
DistributorType b
) pure returns (bool isDifferent) {
return DistributorType.unwrap(a) != DistributorType.unwrap(b);
}
// library functions that operate on user defined type
library DistributorTypeLib {
function zero() public pure returns (DistributorType) {
return DistributorType.wrap(bytes8(0));
}
// @dev Converts a referral string into an id.
function toDistributorType(NftId distributionNftId, string memory name) public pure returns (DistributorType) {
return DistributorType.wrap(bytes8(keccak256(abi.encode(distributionNftId, name))));
}
/// @dev Returns the key32 value for the specified nft id and object type.
function toKey32(DistributorType id) public pure returns (Key32 key) {
return Key32Lib.toKey32(DISTRIBUTOR_TYPE(), toKeyId(id));
}
/// @dev Returns the key id value for the specified nft id
function toKeyId(DistributorType id) public pure returns (KeyId keyId) {
return KeyId.wrap(bytes31(DistributorType.unwrap(id)));
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Amount, AmountLib} from "./Amount.sol";
import {UFixed, UFixedLib} from "./UFixed.sol";
struct Fee {
// slot 0
UFixed fractionalFee;
Amount fixedFee;
}
library FeeLib {
/// @dev Return a zero fee struct (0, 0)
function zero() public pure returns (Fee memory fee) {
return Fee(UFixed.wrap(0), AmountLib.zero());
}
/// @dev Converts the uint256 to a fee struct.
function toFee(
UFixed fractionalFee,
uint256 fixedFee
) public pure returns (Fee memory fee) {
return Fee(fractionalFee, AmountLib.toAmount(fixedFee));
}
/// @dev Calculates fee and net amounts for the provided parameters
function calculateFee(
Fee memory fee,
Amount amount
)
public
pure
returns (
Amount feeAmount,
Amount netAmount
)
{
netAmount = amount;
if(gtz(fee)) {
UFixed fractionalAmount =
amount.toUFixed() * fee.fractionalFee;
feeAmount = AmountLib.toAmount(fractionalAmount.toInt()) + fee.fixedFee;
netAmount = netAmount - feeAmount;
}
}
/// @dev Return the percent fee struct (x%, 0)
function percentageFee(uint8 percent) public pure returns (Fee memory fee) {
return Fee(UFixedLib.toUFixed(percent, -2), AmountLib.zero());
}
// pure free functions for operators
function eq(Fee memory a, Fee memory b) public pure returns (bool isSame) {
return a.fixedFee == b.fixedFee && a.fractionalFee == b.fractionalFee;
}
function gtz(Fee memory fee) public pure returns (bool) {
return UFixed.unwrap(fee.fractionalFee) > 0 || fee.fixedFee.gtz();
}
function eqz(Fee memory fee) public pure returns (bool) {
return fee.fixedFee.eqz() && UFixed.unwrap(fee.fractionalFee) == 0;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {ObjectType} from "./ObjectType.sol";
type Key32 is bytes32;
type KeyId is bytes31;
// type bindings
using {
eqKey32 as ==,
neKey32 as !=,
Key32Lib.toKeyId,
Key32Lib.toObjectType
} for Key32 global;
// @dev Returns true iff keys are identical
function eqKey32(Key32 a, Key32 b) pure returns (bool isSame) {
return Key32.unwrap(a) == Key32.unwrap(b);
}
// @dev Returns true iff keys are different
function neKey32(Key32 a, Key32 b) pure returns (bool isDifferent) {
return Key32.unwrap(a) != Key32.unwrap(b);
}
library Key32Lib {
uint8 public constant TYPE_SHIFT = 31 * 8;
uint8 public constant ID_SHIFT = uint8(32 * 8 - TYPE_SHIFT);
bytes32 public constant TYPE_MASK = bytes32(bytes1(type(uint8).max)); // [32] byte in bytes32
bytes32 public constant ID_MASK = bytes32(~TYPE_MASK); // [0..31] bytes in bytes32
function toKey32(ObjectType objectType, KeyId id) public pure returns (Key32) {
uint256 uintObjectType = ObjectType.unwrap(objectType);
uint256 uintId = uint248(KeyId.unwrap(id));
uint256 uintKey = (uintObjectType << TYPE_SHIFT) + uintId;
return Key32.wrap(bytes32(uintKey));
}
function toObjectType(Key32 key) public pure returns (ObjectType objectType) {
bytes32 key32 = Key32.unwrap(key);
objectType = ObjectType.wrap(uint8(uint256(key32 & TYPE_MASK) >> TYPE_SHIFT));
}
function toKeyId(Key32 key) public pure returns (KeyId id) {
bytes32 key32 = Key32.unwrap(key);
id = KeyId.wrap(bytes31((key32 & ID_MASK) << ID_SHIFT));
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Key32} from "../type/Key32.sol";
// based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/EnumerableSet.sol
library LibKey32Set {
struct Set {
Key32[] keys;
mapping(Key32 key => uint256 index) at;
}
error ErrorKey32SetAlreadyAdded(Key32 key);
error ErrorKey32SetNotInSet(Key32 key);
function add(Set storage set, Key32 key) external {
if (set.at[key] > 0) {
revert ErrorKey32SetAlreadyAdded(key);
}
set.keys.push(key);
set.at[key] = set.keys.length;
}
function remove(Set storage set, Key32 key) external {
uint256 nftIdIndex = set.at[key];
if (nftIdIndex == 0) {
revert ErrorKey32SetNotInSet(key);
}
uint256 toDeleteIndex = nftIdIndex - 1;
uint256 lastIndex = set.keys.length - 1;
if (lastIndex != toDeleteIndex) {
Key32 lastId = set.keys[lastIndex];
set.keys[toDeleteIndex] = lastId;
set.at[lastId] = nftIdIndex; // Replace lastValue's index to valueIndex
}
set.keys.pop();
delete set.at[key];
}
function isEmpty(Set storage set) external view returns(bool empty) {
return set.keys.length == 0;
}
function contains(Set storage set, Key32 key) external view returns(bool inSet) {
return set.at[key] > 0;
}
function size(Set storage set) external view returns(uint256 length) {
return set.keys.length;
}
function getElementAt(Set storage set, uint256 index) external view returns(Key32 key) {
return set.keys[index];
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {ObjectType} from "./ObjectType.sol";
// uint96 allows for chain ids up to 13 digits
type NftId is uint96;
// type bindings
using {
eqNftId as ==,
neNftId as !=,
NftIdLib.toInt,
NftIdLib.gtz,
NftIdLib.eqz,
NftIdLib.eq,
NftIdLib.toKeyId,
NftIdLib.toKey32
} for NftId global;
// pure free functions for operators
function eqNftId(NftId a, NftId b) pure returns (bool isSame) {
return NftIdLib.eq(a, b);
}
function neNftId(NftId a, NftId b) pure returns (bool isDifferent) {
return NftIdLib.ne(a, b);
}
// library functions that operate on user defined type
library NftIdLib {
/// @dev the zero nft id
/// is never a valid nft id and implies a non-initialized value
function zero() public pure returns (NftId) {
return NftId.wrap(0);
}
/// @dev Converts the uint256 to a NftId.
function toNftId(uint256 id) public pure returns (NftId) {
return NftId.wrap(uint96(id));
}
/// @dev Converts the NftId to a uint256.
function toInt(NftId nftId) public pure returns (uint96) {
return uint96(NftId.unwrap(nftId));
}
/// @dev Returns true if the value is non-zero (> 0).
function gtz(NftId a) public pure returns (bool) {
return NftId.unwrap(a) > 0;
}
/// @dev Returns true if the value is zero (== 0).
function eqz(NftId a) public pure returns (bool) {
return NftId.unwrap(a) == 0;
}
/// @dev Returns true if the values are equal (==).
function eq(NftId a, NftId b) public pure returns (bool isSame) {
return NftId.unwrap(a) == NftId.unwrap(b);
}
/// @dev Returns true if the values are not equal (!=).
function ne(NftId a, NftId b) public pure returns (bool isSame) {
return NftId.unwrap(a) != NftId.unwrap(b);
}
/// @dev Returns the key32 value for the specified nft id and object type.
function toKey32(NftId id, ObjectType objectType) public pure returns (Key32 key) {
return Key32Lib.toKey32(objectType, toKeyId(id));
}
/// @dev Returns the key id value for the specified nft id
function toKeyId(NftId id) public pure returns (KeyId keyId) {
return KeyId.wrap(bytes31(uint248(NftId.unwrap(id))));
}
function toNftId(KeyId keyId) public pure returns (NftId nftId) {
uint248 keyIdInt = uint248(bytes31(KeyId.unwrap(keyId)));
assert(keyIdInt < type(uint96).max);
return NftId.wrap(uint96(keyIdInt));
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {NftId} from "../type/NftId.sol";
// based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/EnumerableSet.sol
library LibNftIdSet {
struct Set {
NftId[] ids;
mapping(NftId nftid => uint256 index) at;
}
error ErrorNftIdSetAlreadyAdded(NftId nftId);
error ErrorNftIdSetNotInSet(NftId nftId);
function add(Set storage set, NftId nftId) external {
if (set.at[nftId] > 0) {
revert ErrorNftIdSetAlreadyAdded(nftId);
}
set.ids.push(nftId);
set.at[nftId] = set.ids.length;
}
function remove(Set storage set, NftId nftId) external {
uint256 nftIdIndex = set.at[nftId];
if (nftIdIndex == 0) {
revert ErrorNftIdSetNotInSet(nftId);
}
uint256 toDeleteIndex = nftIdIndex - 1;
uint256 lastIndex = set.ids.length - 1;
if (lastIndex != toDeleteIndex) {
NftId lastId = set.ids[lastIndex];
set.ids[toDeleteIndex] = lastId;
set.at[lastId] = nftIdIndex; // Replace lastValue's index to valueIndex
}
set.ids.pop();
delete set.at[nftId];
}
function isEmpty(Set storage set) external view returns(bool empty) {
return set.ids.length == 0;
}
function contains(Set storage set, NftId nftId) external view returns(bool inSet) {
return set.at[nftId] > 0;
}
function size(Set storage set) external view returns(uint256 length) {
return set.ids.length;
}
function getElementAt(Set storage set, uint256 index) external view returns(NftId nftId) {
return set.ids[index];
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {StrLib} from "./String.sol";
import {VersionPart} from "./Version.sol";
type ObjectType is uint8;
// type bindings
using {
eqObjectType as ==,
neObjectType as !=,
ObjectTypeLib.toInt,
ObjectTypeLib.toName,
ObjectTypeLib.eqz,
ObjectTypeLib.eq,
ObjectTypeLib.gtz
} for ObjectType global;
//--- GIF object types/domains (rage: 1 - 99) -------------------------------//
function PROTOCOL() pure returns (ObjectType) {
return ObjectType.wrap(1);
}
function REGISTRY() pure returns (ObjectType) {
return ObjectType.wrap(2);
}
function STAKING() pure returns (ObjectType) {
return ObjectType.wrap(3);
}
function RELEASE() pure returns (ObjectType) {
return ObjectType.wrap(6);
}
function ROLE() pure returns (ObjectType) {
return ObjectType.wrap(7);
}
function SERVICE() pure returns (ObjectType) {
return ObjectType.wrap(8);
}
function INSTANCE() pure returns (ObjectType) {
return ObjectType.wrap(10);
}
/// @dev Generic component object type.
/// Component role id range is 11-19.
/// Stick to this range for new component object types.
function COMPONENT() pure returns (ObjectType) {
return ObjectType.wrap(11);
}
/// @dev Product object type.
/// IMPORTANT the actual value has an influence on the corresponding role id (RoleIdLib.sol).
/// Do not change this value without updating the corresponding role id calculation.
function PRODUCT() pure returns (ObjectType) {
return ObjectType.wrap(12);
}
function ORACLE() pure returns (ObjectType) {
return ObjectType.wrap(13);
}
function DISTRIBUTION() pure returns (ObjectType) {
return ObjectType.wrap(14);
}
function POOL() pure returns (ObjectType) {
return ObjectType.wrap(15);
}
/// @dev Application object type.
/// Range for NFT objects created thorugh components is 20-29.
function APPLICATION() pure returns (ObjectType) {
return ObjectType.wrap(20);
}
function POLICY() pure returns (ObjectType) {
return ObjectType.wrap(21);
}
function BUNDLE() pure returns (ObjectType) {
return ObjectType.wrap(22);
}
function DISTRIBUTOR() pure returns (ObjectType) {
return ObjectType.wrap(23);
}
/// @dev Stake object type.
/// NFT object type is 30
function STAKE() pure returns (ObjectType) {
return ObjectType.wrap(30);
}
/// @dev Staking target object type.
function TARGET() pure returns (ObjectType) {
return ObjectType.wrap(31);
}
/// @dev Accounting object type.
/// Range for non-NFT types created through components is 40+
function ACCOUNTING() pure returns (ObjectType) {
return ObjectType.wrap(40);
}
function FEE() pure returns (ObjectType) {
return ObjectType.wrap(41);
}
function PRICE() pure returns (ObjectType) {
return ObjectType.wrap(42);
}
function PREMIUM() pure returns (ObjectType) {
return ObjectType.wrap(43);
}
function RISK() pure returns (ObjectType) {
return ObjectType.wrap(44);
}
function CLAIM() pure returns (ObjectType) {
return ObjectType.wrap(45);
}
function PAYOUT() pure returns (ObjectType) {
return ObjectType.wrap(46);
}
function REQUEST() pure returns (ObjectType) {
return ObjectType.wrap(47);
}
function DISTRIBUTOR_TYPE() pure returns (ObjectType) {
return ObjectType.wrap(48);
}
function REFERRAL() pure returns (ObjectType) {
return ObjectType.wrap(49);
}
/// @dev Object type for GIF core target roles.
function CORE() pure returns (ObjectType) {
return ObjectType.wrap(97);
}
/// @dev Object type for target roles of contracts outside the GIF framework.
/// Example: Custom supporting contracts for a product component.
function CUSTOM() pure returns (ObjectType) {
return ObjectType.wrap(98);
}
/// @dev Object type that includes any other object type.
/// Note that eq()/'==' does not take this property into account.
function ALL() pure returns (ObjectType) {
return ObjectType.wrap(99);
}
// other pure free functions for operators
function eqObjectType(ObjectType a, ObjectType b) pure returns (bool isSame) {
return ObjectType.unwrap(a) == ObjectType.unwrap(b);
}
function neObjectType(ObjectType a, ObjectType b) pure returns (bool isSame) {
return ObjectType.unwrap(a) != ObjectType.unwrap(b);
}
// library functions that operate on user defined type
library ObjectTypeLib {
function zero() public pure returns (ObjectType) {
return ObjectType.wrap(0);
}
/// @dev Converts the uint256 into ObjectType.
function toObjectType(uint256 objectType) public pure returns (ObjectType) {
return ObjectType.wrap(uint8(objectType));
}
/// @dev Converts the NftId to a uint256.
function toInt(ObjectType objectType) public pure returns (uint96) {
return uint96(ObjectType.unwrap(objectType));
}
/// @dev Returns true if the value is non-zero (> 0).
function gtz(ObjectType a) public pure returns (bool) {
return ObjectType.unwrap(a) > 0;
}
/// @dev Returns true if the value is zero (== 0).
function eqz(ObjectType a) public pure returns (bool) {
return ObjectType.unwrap(a) == 0;
}
/// @dev Returns true if the values are equal (==).
function eq(ObjectType a, ObjectType b) public pure returns (bool isSame) {
return ObjectType.unwrap(a) == ObjectType.unwrap(b);
}
/// @dev Returns true if the values are not equal (!=).
function ne(ObjectType a, ObjectType b) public pure returns (bool isSame) {
return ObjectType.unwrap(a) != ObjectType.unwrap(b);
}
/// @dev Returns the type/domain name for the provided object type
function toName(ObjectType objectType) public pure returns (string memory name) {
if (objectType == REGISTRY()) {
return "Registry";
} else if (objectType == STAKING()) {
return "Staking";
} else if (objectType == RELEASE()) {
return "Release";
} else if (objectType == INSTANCE()) {
return "Instance";
} else if (objectType == COMPONENT()) {
return "Component";
} else if (objectType == PRODUCT()) {
return "Product";
} else if (objectType == ORACLE()) {
return "Oracle";
} else if (objectType == DISTRIBUTION()) {
return "Distribution";
} else if (objectType == POOL()) {
return "Pool";
} else if (objectType == APPLICATION()) {
return "Application";
} else if (objectType == POLICY()) {
return "Policy";
} else if (objectType == CLAIM()) {
return "Claim";
} else if (objectType == PRICE()) {
return "Price";
} else if (objectType == BUNDLE()) {
return "Bundle";
} else if (objectType == RISK()) {
return "Risk";
} else if (objectType == ACCOUNTING()) {
return "Accounting";
}
// fallback: ObjectType<obect-type-int>
return string(
abi.encodePacked(
"ObjectType",
StrLib.uintToString(
toInt(objectType))));
}
// TODO move to IService
function toVersionedName(
string memory name,
string memory suffix,
VersionPart release
)
external
pure
returns (string memory versionedName)
{
string memory versionName = "V0";
if (release.toInt() >= 10) {
versionName = "V";
}
versionedName = string(
abi.encodePacked(
name,
suffix,
versionName,
release.toString()));
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
// uint40 allows for 65'535 claims with 16'777'216 payouts each per policy
type PayoutId is uint40;
import {ClaimId} from "./ClaimId.sol";
import {PAYOUT} from "./ObjectType.sol";
import {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {NftId} from "./NftId.sol";
// type bindings
using {
eqPayoutId as ==,
nePayoutId as !=,
PayoutIdLib.eqz,
PayoutIdLib.gtz,
PayoutIdLib.toInt,
PayoutIdLib.toClaimId,
PayoutIdLib.toPayoutNo,
PayoutIdLib.toKey32
} for PayoutId global;
// pure free functions for operators
function eqPayoutId(PayoutId a, PayoutId b) pure returns (bool isSame) {
return PayoutId.unwrap(a) == PayoutId.unwrap(b);
}
function nePayoutId(PayoutId a, PayoutId b) pure returns (bool isDifferent) {
return PayoutId.unwrap(a) != PayoutId.unwrap(b);
}
// library functions that operate on user defined type
library PayoutIdLib {
/// @dev Converts the PayoutId to a uint.
function zero() public pure returns (PayoutId) {
return PayoutId.wrap(0);
}
/// @dev Converts an uint into a PayoutId.
function toPayoutId(ClaimId claimId, uint24 payoutNo) public pure returns (PayoutId) {
return PayoutId.wrap((uint40(ClaimId.unwrap(claimId)) << 24) + payoutNo);
}
function toClaimId(PayoutId payoutId) public pure returns (ClaimId) {
return ClaimId.wrap(uint16(PayoutId.unwrap(payoutId) >> 24));
}
function toPayoutNo(PayoutId payoutId) public pure returns (uint24) {
return uint24(PayoutId.unwrap(payoutId) & 16777215);
}
/// @dev Converts the PayoutId to a uint.
function toInt(PayoutId a) public pure returns (uint40) {
return PayoutId.unwrap(a);
}
/// @dev Returns true if the value is non-zero (> 0).
function gtz(PayoutId a) public pure returns (bool) {
return PayoutId.unwrap(a) > 0;
}
/// @dev Returns true if the value is zero (== 0).
function eqz(PayoutId a) public pure returns (bool) {
return PayoutId.unwrap(a) == 0;
}
/// @dev Converts the PayoutId and NftId to a Key32.
function toKey32(PayoutId payoutId, NftId policyNftId) public pure returns (Key32) {
return Key32Lib.toKey32(PAYOUT(), toKeyId(payoutId, policyNftId));
}
/// @dev Converts the PayoutId and NftId to a Key32.
function toKeyId(PayoutId payoutId, NftId policyNftId) public pure returns (KeyId) {
return KeyId.wrap(
bytes31(
bytes15(
uint120(
(NftId.unwrap(policyNftId) << 40) + PayoutId.unwrap(payoutId)))));
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {NftId} from "./NftId.sol";
import {REFERRAL} from "./ObjectType.sol";
type ReferralId is bytes8;
type ReferralStatus is uint8;
// type bindings
using {
eqReferralId as ==,
neReferralId as !=,
ReferralLib.eqz,
ReferralLib.toInt,
ReferralLib.toKey32
} for ReferralId global;
using {
eqReferralStatus as ==,
neReferralStatus as !=
} for ReferralStatus global;
// general pure free functions
// @dev Returns true iff a and b are identical
function eqReferralId(ReferralId a, ReferralId b) pure returns (bool isSame) {
return ReferralId.unwrap(a) == ReferralId.unwrap(b);
}
// @dev Returns true iff a and b are different
function neReferralId(ReferralId a, ReferralId b) pure returns (bool isDifferent) {
return ReferralId.unwrap(a) != ReferralId.unwrap(b);
}
// @dev Returns true iff a and b are identical
function eqReferralStatus(ReferralStatus a, ReferralStatus b) pure returns (bool isSame) {
return ReferralStatus.unwrap(a) == ReferralStatus.unwrap(b);
}
// @dev Returns true iff a and b are different
function neReferralStatus(ReferralStatus a, ReferralStatus b) pure returns (bool isDifferent) {
return ReferralStatus.unwrap(a) != ReferralStatus.unwrap(b);
}
function REFERRAL_OK() pure returns (ReferralStatus) {
return ReferralStatus.wrap(10);
}
function REFERRAL_ERROR_UNKNOWN() pure returns (ReferralStatus) {
return ReferralStatus.wrap(100);
}
function REFERRAL_ERROR_EXPIRED() pure returns (ReferralStatus) {
return ReferralStatus.wrap(110);
}
function REFERRAL_ERROR_EXHAUSTED() pure returns (ReferralStatus) {
return ReferralStatus.wrap(120);
}
library ReferralLib {
function zero() public pure returns (ReferralId) {
return ReferralId.wrap(bytes8(0));
}
// @dev Converts a referral string into an id.
function toReferralId(NftId distributionNftId, string memory referral) public pure returns (ReferralId) {
return ReferralId.wrap(bytes8(keccak256(abi.encode(distributionNftId, referral))));
}
function toReferralStatus(uint8 status) public pure returns (ReferralStatus) {
return ReferralStatus.wrap(status);
}
/// @dev Converts a referral id into a uint256.
function toInt(ReferralId referralId) public pure returns (uint256) {
return uint64(ReferralId.unwrap(referralId));
}
/// @dev Returns the key32 value for the specified nft id and object type.
function toKey32(ReferralId id) public pure returns (Key32 key) {
return Key32Lib.toKey32(REFERRAL(), toKeyId(id));
}
/// @dev Returns the key id value for the specified nft id
function toKeyId(ReferralId id) public pure returns (KeyId keyId) {
return KeyId.wrap(bytes31(ReferralId.unwrap(id)));
}
function eqz(ReferralId id) public pure returns (bool) {
return eqReferralId(id, zero());
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {REQUEST} from "./ObjectType.sol";
type RequestId is uint64;
// type bindings
using {
eqRequestId as ==,
neRequestId as !=,
RequestIdLib.eqz,
RequestIdLib.gtz,
RequestIdLib.toInt,
RequestIdLib.toKey32
} for RequestId global;
// general pure free functions
function eqRequestId(RequestId a, RequestId b) pure returns (bool isSame) {
return RequestIdLib.eq(a, b);
}
function neRequestId(RequestId a, RequestId b) pure returns (bool isSame) {
return RequestIdLib.ne(a, b);
}
library RequestIdLib {
// @dev zero element to refer to a non existing/initialized request
function zero() public pure returns (RequestId) {
return RequestId.wrap(0);
}
// @dev Converts an int id into a request id.
function toRequestId(uint256 id) public pure returns (RequestId) {
return RequestId.wrap(uint64(id));
}
// @dev Converts a request id back to an int value.
function toInt(RequestId requestId) public pure returns (uint256) {
return RequestId.unwrap(requestId);
}
// @dev Returns true iff request id a == 0
function eqz(RequestId a) public pure returns (bool) {
return RequestId.unwrap(a) == 0;
}
// @dev Returns true iff request id a > 0
function gtz(RequestId a) public pure returns (bool) {
return RequestId.unwrap(a) > 0;
}
// @dev Returns true iff risk ids a and b are identical
function eq(RequestId a, RequestId b) public pure returns (bool isSame) {
return RequestId.unwrap(a) == RequestId.unwrap(b);
}
// @dev Returns true iff risk ids a and b are different
function ne(RequestId a, RequestId b) public pure returns (bool isSame) {
return RequestId.unwrap(a) != RequestId.unwrap(b);
}
/// @dev Returns the key32 value for the specified nft id and object type.
function toKey32(RequestId id) public pure returns (Key32 key) {
return Key32Lib.toKey32(REQUEST(), toKeyId(id));
}
/// @dev Returns the key id value for the specified nft id
function toKeyId(RequestId id) public pure returns (KeyId keyId) {
return KeyId.wrap(bytes31(uint248(RequestId.unwrap(id))));
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {RequestId} from "../type/RequestId.sol";
// based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/EnumerableSet.sol
library LibRequestIdSet {
struct Set {
RequestId[] ids;
mapping(RequestId requestId => uint256 index) at;
}
error ErrorRequestIdSetAlreadyAdded(RequestId requestId);
error ErrorRequestIdSetNotInSet(RequestId requestId);
function add(Set storage set, RequestId requestId) external {
if (set.at[requestId] > 0) {
revert ErrorRequestIdSetAlreadyAdded(requestId);
}
set.ids.push(requestId);
set.at[requestId] = set.ids.length;
}
function remove(Set storage set, RequestId requestId) external {
uint256 requestIdIndex = set.at[requestId];
if (requestIdIndex == 0) {
revert ErrorRequestIdSetNotInSet(requestId);
}
uint256 toDeleteIndex = requestIdIndex - 1;
uint256 lastIndex = set.ids.length - 1;
if (lastIndex != toDeleteIndex) {
RequestId lastId = set.ids[lastIndex];
set.ids[toDeleteIndex] = lastId;
set.at[lastId] = requestIdIndex; // Replace lastValue's index to valueIndex
}
set.ids.pop();
delete set.at[requestId];
}
function isEmpty(Set storage set) external view returns(bool empty) {
return set.ids.length == 0;
}
function contains(Set storage set, RequestId requestId) external view returns(bool inSet) {
return set.at[requestId] > 0;
}
function size(Set storage set) external view returns(uint256 length) {
return set.ids.length;
}
function getElementAt(Set storage set, uint256 index) external view returns(RequestId requestId) {
return set.ids[index];
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {NftId} from "./NftId.sol";
import {RISK} from "./ObjectType.sol";
type RiskId is bytes8;
// type bindings
using {
eqRiskId as ==,
neRiskId as !=,
RiskIdLib.eq,
RiskIdLib.eqz,
RiskIdLib.gtz,
RiskIdLib.toInt,
RiskIdLib.toKeyId,
RiskIdLib.toKey32
} for RiskId global;
// general pure free functions
// @dev Returns true iff risk ids a and b are identical
function eqRiskId(RiskId a, RiskId b) pure returns (bool isSame) {
return RiskId.unwrap(a) == RiskId.unwrap(b);
}
// @dev Returns true iff risk ids a and b are different
function neRiskId(RiskId a, RiskId b) pure returns (bool isDifferent) {
return RiskId.unwrap(a) != RiskId.unwrap(b);
}
library RiskIdLib {
function zero() public pure returns (RiskId) {
return RiskId.wrap(bytes8(0));
}
// @dev Converts a risk id into a uint256.
function toInt(RiskId riskId) public pure returns (uint256) {
return uint64(RiskId.unwrap(riskId));
}
// @dev Converts a risk id string with a product NftId into a risk id.
function toRiskId(NftId productNftId, bytes32 risk) public pure returns (RiskId) {
return RiskId.wrap(bytes8(keccak256(abi.encode(productNftId, risk))));
}
/// @dev Returns the key32 value for the specified risk id.
function toKey32(RiskId riskId) public pure returns (Key32 key) {
return Key32Lib.toKey32(RISK(), toKeyId(riskId));
}
/// @dev Returns the key id value for the specified nft id
function toKeyId(RiskId id) public pure returns (KeyId keyId) {
return KeyId.wrap(bytes31(RiskId.unwrap(id)));
}
function toRiskId(KeyId keyId) public pure returns (RiskId riskId) {
riskId = RiskId.wrap(bytes8(KeyId.unwrap(keyId)));
assert(toInt(riskId) < 2**64);
}
function eq(RiskId a, RiskId b) public pure returns (bool isSame) {
return eqRiskId(a, b);
}
function eqz(RiskId a) public pure returns (bool isZero) {
return eqRiskId(a, zero());
}
function gtz(RiskId a) public pure returns (bool isZero) {
return uint64(RiskId.unwrap(a)) > 0;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {ObjectType, ROLE} from "./ObjectType.sol";
import {VersionPart, VersionPartLib} from "./Version.sol";
type RoleId is uint64;
// type bindings
using {
eqRoleId as ==,
neRoleId as !=,
RoleIdLib.toInt,
RoleIdLib.isServiceRole,
RoleIdLib.eqz,
RoleIdLib.gtz
} for RoleId global;
// general pure free functions
// @dev Returns true iff role ids a and b are identical
function eqRoleId(RoleId a, RoleId b) pure returns (bool isSame) {
return RoleId.unwrap(a) == RoleId.unwrap(b);
}
// @dev Returns true iff role ids a and b are different
function neRoleId(RoleId a, RoleId b) pure returns (bool isDifferent) {
return RoleId.unwrap(a) != RoleId.unwrap(b);
}
//--- OpenZeppelin provided roles -------------------------------------------//
/// @dev Role ID needs to match with oz AccessManager.ADMIN_ROLE
function ADMIN_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(type(uint64).min); }
/// @dev Role ID needs to match with oz AccessManager.PUBLIC_ROLE
function PUBLIC_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(type(uint64).max); }
//--- GIF roles (range: 1-99) ----------------------------------------------//
/// @dev cental role for gif release management.
/// this role is necessary to call ReleaseManager.createNextRelease/activateNextRelease
/// the actual deployment of a release requires the GIF_MANAGER_ROLE.
/// GIF_ADMIN_ROLE is the admin of the GIF_MANAGER_ROLE.
/// only a single holder may hold this role at any time
function GIF_ADMIN_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(1); }
/// @dev role for token whith/blacklisting, deploying and registering the services for a new major release
/// registering services for a new major release is only possible after a new initial release has been created by the GIF_ADMIN_ROLE
/// token white/blacklisting is possible for any active release
function GIF_MANAGER_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(2); }
/// @dev role for registering remote staking targets and reporting remote total value locked amounts.
function GIF_REMOTE_MANAGER_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(3); }
// TODO check if/where this is really needed
/// @dev role assigned to release registry, release specfic to lock/unlock a release
function RELEASE_REGISTRY_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(4); }
/// @dev role assigned to every instance owner
function INSTANCE_OWNER_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(5); }
// TODO upate role id ranges
//--- GIF core contract roles (range: 200 - 9'900) --------------------------//
// created and assigned during initial deployment for registry and staking
// granting for instances and components in instance service
// object type * 100 + 0, examples:
// - registry contract role: 200
// - staking contract role: 300
// - instance contract role: 1000
//--- GIF service roles (range 201 - 9'9xx) ---------------------------------//
// created and assigned by release manager contract
// object type * 100 + 1/major version, examples:
// - registry service role (any version): 299
// - registry service role (version 3): 203
// - registry service role (any version): 399
// - staking service role: (version 3): 303
// - application service role (version 3): 2003
//--- GIF component contract roles (range 12'001 - 19'099) ------------------//
// the min value of 12'001 is based on the following calculation:
// object type * 1000 + 1 where the lowest object type is 12 (product)
// assigned at component registration time
// object type * 1000 + instane specific component counter
// on any instance a maximum number of 999 components may be deployed
// examples:
// - 1st pool on instance: 15001
// - 1st distribution on instance: 14002
// - 1st product on instance: 12003
// - 2nd pool on instance: 15004
// - 2nd distribution on instance: 14005
// - 2nd product on instance: 12006
//--- Custom roles (range >= 1'000'000) -------------------------------------//
function CUSTOM_ROLE_MIN() pure returns (RoleId) { return RoleIdLib.toRoleId(1000000); }
library RoleIdLib {
error ErrorRoleIdTooBig(uint256 roleId);
// constant values need to match with AccessAdminLib.SERVICE_ROLE_*
uint64 public constant SERVICE_ROLE_MIN = 1000;
uint64 public constant SERVICE_ROLE_MAX = 99099; // 99 (max object type) * 1000 + 99
uint64 public constant SERVICE_ROLE_FACTOR = 1000;
/// @dev Converts the RoleId to a uint.
function zero() public pure returns (RoleId) {
return RoleId.wrap(0);
}
/// @dev Converts an uint into a role id.
function toRoleId(uint256 a) public pure returns (RoleId) {
if (a > type(uint64).max) {
revert ErrorRoleIdTooBig(a);
}
return RoleId.wrap(uint64(a));
}
function isServiceRole(RoleId roleId)
public
pure
returns (bool)
{
uint256 roleIdInt = RoleId.unwrap(roleId);
return roleIdInt >= SERVICE_ROLE_MIN && roleIdInt <= SERVICE_ROLE_MAX;
}
function toGenericServiceRoleId(
ObjectType objectType
)
public
pure
returns (RoleId)
{
return toServiceRoleId(
objectType,
VersionPartLib.releaseMax());
}
function toServiceRoleId(
ObjectType serviceDomain,
VersionPart release
)
public
pure
returns (RoleId serviceRoleId)
{
uint256 serviceRoleIdInt =
SERVICE_ROLE_MIN
+ SERVICE_ROLE_FACTOR * (serviceDomain.toInt() - 1)
+ release.toInt();
return toRoleId(serviceRoleIdInt);
}
/// @dev Converts the role id to a uint.
function toInt(RoleId a) public pure returns (uint64) {
return uint64(RoleId.unwrap(a));
}
/// @dev Returns true if the value is non-zero (> 0).
function gtz(RoleId a) public pure returns (bool) {
return RoleId.unwrap(a) > 0;
}
/// @dev Returns true if the value is zero (== 0).
function eqz(RoleId a) public pure returns (bool) {
return RoleId.unwrap(a) == 0;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
/// @dev Target: Cover durations of 1000 years.
type Seconds is uint40;
using {
SecondsEq as ==,
SecondsLt as <,
SecondsGt as >,
SecondsAdd as +,
SecondsLib.eqz,
SecondsLib.gtz,
SecondsLib.eq,
SecondsLib.gt,
SecondsLib.lt,
SecondsLib.toInt,
SecondsLib.add
} for Seconds global;
function SecondsEq(Seconds duration1, Seconds duration2) pure returns (bool) {
return SecondsLib.eq(duration1, duration2);
}
function SecondsLt(Seconds duration1, Seconds duration2) pure returns (bool) {
return SecondsLib.lt(duration1, duration2);
}
function SecondsGt(Seconds duration1, Seconds duration2) pure returns (bool) {
return SecondsLib.gt(duration1, duration2);
}
function SecondsAdd(Seconds duration1, Seconds duration2) pure returns (Seconds) {
return SecondsLib.add(duration1, duration2);
}
library SecondsLib {
error ErrorSecondsLibDurationTooBig(uint256 duration);
function zero() public pure returns (Seconds) {
return Seconds.wrap(0);
}
function max() public pure returns (Seconds) {
return Seconds.wrap(_max());
}
function fromHours(uint32 numberOfHours) public pure returns (Seconds duration) {
return Seconds.wrap(numberOfHours * 3600);
}
function oneDay() public pure returns (Seconds duration) {
return Seconds.wrap(24 * 3600);
}
function fromDays(uint32 numberOfDays) public pure returns (Seconds duration) {
return Seconds.wrap(numberOfDays * 24 * 3600);
}
function oneYear() public pure returns (Seconds duration) {
return Seconds.wrap(365 * 24 * 3600);
}
/// @dev converts the uint duration into Seconds
/// function reverts if duration is exceeding max Seconds value
function toSeconds(uint256 duration) public pure returns (Seconds) {
// if(duration > type(uint40).max) {
if(duration > _max()) {
revert ErrorSecondsLibDurationTooBig(duration);
}
return Seconds.wrap(uint40(duration));
}
/// @dev return true if duration equals 0
function eqz(Seconds duration) public pure returns (bool) {
return Seconds.unwrap(duration) == 0;
}
/// @dev return true if duration is larger than 0
function gtz(Seconds duration) public pure returns (bool) {
return Seconds.unwrap(duration) > 0;
}
/// @dev return true iff duration1 and duration2 are the same
function eq(Seconds duration1, Seconds duration2) public pure returns (bool) {
return Seconds.unwrap(duration1) == Seconds.unwrap(duration2);
}
/// @dev return true if duration1 is larger than duration2
function gt(Seconds duration1, Seconds duration2) public pure returns (bool) {
return Seconds.unwrap(duration1) > Seconds.unwrap(duration2);
}
/// @dev return true if duration1 is smaller than duration2
function lt(Seconds duration1, Seconds duration2) public pure returns (bool) {
return Seconds.unwrap(duration1) < Seconds.unwrap(duration2);
}
/// @dev returns the smaller of the duration
function min(Seconds duration1, Seconds duration2) public pure returns (Seconds) {
if (Seconds.unwrap(duration1) < Seconds.unwrap(duration2)) {
return duration1;
}
return duration2;
}
/// @dev return add duration1 and duration2
function add(Seconds duration1, Seconds duration2) public pure returns (Seconds) {
return Seconds.wrap(Seconds.unwrap(duration1) + Seconds.unwrap(duration2));
}
function toInt(Seconds duration) public pure returns (uint256) {
return uint256(uint40(Seconds.unwrap(duration)));
}
function _max() internal pure returns (uint40) {
// IMPORTANT: type nees to match with actual definition for Seconds
return type(uint40).max;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
type Selector is bytes4;
// type bindings
using {
eqSelector as ==,
neSelector as !=,
SelectorLib.toBytes4,
SelectorLib.toString,
SelectorLib.eqz
} for Selector global;
// pure free functions for operators
function eqSelector(Selector s1, Selector s2) pure returns (bool isSame) {
return SelectorLib.eq(s1, s2);
}
function neSelector(Selector s1, Selector s2) pure returns (bool isDifferent) {
return SelectorLib.ne(s1, s2);
}
// library functions that operate on user defined type
library SelectorLib {
function zero() public pure returns (Selector) {
return Selector.wrap("");
}
function eqz(Selector s) public pure returns (bool) {
return Selector.unwrap(s) == "";
}
function eq(Selector s1, Selector s2) public pure returns (bool isSame) {
return Selector.unwrap(s1) == Selector.unwrap(s2);
}
function ne(Selector s1, Selector s2) public pure returns (bool isDifferent) {
return Selector.unwrap(s1) != Selector.unwrap(s2);
}
function toSelector(bytes4 selector) public pure returns (Selector) {
return Selector.wrap(selector);
}
function toBytes4(Selector s) public pure returns (bytes4) {
return Selector.unwrap(s);
}
function toString(Selector s) public pure returns (string memory) {
return string(abi.encode(Selector.unwrap(s)));
}
}
// selector specific set library
// based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/EnumerableSet.sol
library SelectorSetLib {
struct Set {
Selector[] selectors;
mapping(Selector selector => uint256 index) at;
}
function add(Set storage set, Selector selector) external {
// selector already in set
if (set.at[selector] > 0) { return; }
set.selectors.push(selector);
set.at[selector] = set.selectors.length;
}
function remove(Set storage set, Selector selector) external {
uint256 selectorIndex = set.at[selector];
// selector not in set
if (selectorIndex == 0) {return; }
uint256 toDeleteIndex = selectorIndex - 1;
uint256 lastIndex = set.selectors.length - 1;
if (lastIndex != toDeleteIndex) {
Selector lastSelector = set.selectors[lastIndex];
set.selectors[toDeleteIndex] = lastSelector;
set.at[lastSelector] = selectorIndex; // Replace lastValue's index to valueIndex
}
set.selectors.pop();
delete set.at[selector];
}
function isEmpty(Set storage set) external view returns(bool empty) {
return set.selectors.length == 0;
}
function contains(Set storage set, Selector selector) external view returns(bool inSet) {
return set.at[selector] > 0;
}
function size(Set storage set) external view returns(uint256 length) {
return set.selectors.length;
}
function at(Set storage set, uint256 index) external view returns(Selector selector) {
return set.selectors[index];
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
// uint96 allows for chain ids up to 13 digits
type StateId is uint8;
// type bindings
using {
eqStateId as ==,
neStateId as !=,
StateIdLib.eqz,
StateIdLib.eq,
StateIdLib.gtz,
StateIdLib.toInt
} for StateId global;
// general pure free functions
function INITIAL() pure returns (StateId) {
return toStateId(1);
}
function SCHEDULED() pure returns (StateId) {
return toStateId(2);
}
function DEPLOYING() pure returns (StateId) {
return toStateId(3);
}
function DEPLOYED() pure returns (StateId) {
return toStateId(4);
}
function ACTIVE() pure returns (StateId) {
return toStateId(5);
}
function SKIPPED() pure returns (StateId) {
return toStateId(6);
}
function APPLIED() pure returns (StateId) {
return toStateId(10);
}
function REVOKED() pure returns (StateId) {
return toStateId(20);
}
function DECLINED() pure returns (StateId) {
return toStateId(30);
}
function COLLATERALIZED() pure returns (StateId) {
return toStateId(40);
}
function SUBMITTED() pure returns (StateId) {
return toStateId(50);
}
function CONFIRMED() pure returns (StateId) {
return toStateId(51);
}
function EXPECTED() pure returns (StateId) {
return toStateId(60);
}
function FULFILLED() pure returns (StateId) {
return toStateId(70);
}
function FAILED() pure returns (StateId) {
return toStateId(7);
}
function CANCELLED() pure returns (StateId) {
return toStateId(72);
}
function PAUSED() pure returns (StateId) {
return toStateId(110);
}
function CLOSED() pure returns (StateId) {
return toStateId(200);
}
function ARCHIVED() pure returns (StateId) {
return toStateId(210);
}
function PAID() pure returns (StateId) {
return toStateId(220);
}
function KEEP_STATE() pure returns (StateId) {
return toStateId(type(uint8).max);
}
/// @dev Converts the uint8 to a StateId.
function toStateId(uint256 id) pure returns (StateId) {
return StateId.wrap(uint8(id));
}
// TODO move to StateIdLib and rename to zero()
/// @dev Return the StateId zero (0)
function zeroStateId() pure returns (StateId) {
return StateId.wrap(0);
}
// pure free functions for operators
function eqStateId(StateId a, StateId b) pure returns (bool isSame) {
return StateId.unwrap(a) == StateId.unwrap(b);
}
function neStateId(StateId a, StateId b) pure returns (bool isDifferent) {
return StateId.unwrap(a) != StateId.unwrap(b);
}
// library functions that operate on user defined type
library StateIdLib {
function zero() public pure returns (StateId) {
return StateId.wrap(0);
}
/// @dev Converts the NftId to a uint256.
function toInt(StateId stateId) public pure returns (uint96) {
return uint96(StateId.unwrap(stateId));
}
/// @dev Returns true if the value is non-zero (> 0).
function gtz(StateId a) public pure returns (bool) {
return StateId.unwrap(a) > 0;
}
/// @dev Returns true if the value is zero (== 0).
function eqz(StateId a) public pure returns (bool) {
return StateId.unwrap(a) == 0;
}
/// @dev Returns true if the values are equal (==).
function eq(StateId a, StateId b) public pure returns (bool isSame) {
return eqStateId(a, b);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {ShortString, ShortStrings} from "@openzeppelin/contracts/utils/ShortStrings.sol";
type Str is bytes32;
using {
StrEq as ==,
StrNe as !=,
StrLib.toString,
StrLib.length
} for Str global;
// pure free function needed for the operator overloading
function StrEq(Str s1, Str s2) pure returns (bool) {
return StrLib.eq(s1, s2);
}
// pure free function needed for the operator overloading
function StrNe(Str s1, Str s2) pure returns (bool) {
return StrLib.ne(s1, s2);
}
library StrLib {
/// @dev converts the provided string into a short string.
/// uses ShortStrings.toShortString
function toStr(string memory str) public pure returns (Str) {
return Str.wrap(ShortString.unwrap(ShortStrings.toShortString(str)));
}
/// @dev return true iff s1 equals s2
function eq(Str s1, Str s2) public pure returns (bool) {
return Str.unwrap(s1) == Str.unwrap(s2);
}
/// @dev return true iff s1 differs from s2
function ne(Str s1, Str s2) public pure returns (bool) {
return Str.unwrap(s1) != Str.unwrap(s2);
}
/// @dev return true iff s1 equals from s2
function eq(string memory s1, string memory s2) public pure returns (bool) {
return keccak256(bytes(s1)) == keccak256(bytes(s2));
}
/// @dev return true iff s1 differs s2
function ne(string memory s1, string memory s2) public pure returns (bool) {
return !eq(s1, s2);
}
/// @dev converts the provided short string into a string.
/// uses ShortStrings.toString
function toString(Str str) public pure returns (string memory) {
return ShortStrings.toString(ShortString.wrap(Str.unwrap(str)));
}
/// @dev converts the provided short string into a string.
/// uses ShortStrings.byteLength
function length(Str str) public pure returns (uint256 byteLength) {
return ShortStrings.byteLength(ShortString.wrap(Str.unwrap(str)));
}
/// @dev Returns the provied int as a string
function uintToString(uint256 value) public pure returns (string memory name) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits = 0;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
uint256 index = digits - 1;
temp = value;
while (temp != 0) {
buffer[index] = bytes1(uint8(48 + temp % 10));
temp /= 10;
if (index > 0) {
index--;
}
}
return string(buffer);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Seconds} from "./Seconds.sol";
/// @dev Target: Cover 10 years with 1 ms block time resolution.
/// Typical block time resolution is 1s.
type Timestamp is uint40;
using {
gtTimestamp as >,
gteTimestamp as >=,
ltTimestamp as <,
lteTimestamp as <=,
eqTimestamp as ==,
neTimestamp as !=,
TimestampLib.eq,
TimestampLib.ne,
TimestampLib.gt,
TimestampLib.gte,
TimestampLib.lt,
TimestampLib.lte,
TimestampLib.gtz,
TimestampLib.eqz,
TimestampLib.addSeconds,
TimestampLib.subtractSeconds,
TimestampLib.toInt
} for Timestamp global;
/// @dev return true if Timestamp a is after Timestamp b
function gtTimestamp(Timestamp a, Timestamp b) pure returns (bool) {
return Timestamp.unwrap(a) > Timestamp.unwrap(b);
}
/// @dev return true if Timestamp a is after or equal to Timestamp b
function gteTimestamp(Timestamp a, Timestamp b) pure returns (bool) {
return Timestamp.unwrap(a) >= Timestamp.unwrap(b);
}
/// @dev return true if Timestamp a is before Timestamp b
function ltTimestamp(Timestamp a, Timestamp b) pure returns (bool) {
return Timestamp.unwrap(a) < Timestamp.unwrap(b);
}
/// @dev return true if Timestamp a is before or equal to Timestamp b
function lteTimestamp(Timestamp a, Timestamp b) pure returns (bool) {
return Timestamp.unwrap(a) <= Timestamp.unwrap(b);
}
/// @dev return true if Timestamp a is equal to Timestamp b
function eqTimestamp(Timestamp a, Timestamp b) pure returns (bool) {
return Timestamp.unwrap(a) == Timestamp.unwrap(b);
}
/// @dev return true if Timestamp a is not equal to Timestamp b
function neTimestamp(Timestamp a, Timestamp b) pure returns (bool) {
return Timestamp.unwrap(a) != Timestamp.unwrap(b);
}
// TODO move to TimestampLib and rename to zero()
/// @dev Return the Timestamp zero (0)
function zeroTimestamp() pure returns (Timestamp) {
return Timestamp.wrap(0);
}
library TimestampLib {
function zero() public pure returns (Timestamp) {
return Timestamp.wrap(0);
}
function max() public pure returns (Timestamp) {
return Timestamp.wrap(type(uint40).max);
}
function current() public view returns (Timestamp) {
return Timestamp.wrap(uint40(block.timestamp));
}
function toTimestamp(uint256 timestamp) public pure returns (Timestamp) {
return Timestamp.wrap(uint40(timestamp));
}
/// @dev return true if Timestamp a is after Timestamp b
function gt(Timestamp a, Timestamp b) public pure returns (bool isAfter) {
return gtTimestamp(a, b);
}
/// @dev return true if Timestamp a is after or the same than Timestamp b
function gte(
Timestamp a,
Timestamp b
) public pure returns (bool isAfterOrSame) {
return gteTimestamp(a, b);
}
/// @dev return true if Timestamp a is before Timestamp b
function lt(Timestamp a, Timestamp b) public pure returns (bool isBefore) {
return ltTimestamp(a, b);
}
/// @dev return true if Timestamp a is before or the same than Timestamp b
function lte(
Timestamp a,
Timestamp b
) public pure returns (bool isBeforeOrSame) {
return lteTimestamp(a, b);
}
/// @dev return true if Timestamp a is equal to Timestamp b
function eq(Timestamp a, Timestamp b) public pure returns (bool isSame) {
return eqTimestamp(a, b);
}
/// @dev return true if Timestamp a is not equal to Timestamp b
function ne(
Timestamp a,
Timestamp b
) public pure returns (bool isDifferent) {
return neTimestamp(a, b);
}
/// @dev return true if Timestamp equals 0
function eqz(Timestamp timestamp) public pure returns (bool) {
return Timestamp.unwrap(timestamp) == 0;
}
/// @dev return true if Timestamp is larger than 0
function gtz(Timestamp timestamp) public pure returns (bool) {
return Timestamp.unwrap(timestamp) > 0;
}
/// @dev return a new timestamp that is duration seconds later than the provided timestamp.
function addSeconds(
Timestamp timestamp,
Seconds duration
) public pure returns (Timestamp) {
return toTimestamp(Timestamp.unwrap(timestamp) + duration.toInt());
}
/// @dev return a new timestamp that is duration seconds earlier than the provided timestamp.
function subtractSeconds(
Timestamp timestamp,
Seconds duration
) public pure returns (Timestamp) {
return toTimestamp(Timestamp.unwrap(timestamp) - duration.toInt());
}
function toInt(Timestamp timestamp) public pure returns (uint256) {
return uint256(uint40(Timestamp.unwrap(timestamp)));
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
/// @dev UFixed is a 160-bit fixed point number with 15 decimals precision.
type UFixed is uint160;
using {
addUFixed as +,
subUFixed as -,
mulUFixed as *,
divUFixed as /,
gtUFixed as >,
gteUFixed as >=,
ltUFixed as <,
lteUFixed as <=,
eqUFixed as ==,
neUFixed as !=,
UFixedLib.gt,
UFixedLib.eqz,
UFixedLib.gtz,
UFixedLib.toInt,
UFixedLib.toInt1000
} for UFixed global;
// TODO move to UFixedLib and rename to zero()
function zeroUFixed() pure returns (UFixed zero) {
return UFixed.wrap(0);
}
function addUFixed(UFixed a, UFixed b) pure returns (UFixed) {
return UFixed.wrap(UFixed.unwrap(a) + UFixed.unwrap(b));
}
function subUFixed(UFixed a, UFixed b) pure returns (UFixed) {
if (a < b) {
revert UFixedLib.UFixedLibNegativeResult();
}
return UFixed.wrap(UFixed.unwrap(a) - UFixed.unwrap(b));
}
function mulUFixed(UFixed a, UFixed b) pure returns (UFixed) {
return
UFixed.wrap(uint160(Math.mulDiv(UFixed.unwrap(a), UFixed.unwrap(b), 10 ** 15)));
}
function divUFixed(UFixed a, UFixed b) pure returns (UFixed) {
if (UFixed.unwrap(b) == 0) {
revert UFixedLib.UFixedLibDivisionByZero();
}
return
UFixed.wrap(uint160(Math.mulDiv(UFixed.unwrap(a), 10 ** 15, UFixed.unwrap(b))));
}
function gtUFixed(UFixed a, UFixed b) pure returns (bool isGreaterThan) {
return UFixed.unwrap(a) > UFixed.unwrap(b);
}
function gteUFixed(UFixed a, UFixed b) pure returns (bool isGreaterThan) {
return UFixed.unwrap(a) >= UFixed.unwrap(b);
}
function ltUFixed(UFixed a, UFixed b) pure returns (bool isGreaterThan) {
return UFixed.unwrap(a) < UFixed.unwrap(b);
}
function lteUFixed(UFixed a, UFixed b) pure returns (bool isGreaterThan) {
return UFixed.unwrap(a) <= UFixed.unwrap(b);
}
function eqUFixed(UFixed a, UFixed b) pure returns (bool isEqual) {
return UFixed.unwrap(a) == UFixed.unwrap(b);
}
function neUFixed(UFixed a, UFixed b) pure returns (bool isEqual) {
return UFixed.unwrap(a) != UFixed.unwrap(b);
}
function gtzUFixed(UFixed a) pure returns (bool isZero) {
return UFixed.unwrap(a) > 0;
}
function eqzUFixed(UFixed a) pure returns (bool isZero) {
return UFixed.unwrap(a) == 0;
}
function deltaUFixed(UFixed a, UFixed b) pure returns (UFixed) {
if (a > b) {
return a - b;
}
return b - a;
}
library UFixedLib {
error UFixedLibNegativeResult();
error UFixedLibDivisionByZero();
error UFixedLibExponentTooSmall(int8 exp);
error UFixedLibExponentTooLarge(int8 exp);
error UFixedLibNumberTooLarge(uint256 number);
int8 public constant EXP = 15;
uint256 public constant MULTIPLIER = 10 ** uint256(int256(EXP));
uint256 public constant MULTIPLIER_HALF = MULTIPLIER / 2;
/// @dev returns the rounding mode DOWN - 0.4 becomes 0, 0.5 becomes 0, 0.6 becomes 0
function ROUNDING_DOWN() public pure returns (uint8) {
return uint8(0);
}
/// @dev returns the rounding mode UP - 0.4 becomes 1, 0.5 becomes 1, 0.6 becomes 1
function ROUNDING_UP() public pure returns (uint8) {
return uint8(1);
}
/// @dev returns the rounding mode HALF_UP - 0.4 becomes 0, 0.5 becomes 1, 0.6 becomes 1
function ROUNDING_HALF_UP() public pure returns (uint8) {
return uint8(2);
}
/// @dev Converts the uint256 to a uint160 based UFixed.
/// This method reverts if the number is too large to fit in a uint160.
function toUFixed(uint256 a) public pure returns (UFixed) {
uint256 n = a * MULTIPLIER;
if (n > type(uint160).max) {
revert UFixedLibNumberTooLarge(a);
}
return UFixed.wrap(uint160(n));
}
/// @dev Converts the uint256 to a UFixed with given exponent.
function toUFixed(uint256 a, int8 exp) public pure returns (UFixed) {
if (EXP + exp < 0) {
revert UFixedLibExponentTooSmall(exp);
}
if (EXP + exp > 48) {
revert UFixedLibExponentTooLarge(exp);
}
uint256 n = a * 10 ** uint8(EXP + exp);
if (n > type(uint160).max) {
revert UFixedLibNumberTooLarge(n);
}
return UFixed.wrap(uint160(n));
}
/// @dev returns the decimals precision of the UFixed type
function decimals() public pure returns (uint256) {
return uint8(EXP);
}
/// @dev Converts a UFixed to a uint256.
function toInt(UFixed a) public pure returns (uint256) {
return toIntWithRounding(a, ROUNDING_HALF_UP());
}
/// @dev Converts a UFixed to a uint256.
function toInt1000(UFixed a) public pure returns (uint256) {
return toIntWithRounding(toUFixed(1000) * a, ROUNDING_HALF_UP());
}
/// @dev Converts a UFixed to a uint256 with given rounding mode.
function toIntWithRounding(UFixed a, uint8 rounding) public pure returns (uint256) {
if (rounding == ROUNDING_HALF_UP()) {
return
Math.mulDiv(
UFixed.unwrap(a) + MULTIPLIER_HALF,
1,
MULTIPLIER,
Math.Rounding.Floor
);
} else if (rounding == ROUNDING_DOWN()) {
return
Math.mulDiv(
UFixed.unwrap(a),
1,
MULTIPLIER,
Math.Rounding.Floor
);
} else {
return
Math.mulDiv(UFixed.unwrap(a), 1, MULTIPLIER, Math.Rounding.Ceil);
}
}
/// @dev adds two UFixed numbers
function add(UFixed a, UFixed b) public pure returns (UFixed) {
return addUFixed(a, b);
}
/// @dev subtracts two UFixed numbers
function sub(UFixed a, UFixed b) public pure returns (UFixed) {
return subUFixed(a, b);
}
/// @dev multiplies two UFixed numbers
function mul(UFixed a, UFixed b) public pure returns (UFixed) {
return mulUFixed(a, b);
}
/// @dev divides two UFixed numbers
function div(UFixed a, UFixed b) public pure returns (UFixed) {
return divUFixed(a, b);
}
/// @dev return true if UFixed a is greater than UFixed b
function gt(UFixed a, UFixed b) public pure returns (bool isGreaterThan) {
return gtUFixed(a, b);
}
/// @dev return true if UFixed a is greater than or equal to UFixed b
function gte(UFixed a, UFixed b) public pure returns (bool isGreaterThan) {
return gteUFixed(a, b);
}
/// @dev return true if UFixed a is less than UFixed b
function lt(UFixed a, UFixed b) public pure returns (bool isGreaterThan) {
return ltUFixed(a, b);
}
/// @dev return true if UFixed a is less than or equal to UFixed b
function lte(UFixed a, UFixed b) public pure returns (bool isGreaterThan) {
return lteUFixed(a, b);
}
/// @dev return true if UFixed a is equal to UFixed b
function eq(UFixed a, UFixed b) public pure returns (bool isEqual) {
return eqUFixed(a, b);
}
/// @dev return true if UFixed a is not zero
function gtz(UFixed a) public pure returns (bool isZero) {
return gtzUFixed(a);
}
/// @dev return true if UFixed a is zero
function eqz(UFixed a) public pure returns (bool isZero) {
return eqzUFixed(a);
}
function zero() public pure returns (UFixed) {
return UFixed.wrap(0);
}
function one() public pure returns (UFixed) {
return UFixed.wrap(uint160(MULTIPLIER));
}
function max() public pure returns (UFixed) {
return UFixed.wrap(type(uint160).max);
}
/// @dev return the absolute delta between two UFixed numbers
function delta(UFixed a, UFixed b) public pure returns (UFixed) {
return deltaUFixed(a, b);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
type VersionPart is uint8;
using {
versionPartGt as >,
versionPartEq as ==,
versionPartNe as !=,
VersionPartLib.eqz,
VersionPartLib.gtz,
VersionPartLib.toInt,
VersionPartLib.toString,
VersionPartLib.isValidRelease
}
for VersionPart global;
function versionPartGt(VersionPart a, VersionPart b) pure returns(bool isGreaterThan) { return VersionPart.unwrap(a) > VersionPart.unwrap(b); }
function versionPartEq(VersionPart a, VersionPart b) pure returns(bool isSame) { return VersionPart.unwrap(a) == VersionPart.unwrap(b); }
function versionPartNe(VersionPart a, VersionPart b) pure returns(bool isSame) { return VersionPart.unwrap(a) != VersionPart.unwrap(b); }
library VersionPartLib {
error ErrorReleaseTooBig(VersionPart releaseMax, VersionPart release);
function releaseMin() public pure returns (VersionPart) { return toVersionPart(3); }
function releaseMax() public pure returns (VersionPart) { return toVersionPart(99); }
function isValidRelease(VersionPart release) external pure returns(bool) {
uint256 releaseInt = VersionPart.unwrap(release);
return 3 <= releaseInt && releaseInt <= 99;
}
function toString(VersionPart a) external pure returns (string memory) {
if (a > releaseMax()) {
revert ErrorReleaseTooBig(releaseMax(), a);
}
uint256 value = VersionPart.unwrap(a);
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits = 0;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
uint index = digits - 1;
temp = value;
while (temp != 0) {
buffer[index] = bytes1(uint8(48 + temp % 10));
temp /= 10;
if (index > 0) {
index--;
}
}
return string(buffer);
}
function eqz(VersionPart a) external pure returns(bool) { return VersionPart.unwrap(a) == 0; }
function gtz(VersionPart a) external pure returns(bool) { return VersionPart.unwrap(a) > 0; }
function toInt(VersionPart a) external pure returns(uint256) { return VersionPart.unwrap(a); }
function toVersionPart(uint256 a) public pure returns(VersionPart) { return VersionPart.wrap(uint8(a)); }
}
type Version is uint24; // contains major,minor,patch version parts
using {
versionGt as >,
versionEq as ==,
VersionLib.toInt,
VersionLib.toUint64,
VersionLib.toMajorPart,
VersionLib.toVersionParts
}
for Version global;
function versionGt(Version a, Version b) pure returns(bool isGreaterThan) { return Version.unwrap(a) > Version.unwrap(b); }
function versionEq(Version a, Version b) pure returns(bool isSame) { return Version.unwrap(a) == Version.unwrap(b); }
library VersionLib {
function toInt(Version version) external pure returns(uint) { return Version.unwrap(version); }
function toUint64(Version version) external pure returns(uint64) { return Version.unwrap(version); }
function toMajorPart(Version version)
external
pure
returns(VersionPart major)
{
uint24 versionInt = Version.unwrap(version);
uint8 majorInt = uint8(versionInt >> 16);
return VersionPart.wrap(majorInt);
}
function toVersionParts(Version version)
external
pure
returns(
VersionPart major,
VersionPart minor,
VersionPart patch
)
{
uint24 versionInt = Version.unwrap(version);
uint8 majorInt = uint8(versionInt >> 16);
versionInt -= majorInt << 16;
uint8 minorInt = uint8(versionInt >> 8);
uint8 patchInt = uint8(versionInt - (minorInt << 8));
return (
VersionPart.wrap(majorInt),
VersionPart.wrap(minorInt),
VersionPart.wrap(patchInt)
);
}
// function toVersionPart(uint256 versionPart) public pure returns(VersionPart) {
// return VersionPart.wrap(uint8(versionPart));
// }
function toVersion(
uint256 major,
uint256 minor,
uint256 patch
)
external
pure
returns(Version)
{
require(
major < 256 && minor < 256 && patch < 256,
"ERROR:VRS-010:VERSION_PART_TOO_BIG");
return Version.wrap(
uint24(
(major << 16) + (minor << 8) + patch));
}
// TODO check for overflow?
function toVersion(uint64 versionNumber) external pure returns(Version) {
//assert(versionNumber <= type(Version).max);
return Version.wrap(uint24(versionNumber));
}
// TODO rename to zero()
function zeroVersion() external pure returns(Version) {
return Version.wrap(0);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
import {Version} from "../type/Version.sol";
/// IMPORTANT
// Upgradeable contract MUST:
// 1) inherit from Versionable
// 2) implement version() function
// 3) implement internal _initialize() function with onlyInitializing modifier
// 4) implement internal _upgrade() function with onlyInitializing modifier (1st version MUST revert)
// 5) have onlyInitialising modifier for each function callable inside _initialize()/_upgrade() (MUST use different functions for initialization/upgrade and normal operations)
// 6) use default empty constructor -> _disableInitializer() is called from Versionable contructor
// 7) use namespace storage (should this be needed)
// 8) since now inheritance is used for upgradability, contract MUST BE inherited ONLY by the next version
// Upgradeable contract SHOULD:
// 9) define all non private methods as virtual (in order to be able to upgrade them latter)
// otherwise, it is still possible to upgrade contract, but everyone who is using it will have to switch to a new fucntions
// in some cases this ok but not in the others...
//
// IMPORTANT
// If introducting/amending storage related to Versionable version MUST:
// 1) define namespace storage struct if accessing storage
// - DO NOT use structs inside, except
// - CAN use structs ONLY inside mappings
// 2) ALWAYS define private getter if accessing storage
// - MUST use default implementation, CAN change ONLY return type
interface IVersionable {
error ErrorVersionableInitializeNotImplemented();
error ErrorVersionableUpgradeNotImplemented();
/**
* @dev IMPORTANT
* implementation MUST be guarded by initializer modifier
* new version MUST inherit from previous version
*/
function initializeVersionable(address activatedBy, bytes memory activationData) external;
/**
* @dev
* implementation MUST be guarded by reinitializer(version().toUint64()) modifier
* new version MUST inherit from previous version
* the first verion MUST revert
*/
function upgradeVersionable(bytes memory upgradeData) external;
/**
* @dev returns version of this contract
* each new implementation MUST implement this function
* version number MUST increase
*/
function getVersion() external pure returns(Version);
}{
"evmVersion": "cancun",
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {
"contracts/type/NftId.sol": {
"NftIdLib": "0xb6fe977f3e63d01a734dd238ba5fc05bf45bf32c"
},
"contracts/type/String.sol": {
"StrLib": "0xb7336e875465659bad9659b25d078c6cace41706"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"flightProductAddress","type":"address"},{"internalType":"string","name":"nftName","type":"string"},{"internalType":"string","name":"nftSymbol","type":"string"},{"internalType":"string","name":"baseUri","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721IncorrectOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721InsufficientApproval","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC721InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC721InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721InvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC721InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC721InvalidSender","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonexistentToken","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ErrorFlightNftAlreadyMinted","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ErrorFlightNftNotAvailable","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ErrorFlightNftNotFlightPolicy","type":"error"},{"inputs":[],"name":"ErrorFlightNftNotMinter","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainNft","outputs":[{"internalType":"contract ChainNft","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flightProduct","outputs":[{"internalType":"contract FlightProduct","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"operator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"NftId","name":"policyNftId","type":"uint96"}],"name":"getPolicyData","outputs":[{"internalType":"RiskId","name":"riskId","type":"bytes8"},{"internalType":"string","name":"flightData","type":"string"},{"internalType":"string","name":"departureTimeLocal","type":"string"},{"internalType":"string","name":"arrivalTimeLocal","type":"string"},{"internalType":"Amount","name":"premiumAmount","type":"uint96"},{"internalType":"Amount[5]","name":"payoutAmounts","type":"uint96[5]"},{"internalType":"bytes1","name":"status","type":"bytes1"},{"internalType":"int256","name":"delayMinutes","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"instanceReader","outputs":[{"internalType":"contract InstanceReader","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract IRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseUri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minterAddress","type":"address"}],"name":"setMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"toString","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60e060405234801561000f575f80fd5b5060405161255d38038061255d83398101604081905261002e9161037e565b3383835f61003c83826104a5565b50600161004982826104a5565b5050506001600160a01b03811661007957604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b61008281610279565b506001600160a01b03841660a081905260408051635ab1bd5360e01b81529051635ab1bd53916004808201926020929091908290030181865afa1580156100cb573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100ef919061055f565b600780546001600160a01b0319166001600160a01b039290921691821790556040805163120726c360e01b8152905163120726c3916004808201926020929091908290030181865afa158015610147573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061016b919061055f565b6001600160a01b03166080816001600160a01b03168152505060a0516001600160a01b031663de7b5d146040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101e6919061055f565b6001600160a01b03166302cd30716040518163ffffffff1660e01b8152600401602060405180830381865afa158015610221573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610245919061055f565b6001600160a01b031660c052600880546001600160a01b03191633179055600961026f82826104a5565b5050505050610581565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6001600160a01b03811681146102de575f80fd5b50565b634e487b7160e01b5f52604160045260245ffd5b5f82601f830112610304575f80fd5b81516001600160401b0381111561031d5761031d6102e1565b604051601f8201601f19908116603f011681016001600160401b038111828210171561034b5761034b6102e1565b604052818152838201602001851015610362575f80fd5b8160208501602083015e5f918101602001919091529392505050565b5f805f8060808587031215610391575f80fd5b845161039c816102ca565b60208601519094506001600160401b038111156103b7575f80fd5b6103c3878288016102f5565b604087015190945090506001600160401b038111156103e0575f80fd5b6103ec878288016102f5565b606087015190935090506001600160401b03811115610409575f80fd5b610415878288016102f5565b91505092959194509250565b600181811c9082168061043557607f821691505b60208210810361045357634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156104a057805f5260205f20601f840160051c8101602085101561047e5750805b601f840160051c820191505b8181101561049d575f815560010161048a565b50505b505050565b81516001600160401b038111156104be576104be6102e1565b6104d2816104cc8454610421565b84610459565b6020601f821160018114610504575f83156104ed5750848201515b5f19600385901b1c1916600184901b17845561049d565b5f84815260208120601f198516915b828110156105335787850151825560209485019460019092019101610513565b508482101561055057868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f6020828403121561056f575f80fd5b815161057a816102ca565b9392505050565b60805160a05160c051611f6c6105f15f395f81816102bb01528181610ad90152610ba901525f8181610343015281816109fc0152610c3b01525f81816102fb0152818161040c0152818161052a015281816105fb015281816107700152818161087d0152610def0152611f6c5ff3fe608060405234801561000f575f80fd5b5060043610610187575f3560e01c80638a690bea116100d9578063a81c08be11610093578063c87b56dd1161006e578063c87b56dd1461039f578063e985e9c5146103b2578063f2fde38b146103c5578063fca3b5aa146103d8575f80fd5b8063a81c08be1461033e578063add226ca14610365578063b88d4fde1461038c575f80fd5b80638a690bea146102b65780638da5cb5b146102dd57806395d89b41146102ee5780639ba80c8b146102f6578063a0712d681461031d578063a22cb46514610330575f80fd5b806342842e0e116101445780636900a3ae1161011f5780636900a3ae1461026757806370a082311461027a578063715018a61461029b5780637b103999146102a3575f80fd5b806342842e0e1461022e57806355f804b3146102415780636352211e14610254575f80fd5b806301ffc9a71461018b57806306fdde03146101b357806307546172146101c8578063081812fc146101f3578063095ea7b31461020657806323b872dd1461021b575b5f80fd5b61019e6101993660046113b7565b6103eb565b60405190151581526020015b60405180910390f35b6101bb610483565b6040516101aa919061140c565b6008546101db906001600160a01b031681565b6040516001600160a01b0390911681526020016101aa565b6101db61020136600461141e565b610512565b610219610214366004611449565b61059c565b005b610219610229366004611473565b6105a8565b61021961023c366004611473565b6105b5565b61021961024f3660046115a2565b6105cf565b6101db61026236600461141e565b6105e3565b6101bb61027536600461141e565b610632565b61028d6102883660046115e6565b61074f565b6040519081526020016101aa565b6102196107db565b6007546101db906001600160a01b031681565b6101db7f000000000000000000000000000000000000000000000000000000000000000081565b6006546001600160a01b03166101db565b6101bb6107ee565b6101db7f000000000000000000000000000000000000000000000000000000000000000081565b61021961032b36600461141e565b6107fd565b61021961021436600461160e565b6101db7f000000000000000000000000000000000000000000000000000000000000000081565b610378610373366004611659565b610a9e565b6040516101aa989796959493929190611674565b61021961039a366004611726565b610d5d565b6101bb6103ad36600461141e565b610d6b565b61019e6103c03660046117a0565b610dc6565b6102196103d33660046115e6565b610e5a565b6102196103e63660046115e6565b610e97565b6040516301ffc9a760e01b81526001600160e01b0319821660048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906301ffc9a790602401602060405180830381865afa158015610459573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061047d91906117cc565b92915050565b60605f8054610491906117e7565b80601f01602080910402602001604051908101604052809291908181526020018280546104bd906117e7565b80156105085780601f106104df57610100808354040283529160200191610508565b820191905f5260205f20905b8154815290600101906020018083116104eb57829003601f168201915b5050505050905090565b60405163020604bf60e21b8152600481018290525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063081812fc906024015b602060405180830381865afa158015610578573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061047d919061181f565b6105a4610ec1565b5050565b6105b0610ec1565b505050565b6105b083838360405180602001604052805f815250610d5d565b6105d7610f55565b60096105a48282611885565b6040516331a9108f60e11b8152600481018290525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636352211e9060240161055d565b6060815f036106585750506040805180820190915260018152600360fc1b602082015290565b815f5b8115610681578061066b81611953565b915061067a9050600a8361197f565b915061065b565b5f816001600160401b0381111561069a5761069a6114b1565b6040519080825280601f01601f1916602001820160405280156106c4576020820181803683370190505b5090505f6106d3600184611992565b90508593505b8315610746576106ea600a856119a5565b6106f59060306119b8565b60f81b82828151811061070a5761070a6119cb565b60200101906001600160f81b03191690815f1a90535061072b600a8561197f565b93508015610741578061073d816119df565b9150505b6106d9565b50949350505050565b6040516370a0823160e01b81526001600160a01b0382811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa1580156107b7573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061047d91906119f4565b6107e3610f55565b6107ec5f610f82565b565b606060018054610491906117e7565b6008546001600160a01b031633146108285760405163255ba8f560e11b815260040160405180910390fd5b5f818152600260205260409020546001600160a01b031615610865576040516309f5437160e11b8152600481018290526024015b60405180910390fd5b6040516331a9108f60e11b8152600481018290525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636352211e90602401602060405180830381865afa1580156108ca573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108ee919061181f565b60075460405163efac97cf60e01b815260048101859052919250610a73916001600160a01b039091169063cefa7fd69073b6fe977f3e63d01a734dd238ba5fc05bf45bf32c9063efac97cf90602401602060405180830381865af4158015610958573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061097c9190611a1b565b6040516001600160e01b031960e084901b1681526001600160601b039091166004820152602401602060405180830381865afa1580156109be573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109e29190611a1b565b600754604051632f2a35f760e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015290911690635e546bee90602401602060405180830381865afa158015610a4a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a6e9190611a1b565b610fd3565b15610a9457604051638135cdbb60e01b81526004810183905260240161085c565b6105a48183611033565b5f60608060605f610aad611399565b60405163bd7d9d8560e01b81526001600160601b03881660048201525f90819081906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063bd7d9d85906024015f60405180830381865afa158015610b1d573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b449190810190611aca565b9050806080015194505f816101c00151511115610b7857806101c00151806020019051810190610b749190611c5d565b9450505b604080820151905163887b1fa960e01b81526001600160c01b0319821660048201529099505f906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063887b1fa9906024015f60405180830381865afa158015610bed573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610c149190810190611cf3565b60400151805190915015610d505760405163a179402960e01b81525f906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063a179402990610c7090859060040161140c565b5f60405180830381865afa158015610c8a573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610cb19190810190611dc7565b8051604051631623433d60e31b815291925073b7336e875465659bad9659b25d078c6cace417069163b11a19e891610cef9160040190815260200190565b5f60405180830381865af4158015610d09573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610d309190810190611eda565b995080604001519850806080015197508060c0015194508060e001519350505b5050919395975091939597565b610d65610ec1565b50505050565b60605f610d76611094565b90505f815111610d945760405180602001604052805f815250610dbf565b80610d9e84610632565b604051602001610daf929190611f22565b6040516020818303038152906040525b9392505050565b60405163e985e9c560e01b81526001600160a01b03838116600483015282811660248301525f917f00000000000000000000000000000000000000000000000000000000000000009091169063e985e9c590604401602060405180830381865afa158015610e36573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dbf91906117cc565b610e62610f55565b6001600160a01b038116610e8b57604051631e4fbdf760e01b81525f600482015260240161085c565b610e9481610f82565b50565b610e9f610f55565b600880546001600160a01b0319166001600160a01b0392909216919091179055565b60405162461bcd60e51b815260206004820152605760248201527f466c696768744e66743a205573652047494620436861696e204e465420636f6e60448201527f747261637420746f20696e7465726163742077697468204e4654732e2053656560648201527f2066756e6374696f6e20676574436861696e4e66742829000000000000000000608482015260a40161085c565b6006546001600160a01b031633146107ec5760405163118cdaa760e01b815233600482015260240161085c565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6040516399009c5160e01b81526001600160601b038084166004830152821660248201525f9073b6fe977f3e63d01a734dd238ba5fc05bf45bf32c906399009c5190604401602060405180830381865af4158015610e36573d5f803e3d5ffd5b6001600160a01b03821661105c57604051633250574960e11b81525f600482015260240161085c565b5f61106883835f6110a3565b90506001600160a01b038116156105b0576040516339e3563760e11b81525f600482015260240161085c565b606060098054610491906117e7565b5f828152600260205260408120546001600160a01b03908116908316156110cf576110cf818486611195565b6001600160a01b03811615611109576110ea5f855f806111f9565b6001600160a01b0381165f90815260036020526040902080545f190190555b6001600160a01b03851615611137576001600160a01b0385165f908152600360205260409020805460010190555b5f8481526002602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b6111a08383836112fd565b6105b0576001600160a01b0383166111ce57604051637e27328960e01b81526004810182905260240161085c565b60405163177e802f60e01b81526001600160a01b03831660048201526024810182905260440161085c565b808061120d57506001600160a01b03821615155b156112ce575f61121c84611361565b90506001600160a01b038316158015906112485750826001600160a01b0316816001600160a01b031614155b801561125b57506112598184610dc6565b155b156112845760405163a9fbf51f60e01b81526001600160a01b038416600482015260240161085c565b81156112cc5783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b50505f90815260046020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b5f6001600160a01b038316158015906113595750826001600160a01b0316846001600160a01b0316148061133657506113368484610dc6565b8061135957505f828152600460205260409020546001600160a01b038481169116145b949350505050565b5f818152600260205260408120546001600160a01b03168061047d57604051637e27328960e01b81526004810184905260240161085c565b6040518060a001604052806005906020820280368337509192915050565b5f602082840312156113c7575f80fd5b81356001600160e01b031981168114610dbf575f80fd5b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f610dbf60208301846113de565b5f6020828403121561142e575f80fd5b5035919050565b6001600160a01b0381168114610e94575f80fd5b5f806040838503121561145a575f80fd5b823561146581611435565b946020939093013593505050565b5f805f60608486031215611485575f80fd5b833561149081611435565b925060208401356114a081611435565b929592945050506040919091013590565b634e487b7160e01b5f52604160045260245ffd5b60405161020081016001600160401b03811182821017156114e8576114e86114b1565b60405290565b60405161014081016001600160401b03811182821017156114e8576114e86114b1565b604051601f8201601f191681016001600160401b0381118282101715611539576115396114b1565b604052919050565b5f6001600160401b03821115611559576115596114b1565b50601f01601f191660200190565b5f61157961157484611541565b611511565b905082815283838301111561158c575f80fd5b828260208301375f602084830101529392505050565b5f602082840312156115b2575f80fd5b81356001600160401b038111156115c7575f80fd5b8201601f810184136115d7575f80fd5b61135984823560208401611567565b5f602082840312156115f6575f80fd5b8135610dbf81611435565b8015158114610e94575f80fd5b5f806040838503121561161f575f80fd5b823561162a81611435565b9150602083013561163a81611601565b809150509250929050565b6001600160601b0381168114610e94575f80fd5b5f60208284031215611669575f80fd5b8135610dbf81611645565b6001600160401b0360c01b8916815261018060208201525f61169a61018083018a6113de565b82810360408401526116ac818a6113de565b905082810360608401526116c081896113de565b9150506001600160601b038616608083015260a08201855f5b60058110156117015781516001600160601b03168352602092830192909101906001016116d9565b5050506001600160f81b03199390931661014082015261016001529695505050505050565b5f805f8060808587031215611739575f80fd5b843561174481611435565b9350602085013561175481611435565b92506040850135915060608501356001600160401b03811115611775575f80fd5b8501601f81018713611785575f80fd5b61179487823560208401611567565b91505092959194509250565b5f80604083850312156117b1575f80fd5b82356117bc81611435565b9150602083013561163a81611435565b5f602082840312156117dc575f80fd5b8151610dbf81611601565b600181811c908216806117fb57607f821691505b60208210810361181957634e487b7160e01b5f52602260045260245ffd5b50919050565b5f6020828403121561182f575f80fd5b8151610dbf81611435565b601f8211156105b057805f5260205f20601f840160051c8101602085101561185f5750805b601f840160051c820191505b8181101561187e575f815560010161186b565b5050505050565b81516001600160401b0381111561189e5761189e6114b1565b6118b2816118ac84546117e7565b8461183a565b6020601f8211600181146118e4575f83156118cd5750848201515b5f19600385901b1c1916600184901b17845561187e565b5f84815260208120601f198516915b8281101561191357878501518255602094850194600190920191016118f3565b508482101561193057868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b634e487b7160e01b5f52601160045260245ffd5b5f600182016119645761196461193f565b5060010190565b634e487b7160e01b5f52601260045260245ffd5b5f8261198d5761198d61196b565b500490565b8181038181111561047d5761047d61193f565b5f826119b3576119b361196b565b500690565b8082018082111561047d5761047d61193f565b634e487b7160e01b5f52603260045260245ffd5b5f816119ed576119ed61193f565b505f190190565b5f60208284031215611a04575f80fd5b5051919050565b8051611a1681611645565b919050565b5f60208284031215611a2b575f80fd5b8151610dbf81611645565b80516001600160c01b031981168114611a16575f80fd5b805161ffff81168114611a16575f80fd5b64ffffffffff81168114610e94575f80fd5b8051611a1681611a5e565b5f82601f830112611a8a575f80fd5b8151602083015f611a9d61157484611541565b9050828152858383011115611ab0575f80fd5b8282602083015e5f92810160200192909252509392505050565b5f60208284031215611ada575f80fd5b81516001600160401b03811115611aef575f80fd5b82016102008185031215611b01575f80fd5b611b096114c5565b611b1282611a0b565b8152611b2060208301611a0b565b6020820152611b3160408301611a36565b6040820152611b4260608301611a0b565b6060820152611b5360808301611a0b565b6080820152611b6460a08301611a36565b60a0820152611b7560c08301611a4d565b60c0820152611b8660e08301611a4d565b60e0820152611b986101008301611a0b565b610100820152611bab6101208301611a0b565b610120820152611bbe6101408301611a70565b610140820152611bd16101608301611a70565b610160820152611be46101808301611a70565b610180820152611bf76101a08301611a70565b6101a08201526101c08201516001600160401b03811115611c16575f80fd5b611c2286828501611a7b565b6101c0830152506101e08201516001600160401b03811115611c42575f80fd5b611c4e86828501611a7b565b6101e083015250949350505050565b5f8060c08385031215611c6e575f80fd5b8251611c7981611645565b9150603f83018413611c89575f80fd5b60405160a081016001600160401b0381118282101715611cab57611cab6114b1565b6040528060c0850186811115611cbf575f80fd5b602086015b81811015611ce5578051611cd781611645565b835260209283019201611cc4565b505050809150509250929050565b5f60208284031215611d03575f80fd5b81516001600160401b03811115611d18575f80fd5b820160608185031215611d29575f80fd5b604051606081016001600160401b0381118282101715611d4b57611d4b6114b1565b6040528151611d5981611645565b81526020820151611d6981611a5e565b602082015260408201516001600160401b03811115611d86575f80fd5b611d9286828501611a7b565b604083015250949350505050565b80516001600160f81b031981168114611a16575f80fd5b805160ff81168114611a16575f80fd5b5f60208284031215611dd7575f80fd5b81516001600160401b03811115611dec575f80fd5b82016101408185031215611dfe575f80fd5b611e066114ee565b81518152611e1660208301611a70565b602082015260408201516001600160401b03811115611e33575f80fd5b611e3f86828501611a7b565b604083015250611e5160608301611a70565b606082015260808201516001600160401b03811115611e6e575f80fd5b611e7a86828501611a7b565b608083015250611e8c60a08301611a0b565b60a0820152611e9d60c08301611da0565b60c082015260e08281015190820152611eb96101008301611db7565b610100820152611ecc6101208301611a70565b610120820152949350505050565b5f60208284031215611eea575f80fd5b81516001600160401b03811115611eff575f80fd5b61135984828501611a7b565b5f81518060208401855e5f93019283525090919050565b5f611359611f308386611f0b565b84611f0b56fea264697066735822122018bd927de31813ab55aa35d7d914507e3ef2afaf156eb5eb9a2c771003e4d63364736f6c634300081a00330000000000000000000000000a413b847648ab254a6b753089254d078e05f838000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000064644504c435900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013466c696768742044656c617920506f6c69637900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003568747470733a2f2f666c6967687464656c61792e696e746567726174696f6e2e65746865726973632e636f6d2f6170692f6e66742f0000000000000000000000
Deployed Bytecode
0x608060405234801561000f575f80fd5b5060043610610187575f3560e01c80638a690bea116100d9578063a81c08be11610093578063c87b56dd1161006e578063c87b56dd1461039f578063e985e9c5146103b2578063f2fde38b146103c5578063fca3b5aa146103d8575f80fd5b8063a81c08be1461033e578063add226ca14610365578063b88d4fde1461038c575f80fd5b80638a690bea146102b65780638da5cb5b146102dd57806395d89b41146102ee5780639ba80c8b146102f6578063a0712d681461031d578063a22cb46514610330575f80fd5b806342842e0e116101445780636900a3ae1161011f5780636900a3ae1461026757806370a082311461027a578063715018a61461029b5780637b103999146102a3575f80fd5b806342842e0e1461022e57806355f804b3146102415780636352211e14610254575f80fd5b806301ffc9a71461018b57806306fdde03146101b357806307546172146101c8578063081812fc146101f3578063095ea7b31461020657806323b872dd1461021b575b5f80fd5b61019e6101993660046113b7565b6103eb565b60405190151581526020015b60405180910390f35b6101bb610483565b6040516101aa919061140c565b6008546101db906001600160a01b031681565b6040516001600160a01b0390911681526020016101aa565b6101db61020136600461141e565b610512565b610219610214366004611449565b61059c565b005b610219610229366004611473565b6105a8565b61021961023c366004611473565b6105b5565b61021961024f3660046115a2565b6105cf565b6101db61026236600461141e565b6105e3565b6101bb61027536600461141e565b610632565b61028d6102883660046115e6565b61074f565b6040519081526020016101aa565b6102196107db565b6007546101db906001600160a01b031681565b6101db7f000000000000000000000000fae8dbf367fe536a28a53c56bbbc885452f16cd181565b6006546001600160a01b03166101db565b6101bb6107ee565b6101db7f00000000000000000000000033605c6e3de781cf10e21930f7b1a11599417ff781565b61021961032b36600461141e565b6107fd565b61021961021436600461160e565b6101db7f0000000000000000000000000a413b847648ab254a6b753089254d078e05f83881565b610378610373366004611659565b610a9e565b6040516101aa989796959493929190611674565b61021961039a366004611726565b610d5d565b6101bb6103ad36600461141e565b610d6b565b61019e6103c03660046117a0565b610dc6565b6102196103d33660046115e6565b610e5a565b6102196103e63660046115e6565b610e97565b6040516301ffc9a760e01b81526001600160e01b0319821660048201525f907f00000000000000000000000033605c6e3de781cf10e21930f7b1a11599417ff76001600160a01b0316906301ffc9a790602401602060405180830381865afa158015610459573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061047d91906117cc565b92915050565b60605f8054610491906117e7565b80601f01602080910402602001604051908101604052809291908181526020018280546104bd906117e7565b80156105085780601f106104df57610100808354040283529160200191610508565b820191905f5260205f20905b8154815290600101906020018083116104eb57829003601f168201915b5050505050905090565b60405163020604bf60e21b8152600481018290525f907f00000000000000000000000033605c6e3de781cf10e21930f7b1a11599417ff76001600160a01b03169063081812fc906024015b602060405180830381865afa158015610578573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061047d919061181f565b6105a4610ec1565b5050565b6105b0610ec1565b505050565b6105b083838360405180602001604052805f815250610d5d565b6105d7610f55565b60096105a48282611885565b6040516331a9108f60e11b8152600481018290525f907f00000000000000000000000033605c6e3de781cf10e21930f7b1a11599417ff76001600160a01b031690636352211e9060240161055d565b6060815f036106585750506040805180820190915260018152600360fc1b602082015290565b815f5b8115610681578061066b81611953565b915061067a9050600a8361197f565b915061065b565b5f816001600160401b0381111561069a5761069a6114b1565b6040519080825280601f01601f1916602001820160405280156106c4576020820181803683370190505b5090505f6106d3600184611992565b90508593505b8315610746576106ea600a856119a5565b6106f59060306119b8565b60f81b82828151811061070a5761070a6119cb565b60200101906001600160f81b03191690815f1a90535061072b600a8561197f565b93508015610741578061073d816119df565b9150505b6106d9565b50949350505050565b6040516370a0823160e01b81526001600160a01b0382811660048301525f917f00000000000000000000000033605c6e3de781cf10e21930f7b1a11599417ff7909116906370a0823190602401602060405180830381865afa1580156107b7573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061047d91906119f4565b6107e3610f55565b6107ec5f610f82565b565b606060018054610491906117e7565b6008546001600160a01b031633146108285760405163255ba8f560e11b815260040160405180910390fd5b5f818152600260205260409020546001600160a01b031615610865576040516309f5437160e11b8152600481018290526024015b60405180910390fd5b6040516331a9108f60e11b8152600481018290525f907f00000000000000000000000033605c6e3de781cf10e21930f7b1a11599417ff76001600160a01b031690636352211e90602401602060405180830381865afa1580156108ca573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108ee919061181f565b60075460405163efac97cf60e01b815260048101859052919250610a73916001600160a01b039091169063cefa7fd69073b6fe977f3e63d01a734dd238ba5fc05bf45bf32c9063efac97cf90602401602060405180830381865af4158015610958573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061097c9190611a1b565b6040516001600160e01b031960e084901b1681526001600160601b039091166004820152602401602060405180830381865afa1580156109be573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109e29190611a1b565b600754604051632f2a35f760e11b81526001600160a01b037f0000000000000000000000000a413b847648ab254a6b753089254d078e05f8388116600483015290911690635e546bee90602401602060405180830381865afa158015610a4a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a6e9190611a1b565b610fd3565b15610a9457604051638135cdbb60e01b81526004810183905260240161085c565b6105a48183611033565b5f60608060605f610aad611399565b60405163bd7d9d8560e01b81526001600160601b03881660048201525f90819081906001600160a01b037f000000000000000000000000fae8dbf367fe536a28a53c56bbbc885452f16cd1169063bd7d9d85906024015f60405180830381865afa158015610b1d573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b449190810190611aca565b9050806080015194505f816101c00151511115610b7857806101c00151806020019051810190610b749190611c5d565b9450505b604080820151905163887b1fa960e01b81526001600160c01b0319821660048201529099505f906001600160a01b037f000000000000000000000000fae8dbf367fe536a28a53c56bbbc885452f16cd1169063887b1fa9906024015f60405180830381865afa158015610bed573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610c149190810190611cf3565b60400151805190915015610d505760405163a179402960e01b81525f906001600160a01b037f0000000000000000000000000a413b847648ab254a6b753089254d078e05f838169063a179402990610c7090859060040161140c565b5f60405180830381865afa158015610c8a573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610cb19190810190611dc7565b8051604051631623433d60e31b815291925073b7336e875465659bad9659b25d078c6cace417069163b11a19e891610cef9160040190815260200190565b5f60405180830381865af4158015610d09573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610d309190810190611eda565b995080604001519850806080015197508060c0015194508060e001519350505b5050919395975091939597565b610d65610ec1565b50505050565b60605f610d76611094565b90505f815111610d945760405180602001604052805f815250610dbf565b80610d9e84610632565b604051602001610daf929190611f22565b6040516020818303038152906040525b9392505050565b60405163e985e9c560e01b81526001600160a01b03838116600483015282811660248301525f917f00000000000000000000000033605c6e3de781cf10e21930f7b1a11599417ff79091169063e985e9c590604401602060405180830381865afa158015610e36573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dbf91906117cc565b610e62610f55565b6001600160a01b038116610e8b57604051631e4fbdf760e01b81525f600482015260240161085c565b610e9481610f82565b50565b610e9f610f55565b600880546001600160a01b0319166001600160a01b0392909216919091179055565b60405162461bcd60e51b815260206004820152605760248201527f466c696768744e66743a205573652047494620436861696e204e465420636f6e60448201527f747261637420746f20696e7465726163742077697468204e4654732e2053656560648201527f2066756e6374696f6e20676574436861696e4e66742829000000000000000000608482015260a40161085c565b6006546001600160a01b031633146107ec5760405163118cdaa760e01b815233600482015260240161085c565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6040516399009c5160e01b81526001600160601b038084166004830152821660248201525f9073b6fe977f3e63d01a734dd238ba5fc05bf45bf32c906399009c5190604401602060405180830381865af4158015610e36573d5f803e3d5ffd5b6001600160a01b03821661105c57604051633250574960e11b81525f600482015260240161085c565b5f61106883835f6110a3565b90506001600160a01b038116156105b0576040516339e3563760e11b81525f600482015260240161085c565b606060098054610491906117e7565b5f828152600260205260408120546001600160a01b03908116908316156110cf576110cf818486611195565b6001600160a01b03811615611109576110ea5f855f806111f9565b6001600160a01b0381165f90815260036020526040902080545f190190555b6001600160a01b03851615611137576001600160a01b0385165f908152600360205260409020805460010190555b5f8481526002602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b6111a08383836112fd565b6105b0576001600160a01b0383166111ce57604051637e27328960e01b81526004810182905260240161085c565b60405163177e802f60e01b81526001600160a01b03831660048201526024810182905260440161085c565b808061120d57506001600160a01b03821615155b156112ce575f61121c84611361565b90506001600160a01b038316158015906112485750826001600160a01b0316816001600160a01b031614155b801561125b57506112598184610dc6565b155b156112845760405163a9fbf51f60e01b81526001600160a01b038416600482015260240161085c565b81156112cc5783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b50505f90815260046020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b5f6001600160a01b038316158015906113595750826001600160a01b0316846001600160a01b0316148061133657506113368484610dc6565b8061135957505f828152600460205260409020546001600160a01b038481169116145b949350505050565b5f818152600260205260408120546001600160a01b03168061047d57604051637e27328960e01b81526004810184905260240161085c565b6040518060a001604052806005906020820280368337509192915050565b5f602082840312156113c7575f80fd5b81356001600160e01b031981168114610dbf575f80fd5b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f610dbf60208301846113de565b5f6020828403121561142e575f80fd5b5035919050565b6001600160a01b0381168114610e94575f80fd5b5f806040838503121561145a575f80fd5b823561146581611435565b946020939093013593505050565b5f805f60608486031215611485575f80fd5b833561149081611435565b925060208401356114a081611435565b929592945050506040919091013590565b634e487b7160e01b5f52604160045260245ffd5b60405161020081016001600160401b03811182821017156114e8576114e86114b1565b60405290565b60405161014081016001600160401b03811182821017156114e8576114e86114b1565b604051601f8201601f191681016001600160401b0381118282101715611539576115396114b1565b604052919050565b5f6001600160401b03821115611559576115596114b1565b50601f01601f191660200190565b5f61157961157484611541565b611511565b905082815283838301111561158c575f80fd5b828260208301375f602084830101529392505050565b5f602082840312156115b2575f80fd5b81356001600160401b038111156115c7575f80fd5b8201601f810184136115d7575f80fd5b61135984823560208401611567565b5f602082840312156115f6575f80fd5b8135610dbf81611435565b8015158114610e94575f80fd5b5f806040838503121561161f575f80fd5b823561162a81611435565b9150602083013561163a81611601565b809150509250929050565b6001600160601b0381168114610e94575f80fd5b5f60208284031215611669575f80fd5b8135610dbf81611645565b6001600160401b0360c01b8916815261018060208201525f61169a61018083018a6113de565b82810360408401526116ac818a6113de565b905082810360608401526116c081896113de565b9150506001600160601b038616608083015260a08201855f5b60058110156117015781516001600160601b03168352602092830192909101906001016116d9565b5050506001600160f81b03199390931661014082015261016001529695505050505050565b5f805f8060808587031215611739575f80fd5b843561174481611435565b9350602085013561175481611435565b92506040850135915060608501356001600160401b03811115611775575f80fd5b8501601f81018713611785575f80fd5b61179487823560208401611567565b91505092959194509250565b5f80604083850312156117b1575f80fd5b82356117bc81611435565b9150602083013561163a81611435565b5f602082840312156117dc575f80fd5b8151610dbf81611601565b600181811c908216806117fb57607f821691505b60208210810361181957634e487b7160e01b5f52602260045260245ffd5b50919050565b5f6020828403121561182f575f80fd5b8151610dbf81611435565b601f8211156105b057805f5260205f20601f840160051c8101602085101561185f5750805b601f840160051c820191505b8181101561187e575f815560010161186b565b5050505050565b81516001600160401b0381111561189e5761189e6114b1565b6118b2816118ac84546117e7565b8461183a565b6020601f8211600181146118e4575f83156118cd5750848201515b5f19600385901b1c1916600184901b17845561187e565b5f84815260208120601f198516915b8281101561191357878501518255602094850194600190920191016118f3565b508482101561193057868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b634e487b7160e01b5f52601160045260245ffd5b5f600182016119645761196461193f565b5060010190565b634e487b7160e01b5f52601260045260245ffd5b5f8261198d5761198d61196b565b500490565b8181038181111561047d5761047d61193f565b5f826119b3576119b361196b565b500690565b8082018082111561047d5761047d61193f565b634e487b7160e01b5f52603260045260245ffd5b5f816119ed576119ed61193f565b505f190190565b5f60208284031215611a04575f80fd5b5051919050565b8051611a1681611645565b919050565b5f60208284031215611a2b575f80fd5b8151610dbf81611645565b80516001600160c01b031981168114611a16575f80fd5b805161ffff81168114611a16575f80fd5b64ffffffffff81168114610e94575f80fd5b8051611a1681611a5e565b5f82601f830112611a8a575f80fd5b8151602083015f611a9d61157484611541565b9050828152858383011115611ab0575f80fd5b8282602083015e5f92810160200192909252509392505050565b5f60208284031215611ada575f80fd5b81516001600160401b03811115611aef575f80fd5b82016102008185031215611b01575f80fd5b611b096114c5565b611b1282611a0b565b8152611b2060208301611a0b565b6020820152611b3160408301611a36565b6040820152611b4260608301611a0b565b6060820152611b5360808301611a0b565b6080820152611b6460a08301611a36565b60a0820152611b7560c08301611a4d565b60c0820152611b8660e08301611a4d565b60e0820152611b986101008301611a0b565b610100820152611bab6101208301611a0b565b610120820152611bbe6101408301611a70565b610140820152611bd16101608301611a70565b610160820152611be46101808301611a70565b610180820152611bf76101a08301611a70565b6101a08201526101c08201516001600160401b03811115611c16575f80fd5b611c2286828501611a7b565b6101c0830152506101e08201516001600160401b03811115611c42575f80fd5b611c4e86828501611a7b565b6101e083015250949350505050565b5f8060c08385031215611c6e575f80fd5b8251611c7981611645565b9150603f83018413611c89575f80fd5b60405160a081016001600160401b0381118282101715611cab57611cab6114b1565b6040528060c0850186811115611cbf575f80fd5b602086015b81811015611ce5578051611cd781611645565b835260209283019201611cc4565b505050809150509250929050565b5f60208284031215611d03575f80fd5b81516001600160401b03811115611d18575f80fd5b820160608185031215611d29575f80fd5b604051606081016001600160401b0381118282101715611d4b57611d4b6114b1565b6040528151611d5981611645565b81526020820151611d6981611a5e565b602082015260408201516001600160401b03811115611d86575f80fd5b611d9286828501611a7b565b604083015250949350505050565b80516001600160f81b031981168114611a16575f80fd5b805160ff81168114611a16575f80fd5b5f60208284031215611dd7575f80fd5b81516001600160401b03811115611dec575f80fd5b82016101408185031215611dfe575f80fd5b611e066114ee565b81518152611e1660208301611a70565b602082015260408201516001600160401b03811115611e33575f80fd5b611e3f86828501611a7b565b604083015250611e5160608301611a70565b606082015260808201516001600160401b03811115611e6e575f80fd5b611e7a86828501611a7b565b608083015250611e8c60a08301611a0b565b60a0820152611e9d60c08301611da0565b60c082015260e08281015190820152611eb96101008301611db7565b610100820152611ecc6101208301611a70565b610120820152949350505050565b5f60208284031215611eea575f80fd5b81516001600160401b03811115611eff575f80fd5b61135984828501611a7b565b5f81518060208401855e5f93019283525090919050565b5f611359611f308386611f0b565b84611f0b56fea264697066735822122018bd927de31813ab55aa35d7d914507e3ef2afaf156eb5eb9a2c771003e4d63364736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000a413b847648ab254a6b753089254d078e05f838000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000064644504c435900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013466c696768742044656c617920506f6c69637900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003568747470733a2f2f666c6967687464656c61792e696e746567726174696f6e2e65746865726973632e636f6d2f6170692f6e66742f0000000000000000000000
-----Decoded View---------------
Arg [0] : flightProductAddress (address): 0x0A413B847648ab254a6B753089254d078E05F838
Arg [1] : nftName (string): FDPLCY
Arg [2] : nftSymbol (string): Flight Delay Policy
Arg [3] : baseUri (string): https://flightdelay.integration.etherisc.com/api/nft/
-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 0000000000000000000000000a413b847648ab254a6b753089254d078e05f838
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [2] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [5] : 4644504c43590000000000000000000000000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000013
Arg [7] : 466c696768742044656c617920506f6c69637900000000000000000000000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000035
Arg [9] : 68747470733a2f2f666c6967687464656c61792e696e746567726174696f6e2e
Arg [10] : 65746865726973632e636f6d2f6170692f6e66742f0000000000000000000000
Loading...
Loading
Loading...
Loading
OVERVIEW
Protect your flight to Devcon (and back) with Etherisc Flight Delay Protection. Get automatic payouts for delayed, cancelled or diverted flights.Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
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.