Skip to content

Instantly share code, notes, and snippets.

@lukasz-glen
Created July 15, 2023 20:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lukasz-glen/c1768ecb0d5138f10d52a07c23e16140 to your computer and use it in GitHub Desktop.
Save lukasz-glen/c1768ecb0d5138f10d52a07c23e16140 to your computer and use it in GitHub Desktop.
A demonstration of approvals with expiration date for ERC721 NFTs
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
/**
* @author lukasz-glen
* @dev This is not production ready implementation.
* Just a demonstration of the idea of approvals with expiration date.
* Block numbers could be used as well.
* The same can be applied to signle approvals, token id based approvals, and others.
* The point is that bool requires 1 storage slot anyway, so it is the same cost if we put there more information.
* Approval-for-all functions from ERC721 are overriden in order to comply with ERC721 standard and approvals with expiration date.
*/
contract TimeLimitedApprove is ERC721 {
event ApprovalForAll(address indexed owner, address indexed operator, uint256 timeLimit);
// Mapping from owner to operator approvals with time limits
mapping(address => mapping(address => uint256)) private __operatorApprovals;
constructor(string memory __name, string memory __symbol) ERC721(__name, __symbol) { }
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
address owner = _msgSender();
require(owner != operator, "ERC721: approve to caller");
__operatorApprovals[owner][operator] = approved ? type(uint256).max : 0;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return __operatorApprovals[owner][operator] == type(uint256).max;
}
function setApprovalForAll(address operator, uint256 timeLimit) public virtual {
address owner = _msgSender();
require(owner != operator, "ERC721: approve to caller");
if (__operatorApprovals[owner][operator] == type(uint256).max) {
emit ApprovalForAll(owner, operator, false);
}
__operatorApprovals[owner][operator] = timeLimit;
emit ApprovalForAll(owner, operator, timeLimit);
}
function getApprovedForAllTimeLimit(address owner, address operator) public view virtual returns (uint256) {
return __operatorApprovals[owner][operator];
}
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual override returns (bool) {
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || getApprovedForAllTimeLimit(owner, spender) >= block.timestamp || getApproved(tokenId) == spender);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment