Description:
ERC20 token contract with Factory capabilities. Standard implementation for fungible tokens on Ethereum.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/MemeVestingBiweekly.sol": {
"content": "// SPDX-License-Identifier: MIT\r
pragma solidity 0.8.30;\r
\r
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";\r
\r
/**\r
* @title MemeVesting (Bi-Weekly, 11 Months)\r
* @notice\r
* • After `fund(amount)` (callable once), the beneficiary may withdraw\r
* 1/TOTAL_PERIODS of `amount` every 14 days.\r
* • `amount` must be divisible by TOTAL_PERIODS.\r
* • MemeDAO wallet (`dao`) can withdraw any amount at any time via `daoWithdraw`.\r
*/\r
contract MemeVesting {\r
/* ────────── Immutable configuration ────────── */\r
IERC20 public immutable token;\r
address public immutable beneficiary;\r
address public immutable dao;\r
\r
/* ────────── Vesting state (set in fund()) ────────── */\r
uint256 public startTime; // timestamp of funding\r
uint256 public initialAmount; // total tokens funded\r
uint256 public biweeklyAmount; // initialAmount / TOTAL_PERIODS\r
uint8 public periodsClaimed; // 0-TOTAL_PERIODS\r
bool public funded;\r
\r
uint256 private constant PERIOD = 14 days;\r
uint8 private constant TOTAL_PERIODS = 24;\r
\r
/* ────────── Constructor ────────── */\r
constructor(IERC20 _token, address _beneficiary, address _dao) {\r
require(_beneficiary != address(0), "Beneficiary zero addr");\r
require(_dao != address(0), "DAO zero addr");\r
\r
token = _token;\r
beneficiary = _beneficiary;\r
dao = _dao;\r
}\r
\r
/* ────────── Funding (one-time) ────────── */\r
function fund(uint256 amount) external {\r
require(!funded, "Already funded");\r
require(amount > 0, "Amount is zero");\r
require(amount % TOTAL_PERIODS == 0, "Amount not divisible by periods");\r
\r
require(token.transferFrom(msg.sender, address(this), amount),\r
"transferFrom failed");\r
\r
funded = true;\r
startTime = block.timestamp;\r
initialAmount = amount;\r
biweeklyAmount = amount / TOTAL_PERIODS;\r
}\r
\r
/* ────────── Modifiers ────────── */\r
modifier onlyBeneficiary() { require(msg.sender == beneficiary, "Not beneficiary"); _; }\r
modifier onlyDAO() { require(msg.sender == dao, "Not MemeDAO"); _; }\r
modifier whenFunded() { require(funded, "Not funded yet"); _; }\r
\r
/* ────────── View helper ────────── */\r
/// @return secondsLeft 0 if a withdrawal is currently possible,\r
/// or seconds until the next tranche unlocks.\r
function timeUntilNextWithdraw() external view returns (uint256 secondsLeft) {\r
if (!funded || periodsClaimed >= TOTAL_PERIODS) {\r
return 0; // not funded yet OR everything already claimed\r
}\r
\r
uint256 nextUnlock = startTime + (uint256(periodsClaimed) + 1) * PERIOD;\r
\r
if (block.timestamp >= nextUnlock) {\r
return 0; // tranche already unlocked\r
}\r
return nextUnlock - block.timestamp;\r
}\r
\r
/* ────────── Beneficiary interface ────────── */\r
function withdraw() external onlyBeneficiary whenFunded {\r
uint8 elapsed = uint8((block.timestamp - startTime) / PERIOD);\r
if (elapsed > TOTAL_PERIODS) elapsed = TOTAL_PERIODS;\r
\r
require(elapsed > periodsClaimed, "Nothing to withdraw");\r
\r
uint8 periodsOwed = elapsed - periodsClaimed;\r
uint256 amount = uint256(periodsOwed) * biweeklyAmount;\r
\r
periodsClaimed = elapsed;\r
require(token.transfer(beneficiary, amount), "transfer failed");\r
}\r
\r
/* ────────── MemeDAO override interface ────────── */\r
function daoWithdraw(uint256 amount) external onlyDAO whenFunded {\r
require(token.transfer(dao, amount), "transfer failed");\r
}\r
}\r
"
},
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": []
}
}}
Submitted on: 2025-09-19 15:10:27
Comments
Log in to comment.
No comments yet.