Skip to content

Instantly share code, notes, and snippets.

@WPSmartContracts
Last active September 25, 2023 19:54
Show Gist options
  • Save WPSmartContracts/9f6c26b918acfde0bf86a5a31f42079a to your computer and use it in GitHub Desktop.
Save WPSmartContracts/9f6c26b918acfde0bf86a5a31f42079a to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
// Standard OpenZeppelin Contracts
import "../utils/Context.sol";
import "../access/Ownable.sol";
import "../security/ReentrancyGuard.sol";
import "../token/ERC20/IERC20.sol";
import "../token/ERC721/IERC721.sol";
import "../token/ERC1155/IERC1155.sol";
import "../utils/Address.sol";
import "../token/ERC20/utils/SafeERC20.sol";
import "../token/ERC721/utils/ERC721Holder.sol";
import "../token/ERC1155/utils/ERC1155Holder.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 ERC721Holder, ERC1155Holder, 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 for recording the last owner activity timestamp
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 for changing trusted accounts
event TrustedAccountChanged(address indexed trustedAccount, bool trusted);
/**
* @dev Constructor for CoconutVault.
* @param expirationTime_ Default expiration lapse in seconds
* @param owner_ The address of the owner of the contract.
*/
constructor (uint256 expirationTime_, address owner_) Ownable() {
require(expirationTime_ > 0, "Expiration time must be positive");
lastActivity = block.timestamp;
expirationLapse = expirationTime_;
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) public onlyOwner {
require(lapse > 0, "Expiration time must 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.
* Updates the last activity in the contract if the sender is the owner.
*/
function deposit() public payable nonReentrant {
require(msg.value > 0, "Transfer value must be positive");
emit CoinsReceived(msg.sender, msg.value);
_updateActivity();
}
/**
* @dev Fallback function to handle transfers of Ether.
* Updates 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.
* Updates the last activity in the contract if the sender is the owner.
* Token transfers 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 must 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.
* Updates the last activity in the contract if the sender is the owner.
* Token transfers 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 must 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.
* Updates the last activity in the contract if the sender is the owner.
* Token transfers 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 must be positive");
require(qty_ > 0, "Quantity must be positive");
_updateActivity();
emit ERC1155Received(msg.sender, address(token_), id_, qty_);
token_.safeTransferFrom(msg.sender, address(this), id_, qty_, '0x');
}
// Withdraw functions:
/**
* @dev Allows the owner or a trusted account after expiration to withdraw native coins.
* @param value_ The amount of native coins to withdraw.
* Updates the last activity in the contract if the sender is the owner.
*/
function withdraw(uint256 value_) external nonReentrant canWithdraw {
require(value_ > 0, "Transfer value must 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.
* Updates 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 must 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.
* Updates 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 must 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.
* Updates 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 must be positive");
require(qty_ > 0, "Quantity must 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_ Whether the account is 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 == owner()) {
lastActivity = block.timestamp;
emit LastActivityUpdate(block.timestamp);
}
}
/**
* @dev Modifier to check if the caller is the owner or a trusted account and the contract has expired.
*/
modifier canWithdraw() {
require(msg.sender == 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