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

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1305 услуг
Разработка vesting-панели для инвесторов
Средний
~3-5 дней
Часто задаваемые вопросы

Направления блокчейн-разработки

Этапы блокчейн-разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1374
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1256
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    965
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1208
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    667
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    954

Инвестор, участвовавший в private round, получает vesting-контракт с графиком разблокировки. Но как ему отслеживать, когда можно забрать токены? Etherscan показывает raw-данные, но неудобен: нет сводки по всем контрактам, нет уведомлений, сложно делать claim. Мы решаем эту проблему — разрабатываем vesting-панели, которые агрегируют данные из всех цепочек в единый интерфейс. За много лет работы создали более 30 таких панелей для токенсейлов и венчурных фондов.

Типичная ситуация: у инвестора 20 контрактов на Ethereum, Arbitrum и Polygon. Каждый имеет свой график (cliff, duration). Вручную следить за разлоками невозможно. Панель показывает общую сумму заблокированных токенов ($5 млн), ближайший разлок (через 7 дней), и позволяет одним кликом забрать всё доступное. В результате нагрузка на поддержку снижается на 80%, а инвесторы довольны.

Почему инвестору нужен личный кабинет, а не Etherscan?

Etherscan показывает raw-данные контракта, но не удобен:

  • Нет сводной информации по всем контрактам инвестора.
  • Не показывает releasable в человекочитаемом виде.
  • Отсутствуют уведомления о новых разлоках.
  • Невозможно сделать claim без переключения между контрактами.

Готовая панель решает эти проблемы и снижает количество обращений в саппорт в 5-10 раз.

Архитектура панели

Минимальный набор данных для каждого инвестора

  • Total allocation — сколько токенов выделено; Vested — сколько разблокировалось к текущему моменту; Released — сколько уже забрано; Releasable — сколько можно забрать прямо сейчас; Locked — ещё под vesting; Vesting schedule — график разблокировки (визуально); Next unlock — когда следующий разлок и сколько.

Как эффективно читать данные из контрактов?

Если используется OpenZeppelin VestingWallet:

import { createPublicClient, http, parseAbi } from "viem";

const VESTING_ABI = parseAbi([
  "function beneficiary() view returns (address)",
  "function start() view returns (uint64)",
  "function duration() view returns (uint64)",
  "function released(address token) view returns (uint256)",
  "function releasable(address token) view returns (uint256)",
  "function vestedAmount(address token, uint64 timestamp) view returns (uint256)",
]);

async function getVestingData(
  vestingAddress: `0x${string}`,
  tokenAddress: `0x${string}`,
  client: PublicClient
) {
  const [start, duration, released, releasable] = await client.multicall({
    contracts: [
      { address: vestingAddress, abi: VESTING_ABI, functionName: "start" },
      { address: vestingAddress, abi: VESTING_ABI, functionName: "duration" },
      {
        address: vestingAddress,
        abi: VESTING_ABI,
        functionName: "released",
        args: [tokenAddress],
      },
      {
        address: vestingAddress,
        abi: VESTING_ABI,
        functionName: "releasable",
        args: [tokenAddress],
      },
    ],
  });
  
  // Общий allocation = баланс контракта + уже released
  const balance = await client.readContract({
    address: tokenAddress,
    abi: parseAbi(["function balanceOf(address) view returns (uint256)"]),
    functionName: "balanceOf",
    args: [vestingAddress],
  });
  
  const totalAllocation = balance.result! + released.result!;
  
  return {
    start: Number(start.result),
    duration: Number(duration.result),
    released: released.result!,
    releasable: releasable.result!,
    totalAllocation,
    locked: totalAllocation - released.result! - releasable.result!,
  };
}

multicall — обязательно использовать для batch запросов. Один вызов к ноде вместо четырёх-пяти последовательных — это ускоряет получение данных в 4-5 раз, критично для производительности при нескольких контрактах.

Frontend: компонент графика vesting

Визуализация schedule помогает инвестору понять когда и сколько он получит:

