Уявіть: ви запускаєте токенсейл, але через помилку в смарт-контракті частина транзакцій відкочується, або користувачі не можуть отримати токени після збору. Такі інциденти коштують репутації та грошей — ми бачили проєкти, що втрачали значні суми через банальний N+1 в UI. За 5+ років ми виробили підхід, що виключає ці ризики: використовуємо Merkle tree для whitelist, автоматизуємо вестинг та проводимо аудит контрактів. Ми розробляємо кастомні платформи для запуску токенів — IDO/ICO Launchpad, інтегровані зі смарт-контрактами пресейлу. Наші рішення працюють на Ethereum, BNB Chain та Polygon, проходять аудит і дозволяють гнучко налаштовувати whitelist, багатофазні продажі та вестинг. Frontend на React+TypeScript з використанням viem та wagmi гарантує точність розрахунків та безпеку транзакцій.
Які проблеми вирішуємо?
Складність управління фазами продажу. Launchpad проходить через чотири стани: Upcoming, Whitelist Round, Public Round, Ended. Кожна фаза вимагає свого UI та логіки. Ми автоматизуємо перемикання станів на основі даних контракту, виключаючи помилки ручного управління.
Whitelist з Merkle tree. Щоб допустити до продажу лише перевірених учасників, ми використовуємо Merkle tree. Це дозволяє зберігати список допуску offchain, а на контракті перевіряти лише корінь. Фронтенд генерує proof для кожної адреси. Без цієї механіки whitelist або дорогий (зберігання масиву в контракті), або централізований (backend-перевірка). Наш підхід балансує між децентралізацією та газ-ефективністю.
Точний розрахунок токенів та вестинг. Користувачі вносять ETH, отримують токени за ціною пресейлу. Ми реалізуємо калькулятор зі зворотним зв'язком у реальному часі, враховуючи персональні ліміти та вже внесені суми. Вестинг налаштовується: лінійний або з кліффом, розподіл за раундами.
Чому Merkle tree — найкращий вибір для whitelist?
Зберігання повного списку whitelist у смарт-контракті коштує дорого: кожен запис додає ~20 000 gas. При списку в 10 000 адрес це близько 200 000 000 gas — економія до $5000 при поточних цінах. Merkle tree скорочує цю вартість у 10 разів, оскільки на блокчейні зберігається лише один 32-байтовий корінь. Учасник надає proof, який перевіряється за ~3000 gas. Це робить launchpad масштабованим та доступним для проєктів з тисячами інвесторів. Детальніше про Merkle tree.
Як ми забезпечуємо безпеку смарт-контрактів?
Всі контракти пишуться на Solidity з використанням OpenZeppelin бібліотек — стандарт безпеки в індустрії. Ми використовуємо патерни ReentrancyGuard, Pausable, Ownable. Перед релізом кожен контракт проходить внутрішній аудит з Foundry (тести покриття >95%) та зовнішній аудит у сертифікованих компаніях (CertiK, Quantstamp). Додатково налаштовуємо multisig для управління коштами та моніторинг на Sentry.
Як ми будуємо launchpad?
- Frontend: React 18, TypeScript, Tailwind CSS, Wagmi, viem. Використовуємо RSC та Suspense для оптимізації завантаження.
- Smart contracts: Solidity, OpenZeppelin, Foundry. Контракти пресейлу з можливістю whitelist, вестингу та refund.
- Інфраструктура: Vercel для фронтенду, AWS для API та бази, IPFS для хостингу метаданих.
- Інтеграції: WalletConnect, MetaMask, Coinbase Wallet, будь-які EVM-гаманці.
Приклад: для одного з клієнтів ми розгорнули launchpad з двофазним продажем (whitelist + public), вестингом 6 місяців та автоматичним клеймінгом. Проєкт зібрав hard cap за 4 години. Всі контракти перевірені CertiK.
Що входить у роботу
| Етап | Що робимо | Строк |
|---|---|---|
| Аналіз та специфікація | Описуємо бізнес-логіку, фази, ліміти, механіку вестингу | 1–2 дні |
| Дизайн UI/UX | Макети екранів для кожної фази, десктоп + мобільні | 2–3 дні |
| Розробка смарт-контрактів | Solidity + тести на Foundry, деплой у testnet | 3–5 днів |
| Frontend | Інтеграція з контрактом, калькулятор, таймер, прогрес-бар | 4–7 днів |
| Аудит та тестування | Внутрішній аудит, code review, виправлення багів | 2–4 дні |
| Запуск та супровід | Деплой, моніторинг, пост-релізна підтримка 2 тижні | 1–2 дні |
Порівняння базового та розширеного пакету
| Параметр | MVP (базовий) | Full Launchpad |
|---|---|---|
| Кількість раундів | 1 | до 5 |
| Whitelist | — | Merkle tree + tiered |
| Вестинг | — | Лінійний/кліфф |
| Клеймінг | Вручну | Автоматично |
| Аудит | Формальний | CertiK/Quantstamp |
| Строк | 4–5 днів | 10–14 днів |
Процес роботи
- Аналітика — описуємо логіку, фази, ліміти, вестинг.
- Проектування — архітектура контрактів, UI/UX макети.
- Реалізація — контракти (Solidity) + фронтенд (React) паралельно.
- Тестування — юніт-тести, інтеграційні тести, code review.
- Деплой — у mainnet, моніторинг, передача документації.
Етапи можуть перетинатися; строки уточнюються після брифу.
Ключові компоненти інтерфейсу
Транзакція внеску з Merkle proof (натисніть, щоб розгорнути)
// hooks/useContribute.ts
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi';
import { getMerkleProof, isWhitelisted } from '@/lib/merkle';
export function useContribute(contractAddress: `0x${string}`, phase: number) {
const { address } = useAccount();
const { writeContract, data: txHash, isPending } = useWriteContract();
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash: txHash });
const contribute = async (ethValue: bigint) => {
if (!address) return;
if (phase === 1) {
if (!isWhitelisted(address)) {
throw new Error('Адрес не в whitelist');
}
const proof = getMerkleProof(address);
writeContract({
address: contractAddress,
abi: PRESALE_ABI,
functionName: 'contribute',
args: [proof],
value: ethValue,
});
} else {
writeContract({
address: contractAddress,
abi: PRESALE_ABI,
functionName: 'contributePublic',
value: ethValue,
});
}
};
return { contribute, txHash, isPending, isConfirming, isSuccess };
}
Компонент таймера зворотного відліку (натисніть, щоб розгорнути)
// components/CountdownTimer.tsx
import { useEffect, useState } from 'react';
interface TimeLeft {
days: number;
hours: number;
minutes: number;
seconds: number;
}
function calcTimeLeft(targetTs: number): TimeLeft {
const diff = Math.max(0, targetTs * 1000 - Date.now());
return {
days: Math.floor(diff / 86_400_000),
hours: Math.floor((diff % 86_400_000) / 3_600_000),
minutes: Math.floor((diff % 3_600_000) / 60_000),
seconds: Math.floor((diff % 60_000) / 1_000),
};
}
export function CountdownTimer({ targetTs, label }: { targetTs: number; label: string }) {
const [timeLeft, setTimeLeft] = useState<TimeLeft>(calcTimeLeft(targetTs));
useEffect(() => {
const interval = setInterval(() => setTimeLeft(calcTimeLeft(targetTs)), 1000);
return () => clearInterval(interval);
}, [targetTs]);
return (
<div className="text-center">
<p className="mb-3 text-sm text-neutral-400">{label}</p>
<div className="flex items-center gap-3">
{[
{ value: timeLeft.days, label: 'дней' },
{ value: timeLeft.hours, label: 'часов' },
{ value: timeLeft.minutes, label: 'минут' },
{ value: timeLeft.seconds, label: 'секунд' },
].map(({ value, label }) => (
<div key={label} className="min-w-[60px] rounded-xl bg-neutral-800 p-3 text-center">
<span className="block text-2xl font-bold tabular-nums">
{String(value).padStart(2, '0')}
</span>
<span className="text-xs text-neutral-500">{label}</span>
</div>
))}
</div>
</div>
);
}
Калькулятор розподілу токенів (натисніть, щоб розгорнути)
// components/ContributionCalculator.tsx
import { formatEther, formatUnits, parseEther } from 'viem';
interface Props {
tokenPrice: bigint;
minContrib: bigint;
maxContrib: bigint;
userContrib: bigint;
tokenDecimals?: number;
}
export function ContributionCalculator({
tokenPrice, minContrib, maxContrib, userContrib, tokenDecimals = 18,
}: Props) {
const [ethAmount, setEthAmount] = useState('');
const ethWei = ethAmount ? parseEther(ethAmount) : 0n;
const tokensReceived = tokenPrice > 0n ? (ethWei * BigInt(10 ** tokenDecimals)) / tokenPrice : 0n;
const remaining = maxContrib - userContrib;
const canContribute = ethWei >= minContrib && ethWei <= remaining;
return (
<div className="space-y-4">
{/* original */}
</div>
);
}
Прогрес-бар з відображенням збору (натисніть, щоб розгорнути)
function FundingProgress({ raised, hardCap, softCap }: { raised: bigint; hardCap: bigint; softCap: bigint }) {
const raisedEth = parseFloat(formatEther(raised));
const hardCapEth = parseFloat(formatEther(hardCap));
const softCapEth = parseFloat(formatEther(softCap));
const progress = (raisedEth / hardCapEth) * 100;
const softCapPercent = (softCapEth / hardCapEth) * 100;
return (
<div>
<div className="mb-2 flex justify-between text-sm">
<span>{raisedEth.toFixed(2)} ETH собрано</span>
<span>{progress.toFixed(1)}%</span>
</div>
<div className="relative h-3 overflow-hidden rounded-full bg-neutral-800">
<div
className="h-full rounded-full bg-gradient-to-r from-blue-600 to-violet-600 transition-all duration-500"
style={{ width: `${Math.min(progress, 100)}%` }}
/>
<div
className="absolute top-0 h-full w-0.5 bg-yellow-400"
style={{ left: `${softCapPercent}%` }}
/>
</div>
<div className="mt-1 flex justify-between text-xs text-neutral-500">
<span>Soft cap: {softCapEth} ETH</span>
<span>Hard cap: {hardCapEth} ETH</span>
</div>
</div>
);
}
Як замовити розробку
Зв'яжіться з нами через форму на сайті — ми оцінимо ваш проєкт, запропонуємо архітектуру та строки. Отримайте консультацію безкоштовно. Замовте розробку launchpad під ваш проєкт та запустіть токенсейл без ризиків.







