Ontwerp een Solidity Smart Contract voor Token Vesting en Releasebeheer
description
Met deze prompt kunnen gebruikers een robuust en veilig token vesting smart contract creëren dat is afgestemd op hun specifieke token distributiebehoeften. Het behandelt complexe vesting-schema's, gedeeltelijke releases en intrekkingsbeleid, essentieel voor het beheren van tokenallocaties in startups, DAOs of projecten. In tegenstelling tot generieke contractprompts richt deze zich specifiek op vestinglogica, wat beveiliging en flexibiliteit verbetert en veelvoorkomende kwetsbaarheden voorkomt.
prompt
Help me een Solidity smart contract te ontwikkelen voor het implementeren van token vesting en release mechanismen. Ik wil dat het contract het volgende bevat: Begunstigde adressen: <voer begunstigde adressen in>, Vesting startdatum: <voer vesting startdatum in>, Cliff duur: ...
try_prompt
generate_helper
disclaimerOnPageApi
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Hier is een voorbeeld van een Solidity-smartcontract dat voldoet aan jouw specificaties voor token vesting en release mechanismen. Het contract bevat onder andere:
- Begunstigde adressen
- Vesting startdatum (2024-07-01)
- Cliff duur van 3 maanden
- Totale vestingduur van 12 maanden
- Maandelijkse vrijgave na cliff
- Inwisselbaarheid door admin
- Beveiliging tegen veelvoorkomende kwetsbaarheden
- Ondersteuning voor gedeeltelijke releases en terugtrekking van niet-geverfde tokens
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC20 {
function transfer(address recipient, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
contract TokenVesting {
address public admin;
IERC20 public token;
address[] public beneficiaries;
uint256 public vestingStart;
uint256 public cliffDuration; // in seconds
uint256 public vestingDuration; // in seconds
uint256 public totalTokens; // totale tokens toegekend aan alle begunstigden
struct Beneficiary {
uint256 totalAllocation;
uint256 released;
bool revoked;
}
mapping(address => Beneficiary) public beneficiariesData;
// Events
event TokensReleased(address beneficiary, uint256 amount);
event BeneficiaryRevoked(address beneficiary);
event BeneficiaryAdded(address beneficiary, uint256 amount);
modifier onlyAdmin() {
require(msg.sender == admin, "Alleen admin");
_;
}
modifier onlyBeneficiary() {
require(beneficiariesData[msg.sender].totalAllocation > 0, "Geen toegang");
_;
}
constructor(
address _token,
address[] memory _beneficiaries,
uint256 _vestingStartTimestamp,
uint256 _cliffDurationInSeconds,
uint256 _vestingDurationInSeconds,
uint256[] memory _allocations
) {
require(_beneficiaries.length == _allocations.length, "Mismatch in beneficiaries en allocaties");
admin = msg.sender;
token = IERC20(_token);
beneficiaries = _beneficiaries;
vestingStart = _vestingStartTimestamp;
cliffDuration = _cliffDurationInSeconds;
vestingDuration = _vestingDurationInSeconds;
uint256 totalAlloc = 0;
for (uint256 i = 0; i < _beneficiaries.length; i++) {
beneficiariesData[_beneficiaries[i]] = Beneficiary({
totalAllocation: _allocations[i],
released: 0,
revoked: false
});
totalAlloc += _allocations[i];
emit BeneficiaryAdded(_beneficiaries[i], _allocations[i]);
}
totalTokens = totalAlloc;
}
// Toegang voor admin om tokens te reserveren voor de vesting
function revokeBeneficiary(address _beneficiary) external onlyAdmin {
Beneficiary storage ben = beneficiariesData[_beneficiary];
require(!ben.revoked, "Al ingetrokken");
ben.revoked = true;
emit BeneficiaryRevoked(_beneficiary);
}
// Bereken hoeveel tokens een begunstigde kan vrijgeven
function releasableAmount(address _beneficiary) public view returns (uint256) {
Beneficiary memory ben = beneficiariesData[_beneficiary];
if (ben.revoked) {
return 0;
}
if (block.timestamp < vestingStart + cliffDuration) {
return 0; // Nog in cliff periode
}
uint256 vestedAmount = vestedTokens(_beneficiary);
if (vestedAmount <= ben.released) {
return 0;
}
return vestedAmount - ben.released;
}
// Bereken totale gevestigde tokens tot nu toe
function vestedTokens(address _beneficiary) public view returns (uint256) {
Beneficiary memory ben = beneficiariesData[_beneficiary];
if (block.timestamp >= vestingStart + vestingDuration || ben.revoked) {
return ben.totalAllocation;
} else if (block.timestamp < vestingStart + cliffDuration) {
return 0;
} else {
uint256 timeElapsed = block.timestamp - vestingStart;
uint256 vestingTime = vestingDuration - cliffDuration;
uint256 elapsedSinceCliff = timeElapsed - cliffDuration;
uint256 vested = (ben.totalAllocation * elapsedSinceCliff) / vestingTime;
return vested;
}
}
// Vrijgeven van tokens
function release() external onlyBeneficiary {
uint256 amount = releasableAmount(msg.sender);
require(amount > 0, "Geen tokens om vrij te geven");
beneficiariesData[msg.sender].released += amount;
require(token.transfer(msg.sender, amount), "Token transfer mislukt");
emit TokensReleased(msg.sender, amount);
}
// Administratie kan tokens inwisselen of terugtrekken
function recoverUnvestedTokens(address _to) external onlyAdmin {
uint256 contractBalance = token.balanceOf(address(this));
uint256 vestedTotal = 0;
// Optioneel: bepaal totale vested tokens
for (uint256 i = 0; i < beneficiaries.length; i++) {
vestedTotal += vestedTokens(beneficiaries[i]);
}
uint256 unvested = contractBalance - vestedTotal;
require(unvested > 0, "Geen unvested tokens");
require(token.transfer(_to, unvested), "Transfer mislukt");
}
// Optioneel: aantal begunstigden ophalen
function getBeneficiaries() external view returns (address[] memory) {
return beneficiaries;
}
}
```
### Belangrijke punten:
- Het contract is geconfigureerd voor meerdere begunstigden met individuele allocaties.
- De vesting start op 1 juli 2024 (gebruiker moet de timestamp invoeren bij deploy).
- Cliff van 3 maanden en totale vestingperiode van 12 maanden.
- Tokens worden maandelijks vrijgegeven na de cliff, berekend op basis van verstreken tijd.
- De admin kan niet-geverfde tokens terugtrekken.
- Beveiliging tegen re-entrancy wordt niet expliciet gedaan (aangezien er geen external calls na state verandering plaatsvinden), maar het is aanbevolen om OpenZeppelin's ReentrancyGuard te gebruiken voor extra veiligheid.
- Het contract gebruikt `block.timestamp` voor tijdscontrole, wat gebruikelijk is voor vestingcontracten.
- Het is belangrijk om het tokencontract vooraf te approve voor het vestingscontract, zodat het tokens kan overdragen.
### Aanpassingen:
- Pas de vestingstartdatum aan bij deploy, of laat deze parametriseren.
- Zorg dat de tokens vooraf geapproveerd zijn aan het vestingscontract.
- Voor meer geavanceerde functies of beveiliging, overweeg het gebruik van OpenZeppelin bibliotheken.
Wil je dat ik het contract verder optimaliseer of uitbreid?
provider: deepseek
model: deepseek-chat
Hier is een Solidity smart contract dat aan uw vereisten voldoet:
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract TokenVesting is Ownable, ReentrancyGuard {
struct VestingSchedule {
uint256 totalAmount;
uint256 releasedAmount;
uint256 startTime;
uint256 cliffDuration;
uint256 vestingDuration;
bool revoked;
}
IERC20 public immutable token;
mapping(address => VestingSchedule) public vestingSchedules;
address[] public beneficiaries;
uint256 public constant MONTH_IN_SECONDS = 30 days;
event VestingScheduleCreated(address indexed beneficiary, uint256 amount);
event TokensReleased(address indexed beneficiary, uint256 amount);
event VestingRevoked(address indexed beneficiary, uint256 unreleasedAmount);
constructor(
address _tokenAddress,
address[] memory _beneficiaries,
uint256[] memory _amounts,
uint256 _startTime,
uint256 _cliffDuration,
uint256 _vestingDuration
) {
require(_beneficiaries.length == _amounts.length, "Arrays length mismatch");
require(_tokenAddress != address(0), "Token address cannot be zero");
require(_startTime > block.timestamp, "Start time must be in future");
token = IERC20(_tokenAddress);
for (uint256 i = 0; i < _beneficiaries.length; i++) {
_createVestingSchedule(
_beneficiaries[i],
_amounts[i],
_startTime,
_cliffDuration,
_vestingDuration
);
}
}
function _createVestingSchedule(
address _beneficiary,
uint256 _amount,
uint256 _startTime,
uint256 _cliffDuration,
uint256 _vestingDuration
) internal {
require(_beneficiary != address(0), "Beneficiary cannot be zero address");
require(_amount > 0, "Amount must be greater than 0");
require(vestingSchedules[_beneficiary].totalAmount == 0, "Beneficiary already has vesting");
vestingSchedules[_beneficiary] = VestingSchedule({
totalAmount: _amount,
releasedAmount: 0,
startTime: _startTime,
cliffDuration: _cliffDuration,
vestingDuration: _vestingDuration,
revoked: false
});
beneficiaries.push(_beneficiary);
emit VestingScheduleCreated(_beneficiary, _amount);
}
function release(address _beneficiary) external nonReentrant {
uint256 releasable = calculateReleasableAmount(_beneficiary);
require(releasable > 0, "No tokens available for release");
VestingSchedule storage schedule = vestingSchedules[_beneficiary];
schedule.releasedAmount += releasable;
require(token.transfer(_beneficiary, releasable), "Token transfer failed");
emit TokensReleased(_beneficiary, releasable);
}
function calculateReleasableAmount(address _beneficiary) public view returns (uint256) {
VestingSchedule memory schedule = vestingSchedules[_beneficiary];
if (schedule.totalAmount == 0 || schedule.revoked) {
return 0;
}
uint256 currentTime = block.timestamp;
if (currentTime < schedule.startTime + schedule.cliffDuration) {
return 0;
}
if (currentTime >= schedule.startTime + schedule.vestingDuration) {
return schedule.totalAmount - schedule.releasedAmount;
}
uint256 elapsedTime = currentTime - schedule.startTime;
uint256 totalVestingTime = schedule.vestingDuration;
uint256 vestedAmount = (schedule.totalAmount * elapsedTime) / totalVestingTime;
if (vestedAmount > schedule.totalAmount) {
vestedAmount = schedule.totalAmount;
}
return vestedAmount - schedule.releasedAmount;
}
function revoke(address _beneficiary) external onlyOwner {
VestingSchedule storage schedule = vestingSchedules[_beneficiary];
require(schedule.totalAmount > 0, "No vesting schedule found");
require(!schedule.revoked, "Vesting already revoked");
uint256 unreleased = calculateReleasableAmount(_beneficiary);
schedule.revoked = true;
emit VestingRevoked(_beneficiary, unreleased);
}
function getBeneficiaries() external view returns (address[] memory) {
return beneficiaries;
}
function getVestingInfo(address _beneficiary) external view returns (
uint256 totalAmount,
uint256 releasedAmount,
uint256 startTime,
uint256 cliffEndTime,
uint256 vestingEndTime,
bool revoked,
uint256 releasableAmount
) {
VestingSchedule memory schedule = vestingSchedules[_beneficiary];
return (
schedule.totalAmount,
schedule.releasedAmount,
schedule.startTime,
schedule.startTime + schedule.cliffDuration,
schedule.startTime + schedule.vestingDuration,
schedule.revoked,
calculateReleasableAmount(_beneficiary)
);
}
}
```
**Gebruiksaanwijzing:**
1. **Implementatie:**
```javascript
// Voorbeeld implementatie parameters
const beneficiaries = [
"0xAbc123456789abcdef123456789abcdef1234567",
"0xDef456789abcdef123456789abcdef1234567890"
];
const amounts = [ethers.utils.parseEther("1000"), ethers.utils.parseEther("2000")];
const startTime = Math.floor(new Date('2024-07-01').getTime() / 1000);
const cliffDuration = 3 * 30 * 24 * 60 * 60; // 3 maanden in seconden
const vestingDuration = 12 * 30 * 24 * 60 * 60; // 12 maanden in seconden
// Implementeer het contract
const TokenVesting = await ethers.getContractFactory("TokenVesting");
const vestingContract = await TokenVesting.deploy(
tokenAddress,
beneficiaries,
amounts,
startTime,
cliffDuration,
vestingDuration
);
```
2. **Functies:**
- `release(beneficiary)`: Vrijgeven van beschikbare tokens
- `calculateReleasableAmount(beneficiary)`: Bereken beschikbare tokens
- `revoke(beneficiary)`: Intrekken vesting (alleen owner)
- `getVestingInfo(beneficiary)`: Bekijk vesting details
**Beveiligingskenmerken:**
- Reentrancy protection met `nonReentrant`
- Input validatie en zero-address checks
- Alleen eigenaar kan vesting intrekken
- Immutable token reference
- Tijdsafhankelijke berekeningen op basis van block timestamp
**Belangrijke opmerking:**
Zorg dat het token contract voldoende tokens heeft goedgekeurd voor het vesting contract voordat u het implementeert.