Розробка приложення на 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-nативний DeFi.
sBTC — механізм bridging BTC на Stacks. Користувач депонує BTC в multisig кошелек під контролем signer'ів мережі, отримує sBTC (1:1 peg) на Stacks. sBTC — це не wrapped BTC під контролем однієї компанії, це децентралізована 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". Контракт не може обійти ці conditions. Це захист від 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.







