Разработка vesting-schedule для команды и инвесторов

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка vesting-schedule для команды и инвесторов
Простая
~2-3 рабочих дня
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1058
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Разработка vesting-schedule для команды и инвесторов

Vesting без cliff — это плохой vesting. Основатель, который получает токены линейно с первого дня, не имеет финансового стимула оставаться в проекте после первых месяцев. Стандартная схема в веб3: 1 год cliff (нет токенов вообще) + 3 года линейного vesting. Инвесторы обычно получают более короткий цикл: 6 месяцев cliff + 18–24 месяца linear.

Смарт-контракт vesting

Промышленный стандарт — OpenZeppelin VestingWallet. Для команды и инвесторов с разными параметрами деплоим отдельные инстансы:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/finance/VestingWallet.sol";

// Деплой для конкретного бенефициара
// VestingWallet(beneficiary, startTimestamp, durationSeconds)

// Команда: cliff 1 год, vesting 4 года суммарно
// start = TGE + 1 год (cliff), duration = 3 года
address teamMemberVesting = address(new VestingWallet(
    teamMemberAddress,
    block.timestamp + 365 days,  // start после cliff
    3 * 365 days                  // 3 года линейного vesting
));

// Пополнить токенами
IERC20(tokenAddress).transfer(teamMemberVesting, allocatedAmount);

VestingWallet — простой и аудированный контракт. Бенефициар вызывает release(tokenAddress) для получения разблокированных токенов. Нельзя отозвать — если нужна revocability (уволенный сотрудник), нужна кастомная реализация.

Revocable vesting для команды

Стандартная VestingWallet не поддерживает revoke. Для сотрудников нужна возможность остановить vesting при увольнении:

contract RevocableVesting is Ownable2Step {
    IERC20 public immutable token;
    address public beneficiary;
    uint256 public immutable start;
    uint256 public immutable cliff;
    uint256 public immutable duration;
    uint256 public immutable totalAllocation;
    uint256 public released;
    bool public revoked;
    uint256 public revokedAt;
    
    constructor(
        address _token,
        address _beneficiary,
        address _owner,
        uint256 _start,
        uint256 _cliff,
        uint256 _duration,
        uint256 _totalAllocation
    ) Ownable2Step() {
        token = IERC20(_token);
        beneficiary = _beneficiary;
        start = _start;
        cliff = _cliff;
        duration = _duration;
        totalAllocation = _totalAllocation;
        _transferOwnership(_owner);
    }
    
    function vestedAmount() public view returns (uint256) {
        uint256 endTime = revoked ? revokedAt : block.timestamp;
        
        if (endTime < start + cliff) return 0;
        if (endTime >= start + cliff + duration) return totalAllocation;
        
        uint256 elapsed = endTime - (start + cliff);
        return totalAllocation * elapsed / duration;
    }
    
    function releasable() public view returns (uint256) {
        return vestedAmount() - released;
    }
    
    function release() external {
        require(msg.sender == beneficiary, "Not beneficiary");
        uint256 amount = releasable();
        require(amount > 0, "Nothing to release");
        
        released += amount;
        token.safeTransfer(beneficiary, amount);
        emit TokensReleased(amount);
    }
    
    // Owner (компания) может отозвать невестед токены
    function revoke() external onlyOwner {
        require(!revoked, "Already revoked");
        revoked = true;
        revokedAt = block.timestamp;
        
        // Уже вестед — отдаём бенефициару
        uint256 vestedNow = vestedAmount() - released;
        if (vestedNow > 0) {
            released += vestedNow;
            token.safeTransfer(beneficiary, vestedNow);
        }
        
        // Невестед — возвращаем в treasury
        uint256 remaining = token.balanceOf(address(this));
        if (remaining > 0) {
            token.safeTransfer(owner(), remaining);
        }
        
        emit VestingRevoked(revokedAt, vestedNow, remaining);
    }
}

Cliff реализован как проверка endTime < start + cliff — до этой даты vestedAmount() возвращает 0. После cliff — линейный рост пропорционально прошедшему времени.

Фабрика для деплоя нескольких контрактов

Деплоить по одному контракту на каждого получателя вручную — неудобно. Фабрика:

contract VestingFactory is Ownable2Step {
    address public token;
    address[] public allVestings;
    mapping(address => address) public vestingOf; // beneficiary => vesting contract
    
    event VestingCreated(address indexed beneficiary, address vestingContract, uint256 amount);
    
    struct VestingParams {
        address beneficiary;
        uint256 amount;
        uint256 cliffDays;
        uint256 vestingDays;
    }
    
    function batchCreate(
        VestingParams[] calldata params,
        uint256 tgeTimestamp
    ) external onlyOwner {
        for (uint256 i = 0; i < params.length; i++) {
            VestingParams memory p = params[i];
            require(vestingOf[p.beneficiary] == address(0), "Already has vesting");
            
            RevocableVesting vesting = new RevocableVesting(
                token,
                p.beneficiary,
                owner(),
                tgeTimestamp,
                p.cliffDays * 1 days,
                p.vestingDays * 1 days,
                p.amount
            );
            
            IERC20(token).safeTransferFrom(msg.sender, address(vesting), p.amount);
            
            allVestings.push(address(vesting));
            vestingOf[p.beneficiary] = address(vesting);
            
            emit VestingCreated(p.beneficiary, address(vesting), p.amount);
        }
    }
}

Один вызов batchCreate — деплой всех контрактов для команды и инвесторов. Апрув токенов на фабрику перед вызовом.

Параметры по типам получателей

Тип Cliff Vesting Revocable
Основатели 12 мес 36 мес после cliff Да
Ранние сотрудники 12 мес 24–36 мес после cliff Да
Seed инвесторы 6–12 мес 12–24 мес после cliff Нет
Стратегические партнёры 6 мес 12–18 мес Частично
Советники 3–6 мес 12–18 мес Нет

Для инвесторов — контракт нередко неотзываемый (это условие инвестиционного соглашения). Для сотрудников — отзываемый при увольнении.

Налоговые соображения

Vesting on-chain — это on-chain события, которые оставляют след. В разных юрисдикциях момент налогообложения разный: при вестинге (unlock) или при продаже. Это не техническая, а юридическая проблема, но смарт-контракт должен эмитить правильные события с timestamps для аудита.

Срок разработки: 3–5 дней для стандартных vesting контрактов с фабрикой и тестами. Аудит опционален для небольших аллокаций, обязателен при суммах >$500k.