import { LineChart, Line, XAxis, YAxis, Tooltip, ReferenceLine } from "recharts";
import { formatUnits } from "viem";

function VestingChart({ start, cliffDuration, vestingDuration, totalAllocation, decimals }) {
  const cliffEnd = start + cliffDuration;
  const vestingEnd = cliffEnd + vestingDuration;
  const now = Date.now() / 1000;
  
  // Генерируем точки для графика
  const dataPoints = [];
  const step = vestingDuration / 30; // 30 точек на период vesting
  
  for (let t = start; t <= vestingEnd; t += step) {
    let vested = 0;
    if (t >= cliffEnd) {
      const elapsed = Math.min(t - cliffEnd, vestingDuration);
      vested = Number(formatUnits(
        BigInt(Math.floor(Number(totalAllocation) * elapsed / vestingDuration)),
        decimals
      ));
    }
    dataPoints.push({
      date: new Date(t * 1000).toLocaleDateString("ru-RU", { month: "short", year: "2-digit" }),
      vested,
    });
  }
  
  return (
    <LineChart width={600} height={300} data={dataPoints}>
      <XAxis dataKey="date" tick={{ fontSize: 11 }} />
      <YAxis tickFormatter={(v) => `${(v / 1000).toFixed(0)}k`} />
      <Tooltip
        formatter={(value) => [`${Number(value).toLocaleString()} tokens`, "Vested"]}
      />
      <ReferenceLine
        x={new Date(now * 1000).toLocaleDateString("ru-RU", { month: "short", year: "2-digit" })}
        stroke="#f59e0b"
        label={{ value: "Сейчас", position: "top" }}
      />
      {cliffDuration > 0 && (
        <ReferenceLine
          x={new Date(cliffEnd * 1000).toLocaleDateString("ru-RU", { month: "short", year: "2-digit" })}
          stroke="#6366f1"
          strokeDasharray="4 4"
          label={{ value: "Cliff", position: "top" }}
        />
      )}
      <Line type="monotone" dataKey="vested" stroke="#10b981" strokeWidth={2} dot={false} />
    </LineChart>
  );
}

Как реализовать claim транзакцию?

Кнопка "Claim" должна корректно обрабатывать все состояния:

import { useWriteContract, useWaitForTransactionReceipt } from "wagmi";

function ClaimButton({ vestingAddress, tokenAddress, releasable, decimals }) {
  const { writeContract, data: txHash, isPending, error } = useWriteContract();
  const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({
    hash: txHash,
  });
  
  const handleClaim = () => {
    writeContract({
      address: vestingAddress,
      abi: VESTING_ABI,
      functionName: "release",
      args: [tokenAddress],
    });
  };
  
  const formattedReleasable = Number(formatUnits(releasable, decimals)).toLocaleString();
  
  if (releasable === 0n) {
    return <Button disabled>Нечего забирать</Button>;
  }
  
  return (
    <div>
      <Button
        onClick={handleClaim}
        disabled={isPending || isConfirming}
      >
        {isPending ? "Подтвердите в кошельке..." :
         isConfirming ? "Ожидание подтверждения..." :
         `Забрать ${formattedReleasable} токенов`}
      </Button>
      {isSuccess && (
        <p className="text-green-600">
          Успешно! {" "}
          <a href={`https://etherscan.io/tx/${txHash}`} target="_blank" rel="noreferrer">
            Транзакция
          </a>
        </p>
      )}
      {error && <p className="text-red-600">{error.shortMessage}</p>}
    </div>
  );
}

Multi-wallet и multi-chain поддержка

Инвесторы используют разные кошельки (MetaMask, WalletConnect, Coinbase Wallet, Ledger). wagmi v2 с ConnectKit или RainbowKit обрабатывает это. Если проект задеплоен на нескольких сетях, инвестор должен видеть все свои vesting контракты в одном месте:

