// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.18; import "./SmartAssetBase.sol"; import "../Interfaces/ISmartAssetSoulbound.sol"; /** * @title SmartAssetSoulbound. * @author Arianee - Dynamic NFTs for real-world use cases and consumer engagement (www.arianee.org). */ abstract contract SmartAssetSoulbound is SmartAssetBase, ISmartAssetSoulbound { /** * @notice SOULBOUND_FEATURE: dummy bytes32 to be used as a feature flag. It is needed to be able to use the {IERC165-supportsInterface} function. */ bytes32 public constant SOULBOUND_FEATURE = keccak256("SOULBOUND_FEATURE"); /** * @dev See {SmartAssetBase-requestToken}. */ function requestToken( uint256 tokenId, bytes calldata signature, address newOwner, bool keepTransferKey, address walletProvider ) public virtual override whenNotPaused { require(keepTransferKey == false, "SmartAssetSoulbound: Forbidden to keep the transfer key"); super.requestToken(tokenId, signature, newOwner, keepTransferKey, walletProvider); } /** * @dev See {SmartAssetBase-setTokenTransferKey}. */ function setTokenTransferKey(uint256 tokenId, address key) public virtual override isApprovedOrOwner(_msgSender(), tokenId) whenNotPaused { address owner = super.ownerOf(tokenId); address issuer = super.footprintOf(tokenId).issuer; require(owner == issuer, "SmartAssetSoulbound: Only the issuer can set the transfer key"); super.setTokenTransferKey(tokenId, key); } /** * @dev See {SmartAssetBase-_transfer} */ function _transfer(address from, address to, uint256 tokenId) internal virtual override { address owner = super.ownerOf(tokenId); address issuer = super.footprintOf(tokenId).issuer; // If the owner is NOT the issuer, the token is soulbound and the transfer can be made only by the issuer to change the owner if needed if (owner != issuer) { require(issuer == _msgSender(), "SmartAssetSoulbound: Only the issuer can transfer the token"); } /** * If the previous condition has not been hit, the owner IS the issuer and the token is not soulbound yet or not anymore for a limited time. * This is either the first transfer of the token to its first "real" owner or a recovery request made by the issuer on the behalf of the owner (i.e the owner lost his wallet and wants to recover his token) */ super._transfer(from, to, tokenId); } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(ISmartAssetSoulbound).interfaceId || super.supportsInterface(interfaceId); } }