DCIPs/assets/eip-5501/contracts/ERC5501Terminable.sol

137 lines
4.0 KiB
Solidity

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;
import "./ERC5501.sol";
import "./IERC5501Terminable.sol";
/**
* @dev Implementation of Terminable extension of https://eips.ethereum.org/EIPS/eip-5501 with OpenZeppelin ERC721 version.
*/
contract ERC5501Terminable is IERC5501Terminable, ERC5501 {
/**
* @dev Structure to hold agreements from both parties to terminate a borrow.
* @notice If both parties agree, it is possible to modify UserInfo even before it expires.
* In such case, isBorrowed status is reverted to false.
*/
struct BorrowTerminationInfo {
bool lenderAgreement;
bool borrowerAgreement;
}
// Mapping from token ID to BorrowTerminationInfo
mapping(uint256 => BorrowTerminationInfo) internal _borrowTerminations;
/**
* @dev Initializes the contract by setting a name and a symbol to the token collection.
*/
constructor(string memory name_, string memory symbol_)
ERC5501(name_, symbol_)
{}
/**
* @dev See {IERC5501-setUser}.
*/
function setUser(
uint256 tokenId,
address user,
uint64 expires,
bool isBorrowed
) public virtual override {
super.setUser(tokenId, user, expires, isBorrowed);
delete _borrowTerminations[tokenId];
emit ResetTerminationAgreements(tokenId);
}
/**
* @dev See {IERC5501Terminable-setBorrowTermination}.
*/
function setBorrowTermination(uint256 tokenId) public virtual override {
UserInfo storage userInfo = _users[tokenId];
require(
userInfo.expires >= block.timestamp && userInfo.isBorrowed,
"ERC5501Terminable: borrow not active"
);
BorrowTerminationInfo storage terminationInfo = _borrowTerminations[
tokenId
];
if (ownerOf(tokenId) == msg.sender) {
terminationInfo.lenderAgreement = true;
emit AgreeToTerminateBorrow(tokenId, msg.sender, true);
}
if (userInfo.user == msg.sender) {
terminationInfo.borrowerAgreement = true;
emit AgreeToTerminateBorrow(tokenId, msg.sender, false);
}
}
/**
* @dev See {IERC5501Terminable-getBorrowTermination}.
*/
function getBorrowTermination(uint256 tokenId)
public
view
virtual
override
returns (bool, bool)
{
return (
_borrowTerminations[tokenId].lenderAgreement,
_borrowTerminations[tokenId].borrowerAgreement
);
}
/**
* @dev See {IERC5501Terminable-terminateBorrow}.
*/
function terminateBorrow(uint256 tokenId) public virtual override {
BorrowTerminationInfo storage info = _borrowTerminations[tokenId];
require(
info.lenderAgreement && info.borrowerAgreement,
"ERC5501Terminable: not agreed"
);
_users[tokenId].isBorrowed = false;
delete _borrowTerminations[tokenId];
emit ResetTerminationAgreements(tokenId);
emit TerminateBorrow(
tokenId,
ownerOf(tokenId),
_users[tokenId].user,
msg.sender
);
}
/**
* @dev See {EIP-165: Standard Interface Detection}.
* https://eips.ethereum.org/EIPS/eip-165
*/
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override
returns (bool)
{
return
interfaceId == type(IERC5501Terminable).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev Hook that is called after any token transfer.
* If user is set and token is borrowed, reset termination agreements.
*/
function _afterTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual override {
super._afterTokenTransfer(from, to, tokenId);
if (from != to && _users[tokenId].isBorrowed) {
delete _borrowTerminations[tokenId];
emit ResetTerminationAgreements(tokenId);
}
}
}