const NETWORKS = [
  { chainId: 1, name: "Ethereum", client: mainnetClient },
  { chainId: 42161, name: "Arbitrum", client: arbitrumClient },
];

async function getAllVestings(investorAddress: string) {
  const results = await Promise.all(
    NETWORKS.map(async (network) => {
      const vestingAddress = VESTING_CONTRACTS[network.chainId]?.[investorAddress];
      if (!vestingAddress) return null;
      
      const data = await getVestingData(vestingAddress, TOKEN_ADDRESS, network.client);
      return { ...data, network: network.name, chainId: network.chainId, vestingAddress };
    })
  );
  
  return results.filter(Boolean);
}

Инвестор-специфические фичи

  • Email / Telegram уведомления об разлоках: за 7 дней до cliff, за 24 часа до каждого значимого unlock. Требует off-chain сервис, который мониторит блокчейн и отправляет уведомления.
  • CSV экспорт для налоговой отчётности: история всех claim транзакций с датами и суммами. Берётся из событий ERC20Transfer или TokensReleased через getLogs или indexer (The Graph).
  • Whitelist check: если токен нельзя продавать до определённой даты (lock-up дополнительный к vesting), это не всегда отражено в vesting контракте — может быть в самом токене. Панель должна это показывать.

Как работает аутентификация?

Для вестинг панели аутентификация через кошелёк (Sign-In with Ethereum, EIP-4361) достаточна и предпочтительна — нет паролей, нет баз пользователей.

import { SiweMessage } from "siwe";

// На клиенте
async function signIn(address: string, chainId: number) {
  const nonce = await fetch("/api/nonce").then((r) => r.text());
  
  const message = new SiweMessage({
    domain: window.location.host,
    address,
    statement: "Sign in to view your vesting schedule",
    uri: window.location.origin,
    version: "1",
    chainId,
    nonce,
  });
  
  const signature = await walletClient.signMessage({
    message: message.prepareMessage(),
  });
  
  await fetch("/api/verify", {
    method: "POST",
    body: JSON.stringify({ message, signature }),
  });
}

Что включает разработка под ключ?

Мы предоставляем готовое решение, которое включает:

  • Backend сервер для агрегации данных и нотификаций (Node.js, NestJS, Prisma).
  • Frontend на Next.js 14 с TypeScript, wagmi v2, RainbowKit.
  • Интеграцию с любым vesting-контрактом (OpenZeppelin, простые ABI).
  • Multi-chain обёртку для 3+ сетей.
  • SIWE-аутентификацию.
  • CSV-экспорт транзакций.
  • Развёртывание в облаке (AWS, DigitalOcean) и настройку CI/CD.
  • Обучение команды и документацию API.

Стек и сравнение подходов

Таблица стека

Компонент Технология
Frontend Next.js 14 + TypeScript
Web3 wagmi v2 + viem + RainbowKit
Чтение данных viem multicall + The Graph (опц.)
Графики Recharts или Victory
Auth SIWE (EIP-4361)
Уведомления cron-сервис + SendGrid / Telegram Bot API

Сравнение: on-chain vs off-chain расчёт vestedAmount

Критерий On-chain (viem multicall) Off-chain (cron + DB)
Задержка данных Реал-тайм (после каждого блока) До 15 минут
Нагрузка на RPC Высокая при многих запросах Низкая (кешированные данные)
Сложность разработки Низкая (только клиент) Средняя (требует инфраструктуры)
Поддержка истории Нет (только текущее состояние) Да (храним историю изменений)
Идеальный сценарий MVP, небольшая аудитория Продакшен с тысячами инвесторов

On-chain подход проще, но при большой нагрузке off-chain надёжнее. Для крупных проектов рекомендуем off-chain — свяжитесь с нами для детального обсуждения.

Сроки и условия

Срок разработки MVP (read-only dashboard + claim): 2–3 недели. Полная панель с уведомлениями, multi-chain, CSV экспортом — 4–6 недель. Стоимость рассчитывается индивидуально — свяжитесь с нами, чтобы мы оценили ваш проект.

Опыт команды

Мы занимаемся блокчейн-разработкой много лет. За это время реализовали 30+ проектов с vesting-механиками, включая токенсейлы панели для DeFi-протоколов и венчурных фондов. Все решения проходят аудит безопасности с помощью Slither и Echidna.

Закажите разработку панели, адаптированной под вашу структуру контрактов. Свяжитесь с нами — поможем спроектировать панель под ваши контракты и бизнес-логику.

Разработка токенов: ERC-20, токеномика, вестинг

«ERC-20 — это просто» — фраза, после которой начинаются проблемы. Базовый transfer написать несложно. Но токен, у которого через шесть месяцев не происходит инфляционный коллапс, governance работает как задумано, а вестинг нельзя обойти через хитрую схему с делегированием — это уже проектирование.

ERC-20: что под капотом

Стандарт ERC-20 — девять функций. Сложность начинается с расширений:

ERC-20Permit (EIP-2612) — gasless approve через подпись. Пользователь подписывает permit(owner, spender, value, deadline, v, r, s) off-chain, spender вызывает permit() + transferFrom() в одной транзакции. Это убирает отдельный approve step. Но: подпись можно перехватить и использовать — нужен deadline и проверка nonce.

ERC-20Votes (EIP-5805) — snapshot балансов для governance. Checkpoint-система хранит историю балансов по номеру блока. getPastVotes(address, blockNumber) — баланс на момент создания proposal, а не текущий. Это предотвращает flash loan governance attack: нельзя занять токены и проголосовать ими в одной транзакции.

Rebasing токены (stETH, Ampleforth) — balanceOf меняется автоматически через изменение internal shares ratio. Высокая сложность интеграции: большинство DeFi протоколов не работают корректно с rebasing без wrapping в non-rebasing версию.

Fee-on-transfer токены — при каждом transfer снимается процент. Ломают AMM расчёты: пул получает меньше, чем ожидал. Uniswap v2/v3 не поддерживают fee-on-transfer нативно — нужны специальные pair/router.

Tokenomics: где математика превращается в экономику

Токеномика — это не таблица в Excel с суммой 100%. Это модель инцентивов, которая либо работает в долгосрочной перспективе, либо создаёт давление продаж которое убьёт проект.

Emission schedule и инфляция

Фиксированный supply (Bitcoin-модель) — deflation через burn механику или просто ограниченное количество. Подходит для store-of-value или utility токенов с ограниченным спросом на новые токены.

Инфляционная модель (Ethereum post-Merge, Curve) — новые токены выпускаются для стимулирования участников. Нужен баланс: emission должен быть ниже или равен value capture протоколом. Если протокол зарабатывает $100k/месяц, а эмиссия в рыночной стоимости $500k/месяц — постоянное давление продаж неизбежно.

Halving schedules (Bitcoin-style) — уменьшение emission со временем. Создаёт предсказуемость, но требует что утилити токена росла чтобы компенсировать падающие rewards для stakers/validators.

Supply distribution

Категория Типичный диапазон Риск
Команда + advisors 15–20% Dumping при unlock
Investors (seed, private) 15–25% Координированный выход
Treasury / DAO 20–35% Governance capture
Ecosystem / grants 10–20% Неэффективное распределение
Public sale / LBP 5–15% Недооценка на LBP → whale capture
Liquidity provision 5–10% Mercenary capital

Нет универсальной формулы. Есть принцип: никакой одной сущности не должно принадлежать >33% voting power при запуске. Иначе governance — фикция.

Vesting контракты: детали имеют значение

Linear vesting с cliff — стандарт для команды и инвесторов. cliff — период после TGE, в течение которого ничего не доступно. После cliff: линейный unlock до duration.

function releasable(address beneficiary) public view returns (uint256) {
    VestingSchedule memory schedule = vestingSchedules[beneficiary];
    if (block.timestamp < schedule.cliff) return 0;

    uint256 elapsed = block.timestamp - schedule.cliff;
    uint256 vestingDuration = schedule.duration - (schedule.cliff - schedule.start);
    uint256 vested = schedule.totalAmount * elapsed / vestingDuration;

    return vested - schedule.released;
}

