
Программирование для Web3: как создают децентрализованные программы?
Разбираемся, из чего состоит стек Web3, как писать смарт-контракты, подключать кошельки, хранить данные децентрализованно и выпускать токены. Полный гайд для разработчика.
Web3 — это не просто новые инструменты, а изменение подхода к созданию приложений. В децентрализованных приложениях (dApp) логика и данные больше не принадлежат одному серверу: ядро переносится в смарт-контракты, а идентичность пользователя — в его кошелёк. Такой сдвиг требует иного мышления о архитектуре, безопасности, UX и DevOps.
Что такое Web3 и чем dApp отличается от обычного приложения?
В классическом вебе у нас есть клиент (браузер/мобильное приложение) и сервер с БД. В Web3 серверная «правда» живёт в блокчейне: код смарт-контрактов исполняется валидаторами, данные транзакций неизменяемы, а доступ к функциям определяется криптографическими ключами. dApp — это связка смарт-контрактов, фронтенда, кошелька и, часто, децентрализованного хранилища.
В результате меняются приоритеты: безопасность контракта важнее скорости релиза, неизменяемость важнее «горячих фиксов», а пользователь сам владеет своими активами — без «сброса пароля через e-mail».
Базовые компоненты стека Web3
Блокчейн-рантайм
EVM-совместимые сети (Ethereum, Polygon, BNB Smart Chain, Avalanche, Base и др.) позволяют писать контракты на Solidity/Vyper. Есть и не-EVM платформы (Solana, Aptos, Sui), где контракты пишут на Rust или Move.
Смарт-контракты
Это неизменяемые программы в блокчейне. Они управляют активами, определяют правила протокола (DEX, лендинг, DAO) и выступают публичным API: любой может вызвать их методы, оплатив газ.
Кошельки
Ключи пользователя — это и его логин, и подпись запросов. Кошельки (MetaMask, Rabby, Phantom и др.) выступают как провайдеры: они подключают фронтенд к сети, показывают пользователю транзакции и подписывают их.
Децентрализованное хранение
Крупные данные и статику часто хранят в IPFS/Arweave/Filecoin. Хэш (CID) закрепляется в контракте, чтобы любой мог проверить подлинность содержимого.
Индексаторы и субграфы
Для удобных запросов используют The Graph или собственные индексаторы: они «распаковывают» события блокчейна в удобные API, чтобы фронтенд не делал сотни прямых вызовов.
Жизненный цикл dApp: от идеи до мейннета
Типичный путь включает проектирование протокола, написание и тестирование контрактов, их деплой в тестовую сеть, развертывание фронтенда, аудит безопасности и выпуск в мейннет. Важно предусмотреть механизмы апгрейда (через прокси), контроль доступа (ролевая модель), аварийное отключение и планы миграций.
Проектирование смарт-контрактов: интерфейсы, события, доступ
Интерфейсы
Придерживайтесь стандартов (ERC-20/721/1155/4626/777/1271/4337 и др.). Это обеспечивает совместимость с кошельками, биржами и аналитикой.
События
Всё важное логируйте через события — их легко индексировать и показывать на фронтенде. События — это «канал связи» вашего контракта с остальным миром.
Доступ и роли
Минимизируйте права. Используйте паттерны Ownable/AccessControl, timelock-контракты для «прозрачных» апгрейдов и экстренный паузер.
Минимальный пример ERC-20 на Solidity (демо)
pragma solidity ^0.8.20; contract SimpleToken { string public name = «DemoToken»; string public symbol = «DEMO»; uint8 public decimals = 18; uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); constructor(uint256 initialSupply) { totalSupply = initialSupply * 10**decimals; balanceOf[msg.sender] = totalSupply; emit Transfer(address(0), msg.sender, totalSupply); } function transfer(address to, uint256 value) external returns (bool) { require(balanceOf[msg.sender] >= value, «INSUFFICIENT»); unchecked { balanceOf[msg.sender] -= value; } balanceOf[to] += value; emit Transfer(msg.sender, to, value); return true; } function approve(address spender, uint256 value) external returns (bool) { allowance[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } function transferFrom(address from, address to, uint256 value) external returns (bool) { uint256 allowed = allowance[from][msg.sender]; require(balanceOf[from] >= value && allowed >= value, «INSUFFICIENT»); unchecked { balanceOf[from] -= value; allowance[from][msg.sender] = allowed — value; } balanceOf[to] += value; emit Transfer(from, to, value); return true; } }
На практике лучше использовать проверенные реализации (например, OpenZeppelin), чтобы снизить риски и ускорить разработку.
Инструменты: Hardhat/Foundry, тестовые сети и деплой
Hardhat
Удобен для плагинов, JS/TS-скриптов и локальной сети. Foundry — быстрый набор на Rust (forge/anvil/cast) с фокусом на тестах и fuzzing. Оба инструмента хорошо дополняют друг друга.
// hardhat.config.ts (простейший пример) import { HardhatUserConfig } from «hardhat/config»; import «@nomicfoundation/hardhat-toolbox»; const config: HardhatUserConfig = { solidity: «0.8.20», networks: { sepolia: { url: process.env.RPC_URL, accounts: [process.env.PRIVATE_KEY as string] } } }; export default config;
Другой пример:
// Скрипт деплоя (scripts/deploy.ts) import { ethers } from «hardhat»; async function main() { const Token = await ethers.getContractFactory(«SimpleToken»); const token = await Token.deploy(1_000_000); await token.waitForDeployment(); console.log(«Token at:», await token.getAddress()); } main().catch((e) => { console.error(e); process.exit(1); });
Фронтенд: подключение кошелька и вызовы контракта
На фронтенде можно работать с контрак том через ethers.js/viem/wagmi. Пользователь взаимодействует через кошелёк: вы читаете состояние контракта вызовами «view», а для транзакций создаёте запрос на подпись.
// Чтение баланса через ethers.js import { ethers } from «ethers»; import abi from «./SimpleTokenABI.json»; async function readBalance(address) { const provider = new ethers.BrowserProvider(window.ethereum); const contract = new ethers.Contract(«0xTOKEN», abi, provider); const bal = await contract.balanceOf(address); console.log(«Balance:», ethers.formatUnits(bal, 18)); }
Другой пример:
// Отправка транзакции import { ethers } from «ethers»; import abi from «./SimpleTokenABI.json»; async function transfer(to, amount) { const provider = new ethers.BrowserProvider(window.ethereum); const signer = await provider.getSigner(); const contract = new ethers.Contract(«0xTOKEN», abi, signer); const tx = await contract.transfer(to, ethers.parseUnits(amount, 18)); const receipt = await tx.wait(); console.log(«Tx mined:», receipt.transactionHash); }
Хранение данных: IPFS/Arweave и связь с контрактом
Большие файлы в блокчейн класть нельзя — это дорого и бессмысленно. Храните их в IPFS/Arweave, а в контракт заносите только их хэш (CID). Так любой сможет проверить, что контент не подменён.
// Псевдокод загрузки файла в IPFS через API-провайдера import { create } from «ipfs-http-client»; const ipfs = create({ url: «https://ipfs.infura.io:5001/api/v0» }); const res = await ipfs.add(fileBuffer); console.log(«CID:», res.cid.toString()); // Далее CID можно сохранить в смарт-контракт
Стандарты токенов и NFT: ERC-20/721/1155
ERC-20 — взаимозаменяемые токены (балансы как числа). ERC-721 — уникальные невзаимозаменяемые токены (NFT). ERC-1155 — мульти-токен стандарт, объединяющий фичи 20 и 721. Использование стандартов обеспечивает работу с кошельками, маркетплейсами и DeFi-протоколами «из коробки».
Безопасность: угрозы и практики защиты
Типовые векторы атак
Реентранси, переполнения/недостаток газа, некорректные апгрейды прокси, уязвимые внешние вызовы, ошибка в правах доступа, неточности в математике комиссий. Любая бага — риск потери средств.
Практики
Аудит сторонними командами, формальные проверки критичных инвариантов, fuzzing-тесты, лимиты на операции, timelock перед апгрейдами, bug bounty. Никогда не деплойте непроверенный код в мейннет.
Тестирование: unit, integration, fuzzing и property-based
Тестируйте не только позитивные сценарии, но и невалидные входы, экстремальные значения, повторные вызовы и экономические инварианты (например, «из воздуха не появляется прибыль»). Используйте coverage-отчёты и дифф-тесты при рефакторинге.
// Пример простого теста Hardhat + Mocha/Chai import { expect } from «chai»; import { ethers } from «hardhat»; describe(«SimpleToken», function () { it(«mints to deployer», async function () { const [owner] = await ethers.getSigners(); const Token = await ethers.getContractFactory(«SimpleToken»); const token = await Token.deploy(1000); const bal = await token.balanceOf(owner.address); expect(bal).to.equal(1000n * 10n ** 18n); }); });
Экономика протокола: комиссии, стимулы, токеномика
Любой DeFi- или игровой протокол — это экономика. Определите, откуда берётся доход, кто платит комиссию, как стимулируются поставщики ликвидности, как распределяются токены и как предотвращаются злонамеренные стратегии (например, сэндвич-атаки).
Масштабирование: L2-роллапы, сиквенсеры и мосты
Для снижения комиссий используют L2-решения (Optimistic/ZK-роллапы). Они обрабатывают транзакции вне L1 и периодически публикуют доказательства. При работе с несколькими сетями учитывайте задержки мостов, возможные «ребятчи» и риски централизованных сиквенсеров.
Аккаунт-абстракция (ERC-4337) и UX без боли
Аккаунт-абстракция позволяет создавать смарт-аккаунты с кастомной логикой подписи, социальным восстановлением и оплатой газа спонсором. Это сильно упрощает онбординг и делает UX ближе к привычным веб-приложениям.
Оракулы и внешние данные
Контракты сами не видят интернет. Для цен, погодных и прочих данных применяют оракулы (Chainlink и др.). Обеспечьте устойчивость к сбоям, проверяйте свежесть и консистентность фидов.
Логирование и аналитика: события, индексаторы, дашборды
В смарт-контрактах события — источник правды. Стройте на их основе субграфы, отправляйте метрики в аналитические системы, делайте публичные дашборды. Это упрощает поддержку и повышает доверие к протоколу.
Практический чек-лист перед релизом
1. Покрытие тестами критичных путей ≥ 90%.
2. Fuzzing и property-based тесты на инварианты.
3. Аудит кода и исправление отчёта.
4. Timelock + ролевой контроль + паузер.
5. План апгрейдов (прокси) и миграций.
6. Документация ABI/адресов/событий.
7. Онбординг: понятные модалки, лимиты, предупреждения.
8. Мониторинг событий, алерты и резервные RPC.
Пример минимального dApp-потока
1) Пользователь подключает кошелёк. 2) Фронтенд читает состояние контракта. 3) Пользователь инициирует действие (например, «перевести токены»). 4) Кошелёк показывает транзакцию, пользователь подписывает. 5) После майнинга фронтенд слушает событие и обновляет интерфейс. Ни логинов, ни паролей — только подписи и адреса.
// Подключение кошелька и получение адреса async function connect() { if (!window.ethereum) throw new Error(«No wallet»); const [acc] = await window.ethereum.request({ method: «eth_requestAccounts» }); return acc; }
Организация репозитория и DevOps для Web3
Разделяйте пакеты: контракты (Solidity), фронтенд (React/Vue/Svelte), скрипты деплоя, субграфы. Сборку и тесты запускайте в CI. Храните артефакты (ABI, адреса, схемы) в версии, сопоставленной с релизом. Для мейннета используйте многоступенчатое согласование и timelock.
Куда развиваться дальше
Освойте один EVM-стек (Solidity + Hardhat/Foundry + ethers/viem), затем попробуйте не-EVM (Solana/Aptos). Разберитесь в L2, ERC-4337 и индексаторах. Потренируйтесь на клон-проектах: простая биржа, NFT-минтер, вольт ERC-4626, DAO-голосование.



























