Skip to content

Instantly share code, notes, and snippets.

@WPSmartContracts
Created September 15, 2023 17:21
Show Gist options
  • Save WPSmartContracts/a0714805c19fe633168fc258b5d7acf8 to your computer and use it in GitHub Desktop.
Save WPSmartContracts/a0714805c19fe633168fc258b5d7acf8 to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "./Dependencies.sol";
/**
* @title CoconutVault
* @dev A smart contract for securely storing and managing native coins, ERC-20 tokens,
* ERC-721 NFTs, and ERC-1155 NFTs. It includes functionalities for deposits,
* withdrawals, and trusted account management in case of emergencies.
*/
contract CoconutVault is Ownable, ReentrancyGuard {
using SafeERC20 for IERC20;
// Timestamp of the last owner activity
uint256 public lastActivity;
// Expiration time in seconds since the last owner activity
uint256 public expirationLapse;
// Mapping of trusted accounts that can withdraw the vault assets in case of expiration
mapping(address => bool) public trustedAccounts;
// Event last owner activity log
event LastActivityUpdate(uint256 timestamp);
// Events for asset deposits
event CoinsReceived(address indexed sender, uint256 value);
event ERC20Received(address indexed sender, address indexed token, uint256 amount);
event ERC721Received(address indexed sender, address indexed nft, uint256 id);
event ERC1155Received(address indexed sender, address indexed nft, uint256 id, uint256 qty);
// Events for asset withdrawals
event CoinsWithdrawn(address indexed beneficiary, uint256 value);
event ERC20Withdrawn(address indexed beneficiary, address indexed token, uint256 amount);
event ERC721Withdrawn(address indexed beneficiary, address indexed nft, uint256 indexed id);
event ERC1155Withdrawn(address indexed beneficiary, address indexed nft, uint256 indexed id, uint256 qty);
event TrustedAccountChanged(address indexed trustedAccount, bool trusted);
/**
* @dev Constructor for CoconutVault.
* @param owner_ The address of the owner of the contract.
*/
constructor (address owner_) Ownable() {
lastActivity = block.timestamp;
// The default expiration lapse is one year (in seconds)
expirationLapse = 31536000;
transferOwnership(owner_);
}
// Owner methods:
/**
* @dev Owner method to manually set the expiration time.
* @param lapse The new expiration time in seconds.
*/
function setExpiration(uint256 lapse) external onlyOwner {
require(lapse > 0, "Expiration time has to be positive");
_updateActivity();
expirationLapse = lapse;
}
/**
* @dev Owner method to manually update the last activity timestamp.
*/
function keepAlive() external onlyOwner {
_updateActivity();
}
// Deposit functions:
/**
* @dev Allows anyone to deposit native coins into the vault.
* Update the last activity in the contract if the sender is the owner
*/
function deposit() public payable nonReentrant {
emit CoinsReceived(msg.sender, msg.value);
_updateActivity();
}
/**
* @dev Fallback function to handle transfers of Ether.
* Update the last activity in the contract if the sender is the owner
*/
receive() external payable {
deposit();
}
/**
* @dev Allows users to deposit ERC-20 tokens into the vault.
* @param token_ The ERC-20 token contract address.
* @param amount_ The amount of tokens to deposit.
* Update the last activity in the contract if the sender is the owner
* Token transfer directly to the contract can be done, but the last activity will not be updated if such transfers are made by the owner
*/
function tokenDeposit(IERC20 token_, uint256 amount_) external nonReentrant {
require(amount_ > 0, "Transfer amount has to be positive");
_updateActivity();
emit ERC20Received(msg.sender, address(token_), amount_);
token_.safeTransferFrom(msg.sender, address(this), amount_);
}
/**
* @dev Allows users to deposit NFT ERC-721 tokens into the vault.
* @param token_ The ERC-721 token contract address.
* @param id_ The NFT ID to deposit.
* Update the last activity in the contract if the sender is the owner
* Token transfer directly to the contract can be done, but the last activity will not be updated if such transfers are made by the owner
*/
function erc721Deposit(IERC721 token_, uint256 id_) external nonReentrant {
require(id_ > 0, "NFT ID has to be positive");
_updateActivity();
emit ERC721Received(msg.sender, address(token_), id_);
token_.safeTransferFrom(msg.sender, address(this), id_);
}
/**
* @dev Allows users to deposit NFT ERC-1155 tokens into the vault.
* @param token_ The ERC-1155 token contract address.
* @param id_ The NFT ID to deposit.
* @param qty_ The number of NFT items to deposit.
* Update the last activity in the contract if the sender is the owner
* Token transfer directly to the contract can be done, but the last activity will not be updated if such transfers are made by the owner
*/
function erc1155Deposit(IERC1155 token_, uint256 id_, uint256 qty_) external nonReentrant {
require(id_ > 0, "NFT ID has to be positive");
require(qty_ > 0, "Quatity has to be positive");
_updateActivity();
emit ERC1155Received(msg.sender, address(token_), id_, qty_);
token_.safeTransferFrom(msg.sender, address(this), id_, qty_, '0x');
}
// Withdraw fundtions:
/**
* @dev Allows the owner or a trusted account after expiration to withdraw native coins.
* @param value_ The amount of native coins to withdraw.
* Update the last activity in the contract if the sender is the owner
*/
function withdraw(uint256 value_) external nonReentrant canWithdraw {
require(value_ > 0, "Transfer value has to be positive");
_updateActivity();
emit CoinsWithdrawn(msg.sender, value_);
(bool success, ) = msg.sender.call{value:value_}("");
require(success, "Transfer failed.");
}
/**
* @dev Allows the owner or a trusted account after expiration to withdraw ERC-20 tokens
* @param token_ The ERC-20 token to withdraw.
* @param amount_ The amount of tokens to withdraw.
* Update the last activity in the contract if the sender is the owner
*/
function tokenWithdraw(IERC20 token_, uint256 amount_) external nonReentrant canWithdraw {
require(amount_ > 0, "Transfer amount has to be positive");
_updateActivity();
emit ERC20Withdrawn(msg.sender, address(token_), amount_);
token_.safeTransfer(msg.sender, amount_);
}
/**
* @dev Allows the owner or a trusted account after expiration to withdraw ERC-721 tokens
* @param token_ The ERC-721 token to withdraw.
* @param id_ The NFT ID of tokens to withdraw.
* Update the last activity in the contract if the sender is the owner
*/
function erc721Withdraw(IERC721 token_, uint256 id_) external nonReentrant canWithdraw {
require(id_ > 0, "NFT ID has to be positive");
_updateActivity();
emit ERC721Withdrawn(msg.sender, address(token_), id_);
token_.safeTransferFrom(address(this), msg.sender, id_);
}
/**
* @dev Allows the owner or a trusted account after expiration to withdraw ERC-1155 tokens
* @param token_ The ERC-1155 token to withdraw.
* @param id_ The NFT ID of tokens to withdraw.
* @param qty_ The number of NFT Items to withdraw.
* Update the last activity in the contract if the sender is the owner
*/
function erc1155Withdraw(IERC1155 token_, uint256 id_, uint256 qty_) external nonReentrant canWithdraw {
require(id_ > 0, "NFT ID has to be positive");
require(qty_ > 0, "Quatity has to be positive");
_updateActivity();
emit ERC1155Withdrawn(msg.sender, address(token_), id_, qty_);
token_.safeTransferFrom(address(this), msg.sender, id_, qty_, "0x");
}
// Manage trusted accounts:
/**
* @dev Add or update a trusted account to withdraw Coins, ERC-20, ERC-721 or ERC-1155 in case of emergency
* @param account_ The address of the beneficiary.
* @param trusted_ is the account trusted or not
*/
function changeTrustedAccount(address account_, bool trusted_) external onlyOwner {
require(account_ != address(0), "Trusted account cannot be the zero address");
_updateActivity();
emit TrustedAccountChanged(account_, trusted_);
trustedAccounts[account_] = trusted_;
}
/**
* @dev Internal function to update the last activity timestamp.
*/
function _updateActivity() internal {
if (msg.sender == this.owner()) {
lastActivity = block.timestamp;
emit LastActivityUpdate(block.timestamp);
}
}
/**
* @dev Modifier to check if caller is owner or a trusted account and the contract has expired
*/
modifier canWithdraw() {
require(msg.sender == this.owner() || (trustedAccounts[msg.sender] && block.timestamp - lastActivity > expirationLapse), "Caller cannot withdraw");
_;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment