126 lines
2.9 KiB
Solidity
126 lines
2.9 KiB
Solidity
|
/*
|
||
|
Vault
|
||
|
|
||
|
SPDX-License-Identifier: CC0-1.0
|
||
|
*/
|
||
|
|
||
|
pragma solidity ^0.8.0;
|
||
|
|
||
|
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
||
|
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
|
||
|
|
||
|
import "./IERC721Holder.sol";
|
||
|
|
||
|
/**
|
||
|
* @title Vault
|
||
|
*
|
||
|
* @notice this contract implements an example "holder" for the proposed
|
||
|
* held token ERC standard.
|
||
|
|
||
|
* This example vault contract allows a user to lock up an ERC721 token for
|
||
|
* a specified period of time, while still reporting the functional owner
|
||
|
*/
|
||
|
contract Vault is ERC165, IERC721Holder {
|
||
|
// members
|
||
|
IERC721 public token;
|
||
|
uint256 public timelock;
|
||
|
mapping(uint256 => address) public owners;
|
||
|
mapping(uint256 => uint256) public locks;
|
||
|
mapping(address => uint256) public balances;
|
||
|
|
||
|
/**
|
||
|
* @param token_ address of token to be stored in vault
|
||
|
* @param timelock_ duration in seconds that tokens will be locked
|
||
|
*/
|
||
|
constructor(address token_, uint256 timelock_) {
|
||
|
token = IERC721(token_);
|
||
|
timelock = timelock_;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc IERC165
|
||
|
*/
|
||
|
function supportsInterface(bytes4 interfaceId)
|
||
|
public
|
||
|
view
|
||
|
virtual
|
||
|
override(ERC165, IERC165)
|
||
|
returns (bool)
|
||
|
{
|
||
|
return
|
||
|
interfaceId == type(IERC721Holder).interfaceId ||
|
||
|
super.supportsInterface(interfaceId);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc IERC721Holder
|
||
|
*/
|
||
|
function heldOwnerOf(address tokenAddress, uint256 tokenId)
|
||
|
external
|
||
|
view
|
||
|
override
|
||
|
returns (address)
|
||
|
{
|
||
|
require(
|
||
|
tokenAddress == address(token),
|
||
|
"ERC721Vault: invalid token address"
|
||
|
);
|
||
|
return owners[tokenId];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc IERC721Holder
|
||
|
*/
|
||
|
function heldBalanceOf(address tokenAddress, address owner)
|
||
|
external
|
||
|
view
|
||
|
override
|
||
|
returns (uint256)
|
||
|
{
|
||
|
require(
|
||
|
tokenAddress == address(token),
|
||
|
"ERC721Vault: invalid token address"
|
||
|
);
|
||
|
return balances[owner];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice deposit and lock a token for a period of time
|
||
|
* @param tokenId ID of token to deposit
|
||
|
*/
|
||
|
function deposit(uint256 tokenId) public {
|
||
|
require(
|
||
|
msg.sender == token.ownerOf(tokenId),
|
||
|
"ERC721Vault: sender does not own token"
|
||
|
);
|
||
|
|
||
|
owners[tokenId] = msg.sender;
|
||
|
locks[tokenId] = block.timestamp + timelock;
|
||
|
balances[msg.sender]++;
|
||
|
|
||
|
emit Hold(msg.sender, address(token), tokenId);
|
||
|
|
||
|
token.transferFrom(msg.sender, address(this), tokenId);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice withdraw token after timelock has elapsed
|
||
|
* @param tokenId ID of token to withdraw
|
||
|
*/
|
||
|
function withdraw(uint256 tokenId) public {
|
||
|
require(
|
||
|
msg.sender == owners[tokenId],
|
||
|
"ERC721Vault: sender does not own token"
|
||
|
);
|
||
|
require(block.timestamp > locks[tokenId], "ERC721Vault: token is locked");
|
||
|
|
||
|
delete owners[tokenId];
|
||
|
delete locks[tokenId];
|
||
|
balances[msg.sender]--;
|
||
|
|
||
|
emit Release(msg.sender, address(token), tokenId);
|
||
|
|
||
|
token.safeTransferFrom(address(this), msg.sender, tokenId);
|
||
|
}
|
||
|
}
|