Типичные ошибки при реализации:

Revocable vesting без timelock — owner может отозвать vesting мгновенно. Если owner key скомпрометирован или команда недобросовестна — все unvested токены могут быть отозваны. Решение: revocation через multisig + governance vote.

Cliff не блокирует governance права — если используется ERC-20Votes, recipient может делегировать voting power с первого дня, даже если токены ещё не unlocked. Нужно явно разделить voting power и claim logic.

Отсутствие emergency pause — если обнаружена уязвимость в vesting контракте, нужна возможность приостановить claim. Pausable + timelock на unpause.

Liquidity Bootstrapping

Запуск ликвидности — критический момент. Три основных подхода:

Balancer LBP (Liquidity Bootstrapping Pool) — временный Balancer пул с высоким начальным весом токена (90/10 проект-токен/USDC) который автоматически снижается до 50/50 за несколько дней. Создаёт нисходящее ценовое давление, препятствуя ботам скупить всё по одной цене. После LBP ликвидность переносится в постоянный пул.

Fjord Foundry — специализированная платформа для LBP и fair launches. Меньше операционного overhead чем прямая интеграция с Balancer.

Uniswap v3 с ограниченным range — добавить ликвидность в узкий диапазон вокруг начальной цены. Высокая capital efficiency, но требует активного управления range.

TWAMM (Time-Weighted AMM) — механика для постепенной продажи/покупки больших объёмов без slippage. Paradigm предложил, реализован в FraxSwap.

Governance токены и voting механики

OpenZeppelin Governor — стандартная реализация on-chain governance. Модульная архитектура: GovernorVotes для counting, GovernorTimelockControl для timelock execution, GovernorSettings для изменяемых параметров.

Quorum — минимальный процент supply для валидности голосования. Слишком высокий quorum = apathy failure (не набирается голосов). Слишком низкий = whale capture. Compound установил quorum 400k COMP (4% supply) — на практике достигается редко без координации крупных holders.

Flash loan governance attack — атакующий занимает токены через flash loan, делегирует их себе, создаёт proposal или голосует, возвращает токены. ERC-20Votes с snapshot по номеру блока полностью блокирует это: нужно иметь токены на момент создания snapshot, который берётся в момент создания proposal.

Delegation — пользователи с малыми балансами часто не голосуют. Liquid delegation (как в Optimism) позволяет делегировать voting power конкретным addresses (delegates) без передачи ownership токенов.

Стек для токен-разработки

Контракты: Solidity 0.8.x, OpenZeppelin Contracts 5.x (ERC20, ERC20Permit, ERC20Votes, Governor, TimelockController, TokenVesting)

Аудит токеномики: Python модели с симуляцией emission/demand, cadCAD для complex systems modeling

Деплой и управление: Foundry scripts, Gnosis Safe для treasury, OpenZeppelin Defender для автоматизации

Аналитика: Dune Analytics для on-chain метрик, Token Terminal для protocol revenue

Процесс

Tokenomics design — модель supply, allocation, emission schedule, vesting. Стресс-тестирование сценариев (bear market, whale exit, governance capture attempt).

Контракт разработка — ERC-20 + extensions, vesting, governance. Foundry fuzz тесты на vesting calculations, governance thresholds.

Аудит — особое внимание на governance attack vectors, vesting bypass, permit replay attacks.

LBP / launch — выбор механики, настройка параметров, мониторинг первых 24 часов.

Post-launch — мониторинг supply distribution через Dune, governance participation metrics, treasury management.

Сроки

  • ERC-20 с permit и basic governance: 2–3 недели
  • Vesting контракт с revocation и cliff: 2–4 недели
  • Полный governance (Governor + Timelock + Token): 4–7 недель
  • Токен + LBP + governance + vesting: 8–14 недель