Разработка приложения на Stacks (Bitcoin L2)
Stacks решает конкретную проблему: Bitcoin — наиболее безопасный и децентрализованный блокчейн, но у него нет смарт-контрактов и нет возможности строить DeFi/NFT/dApp поверх него. Большинство "Bitcoin DeFi" на самом деле строится на Ethereum или Solana с ценой BTC только как ориентиром. Stacks добавляет smart contract слой, который криптографически привязан к Bitcoin через Proof of Transfer (PoX) консенсус — каждый Stacks блок анкорируется в Bitcoin блок, получая его finality.
Архитектура Stacks: что важно понимать разработчику
Proof of Transfer (PoX) и Nakamoto Release
До Nakamoto Release (2024) у Stacks была главная проблема: finality транзакции зависела от Bitcoin блока, то есть ~10 минут. Это неприемлемо для большинства dApp.
Nakamoto Release изменил это кардинально. Теперь Stacks производит быстрые блоки (~5 сек) внутри одного Bitcoin блока, а finality всё равно привязана к Bitcoin через anchoring. Атака на историю Stacks требует атаки на Bitcoin — именно этого и хочет Bitcoin-нативный DeFi.
sBTC — механизм bridging BTC на Stacks. Пользователь депонирует BTC в multisig кошелёк под контролем signer'ов сети, получает sBTC (1:1 peg) на Stacks. sBTC — это не wrapped BTC под контролем одной компании, это decentralized peg с threshold signature схемой Signers сети. Для разработчика: sBTC это Stacks fungible token, которым можно оперировать в Clarity контрактах.
Clarity vs Solidity: принципиальные отличия
Clarity — язык смарт-контрактов Stacks. Не компилируется — интерпретируется. Это принципиальный дизайн-выбор.
Decidability: программы на Clarity decidable — можно статически определить все ветви выполнения и стоимость газа. Нет рекурсии, нет goto. Это делает аудит проще и formal verification возможным.
No reentrancy by design: Clarity не позволяет внешним вызовам изменять состояние после contract-call?, если контракт уже в стеке вызовов. Классическая reentrancy атака (DAO hack) невозможна конструктивно.
Post-conditions: вызывающий (не контракт!) может задать conditions на то, что произойдёт с его активами. Пользователь может сказать: "эту транзакцию принять только если с моего кошелька спишется не более X STX". Контракт не может эти условия обойти. Это защита от approvals exploit типа.
;; Post-condition пример (задаётся в транзакции, не в контракте)
;; Stacks.js автоматически добавляет при корректном использовании SDK
Разработка на Clarity: практика
Базовый смарт-контракт: fungible token
;; sip-010-trait — стандарт fungible token на Stacks (аналог ERC-20)
(impl-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait)
(define-fungible-token my-token u1000000000) ;; max supply
(define-constant contract-owner tx-sender)
(define-constant ERR_UNAUTHORIZED (err u100))
(define-constant ERR_INSUFFICIENT_BALANCE (err u101))
(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34))))
(begin
(asserts! (is-eq tx-sender sender) ERR_UNAUTHORIZED)
(try! (ft-transfer? my-token amount sender recipient))
(match memo to-print (print to-print) 0x)
(ok true)
)
)
(define-read-only (get-balance (who principal))
(ok (ft-get-balance my-token who))
)
(define-read-only (get-total-supply)
(ok (ft-get-supply my-token))
)
Взаимодействие с sBTC
sBTC имплементирует SIP-010 trait, значит работа с ним не отличается от любого другого fungible token:
;; Адрес sBTC контракта на mainnet
(define-constant SBTC_CONTRACT 'SM3VDXK3WZZSA84XXFKAFAF15SSKSY0NRY41R6Q5.sbtc-token)
(define-public (deposit-sbtc (amount uint))
(begin
;; Переводим sBTC от пользователя в этот контракт
(try! (contract-call? SBTC_CONTRACT transfer
amount
tx-sender
(as-contract tx-sender)
none
))
;; Логика протокола...
(ok true)
)
)
Maps и state management
;; Эквивалент mapping в Solidity
(define-map user-positions
{ user: principal }
{ deposited: uint, rewards-earned: uint, last-claim-block: uint }
)
(define-public (stake (amount uint))
(let (
(current-pos (default-to
{ deposited: u0, rewards-earned: u0, last-claim-block: block-height }
(map-get? user-positions { user: tx-sender })
))
)
(try! (contract-call? SBTC_CONTRACT transfer amount tx-sender (as-contract tx-sender) none))
(map-set user-positions
{ user: tx-sender }
{ deposited: (+ (get deposited current-pos) amount),
rewards-earned: (get rewards-earned current-pos),
last-claim-block: block-height }
)
(ok true)
)
)
Tooling и разработческая срега
Clarinet — главный инструмент разработки на Clarity (аналог Foundry для EVM):
# Создание проекта
clarinet new my-protocol
cd my-protocol
# Структура:
# contracts/ — .clar файлы
# tests/ — TypeScript тесты через Vitest
# Clarinet.toml
# Запуск тестов
clarinet test
# Локальный devnet
clarinet devnet start
Тесты на TypeScript через Clarinet SDK:
import { describe, it, expect } from "vitest";
import { Cl } from "@stacks/transactions";
import { initSimnet } from "@hirosystems/clarinet-sdk";
const simnet = await initSimnet();
const accounts = simnet.getAccounts();
const deployer = accounts.get("deployer")!;
describe("my-token", () => {
it("should transfer tokens correctly", () => {
const { result } = simnet.callPublicFn(
"my-token",
"transfer",
[Cl.uint(100), Cl.principal(deployer), Cl.principal(accounts.get("wallet_1")!), Cl.none()],
deployer
);
expect(result).toBeOk(Cl.bool(true));
});
});
Stacks Explorer (explorer.stacks.co) — браузер транзакций. Hiro Platform — managed deployment и monitoring. Stacks.js — официальный TypeScript SDK для frontend:
import { openContractCall } from "@stacks/connect";
import { uintCV, standardPrincipalCV } from "@stacks/transactions";
await openContractCall({
contractAddress: "SP...",
contractName: "my-protocol",
functionName: "stake",
functionArgs: [uintCV(1000000)], // 1 sBTC = 100,000,000 satoshi
postConditions: [
// Защита: пользователь отдаёт не более 1 sBTC
makeStandardFungiblePostCondition(
senderAddress, FungibleConditionCode.Equal, 1000000n, sBTCAssetInfo
)
],
});
Типичные use cases на Stacks
Bitcoin-native DeFi: lending/borrowing с sBTC как collateral. Пользователь не продаёт BTC, использует его как залог для получения стейблкоина. Протоколы типа Zest Protocol строятся именно на этом.
Bitcoin NFT: Ordinals позволяют вписывать данные в Bitcoin транзакции, Stacks добавляет programmable ownership и marketplace смарт-контракты. SIP-009 (NFT trait) — стандарт NFT на Stacks.
DAO и governance: Stacks подходит для управления Bitcoin treasury. Multisig на Stacks с Clarity логикой голосования, treasury в sBTC.
Names и identity: BNS (Bitcoin Name System) встроен в Stacks — .btc домены регистрируются через смарт-контракты.
Deployment и mainnet
Деплой контракта — on-chain транзакция, стоит STX (governance token сети). На mainnet:
clarinet deployments apply --mainnet
Верификация контракта публична — код виден в Explorer. Нет нужды отдельно верифицировать как на Etherscan.
Upgrade контрактов: Clarity контракты immutable после деплоя (нет proxy паттерна из коробки). Апгрейды возможны через явный migration паттерн — деплой нового контракта и migration state через on-chain вызовы. Это делает безопасность более предсказуемой, но требует планирования upgradability при проектировании.
Этапы разработки
| Фаза | Содержание | Длительность |
|---|---|---|
| Architecture | Проектирование контрактов, sBTC интеграция, post-conditions | 1–2 нед |
| Clarity contracts | Разработка core логики с тестами в Clarinet | 3–6 нед |
| Frontend | Stacks.js интеграция, Hiro Wallet / Leather wallet connect | 2–4 нед |
| Testnet | Деплой на testnet, публичное тестирование | 2–3 нед |
| Audit | Code review, формальная верификация через Clarity analyzer | 2–4 нед |
| Mainnet | Деплой, мониторинг через Hiro Platform | 1 нед |
Преимущество Stacks для разработчика из EVM мира: меньше attack vectors (нет reentrancy, нет overflow), статически анализируемый код, post-conditions как built-in защита. Компромисс: меньшая экосистема инструментов, меньший developer community, специфичный синтаксис Lisp-подобного Clarity.







