You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
94 lines
3.2 KiB
Solidity
94 lines
3.2 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
// ERC721A Contracts v4.2.3
|
|
// Creator: Chiru Labs
|
|
|
|
pragma solidity ^0.8.4;
|
|
|
|
import './IERC4907A.sol';
|
|
import '../ERC721A.sol';
|
|
|
|
/**
|
|
* @title ERC4907A
|
|
*
|
|
* @dev [ERC4907](https://eips.ethereum.org/EIPS/eip-4907) compliant
|
|
* extension of ERC721A, which allows owners and authorized addresses
|
|
* to add a time-limited role with restricted permissions to ERC721 tokens.
|
|
*/
|
|
abstract contract ERC4907A is ERC721A, IERC4907A {
|
|
// The bit position of `expires` in packed user info.
|
|
uint256 private constant _BITPOS_EXPIRES = 160;
|
|
|
|
// Mapping from token ID to user info.
|
|
//
|
|
// Bits Layout:
|
|
// - [0..159] `user`
|
|
// - [160..223] `expires`
|
|
mapping(uint256 => uint256) private _packedUserInfo;
|
|
|
|
/**
|
|
* @dev Sets the `user` and `expires` for `tokenId`.
|
|
* The zero address indicates there is no user.
|
|
*
|
|
* Requirements:
|
|
*
|
|
* - The caller must own `tokenId` or be an approved operator.
|
|
*/
|
|
function setUser(
|
|
uint256 tokenId,
|
|
address user,
|
|
uint64 expires
|
|
) public virtual override {
|
|
// Require the caller to be either the token owner or an approved operator.
|
|
address owner = ownerOf(tokenId);
|
|
if (_msgSenderERC721A() != owner)
|
|
if (!isApprovedForAll(owner, _msgSenderERC721A()))
|
|
if (getApproved(tokenId) != _msgSenderERC721A()) _revert(SetUserCallerNotOwnerNorApproved.selector);
|
|
|
|
_packedUserInfo[tokenId] = (uint256(expires) << _BITPOS_EXPIRES) | uint256(uint160(user));
|
|
|
|
emit UpdateUser(tokenId, user, expires);
|
|
}
|
|
|
|
/**
|
|
* @dev Returns the user address for `tokenId`.
|
|
* The zero address indicates that there is no user or if the user is expired.
|
|
*/
|
|
function userOf(uint256 tokenId) public view virtual override returns (address) {
|
|
uint256 packed = _packedUserInfo[tokenId];
|
|
assembly {
|
|
// Branchless `packed *= (block.timestamp <= expires ? 1 : 0)`.
|
|
// If the `block.timestamp == expires`, the `lt` clause will be true
|
|
// if there is a non-zero user address in the lower 160 bits of `packed`.
|
|
packed := mul(
|
|
packed,
|
|
// `block.timestamp <= expires ? 1 : 0`.
|
|
lt(shl(_BITPOS_EXPIRES, timestamp()), packed)
|
|
)
|
|
}
|
|
return address(uint160(packed));
|
|
}
|
|
|
|
/**
|
|
* @dev Returns the user's expires of `tokenId`.
|
|
*/
|
|
function userExpires(uint256 tokenId) public view virtual override returns (uint256) {
|
|
return _packedUserInfo[tokenId] >> _BITPOS_EXPIRES;
|
|
}
|
|
|
|
/**
|
|
* @dev Override of {IERC165-supportsInterface}.
|
|
*/
|
|
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721A, IERC721A) returns (bool) {
|
|
// The interface ID for ERC4907 is `0xad092b5c`,
|
|
// as defined in [ERC4907](https://eips.ethereum.org/EIPS/eip-4907).
|
|
return super.supportsInterface(interfaceId) || interfaceId == 0xad092b5c;
|
|
}
|
|
|
|
/**
|
|
* @dev Returns the user address for `tokenId`, ignoring the expiry status.
|
|
*/
|
|
function _explicitUserOf(uint256 tokenId) internal view virtual returns (address) {
|
|
return address(uint160(_packedUserInfo[tokenId]));
|
|
}
|
|
}
|