// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)pragmasolidity ^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.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
function_contextSuffixLength() internalviewvirtualreturns (uint256) {
return0;
}
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)pragmasolidity ^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}.
*/interfaceIERC165{
/**
* @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.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}
Contract Source Code
File 4 of 9: IERC721.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)pragmasolidity ^0.8.20;import {IERC165} from"../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/interfaceIERC721isIERC165{
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/eventApproval(addressindexed owner, addressindexed approved, uint256indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/eventApprovalForAll(addressindexed owner, addressindexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/functionbalanceOf(address owner) externalviewreturns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/functionownerOf(uint256 tokenId) externalviewreturns (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.
*/functionsafeTransferFrom(addressfrom, address to, uint256 tokenId, bytescalldata 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.
*/functionsafeTransferFrom(addressfrom, 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.
*/functiontransferFrom(addressfrom, 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.
*/functionapprove(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.
*/functionsetApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/functiongetApproved(uint256 tokenId) externalviewreturns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/functionisApprovedForAll(address owner, address operator) externalviewreturns (bool);
}
// SPDX-License-Identifier: MIT OR Apache-2pragmasolidity ^0.8.24;import"../external/ILockBox.sol";
interfaceIPNS{
// Events/// A pre-registration was created/// @param sender who did the preregistration/// @param nameHash The hash of the lockup ID, name, and initial records./// @param lockupId The lockup ID that is included in the hash.eventPreregister(address sender, bytes32 nameHash, uint64 lockupId);
/// A preregistraton was deleted/// @param sender who destroyed the preregistration/// @param nameHash The hash of the lockup ID, name, and initial records./// @param lockupId The lockup ID.eventDestroyPrereg(address sender, bytes32 nameHash, uint64 lockupId);
/// A domain was registered/// @param sender who registered the domain (not necessarily the owner)/// @param id The ID of the new domain/// @param lockupId The lockup ID of the lockbox that owns the domain/// @param name The domain name itself/// @param records Initial records that were associated with the domaineventRegister(address sender, uint64 id, uint64 lockupId, bytes name, bytes records);
/// Domain records were updated/// @param sender who updated the records/// @param id The ID of the (sub)domain/// @param records The new records to entereventUpdateRecords(address sender, uint64 id, bytes records);
/// A domain was taken over, this means either the owner is switching it to a different lockup/// or the owner unlocked the lockup and so now it is up for grabs and someone took it./// @param sender who did the takeover/// @param id The domain ID/// @param oldLockupId The old lockup ID/// @param newLockupId The new lockup ID/// @param records new records that were assignedeventTakeover(address sender, uint64 id, uint64 oldLockupId, uint64 newLockupId, bytes records);
/// The owner of the domain has created a subdomain/// @param sender who made the subdomain/// @param id The ID of the subdomain/// @param parentId The ID of the domain/// @param name The fully qualified domain name of the subdomain/// @param records The initial DNS records to set for the subdomaineventCreateSubdomain(address sender, uint64 id, uint64 parentId, bytes name, bytes records);
/// A domain or subdomain has been deleted./// This could be done by the owner of the domain, or in the case that the underlying lockup was/// unlocked, this could be done by anyone - the same as takeover./// @param sender who did the operation./// @param id the ID of the (sub)domain.eventDestroy(address sender, uint64 id);
/// A (sub)domain was blacklisted or unblacklisted./// @param sender Who did the operation/// @param id The ID of the domain/// @param isBlacklisted True if the operation has made the domain blacklisted.eventBlacklist(address sender, uint64 id, bool isBlacklisted);
/// The stored representation of a domain or subdomain.structDomain {
/// If this is a top level domain, then this is the lockup id of the owner./// If this is a subdomain, then this is the ID of the top level domain.uint64 owner;
/// Number of subdomains, if 0xff then this IS a subdomain.uint8 subdomains;
/// True if the admin has marked this domain as blacklisted for the purpose of public resolvers.bool blacklisted;
/// The FQDN of this domain (excluding .pkt. at the end)bytes name;
/// Binary encoded representation of the records as updated by the ownerbytes records;
}
/// The lockbox of which the lockup holder is the owner and controller of the domain.functionlockboxContract() externalreturns (ILockBox);
/// The minimum amount of value that you must have locked in order to register / preregister/// a domain. This starts at double the amount of the previous registration, and it decays/// by 50% per halvingSeconds seconds.functioncurrentMinLockup() externalviewreturns (uint256 price);
/// Get the value of assets in a given lockbox./// This function does not care whether the lockbox is staked for a time period or is even matured,/// only that it is not withdrawn, because when it is withdrawn the lockup entry is deleted so/// the values becomes zero.functiongetLockupValuation(uint256 lockupId) externalviewreturns (uint256);
/// Compute the hash which must be passed to preregister as the nameHash./// /// @param lockupId The lockup which will become the owner of the domain./// @param name The domain name, dots are forbidden in domain names because they would interfere/// with other people creating subdomains. Other than that, no error checking is done/// at the contract level, but capital letters or disallowed characters will result in/// a domain that cannot be resolved./// @param records A byte array which represents a packed list of DNS records which will be used./// @return nameHash A hash that can be used for calling preregister()functioncomputePreregHash(uint64 lockupId,
bytescalldata name,
bytescalldata records
) externalpurereturns (bytes32);
/// In order to prevent malicious actors from watching for unconfirmed name registrations and/// frontrunning them with their own, you are required to pre-register before you register a name./// You pre-register by passing in the nameHash (which you can compute with computePreregHash()) plus/// the lockup ID of the lockup that you plan to use for this name./// Preregistrations are valid for 24 hours, or until the underlying lockup is unstaked. After either/// of these events, preregistrations are nolonger usable and can be removed by anyone by calling/// destroyPrereg()./// /// @param nameHash The hash of the name, lockupId, and records you intend to insert. Can be computed/// using computePreregHash()/// @param lockupId The id of the lockup which should take ownership of this domain, you must own this/// lockup in order to pre-register. This lockup's value (i.e. getLockupValuation()) must/// be at least equal to currentMinLockup(). This lockup must not have been used to/// register any other currently active domain. Ownership of the domain can be switched/// to a different lockup using the takeover() function, but that lockup must have a/// value of at least currentMinLockup() AT THE TIME OF takeover()./// If the lockup is unstaked while the domain is active, anyone with a qualifying lockup/// will be able to takeover() the domain, and anyone at all will be able to destroy() it./// This id MUST match the one passed to computePreregHash() or else register() will fail.functionpreregister(bytes32 nameHash, uint64 lockupId) external;
/// A preregistration can be removed under one of the following three conditions:/// 1. It has expired (it is more than 24 hours old)/// 2. The lockup which owns it has been unstaked/// 3. You are authorized to administer the lockup (normally meaning, you're the owner)////// The arguments to this function are the same as those to preregister().functiondestroyPrereg(bytes32 nameHash, uint64 lockupId) external;
/// After pre-registering, you can register your domain. You cannot register in the same block as/// pre-registering because that would enable front-running attacks. This function can be called by/// anyone, not only the person who pre-registered, but ownership will go to the person who/// pre-registered. The arguments to this function MUST be precisely the same as those passed to/// computePreregHash() when creating the pre-registration hash.functionregister(uint64 lockupId,
bytescalldata name,
bytescalldata records
) external;
/// Get the lockup ID of the lockup that owns the domain.////// @param id The ID of the domain/// @return The lockbox ID of the owner lockupfunctionownerLockup(uint64 id) externalviewreturns (uint64);
/// Check if a given address is authorized to manipulate the lockup which underlies this domain./// See: ERC-721 getApproved() and isApprovedForAll()/// /// @param id The ID of the domain/// @param who The address to check/// @return true if the address is authorizedfunctionauthorizedFor(uint64 id, address who) externalviewreturns (bool);
/// Update the DNS records for a domain or subdomain./// This can only be called by an authorized party for the lockup that owns the domain./// /// @param id The id of the domain or subdomain, use getDomainIdByFQDN() to look it up./// @param records A binary packed representation of the domain records.functionupdateRecords(uint64 id,
bytescalldata records
) external;
/// Take over a domain which is either your own, and you are switching which lockup is/// associated with the domain, or it is somebody elses and they have unstaked their lockup./// In order to takeover a domain, you must have the same lockup valuation that you would need to/// preregister./// You cannot takeover a subdomain, but after you have taken over the parent domain, you can then/// updateRecords() and/or destroy() any of it's subdomains as you are now the owner./// /// @param id The ID of the domain./// @param newLockupId The lockup ID that you wish to associate to this domain./// @param records New domain records which you wish to set to this domain. If this is an empty array/// then the domain's records will remain unchanged.functiontakeover(uint64 id,
uint64 newLockupId,
bytescalldata records
) external;
/// Create a subdomain of your domain./// To create a subdomain, you must own the parent domain. You cannot create a subdomain of a subdomain/// but you can create a subdomain with a subdomainLabel that has dots in it, for example/// "the.coolest.subdomain" under domain "cjd" will resolve as "the.coolest.subdomain.cjd(.pkt)"./// Subdomains cannot be sold off to anyone else, you, the lockbox owner, always own all subdomains/// because you are uniquely able to unstake and cause the domain and all of it's subdomains to be lost./// Each domain can have a maximum of 254 subdomains./// /// @param parentId The ID of the domain underwhich to create a subdomain, use getDomainIdByFQDN() to get/// the id of your domain./// @param subdomainLabel The name of the subdomain. Dots are legal in subdomain names./// Normal DNS rules apply to naming but are not checked at the contract level./// @param records A binary packed representation of the DNS records which should apply to this subdomain.functioncreateSubdomain(uint64 parentId,
bytescalldata subdomainLabel,
bytescalldata records
) external;
/// Destroy a domain or subdomain. A domain cannot be destroyed until after ALL of it's subdomains have/// been destroyed. You may destroy a domain or subdomain if you are authorized (see authorizedFor()), or/// if the owner has unstaked the lockbox which controls it./// /// @param id The ID of the domain or subdomain to destroy, see getDomainIdByFQDN()functiondestroy(uint64 id) external;
/// A structure allowing you to create multiple new subdomains in the same transaction.structNewSubdomain {
/// ID of the parent domain for the new subdomain.uint64 parentId;
/// The subdomain labelbytes subdomainLabel;
/// Binary packed representation of domain recordsbytes records;
}
/// A structure allowing for multiple DNS record updates in a single transaction.structRecordUpdate {
/// The ID of the domain whose records should be updated.uint64 id;
/// A binary packed representation of the records to update.bytes records;
}
/// Perform multiple updates in a single transaction./// /// @param newSubdomains New subdomains to create/// @param recordUpdates DNS records to update/// @param destroyDomains Domains / subdomains to destroyfunctionupdateMultiple(
NewSubdomain[] calldata newSubdomains,
RecordUpdate[] calldata recordUpdates,
uint64[] calldata destroyDomains
) external;
// View functions/// Get the next domain ID./// Any ID greater than or equal to this is guaranteed not to corrispond to an existing domain.functionnextDomainId() externalviewreturns (uint);
/// Observe the ID free list. Calling this with last = 0 will give you the head of the list, call with the/// value given to walk the list.functiondomainIdFreeList(uint64 last) externalviewreturns (uint);
/// Get the details of a domain by its ID./// /// @param id The id of the domain or subdomain to get information for, see getDomainIdByFQDN()/// @return owner The owner of the domain, if this is a subdomain then this is the domain ID of the parent,/// otherwise it is the lockup ID of the lockup that owns the domain./// @return subdomains The number of subdomains, if this is 0xff then this IS a subdomain./// @return blacklisted True if the domain has been blacklisted/// @return name The name of the domain, if this is empty then it means the domain does not exist./// @return records A binary packed representation of the domain records.functiongetDomain(uint64 id) externalviewreturns (uint64 owner,
uint8 subdomains,
bool blacklisted,
bytesmemory name,
bytesmemory records
);
/// Get multiple domains in one RPC call./// /// @param ids The list of IDs of domains to get./// @return out The domains that have been accessed. If an ID in the list doesn't exist, the resulting/// Domain's `name` field will be empty.functiongetDomains(uint64[] calldata ids) externalviewreturns (Domain[] memory out);
/// Get the domain ID of a domain by the lockup ID of the lock box./// /// @param lockupId The lockup id/// @return The domain ID, or zero if there is no domain for this lock box.functiongetDomainIdByLockupId(uint64 lockupId) externalviewreturns (uint64);
/// Get the domain ID of a domain by the full domain name (except the ".pkt." suffix)./// /// @param fqdn The domain name, for example "the.coolest.is.cjd" will get the subdomain/// the.coolest.is.cjd.pkt/// @return The domain ID, or zero if there is no domain for this name.functiongetDomainIdByFQDN(bytescalldata fqdn) externalviewreturns (uint64);
/// Get the internal info about the minimum lockup value needed for registering a domain./// Normally you should use currentMinLockup() but this will give you the internal state./// /// @return pricer The tool for determining the current cost (min lockup) of a domain/// @return lastRegTime Time of last domain registration (seconds since the epoch)/// @return lastRegPrice The lockup value from the most recently registered domain.functiongetPricingInfo() externalviewreturns (address pricer,
uint64 lastRegTime,
uint256 lastRegPrice
);
/// Get the internal state of the pre-registration table. Normally you should not need this./// /// @param nameHash The pre-registration hash as computed by computePreregHash()/// @return timestamp The timestamp of the pre-registration entry./// @return lockupId The lockup ID that is associated with the pre-registration.functiongetPrereg(bytes32 nameHash) externalviewreturns (uint64 timestamp,
uint64 lockupId
);
/// Check if a given address is whitelisted for registration right now./// /// @param addr The address to check./// @return Whether or not the address is whitelisted.functionisAddressWhitelisted(address addr) externalviewreturns (bool);
/// Check whether the address whitelist is currently activefunctionisRegistrationWhitelistActive() externalviewreturns (bool);
/// Get the address which is currently delegated as the admin.functiongetAdmin() externalviewreturns (address);
// Administrative functions/// Set the administrator address (owner only)functionsetAdmin(address admin) external;
/// Set the price halving time (admin only)functionsetPricer(address pricer) external;
/// Change the effective lockbox (for upgradability)functionsetLockbox(address lbox) external;
/// A struct for adding and removing addresses from the whiteliststructAllowedAddress {
/// The relevant addressaddress addr;
/// True if the address is being added, false if removedbool allowed;
}
/// Update the list of addresses which are allowed to register domains (admin only)./// @param allowed The list of addresses to add/remove/// @param activate If true, the whitelist begins enforcing, if false, then it stops enforcing.functionupdateRegistrationWhitelist(AllowedAddress[] calldata allowed, bool activate) external;
/// Blacklist or unblacklist a domain (admin only)/// /// @param id The ID of the domain or subdomain/// @param blacklisted True to blacklist, false to unblacklistfunctionsetDomainBlacklisted(uint64 id, bool blacklisted) external;
}
Contract Source Code
File 7 of 9: IdProvider.sol
// SPDX-License-Identifier: MIT OR Apache-2pragmasolidity ^0.8.24;libraryIdProvider{
structIds {
mapping(uint=>uint) freeList;
uint nextId;
}
// Initialize the state for IDManagerfunctioninit(Ids storageself) internal{
self.nextId =1; // Start the ID counter from 1 (or whatever number you'd like)
}
// Take an ID, either from the free list or by incrementing the next IDfunctiontake(Ids storageself) internalreturns (uint id) {
id =self.freeList[0];
if (id >0) {
// Reuse an ID from the free listself.freeList[0] =self.freeList[id];
deleteself.freeList[id];
} else {
// No free IDs, use the next available ID
id =self.nextId++;
}
}
// Release an ID, adding it to the free listfunctionrelease(Ids storageself, uint id) internal{
uint fdid =self.freeList[0];
if (fdid >0) {
self.freeList[id] = fdid;
}
self.freeList[0] = id;
}
}
Contract Source Code
File 8 of 9: Ownable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)pragmasolidity ^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.
*/abstractcontractOwnableisContext{
addressprivate _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/errorOwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/errorOwnableInvalidOwner(address owner);
eventOwnershipTransferred(addressindexed previousOwner, addressindexed 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.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
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.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
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) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Contract Source Code
File 9 of 9: PNS.sol
// SPDX-License-Identifier: MIT OR Apache-2pragmasolidity ^0.8.24;import"@openzeppelin/contracts/access/Ownable.sol";
import"./external/ILockBox.sol";
import"./interface/IPNS.sol";
import"./interface/IDomainPricer.sol";
import"./libraries/IdProvider.sol";
contractPNSisOwnable, IPNS{
usingIdProviderforIdProvider.Ids;
/// How long a preregistration survives for, before it is invalidateduint64publicconstant PREREG_LIFETIME_SECONDS =60*60*24;
/// The lockbox of which the lockup holder is the owner and controller of the domain.
ILockBox private self_lockboxContract;
/// An ID provider which reuses IDs when possible
IdProvider.Ids private self_ids;
/// Time of the last domain preregistrationuint64private self_lastRegTimeSec;
/// Price of the previous registrationuint256private self_lastRegPrice =1;
/// Pricer for domains
IDomainPricer private self_pricer;
/// A mapping of domains by IDmapping(uint64=> Domain) private self_domainById;
/// Mapping of domain ID by lockup ID, subdomains are excludedmapping(uint64=>uint64) private self_domainByLockupId;
/// Mapping of domain ID by fully qualified domain name (excluding .pkt. suffix)mapping(bytes=>uint64) private self_domainByFQDN;
/// The pre-registration structurestructPrereg {
/// The time when the pre-registration took place.uint64 timestamp;
/// The lockup ID which pre-registered.uint64 lockupId;
}
/// Pre-registration (to avoid getting front-run by someone else during registration)/// The key is the keccak256 hash of abi.encode(name,lockupId,records) where records/// are the initial records to store when registering.mapping(bytes32=> Prereg) private self_preregs;
/// A list of addresses that are authorized to register domains at this timemapping(address=>bool) private self_registrationWhitelist;
/// If true, the registration whitelist is currently activeboolprivate self_registrationWhitelistActive;
/// The administrator address, this address can be configured by the owner.addressprivate self_admin;
/// @param _lockboxContract The contract of the PKT LockBox/// @param _whitelistActive True if a registration whitelist should be set as active now.constructor(ILockBox _lockboxContract, bool _whitelistActive, address pricer) Ownable(msg.sender) {
self_lockboxContract = _lockboxContract;
self_registrationWhitelistActive = _whitelistActive;
self_admin =msg.sender;
self_ids.init();
self_pricer = IDomainPricer(pricer);
}
functioncurrentMinLockup() externalviewoverridereturns (uint256 price) {
return self_pricer.getPrice(self_lastRegPrice, self_lastRegTimeSec);
}
functiongetLockupValuation(uint256 lockupId) publicoverrideviewreturns (uint256) {
(
uint256 amountAsset,
,
uint256 lpTokenValuation,
,
,
,
,
,
) = self_lockboxContract.lockups(lockupId);
return amountAsset + lpTokenValuation;
}
functioncomputePreregHash(uint64 lockupId,
bytescalldata name,
bytescalldata records
) publicoverridepurereturns (bytes32) {
returnkeccak256(abi.encode(name,lockupId,records));
}
functionpreregister(bytes32 nameHash, uint64 lockupId) externaloverride{
_checkAuthorizedForLockup(msg.sender, lockupId);
require(self_domainByLockupId[lockupId] ==0, "Lockup already used for another domain");
if (self_registrationWhitelistActive) {
require(self_registrationWhitelist[msg.sender], "Only whitelisted addresses can register at this time");
}
{
uint256 value = getLockupValuation(lockupId);
require(value >= getUpdateCurrentMinLockup(), "Lockup does not have enough value of assets");
self_lastRegTimeSec =uint64(block.timestamp);
self_lastRegPrice = value;
}
{
Prereg storage pr = self_preregs[nameHash];
pr.lockupId = lockupId;
pr.timestamp =uint64(block.timestamp);
}
emit Preregister(msg.sender, nameHash, lockupId);
}
functiondestroyPrereg(bytes32 nameHash, uint64 lockupId) externaloverride{
Prereg storage pr = self_preregs[nameHash];
require(pr.lockupId == lockupId, "Mismatching lockup id");
if (uint64(block.timestamp) < pr.timestamp + PREREG_LIFETIME_SECONDS) {
uint256 value = getLockupValuation(lockupId);
if (value >0) {
_checkAuthorizedForLockup(msg.sender, lockupId);
}
}
delete self_preregs[nameHash];
emit DestroyPrereg(msg.sender, nameHash, lockupId);
}
functionregister(uint64 lockupId,
bytescalldata name,
bytescalldata records
) externaloverride{
require(name.length>0, "Name cannot be empty");
for (uint i =0; i < name.length; i++) {
// This is the only restriction we actually enforce at the contract level// because a name registered with dots in it will interfere with someone// else creating a subdomain.// All other restrictions are handled by the resolver.// for example: a domain with a \0 in it could be registered but the resolvers will ignore it.require(name[i] !='.', "Names cannot have dots in them");
}
{
bytes32 nameHash = computePreregHash(lockupId, name, records);
Prereg memory pr = self_preregs[nameHash];
require(pr.lockupId == lockupId, "Must use same lockup when registering as preregistering");
require(block.timestamp> pr.timestamp &&block.timestamp< pr.timestamp + PREREG_LIFETIME_SECONDS,
"Domain must be pre-registered first");
delete self_preregs[nameHash];
}
{
uint256 value = getLockupValuation(lockupId);
require(value >0, "Cannot register a domain with a lockup which has been released");
}
require(self_domainByLockupId[lockupId] ==0, "Lockbox already used for another domain");
require(self_domainByFQDN[name] ==0, "Domain already registered");
{
uint64 id =uint64(self_ids.take());
Domain storage dom = self_domainById[id];
dom.name= name;
dom.owner = lockupId;
dom.records = records;
dom.subdomains =0;
dom.blacklisted =false;
self_domainByLockupId[lockupId] = id;
self_domainByFQDN[name] = id;
emit Register(msg.sender, id, lockupId, name, records);
}
}
functionownerLockup(uint64 id) publicviewoverridereturns (uint64) {
Domain memory dom = self_domainById[id];
require(self_domainByFQDN[dom.name] == id, "Domain does not exist");
uint64 owner = dom.owner;
if (dom.subdomains ==0xff) {
owner = self_domainById[dom.owner].owner;
}
return owner;
}
functionauthorizedFor(uint64 id, address who) publicviewoverridereturns (bool) {
uint64 lockupId = ownerLockup(id);
return _isAuthorizedForLockup(who, lockupId);
}
functionupdateRecords(uint64 id,
bytescalldata records
) publicoverride{
Domain storage dom = self_domainById[id];
require(self_domainByFQDN[dom.name] == id, "Domain does not exist");
uint64 owner = dom.owner;
if (dom.subdomains ==0xff) {
owner = self_domainById[dom.owner].owner;
}
_checkAuthorizedForLockup(msg.sender, owner);
dom.records = records;
emit UpdateRecords(msg.sender, id, records);
}
functiontakeover(uint64 id,
uint64 newLockupId,
bytescalldata records
) externaloverride{
{
uint256 value = getLockupValuation(newLockupId);
require(value >= getUpdateCurrentMinLockup(), "Lockup does not have enough value of assets");
self_lastRegTimeSec =uint64(block.timestamp);
self_lastRegPrice = value;
}
{
Domain storage dom = self_domainById[id];
require(self_domainByFQDN[dom.name] == id, "Domain does not exist");
require(dom.subdomains <0xff, "Can't takeover a subdomain");
uint64 oldLockupId = dom.owner;
require(getLockupValuation(oldLockupId) ==0|| _isAuthorizedForLockup(msg.sender, oldLockupId),
"Can only takeover a domain if the lockup was released or you're authorized by the owner");
dom.owner = newLockupId;
if (records.length>0) {
dom.records = records;
}
emit Takeover(msg.sender, id, oldLockupId, newLockupId, records);
}
}
functioncreateSubdomain(uint64 parentId,
bytescalldata subdomainLabel,
bytescalldata records
) publicoverride{
bytesmemory fqdn;
bool blacklisted;
{
Domain storage dom = self_domainById[parentId];
require(self_domainByFQDN[dom.name] == parentId, "Parent domain does not exist");
// This also triggers is someone tries to make 255 subdomainsrequire(dom.subdomains <0xfe, "Can't make a subdomain of a subdomain");
_checkAuthorizedForLockup(msg.sender, dom.owner);
dom.subdomains++;
fqdn =bytes.concat(subdomainLabel, bytes("."), dom.name);
blacklisted = dom.blacklisted;
require(self_domainByFQDN[fqdn] ==0, "Subdomain already exists");
}
{
uint64 id =uint64(self_ids.take());
self_domainByFQDN[fqdn] = id;
Domain storage dom = self_domainById[id];
dom.subdomains =0xff;
dom.name= fqdn;
dom.owner = parentId;
dom.records = records;
dom.blacklisted = blacklisted;
emit CreateSubdomain(msg.sender, id, parentId, fqdn, records);
}
}
functiondestroy(uint64 id) publicoverride{
Domain storage dom = self_domainById[id];
require(self_domainByFQDN[dom.name] == id, "Domain does not exist");
uint64 lockup;
if (dom.subdomains ==0xff) {
Domain storage parent = self_domainById[dom.owner];
lockup = parent.owner;
parent.subdomains--;
} else {
require(dom.subdomains ==0, "Can't destroy a domain which still has subdomains");
lockup = dom.owner;
delete self_domainByLockupId[lockup];
}
if (!_isAuthorizedForLockup(msg.sender, lockup)) {
uint256 value = getLockupValuation(lockup);
require(value ==0, "Cannot destroy someone else's domain unless their lockup is released");
}
delete self_domainByFQDN[dom.name];
delete self_domainById[id];
self_ids.release(id);
emit Destroy(msg.sender, id);
}
functionupdateMultiple(
NewSubdomain[] calldata newSubdomains,
RecordUpdate[] calldata recordUpdates,
uint64[] calldata destroyDomains
) externaloverride{
for (uint8 i =0; i < newSubdomains.length; i++) {
NewSubdomain calldata ns = newSubdomains[i];
createSubdomain(ns.parentId, ns.subdomainLabel, ns.records);
}
for (uint8 i =0; i < recordUpdates.length; i++) {
RecordUpdate calldata ru = recordUpdates[i];
updateRecords(ru.id, ru.records);
}
for (uint8 i =0; i < destroyDomains.length; i++) {
destroy(destroyDomains[i]);
}
}
// View functions/// The lockbox of which the lockup holder is the owner and controller of the domain.functionlockboxContract() externaloverrideviewreturns (ILockBox) {
return self_lockboxContract;
}
functionnextDomainId() externaloverrideviewreturns (uint) {
return self_ids.nextId;
}
functiondomainIdFreeList(uint64 last) externaloverrideviewreturns (uint) {
return self_ids.freeList[last];
}
functiongetDomain(uint64 id) externaloverrideviewreturns (uint64 owner,
uint8 subdomains,
bool blacklisted,
bytesmemory name,
bytesmemory records
) {
Domain memory dom = self_domainById[id];
owner = dom.owner;
name = dom.name;
records = dom.records;
subdomains = dom.subdomains;
blacklisted = dom.blacklisted;
}
functiongetDomains(uint64[] calldata ids) externaloverrideviewreturns (Domain[] memory out) {
out =new Domain[](ids.length);
for (uint i =0; i < ids.length; i++) {
out[i] = self_domainById[ids[i]];
}
}
functiongetDomainIdByLockupId(uint64 lockupId) externaloverrideviewreturns (uint64) {
return self_domainByLockupId[lockupId];
}
functiongetDomainIdByFQDN(bytescalldata fqdn) externaloverrideviewreturns (uint64) {
return self_domainByFQDN[fqdn];
}
functiongetPricingInfo() externaloverrideviewreturns (address pricer,
uint64 lastRegTime,
uint256 lastRegPrice
) {
pricer =address(self_pricer);
lastRegTime = self_lastRegTimeSec;
lastRegPrice = self_lastRegPrice;
}
functiongetPrereg(bytes32 nameHash) externaloverrideviewreturns (uint64 timestamp,
uint64 lockupId
) {
Prereg memory pr = self_preregs[nameHash];
lockupId = pr.lockupId;
timestamp = pr.timestamp;
}
functionisAddressWhitelisted(address addr) externaloverrideviewreturns (bool) {
return self_registrationWhitelist[addr];
}
functionisRegistrationWhitelistActive() externaloverrideviewreturns (bool) {
return self_registrationWhitelistActive;
}
functiongetAdmin() externaloverrideviewreturns (address) {
return self_admin;
}
// Administrative functionsfunctionsetAdmin(address admin) externaloverrideonlyOwner{
self_admin = admin;
}
functionsetPricer(address pricer) externaloverride{
require(msg.sender== self_admin, "Admin only");
self_pricer = IDomainPricer(pricer);
}
functionsetLockbox(address lbox) externaloverride{
require(msg.sender== self_admin, "Admin only");
self_lockboxContract = ILockBox(lbox);
}
functionupdateRegistrationWhitelist(AllowedAddress[] calldata allowed, bool activate) externaloverride{
require(msg.sender== self_admin, "Admin only");
for (uint8 i =0; i < allowed.length; i++) {
if (allowed[i].allowed) {
self_registrationWhitelist[allowed[i].addr] =true;
} else {
delete self_registrationWhitelist[allowed[i].addr];
}
}
self_registrationWhitelistActive = activate;
}
functionsetDomainBlacklisted(uint64 id, bool blacklisted) externaloverride{
require(msg.sender== self_admin, "Admin only");
Domain storage dom = self_domainById[id];
require(self_domainByFQDN[dom.name] == id, "Domain does not exist");
dom.blacklisted = blacklisted;
emit Blacklist(msg.sender, id, blacklisted);
}
// Privatefunction_isAuthorizedForLockup(address who, uint64 lockupId) privateviewreturns (bool) {
address owner = self_lockboxContract.ownerOf(lockupId);
if (owner == who) {
returntrue;
}
if (self_lockboxContract.getApproved(lockupId) == who) {
returntrue;
}
return self_lockboxContract.isApprovedForAll(owner, who);
}
function_checkAuthorizedForLockup(address who, uint64 lockupId) privateview{
require(_isAuthorizedForLockup(who, lockupId), "Unauthorized");
}
functiongetUpdateCurrentMinLockup() privatereturns (uint256 price) {
return self_pricer.getUpdatePrice(self_lastRegPrice, self_lastRegTimeSec);
}
}