diff --git a/docs/build/.gitbook.yaml b/docs/build/.gitbook.yaml new file mode 100644 index 0000000000..253e89b1e5 --- /dev/null +++ b/docs/build/.gitbook.yaml @@ -0,0 +1,36 @@ +root: ./ + +redirects: + guides-and-tutorials/hello-stacks-quickstart-tutorial: README.md + guides-and-tutorials/clarity-crash-course: clarity-crash-course.md + + # bitcoin-integration redirects + guides-and-tutorials/bitcoin-integration/sending-bitcoin-with-leather-wallet: bitcoin-integration/sending-bitcoin-with-leather.md + guides-and-tutorials/bitcoin-integration/parsing-a-bitcoin-transaction: bitcoin-integration/parsing-a-bitcoin-transaction.md + guides-and-tutorials/bitcoin-integration/verifying-a-bitcoin-transaction: bitcoin-integration/verifying-a-bitcoin-transaction.md + + # create-tokens redirects + guides-and-tutorials/tokens/creating-a-nft: create-tokens/creating-a-nft.md + guides-and-tutorials/tokens/creating-a-fungible-token: create-tokens/creating-a-ft.md + + # build-a-frontend redirects + guides-and-tutorials/frontend/authentication-with-stacks.js: build-a-frontend/authentication-with-stacks.js.md + guides-and-tutorials/frontend/post-conditions-with-stacks.js: build-a-frontend/post-conditions-with-stacks.js.md + guides-and-tutorials/frontend/sending-transactions-with-stacks.js: build-a-frontend/sending-transactions-with-stacks.js.md + + # testing-smart-contracts redirects + guides-and-tutorials/testing-smart-contracts/fuzz-testing: testing-smart-contracts/fuzz-testing.md + + # sbtc redirects + guides-and-tutorials/sbtc/best-practices-for-running-an-sbtc-signer: sbtc/best-practices-for-running-an-sbtc-signer.md + guides-and-tutorials/sbtc/earn-sbtc-rewards: sbtc/how-to-earn-sbtc-rewards.md + guides-and-tutorials/sbtc/how-to-run-sbtc-signer: sbtc/how-to-run-sbtc-signer.md + guides-and-tutorials/sbtc/how-to-use-the-sbtc-bridge: sbtc/how-to-use-the-sbtc-bridge.md + guides-and-tutorials/sbtc/sbtc-builder-quickstart: sbtc/sbtc-builder-quickstart.md + + # oracles redirects + guides-and-tutorials/oracles: price-oracles.md + + # misc.-guides redirects + guides-and-tutorials/build-a-borrowing-and-lending-protocol: misc.-guides/build-a-borrowing-and-lending-protocol.md + guides-and-tutorials/community-tutorials: misc.-guides/community-tutorials.md diff --git a/docs/build/.gitbook/assets/Frame 316125324.jpg b/docs/build/.gitbook/assets/Frame 316125324.jpg new file mode 100644 index 0000000000..8964e1069c Binary files /dev/null and b/docs/build/.gitbook/assets/Frame 316125324.jpg differ diff --git a/docs/build/.gitbook/assets/Frame 316126251.jpg b/docs/build/.gitbook/assets/Frame 316126251.jpg new file mode 100644 index 0000000000..ecaaf0c522 Binary files /dev/null and b/docs/build/.gitbook/assets/Frame 316126251.jpg differ diff --git a/docs/build/.gitbook/assets/GcHiZWjXgAA21Kf.jpeg b/docs/build/.gitbook/assets/GcHiZWjXgAA21Kf.jpeg new file mode 100644 index 0000000000..0209b9426a Binary files /dev/null and b/docs/build/.gitbook/assets/GcHiZWjXgAA21Kf.jpeg differ diff --git a/docs/build/.gitbook/assets/Group 316124778 (1).png b/docs/build/.gitbook/assets/Group 316124778 (1).png new file mode 100644 index 0000000000..5bfe3c487a Binary files /dev/null and b/docs/build/.gitbook/assets/Group 316124778 (1).png differ diff --git a/docs/build/.gitbook/assets/Group 316124778 (2).png b/docs/build/.gitbook/assets/Group 316124778 (2).png new file mode 100644 index 0000000000..9b806f1b58 Binary files /dev/null and b/docs/build/.gitbook/assets/Group 316124778 (2).png differ diff --git a/docs/build/.gitbook/assets/Group 316124778 (3).png b/docs/build/.gitbook/assets/Group 316124778 (3).png new file mode 100644 index 0000000000..3319d3c070 Binary files /dev/null and b/docs/build/.gitbook/assets/Group 316124778 (3).png differ diff --git a/docs/build/.gitbook/assets/Group 316124778 (4).png b/docs/build/.gitbook/assets/Group 316124778 (4).png new file mode 100644 index 0000000000..86eb052ce8 Binary files /dev/null and b/docs/build/.gitbook/assets/Group 316124778 (4).png differ diff --git a/docs/build/.gitbook/assets/image (1).png b/docs/build/.gitbook/assets/image (1).png new file mode 100644 index 0000000000..90ba3f571b Binary files /dev/null and b/docs/build/.gitbook/assets/image (1).png differ diff --git a/docs/build/.gitbook/assets/image (10).png b/docs/build/.gitbook/assets/image (10).png new file mode 100644 index 0000000000..8177a76839 Binary files /dev/null and b/docs/build/.gitbook/assets/image (10).png differ diff --git a/docs/build/.gitbook/assets/image (11).png b/docs/build/.gitbook/assets/image (11).png new file mode 100644 index 0000000000..ac78447329 Binary files /dev/null and b/docs/build/.gitbook/assets/image (11).png differ diff --git a/docs/build/.gitbook/assets/image (12).png b/docs/build/.gitbook/assets/image (12).png new file mode 100644 index 0000000000..9210b58c1a Binary files /dev/null and b/docs/build/.gitbook/assets/image (12).png differ diff --git a/docs/build/.gitbook/assets/image (13).png b/docs/build/.gitbook/assets/image (13).png new file mode 100644 index 0000000000..715ec833fe Binary files /dev/null and b/docs/build/.gitbook/assets/image (13).png differ diff --git a/docs/build/.gitbook/assets/image (14).png b/docs/build/.gitbook/assets/image (14).png new file mode 100644 index 0000000000..514951c8b0 Binary files /dev/null and b/docs/build/.gitbook/assets/image (14).png differ diff --git a/docs/build/.gitbook/assets/image (2).png b/docs/build/.gitbook/assets/image (2).png new file mode 100644 index 0000000000..1c637648c3 Binary files /dev/null and b/docs/build/.gitbook/assets/image (2).png differ diff --git a/docs/build/.gitbook/assets/image (3).png b/docs/build/.gitbook/assets/image (3).png new file mode 100644 index 0000000000..461e4360ca Binary files /dev/null and b/docs/build/.gitbook/assets/image (3).png differ diff --git a/docs/build/.gitbook/assets/image (4).png b/docs/build/.gitbook/assets/image (4).png new file mode 100644 index 0000000000..445dd9848d Binary files /dev/null and b/docs/build/.gitbook/assets/image (4).png differ diff --git a/docs/build/.gitbook/assets/image (5).png b/docs/build/.gitbook/assets/image (5).png new file mode 100644 index 0000000000..ce90bce016 Binary files /dev/null and b/docs/build/.gitbook/assets/image (5).png differ diff --git a/docs/build/.gitbook/assets/image (6).png b/docs/build/.gitbook/assets/image (6).png new file mode 100644 index 0000000000..850b421678 Binary files /dev/null and b/docs/build/.gitbook/assets/image (6).png differ diff --git a/docs/build/.gitbook/assets/image (7).png b/docs/build/.gitbook/assets/image (7).png new file mode 100644 index 0000000000..35bf7c4f53 Binary files /dev/null and b/docs/build/.gitbook/assets/image (7).png differ diff --git a/docs/build/.gitbook/assets/image (8).png b/docs/build/.gitbook/assets/image (8).png new file mode 100644 index 0000000000..c866b0dc8e Binary files /dev/null and b/docs/build/.gitbook/assets/image (8).png differ diff --git a/docs/build/.gitbook/assets/image (9).png b/docs/build/.gitbook/assets/image (9).png new file mode 100644 index 0000000000..8f6083d420 Binary files /dev/null and b/docs/build/.gitbook/assets/image (9).png differ diff --git a/docs/build/.gitbook/assets/image 11.png b/docs/build/.gitbook/assets/image 11.png new file mode 100644 index 0000000000..01595e7d39 Binary files /dev/null and b/docs/build/.gitbook/assets/image 11.png differ diff --git a/docs/build/.gitbook/assets/image 16.png b/docs/build/.gitbook/assets/image 16.png new file mode 100644 index 0000000000..099e6b71c7 Binary files /dev/null and b/docs/build/.gitbook/assets/image 16.png differ diff --git a/docs/build/.gitbook/assets/image 2.png b/docs/build/.gitbook/assets/image 2.png new file mode 100644 index 0000000000..b9cb30d7a1 Binary files /dev/null and b/docs/build/.gitbook/assets/image 2.png differ diff --git a/docs/build/.gitbook/assets/image 22.png b/docs/build/.gitbook/assets/image 22.png new file mode 100644 index 0000000000..ff2615e114 Binary files /dev/null and b/docs/build/.gitbook/assets/image 22.png differ diff --git a/docs/build/.gitbook/assets/image 3.png b/docs/build/.gitbook/assets/image 3.png new file mode 100644 index 0000000000..6d0423b88b Binary files /dev/null and b/docs/build/.gitbook/assets/image 3.png differ diff --git a/docs/build/.gitbook/assets/image 4.png b/docs/build/.gitbook/assets/image 4.png new file mode 100644 index 0000000000..3842a7c913 Binary files /dev/null and b/docs/build/.gitbook/assets/image 4.png differ diff --git a/docs/build/.gitbook/assets/image 5.png b/docs/build/.gitbook/assets/image 5.png new file mode 100644 index 0000000000..6edd5519d7 Binary files /dev/null and b/docs/build/.gitbook/assets/image 5.png differ diff --git a/docs/build/.gitbook/assets/image 6.png b/docs/build/.gitbook/assets/image 6.png new file mode 100644 index 0000000000..55f9302b83 Binary files /dev/null and b/docs/build/.gitbook/assets/image 6.png differ diff --git a/docs/build/.gitbook/assets/image 7.png b/docs/build/.gitbook/assets/image 7.png new file mode 100644 index 0000000000..26d2b2a603 Binary files /dev/null and b/docs/build/.gitbook/assets/image 7.png differ diff --git a/docs/build/.gitbook/assets/image 8.png b/docs/build/.gitbook/assets/image 8.png new file mode 100644 index 0000000000..1d60b4bce2 Binary files /dev/null and b/docs/build/.gitbook/assets/image 8.png differ diff --git a/docs/build/.gitbook/assets/image.png b/docs/build/.gitbook/assets/image.png new file mode 100644 index 0000000000..cc6def5437 Binary files /dev/null and b/docs/build/.gitbook/assets/image.png differ diff --git a/docs/build/.gitbook/assets/stacks-devs-twitter-header.png b/docs/build/.gitbook/assets/stacks-devs-twitter-header.png new file mode 100644 index 0000000000..5198600901 Binary files /dev/null and b/docs/build/.gitbook/assets/stacks-devs-twitter-header.png differ diff --git a/docs/build/README.md b/docs/build/README.md new file mode 100644 index 0000000000..2ca4918f66 --- /dev/null +++ b/docs/build/README.md @@ -0,0 +1,686 @@ +--- +cover: .gitbook/assets/stacks-devs-twitter-header.png +coverY: 0 +--- + +# Developer Quickstart + +

source: Hiro blog

+ +## Build Your First Stacks App in 30 Minutes + +Looking to see what building on Stacks is all about? You're in the right place. + +This tutorial will help you build a working Stacks application in just 30 minutes. You'll learn the essential tools and concepts needed to build decentralized applications on Stacks, the leading Bitcoin L2. + +What you'll build: A simple message board where users can post messages to the blockchain and read messages from others. + +What you'll learn: + +* How to write a Clarity smart contract +* How to deploy contracts to Stacks testnet +* How to connect a wallet to your app +* How to interact with contracts from a frontend + +Prerequisites: + +* Basic familiarity with web development (HTML, CSS, JavaScript) +* A modern web browser +* 30 minutes of your time + +Let's get started! + +{% stepper %} +{% step %} +### Step 1: Set Up Your Wallet (5 minutes) + +First, you'll need a Stacks wallet to interact with the blockchain. + +#### Install Leather Wallet + +1. Visit [leather.io](https://leather.io/) and install the browser extension +2. Create a new wallet or import an existing one +3. **Important**: Switch to the **Testnet** network in your wallet settings +4. Get testnet STX tokens from the [Stacks Testnet Faucet](https://explorer.hiro.so/sandbox/faucet?chain=testnet) + +{% hint style="info" %} +Testnet STX tokens are free and used for testing. They have no real value but let you experiment with Stacks development without cost. +{% endhint %} + +Your wallet is now ready for testnet development! + +{% hint style="info" %} +You don't have to use Leather, two other wallets popular with Stacks users are [Xverse](https://xverse.app/) and [Asigna](https://asigna.io/) if you need a multisig. +{% endhint %} +{% endstep %} + +{% step %} +### Step 2: Write Your First Clarity Contract (10 minutes) + +Clarity is Stacks' smart contract language, designed for safety and predictability. Let's write a simple message board contract. + +Clarity is inspired by LISP and uses a functional programming approach. Everything in Clarity is an expression wrapped in parentheses. This can be a bit overwhelming at first if you are used to languages like JavaScript or Solidity, but the learning curve is short and Clarity is a simple language to understand once you dive in and start using it. + +For a more detailed introduction, check out the [Clarity Crash Course](clarity-crash-course.md) in the docs. + +#### Write the Contract + +Open [Clarity Playground](https://play.hiro.so/) in your browser. This is an online IDE where you can write and test Clarity code without installing anything. + +Delete the existing code and replace it with this message board contract: + +```clarity +;; Simple Message Board Contract +;; This contract allows users to post and read messages + +;; Define a map to store messages +;; Key: message ID (uint), Value: message content (string-utf8 280) +(define-map messages uint (string-utf8 280)) + +;; Define a map to store message authors +(define-map message-authors uint principal) + +;; Counter for message IDs +(define-data-var message-count uint u0) + +;; Public function to add a new message +(define-public (add-message (content (string-utf8 280))) + (let ((id (+ (var-get message-count) u1))) + (map-set messages id content) + (map-set message-authors id tx-sender) + (var-set message-count id) + (ok id))) + +;; Read-only function to get a message by ID +(define-read-only (get-message (id uint)) + (map-get? messages id)) + +;; Read-only function to get message author +(define-read-only (get-message-author (id uint)) + (map-get? message-authors id)) + +;; Read-only function to get total message count +(define-read-only (get-message-count) + (var-get message-count)) + +;; Read-only function to get the last few messages +(define-read-only (get-recent-messages (count uint)) + (let ((total-count (var-get message-count))) + (if (> count total-count) + (map get-message (list u1 u2 u3 u4 u5)) + (map get-message (list + (- total-count (- count u1)) + (- total-count (- count u2)) + (- total-count (- count u3)) + (- total-count (- count u4)) + (- total-count (- count u5))))))) +``` + +#### Test the Contract + +Click "Deploy", and go to the command line in the bottom right corner and try calling the functions. + +We are using the `contract-call?` method to call the functions in the contract that we just deployed within the playground. + +```clarity +;; Test adding a message +(contract-call? .contract-1 add-message u"Hello, Stacks!") + +;; Test reading the message +(contract-call? .contract-1 get-message u1) + +;; Test getting the count +(contract-call? .contract-1 get-message-count) +``` + +You should see the contract working in the evaluation panel on the right! + +#### Key Clarity Concepts Explained + +* `define-map`: creates a map / key-value store on-chain (like a simple table). +* `define-data-var`: creates a single persistent variable (used for counters, settings). +* `define-public`: public function that can modify blockchain state. +* `define-read-only`: functions that can only read state and don't modify it. +* `tx-sender`: automatically set to the address of whoever called the function (useful for authentication). +* `let`: create local variables inside functions. +* All public functions return a response type: `(ok value)` or `(err error)`. + +
+ +🔍 Deep Dive: Understanding the Contract Code (Optional) + +Want to understand exactly what each part of the contract is doing? Let's walk through every function and concept used in our message board contract. Links to the official documentation are included for each function, so you may dive deeper if you want. + +**How We Store Data on the Blockchain** + +We use `define-map` to create what's essentially a database table on the blockchain: + +```clarity +(define-map messages uint (string-utf8 280)) +``` + +We also create another map to track who wrote each message: + +```clarity +(define-map message-authors uint principal) +``` + +We keep a message counter with: + +```clarity +(define-data-var message-count uint u0) +``` + +**The Heart of Our Contract: Adding Messages** + +The primary state-changing function: + +```clarity +(define-public (add-message (content (string-utf8 280))) + (let ((id (+ (var-get message-count) u1))) + (map-set messages id content) + (map-set message-authors id tx-sender) + (var-set message-count id) + (ok id))) +``` + +Key points: + +* `var-get` reads the message counter. +* `+` uses prefix notation (LISP-style). +* `map-set` stores the content and author. +* `tx-sender` is the caller's address. +* The function returns `(ok id)` on success. + +**Reading Messages Back** + +Example read-only function: + +```clarity +(define-read-only (get-message (id uint)) + (map-get? messages id)) +``` + +`map-get?` returns `(some value)` or `none`, forcing explicit handling of missing data. + +Other read-only functions: + +```clarity +(define-read-only (get-message-author (id uint)) + (map-get? message-authors id)) + +(define-read-only (get-message-count) + (var-get message-count)) +``` + +**A More Complex Function: Getting Recent Messages** + +```clarity +(define-read-only (get-recent-messages (count uint)) + (let ((total-count (var-get message-count))) + (if (> count total-count) + (map get-message (list u1 u2 u3 u4 u5)) + (map get-message (list + (- total-count (- count u1)) + (- total-count (- count u2)) + (- total-count (- count u3)) + (- total-count (- count u4)) + (- total-count (- count u5))))))) +``` + +This shows conditional logic (`if`), prefix operators, `map` to apply a function over a list, and list arithmetic to determine recent message IDs. + +**What Makes Clarity Special** + +* Response types: functions return `(ok value)` or `(err error)` and state changes are reverted on `err`. +* Optional types: `map-get?` returns `some` or `none` instead of null. +* Static analysis at deployment time prevents many runtime errors. +* No recursion or unbounded loops (decidability), making execution costs predictable. +* `tx-sender` vs `contract-caller` — be cautious about which you use for authorization. + +{% hint style="warning" %} +**Important**: Be careful when using `tx-sender` vs `contract-caller` in your contracts. While `tx-sender` refers to the original transaction sender and remains constant throughout the entire transaction chain, `contract-caller` refers to the most recent principal in the transaction chain and can change with each internal function or contract call. This difference is crucial for security - malicious contracts can potentially exploit `tx-sender`'s persistent context to bypass admin checks if you're not careful. For simple contracts like our message board, `tx-sender` is appropriate, but for more complex authorization logic, consider whether you need the original sender or the immediate caller. + +For more details on this, check out [this excellent blog post](https://www.setzeus.com/public-blog-post/clarity-carefully-tx-sender) from Clarity developer [setzeus](https://x.com/setzeus). +{% endhint %} + +Clarity's type safety and static analysis help catch issues at deploy time. This contract demonstrates common patterns: maps, data vars, public functions, read-only queries, tx-sender usage, and predictable response types. + +
+{% endstep %} + +{% step %} +### Step 3: Deploy Your Contract (5 minutes) + +Now let's deploy your contract to the Stacks testnet so you can interact with it from a web application. + +#### Deploy via Stacks Explorer + +1. Visit the [Stacks Explorer Sandbox](https://explorer.hiro.so/sandbox/deploy?chain=testnet) +2. Connect your Leather wallet (make sure you're on testnet) +3. Paste your contract code into the editor +4. Give your contract a name (e.g., "message-board") or just use the default generated name +5. Click "Deploy Contract" +6. Confirm the transaction in your wallet + +The deployment should only take a few seconds. Once complete, you'll see your contract address in the explorer. Here's [an example transaction](https://explorer.hiro.so/txid/0x3df7b597d1bbb3ce1598b1b0e28b7cbed38345fcf3fb33ae387165e13085e5d8?chain=testnet) deploying this contract. + +#### Test Your Deployed Contract + +1. In the explorer, find your deployed contract +2. Scroll down a bit and click on "Available Functions" to view its functions +3. Try calling `add-message` with a test message (you'll need to change the post conditions toggle to allow mode, there is a dedicated docs page talking about [Post Conditions](build-a-frontend/post-conditions-with-stacks.js.md) on Stacks) +4. Call `get-message` with ID `u1` to read it back +5. Call `get-message-count` to see the total + +Your contract is now live and functional on the blockchain! +{% endstep %} + +{% step %} +### Step 4: Build the Frontend (10 minutes) + +Let's create a simple web interface to interact with your contract. + +#### Set Up the Project + +Create a new React project: + +```bash +npm create vite@latest my-message-board -- --template react +cd my-message-board +npm install +``` + +Install the Stacks.js libraries: + +```bash +npm install @stacks/connect @stacks/transactions @stacks/network +``` + +#### Create the App Component + +Replace the contents of `src/App.jsx` with the following: + +{% hint style="info" %} +Since this is a quickstart, we won't dive into a long explanation of exactly what this code is doing. We suggest going and checking out [Hiro's Docs](https://docs.hiro.so/stacks/stacks.js) in order to get a handle on how stacks.js works. +{% endhint %} + +```jsx +import { useState, useEffect } from "react"; +import { connect, disconnect, isConnected, request } from "@stacks/connect"; +import { + fetchCallReadOnlyFunction, + stringUtf8CV, + uintCV, +} from "@stacks/transactions"; +import "./App.css"; + +const network = "testnet"; + +// Replace with your contract address +const CONTRACT_ADDRESS = "YOUR_CONTRACT_ADDRESS_HERE"; +const CONTRACT_NAME = "message-board"; + +function App() { + const [connected, setConnected] = useState(false); + const [messages, setMessages] = useState([]); + const [newMessage, setNewMessage] = useState(""); + const [loading, setLoading] = useState(false); + + useEffect(() => { + setConnected(isConnected()); + if (isConnected()) { + loadMessages(); + } + }, []); + + // Check for connection changes + useEffect(() => { + const checkConnection = () => { + const connectionStatus = isConnected(); + if (connectionStatus !== connected) { + setConnected(connectionStatus); + if (connectionStatus) { + loadMessages(); + } + } + }; + + const intervalId = setInterval(checkConnection, 500); + return () => clearInterval(intervalId); + }, [connected]); + + const connectWallet = async () => { + try { + await connect({ + appDetails: { + name: "Message Board", + icon: window.location.origin + "/logo.svg", + }, + onFinish: () => { + setConnected(true); + // Small delay to ensure connection is fully established + setTimeout(() => { + loadMessages(); + }, 100); + }, + }); + } catch (error) { + console.error("Connection failed:", error); + } + }; + + const disconnectWallet = () => { + disconnect(); + setConnected(false); + setMessages([]); + }; + + const loadMessages = async () => { + try { + // Get message count + const countResult = await fetchCallReadOnlyFunction({ + contractAddress: CONTRACT_ADDRESS, + contractName: CONTRACT_NAME, + functionName: "get-message-count", + functionArgs: [], + network, + senderAddress: CONTRACT_ADDRESS, + }); + + const count = parseInt(countResult.value); + + // Load recent messages + const messagePromises = []; + for (let i = Math.max(1, count - 4); i <= count; i++) { + messagePromises.push( + fetchCallReadOnlyFunction({ + contractAddress: CONTRACT_ADDRESS, + contractName: CONTRACT_NAME, + functionName: "get-message", + functionArgs: [uintCV(i)], + network, + senderAddress: CONTRACT_ADDRESS, + }) + ); + } + + const messageResults = await Promise.all(messagePromises); + const loadedMessages = messageResults + .map((result, index) => ({ + id: count - messageResults.length + index + 1, + content: result.value.value, + })) + .filter((msg) => msg.content !== undefined); + + setMessages(loadedMessages); + } catch (error) { + console.error("Error loading messages:", error); + } + }; + + const postMessage = async () => { + if (!newMessage.trim()) return; + + setLoading(true); + try { + const result = await request("stx_callContract", { + contract: `${CONTRACT_ADDRESS}.${CONTRACT_NAME}`, + functionName: "add-message", + functionArgs: [stringUtf8CV(newMessage)], + network, + }); + + console.log("Transaction submitted:", result.txid); + setNewMessage(""); + + // Reload messages after a delay to allow the transaction to process + setTimeout(() => { + loadMessages(); + setLoading(false); + }, 2000); + } catch (error) { + console.error("Error posting message:", error); + setLoading(false); + } + }; + + return ( +
+
+

📝 Stacks Message Board

+ + {!connected ? ( + + ) : ( + + )} +
+ + {connected && ( +
+
+

Post a Message

+
+ setNewMessage(e.target.value)} + placeholder="What's on your mind?" + maxLength={280} + disabled={loading} + /> + +
+
+ +
+

Recent Messages

+ + {messages.length === 0 ? ( +

No messages yet. Be the first to post!

+ ) : ( +
    + {messages.map((message) => ( +
  • + Message #{message.id}: {message.content} +
  • + ))} +
+ )} +
+
+ )} +
+ ); +} + +export default App; +``` + +#### Add Basic Styling + +Update `src/App.css`: + +```css +.App { + max-width: 800px; + width: 100%; + padding: 20px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", + "Helvetica Neue", sans-serif; +} + +.App-header { + text-align: center; + margin-bottom: 40px; + padding: 20px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border-radius: 12px; + color: white; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); +} + +.App-header h1 { + color: white; + margin-bottom: 20px; + font-size: 2.5rem; + font-weight: 700; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.connect-button, +.disconnect-button { + background-color: rgba(255, 255, 255, 0.2); + color: white; + border: 2px solid rgba(255, 255, 255, 0.3); + padding: 12px 28px; + border-radius: 8px; + cursor: pointer; + font-size: 16px; + font-weight: 600; + transition: all 0.3s ease; + backdrop-filter: blur(10px); +} + +.connect-button:hover, +.disconnect-button:hover { + background-color: rgba(255, 255, 255, 0.3); + border-color: rgba(255, 255, 255, 0.5); + transform: translateY(-2px); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); +} + +.post-message { + margin-bottom: 40px; + padding: 20px; + border: 1px solid #e5e7eb; + border-radius: 8px; +} + +.message-input { + display: flex; + gap: 10px; + margin-top: 10px; +} + +.message-input input { + flex: 1; + padding: 10px; + border: 1px solid #d1d5db; + border-radius: 4px; + font-size: 16px; +} + +.message-input button { + background-color: #10b981; + color: white; + border: none; + padding: 10px 20px; + border-radius: 4px; + cursor: pointer; +} + +.message-input button:disabled { + background-color: #9ca3af; + cursor: not-allowed; +} + +.messages { + padding: 20px; + border: 1px solid #e5e7eb; + border-radius: 8px; +} + +.refresh-button { + background-color: #6b7280; + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + margin-bottom: 20px; +} + +.messages ul { + list-style: none; + padding: 0; +} + +.messages li { + padding: 10px; + border-bottom: 1px solid #e5e7eb; + margin-bottom: 10px; +} + +.messages li:last-child { + border-bottom: none; +} +``` + +#### Update the Contract Address + +1. Go back to the Stacks Explorer and find your deployed contract +2. Copy the contract address (it looks like `ST1ABC...123.message-board`) +3. Replace `YOUR_CONTRACT_ADDRESS_HERE` in the App.jsx file with your actual contract address and the contract name with the actual name + +#### Run Your App + +```bash +npm run dev +``` + +Visit `http://localhost:5173` and you should see your message board app! Connect your wallet and try posting a message. +{% endstep %} +{% endstepper %} + +## Congratulations! 🎉 + +You've just built your first Stacks application! Here's what you accomplished: + +* ✅ Wrote a Clarity smart contract with data storage and public functions +* ✅ Deployed the contract to Stacks testnet +* ✅ Built a React frontend that connects to a Stacks wallet +* ✅ Integrated your frontend with your smart contract +* ✅ Posted and read data from the blockchain + +## Next Steps + +Now that you have the basics down, here are some ways to continue your Stacks development journey: + +### Learn More About Clarity + +* [**Clarity Book**](https://book.clarity-lang.org/): Comprehensive guide to Clarity development +* [**Clarity Reference**](https://docs.stacks.co/docs/clarity): Complete documentation of Clarity functions +* [**Clarity Crash Course**](https://docs.stacks.co/docs/clarity-crash-course): Quick introduction to Clarity concepts + +### Explore Advanced Features + +* Error Handling: Learn about Clarity's `try!` and `unwrap!` functions +* Access Control: Implement admin functions and permissions +* Token Standards: Build fungible (SIP-010) and non-fungible (SIP-009) tokens + +### Development Tools + +* [**Clarinet**](https://github.com/hirosystems/clarinet): Local development environment for Clarity +* [**Hiro Platform**](https://platform.hiro.so/): Hosted development environment +* [**Stacks Explorer**](https://explorer.stacks.co/): View transactions and contracts on mainnet + +### Community Resources + +* [**Stacks Discord**](https://discord.gg/stacks): Connect with other developers +* [**Stacks Forum**](https://forum.stacks.org/): Ask questions and share projects +* [**Stacks GitHub**](https://github.com/stacks-network): Contribute to the ecosystem + diff --git a/docs/build/SUMMARY.md b/docs/build/SUMMARY.md new file mode 100644 index 0000000000..9505654460 --- /dev/null +++ b/docs/build/SUMMARY.md @@ -0,0 +1,30 @@ +# Table of contents + +* [Developer Quickstart](README.md) +* [Clarity Crash Course](clarity-crash-course.md) +* [Bitcoin Integration](bitcoin-integration/README.md) + * [Sending Bitcoin with Leather](bitcoin-integration/sending-bitcoin-with-leather.md) + * [Verifying a Bitcoin Transaction](bitcoin-integration/verifying-a-bitcoin-transaction.md) + * [Parsing a Bitcoin Transaction](bitcoin-integration/parsing-a-bitcoin-transaction.md) +* [Create Tokens](create-tokens/README.md) + * [Creating a NFT](create-tokens/creating-a-nft.md) + * [Creating a FT](create-tokens/creating-a-ft.md) +* [Build a Frontend](build-a-frontend/README.md) + * [Post Conditions with Stacks.js](build-a-frontend/post-conditions-with-stacks.js.md) + * [Authentication with Stacks.js](build-a-frontend/authentication-with-stacks.js.md) + * [Sending Transactions with Stacks.js](build-a-frontend/sending-transactions-with-stacks.js.md) +* [Testing Smart Contracts](testing-smart-contracts/README.md) + * [Fuzz Testing](testing-smart-contracts/fuzz-testing.md) +* [sBTC](sbtc/README.md) + * [sBTC Builder Quickstart](sbtc/sbtc-builder-quickstart.md) + * [How to Run sBTC Signer](sbtc/how-to-run-sbtc-signer.md) + * [Best Practices for running an sBTC Signer](sbtc/best-practices-for-running-an-sbtc-signer.md) + * [How to Use the sBTC Bridge](sbtc/how-to-use-the-sbtc-bridge.md) + * [How to Use the sBTC Bridge with Fordefi](sbtc/how-to-use-the-sbtc-bridge-with-fordefi.md) + * [How to Earn sBTC Rewards](sbtc/how-to-earn-sbtc-rewards.md) +* [Price Oracles](price-oracles.md) + +## Misc. Guides + +* [Build a Borrowing & Lending Protocol](misc.-guides/build-a-borrowing-and-lending-protocol.md) +* [Community Tutorials](misc.-guides/community-tutorials.md) diff --git a/docs/build/bitcoin-integration/README.md b/docs/build/bitcoin-integration/README.md new file mode 100644 index 0000000000..17ee26dcbf --- /dev/null +++ b/docs/build/bitcoin-integration/README.md @@ -0,0 +1,3 @@ +# Bitcoin Integration + +One of the unique features of the Stack chain and the Clarity language is that it allows for directly reading from the Bitcoin chain itself. These tutorials walk you through some different ways you can accomplish that. diff --git a/docs/build/bitcoin-integration/parsing-a-bitcoin-transaction.md b/docs/build/bitcoin-integration/parsing-a-bitcoin-transaction.md new file mode 100644 index 0000000000..892c9f853d --- /dev/null +++ b/docs/build/bitcoin-integration/parsing-a-bitcoin-transaction.md @@ -0,0 +1,79 @@ +# Parsing a Bitcoin Transaction + +While we can verify that a transaction was mined using a library and Clarity's built-in functions (see the Verifying a transaction on the BTC chain docs), we can also parse a Bitcoin transaction using Clarity directly. + +This doesn't require access to the chain — all we need is the raw transaction hex. + +If you aren't familiar with how Bitcoin transactions are encoded in raw form, take a quick look at that. The short version is that all of the data from a Bitcoin transaction is encoded in hex form in a string of bytes; we can slice out pieces of that hex value to pull out all of our transaction data. + +The process to do this is relatively complex, but the Clarity-Bitcoin library provides a function called `parse-tx` that makes this simple. All we need to do is pass it a raw transaction hex and we get back the data of the transaction, including inputs and outputs. + +{% hint style="warning" %} +Note that currently the library only supports legacy transactions. Work to support segwit and taproot transactions is underway. +{% endhint %} + +You can view a deployed version of the library on the explorer: https://explorer.hiro.so/txid/0xd493b9ada8899be8773d3f55b21d300ef83ac5c0dd38c8a4dd52a295bd71d539?chain=mainnet + +And the GitHub repo here: https://github.com/friedger/clarity-bitcoin + +The `parse-tx` function looks like this: + +{% code title="parse-tx.clar" %} +```clarity +(define-read-only (parse-tx (tx (buff 1024))) + (let ((ctx { txbuff: tx, index: u0}) + (parsed-version (try! (read-uint32 ctx))) + (parsed-txins (try! (read-txins (get ctx parsed-version)))) + (parsed-txouts (try! (read-txouts (get ctx parsed-txins)))) + (parsed-locktime (try! (read-uint32 (get ctx parsed-txouts))))) + (ok {version: (get uint32 parsed-version), + ins: (get txins parsed-txins), + outs: (get txouts parsed-txouts), + locktime: (get uint32 parsed-locktime)}))) +``` +{% endcode %} + +Example raw transaction hex (from a block explorer API like mempool): + +`0200000000010196277c04c986c1ad78c909287fd12dba2924324699a0232e0533f46a6a3916bb0100000000ffffffff026400000000000000160014274ae586ad2035efb4c25049c155f98310d7e106ca16440000000000160014599bcef6387256c6b019030c421b4a4d382fe2600247304402204d94a1e4047ca38a450177ccb6f88585ca147f1939df343d8ac5d962c5f35bb302206f7fa42c21c47ebccdc460393d35c5dfd3b6f0a26cf10fac23d3e6fab71835c20121020cb972a66e3fb1cdcc9efcad060b4457ebec534942700d4af1c0d82a33aa13f100000000` + +You can paste this into a raw transaction decoder like this one to inspect the decoded fields: https://live.blockcypher.com/btc/decodetx/ + +If using stacks.js, pass the raw hex to your Clarity function as a buffer like this: + +{% code title="JavaScript (stacks.js)" %} +```javascript +bufferCV(Buffer.from(txRaw, "hex")); +``` +{% endcode %} + +Where `txRaw` is a string containing the raw transaction hex. Passing that buffer into `parse-tx` returns the parsed transaction object with version, inputs (ins), outputs (outs), and locktime. You can then extract whatever fields you need from that returned data. + +{% stepper %} +{% step %} +### Get the raw transaction hex + +Obtain the raw transaction hex from a Bitcoin block explorer API (for example, mempool or blockcypher). This is a single hex string representing the entire transaction. +{% endstep %} + +{% step %} +### Pass the hex into Clarity-Bitcoin's parser + +Convert the hex string to a buffer and pass it to the `parse-tx` function (via your Stacks/Clarity call). In stacks.js: + +`bufferCV(Buffer.from(txRaw, "hex"));` +{% endstep %} + +{% step %} +### Use the parsed result + +`parse-tx` returns an object containing: + +* version +* ins (transaction inputs) +* outs (transaction outputs) +* locktime + +Extract whatever fields you need from that returned object. +{% endstep %} +{% endstepper %} diff --git a/docs/build/bitcoin-integration/sending-bitcoin-with-leather.md b/docs/build/bitcoin-integration/sending-bitcoin-with-leather.md new file mode 100644 index 0000000000..fa90d3aaba --- /dev/null +++ b/docs/build/bitcoin-integration/sending-bitcoin-with-leather.md @@ -0,0 +1,56 @@ +# Sending Bitcoin with Leather + +

source: leather.io

+ +Using Leather's web wallet, you can initiate a simple Bitcoin transaction from a JS app in a few lines of code. + +{% hint style="info" %} +You must be authenticated with the Leather wallet for this to work. See the Authentication with Stacks.js tutorial for how to authenticate before using the API. +{% endhint %} + +{% stepper %} +{% step %} +### Prepare the send call + +Use the `window.btc?.request("sendTransfer", ...)` API to initiate a transaction. Provide the destination address and the amount in satoshis. + +```javascript +const sendBitcoin = async () => { + const resp = await window.btc?.request("sendTransfer", { + address: "tb1qya9wtp4dyq67ldxz2pyuz40esvgd0cgx9s3pjl", // replace with the recipient address + amount: "10000", // amount in satoshis + }); + + // Storing txid in local storage + // We'll get back the transaction ID, which we can then use as needed + if (typeof window !== "undefined") { + localStorage.setItem("txid", JSON.stringify(resp.result.txid)); + } + + // Optionally mark the transaction as pending and poll a Bitcoin API (e.g., mempool.space) to check confirmation status + localStorage.setItem("txStatus", "pending"); +}; +``` +{% endstep %} + +{% step %} +### Hook up the UI + +Call the `sendBitcoin` function from your UI (for example, a button click). + +{% code title="Example component" %} +```javascript + +``` +{% endcode %} +{% endstep %} + +{% step %} +### Next steps + +* To verify a transaction was mined, use the returned txid and query a Bitcoin explorer or API (for example, mempool.space). +* See the "Verifying a transaction on the BTC chain" recipe for a more complete flow using the returned transaction ID as a starting point. + + +{% endstep %} +{% endstepper %} diff --git a/docs/build/bitcoin-integration/verifying-a-bitcoin-transaction.md b/docs/build/bitcoin-integration/verifying-a-bitcoin-transaction.md new file mode 100644 index 0000000000..2804d7e75d --- /dev/null +++ b/docs/build/bitcoin-integration/verifying-a-bitcoin-transaction.md @@ -0,0 +1,221 @@ +# Verifying a Bitcoin Transaction + +One of the coolest things about Clarity is that it allows us to have visibility into the state of the Bitcoin blockchain. Since Stacks blocks are mined in lockstep with Bitcoin blocks, we can directly read the burnchain header info of each Bitcoin block using Clarity's built-in [`get-burn-block-info?`](https://docs.stacks.co/docs/clarity/language-functions#get-burn-block-info-clarity2) function. + +There are quite a few relatively complex things that need to happen to do this successfully, but a [clarity-bitcoin library](https://github.com/friedger/clarity-bitcoin/) exists to make the process a lot easier and handle some of the heavy lifting for us. + +Let's take a look at how to verify a Bitcoin transaction was mined using Clarity using the library. If you take a look at the `clarity-bitcoin.clar` file in the linked repo, you'll find a function called `was-tx-mined-compact`. That's what we'll be working with, and it looks like this: + +{% code title="clarity-bitcoin.clar" %} +```clarity +(define-read-only (was-tx-mined-compact (height uint) (tx (buff 1024)) (header (buff 80)) (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint})) + (let ((block (unwrap! (parse-block-header header) (err ERR-BAD-HEADER)))) + (was-tx-mined-internal height tx header (get merkle-root block) proof))) +``` +{% endcode %} + +The transaction itself is relatively simple, but there's a lot happening within other private function calls. I encourage you to read the contract for yourself and trace what is happening, step-by-step, when we call this function. + +For now, we'll just go over how to actually call this function successfully. + +You can see that it takes a few pieces of information: + +* `(height uint)` the block height you are looking to verify the transaction within +* `(tx (buff 1024))` the raw transaction hex of the transaction you are looking to verify +* `(header (buff 80))` the block header of the block +* `(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint})` a merkle proof formatted as a tuple + +{% hint style="info" %} +A Merkle proof is a compact way to prove that a transaction is included in a block in the Bitcoin blockchain. Here's how it works: +{% endhint %} + +{% stepper %} +{% step %} +### How transactions are combined into the Merkle root + +Transactions in a block are hashed and paired, then the hashes of the pairs are hashed and paired, and so on until a single hash remains — this is called the Merkle root. +{% endstep %} + +{% step %} +### Merkle root in the block header + +The Merkle root is included in the block header. By providing the hashes that lead from a transaction's hash up to the Merkle root, along with the block header, one can prove that the transaction is included in that block. +{% endstep %} + +{% step %} +### Merkle proof (Merkle path) + +The hashes that connect a transaction to the Merkle root are called the Merkle proof or Merkle path. By providing the Merkle proof along with the transaction hash and block header, anyone can verify that the transaction is part of that block. +{% endstep %} + +{% step %} +### Efficient decentralized verification + +This allows for efficient decentralized verification of transactions without having to download the entire blockchain. One only needs the transaction hash, Merkle proof, and block header to verify. +{% endstep %} +{% endstepper %} + +Once we have this information, we can call into the `clarity-bitcoin.clar` contract and pass in this data. A common practice would be to get this data from a Bitcoin block explorer API like Mempool.space or Blockstream's esplora, parse it into the correct format for this helper, and then pass it to this function. + +We could do that directly via this contract if we just need a direct response on if the transaction was included or not, but more likely we would want to integrate this functionality into a Clarity contract of our own where we can `asserts!` that a transaction was mined before taking another action. + +Here's a basic example where we are calling [Blockstream's API](https://github.com/Blockstream/esplora/blob/master/API.md) using JavaScript, parsing the data into the right format, and then calling into our own `mint` function to only mint an NFT if the selected transaction was mined. + +We can get all the information we need with nothing but the transaction ID, which will usually be passed to us when we use a wallet like [Hiro's web wallet](https://hirowallet.gitbook.io/developers/bitcoin/sign-transactions/sending-bitcoin) to initiate the Bitcoin transaction. + +Let's go through the code we can use to implement this. For full context, this code is taken from the example [bitbadge](https://github.com/kenrogers/bitbadge) repo, which you can take a look at. For a complete step-by-step walkthrough of how to implement this, check out the [Bitcoin Primer](https://bitcoinprimer.dev/). + +Here's the mint function: + +{% code title="contract.clar" %} +```clarity +(define-public (mint (recipient principal) (height uint) (tx (buff 1024)) (header (buff 80)) (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint})) + (let + ( + (token-id (+ (var-get last-token-id) u1)) + (tx-was-mined (try! (contract-call? .clarity-bitcoin was-tx-mined-compact height tx header proof))) + ) + (asserts! (is-eq tx-sender contract-owner) err-owner-only) + (asserts! (is-eq tx-was-mined true) err-tx-not-mined) + (try! (nft-mint? bitbadge token-id recipient)) + (var-set last-token-id token-id) + (ok token-id) + ) +) +``` +{% endcode %} + +Note the `(asserts! (is-eq tx-was-mined true) err-tx-not-mined)` line. This is what is doing the heavy lifting. + +{% hint style="warning" %} +Right now the clarity-bitcoin library only supports legacy transactions. Work is in-progress to add support for segwit, but until then we have to do a bit of work on the front end to strip the witness data from the raw transaction hex. +{% endhint %} + +Here's the JavaScript code we can use to get the data we need. + +First we get the raw transaction and the merkle proof (we do this when we first get the transaction ID back). + +The `useEffect` here is so that we can check to see if the transaction was confirmed every 10 seconds before we get the rest of the information. + +{% code title="useEffect - poll transaction status" %} +```javascript +// Effect hook to check and see if the tx has been confirmed using blockstream API +useEffect(() => { + const intervalId = setInterval(() => { + const txid = JSON.parse(localStorage.getItem("txid")); + if (txid) { + fetch(`https://blockstream.info/testnet/api/tx/${txid}/status`) + .then((response) => response.json()) + .then(async (status) => { + // set txStatus in localStorage if it is confirmed, otherwise we want to leave it pending + if (status.confirmed) { + localStorage.setItem("txStatus", "confirmed"); + // set the block details + const blockDetails = { + block_height: status.block_height, + block_hash: status.block_hash, + }; + setBlockDetails(blockDetails); + localStorage.setItem("blockDetails", JSON.stringify(blockDetails)); + // fetch and set the tx raw + const rawResponse = await fetch( + `https://blockstream.info/testnet/api/tx/${txid}/hex` + ); + const txRaw = await rawResponse.text(); + localStorage.setItem("txRaw", txRaw); + // fetch and set the merkle proof + const proofResponse = await fetch( + `https://blockstream.info/testnet/api/tx/${txid}/merkle-proof` + ); + const txMerkleProof = await proofResponse.json(); + localStorage.setItem( + "txMerkleProof", + JSON.stringify(txMerkleProof) + ); + clearInterval(intervalId); + } + }) + .catch((err) => console.error(err)); + } + }, 10000); + return () => clearInterval(intervalId); // Clean up on component unmount +}, []); +``` +{% endcode %} + +Then we get and parse the rest of the data when we call the actual mint function. + +{% code title="mintBitbadge - prepare and call contract" %} +```javascript +// This function retrieves raw transaction and merkle proof from localStorage and calls the mint Clarity function +const mintBitbadge = async () => { + // Retrieving rawTx and merkleProof from local storage + let txRaw = ""; + let txMerkleProof = ""; + + if (typeof window !== "undefined") { + txRaw = removeWitnessData(localStorage.getItem("txRaw")); + txMerkleProof = JSON.parse(localStorage.getItem("txMerkleProof")); + } + + // First we need to verify that the sender of this transaction is the same as the user that is signed in + if (!verifyCorrectSender()) { + console.log("wrong sender"); + return false; + } + + const blockHeight = blockDetails.block_height; + + // Fetch the block hash + const blockHashResponse = await fetch( + `https://blockstream.info/testnet/api/block-height/${blockHeight}` + ); + const blockHash = await blockHashResponse.text(); + + // Fetch the block header + const blockHeaderResponse = await fetch( + `https://blockstream.info/testnet/api/block/${blockHash}/header` + ); + const blockHeaderHex = await blockHeaderResponse.text(); + + const txIndex = txMerkleProof.pos; + const hashes = txMerkleProof.merkle.map( + (hash) => bufferCV(hexToBytes(hash).reverse()) // lib needs reversed hashes + ); // Convert each hash to BufferCV and reverse it + + const functionArgs = [ + principalCV(userData.profile.stxAddress.testnet), + uintCV(blockHeight), + bufferCV(Buffer.from(txRaw, "hex")), + bufferCV(Buffer.from(blockHeaderHex, "hex")), + tupleCV({ + "tx-index": uintCV(txIndex), + hashes: listCV(hashes), + "tree-depth": uintCV(txMerkleProof.merkle.length), + }), + ]; + + const contractAddress = "ST3QFME3CANQFQNR86TYVKQYCFT7QX4PRXM1V9W6H"; // Replace with your contract address + const contractName = "bitbadge-v3"; // Replace with your contract name + const functionName = "mint"; // Replace with your function name + + const options = { + contractAddress, + contractName, + functionName, + functionArgs, + appDetails: { + name: "BitBadge", + icon: "https://freesvg.org/img/bitcoin.png", + }, + onFinish: (data) => { + console.log(data); + }, + }; + + await openContractCall(options); +}; +``` +{% endcode %} + +That's the end-to-end flow: retrieve tx raw and merkle proof from Blockstream's API, adapt formats (strip witness data for legacy-only support, reverse hashes for the library), assemble the arguments, and call the Clarity contract function that verifies inclusion using the clarity-bitcoin helper. diff --git a/docs/build/build-a-frontend/README.md b/docs/build/build-a-frontend/README.md new file mode 100644 index 0000000000..e3a68f560c --- /dev/null +++ b/docs/build/build-a-frontend/README.md @@ -0,0 +1,5 @@ +# Build a Frontend + +A major part of building full-stack Stacks applications is creating a UI with a solid user experience. One of your primary tools for this is Stacks.js, a JavaScript library built by Hiro that simplifies working with contracts and the chain. + +Hiro provides documentation to help you use Stacks.js to build a frontend for your Clarity contracts. diff --git a/docs/build/build-a-frontend/authentication-with-stacks.js.md b/docs/build/build-a-frontend/authentication-with-stacks.js.md new file mode 100644 index 0000000000..31a194f753 --- /dev/null +++ b/docs/build/build-a-frontend/authentication-with-stacks.js.md @@ -0,0 +1,101 @@ +# Authentication with Stacks.js + +Authenticating with a Stacks wallet is a common task when building Stacks dapps. Below is a React-based example (from the Hello Stacks tutorial) showing how to set up front-end authentication with Stacks.js and access user data in the UI. + +{% hint style="info" %} +This example uses React, but the patterns can be adapted to other front-end frameworks. +{% endhint %} + +## Install + +{% code title="Install with yarn" %} +```bash +yarn add @stacks/connect +``` +{% endcode %} + +## Stacks.js Code (React) + +This example implements the authentication flow and loads user data when the user signs in. + +{% code title="App.jsx" %} +```javascript +import { + AppConfig, + UserSession, + AuthDetails, + showConnect, +} from "@stacks/connect"; +import { useState, useEffect } from "react"; + +function App() { + const [message, setMessage] = useState(""); + const [transactionId, setTransactionId] = useState(""); + const [currentMessage, setCurrentMessage] = useState(""); + const [userData, setUserData] = useState(undefined); + + const appConfig = new AppConfig(["store_write"]); + const userSession = new UserSession({ appConfig }); + + const appDetails = { + name: "Hello Stacks", + icon: "https://freesvg.org/img/1541103084.png", + }; + + const connectWallet = () => { + showConnect({ + appDetails, + onFinish: () => window.location.reload(), + userSession, + }); + }; + + useEffect(() => { + if (userSession.isSignInPending()) { + userSession.handlePendingSignIn().then((userData) => { + setUserData(userData); + }); + } else if (userSession.isUserSignedIn()) { + setUserData(userSession.loadUserData()); + } + }, []); + + return ( +
+ +

Hello Stacks

+
+ ); +} + +export default App; +``` +{% endcode %} + +This is all the code needed to authenticate and access user data in the UI. + +## Example: Connect Wallet Button (conditional) + +Here is how you might render a Connect Wallet button only when there is no signed-in user: + +{% code title="Conditional Connect Button" %} +```javascript +{ + !userData && ( + + ); +} +``` +{% endcode %} + +When clicked, `connectWallet` calls `showConnect` from @stacks/connect, which opens the wallet for the user to sign in. After sign-in completes, the app reloads (via `onFinish`) and the `useEffect` code loads and stores the user data in state. diff --git a/docs/build/build-a-frontend/post-conditions-with-stacks.js.md b/docs/build/build-a-frontend/post-conditions-with-stacks.js.md new file mode 100644 index 0000000000..04eee406d4 --- /dev/null +++ b/docs/build/build-a-frontend/post-conditions-with-stacks.js.md @@ -0,0 +1,133 @@ +# Post Conditions with Stacks.js + +The content in this recipe has been pulled from the [tutorial on post conditions](https://dev.to/stacks/understanding-stacks-post-conditions-e65). Post conditions are an additional safety feature built into the Stacks chain itself that help to protect end users. + +Rather than being a function of Clarity smart contracts, they are implemented on the client side and meant to be an additional failsafe against malicious contracts. + +Put simply, post conditions are a set of conditions that must be met before a user's transaction will execute. The primary goal behind post conditions is to limit the amount of damage that can be done to a user's assets due to a bug, intentional or otherwise. + +So they are sent as part of the transaction when the user initiates it, meaning we need a frontend library to utilize them. + +Whenever you are transferring an asset (fungible or non-fungible) from one address to another, you should take advantage of post conditions. + +We're going to use [Stacks.js](https://github.com/hirosystems/stacks.js/tree/master/packages/transactions#post-conditions) to familiarize ourselves with them. + +### STX Post Condition + +```js +import { + FungibleConditionCode, + makeStandardSTXPostCondition, + makeContractSTXPostCondition, +} from "@stacks/transactions"; + +// With a standard principal +const postConditionAddress = "SP2ZD731ANQZT6J4K3F5N8A40ZXWXC1XFXHVVQFKE"; +const postConditionCode = FungibleConditionCode.GreaterEqual; +const postConditionAmount = 12345n; + +const standardSTXPostCondition = makeStandardSTXPostCondition( + postConditionAddress, + postConditionCode, + postConditionAmount +); + +// With a contract principal +const contractAddress = "SPBMRFRPPGCDE3F384WCJPK8PQJGZ8K9QKK7F59X"; +const contractName = "test-contract"; + +const contractSTXPostCondition = makeContractSTXPostCondition( + contractAddress, + contractName, + postConditionCode, + postConditionAmount +); +``` + +### Fungible Token Post Condition + +```js +import { + FungibleConditionCode, + createAssetInfo, + makeStandardFungiblePostCondition, +} from "@stacks/transactions"; + +// With a standard principal +const postConditionAddress = "SP2ZD731ANQZT6J4K3F5N8A40ZXWXC1XFXHVVQFKE"; +const postConditionCode = FungibleConditionCode.GreaterEqual; +const postConditionAmount = 12345n; +const assetAddress = "SP62M8MEFH32WGSB7XSF9WJZD7TQB48VQB5ANWSJ"; +const assetContractName = "test-asset-contract"; +const fungibleAssetInfo = createAssetInfo(assetAddress, assetContractName); + +const standardFungiblePostCondition = makeStandardFungiblePostCondition( + postConditionAddress, + postConditionCode, + postConditionAmount, + fungibleAssetInfo +); + +// With a contract principal +const contractAddress = "SPBMRFRPPGCDE3F384WCJPK8PQJGZ8K9QKK7F59X"; +const contractName = "test-contract"; +const assetAddress = "SP62M8MEFH32WGSB7XSF9WJZD7TQB48VQB5ANWSJ"; +const assetContractName = "test-asset-contract"; +const fungibleAssetInfo = createAssetInfo(assetAddress, assetContractName); + +const contractFungiblePostCondition = makeContractFungiblePostCondition( + contractAddress, + contractName, + postConditionCode, + postConditionAmount, + fungibleAssetInfo +); +``` + +### NFT Post Condition + +```js +import { + NonFungibleConditionCode, + createAssetInfo, + makeStandardNonFungiblePostCondition, + makeContractNonFungiblePostCondition, + bufferCVFromString, +} from "@stacks/transactions"; + +// With a standard principal +const postConditionAddress = "SP2ZD731ANQZT6J4K3F5N8A40ZXWXC1XFXHVVQFKE"; +const postConditionCode = NonFungibleConditionCode.Owns; +const assetAddress = "SP62M8MEFH32WGSB7XSF9WJZD7TQB48VQB5ANWSJ"; +const assetContractName = "test-asset-contract"; +const assetName = "test-asset"; +const tokenAssetName = bufferCVFromString("test-token-asset"); +const nonFungibleAssetInfo = createAssetInfo( + assetAddress, + assetContractName, + assetName +); + +const standardNonFungiblePostCondition = makeStandardNonFungiblePostCondition( + postConditionAddress, + postConditionCode, + nonFungibleAssetInfo, + tokenAssetName +); + +// With a contract principal +const contractAddress = "SPBMRFRPPGCDE3F384WCJPK8PQJGZ8K9QKK7F59X"; +const contractName = "test-contract"; + +const contractNonFungiblePostCondition = makeContractNonFungiblePostCondition( + contractAddress, + contractName, + postConditionCode, + nonFungibleAssetInfo, + tokenAssetName +); +``` + +### Sample App + +Here's a [sample application](https://github.com/kenrogers/fabulous-frogs) that utilizes post conditions on the front end to secure user assets. diff --git a/docs/build/build-a-frontend/sending-transactions-with-stacks.js.md b/docs/build/build-a-frontend/sending-transactions-with-stacks.js.md new file mode 100644 index 0000000000..a3bfe28cea --- /dev/null +++ b/docs/build/build-a-frontend/sending-transactions-with-stacks.js.md @@ -0,0 +1,60 @@ +# Sending Transactions with Stacks.js + +Any Stacks dapp is going to require sending transactions at some point, so how do we do that? We use the `@stacks/transactions` package. + +Again, this particular snippet is pulled from our Hello Stacks tutorial. + +{% hint style="warning" %} +When you send Stacks transactions, don't forget to utilize post conditions. +{% endhint %} + +But first, you'll need to install the right NPM package. + +{% code title="Install" %} +```bash +yarn add @stacks/transactions +``` +{% endcode %} + +### Stacks.js Frontend Code + +Assuming you are authenticated, you'll need to import from the `network` package to connect and import the right Clarity values to convert. + +You need to convert values from JS into the right format for Clarity values to ingest. You can view the complete list of types on the Stacks.js docs: https://stacks.js.org/modules/\_stacks\_transactions#constructing-clarity-values + +{% code title="Imports" %} +```js +import { StacksMocknet } from "@stacks/network"; +import { stringUtf8CV } from "@stacks/transactions"; +``` +{% endcode %} + +Let's assume we have a message that we need to send. + +``` +Hello this is something I need to say. +``` + +{% code title="Submitting a contract call" %} +```js +const submitMessage = async (e) => { + e.preventDefault(); + + const network = new StacksMocknet(); + + const options = { + contractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + contractName: "hello-stacks", + functionName: "write-message", + functionArgs: [stringUtf8CV(message)], + network, + appDetails, + onFinish: ({ txId }) => console.log(txId), + }; + + await openContractCall(options); +}; +``` +{% endcode %} + +For more details on the different types of transactions you can send, the Stacks.js docs have multiple examples: https://stacks.js.org/modules/\_stacks\_transactions diff --git a/docs/build/clarity-crash-course.md b/docs/build/clarity-crash-course.md new file mode 100644 index 0000000000..ce6a80f4f9 --- /dev/null +++ b/docs/build/clarity-crash-course.md @@ -0,0 +1,96 @@ +# Clarity Crash Course + +## Clarity Crash Course + +This is designed for people with some programming experience who are new to Clarity. You don't need prior smart contract development experience, but if you have experience with languages like Solidity, you'll pick this up quickly. + +Once you've familiarized yourself with the language, consider the book [Clarity of Mind](https://book.clarity-lang.org/title-page.html) or the course [Clarity Universe](https://clarity-lang.org/universe) to continue your learning. + +### Your First Clarity Smart Contract + +We're going to build a basic Clarity smart contract using [Clarity Tools](https://clarity.tools/code/new), so you won't have to install anything for this introduction. Visit that link and it will open up a new contract for you. + +Clarity Tools evaluates Clarity code in real-time — handy for experimenting and seeing immediate results. + +The initial example contains comments (two semicolons) and a public function declaration. Clarity's syntax is inspired by LISP: everything is an expression wrapped in parentheses. Function definitions, variable declarations, and parameters are lists inside lists. This makes Clarity concise and readable once you get used to it. + +{% stepper %} +{% step %} +### Understanding a simple expression + +We can think of some constructs as function calls. For example: + +* Call a function called `define-read-only` (a built-in function). +* Pass it a parameter `hello`, which corresponds to the method signature. +* Pass it a parameter `"Hello"`, which corresponds to the function body. + +You can refer to the [`define-read-only`](https://docs.stacks.co/docs/write-smart-contracts/clarity-language/language-functions#define-read-only) documentation for details. +{% endstep %} + +{% step %} +### Everything is an expression + +Clarity treats everything as expressions inside expressions. Function definitions are calls to built-in functions; the function body is an expression. This uniformity helps reasoning about programs in Clarity. +{% endstep %} + +{% step %} +### Use LISP-like nesting + +Expect nested parentheses and expressions. You’ll often read code as lists inside lists, where each parentheses-enclosed group represents a call or expression. +{% endstep %} +{% endstepper %} + +Let's expand on these ideas by creating a new function. Paste this into Clarity Tools: + +{% code title="contract.clar" %} +```clarity +(define-data-var count int 0) +(define-public (add-number (number int)) + (let + ( + (current-count count) + ) + + (var-set count (+ 1 number)) + (ok (var-get count)) + ) +) + + +(add-number 5) +``` +{% endcode %} + +If you type that into Clarity Tools, you'll see the result printed is 6. + +#### What this code does + +* (define-data-var count int 0)\ + Defines a persistent state variable `count` initialized to 0. This value is persisted on-chain when the contract is deployed. +* Clarity is interpreted, not compiled. When the contract is deployed, top-level expressions run (so the data var initialization happens at deploy time). +* (define-public (add-number (number int)) ...)\ + Defines a public function `add-number` that takes a single parameter `number` of type `int`. Public functions can modify chain state and be called from outside the contract. + +{% hint style="info" %} +In Clarity, there are public, private, and read-only functions: + +* public: can modify chain state and be called externally. +* private: can modify state but only be called within the contract. +* read-only: will fail if they attempt to modify state. +{% endhint %} + +* (let ((current-count count)) ...)\ + The `let` expression wraps multiple steps into a single expression (function bodies must evaluate to a single expression). It also declares local variables for use inside the function. Here `current-count` is a function-local variable set to `count`. +* (var-set count (+ 1 number))\ + Sets the persistent `count` to `1 + number`. The `+` is itself a function call with operands as parameters. +* (ok (var-get count))\ + Returns the new value of `count` wrapped in an `ok` response to indicate success. +* (add-number 5)\ + Calls the function with parameter 5 (this is how you can invoke the function in an interactive environment like Clarity Tools). + +This brief overview should get your feet wet with Clarity. For deeper learning, we recommend: + +* The [Clarity Book](https://book.clarity-lang.org/title-page.html) +* [Clarity Universe](https://clarity-lang.org/universe) + +If you prefer jumping into reference material and examples, the Clarity docs contain guides and sample contracts to explore. diff --git a/docs/build/create-tokens/README.md b/docs/build/create-tokens/README.md new file mode 100644 index 0000000000..10dee17353 --- /dev/null +++ b/docs/build/create-tokens/README.md @@ -0,0 +1,3 @@ +# Create Tokens + +Rather than needing to work with external libraries, Clarity has built-in functions that make working with fungible and non-fungible tokens a breeze. diff --git a/docs/build/create-tokens/creating-a-ft.md b/docs/build/create-tokens/creating-a-ft.md new file mode 100644 index 0000000000..f51f809a64 --- /dev/null +++ b/docs/build/create-tokens/creating-a-ft.md @@ -0,0 +1,102 @@ +# Creating a FT + +

source: Hiro blog

+ +Much the same as creating an NFT, Clarity also makes creating a fungible token (FT) trivial as well. + +The process and code are much the same. + +### Trait + +Just like with the NFT, it's a good idea to create a trait when you create a fungible token so that different tools and protocols can be confident in building interfaces for those tokens. + +Since FTs may be divisible, the [FT official mainnet deployment trait address](https://explorer.stacks.co/txid/0x99e01721e57adc2c24f7d371b9d302d581dba1d27250c7e25ea5f241af14c387?chain=mainnet) has additional functions. + +{% code title="sip-010-trait.clar" %} +```clarity +(define-trait sip-010-trait + ( + ;; Transfer from the caller to a new principal + (transfer (uint principal principal (optional (buff 34))) (response bool uint)) + + ;; the human readable name of the token + (get-name () (response (string-ascii 32) uint)) + + ;; the ticker symbol, or empty if none + (get-symbol () (response (string-ascii 32) uint)) + + ;; the number of decimals used, e.g. 6 would mean 1_000_000 represents 1 token + (get-decimals () (response uint uint)) + + ;; the balance of the passed principal + (get-balance (principal) (response uint uint)) + + ;; the current total supply (which does not need to be a constant) + (get-total-supply () (response uint uint)) + + ;; an optional URI that represents metadata of this token + (get-token-uri () (response (optional (string-utf8 256)) uint)) + ) +) +``` +{% endcode %} + +Now let's see how we might implement this in Clarity, just like we would an NFT. + +### Clarity Code + +{% code title="fungible-token.clar" %} +```clarity +(impl-trait 'SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard.sip-010-trait) + + +(define-constant contract-owner tx-sender) +(define-constant err-owner-only (err u100)) +(define-constant err-not-token-owner (err u101)) + +;; No maximum supply! +(define-fungible-token clarity-coin) + +(define-public (transfer (amount uint) (sender principal) (recipient principal) (memo (optional (buff 34)))) + (begin + (asserts! (is-eq tx-sender sender) err-not-token-owner) + (try! (ft-transfer? clarity-coin amount sender recipient)) + (match memo to-print (print to-print) 0x) + (ok true) + ) +) + +(define-read-only (get-name) + (ok "Clarity Coin") +) + +(define-read-only (get-symbol) + (ok "CC") +) + +(define-read-only (get-decimals) + (ok u6) +) + +(define-read-only (get-balance (who principal)) + (ok (ft-get-balance clarity-coin who)) +) + +(define-read-only (get-total-supply) + (ok (ft-get-supply clarity-coin)) +) + + +(define-read-only (get-token-uri) + (ok none) +) + + +(define-public (mint (amount uint) (recipient principal)) + (begin + (asserts! (is-eq tx-sender contract-owner) err-owner-only) + (ft-mint? clarity-coin amount recipient) + ) +) +``` +{% endcode %} diff --git a/docs/build/create-tokens/creating-a-nft.md b/docs/build/create-tokens/creating-a-nft.md new file mode 100644 index 0000000000..b2c118550e --- /dev/null +++ b/docs/build/create-tokens/creating-a-nft.md @@ -0,0 +1,92 @@ +# Creating a NFT + +

source: Hiro blog

+ +{% hint style="info" %} +For a more in-depth discussion of NFTs in Clarity and how to create them, check out the [NFTs chapter in the Clarity book](https://book.clarity-lang.org/ch10-01-sip009-nft-standard.html). +{% endhint %} + +## Creating a NFT + +Clarity makes creating NFTs incredibly easy. With built-in functions for creating and working with the token, you can have an NFT created in less than 10 minutes of work. + +Let's see how. + +#### Trait + +The first thing we need when we create an NFT is a trait. A trait is an interface that allows us to create an NFT with a defined set of functions. Its primary purpose is to ensure that NFTs are composable and different tools know how to interact with them. + +By implementing a trait that the community agrees on, all protocols and products know how they can interact with an NFT. + +The official mainnet trait can be [found on the Stacks Explorer](https://explorer.stacks.co/txid/0x80eb693e5e2a9928094792080b7f6d69d66ea9cc881bc465e8d9c5c621bd4d07?chain=mainnet) and looks like this: + +{% code title="nft-trait.clar" %} +```clarity +(define-trait nft-trait + ( + ;; Last token ID, limited to uint range + (get-last-token-id () (response uint uint)) + + ;; URI for metadata associated with the token + (get-token-uri (uint) (response (optional (string-ascii 256)) uint)) + + ;; Owner of a given token identifier + (get-owner (uint) (response (optional principal) uint)) + + ;; Transfer from the sender to a new principal + (transfer (uint principal principal) (response bool uint)) + ) +) +``` +{% endcode %} + +All we are doing here is defining the function signatures for functions we'll need to implement in our Clarity contract, which we can see a simple version of below. + +#### Clarity Code + +This is the Clarity code we need in order to create an NFT, with one additional function, `mint` that allows us to actually create a new NFT. This `mint` function is not needed to adhere to the trait. + +{% code title="amazing-aardvarks.clar" %} +```clarity +(impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait) + +(define-non-fungible-token amazing-aardvarks uint) + +(define-data-var last-token-id uint u0) + +(define-constant contract-owner tx-sender) +(define-constant err-owner-only (err u100)) +(define-constant err-not-token-owner (err u101)) + +(define-read-only (get-last-token-id) + (ok (var-get last-token-id)) +) + +(define-read-only (get-token-uri (token-id uint)) + (ok none) +) + +(define-read-only (get-owner (token-id uint)) + (ok (nft-get-owner? amazing-aardvarks token-id)) +) + +(define-public (transfer (token-id uint) (sender principal) (recipient principal)) + (begin + (asserts! (is-eq tx-sender sender) err-not-token-owner) + (nft-transfer? amazing-aardvarks token-id sender recipient) + ) +) + +(define-public (mint (recipient principal)) + (let + ( + (token-id (+ (var-get last-token-id) u1)) + ) + (asserts! (is-eq tx-sender contract-owner) err-owner-only) + (try! (nft-mint? amazing-aardvarks token-id recipient)) + (var-set last-token-id token-id) + (ok token-id) + ) +) +``` +{% endcode %} diff --git a/docs/build/misc.-guides/build-a-borrowing-and-lending-protocol.md b/docs/build/misc.-guides/build-a-borrowing-and-lending-protocol.md new file mode 100644 index 0000000000..1aa8dd4ded --- /dev/null +++ b/docs/build/misc.-guides/build-a-borrowing-and-lending-protocol.md @@ -0,0 +1,11 @@ +# Build a Borrowing & Lending Protocol + +In this in-depth tutorial, you'll get a holistic overview of the entire Stacks development process by building a very simple borrowing and lending protocol called Lagoon. + +We'll utilize the Hiro Platform to quickly get our contract set up and written. In an upcoming version of this tutorial, we'll also be creating the frontend and tests for our contract. + +A written version is also in the works. + +We'll also dig into sBTC a bit, the upcoming decentralized BTC peg, and how you can begin experimenting with it now. + +{% embed url="https://www.youtube.com/watch?v=-JFZ60UGODY" %} diff --git a/docs/build/misc.-guides/community-tutorials.md b/docs/build/misc.-guides/community-tutorials.md new file mode 100644 index 0000000000..54448e5058 --- /dev/null +++ b/docs/build/misc.-guides/community-tutorials.md @@ -0,0 +1,27 @@ +# Community Tutorials + +

source: EasyA x Stacks hackathon at Harvard University [2024]

+ +These tutorials have been created by various developers of the Stacks community. Have a tutorial to suggest? View the [contribution guide](http://localhost:3000/docs/contribute/docs) to submit a PR with a new tutorial added. + +### Written Tutorials + +* [Create a server-side rendered Stacks app with Remix](https://micro-stacks.dev/guides/with-remix) +* [Build a Stacks app with Next.js](https://micro-stacks.dev/guides/with-nextjs) +* [Creating a Voting Contract](https://www.clearness.dev/01-voting-clarity-smart-contract/01-getting-started) +* [Building an NFT with Stacks and Clarity](https://blog.developerdao.com/building-an-nft-with-stacks-and-clarity) +* [Minting NFTs with QuickNode](https://www.quicknode.com/guides/web3-sdks/how-to-mint-nfts-on-the-stacks-blockchain) +* [Order Book Contract Walkthrough](https://byzantion.hiro.so/) +* [Build a DEX on Stacks](https://www.pointer.gg/tutorials/build-a-dex-with-stacks/56abb3a4-05c1-4608-b096-f82189e9f759) +* [NFT Tutorial](https://docs.hiro.so/tutorials/clarity-nft) +* [Billboard Tutorial](https://docs.hiro.so/tutorials/clarity-billboard) +* [Integrating NFTs Into a Game](https://gamefi-stacks.gitbook.io/gamefistacks/tutorials/integrate-nfts-into-game) +* [Building on Stacks](https://github.com/amoweolubusayo/stacks-clarinet-tutorial) + +### Video Tutorials + +* [Web3 for Bitcoin](https://www.crowdcast.io/e/web3-for-bitcoin/) + +### Other Resources + +There are also a great amount of both tutorials and developer tools in the [Awesome Stacks repo](https://github.com/friedger/awesome-stacks-chain#clarity-resources). diff --git a/docs/build/price-oracles.md b/docs/build/price-oracles.md new file mode 100644 index 0000000000..7c82fe19e2 --- /dev/null +++ b/docs/build/price-oracles.md @@ -0,0 +1,42 @@ +# Price Oracles + +

source: Hiro

+ +## Price‑Feed Oracles on Stacks + +Smart contracts written in **Clarity** run in a deterministic sandbox: they can read data in the Stacks and Bitcoin chainstate, but _nothing else_. Whenever your dApp needs the latest **BTC/USD**, **STX/BTC**, or any other market price, you’ll rely on an **oracle** to bring that data on‑chain in a verifiable way. + +This page explains why price‑feed oracles matter on Stacks and links to the specific oracle provider docs with instructions on how to integrate them. + +*** + +### Why you need a price‑feed oracle + +Here are some possible scenarios where you might need an oracle. + +| On‑chain need | Typical Stacks use case | What the oracle supplies | +| ------------------------------------ | ---------------------------------------------- | ----------------------------------- | +| **Liquidations & collateral ratios** | Lending / borrowing protocols, margin trading | Signed price updated every N blocks | +| **Stablecoin peg maintenance** | BTC‑backed or exogenous‑collateral stablecoins | Reference BTC/USD (or other) price | +| **AMM curve calculations** | DEXs that tune fees or rebalance pools | Time‑weighted average price (TWAP) | +| **Derivatives settlement** | Options, futures, or perpetual swaps | Final settlement price at expiry | + +{% hint style="info" %} +Rule of thumb: if your contract’s math depends on a real‑time market price, you need a price‑feed oracle. +{% endhint %} + +### Oracle Providers + +There are two oracle providers that Stacks builders commonly use for price data: Pyth and DIA. + +**Pyth** + +Pyth is a pull-based oracle. Trust Machines currently maintains the Pyth bridge. See the docs and Clarity contracts on Trust Machine's GitHub [repo](https://github.com/Trust-Machines/stacks-pyth-bridge) for the bridge. Check out the step-by-step guide from the Hiro blog: + +{% embed url="https://www.hiro.so/blog/using-real-time-price-data-in-clarity" %} + +**DIA** + +DIA is another oracle provider used by Stacks builders. See DIA's [guide](https://nexus.diadata.org/how-to-guides/fetch-price-data/chain-specific-guide/stacks) for how to use DIA oracles with Stacks. Check out the video tutorial to learn more on how DIA works for Clarity smart contracts: + +{% embed url="https://youtu.be/bhWQxHGpv2s?si=dWlBAEAuYtoQj2sC" %} diff --git a/docs/build/sbtc/README.md b/docs/build/sbtc/README.md new file mode 100644 index 0000000000..fff18bd7e8 --- /dev/null +++ b/docs/build/sbtc/README.md @@ -0,0 +1,7 @@ +# sBTC + +The guides in this section provide step-by-step instructions for interacting with sBTC, including operating as a signer and (coming soon) developer guides on how to interact with sBTC as an application developer. + +{% hint style="info" %} +In order to run an sBTC signer you must be one of the [approved signers](https://github.com/stacks-network/sbtc/discussions/624) described in [SIP-028](https://github.com/andrerserrano/sips/blob/main/sips/sip-028/sip-028-sbtc_peg.md). +{% endhint %} diff --git a/docs/build/sbtc/best-practices-for-running-an-sbtc-signer.md b/docs/build/sbtc/best-practices-for-running-an-sbtc-signer.md new file mode 100644 index 0000000000..d614a3b677 --- /dev/null +++ b/docs/build/sbtc/best-practices-for-running-an-sbtc-signer.md @@ -0,0 +1,121 @@ +# Best Practices for running an sBTC Signer + +The following best practices suggest how to create a resilient setup for running your sBTC Signer. + +## Protect your private key and have a cold-storage backup + +* Prevent unauthorised access to the sBTC Signer private key. +* Keep an offline, secure backup of your sBTC Signer private key (e.g., hardware security modules or encrypted storage devices). + +## Backup your sBTC Signer PostgreSQL DB + +* Perform daily backups of the sBTC Signer PostgreSQL DB. +* Periodically verify the integrity of backups (see steps below). + +### Verifying integrity of PostgreSQL DB backups + +{% stepper %} +{% step %} +### Import the backup + +Import the backup into a fresh PostgreSQL instance. The database alone is sufficient — you do not need to spin up a Stacks or Bitcoin node or the sBTC signer. +{% endstep %} + +{% step %} +### Run the verification query + +Execute the following query: + +{% code title="PostgreSQL" %} +``` +``` +{% endcode %} + +```sql +SELECT aggregate_key FROM sbtc_signer.dkg_shares +WHERE dkg_shares_status = 'verified' +ORDER BY created_at DESC; +``` + +This returns rows like: + +```sql + aggregate_key +---------------------------------------------------------------------- + \x03d8c4344861fc7590fd812c24884a3bfd9374d8ba865a787ff53c9060020aa967 + \x03f898f8a6ddb86dd4608dd168355ec6135fe2839222240c01942e8e7e50dd4c89 +(2 rows) +``` + +The most recent `aggregate_key` is the first row. +{% endstep %} + +{% step %} +### Compare with the on-chain aggregate key + +Fetch the current aggregate pubkey from the sbtc-registry contract and compare it to the most recent `aggregate_key` from the DB query: + +```bash +curl -s 'https://api.hiro.so/v2/contracts/call-read/SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4/sbtc-registry/get-current-aggregate-pubkey' \ + -H 'content-type: application/json' --data-raw '{"sender":"SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4","arguments":[]}' | jq .result +``` + +Example output: + +``` +"0x020000002103d8c4344861fc7590fd812c24884a3bfd9374d8ba865a787ff53c9060020aa967" +``` + +Discard the prefix `0x02000000210` (Clarity encoding). The remaining hex `3d8c4344861fc7590fd812c24884a3bfd9374d8ba865a787ff53c9060020aa967` should match the first row of the PostgreSQL query (excluding `\x0` which indicates hex encoding). +{% endstep %} +{% endstepper %} + +## Setup proper access control + +* Require hardware 2FA keys for access control (e.g., YubiKey) to connect through SSH, to authenticate to AWS, and for every other relevant action. +* Follow the principle of least privilege: if you don’t need access, you don’t get access; if you get access, it expires after the action is taken. + +{% hint style="info" %} +Optional, but strongly recommended: Implement a "4-eyes" process (require that any activity by an individual must be reviewed or approved by a second individual) to access critical resources (e.g., deploying a new version of the sBTC signer). +{% endhint %} + +## Maintain a strict firewall configuration + +* Allow connections to your sBTC signer `listen_on` address (used for P2P communication). +* Do not expose any non-essential service to the internet: use a DEFAULT DENY policy with explicit ALLOWs for necessary network traffic (such as sBTC signer P2P and SSH). + +## Maintain a robust secrets management program + +* Ensure all relevant secrets are safely managed and rotated (where possible), e.g., if someone leaves the team. + +## Monitor and observe your sBTC Signer + +* Retain at least 90 days of logs for the sBTC Signer, the Stacks node, and the Bitcoin node. +* The sBTC signer can optionally expose Prometheus metrics (see `prometheus_exporter_endpoint` configuration option). + +{% hint style="info" %} +You can use Prometheus metrics to monitor signer health. For example, see how Alloy can be configured to collect metrics on Grafana Cloud: ../running-a-signer/how-to-monitor-signer.md +{% endhint %} + +## Provision dedicated downstream components + +* Run a dedicated Bitcoin node and Stacks node for your sBTC Signer. + * Ensure the nodes are provisioned with the minimum hardware requirements described here: https://docs.stacks.co/guides-and-tutorials/running-a-signer#minimum-system-requirements + * Nodes should be exclusively dedicated to serve the sBTC Signer. Avoid re-using them to serve other clients as that may negatively affect performance (no mock-signing, no Stacks API nodes). + +## Monitor new software releases + +* Stay up-to-date with new releases, patches, and security advisories for all used operating systems, software and packages. + * https://www.cve.org/ is a useful resource for popular software packages. + * Subscribe to security notifications from your vendors. + * Join relevant messaging channels as applicable (Discord, Slack, etc.). +* Exercise vulnerability management for all packages. +* Apply updates promptly, especially those addressing security vulnerabilities. +* Use inventory and patch management software, if available. + +## Ensure redundancy in operations + +* Ensure that multiple, trusted system administrators can manage and maintain your sBTC Signer instance. +* Where feasible, system administrators should span different time zones. +* Document your operations procedures and ensure that relevant personnel have access to them. + diff --git a/docs/build/sbtc/how-to-earn-sbtc-rewards.md b/docs/build/sbtc/how-to-earn-sbtc-rewards.md new file mode 100644 index 0000000000..683cd3ce37 --- /dev/null +++ b/docs/build/sbtc/how-to-earn-sbtc-rewards.md @@ -0,0 +1,37 @@ +# How to Earn sBTC Rewards + +As part of the launch of sBTC, you can enroll in the sBTC rewards program and earn real yield on your BTC, paid in sBTC. + +The rewards program is non-custodial — your sBTC remains in your wallet. + +There are only 3 steps to participate in the sBTC rewards. + +{% stepper %} +{% step %} +**Mint sBTC** + +To get started, first mint your sBTC by using the bridge at [app.stacks.co](https://sbtc.stacks.co/). +{% endstep %} + +{% step %} +**Connect your wallet** + +After your sBTC has been minted to your wallet, visit the rewards program site at [bitcoinismore.org](https://bitcoinismore.org/) and connect your wallet. Then click the "Earn Rewards" button. + +
+ +{% hint style="info" %} +You may need to enable the sBTC token listing display in your wallet before it is visible. In Leather, you can do this by clicking "Manage Tokens" and toggling on sBTC. + +![](<../.gitbook/assets/image (3).png>) +{% endhint %} +{% endstep %} + +{% step %} +**Enroll in the rewards program** + +Finally, execute a Stacks transaction using your connected wallet to enroll in the program. You will need enough STX to cover the transaction fee to enroll in the rewards program. + +
+{% endstep %} +{% endstepper %} diff --git a/docs/build/sbtc/how-to-run-sbtc-signer.md b/docs/build/sbtc/how-to-run-sbtc-signer.md new file mode 100644 index 0000000000..baea1839c8 --- /dev/null +++ b/docs/build/sbtc/how-to-run-sbtc-signer.md @@ -0,0 +1,193 @@ +# How to Run sBTC Signer + +{% hint style="info" %} +This documentation provides guidelines, best-practices and recommendations for running an sBTC Signer. Review it and adapt it to your infrastructure policy before deploying it. +{% endhint %} + +{% hint style="warning" %} +Each sBTC signer will control a set of signing shares used to sign transactions on both Bitcoin and Stacks. + +Such shares will be encrypted by using the `private_key` specified in the Signer's config and stored in the PostgreSQL database attached to each signer. + +It is of the utmost importance to follow the recommendations below. +{% endhint %} + +{% stepper %} +{% step %} +### Prevent unauthorized access to signer infrastructure + +Prevent unauthorized access to the sBTC Signer infrastructure (the signer itself, its private key, and the associated PostgreSQL database). +{% endstep %} + +{% step %} +### Keep an offline, secure backup of the Signer private key + +Keep an offline, secure backup of the sBTC Signer private key. +{% endstep %} + +{% step %} +### Regularly backup PostgreSQL database + +Regularly backup the PostgreSQL database and store it in a secure location. +{% endstep %} +{% endstepper %} + +See [here](best-practices-for-running-an-sbtc-signer.md) for additional best practices to run an sBTC signer. + +## Minimum System Requirements + +Below are the **minimum required specs** to be able to run a sBTC signer. + +* 2 CPU +* 4GB memory +* 50GB storage + +Note that these are in _addition_ to the hardware requirements for running a Stacks node and Bitcoin node outlined in the [How to Run a Signer doc](https://app.gitbook.com/s/4cpTb2lbw0LAOuMHrvhA/run-a-signer). + +## Connection diagram + +
+ +## Configure your Bitcoin node + +### Minimum version + +You will need `bitcoind` version 25 or higher. + +### Settings + +Your Bitcoin node must include these settings for sBTC signer operation: + +* `txindex=1`: Transaction indexing must be enabled +* `server=1`: RPC server must be enabled + +### RPC-Based Block Detection + +Starting with sBTC v1.1.0, the signer uses RPC polling instead of ZeroMQ for block detection. + +The signer connects to Bitcoin Core via RPC and polls for new bitcoin blocks. This process works as follows: + +{% stepper %} +{% step %} +### Bitcoin Core validates a new block + +Bitcoin Core validates a new block. +{% endstep %} + +{% step %} +### Signer detects the block via RPC polling + +Signer detects the block via RPC polling. +{% endstep %} + +{% step %} +### Signer processes relevant sBTC transactions + +Signer processes relevant sBTC transactions. +{% endstep %} +{% endstepper %} + +### Example + +```bash +bitcoind \ + -server \ + -datadir=${BITCOIN_DATA} \ + -rpcbind=0.0.0.0 \ + -rpcuser=${BITCOIN_RPC_USERNAME} \ + -rpcpassword=${BITCOIN_RPC_PASSWORD} \ + -rpcport=${BITCOIN_RPC_PORT} \ + -rpcallowip=0.0.0.0/0 \ + -rpcallowip=::/0 \ + -txindex +``` + +## Configure your Stacks node + +### Minimum version + +Please ensure your Stacks version is up-to-date (using the latest release). + +### Event observer + +You will need to add a _new_ event observer that relays information from the sBTC smart contracts to the sBTC signer: + +```toml +[[events_observer]] +endpoint = "sbtc-signer:8801" +events_keys = [ + "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry::print", +] +``` + +### Reference configuration + +See [here](https://github.com/stacks-network/sbtc/blob/main/docker/mainnet/nodes/stacks/Config.toml.in). + +## Configure your sBTC Signer + +The signer configuration file (`signer-config.toml`) defines the signer's operation parameters. The configuration sections include: + +### Blocklist Client Settings + +```toml +[blocklist_client] +endpoint = "http://blocklist-client:3032" +``` + +### Bitcoin Connection Settings + +Defines how the signer connects to Bitcoin Core: + +```toml +[bitcoin] +rpc_endpoints = ["http://user:pass@your-bitcoin-node:8332"] + +# Note: block_hash_stream_endpoints are no longer used as of v1.1.0 + +# The signer now uses RPC polling for block detection +``` + +### Core Signer Parameters + +Defines the signer's identity and network participation: + +```toml +[signer] +private_key = "your-private-key" # 32 or 33-byte hex format +network = "mainnet" +deployer = "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4" +``` + +### P2P Network Configuration + +Controls how the signer communicates with other network participants: + +```toml +[signer.p2p] +listen_on = ["tcp://0.0.0.0:4122"] +``` + +The signer operates on port 4122 by default and supports both TCP and QUIC protocols for peer communication. The signer will attempt QUIC connections first for improved performance, automatically falling back to TCP if QUIC is unavailable or blocked on the network. + +### Reference configuration + +See [here](https://github.com/stacks-network/sbtc/blob/main/docker/mainnet/sbtc-signer/signer-config.toml.in). + +## Set up your containers + +See [here](https://github.com/stacks-network/sbtc/blob/main/docker/mainnet/docker-compose.yml) for a Docker Compose including all the required components. + +{% hint style="warning" %} +When deploying with Docker, always use [immutable image tags](https://docs.docker.com/reference/cli/docker/image/pull/#pull-an-image-by-digest-immutable-identifier) - the image digests are provided below. Verify the attestation of these images using this [guide](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds#verifying-artifact-attestations-with-the-github-cli). + +We publish our images on [GitHub Container Registry](https://github.com/stacks-sbtc/sbtc/pkgs/container/sbtc). +{% endhint %} + +## Monitoring + +Monitoring Details TBD + +## Troubleshooting + +Troubleshooting Guide TBD diff --git a/docs/build/sbtc/how-to-use-the-sbtc-bridge-with-fordefi.md b/docs/build/sbtc/how-to-use-the-sbtc-bridge-with-fordefi.md new file mode 100644 index 0000000000..344037a335 --- /dev/null +++ b/docs/build/sbtc/how-to-use-the-sbtc-bridge-with-fordefi.md @@ -0,0 +1,130 @@ +# How to Use the sBTC Bridge with Fordefi + +{% hint style="warning" %} +This guide is specifically for entities or teams that use [Fordefi](https://fordefi.com/) as it will demonstrate the flow for a multi-approval transaction policy setup. This assumes you have the Fordefi wallet setup with its browser extension and with its mobile app. +{% endhint %} + +The sBTC Bridge is a web application allowing you to convert your BTC into sBTC on the Stacks chain. If you aren't familiar with sBTC, be sure to check out the [sBTC Conceptual Guide](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/sbtc) to understand how it works. + +{% hint style="danger" %} +Ensure that you are using the bridge located at [sbtc.stacks.co](https://sbtc.stacks.co/). This is the only official sBTC bridge. +{% endhint %} + +The sBTC Bridge has been designed to be as simple as possible to use. But specifically for this guide, a **2-of-2 approval transaction policy**, targeting Bitcoin transactions, has already been setup in the Fordefi UI. It is assumed you have a similar setup as this guide will walkthrough the different steps needed to take in such a scenario where multiple parties need to approve a transaction. + +If you need assistance in setting up such a transaction policy in Fordefi, check out their dedicated [docs](https://docs.fordefi.com/user-guide/policies). + +### Walkthrough for minting sBTC + +Here are the necessary steps to convert your BTC to sBTC using Fordefi: + +{% stepper %} +{% step %} +#### Confirm your BTC and STX vaults + +First, you'll need to make sure you have a vault for Bitcoin, and a separate vault for Stacks. Both of these vaults will be used later when connecting with the sBTC Bridge app. + +

A vault for native Bitcoin assets

+ +

A vault for native Stacks assets

+{% endstep %} + +{% step %} +#### Connect your Fordefi wallet extension + +First, you'll need to connect your Fordefi wallet to the sBTC Bridge app. + +

Choose the option for Fordefi in the wallet selector modal

+{% endstep %} + +{% step %} +#### Choose which Bitcoin and Stacks vault you want to use + +Next, the Fordefi extension will want you to select which Bitcoin vault, and then which Stacks vault you'd want to use. The reasoning for this is because you'll be needing to send a bitcoin transaction first from your Bitcoin vault, then you'll be receiving sBTC to your Stacks vault. + +

The selected Bitcoin vault needs to have at least the minimum required amount (0.001 BTC) of bitcoin to peg-in

+ +

When both vaults are selected, you'll be able to see both at the top of the Fordefi extension when connected

+{% endstep %} + +{% step %} +#### Choose the amount of BTC to deposit + +After your wallet is connected, choose how much BTC you would like to convert to sBTC. + +{% hint style="info" %} +There are two transaction fees required to mint your sBTC. The first is when they initiate the bitcoin deposit transaction within their wallet. The second is a fee used to consolidate the deposit UTXOs into the single Signer's UTXO. This separate transaction fee happens automatically and is set to a max of 80k sats. This is automatically deducted from your minted sBTC. This is not a Signer fee but a regular bitcoin transaction fee. +{% endhint %} + +
+{% endstep %} + +{% step %} +#### Choose the Stacks address to mint the sBTC to + +Next, enter the Stacks address you would like your sBTC minted to. This will just be the Stacks address associated with the Stacks vault that you selected earlier when connecting your Fordefi wallet extension. + +

Review the inputted STX address and then confirm

+{% endstep %} + +{% step %} +#### Create initial BTC transfer + +Your Fordefi wallet extension will pop up prompting you to create the BTC transaction. This transaction is the initial peg-in transfer for your BTC to the sBTC Signers. Hit 'Create' after you confirm the transaction details and necessary approval details. + +{% hint style="info" %} +If you have a transaction policy setup with certain approvals required, hitting 'Create' will not initiate the bitcoin transaction, it will simply store this unsigned transaction in your Fordefi wallet until all necessary approvals are met and then finally signed. +{% endhint %} + +

You'll notice near the bottom of the Create Transaction view of the Fordefi extension is the required approval details. Be certain the other approvers are available to approve the transaction in a timely manner.

+ +

If you ever navigate back to your Fordefi web UI or extension UI, you'll notice this transaction will be marked as 'Pending approval'.

+{% endstep %} + +{% step %} +#### Approve transaction by approvers + +Upon notice of transaction to approvers, each approver will need to approve transaction in their Fordefi mobile wallets before the completion of the final step, which is signing the transaction by the initiator. + +Each approver will need to pull up the pending transaction in their Fordefi mobile wallet and hit 'Approve'. + +

POV of approving transaction by approver

+{% endstep %} + +{% step %} +#### Sign approved transaction + +Once all transaction policies are satisfied and approved, the initiator will need to officially sign the transaction in their Fordefi mobile wallet. + +This mobile signature action will then notify the sBTC Bridge app. + +

The initiator will need to hit 'Sign' once approvals and transaction details are confirmed

+{% endstep %} + +{% step %} +#### Receive your sBTC + +Back in the sBTC Bridge app UI, you can monitor the status of your transaction to see when it has been completed, at which point you can see the sBTC in your Fordefi wallet. It will go through three stages: + +* Pending - Your [Bitcoin transaction](https://mempool.space/tx/6b5e63fbe4e4a4835dcf096ca2d2a8c112898692e28a4c5b38cb39e3e9837604) is processing +* Minting - Your Bitcoin transaction has processed and the [sBTC signers are minting](https://explorer.hiro.so/txid/a9e232289d2c6e50150b034894182d341343e7064b27c8dccbd25ebca79b2947?chain=mainnet) your sBTC +* Completed - Your sBTC has been minted to your wallet + +

The bitcoin and sBTC transactions will take some time to be completely processed by the Signers

+ +

Once both the bitcoin and sBTC mint transactions are confirmed, the sBTC Bridge app will show a 'Complete' status

+ +

You'll be able to see the results of these transactions in your Fordefi wallet

+{% endstep %} +{% endstepper %} + +### Reclaiming BTC + +If your sBTC mint fails, you can reclaim your sBTC. You can do this via the bridge by visiting the reclaim page at https://sbtc.stacks.co/\/reclaim and replacing the bracketed text with your transaction ID as shown below:\ +[https://sbtc.stacks.co/8f37f750b6646f0a217121201967170bd3cfef5f2ebd4f30f359b5e9308470c4/reclaim](https://sbtc.stacks.co/8f37f750b6646f0a217121201967170bd3cfef5f2ebd4f30f359b5e9308470c4/reclaim) + +There is an intermediate step in between depositing BTC and the sBTC signers consolidating it into the single signer UTXO. If the transaction is not picked up by signers, you can reclaim it using this UI. Note there is a 'Lock Time' field on the Reclaim page. That indicates the amount of blocks that must have passed in order to reclaim your BTC. + +
+ +This initiates a Bitcoin transaction that will transfer your BTC back to you. diff --git a/docs/build/sbtc/how-to-use-the-sbtc-bridge.md b/docs/build/sbtc/how-to-use-the-sbtc-bridge.md new file mode 100644 index 0000000000..8356fe3d82 --- /dev/null +++ b/docs/build/sbtc/how-to-use-the-sbtc-bridge.md @@ -0,0 +1,83 @@ +# How to Use the sBTC Bridge + +The sBTC bridge is a web application allowing you to convert your BTC into sBTC on the Stacks chain. + +{% hint style="danger" %} +Ensure that you are using the bridge located at [sbtc.stacks.co](https://sbtc.stacks.co/). This is the only official sBTC bridge. +{% endhint %} + +If you aren't familiar with sBTC, be sure to check out the [sBTC Conceptual Guide](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/sbtc) to understand how it works. + +The bridge has been designed to be as simple as possible to use. In order to utilize sBTC, all you need to do is send a Bitcoin transaction using a supported wallet (like [Leather](https://leather.io/) or [Xverse](https://www.xverse.app/)). + +Below you'll find both a video and written walkthrough of using the bridge. + +### Video Walkthrough + +{% embed url="https://youtu.be/XZruuDgTo4k" %} + +### Written Walkthrough + +There are 5 simple steps to convert your BTC to sBTC. + +{% stepper %} +{% step %} +#### Connect your wallet + +First, you'll need to connect your wallet to the bridge UI. Currently Leather and Xverse are supported, with more on the way. + +
+{% endstep %} + +{% step %} +#### Choose the amount to deposit + +After your wallet is connected, choose how much BTC you would like to convert to sBTC. + +
+ +{% hint style="info" %} +There are two transaction fees required to mint your sBTC. The first is set by the user manually when they initiate the deposit transaction within their wallet. The second is a fee used to consolidate the deposit UTXOs into the single signer UTXO. This separate transaction fee happens automatically and is set to a max of 80k sats. This is automatically deducted from your minted sBTC. This is not a signer fee but a regular Bitcoin transaction fee. +{% endhint %} +{% endstep %} + +{% step %} +#### Choose the Stacks address to mint to + +Next, enter the Stacks address you would like your sBTC minted to. + +
+{% endstep %} + +{% step %} +#### Initiate the transaction + +After you choose your Stacks address, you'll use your connected wallet to transfer the BTC. + +
+{% endstep %} + +{% step %} +#### Receive your sBTC + +In the UI, you can monitor the status of your transaction to see when it has been completed, at which point you can see the sBTC in your wallet. It will go through three stages: + +* Pending - Your Bitcoin transaction is processing +* Minting - Your Bitcoin transaction has processed and the sBTC signers are minting your sBTC +* Completed - Your sBTC has been minted to your wallet + +Note that you may need to enable the display of the sBTC token within your wallet by clicking on 'Manage Tokens' and enabling sBTC. + +
+{% endstep %} +{% endstepper %} + +### Reclaiming BTC + +If your sBTC mint fails, you can reclaim your sBTC. You can do this via the bridge by visiting the reclaim page at https://sbtc.stacks.co/\/reclaim and replacing the bracketed text with your transaction ID, eg. [https://sbtc.stacks.co/8f37f750b6646f0a217121201967170bd3cfef5f2ebd4f30f359b5e9308470c4/reclaim](https://sbtc.stacks.co/8f37f750b6646f0a217121201967170bd3cfef5f2ebd4f30f359b5e9308470c4/reclaim) + +There is an intermediate step in between depositing BTC and the sBTC signers consolidating it into the single signer UTXO. If the transaction is not picked up by signers, you can reclaim it using this UI. Note there is a 'Lock Time' field on the Reclaim page. That indicates the amount of blocks that must have passed in order to reclaim your BTC. + +
+ +This initiates a Bitcoin transaction that will transfer your BTC back to you. diff --git a/docs/build/sbtc/sbtc-builder-quickstart.md b/docs/build/sbtc/sbtc-builder-quickstart.md new file mode 100644 index 0000000000..3379e627f0 --- /dev/null +++ b/docs/build/sbtc/sbtc-builder-quickstart.md @@ -0,0 +1,158 @@ +# sBTC Builder Quickstart + +

source: Hiro

+ +Get up and running with sBTC in 30 minutes or less. This guide covers the essentials for working with sBTC as a SIP-010 token in your smart contracts. + +### What is sBTC? + +sBTC is Bitcoin on Stacks. It's a SIP-010 fungible token that maintains a 1:1 peg with Bitcoin, enabling you to use Bitcoin in smart contracts and DeFi applications on the Stacks blockchain. + +Key points: + +* **1:1 Bitcoin peg**: 1 sBTC always equals 1 BTC +* **SIP-010 token**: Works like any other fungible token on Stacks +* **Programmable**: Use Bitcoin in smart contracts, DeFi protocols, and dApps + +### Quick Setup + +#### Prerequisites + +In order to get the most from this quickstart, you should familiarize yourself with Clarity, Clarinet, Stacks.js, and the Hiro Platform. These are the fundamental building blocks of building Stacks applications. + +* [Stacks Developer Quickstart](https://app.gitbook.com/o/hoh4mQXTl8NvI3cETroY/s/Zz9BLmTU9oydDpL3qiUh/) - For a quick holistic introduction to the Stacks development process, tools, and fundamentals +* [Clarity Crash Course](../clarity-crash-course.md) - For a quick introduction to Clarity +* [Clarinet Docs](https://docs.hiro.so/tools/clarinet) +* [Stacks.js Docs](https://docs.hiro.so/reference/stacks.js) + +Choose your preferred development environment: + +#### Hiro Platform (Recommended) + +The fastest way to start building with sBTC is using the Hiro Platform's hosted devnet. The Platform integrates with your GitHub account. You can either import an existing project from GitHub or start with a Platform template and have it synced with your GitHub account. + +After you create the project in the Platform, you can clone it locally and work with the Platform's cloud devnet by connecting your API key as described in the template's README files. This will allow you to work on your code locally but let Platform handle the complexities of actually running the devnet. + +{% stepper %} +{% step %} +### Create an account + +Create an account at:\ +https://platform.hiro.so +{% endstep %} + +{% step %} +### Create or import a project + +* Select a template or import your own project from GitHub. There are several templates available to use as a starting point. Some have only smart contracts and some are full-stack dapp templates. Starting with one of these ensures you are building with best practices. +* Navigate to your project dashboard +{% endstep %} + +{% step %} +### Start devnet + +* Click the "Devnet" tab +* Click "Start Devnet" +* Wait for status to show "Active" +{% endstep %} + +{% step %} +### Connect your wallet + +* Your devnet wallets are automatically funded with STX and sBTC +* Use the provided wallet addresses within the templates +* The platform templates are automatically connected to Devnet, and there are instructions in the READMEs of the templates for how to connect your frontend to your Devnet instance +{% endstep %} +{% endstepper %} + +#### Local with Clarinet + +If you would prefer to have your devnet running locally instead of in the Platform cloud, you can run one yourself. + +{% stepper %} +{% step %} +### Install Clarinet (version 3.x) + +```bash +brew install clarinet +``` +{% endstep %} + +{% step %} +### Create a new project + +```bash +clarinet new my-sbtc-project +cd my-sbtc-project +``` +{% endstep %} + +{% step %} +### Add sBTC requirements + +```bash +clarinet requirements add SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-deposit +``` + +This automatically includes the sBTC token contract in your Clarinet context so you can reference it within your contracts. +{% endstep %} + +{% step %} +### Start devnet + +```bash +clarinet devnet start +``` + +With either of these options, your Devnet wallets are automatically funded with sBTC. You just need to include the sBTC contract in your contract requirements as shown above. +{% endstep %} +{% endstepper %} + +### Working with sBTC in Smart Contracts + +sBTC follows the SIP-010 standard, making it easy to integrate into your contracts. + +The primary function you'll be using is the `transfer` function. That's because sBTC exists as a 1:1 Bitcoin peg via a SIP-010 token. Minting is handled by the protocol, the main function of writing smart contracts that use sBTC is to move it around, which means using the `transfer` function. + +Here's a very basic example of how to transfer sBTC within your contract. + +#### Basic Transfer Example + +Create a new contract that accepts sBTC payments. You can do this within the Clarinet project folder with `clarinet contract new sbtc-payment`. + +{% code title="contracts/sbtc-payment.clar" %} +```clarity +;; contracts/sbtc-payment.clar + +;; Define the sBTC token contract reference +(define-constant sbtc-token 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token) + +;; Error codes +(define-constant err-insufficient-balance (err u100)) +(define-constant err-transfer-failed (err u101)) + +;; Accept sBTC payment +(define-public (pay-with-sbtc (amount uint) (recipient principal)) + (contract-call? sbtc-token transfer + amount + tx-sender + recipient + none)) +``` +{% endcode %} + +You can test out this contract by either using the UI within the Platform to call the functions directly if you have devnet running or by opening the console with `clarinet console`. + +Once you do that you'll see that your devnet accounts have automatically been funded with sBTC. + +
+ +Once you are ready to deploy to testnet, you can do so by editing your deployment plan as outlined in [this guide](https://docs.hiro.so/tools/clarinet/sbtc-integration). + +### Conclusion + +You can build pretty much anything you want using this simple foundation, as all of the complexity of sBTC is handled behind the scenes by the protocol. + +What's needed now is for builders to take this foundation and build interesting, useful things with it. sBTC unlocks a lot of additional functionality for Bitcoin that previously would have only been possible with either custodied solutions or slow, complex solutions with poor UX. + +If you are interested, you can read more about how sBTC works in the [sBTC Concept Guide](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/sbtc). diff --git a/docs/build/testing-smart-contracts/README.md b/docs/build/testing-smart-contracts/README.md new file mode 100644 index 0000000000..08a6f49e9d --- /dev/null +++ b/docs/build/testing-smart-contracts/README.md @@ -0,0 +1,12 @@ +# Testing Smart Contracts + +{% hint style="danger" %} +Smart contracts are immutable once deployed. Bugs are permanent. Test them thoroughly. +{% endhint %} + +This section covers testing Clarity contracts. + +* [Fuzz Testing](fuzz-testing.md): Use Rendezvous to hammer your contract with random inputs. It helps expose edge cases and vulnerabilities. +* [Clarity Unit Testing](https://github.com/stacks-network/clarunit) + +More guides will follow. diff --git a/docs/build/testing-smart-contracts/fuzz-testing.md b/docs/build/testing-smart-contracts/fuzz-testing.md new file mode 100644 index 0000000000..57d3d73b1e --- /dev/null +++ b/docs/build/testing-smart-contracts/fuzz-testing.md @@ -0,0 +1,148 @@ +# Fuzz Testing + +Smart contracts on Stacks are immutable. Bugs are forever. Test early. Test often. Fuzzing finds edge cases that unit tests often miss. + +## What is Fuzz Testing? + +Fuzzing hits your code with random inputs. It helps uncover unexpected behavior and subtle bugs. Unlike unit tests, it explores paths you didn't think of. + +## What is Rendezvous? + +Rendezvous (`rv`) is a Clarity fuzzer. It supports: + +### Property-Based Testing + +You extract properties about your smart contract using Clarity. Rendezvous checks them multiple times with random inputs, in a stateful manner (the smart contract's state is not refreshed during the run). + +**What is a property?** + +A property is a universal truth about your smart contract's state, functions, etc. + +**How to extract a property?** + +Say that your smart contract has a function that reverses a list of `uint`s. In this case, one property can be that "reversing a list twice returns the original list". The property will look like this: + +```clarity +(define-public (test-reverse-list (seq (list 127 uint))) + (begin + (asserts! + (is-eq seq + (reverse-uint + (reverse-uint seq) + ) + ) + (err u999) + ) + (ok true) + ) +) +``` + +**Making your property valid for Rendezvous** + +> For a property to be cosidered valid by Rendezvous, it has to comply with the following rules: +> +> * Function name starts with `test-` +> * Function is declared as `public` +> * Test passes when it returns `(ok true)` +> * Test would be discarded if it returned `(ok false)` +> * Test fails if it returns an error or throws an exception + +*** + +### Invariant Testing + +You define read-only conditions in Clarity that must always hold true. Rendezvous attempts to create state transitions in your smart contract and continuously checks the conditions you defined to hold. + +**What is an invariant?** + +An invariant is a general truth regarding your smart contract's internal state. It will not be able to mutate the state, its role being solely to check the integrity of the state. + +**How to extract an invariant?** + +Say that you have a counter contract, having functions to `increment` and `decrement`. In this case, you could use the Rendezvous [`context`](https://stacks-network.github.io/rendezvous/chapter_6.html?#the-rendezvous-context) to extract an invariant regarding your smart contract's internal state: + +```clarity +(define-read-only (invariant-counter-gt-zero) + (let + ( + (increment-num-calls + (default-to u0 (get called (map-get? context "increment"))) + ) + (decrement-num-calls + (default-to u0 (get called (map-get? context "decrement"))) + ) + ) + (if + (<= increment-num-calls decrement-num-calls) + true + (> (var-get counter) u0) + ) + ) +) +``` + +**Making your invariant valid for Rendezvous** + +> For an invariant to be cosidered valid by Rendezvous, it has to complain to the following ruleset: +> +> * Function name starts with invariant- +> * Function is declared as read-only (not public) +> * Function returns a boolean value (true if the invariant holds, false if violated) +> * The test can use the special context map to access execution history + +## Why Test in Clarity? + +{% stepper %} +{% step %} +### Same constraints as production + +Tests operate under the exact same constraints as production code. +{% endstep %} + +{% step %} +### Better understanding of Clarity + +Writing tests in Clarity improves your familiarity with the language and its semantics. +{% endstep %} + +{% step %} +### No need to expose internals + +You don't have to expose internal functions as public solely for testing. +{% endstep %} + +{% step %} +### Fewer tools to manage + +Running tests in Clarity reduces the number of external tools and integrations you need to maintain. +{% endstep %} +{% endstepper %} + +## Getting Started + +Put tests next to contracts. Rendezvous will find them. + +``` +my-project/ +├── Clarinet.toml +├── contracts/ +│ ├── my-contract.clar # Contract +│ ├── my-contract.tests.clar # Tests +└── settings/ + └── Devnet.toml +``` + +### Installation + +To install Rendezvous as a dependency in your project, use `npm`: + +``` +npm install @stacks/rendezvous +``` + +This will add Rendezvous to your project's `node_modules` and update your `package.json`. + +## Rendezvous Docs + +See full docs at: [https://stacks-network.github.io/rendezvous](https://stacks-network.github.io/rendezvous/) diff --git a/docs/contribute/README.md b/docs/contribute/README.md new file mode 100644 index 0000000000..53a04b8288 --- /dev/null +++ b/docs/contribute/README.md @@ -0,0 +1,29 @@ +# Contribute + +### Contribute to Stacks Core + +There is a [detailed contribution guide](https://github.com/stacks-network/stacks-core/blob/master/CONTRIBUTING.md) that lives in the Stacks core GitHub repository that is the best place to get started contributing to Stacks. + +### Contribute to these Docs + +The Stacks docs are built using GitBook with a two-way sync with the [docs repository on GitHub](https://github.com/stacks-network/docs). + +Because of this two-way sync, you can contribute to the documentation in one of two ways: + +{% stepper %} +{% step %} +### Fork the repo and create a PR + +You can fork the docs repo, add your change, and then create a PR to be merged into the main docs. +{% endstep %} + +{% step %} +### Create an issue + +You can create an issue, and someone that works on the docs will take a look and implement it if it is a necessary change. +{% endstep %} +{% endstepper %} + +What kinds of changes are we looking for? + +If you see a typo, a missing tutorial, an unclear explanation, or really anything else you think could improve the quality of the documentation, please feel free to open an issue or create a pull request. diff --git a/docs/contribute/SUMMARY.md b/docs/contribute/SUMMARY.md new file mode 100644 index 0000000000..de25e5d9b1 --- /dev/null +++ b/docs/contribute/SUMMARY.md @@ -0,0 +1,3 @@ +# Table of contents + +* [Contribute](README.md) diff --git a/docs/learn/.gitbook.yaml b/docs/learn/.gitbook.yaml new file mode 100644 index 0000000000..515d276164 --- /dev/null +++ b/docs/learn/.gitbook.yaml @@ -0,0 +1,61 @@ +root: ./ + +redirects: + # stacks-101 redirects + concepts/stacks-101/what-is-stacks: stacks-101/what-is-stacks.md + concepts/stacks-101/bitcoin-connection: stacks-101/bitcoin-connection.md + concepts/stacks-101/proof-of-transfer: stacks-101/proof-of-transfer.md + concepts/stacks-101/stacks-among-other-layers: stacks-101/stacks-among-other-layers.md + concepts/stacks-101/financial-incentive-and-security-budget: stacks-101/financial-incentive-and-security-budget.md + + # block-production redirects + concepts/block-production/bitcoin-finality: block-production/bitcoin-finality.md + concepts/block-production/bitcoin-reorgs: block-production/bitcoin-reorgs.md + concepts/block-production/mining: block-production/mining.md + concepts/block-production/stackers-and-signing: block-production/signing.md + concepts/block-production/stacking: block-production/stacking.md + + # clarity redirects + concepts/clarity/decidability: clarity/decidability.md + concepts/clarity/overview: clarity/overview.md + + # network-fundamentals redirects + concepts/network-fundamentals/accounts: network-fundamentals/accounts.md + concepts/network-fundamentals/authentication: network-fundamentals/authentication.md + concepts/network-fundamentals/bitcoin-name-system: network-fundamentals/bitcoin-name-system.md + concepts/network-fundamentals/mainnet-and-testnets: network-fundamentals/mainnet-and-testnets.md + concepts/network-fundamentals/network: network-fundamentals/network-basics.md + concepts/network-fundamentals/sips: network-fundamentals/sips.md + concepts/network-fundamentals/technical-specifications/audits: network-fundamentals/technical-specifications/audits.md + + # transactions redirects + concepts/transactions/post-conditions: transactions/post-conditions.md + concepts/transactions/transactions: transactions/how-transactions-work.md + + # sbtc redirects + concepts/sbtc/core-features: sbtc/core-features.md + concepts/sbtc/emily: sbtc/emily-api.md + concepts/sbtc/peg-wallet-utxo: sbtc/peg-wallet-utxo.md + concepts/sbtc/sbtc-audits: sbtc/sbtc-audits.md + concepts/sbtc/sbtc-faq: sbtc/sbtc-faq.md + + # sbtc auxiliary-features redirects + concepts/sbtc/auxiliary-features/fee-sponsorship: sbtc/auxiliary-features/transaction-fee-sponsorship.md + concepts/sbtc/auxiliary-features/signer-wallet-rotation: sbtc/auxiliary-features/signer-wallet-rotation.md + + # sbtc clarity-contracts redirects + concepts/sbtc/clarity-contracts/sbtc-bootstrap-signers: sbtc/clarity-contracts/sbtc-bootstrap-signers.md + concepts/sbtc/clarity-contracts/sbtc-deposit: sbtc/clarity-contracts/sbtc-deposit.md + concepts/sbtc/clarity-contracts/sbtc-registry: sbtc/clarity-contracts/sbtc-registry.md + concepts/sbtc/clarity-contracts/sbtc-signers: sbtc/clarity-contracts/sbtc-signers.md + concepts/sbtc/clarity-contracts/sbtc-token: sbtc/clarity-contracts/sbtc-token.md + concepts/sbtc/clarity-contracts/sbtc-withdrawal: sbtc/clarity-contracts/sbtc-withdrawal.md + + # sbtc operations redirects + concepts/sbtc/operations/deposit-withdrawal-times: sbtc/sbtc-operations/deposit-vs-withdrawal-times.md + concepts/sbtc/operations/deposit: sbtc/sbtc-operations/deposit.md + concepts/sbtc/operations/withdrawal: sbtc/sbtc-operations/withdrawal.md + + # sbtc walkthroughs redirects + concepts/sbtc/walkthroughs/sbtc-transaction-lifecycle: sbtc/walkthroughs/sbtc-transaction-walkthrough.md + concepts/sbtc/walkthroughs/signer-process: sbtc/walkthroughs/signer-process-walkthrough.md diff --git a/docs/learn/.gitbook/assets/AD_4nXdlY8MKm4IEls6XieRtpunfge6KTNSw2HT_o9iD8FgIL3RCJuzKa781Ft oXNCEn_rIqMqu0_hqD5 GPrF9cT6rFXdnA1BASFoU3Uy6VgR2ARfp 0FnLgrM7GH7hdx Ia2m_DpdonZmlwqTMd1sQe0XqgX4 b/docs/learn/.gitbook/assets/AD_4nXdlY8MKm4IEls6XieRtpunfge6KTNSw2HT_o9iD8FgIL3RCJuzKa781Ft oXNCEn_rIqMqu0_hqD5 GPrF9cT6rFXdnA1BASFoU3Uy6VgR2ARfp 0FnLgrM7GH7hdx Ia2m_DpdonZmlwqTMd1sQe0XqgX4 new file mode 100644 index 0000000000..3f022d11c4 Binary files /dev/null and b/docs/learn/.gitbook/assets/AD_4nXdlY8MKm4IEls6XieRtpunfge6KTNSw2HT_o9iD8FgIL3RCJuzKa781Ft oXNCEn_rIqMqu0_hqD5 GPrF9cT6rFXdnA1BASFoU3Uy6VgR2ARfp 0FnLgrM7GH7hdx Ia2m_DpdonZmlwqTMd1sQe0XqgX4 differ diff --git a/docs/learn/.gitbook/assets/Blockstack_Desktop_Wallet_Pentest_Report_11-12-2020.pdf b/docs/learn/.gitbook/assets/Blockstack_Desktop_Wallet_Pentest_Report_11-12-2020.pdf new file mode 100644 index 0000000000..72b6b717bf Binary files /dev/null and b/docs/learn/.gitbook/assets/Blockstack_Desktop_Wallet_Pentest_Report_11-12-2020.pdf differ diff --git a/docs/learn/.gitbook/assets/Clarity Alliance - sBTC Rewards Program.pdf b/docs/learn/.gitbook/assets/Clarity Alliance - sBTC Rewards Program.pdf new file mode 100644 index 0000000000..3fab1ee354 Binary files /dev/null and b/docs/learn/.gitbook/assets/Clarity Alliance - sBTC Rewards Program.pdf differ diff --git a/docs/learn/.gitbook/assets/Clarity Alliance - sBTC-2.pdf b/docs/learn/.gitbook/assets/Clarity Alliance - sBTC-2.pdf new file mode 100644 index 0000000000..46fb8bb7ca Binary files /dev/null and b/docs/learn/.gitbook/assets/Clarity Alliance - sBTC-2.pdf differ diff --git a/docs/learn/.gitbook/assets/Clarity Alliance - sBTC.pdf b/docs/learn/.gitbook/assets/Clarity Alliance - sBTC.pdf new file mode 100644 index 0000000000..46fb8bb7ca Binary files /dev/null and b/docs/learn/.gitbook/assets/Clarity Alliance - sBTC.pdf differ diff --git a/docs/learn/.gitbook/assets/CoinFabrik - Signer Binary.pdf b/docs/learn/.gitbook/assets/CoinFabrik - Signer Binary.pdf new file mode 100644 index 0000000000..dc059e72dc Binary files /dev/null and b/docs/learn/.gitbook/assets/CoinFabrik - Signer Binary.pdf differ diff --git a/docs/learn/.gitbook/assets/CoinFabrik - StackerDB.pdf b/docs/learn/.gitbook/assets/CoinFabrik - StackerDB.pdf new file mode 100644 index 0000000000..b0e846fba3 Binary files /dev/null and b/docs/learn/.gitbook/assets/CoinFabrik - StackerDB.pdf differ diff --git a/docs/learn/.gitbook/assets/CoinFabrik - Stacks LibSigner.pdf b/docs/learn/.gitbook/assets/CoinFabrik - Stacks LibSigner.pdf new file mode 100644 index 0000000000..6e1a11c272 Binary files /dev/null and b/docs/learn/.gitbook/assets/CoinFabrik - Stacks LibSigner.pdf differ diff --git a/docs/learn/.gitbook/assets/CoinFabrik - Stacks Signer Audit.pdf b/docs/learn/.gitbook/assets/CoinFabrik - Stacks Signer Audit.pdf new file mode 100644 index 0000000000..4a733d4e4f Binary files /dev/null and b/docs/learn/.gitbook/assets/CoinFabrik - Stacks Signer Audit.pdf differ diff --git a/docs/learn/.gitbook/assets/CoinFabrik - WSTS.pdf b/docs/learn/.gitbook/assets/CoinFabrik - WSTS.pdf new file mode 100644 index 0000000000..1b73a0857e Binary files /dev/null and b/docs/learn/.gitbook/assets/CoinFabrik - WSTS.pdf differ diff --git a/docs/learn/.gitbook/assets/CoinFabrik_WSTS.pdf b/docs/learn/.gitbook/assets/CoinFabrik_WSTS.pdf new file mode 100644 index 0000000000..1b73a0857e Binary files /dev/null and b/docs/learn/.gitbook/assets/CoinFabrik_WSTS.pdf differ diff --git a/docs/learn/.gitbook/assets/Coinfabrik - Stacks PoX.pdf b/docs/learn/.gitbook/assets/Coinfabrik - Stacks PoX.pdf new file mode 100644 index 0000000000..e9678769ea Binary files /dev/null and b/docs/learn/.gitbook/assets/Coinfabrik - Stacks PoX.pdf differ diff --git a/docs/learn/.gitbook/assets/Frame 316125325.jpg b/docs/learn/.gitbook/assets/Frame 316125325.jpg new file mode 100644 index 0000000000..884cae9020 Binary files /dev/null and b/docs/learn/.gitbook/assets/Frame 316125325.jpg differ diff --git a/docs/learn/.gitbook/assets/Frame 316125960.jpg b/docs/learn/.gitbook/assets/Frame 316125960.jpg new file mode 100644 index 0000000000..5d63e59df0 Binary files /dev/null and b/docs/learn/.gitbook/assets/Frame 316125960.jpg differ diff --git a/docs/learn/.gitbook/assets/Frame 316126243.jpg b/docs/learn/.gitbook/assets/Frame 316126243.jpg new file mode 100644 index 0000000000..c5e96dfcbd Binary files /dev/null and b/docs/learn/.gitbook/assets/Frame 316126243.jpg differ diff --git a/docs/learn/.gitbook/assets/Frame 316126254.jpg b/docs/learn/.gitbook/assets/Frame 316126254.jpg new file mode 100644 index 0000000000..dfb22d9988 Binary files /dev/null and b/docs/learn/.gitbook/assets/Frame 316126254.jpg differ diff --git a/docs/learn/.gitbook/assets/Frame 316126255.jpg b/docs/learn/.gitbook/assets/Frame 316126255.jpg new file mode 100644 index 0000000000..2d78244848 Binary files /dev/null and b/docs/learn/.gitbook/assets/Frame 316126255.jpg differ diff --git a/docs/learn/.gitbook/assets/Frame 316126258.jpg b/docs/learn/.gitbook/assets/Frame 316126258.jpg new file mode 100644 index 0000000000..273720c216 Binary files /dev/null and b/docs/learn/.gitbook/assets/Frame 316126258.jpg differ diff --git a/docs/learn/.gitbook/assets/Frame 316126264 (1).jpg b/docs/learn/.gitbook/assets/Frame 316126264 (1).jpg new file mode 100644 index 0000000000..8edeb3144e Binary files /dev/null and b/docs/learn/.gitbook/assets/Frame 316126264 (1).jpg differ diff --git a/docs/learn/.gitbook/assets/Frame 316126264.jpg b/docs/learn/.gitbook/assets/Frame 316126264.jpg new file mode 100644 index 0000000000..8edeb3144e Binary files /dev/null and b/docs/learn/.gitbook/assets/Frame 316126264.jpg differ diff --git a/docs/learn/.gitbook/assets/Group 316124776.png b/docs/learn/.gitbook/assets/Group 316124776.png new file mode 100644 index 0000000000..352540fc4d Binary files /dev/null and b/docs/learn/.gitbook/assets/Group 316124776.png differ diff --git a/docs/learn/.gitbook/assets/Group 316124777 (1).png b/docs/learn/.gitbook/assets/Group 316124777 (1).png new file mode 100644 index 0000000000..07b169772b Binary files /dev/null and b/docs/learn/.gitbook/assets/Group 316124777 (1).png differ diff --git a/docs/learn/.gitbook/assets/Group 316124777 (2).png b/docs/learn/.gitbook/assets/Group 316124777 (2).png new file mode 100644 index 0000000000..b203a65e29 Binary files /dev/null and b/docs/learn/.gitbook/assets/Group 316124777 (2).png differ diff --git a/docs/learn/.gitbook/assets/NCC_Group_Stacks_Blockchain_Audit_Report_2020-11-23_v1.0.pdf b/docs/learn/.gitbook/assets/NCC_Group_Stacks_Blockchain_Audit_Report_2020-11-23_v1.0.pdf new file mode 100644 index 0000000000..bed4f3bd8b Binary files /dev/null and b/docs/learn/.gitbook/assets/NCC_Group_Stacks_Blockchain_Audit_Report_2020-11-23_v1.0.pdf differ diff --git a/docs/learn/.gitbook/assets/NCC_Group_Stacks_Wallet_Report_2020-11-17_v1.0.pdf b/docs/learn/.gitbook/assets/NCC_Group_Stacks_Wallet_Report_2020-11-17_v1.0.pdf new file mode 100644 index 0000000000..cd00d448fa Binary files /dev/null and b/docs/learn/.gitbook/assets/NCC_Group_Stacks_Wallet_Report_2020-11-17_v1.0.pdf differ diff --git a/docs/learn/.gitbook/assets/Ottersec - WSTS.pdf b/docs/learn/.gitbook/assets/Ottersec - WSTS.pdf new file mode 100644 index 0000000000..b3929dbb39 Binary files /dev/null and b/docs/learn/.gitbook/assets/Ottersec - WSTS.pdf differ diff --git a/docs/learn/.gitbook/assets/Ottersec - sBTC Withdrawal.pdf b/docs/learn/.gitbook/assets/Ottersec - sBTC Withdrawal.pdf new file mode 100644 index 0000000000..b093ed87bf Binary files /dev/null and b/docs/learn/.gitbook/assets/Ottersec - sBTC Withdrawal.pdf differ diff --git a/docs/learn/.gitbook/assets/Ottersec - stacks_sbtc_audit_final.pdf b/docs/learn/.gitbook/assets/Ottersec - stacks_sbtc_audit_final.pdf new file mode 100644 index 0000000000..b093ed87bf Binary files /dev/null and b/docs/learn/.gitbook/assets/Ottersec - stacks_sbtc_audit_final.pdf differ diff --git a/docs/learn/.gitbook/assets/Ottersec - stacks_wsts_audit_final.pdf b/docs/learn/.gitbook/assets/Ottersec - stacks_wsts_audit_final.pdf new file mode 100644 index 0000000000..b3929dbb39 Binary files /dev/null and b/docs/learn/.gitbook/assets/Ottersec - stacks_wsts_audit_final.pdf differ diff --git a/docs/learn/.gitbook/assets/Quantstamp - Network State Machine.pdf b/docs/learn/.gitbook/assets/Quantstamp - Network State Machine.pdf new file mode 100644 index 0000000000..150723b091 Binary files /dev/null and b/docs/learn/.gitbook/assets/Quantstamp - Network State Machine.pdf differ diff --git a/docs/learn/.gitbook/assets/Screenshot 2025-10-11 at 3.45.40 PM.png b/docs/learn/.gitbook/assets/Screenshot 2025-10-11 at 3.45.40 PM.png new file mode 100644 index 0000000000..2532146af2 Binary files /dev/null and b/docs/learn/.gitbook/assets/Screenshot 2025-10-11 at 3.45.40 PM.png differ diff --git a/docs/learn/.gitbook/assets/image (1).png b/docs/learn/.gitbook/assets/image (1).png new file mode 100644 index 0000000000..38bbfbae84 Binary files /dev/null and b/docs/learn/.gitbook/assets/image (1).png differ diff --git a/docs/learn/.gitbook/assets/image (10).png b/docs/learn/.gitbook/assets/image (10).png new file mode 100644 index 0000000000..47111d002c Binary files /dev/null and b/docs/learn/.gitbook/assets/image (10).png differ diff --git a/docs/learn/.gitbook/assets/image (11).png b/docs/learn/.gitbook/assets/image (11).png new file mode 100644 index 0000000000..3716767855 Binary files /dev/null and b/docs/learn/.gitbook/assets/image (11).png differ diff --git a/docs/learn/.gitbook/assets/image (12).png b/docs/learn/.gitbook/assets/image (12).png new file mode 100644 index 0000000000..16dc0b816b Binary files /dev/null and b/docs/learn/.gitbook/assets/image (12).png differ diff --git a/docs/learn/.gitbook/assets/image (13).png b/docs/learn/.gitbook/assets/image (13).png new file mode 100644 index 0000000000..020878ff7d Binary files /dev/null and b/docs/learn/.gitbook/assets/image (13).png differ diff --git a/docs/learn/.gitbook/assets/image (14).png b/docs/learn/.gitbook/assets/image (14).png new file mode 100644 index 0000000000..341e6adaba Binary files /dev/null and b/docs/learn/.gitbook/assets/image (14).png differ diff --git a/docs/learn/.gitbook/assets/image (15).png b/docs/learn/.gitbook/assets/image (15).png new file mode 100644 index 0000000000..26036ee1fa Binary files /dev/null and b/docs/learn/.gitbook/assets/image (15).png differ diff --git a/docs/learn/.gitbook/assets/image (16).png b/docs/learn/.gitbook/assets/image (16).png new file mode 100644 index 0000000000..1df8dcc9ca Binary files /dev/null and b/docs/learn/.gitbook/assets/image (16).png differ diff --git a/docs/learn/.gitbook/assets/image (17).png b/docs/learn/.gitbook/assets/image (17).png new file mode 100644 index 0000000000..26b4e9ac6c Binary files /dev/null and b/docs/learn/.gitbook/assets/image (17).png differ diff --git a/docs/learn/.gitbook/assets/image (18).png b/docs/learn/.gitbook/assets/image (18).png new file mode 100644 index 0000000000..462a9ff6b7 Binary files /dev/null and b/docs/learn/.gitbook/assets/image (18).png differ diff --git a/docs/learn/.gitbook/assets/image (19).png b/docs/learn/.gitbook/assets/image (19).png new file mode 100644 index 0000000000..afa91fa7dd Binary files /dev/null and b/docs/learn/.gitbook/assets/image (19).png differ diff --git a/docs/learn/.gitbook/assets/image (2).png b/docs/learn/.gitbook/assets/image (2).png new file mode 100644 index 0000000000..402906fc4f Binary files /dev/null and b/docs/learn/.gitbook/assets/image (2).png differ diff --git a/docs/learn/.gitbook/assets/image (20).png b/docs/learn/.gitbook/assets/image (20).png new file mode 100644 index 0000000000..56cb1a4122 Binary files /dev/null and b/docs/learn/.gitbook/assets/image (20).png differ diff --git a/docs/learn/.gitbook/assets/image (21).png b/docs/learn/.gitbook/assets/image (21).png new file mode 100644 index 0000000000..4b4b6fb18a Binary files /dev/null and b/docs/learn/.gitbook/assets/image (21).png differ diff --git a/docs/learn/.gitbook/assets/image (22).png b/docs/learn/.gitbook/assets/image (22).png new file mode 100644 index 0000000000..a2f699bc2a Binary files /dev/null and b/docs/learn/.gitbook/assets/image (22).png differ diff --git a/docs/learn/.gitbook/assets/image (23).png b/docs/learn/.gitbook/assets/image (23).png new file mode 100644 index 0000000000..4b4a055139 Binary files /dev/null and b/docs/learn/.gitbook/assets/image (23).png differ diff --git a/docs/learn/.gitbook/assets/image (24).png b/docs/learn/.gitbook/assets/image (24).png new file mode 100644 index 0000000000..0f140b843b Binary files /dev/null and b/docs/learn/.gitbook/assets/image (24).png differ diff --git a/docs/learn/.gitbook/assets/image (25).png b/docs/learn/.gitbook/assets/image (25).png new file mode 100644 index 0000000000..5b26b1c9eb Binary files /dev/null and b/docs/learn/.gitbook/assets/image (25).png differ diff --git a/docs/learn/.gitbook/assets/image (26).png b/docs/learn/.gitbook/assets/image (26).png new file mode 100644 index 0000000000..c269977a82 Binary files /dev/null and b/docs/learn/.gitbook/assets/image (26).png differ diff --git a/docs/learn/.gitbook/assets/image (27).png b/docs/learn/.gitbook/assets/image (27).png new file mode 100644 index 0000000000..6b3929bea8 Binary files /dev/null and b/docs/learn/.gitbook/assets/image (27).png differ diff --git a/docs/learn/.gitbook/assets/image (3).png b/docs/learn/.gitbook/assets/image (3).png new file mode 100644 index 0000000000..96676a53a7 Binary files /dev/null and b/docs/learn/.gitbook/assets/image (3).png differ diff --git a/docs/learn/.gitbook/assets/image (4).png b/docs/learn/.gitbook/assets/image (4).png new file mode 100644 index 0000000000..412a18b787 Binary files /dev/null and b/docs/learn/.gitbook/assets/image (4).png differ diff --git a/docs/learn/.gitbook/assets/image (5).png b/docs/learn/.gitbook/assets/image (5).png new file mode 100644 index 0000000000..b908551494 Binary files /dev/null and b/docs/learn/.gitbook/assets/image (5).png differ diff --git a/docs/learn/.gitbook/assets/image (6).png b/docs/learn/.gitbook/assets/image (6).png new file mode 100644 index 0000000000..b5725bb6dc Binary files /dev/null and b/docs/learn/.gitbook/assets/image (6).png differ diff --git a/docs/learn/.gitbook/assets/image (7).png b/docs/learn/.gitbook/assets/image (7).png new file mode 100644 index 0000000000..572743764d Binary files /dev/null and b/docs/learn/.gitbook/assets/image (7).png differ diff --git a/docs/learn/.gitbook/assets/image (8).png b/docs/learn/.gitbook/assets/image (8).png new file mode 100644 index 0000000000..5e2e14ea39 Binary files /dev/null and b/docs/learn/.gitbook/assets/image (8).png differ diff --git a/docs/learn/.gitbook/assets/image (9).png b/docs/learn/.gitbook/assets/image (9).png new file mode 100644 index 0000000000..bc81eabb22 Binary files /dev/null and b/docs/learn/.gitbook/assets/image (9).png differ diff --git a/docs/learn/.gitbook/assets/image.png b/docs/learn/.gitbook/assets/image.png new file mode 100644 index 0000000000..27ae573acc Binary files /dev/null and b/docs/learn/.gitbook/assets/image.png differ diff --git a/docs/learn/README.md b/docs/learn/README.md new file mode 100644 index 0000000000..59d7fb6bda --- /dev/null +++ b/docs/learn/README.md @@ -0,0 +1,80 @@ +--- +layout: + width: default + title: + visible: true + description: + visible: true + tableOfContents: + visible: true + outline: + visible: true + pagination: + visible: true + metadata: + visible: true +--- + +# Start Here + +
+ +## Stacks: The TL;DR + +**Stacks is the leading Bitcoin L2, bringing smart contract functionality to Bitcoin, without modifying Bitcoin itself.** + +{% hint style="info" %} +Want to get a guided introduction to everything you need to know to become a Stacks developer? The Stacks Primer is a 5-day email course designed to take you from brand new to building your first contract, and even how to get paid for building out your own project. + +[Take the Course](https://stacks.org/dev) +{% endhint %} + +It does so through three key components, that we'll dig into in more detail in the rest of the docs: + +#### Proof of Transfer + +Proof of Transfer (PoX) is the block production mechanism of the Stacks chain. Essentially, it attempts to recreate the block production patterns of PoW programmatically. Stacks miners spend BTC for a chance to mine new Stacks blocks. Under the hood, this block production mechanism anchors Stacks blocks to Bitcoin blocks, making it as hard to reverse a Stacks block as it is to reverse a Bitcoin block. That's a big claim, and we unpack it in further detail in the sections on Nakamoto block production. + +#### Clarity + +Clarity is the smart contract language that Stacks uses. It has been designed from the ground up to make it easier for developers to write safe, secure smart contracts. Additionally, since it has been purpose-built for Stacks and Bitcoin, there are built-in functions for reading Bitcoin state, which means you can use Bitcoin state to perform actions in Clarity. For example, you could set up a check to make sure a particular Bitcoin transaction has occurred before executing a mint function in Clarity, which just so happens to be what happens with the third component: sBTC. + +#### sBTC + +sBTC is the trust-minimized 2-way Bitcoin peg on the Stacks layer. sBTC is the key to making Bitcoin programmable and bringing full smart contract functionality to Bitcoin via Stacks. sBTC is not a federation, but operates as an open-network, decentralized 2-way peg solution to bring smart contract functionality to Bitcoin with as little counterparty risk as possible. There is an entire section of these docs dedicated to explaining[ sBTC](broken-reference). + +### How to Use These Docs + +{% embed url="https://www.youtube.com/watch?v=eMbzbR53Avo" %} + +### AI-Powered Semantic Search + +Looking for something specific? These docs are integrated with AI-powered semantic search — hit `Cmd/Ctrl + K` to open up the search box and ask the docs whatever you like. + +### What Next? + +#### Learn About Stacks + +Looking to learn more about exactly how Stacks works? The section in the left navigation is where you'll want to go. The "[What is Stacks](stacks-101/what-is-stacks.md)" page is the best place to start your learning journey. This is where you can dive deep into exactly how Stacks works and learn about all the different building blocks. + +#### Build a Stacks Dapp + +Are you a developer itching to get building? The [Quickstart tutorial](https://app.gitbook.com/o/hoh4mQXTl8NvI3cETroY/s/Zz9BLmTU9oydDpL3qiUh/) is the best place to start. It will introduce you to the essential things you need to know to build on Stacks in just 30 minutes. After that, check the rest of the Guides & Tutorials to learn how to build things like DeFi apps, crowdfunding, and collectibles, among other use cases. + +#### Run a Stacks Node + +Looking to run a Stacks node? You can either run a follower node or a miner node. We have guides for how to do both on testnet and mainnet in the "[Run a Node](https://app.gitbook.com/o/hoh4mQXTl8NvI3cETroY/s/4cpTb2lbw0LAOuMHrvhA/)" section of the Guides. + +#### Run a Signer + +Signers are a critical component of the Stacks ecosystem and are in charge of validating and appending new Stacks blocks and sBTC transactions. We have an entire section dedicated to [running a signer](https://app.gitbook.com/s/4cpTb2lbw0LAOuMHrvhA/run-a-signer). + +#### Stack Your STX + +Stacking is one of the key components behind Stacks and the Proof of Transfer consensus mechanism. There are many different ways you can stack depending on if you are stacking solo, stacking in a pool, and running a signer or not. We have a [section on stacking](https://app.gitbook.com/s/4cpTb2lbw0LAOuMHrvhA/stacking-stx) to walk you through the process no matter your situation. + +#### Get More Involved + +Looking to grow your career in the Stacks ecosystem? Be sure to start working on your own project and submit it to the [Code for STX](https://stacks.org/code-for-stx) program to earn STX every month just for working on your project. And, if you're feeling up to the challenge, apply to the Clarity Collective, an exclusive community of proven, committed Stacks builders all dedicated to becoming exceptional Stacks developers. + +Next up, dig into exactly what Stacks is and how it works 👇🏻 diff --git a/docs/learn/SUMMARY.md b/docs/learn/SUMMARY.md new file mode 100644 index 0000000000..e0214113b4 --- /dev/null +++ b/docs/learn/SUMMARY.md @@ -0,0 +1,53 @@ +# Table of contents + +* [Start Here](README.md) +* [Stacks 101](stacks-101/README.md) + * [What Is Stacks?](stacks-101/what-is-stacks.md) + * [The Bitcoin Connection](stacks-101/bitcoin-connection.md) + * [Proof of Transfer (PoX)](stacks-101/proof-of-transfer.md) + * [Stacks Among Other Layers](stacks-101/stacks-among-other-layers.md) + * [Financial Incentive And Security Budget](stacks-101/financial-incentive-and-security-budget.md) +* [Network Fundamentals](network-fundamentals/README.md) + * [Network Basics](network-fundamentals/network-basics.md) + * [Mainnet and Testnets](network-fundamentals/mainnet-and-testnets.md) + * [Accounts](network-fundamentals/accounts.md) + * [Authentication](network-fundamentals/authentication.md) + * [Bitcoin Name System](network-fundamentals/bitcoin-name-system.md) + * [SIPs](network-fundamentals/sips.md) + * [Technical Specifications](network-fundamentals/technical-specifications/README.md) + * [Audits](network-fundamentals/technical-specifications/audits.md) +* [Block Production](block-production/README.md) + * [Mining](block-production/mining.md) + * [Signing](block-production/signing.md) + * [Bitcoin Finality](block-production/bitcoin-finality.md) + * [Bitcoin Reorgs](block-production/bitcoin-reorgs.md) + * [Stacking](block-production/stacking.md) +* [Transactions](transactions/README.md) + * [How Transactions Work](transactions/how-transactions-work.md) + * [Post Conditions](transactions/post-conditions.md) +* [Clarity](clarity/README.md) + * [Overview](clarity/overview.md) + * [Decidability](clarity/decidability.md) +* [sBTC](sbtc/README.md) + * [Core Features](sbtc/core-features.md) + * [sBTC Operations](sbtc/sbtc-operations/README.md) + * [Deposit](sbtc/sbtc-operations/deposit.md) + * [Withdrawal](sbtc/sbtc-operations/withdrawal.md) + * [Deposit vs Withdrawal Times](sbtc/sbtc-operations/deposit-vs-withdrawal-times.md) + * [Emily API](sbtc/emily-api.md) + * [Peg Wallet UTXO](sbtc/peg-wallet-utxo.md) + * [Clarity Contracts](sbtc/clarity-contracts/README.md) + * [sBTC Signers](sbtc/clarity-contracts/sbtc-signers.md) + * [sBTC Token](sbtc/clarity-contracts/sbtc-token.md) + * [sBTC Registry](sbtc/clarity-contracts/sbtc-registry.md) + * [sBTC Withdrawal](sbtc/clarity-contracts/sbtc-withdrawal.md) + * [sBTC Deposit](sbtc/clarity-contracts/sbtc-deposit.md) + * [sbtc bootstrap signers](sbtc/clarity-contracts/sbtc-bootstrap-signers.md) + * [Auxiliary Features](sbtc/auxiliary-features/README.md) + * [Transaction Fee Sponsorship](sbtc/auxiliary-features/transaction-fee-sponsorship.md) + * [Signer Wallet Rotation](sbtc/auxiliary-features/signer-wallet-rotation.md) + * [Walkthroughs](sbtc/walkthroughs/README.md) + * [Signer Process Walkthrough](sbtc/walkthroughs/signer-process-walkthrough.md) + * [sBTC Transaction Walkthrough](sbtc/walkthroughs/sbtc-transaction-walkthrough.md) + * [sBTC FAQ](sbtc/sbtc-faq.md) + * [sBTC Audits](sbtc/sbtc-audits.md) diff --git a/docs/learn/block-production/README.md b/docs/learn/block-production/README.md new file mode 100644 index 0000000000..7c41fa0e17 --- /dev/null +++ b/docs/learn/block-production/README.md @@ -0,0 +1,35 @@ +# Block Production + +Block production is a key concept to understand how Stacks operates under the hood. This section walks through the three main actions that need to happen for the Stacks chain to operate. + +{% stepper %} +{% step %} +### Mining + +Miners are responsible for building and proposing new blocks on the Stacks chain. +{% endstep %} + +{% step %} +### Signing + +Signing is the process used to validate blocks and sign sBTC deposits and withdrawals. Stackers participate in signing once they meet stacking prerequisites. +{% endstep %} + +{% step %} +### Stacking + +Stacking is an action performed by stackers that is a necessary prerequisite to signing. It enables participation in validation and earning rewards. +{% endstep %} +{% endstepper %} + +There are two primary parties in Stacks block production: miners and stackers. Miners build and propose new blocks, while stackers validate those blocks and sign sBTC deposits and withdrawals. Stacking enables stackers to participate in signing. + +{% hint style="info" %} +For an in-depth technical overview of block production after the Nakamoto Upgrade, see SIP-021: + +https://github.com/stacksgov/sips/blob/feat/sip-021-nakamoto/sips/sip-021/sip-021-nakamoto.md +{% endhint %} + +Here's a diagram outlining the block production process under Nakamoto rules. The following docs dig into each part in detail. + +
diff --git a/docs/learn/block-production/bitcoin-finality.md b/docs/learn/block-production/bitcoin-finality.md new file mode 100644 index 0000000000..a86c441975 --- /dev/null +++ b/docs/learn/block-production/bitcoin-finality.md @@ -0,0 +1,46 @@ +# Bitcoin Finality + +The concept of 100% Bitcoin finality is crucial to the design of Stacks. This is what turns Stacks into a true Bitcoin L2 and allows it to leverage all of the security inherent in Bitcoin. + +Finality refers to the point at which transactions are irreversible. Once a blockchain reaches finality, it is nearly impossible to change the ledger's history without undertaking extraordinary measures that are often computationally and economically prohibitive. + +When we talk about Stacks blocks having 100% Bitcoin finality, we mean that they are as hard to reverse as Bitcoin transactions themselves. + +That's a bold claim, so how does Stacks accomplish that? + +As discussed above, miners are responsible for producing Stacks blocks in their tenure, which corresponds to a single Bitcoin block. As part of their block commit transaction, which is the transaction that previously committed the hash of the next Stacks block to the Bitcoin chain, miners will instead be required to add an indexed block hash. + +The indexed block hash is the hash of the first block produced by the last Stacks miner in their tenure. This is the SHA512/256 hash of both the consensus hash of all previously-accepted Bitcoin transactions that Stacks recognizes, as well as the hash of the block itself. + +This will anchor the Stacks chain history to Bitcoin up to the start of the previous miner's tenure, as well as all causally-dependent Bitcoin state that Stacks has processed. This ensures Bitcoin finality, resolves miner connectivity issues by putting fork prevention on stackers, and allows nodes with up-to-date copies of the Stacks chain state to identify which Stacks blocks are affected by a Bitcoin reorg and recover the affected Stacks transactions. + +This relationship between Stackers, miners, Bitcoin blocks, and Stacks blocks is what maintains Bitcoin finality while allowing miners to rapidly produce Stacks blocks. Bitcoin finality is achieved because at every Bitcoin block N + 1, the state of the Stacks chain as of the start of tenure N is written to Bitcoin. Even if at a future date all of the former Stackers’ signing keys were compromised, they would be unable to rewrite Stacks history for tenure N without rewriting Bitcoin history back to tenure N + 1. + +Because of this, Stacks transactions can be considered to have Bitcoin finality after the tenure they are a part of concludes, or Bitcoin block N + 1. As an example, if I initiate a Stacks transaction that gets confirmed by a Stacks miner, at the conclusion of that miner's tenure (the end of the current Bitcoin block) that transaction will be written to Bitcoin as part of the Stacks chain state and all future miners are required to build off of that chain tip, making reversing the transaction as difficult as reversing the corresponding Bitcoin transaction. + +{% hint style="info" %} +Key point: At every Bitcoin block N + 1 the state of the Stacks chain as of the start of tenure N is anchored to Bitcoin. This makes reversing Stacks history for tenure N as hard as rewriting Bitcoin history back to N + 1. +{% endhint %} + +## Nakamoto Transactions and Bitcoin Reorgs + +If Nakamoto transactions follow Bitcoin finality, what happens if Bitcoin forks? + +In order to answer this question, we need to distinguish between two types of Stacks transactions: Bitcoin-reliant and internal. + +{% hint style="info" %} +* **Bitcoin-reliant** transactions are transactions that read Bitcoin state. If Bitcoin forks, these transactions will change. For these, you cannot do better than following Bitcoin finality. For example, if you moved BTC from L1 to L2, you must wait for Bitcoin finality before your L2 BTC can be used (you don’t have any L2 BTC if the L1 transaction becomes unconfirmed due to a fork). +* **Internal** transactions don't rely on Bitcoin state, and thus won't change if Bitcoin forks. These can have faster confirmations because even if Bitcoin forks, signers can ensure they are re-processed in the same order. +{% endhint %} + +The key takeaway is this: + +Under Nakamoto Stacks, transactions won’t impactfully reorganize due to a Bitcoin fork. Not only is reorging relatively infrequent, but transactions on Stacks that got reorganized due to a Bitcoin fork behave just as reorganized Bitcoin transactions do. With some future analysis, transactions purely on the L2 chain may one day be entirely unaffected. + +
+ +Read more about Bitcoin reorg behavior + +If you are interested in learning more about how this works, see the [Bitcoin Reorgs](bitcoin-reorgs.md) page of the docs. + +
diff --git a/docs/learn/block-production/bitcoin-reorgs.md b/docs/learn/block-production/bitcoin-reorgs.md new file mode 100644 index 0000000000..86dfec31ec --- /dev/null +++ b/docs/learn/block-production/bitcoin-reorgs.md @@ -0,0 +1,33 @@ +# Bitcoin Reorgs + +Under Nakamoto Stacks transactions don’t impactfully reorganize due to a Bitcoin fork. Not only is reorging relatively infrequent, but transactions on Stacks that got reorganized due to a Bitcoin fork behave just as reorganized Bitcoin transactions do. With some future analysis, transactions purely on the L2 chain may one day be entirely unaffected. + +Understanding this concept fundamentally comes down to understanding finality on post-Nakamoto Stacks. + +{% hint style="info" %} +Under Nakamoto the Stacks chain won’t fork on its own. It is designed not to fork with only special exceptions, and it’s entirely infeasible for Stacks to fork on its own if even 31% of Stackers don’t want it to fork, and even then it would likely only happen within the span of a single tenure. + +The only case in which Stacks forks post-Nakamoto is if Bitcoin forks cause it to fork. +{% endhint %} + +Under Nakamoto, instead of winning the right to make a single block, miners win the right to make a ton of blocks, and during that time we say they’re under “tenure”. Every single Stacks block produced in a tenure requires at least 70% of Stackers to approve (sign) it for it to be included in the Stacks blockchain. The Stackers are watching the Bitcoin blockchain and will only sign blocks from the miner that won the latest sortition. + +Now, let’s imagine that Bitcoin reorganizes itself and the Stackers were watching a Bitcoin fork that is now sub-optimal. The Stackers would essentially go back in time to the latest common sortition between the fork that they were watching and the new best Bitcoin fork and start signing the blocks within the tenures from there. Note that 70% of the Stackers will be doing the same thing all at once, and the moment 70% agree to start signing from the latest tenure on the new Bitcoin fork there’s a new singularly optimal Stacks blockchain. + +So what happens to the transactions that were confirmed on the tenure that got reorganized? Nothing. Still in the mempool as if the reorganized tenure didn’t happen. For anything within the Stacks blockchain everything is fine. + +This is 1:1 with a Bitcoin fork reorganizing a Bitcoin transaction. You shouldn’t consider a transaction on Bitcoin final if it’s near the chain tip, and you shouldn’t consider a Stacks transaction final if it’s near the tenure tip. + +
+ +Replaying Transactions + +Since 70% of the signers have to sign any Stacks block included in the chain at least 70% of signers know the state of the chain before and after a Bitcoin fork causes a Stacks reorg. + +There’s a catch to this that makes enforcing it difficult: if a transaction were dependent on something on the Bitcoin blockchain that also got reorganized (a peg-in, for example), that transaction would now be invalid. Taint analysis is when you attempt to answer the questions “which transaction interacted with the now-orphaned Bitcoin blockchain in a way that makes them invalid (tainted) in the new chain” and then also “which transactions interacted with the now invalid (tainted) transaction such that they are now also invalid”. There’s a cascading effect, but enforcing any kind of replay requires that the Stackers and the Miners can identify which transactions can get replayed at all. + +Taint analysis, and subsequently replay enforcement, can be added in the future. + +For the first release, Nakamoto explicitly ties the Stacks blockchain to the Bitcoin blockchain such that there’s only one optimal Stacks fork tied to Bitcoin at any given point. This is completely 1:1 with the Bitcoin Blockchain behavior, but on the tenure scale. + +
diff --git a/docs/learn/block-production/mining.md b/docs/learn/block-production/mining.md new file mode 100644 index 0000000000..82e9086239 --- /dev/null +++ b/docs/learn/block-production/mining.md @@ -0,0 +1,152 @@ +# Mining + +

source: Hiro blog

+ +{% hint style="info" %} +This is conceptual guide that covers how mining works. For practical steps on how to setup your own miner please refer to the guides to running a miner on [mainnet](https://app.gitbook.com/s/4cpTb2lbw0LAOuMHrvhA/run-a-miner/mine-mainnet-stacks-tokens) and [testnet](https://app.gitbook.com/s/4cpTb2lbw0LAOuMHrvhA/run-a-miner/mine-testnet-stacks-tokens). +{% endhint %} + +### Miner Tenures + +In previous version of Stacks (before the Nakamoto Upgrade), Stacks miners would mine new Stacks blocks at a one-to-one cadence with Bitcoin blocks. + +After Nakamoto, this is no longer the case. Under Nakamoto rules, miners are instead selected for a tenure that corresponds to a Bitcoin block. During this tenure, miners build and propose several Stacks blocks (roughly every 10 seconds) and stackers will approve and append them (next section). + +To be considered for a tenure, a miner must have a block commit included in a Bitcoin block. If a miner wishes to update their commitment after submission, they may use Bitcoin Replace-By-Fee. + +### Coinbase rewards + +Miners receive coinbase rewards for tenures they win. + +The reward amounts are: + +* 1000 STX per tenure are released in the first 4 years of mining +* 500 STX per tenure are released during the following 4 years +* 250 STX per tenure are released during the following 4 years +* 125 STX per tenure are released from then on indefinitely. + +These "halvings" are synchronized with Bitcoin halvings. + +
+ +### Transaction fees + +Miners receive Stacks fees for transactions mined in any block they produce. + +### Reward maturity + +Block rewards and transaction fees take 100 blocks on the Bitcoin blockchain to mature. After successfully mining a block your rewards appear in your Stacks account after \~24 hours. + +### Mining with proof-of-transfer + +Miners commit Bitcoin to **two** addresses in every leader block commit. The amount committed to each address must be the same. The addresses are chosen from the current reward set of stacking participants. Addresses are chosen using a verifiable-random-function, and determining the correct two addresses for a given block requires monitoring the Stacks chain. + +For more detailed information on this process, read [SIP-007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md), which describes proof of transfer in detail. + +
+ +PoX mining is a modification of Proof-of-Burn (PoB) mining, where instead of sending the committed Bitcoin to a burn address, it's transferred to eligible STX holders that participate in the stacking protocol. + +{% hint style="info" %} +A PoX miner can only receive newly minted STX tokens when they transfer Bitcoin to eligible owners of STX tokens +{% endhint %} + +
+ +Miners run Stacks nodes with mining enabled to participate in the PoX mechanism. The node implements the PoX mechanism, which ensures proper handling and incentives through four key phases: + +* Registration: miners register for a future election by sending consensus data to the network +* Commitment: registered miners transfer Bitcoin to participate in the election. Committed BTC are sent to a set participating STX token holders +* Election: a verifiable random function chooses one miner for a new tenure to write blocks on the Stacks blockchain +* Assembly: the elected miner writes the new blocks by pulling transactions from the mempool and collects rewards in form of new STX tokens + +### Probability to mine next block + +The miner who is selected to mine the next block is chosen depending on the amount of BTC the miners sent, that is, transferred or burnt. + +The probability for a miner to mine the next block is determined using a variation of the Assumed Total Commitment with Carryforward (ATC-C) MEV mitigation strategy described in [this document](https://github.com/stacksgov/sips/blob/feat/sip-021-nakamoto/sips/sip-021/MEV-Report.pdf) to allocate block rewards to miners. The probability a miner will win the sortition and be granted the current tenure will be based on a function that accounts for the total block commit spend on the blocks leading up to the current sortition. + +You can read more about this in the [MEV section of SIP-021](https://github.com/stacksgov/sips/blob/feat/sip-021-nakamoto/sips/sip-021/sip-021-nakamoto.md#block-reward-distribution-and-mev). + +While there is no minimum BTC commitment enforced by the protocol, in practice, there's a floor constrained by [dust](https://unchained-capital.com/blog/dust-thermodynamics/): basically, if the fees for a transaction exceed the value of the spent output, it's considered dust. How dust is [calculated](https://github.com/bitcoin/bitcoin/blob/master/src/policy/policy.cpp#L14) depends on a number of factors, we've found 5,500 satoshis to be good lower bound per [output](https://learnmeabitcoin.com/technical/output). Bitcoin transactions from Stacks miners contain two outputs (for Proof-of-Transfer), so a commitment of at least 11,000 satoshis / block is recommended. + +To calculate the amount of BTC to send miners should: + +* Guess the price BTC/STX for the next day (100 blocks later) +* Guess the total amount of BTCs committed by all miners + +Stackers are in charge of both validating and appending new blocks and conducting miner tenure changes. The next section will explain how that works, and then we'll see how this process results in Bitcoin finality. + +### Stacks mining in practice + +If you take a look at [SIgnal21's mining dashboard](https://app.signal21.io/stacks/mining), you can view some interesting data about mining on the Stacks network, including BTC spent per block, STX earned per block, the total number of miners over the course of the chain's history, and the number of miners for any given block. + +Many people notice the seemingly small number of miners on Stacks. Without context, this can sometimes raise eyebrows. Let's dig into how mining works on Stacks so we can understand why this isn't an issue for decentralization. + +Stacks miners function similarly to sequencers in L2 systems in that they are only responsible for constructing and proposing new blocks, not appending them to the chain. But unlike most Ethereum L2s that operate with just a single centralized sequencer, Stacks consistently has at least 4-5 miners with open membership allowing anyone to join. + +It's important to note that there are two primary parties involved in the block production process on Stacks: miners and stackers. + +These two roles serve complementary relationships in the [block production process](file:///), and stackers drastically reduce any potential destructive power miners have over the chain. + +Miners cannot reorganize the chain. In the worst case, all miners can do is omit (some kinds of) transactions, and all that is required to address this is to run your own miner. + +Furthermore, more miners on the network would mean fewer BTC rewards for Stackers, as miners would have to spend more of their funds on Bitcoin L1 fees rather than sending it to the Stackers. + +{% hint style="info" %} +**Wouldn't more miners mean more competition, meaning more rewards?** + +The reason more miners means fewer rewards is because miners act economically rationally, and they don't have an unlimited amount of BTC to work with. + +Miners are paying their PoX commitments plus their Bitcoin fees for a chance to win the coinbase (1,000 STX) plus fees for a tenure. If there are more miners, they will each pay less, because they will have a lower chance of winning. They can't pay ever-increasing amounts of BTC because at some point they will never be profitable, so there is a limit to how much BTC they can spend in order to try and win a tenure. + +As they pay less, the Bitcoin fee becomes a more significant portion of their expenses, and that also decreases their odds of winning the tenure. + +Here's a concrete example: + +Let's say Stacks is trading at 1,000 Sats per STX. + +The total spend from all miners, if everyone is acting logically and we ignore Stacks fees, would be less than 1,000,000 Sats (1,000 STX coinbase \* 1000 Sats/STX). + +If that is from 5 miners, then it could be 10,000 Sats (2,000 Sats for each transaction) going to Bitcoin fees and 990,000 Sats going to PoX. + +If there are 100 miners, then it would be 200,000 Sats going to Bitcoin fees, and 800,000 Sats going to PoX. +{% endhint %} + +This creates a natural economic equilibrium where: + +{% stepper %} +{% step %} +### Enough miners participate to ensure blocks are produced reliably + +Content as above describing reliability. +{% endstep %} + +{% step %} +### Stackers receive optimal BTC rewards + +Content as above describing rewards optimization. +{% endstep %} + +{% step %} +### The network maintains censorship resistance without unnecessary mining competition + +Content as above describing censorship resistance. +{% endstep %} +{% endstepper %} + +This design is intentional - by having stackers as complimentary security guarantors who receive BTC rewards via PoX, Stacks achieves security without requiring an excessive number of miners competing solely to win block production rights. + +Unlike other chains where miners alone determine the canonical chain, Stacks' two-party system provides stronger guarantees: + +* Miners cannot force invalid transactions or blocks (stackers won't sign them, and even if they did, the nodes would not accept them) +* No miner can unilaterally reorg the chain (stackers control chain finality) +* The 70% stacker threshold signature requirement ensures broad consensus before blocks are accepted + +This separation of concerns between miners and stackers is what makes Stacks uniquely secure despite having a small number of miners. + +### What About Microblocks? + +Microblocks are a legacy feature of the previous version of Stacks that no longer exist. They were originally created as a way to improve transaction throughput, but without the functionality of Nakamoto, they never worked in practice. + +Instead of microblocks, Nakamoto instead utilizes a block production structure that creates Stacks blocks at a rapid cadence as described here. diff --git a/docs/learn/block-production/signing.md b/docs/learn/block-production/signing.md new file mode 100644 index 0000000000..d92f6628b3 --- /dev/null +++ b/docs/learn/block-production/signing.md @@ -0,0 +1,81 @@ +# Signing + +Stackers play an essential role in the Nakamoto system that had previously been the responsibility of miners. Before, miners both decided the contents of blocks, and decided whether or not to include them in the chain (i.e. by deciding whether or not to confirm them). In this system each actor has the following responsibilities necessary to make the system function reliably without forks: + +* **Miners** decide the contents of blocks. +* **Stackers** decide whether or not the block is included in the chain. + +The bulk of the complexity of the Nakamoto changes is in separating these two concerns while ensuring that both mining and Stacking remain open-membership processes. **Crucially, anyone can become a miner and anyone can become a Stacker, just as before.** The most substantial changes are in getting miners and Stackers to work together in their new roles to achieve this proposal's goals. + +The key idea is that Stackers are required to acknowledge and validate a miner's block before it can be appended to the chain. To do so, Stackers must first agree on the canonical chain tip, and then apply (and roll back) the block on this chain tip to determine its validity. Once Stackers agree that the block is both canonical and valid, they collectively sign it and replicate it to the rest of the Stacks peer network. Only at this point do nodes append the block to their chain histories. + +This new behavior prevents forks from arising. If a miner builds a block atop a stale tip, Stackers will refuse to sign the block. If Stackers cannot agree on the canonical Stacks tip, then no block will be appended in the first place. While this behavior creates a new failure mode for Stacks -- namely, the chain can halt indefinitely if Stackers cannot agree on the chain tip -- this is mitigated by having a large and diverse body of Stackers such that enough of them are online at all times to meet quorum and incentivizing them via PoX rewards to act as such. + +### Stacker Signing + +{% hint style="info" %} +You can view a list of all of the [active signers](https://explorer.hiro.so/signers?chain=mainnet) on Hiro's block explorer. +{% endhint %} + +We'll cover how stacking works in the Stacking section and the sBTC signing in the sBTC section; here we'll cover the signing process as it relates to Stacks block production. + +The means by which Stackers agree on the canonical chain tip and agree to append blocks is tied to PoX. In each reward cycle, a Stacker clinches one or more reward slots; there are at most 4,000 reward slots per reward cycle. Stackers vote to accept blocks by producing a weighted threshold signature over the block. The signature must represent a substantial fraction of the total STX locked in PoX (the threshold), and each Stacker's share of the signature (its weight) is proportional to the fraction of locked STX it owns. + +The weighted threshold signature is a Schnorr signature generated through a variation of the [FROST protocol](https://eprint.iacr.org/2020/852.pdf). Each Stacker generates a signing key pair, and they collectively generate an aggregate public key for nodes to use to verify signatures computed through a distributed signing protocol. This signing protocol allocates shares of the associated aggregate private key to Stackers proportional to the number of reward slots they clinch. No Stacker learns the aggregate private key; Stackers instead compute shares of the private key and use them to compute shares of a signature, which can be combined into a single Schnorr signature. + +When a miner produces a block, Stackers execute a distributed signing protocol to collectively generate a single Schnorr signature for the block. Crucially, the signing protocol will succeed only if at least X% of the reward slots are accounted for in the aggregate signature. Nakamoto is currently set to use a 70% signing threshold -- at least 70% of the reward slots (by proxy, 70% of the stacked STX) must sign a block in order to append it to the Stacks blockchain. + +Nakamoto uses the [WSTS protocol with the FIRE extension](https://trust-machines.github.io/wsts/wsts.pdf), which admits a distributed key generation and signature generation algorithm pair whose CPU and network bandwidth complexity grows with the number of distinct Stackers. The FIRE extension enables WSTS to tolerate byzantine Stackers. + +Here is a diagram outlining the relationship between signing and stacking. + +
+ +### Validating and Appending New Blocks + +When miners are selected for a new tenure, they begin building new blocks from transactions in the mempool. They then send those blocks to stackers for approval. Stackers must approve the blocks with a quorum of at least 70% for them to be appended to the chain. + +Stackers will approve a block based on several properties: + +* The block is well-formed + * It has the correct version and mainnet/testnet flag + * Its header contains the right number of Stacks blocks preceding this one. + * Its header contains the correct total Bitcoin spent in the sortition that elected the current tenure. + * Its header contains the same Bitcoin block hash as the Bitcoin block that contains its tenure's block-commit transaction\* + * Its header contains the correct parent block ID of the immediate parent of this block.\* + * The transaction Merkle tree root is consistent with the transactions + * The state root hash matches the MARF tip root hash once all transactions are applied + * The block header has a valid ECDSA signature from the miner. + * The block header has a valid WSTS Schnorr signature from the set of Stackers. +* All Bitcoin transactions since the last valid sortition up to (but not including) this tenure's block-commit’s Bitcoin block have been applied to the Stacks chain state\* +* In the case of a tenure start block: + * The first transaction is the `TenureChange` transaction. + * The first transaction after the `TenureChange` transaction is a `Coinbase`. + +The properties marked with \* are collectively how Stacks ensures Bitcoin finality. By adhering to these properties, it ensures that miners are only able to append blocks if they build atop the correct chain tip, which also anchors the history to Bitcoin. + +Stackers, by validating these rules, ensure Bitcoin finality. We'll talk about this more in the next section. + +### Conducting Miner Tenure Changes + +The other primary signing responsibility in block production involves conducting tenure change transactions. As discussed in the mining section, miners will submit a `block-commit` transaction on the Bitcoin chain to initiate mining. If they are selected, stackers will detect that and create a `tenure-change` transaction. + +This tenure change transaction includes: + +| Name | Description | Representation | +| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| tenure consensus hash | Consensus hash of this tenure. Corresponds to the sortition in which the miner of this block was chosen. It may be the case that this miner's tenure gets extended across subsequent sortitions; if this happens, then this `consensus hash` value remains the same as the sortition in which the winning block-commit was mined. | 20 bytes | +| previous tenure consensus hash | Consensus hash of the previous tenure. Corresponds to the sortition of the previous winning block-commit. | 20 bytes | +| burn view consensus hash | Current consensus hash on the underlying burnchain. Corresponds to the last-seen sortition. | 20 bytes | +| previous tenure end | The index block hash of the last Stacks block from the previous tenure. | 32 bytes | +| previous tenure blocks | The number of blocks produced since the last sortition-linked tenure. | 4 bytes, big-endian | +| cause |

A flag to indicate the cause of this tenure change
- 0x00 indicates that a sortition occurred, and a new miner should begin producing blocks.
- 0x01 indicates that the current miner should continue producing blocks. The current miner’s tenure execution budget is reset upon processing this transaction.

| 1 byte | +| pubkey hash | The ECDSA public key hash of the current tenure. | 20 bytes | + +This tenure change transaction is then sent to the newly elected miner and they must include it as the first transaction in their first block, otherwise stackers will not approve it. + +This process is then repeated over and over as new miners are elected for tenures. + +Be sure to take a look at [SIP-021](https://github.com/stacksgov/sips/blob/feat/sip-021-nakamoto/sips/sip-021/sip-021-nakamoto.md) to get a detailed description of exactly what happens under the hood during these processes. + +Next up, let's dig a little deeper into this idea of Bitcoin finality and how the Stacks block production mechanism achieves it. diff --git a/docs/learn/block-production/stacking.md b/docs/learn/block-production/stacking.md new file mode 100644 index 0000000000..601afc88ea --- /dev/null +++ b/docs/learn/block-production/stacking.md @@ -0,0 +1,197 @@ +# Stacking + +### Introduction + +Stacking rewards Stacks (STX) token holders with bitcoin for providing a valuable service to the network by locking up their tokens for a certain time and participating as consensus-critical signers. If you aren't familiar with the concept of signers in Stacks, be sure to check out the [Signing section](https://app.gitbook.com/o/hoh4mQXTl8NvI3cETroY/s/H74xqoobupBWwBsVMJhK/block-production/signing). This document is a conceptual overview of stacking and how it works. + +`pox-4.clar` is the stacking contract. If you are interested in experimenting with proof of transfer use cases including state changes, solo stacking, and pool stacking, all the functions you’ll need can be found at the deployed contract: + +* Testnet: https://explorer.hiro.so/txid/0xfba7f786fae1953fa56f4e56aeac053575fd48bf72360523366d739e96613da3?chain=testnet +* Mainnet: https://explorer.hiro.so/txid/0xc6d6e6ec82cabb2d7a9f4b85fcc298778d01186cabaee01685537aca390cdb46?chain=mainnet + +### Stacking vs Staking + +While stacking on the Stacks network can be conceptually similar to staking, Stacks is not a PoS network and there are a couple key differences. + +There are two primary differences between stacking in Stacks and staking in PoS networks. + +#### Yield generated in burnchain token + +In staking, users lock one token and earn their yield in the same token. In stacking, users lock one token (STX) and earn a yield in the "burnchain" token (BTC), rather than the same token that was locked. In PoX, the yield comes from a finite, external source (Bitcoin deposits from Stacks miners). In PoS, the yield comes from the currency's issuance schedule itself. + +How are these issuance rates set? In Ethereum, issuance rates are determined by network usage. Ethereum's goal is to create a deflationary money supply, so the issuance rate is determined depending on the usage of the network. In order for an Ethereum transaction to be considered valid, it must include a base fee that is burned during transaction execution. The [issuance rate is algorithmically determined](https://ethereum.org/en/roadmap/merge/issuance/#post-merge) block-by-block depending on how much ETH is being burned by these base fees plus normal gas fees. + +Stacking doesn't generate yield in the same token and therefore doesn't need to issue new STX for stacking rewards. Stacking yield requires an input of an external token (BTC). Stacks does have an issuance rate and does generate new STX tokens, but that process is separate from stacking and the stacking yield mechanism. + +#### No slashing + +Although stackers do fulfill a consensus-critical role in Stacks by serving as signers, there is no concept of slashing in PoX (Proof of Transfer). + +Rather, if stackers do not perform their duties as signers, they simply cannot unlock their STX tokens and will not receive their BTC rewards. + +Stacking is a built-in action, required by the "proof-of-transfer" (PoX) mechanism. The PoX mechanism is executed by every miner on the Stacks network. + +{% hint style="info" %} +Stacking functionality is implemented as a smart contract, using Clarity. Read more about [the contract](https://app.gitbook.com/s/GVj1Z9vMuEOMe7oH7Wnq/clarity/example-contracts/stacking). +{% endhint %} + +### Locking and Unlocking STX + +When STX tokens are "locked", no transfer of STX tokens occurs. Locking STX tokens is non-custodial, and STX tokens remain in your wallet. When you initiate a stacking transaction those tokens are locked and unspendable at the protocol level, but they do not leave the stacker's wallet. + +At the end of the lock period, they will be automatically unlocked (spendable at the protocol level). This occurs implicitly; there is no direct transaction that unlocks them. + +### Stacking flow + +The Stacking mechanism can be presented as a flow of actions: + +
+ +{% stepper %} +{% step %} +### Make API calls to get details about the upcoming reward cycle + +Query the network to discover the upcoming cycle parameters and timing. +{% endstep %} + +{% step %} +### Confirm eligibility for a specific Stacks account + +Verify the account meets the minimum requirements and is eligible to participate. +{% endstep %} + +{% step %} +### Confirm the BTC reward address and lockup duration + +Specify the Bitcoin address to receive payouts and input the desired lockup period. +{% endstep %} + +{% step %} +### Broadcast the stacking transaction to lock STX + +The transaction is broadcast and the STX tokens are locked. This must happen before the prepare phase of the next reward cycle (the last 100 Bitcoin blocks of the ongoing reward phase). +{% endstep %} + +{% step %} +### Reward cycles execute and BTC rewards are sent + +The stacking mechanism executes reward cycles and sends out rewards to the configured BTC reward address. +{% endstep %} + +{% step %} +### Monitor unlocking timing and rewards during lockup + +During the lockup period, you can obtain details about unlocking timing, expected rewards, and more. +{% endstep %} + +{% step %} +### Tokens are released after the lockup period + +Once the lockup period has passed, the tokens become spendable again. +{% endstep %} + +{% step %} +### Display reward history + +Show historical details like earnings for previous reward cycles. +{% endstep %} +{% endstepper %} + +{% hint style="info" %} +Keep in mind that the target duration for a reward cycle is \~2 weeks. This duration is based on the target block time of the Bitcoin network (10 minutes) and can be higher at times due to [confirmation time variances](https://www.blockchain.com/charts/median-confirmation-time) of the Bitcoin network. +{% endhint %} + +### Stacking delegation flow + +There are two main ways you can stack: solo stacking and delegated stacking. + +{% stepper %} +{% step %} +### Solo stacking + +Solo stacking follows the general stacking flow. You stack your own STX tokens and run your own signer. To operate as a solo stacker, you must have a minimum amount of STX tokens. This minimum is dynamic and can be found by viewing the [pox endpoint of the API](https://api.testnet.hiro.so/v2/pox) in the `min_threshold_ustx` field. +{% endstep %} + +{% step %} +### Delegated stacking + +
+ +Delegated stacking differs: + +* Before stacking on behalf of a token holder, the delegator must be granted permission by the account owner. Permission is restricted to a maximum amount the delegator may stack; the maximum can be set higher than available funds. An account can be associated with only one delegator. +* The account sets the delegation relationship. They can optionally restrict the Bitcoin reward address that must be used for payouts and specify an expiration burn block height to limit the delegation duration. +* Delegators lock STX from different accounts ("pooling phase") until they reach the minimum required to participate in stacking. +* Once the delegator locks enough STX, they can finalize and commit participation in the next reward cycle. +* Some delegation relationships may allow the STX holder to receive payouts directly from the miner. +* Delegation can terminate automatically based on expiration rules or by actively revoking delegation rights. +{% endstep %} +{% endstepper %} + +### Token holder eligibility + +Stacks (STX) token holders don't automatically receive stacking rewards. To participate, they must: + +* Commit to participation before a reward cycle begins +* Commit at least the minimum amount of STX tokens to secure a reward slot, or pool with others to reach the minimum +* Lock up STX tokens for a specified period +* Provide a supported Bitcoin address to receive rewards +* Maintain their signer software (if they operate a signer) + +
+ +Token holders have a variety of providers and tools to support their participation in stacking. The Stacks website contains a [list of pools and stacking options](https://www.stacks.co/learn/stacking#startstacking). + +### Stacking in the PoX consensus algorithm + +Stacking is a built-in capability of PoX and occurs through a set of actions on the Stacks blockchain. The [full proof-of-transfer implementation details](https://github.com/stacks-network/stacks-blockchain/blob/develop/sip/sip-007-stacking-consensus.md) are in SIP-007. Below is a summary of the most relevant actions of the algorithm. + +{% hint style="info" %} +Note that SIP-007 describes stacking before Nakamoto. While much of the functionality remains the same, stackers now have the additional responsibility of operating as signers as outlined in [SIP-021](https://github.com/stacksgov/sips/blob/feat/sip-021-nakamoto/sips/sip-021/sip-021-nakamoto.md). +{% endhint %} + +
+ +Stacking happens in reward cycles of 2100 Bitcoin blocks (roughly two weeks). Reward cycles are split into two phases: the prepare phase and the reward phase. + +* The prepare phase lasts 100 Bitcoin blocks and is where the new stackers for the upcoming reward phase are selected by the PoX anchor block (see SIP-007 for details). +* Because Stacks does not fork after the Nakamoto upgrade, the PoX anchor block is always known 100 Bitcoin blocks before the start of the next reward cycle. It is the last tenure-start block that precedes the prepare phase. +* The PoX anchor block identifies the next stackers. They have 100 Bitcoin blocks to prepare for signing Stacks blocks, including completing a Distributed Key Generation round for signing blocks. +* The PoX contract requires stackers to register their block-signing keys when they stack or delegate-stack STX, so the entire network can validate signatures on blocks. + +This process is handled by [running a signer](https://app.gitbook.com/s/4cpTb2lbw0LAOuMHrvhA/run-a-signer) and then subsequently conducting stacking operations as that signer. + +### Stacking and Signing + +Stacking and signing are distinct actions, but both are necessary. Signers must stack their STX tokens, and you cannot stack STX without associated signing information. The nuance depends on solo vs delegated stacking. + +### Solo Stacking + +If you are solo stacking, you have two options for signing. + +#### Run your own signer + +You can run your own signer by following the How to Run a Signer guide. This requires technical knowledge and resources for running a machine. See the guide for details. + +#### Work with another signer + +If you don't want to run your own signer, you can collaborate with another signer and include their signature in your stacking transactions. Details on how to do this are in the [Stack STX](https://app.gitbook.com/s/4cpTb2lbw0LAOuMHrvhA/stacking-stx) guide. + +### Delegated Stacking + +If you delegate your STX to a pool operator, you do not need to run a signer. The pool operator conducts the actual stacking transaction and is responsible for running the signer. + +If you are a pool operator, see the [operate-a-pool guide](https://app.gitbook.com/s/4cpTb2lbw0LAOuMHrvhA/stacking-stx/operate-a-stacking-pool). + +### How and Where to Stack + +Options for stacking include solo stacking, participating in a pool, using an exchange, and liquid stacking. The Stacks website has a [stacking page](https://www.stacks.co/learn/stacking) describing these options. + +For detailed instructions on how to stack, see the [Stack STX guides](https://app.gitbook.com/s/4cpTb2lbw0LAOuMHrvhA/stacking-stx). + +Tools and explorers for stacking data and statistics: + +* https://app.signal21.io/ +* https://www.stacking-tracker.com/ +* https://www.stakingrewards.com/calculator?asset=stacks +* https://stacking.tools/ diff --git a/docs/learn/clarity/README.md b/docs/learn/clarity/README.md new file mode 100644 index 0000000000..06cd4c40a5 --- /dev/null +++ b/docs/learn/clarity/README.md @@ -0,0 +1,57 @@ +# Clarity + +
+ +Clarity is the smart contract language that Stacks uses. It has been built from the ground up to make it easier for developers to write safe, secure smart contracts. Clarity has several unique features that make it an ideal choice for writing smart contracts. We'll go over an overview of Clarity here, and highly recommend checking out the [Clarity Crash Course](https://app.gitbook.com/u/ZrQItu6D9bMKmf1HfsLTnGc05WZ2) guide to dig in and get started learning Clarity. + +Clarity is a **decidable** smart contract language that optimizes for predictability and security, designed for the Stacks blockchain. Smart contracts allow developers to encode essential business logic on a blockchain. + +The design decisions behind Clarity were based heavily on taking lessons learned in common Solidity exploits and creating a language that has been purpose-built for safety and security in mind. + +These docs serve primarily as a reference for the functions and keywords that you can use in Clarity. + +In order to learn Clarity, we recommend diving into the [Clarity of Mind](https://book.clarity-lang.org/), an online book to teach you everything you need to know to build robust smart contracts, or joining a [Clarity Camp](https://clarity-lang.org/universe#camp), the cohort-based immersive Clarity experience. + +### What makes Clarity different + +The following section is an excerpt from the book, [Clarity of Mind](https://book.clarity-lang.org/ch00-00-introduction.html): + +The number of smart contract languages grows by the year. Choosing a first language can be challenging, especially for a beginner. The choice is largely dictated by the ecosystem you are interested in, although some languages are applicable to more than just one platform. Each language has its own upsides and downsides and it is out of the scope of this book to look at all of them. Instead, we will focus on what sets Clarity apart and why it is a prime choice if you require the utmost security and transparency. + +One of the core precepts of Clarity is that it is secure by design. The design process was guided by examining common pitfalls, mistakes, and vulnerabilities in the field of smart contract engineering as a whole. There are countless real world examples of where developer failure led to the loss or theft of vast amounts of tokens. To name two big ones: an issue that has become known as the Parity bug led to the irreparable loss of millions of dollars worth of Ethereum. Second, the hacking of The DAO (a "Decentralized Autonomous Organization") caused financial damage so great that the Ethereum Foundation decided to issue a contentious hard fork that undid the theft. These and many other mistakes could have been prevented in the design of the language itself. + +#### Clarity is interpreted, not compiled + +Clarity code is interpreted and committed to the chain exactly as written. Solidity and other languages are compiled to byte-code before it is submitted to the chain. The danger of compiled smart contract languages is two-fold: first, a compiler adds a layer of complexity. A bug in the compiler may lead to different byte-code than was intended and thus carries the risk of introducing a vulnerability. Second, byte-code is not human-readable, which makes it very hard to verify what the smart contract is actually doing. Ask yourself, would you sign a contract you cannot read? If your answer is no, then why should it be any different for smart contracts? With Clarity, what you see is what you get. + +#### Clarity is decidable + +A decidable language has the property that from the code itself, you can know with certainty what the program will do. This avoids issues like the halting problem. With Clarity you know for sure that given any input, the program will halt in a finite number of steps. In simple terms: it is guaranteed that program execution will end. Decidability also allows for complete static analysis of the call graph so you get an accurate picture of the exact cost before execution. There is no way for a Clarity call to "run out of gas" in the middle of the call. We explore this idea more, along with a discussion on Turing completeness, in the security deep dive on decidability. + +#### Clarity does not permit reentrancy + +Reentrancy is a situation where one smart contract calls into another, which then calls back into the first contract—the call "re-enters" the same logic. It may allow an attacker to trigger multiple token withdrawals before the contract has had a chance to update its internal balance sheet. Clarity's design considers reentrancy an anti-feature and disallows it on the language level. + +#### Clarity guards against overflow and underflows + +Overflows and underflows happen when a calculation results in a number that is either too large or too small to be stored, respectively. These events throw smart contracts into disarray and may intentionally be triggered in poorly written contracts by attackers. Usually this leads to a situation where the contract is either frozen or drained of tokens. Overflows and underflows of any kind automatically cause a transaction to be aborted in Clarity. + +#### Support for custom tokens is built-in + +Issuance of custom fungible and non-fungible tokens is a popular use-case for smart contracts. Custom token features are built into the Clarity language. Developers do not need to worry about creating an internal balance sheet, managing supply, and emitting token events. Creating custom tokens is covered in depth in later chapters. + +#### On Stacks, transactions are secured by post conditions + +In order to further safeguard user tokens, post conditions can be attached to transactions to assert the chain state has changed in a certain way once the transaction has completed. For example, a user calling into a smart contract may attach a post condition that states that after the call completes, exactly 500 STX should have been transferred from one address to another. If the post condition check fails, then the entire transaction is reverted. Since custom token support is built right into Clarity, post conditions can also be used to guard any other token in the same way. + +#### Returned responses cannot be left unchecked + +Public contract calls must return a so-called response that indicates success or failure. Any contract that calls another contract is required to properly handle the response. Clarity contracts that fail to do so are invalid and cannot be deployed on the network. Other languages like Solidity permit the use of low level calls without requiring the return value to be checked. For example, a token transfer can fail silently if the developer forgets to check the result. In Clarity it is not possible to ignore errors, although that obviously does prevent buggy error handling on behalf of the developer. Responses and error handling are covered extensively in the chapters on functions and control flow. + +#### Composition over inheritance + +Clarity adopts a composition over inheritance. It means that Clarity smart contracts do not inherit from one another like you see in languages like Solidity. Developers instead define traits which are then implemented by different smart contracts. It allows contracts to conform to different interfaces with greater flexibility. There is no need to worry about complex class trees and contracts with implicit inherited behavior. + +#### Access to the base chain: Bitcoin + +Clarity smart contracts can read the state of the Bitcoin base chain. It means you can use Bitcoin transactions as a trigger in your smart contracts! Clarity also features a number of built-in functions to verify secp256k1 signatures and recover keys. diff --git a/docs/learn/clarity/decidability.md b/docs/learn/clarity/decidability.md new file mode 100644 index 0000000000..427424c44e --- /dev/null +++ b/docs/learn/clarity/decidability.md @@ -0,0 +1,173 @@ +# Decidability + +### What does it mean for a language to be Non-Turing Complete or Decidable? + +Non-Turing complete and decidable are two terms you will often hear about the security advantages of Clarity, but what do they mean? + +While related, they are not quite interchangeable, since there are a few differences. + +#### Non-Turing Complete + +A system or language is non-Turing complete if it cannot simulate a Turing machine, which is an abstract model of computation. Non-Turing complete systems have limited computational power compared to Turing complete systems. A Turing-complete system or language can simulate any Turing machine. Examples of non-Turing complete systems include finite state machines and some domain-specific languages (like Clarity). + +Non-Turing complete languages typically cannot express all possible algorithms. Specifically, some problems whose solutions require unbounded loops or recursion cannot be expressed using non-Turing complete languages. This last property is especially important in the context of Clarity, as it makes it so that features like unbounded loops and reentrancy are disallowed at a language level. + +#### Decidable + +A problem is decidable if there exists an algorithm that can always determine whether a given input has a particular property or not in a finite amount of time. In other words, a decidable problem can be solved by a Turing machine that is guaranteed to halt for all input instances. Decidability is a property of problems, whereas Turing completeness is a property of languages or computational systems. + +The fact that Clarity is decidable means that developers (and tooling) can more easily reason about and predict with certainty the behavior of Clarity contracts, regardless of the input. + +### Mindset of a Smart Contract Developer + +Before we dive into specifics, let's first set the context and viewpoint we should hold as smart contract developers who want to write secure code. + +As you explore further into the security properties of Solidity and Clarity, you'll see that there are always mitigation steps that _can_ be taken by developers to help address some of these security issues. + +The main issue, with this line of thinking, is it increases the odds of human error in smart contract security. If we can preserve functionality while mitigating the chance of human error as much as possible, we should do so. + +### Should smart contracts be Turing complete? + +We will discover new applications for smart contracts. These applications will go beyond current smart contracts, traditional contracts, and may even open new economic opportunities. Given these possibilities, how should we build our smart contracts? What characteristics should our smart contract languages have? + +It is good practice to separate data from programs. Should smart contracts be data, or programs, or something in between? If smart contracts are data, then should the programs that execute them be Turing complete or perhaps less powerful? If smart contracts are programs, then what language should smart contracts be written in? What characteristics should this programming language have? + +The Church–Turing thesis is the hypothesis that all formal notions of computation are captured by Turing machines or modern computers. A programming language is Turing complete if it captures all formal notions of computation. Many programming languages are Turing complete. For example, Python, C++, Rust, Java, Lisp, and Solidity are all Turing complete. + +Consider a program and its input. In the worst case, determining this program’s output is impossible. Validating a program, on a particular input, is done by generating a proof-of-correctness. + +Proofs-of-correctness are logical proofs that can be mechanically validated. Finding proofs-of-correctness for programs and their input is undecidable. Kurt Gödel showed there are undecidable logical statements. + +This indicates all programs in Turing complete languages cannot be validated in the worst case. Thus, Turing complete smart contract languages must allow contracts that cannot be validated. + +Alonzo Church and Alan Turing showed there are problems that are uncomputable. Uncomputable problems cannot be solved by any Turing machine. Hence, assuming the Church–Turing thesis, these uncomputable problems cannot be solved by any computer. + +We'll explore this idea further later in this section. + +Turing complete languages are very expressive. In fact, assuming the Church–Turing thesis, Turing complete languages are as expressive as possible in some sense. + +Is there a trade-off? What types of problems can occur with uncomputable problems and programs whose validity may be undecidable? + +As smart contracts subsume parts of contract law, consider the large body of laws and regulations for tax law. + +For instance, US tax law and regulations take up several million words. International tax law and regulations pushes these numbers much higher. + +Are these laws and regulations programs or are they data? If tax law were to be written in a Turing complete language, then the law may codify uncomputable problems. It is an accountant’s nightmare for their advice to be undecidable. + +Clarity is non-Turing complete, yet very expressive. This makes it so that Clarity is decidable and cannot encode uncomputable problems. There are discussions and papers on smart contract languages such as Solidity that propose subsets of Solidity that are non-Turing complete. These subsets are decidable and cannot encode uncomputable problems. However, there is no consensus on which subsets to work with and they are not widely used. + +### Advantages of Decidability in Smart Contracts + +Why is decidability important in the context of smart contracts? + +First, it is not possible for a Clarity call to run out of gas in the middle of a call. Because of its decidability, it is possible to get a complete static analysis of the call graph to get an accurate picture of the cost before execution. + +Solidity allows for unbounded loops, recursion, and dynamic function calls, which makes it difficult to accurately predict the execution cost or gas usage beforehand. As a result, Solidity contracts may run out of gas during execution if the gas limit is not set appropriately or if the contract encounters a scenario with unexpectedly high computational requirements. + +One practical example is the issue of a specific kind of DoS attack in Solidity, where the contract is rendered inoperable because of unbounded execution constraints. An example of this is the GovernMental attack, where a mapping that needed to be deleted for a payout became so large that working with it exceeded the block gas limit. + +There are a few different properties of Clarity's language design that prevents such DoS attacks. + +The reason that the analysis system can accurately estimate the execution cost is because certain functionality is intentionally limited in Clarity. + +For example, there is no recursion in Clarity, so we can't infinitely call into a function over and over. + +Data types in Clarity are also restricted. Any data types that don't require a hard length limit are not iterable. + +Maps and tuples, for example, do not require you to enter a maximum length when defining them, but you also can't iterate over them. + +Lists, on the other hand, which are iterable, do require the developer to define an upper limit when defining them. This is a large part of what allows an accurate static analysis of Clarity contracts. + +So how would we implement a mapping of an undefined size in Clarity? We wouldn't, because it's an anti-pattern in smart contract design. + +Instead, Clarity forces us to think of a better solution to our problem. For example, implementing a way for users to handle mapping/list element operations themselves, instead of mass operations handled at the contract level. + +If you [analyze the GovernMental attack](https://hackernoon.com/smart-contract-attacks-part-2-ponzi-games-gone-wrong-d5a8b1a98dd8#h-attack-2-call-stack-attack), you'll see that it took advantage of multiple security issues, all of which are mitigated in Clarity. You'll also see that a fix was added to make it economically infeasible to carry out this type of attack again. + +This brings up another crucial point when setting appropriate mental models for smart contracts and blockchain systems: complexity means more potential bugs, which means adding more complexity to address those bugs. + +When this happens over and over again, we are trapping ourselves into creating an evermore complex system. Addressing these issues at the language level prevents this ever-growing complexity. + +For a deep dive into how Clarity was designed, check out [SIP-002](https://github.com/stacksgov/sips/blob/main/sips/sip-002/sip-002-smart-contract-language.md). + +{% hint style="info" %} +You can view some more common smart contract vulnerabilities and how they are mitigated in [this article](https://stacks.org/bringing-clarity-to-8-dangerous-smart-contract-vulnerabilities/). +{% endhint %} + +This has second-order effects as well when we look at security testing and auditing. One of the common tools for testing smart contracts is formal verification, where we mathematically prove that certain properties of smart contracts will or will not remain true in all cases. + +This can lead to the path explosion problem, where there are so many paths available that formal verification becomes incredibly difficult. This problem is mitigated in Clarity, since there is not chance of a program encountering an unbounded loop. + +This leads us to a more general mental model for thinking about decidability as smart contracts continue to become a larger part of our economy. Remember that the goal with blockchain systems is to create an open, transparent, fair financial system. + +This means that smart contracts will be responsible for managing large amounts of wealth for ever-growing amounts of people. As smart contracts encompass more financial structures, their complexity and usage will grow. + +Complexity is the enemy of security. The more complex a system is, the more danger there is in creating uncomputable problems when there are no hard restrictions on the execution steps that can be taken. + +This is deadly in financial infrastructure that is not only open and transparent, but immutable. Let's explore this idea of uncomputability a bit more. + +### Intuition on Uncomputability + +Intuitively, uncomputability is an algorithmic view of undecidability. Uncomputability has the same foundations as undecidability. Undecidable questions are framed as logic statements or statements about integers. Of course, programs are logic statements and may even be viewed as integers, though we view programs differently. We often view programs with additional details of memory models, implementation details, and execution semantics. + +The [Halting problem](https://en.wikipedia.org/wiki/Halting_problem): As an example, given any program `P` and any finite input `I` for `P`, then the Halting Problem is the challenge of determining if `P` halts on input `I`. + +Alonzo Church and Alan Turing showed the Halting Problem is unsolvable. + +Christopher Strachey gave an intuitive proof-by-contradiction showing the Halting problem is uncomputable. This is set up by supposing there is a program `H` that can solve the Halting problem for any program `P`. `H(P)` returns true if `P` halts and false otherwise. Then build a program `P` that does not halt when `H(P)` is true, giving a contradiction. Similarly, this program `P` halts when `H(P)` is false, also a contradiction. + +Uncomputable problems are problems that cannot be solved by an algorithm or a computer, no matter how much time or resources are provided. These problems exist in various forms, and one such example is the Post correspondence problem, which was proposed by Emil Post. + +The Post correspondence problem can be described using pairs of strings and an integer. Imagine you have n pairs of strings, called P. These strings are made up of characters from a character set, such as UTF-8 or any other alphabet with at least two symbols. The pairs of strings look like this: + +``` +P = { (x1, y1), (x2, y2), … , (xn, yn) } +``` + +Now, you also have an integer m that is greater than 0. The Post correspondence problem asks whether there is a way to create a list of indices (i1, i2, …, im) using the given pairs of strings. You can repeat these indices if needed, with one condition: when you combine the x strings from the pairs using the indices, the resulting string must be equal to the combined y strings from the same pairs using the same indices. In other words: + +``` +x(i1) x(i2) … x(im) = y(i1) y(i2) … y(im) +``` + +When developers try to solve the Post correspondence problem, they often attempt to use indeterminate loops (loops without a fixed number of iterations) rather than recursion. This is because the problem seems to require searching through different combinations of indices until a solution is found or it's proven that no solution exists. + +In simple terms, the Post correspondence problem involves trying to find a sequence of indices that, when applied to the given pairs of strings, produces equal concatenated strings from both the x and y components. This problem is considered uncomputable because there is no general algorithm that can solve it for all possible input pairs of strings and integers. + +It turns out, many questions about how programs behave are uncomputable. This has a number of consequences for smart contracts that are built in Turing complete languages, many of which we are not aware of yet but will surely become aware of as we encounter them in the future. + +### Raymond Smullyan’s Intuition on Undecidability + +This is a part of Raymond Smullyan’s approach to understanding undecidability in propositional logic. It uses meta-information to show something must be true, though it cannot be proved in propositional logic. This is based on a paradox. + +In propositional logic, a logical statement is undecidable if we cannot prove it true or false. Given a propositional logic statement S, a proof is a sequence of formal logical deductions, starting from basic facts and ending by indicating if S is true or false. + +Smullyan starts with an island of Knights and Knaves. Knights always tell the truth. Knaves always lie. We cannot distinguish islanders otherwise. + +There is a great logician named Ray. Whatever Ray proves is true. This is just like a good theorem prover. + +An islander Jack proclaims: “You cannot prove I am a Knight” to the logician Ray. + +The next reasoning is based on meta-knowledge of this situation. This meta-knowledge shows that some problems are undecidable in propositional logic. + +If Ray can prove Jack is a Knight, then Jack must be a Knave, since Jack must have lied. That is because Ray proved Jack is a Knight. Since Jack is a Knave, Ray’s proof contradicts the assumption that Ray only proves true things. So, this case cannot hold. + +If Ray cannot prove Jack is a Knight, then Jack must be a Knight, since Jack stated the truth. But Ray cannot prove the fact that Jack is a Knight. + +In the context of smart contracts and programming languages, Turing complete languages like Solidity come with the possibility of undecidable problems. + +These undecidable problems are similar to the paradox presented in the Knights and Knaves story, where it's impossible to determine whether Jack is a Knight or a Knave based on the given information. + +In the Knights and Knaves story, Ray is analogous to a theorem prover or a smart contract in a Turing complete language. Ray is faced with a statement that is undecidable within the constraints of the system (Knights and Knaves), which leads to a paradox. + +Similarly, a Turing complete smart contract language might face undecidable problems that can't be resolved, leading to unexpected behavior, vulnerabilities, or resource consumption issues (like running out of gas in Ethereum). + +On the other hand, non-Turing complete languages like Clarity are designed to avoid undecidable problems by limiting their expressiveness. + +In the context of the Knights and Knaves story, a non-Turing complete language would simply not allow Jack to make a statement that could lead to a paradox. By disallowing certain features like unbounded loops and recursion, non-Turing complete languages can provide stronger guarantees about the behavior and resource usage of smart contracts. + +This predictability is desirable in many cases, especially when dealing with high-value transactions or critical systems. + +### Reference + +The Mathematics of Various Entertaining Subjects: Research in Recreational Math Illustrated Edition, Jennifer Beineke (Editor), Jason Rosenhouse (Editor), Raymond M. Smullyan (Foreword), Princeton University Press, 2016. diff --git a/docs/learn/clarity/overview.md b/docs/learn/clarity/overview.md new file mode 100644 index 0000000000..ea7c18330a --- /dev/null +++ b/docs/learn/clarity/overview.md @@ -0,0 +1,55 @@ +# Overview + +

source: Hiro blog

+ +Clarity is a **decidable** smart contract language that optimizes for predictability and security, designed for the Stacks blockchain. Smart contracts allow developers to encode essential business logic on a blockchain. + +The design decisions behind Clarity were based heavily on taking lessons learned in common Solidity exploits and creating a language that has been purpose-built for safety and security in mind. + +These docs serve primarily as a reference for the functions and keywords that you can use in Clarity. + +In order to learn Clarity, we recommend diving into the [Clarity of Mind](https://book.clarity-lang.org/), an online book to teach you everything you need to know to build robust smart contracts, or joining a [Clarity Camp](https://clarity-lang.org/universe#camp), the cohort-based immersive Clarity experience. + +### What makes Clarity different + +The following section is an excerpt from the excellent book, [Clarity of Mind](https://book.clarity-lang.org/ch00-00-introduction.html): + +The number of smart contract languages grows by the year. Choosing a first language can be challenging, especially for a beginner. The choice is largely dictated by the ecosystem you are interested in, although some languages are applicable to more than just one platform. Each language has its own upsides and downsides and it is out of the scope of this book to look at all of them. Instead, we will focus on what sets Clarity apart and why it is a prime choice if you require the utmost security and transparency. + +One of the core precepts of Clarity is that it is secure by design. The design process was guided by examining common pitfalls, mistakes, and vulnerabilities in the field of smart contract engineering as a whole. There are countless real world examples of where developer failure led to the loss or theft of vast amounts of tokens. To name two big ones: an issue that has become known as the Parity bug led to the irreparable loss of millions of dollars worth of Ethereum. Second, the hacking of The DAO (a "Decentralized Autonomous Organization") caused financial damage so great that the Ethereum Foundation decided to issue a contentious hard fork that undid the theft. These and many other mistakes could have been prevented in the design of the language itself. + +#### Clarity is interpreted, not compiled + +Clarity code is interpreted and committed to the chain exactly as written. Solidity and other languages are compiled to byte-code before it is submitted to the chain. The danger of compiled smart contract languages is two-fold: first, a compiler adds a layer of complexity. A bug in the compiler may lead to different byte-code than was intended and thus carries the risk of introducing a vulnerability. Second, byte-code is not human-readable, which makes it very hard to verify what the smart contract is actually doing. Ask yourself, would you sign a contract you cannot read? If your answer is no, then why should it be any different for smart contracts? With Clarity, what you see is what you get. + +#### Clarity is decidable + +A decidable language has the property that from the code itself, you can know with certainty what the program will do. This avoids issues like the halting problem. With Clarity you know for sure that given any input, the program will halt in a finite number of steps. In simple terms: it is guaranteed that program execution will end. Decidability also allows for complete static analysis of the call graph so you get an accurate picture of the exact cost before execution. There is no way for a Clarity call to "run out of gas" in the middle of the call. We explore this idea more, along with a discussion on Turing completeness, in the security deep dive on decidability. + +#### Clarity does not permit reentrancy + +Reentrancy is a situation where one smart contract calls into another, which then calls back into the first contract—the call "re-enters" the same logic. It may allow an attacker to trigger multiple token withdrawals before the contract has had a chance to update its internal balance sheet. Clarity's design considers reentrancy an anti-feature and disallows it on the language level. + +#### Clarity guards against overflow and underflows + +Overflows and underflows happen when a calculation results in a number that is either too large or too small to be stored, respectively. These events throw smart contracts into disarray and may intentionally be triggered in poorly written contracts by attackers. Usually this leads to a situation where the contract is either frozen or drained of tokens. Overflows and underflows of any kind automatically cause a transaction to be aborted in Clarity. + +#### Support for custom tokens is built-in + +Issuance of custom fungible and non-fungible tokens is a popular use-case for smart contracts. Custom token features are built into the Clarity language. Developers do not need to worry about creating an internal balance sheet, managing supply, and emitting token events. Creating custom tokens is covered in depth in later chapters. + +#### On Stacks, transactions are secured by post conditions + +In order to further safeguard user tokens, post conditions can be attached to transactions to assert the chain state has changed in a certain way once the transaction has completed. For example, a user calling into a smart contract may attach a post condition that states that after the call completes, exactly 500 STX should have been transferred from one address to another. If the post condition check fails, then the entire transaction is reverted. Since custom token support is built right into Clarity, post conditions can also be used to guard any other token in the same way. + +#### Returned responses cannot be left unchecked + +Public contract calls must return a so-called response that indicates success or failure. Any contract that calls another contract is required to properly handle the response. Clarity contracts that fail to do so are invalid and cannot be deployed on the network. Other languages like Solidity permit the use of low level calls without requiring the return value to be checked. For example, a token transfer can fail silently if the developer forgets to check the result. In Clarity it is not possible to ignore errors, although that obviously does prevent buggy error handling on behalf of the developer. Responses and error handling are covered extensively in the chapters on functions and control flow. + +#### Composition over inheritance + +Clarity adopts a composition over inheritance. It means that Clarity smart contracts do not inherit from one another like you see in languages like Solidity. Developers instead define traits which are then implemented by different smart contracts. It allows contracts to conform to different interfaces with greater flexibility. There is no need to worry about complex class trees and contracts with implicit inherited behavior. + +#### Access to the base chain: Bitcoin + +Clarity smart contracts can read the state of the Bitcoin base chain. It means you can use Bitcoin transactions as a trigger in your smart contracts! Clarity also features a number of built-in functions to verify secp256k1 signatures and recover keys. diff --git a/docs/learn/network-fundamentals/README.md b/docs/learn/network-fundamentals/README.md new file mode 100644 index 0000000000..9cbb5d7a9e --- /dev/null +++ b/docs/learn/network-fundamentals/README.md @@ -0,0 +1,5 @@ +# Network Fundamentals + +Now that you have a high-level understanding of what Stacks is and how it works, let's dive into some more details of all of the components that make up the Stacks Bitcoin L2. + +We're going to start with the basics of how the network is actually structured and the components that comprise it. diff --git a/docs/learn/network-fundamentals/accounts.md b/docs/learn/network-fundamentals/accounts.md new file mode 100644 index 0000000000..e7f908c51b --- /dev/null +++ b/docs/learn/network-fundamentals/accounts.md @@ -0,0 +1,105 @@ +# Accounts + +
+ +### Introduction + +Stacks uses an accounts-based model, more similar to Ethereum, rather than a [UTXO](https://learnmeabitcoin.com/technical/transaction/utxo/) model like Bitcoin. In a UTXO model, the network operates as a ledger, with each UTXO being analagous to a cash bill. + +With an accounts-based model, each account is associated with a balance and that balance can be added to or subtracted from. + +Stacks accounts are entities that own assets, like Stacks (STX) tokens. An account has an address, private key, nonce, and one or more asset balances. + +{% hint style="info" %} +The cryptographic signature algorithm used in Stacks is [**secp256k1**](https://en.bitcoinwiki.org/wiki/Secp256k1). + +Additionally, [Ed25519](https://ed25519.cr.yp.to/) is also used just for the VRF (Verifiable Random Function). +{% endhint %} + +Assets cannot leave an account without an action from the account owner. All changes to assets (and the balances of the account) require a corresponding transaction. + +{% hint style="info" %} +The transaction type doesn't need to be a token transfer - contract deploy and contract call transactions can change the balances of an account +{% endhint %} + +### Creation + +An account is generated from a 24-word mnemonic phrase. This is often referred to as the **seed phrase**. The seed phrase provides access to Stacks accounts. + +{% hint style="danger" %} +If the seed phrase is lost, access to the associated account cannot be restored. No person or organization can recover a lost seed phrase. +{% endhint %} + +The easiest way to generate a new Stacks account is to use the [Stacks CLI](https://github.com/hirosystems/stacks.js/tree/master/packages/cli): + +{% code title="Generate a new account (CLI)" %} +```bash +# install CLI globally +npm install --global @stacks/cli + +# generate a new account and store details in a new file + +# '-t' option makes this a testnet account +stx make_keychain -t > cli_keychain.json +``` +{% endcode %} + +`make_keychain` creates the following file: + +```js +{ + "mnemonic": "aaa bbb ccc ddd ...", + "keyInfo": { + "privateKey": "5a3f1f15245bb3fb...", + "address": "STJRM2AMVF90ER6G3RW1QTF85E3HZH37006D5ER1", + "btcAddress": "biwSd6KTEvJcyX2R8oyfgj5REuLzczMYC1", + "wif": "L4HXn7PLmzoNW...", + "index": 0 + } +} +``` + +{% hint style="info" %} +Check out the [Stacks CLI reference](https://docs.hiro.so/references/stacks-cli) for more details +{% endhint %} + +| Field | Description | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `mnemonic` | A 24-word seed phrase used to access the account, generated using [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) with 256 bits of entropy | +| `keyInfo.privateKey` | Private key for the account. Required for token transfers and often referred to as `senderKey` | +| `keyInfo.address` | Stacks address for the account | +| `keyInfo.btcAddress` | Corresponding BTC address for the account. | +| `keyInfo.wif` | Private key of the btcAddress in compressed format. | +| `keyInfo.index` | Nonce for the account, starting at 0 | + +Note that a new account automatically exists for each new private key. There is no need to manually instantiate an account on the Stacks blockchain. + +{% hint style="info" %} +Addresses are created by generating the [RIPEMD-160 hash](https://en.wikipedia.org/wiki/RIPEMD#RIPEMD-160_hashes) of the [SHA256](https://en.bitcoinwiki.org/wiki/SHA-256) of the public key. BTC addresses are encoded with [Base58Check](https://bitcoin.it/wiki/Base58Check_encoding). For Stacks addresses, [c32check](https://github.com/stacks-network/c32check) is used. Deriving an address from a public key can be done without internet access, for instance using the c32check `c32addressDecode` method. +{% endhint %} + +Alternatively to the CLI creation, the [Stacks Transactions JS](https://github.com/hirosystems/stacks.js/tree/master/packages/transactions) library can be used: + +{% code title="Generate a private key & derive address (transactions library)" %} +```js +import { + makeRandomPrivKey, + privateKeyToString, + getAddressFromPrivateKey, + TransactionVersion, + getPublicKey, +} from "@stacks/transactions"; + +const privateKey = makeRandomPrivKey(); + +// Get public key from private +const publicKey = getPublicKey(privateKey); + +const stacksAddress = getAddressFromPrivateKey( + privateKeyToString(privateKey), + TransactionVersion.Testnet // remove for Mainnet addresses +); +``` +{% endcode %} + +Finally, you can generate new account using a Stacks-enabled wallet like [Leather](https://leather.io/), [Xverse](https://www.xverse.app/), or [Asigna](https://asigna.io/). diff --git a/docs/learn/network-fundamentals/authentication.md b/docs/learn/network-fundamentals/authentication.md new file mode 100644 index 0000000000..9b60949a6c --- /dev/null +++ b/docs/learn/network-fundamentals/authentication.md @@ -0,0 +1,103 @@ +# Authentication + +

source: Hiro blog

+ +### Introduction + +This guide explains how authentication is performed on the Stacks blockchain. + +Authentication provides a way for users to identify themselves to an app while retaining complete control over their credentials and personal details. + +Users who register for your app can subsequently authenticate to any other app with support for the [Bitcoin Name System](bitcoin-name-system.md) and vice versa. + +### Web2 vs Web3 Authentication + +If you come from the web2 world, you are likely used to authenticating with usernames and passwords, where the user's info is stored in a database. Upon entering the password, it is hashed and compared to the hash stored in the database and, if it matches, the user is logged in. + +Web3 authentication works a bit differently. One of the core philosophies of web3 is data ownership, which means users are in control of their data, including their authentication. + +Authentication in the Bitcoin and Stacks world makes use of cryptography to generate private keys, public keys and addresses. + +We'll go over the basics here, but if you want to dig in, [Learn Me a Bitcoin](https://learnmeabitcoin.com/beginners/guide/keys-addresses/) is a great place to start. + +To generate a Stacks account, we generate a private key from a 24 word mnemonic phrase, as discussed in the previous section. This private key is then used to generate a public key using a one-way hash function. Meaning you can derive a public key from a private key, but not vice versa. + +A user's private key is their main source of security and is used to authenticate them. Do not lose your private key. + +So, when I use a wallet app like [Leather](https://leather.io/) and I want to use it to authenticate with a dapp like StackingDAO, what I am doing is that I am giving my wallet my private key, proving that I own the corresponding address. + +The wallet will then pass that information (my address and public key) to the app along with a signature. + +A signature can be thought of as proof that I own the private key without actually revealing the private key. That mechanism is how I can use the same wallet and address to log in to any app that supports Stacks authentication. + +Third Web has a great [conceptual primer](https://blog.thirdweb.com/web3-auth/) on web3 authentication. + +For a more practical introduction, take a look at the [Quickstart tutorial ](https://app.gitbook.com/o/hoh4mQXTl8NvI3cETroY/s/Zz9BLmTU9oydDpL3qiUh/)and [Hiro's Stacks.js docs](https://docs.hiro.so/stacks/connect/guides/authenticate-users). + +### How it works + +The authentication flow with Stacks is similar to the typical client-server flow used by centralized sign in services (for example, OAuth). However, with Stacks the authentication flow happens entirely client-side. + +{% hint style="info" %} +This explanation is here so you can understand how this process works, but the bulk of this functionality is handled by the wallet and the JS library you use. Take a look at the [Stacks.js docs](https://docs.hiro.so/stacks/stacks.js/concepts/accounts-and-addresses) for more info. +{% endhint %} + +An app and authenticator, such as the [Leather wallet](https://leather.io/), communicate during the authentication flow by passing back and forth two tokens. The requesting app sends the authenticator an `authRequest` token. Once a user approves authentication, the authenticator responds to the app with an `authResponse` token. + +These tokens are based on [a JSON Web Token (JWT) standard](https://tools.ietf.org/html/rfc7519) with additional support for the `secp256k1` curve used by Bitcoin and many other cryptocurrencies. They are passed via URL query strings. + +When a user chooses to authenticate an app, it sends the `authRequest` token to the authenticator via a URL query string with an equally named parameter: + +`https://wallet.hiro.so/...?authRequest=j902120cn829n1jnvoa...` + +When the authenticator receives the request, it generates an `authResponse` token for the app using an ephemeral transit key. The ephemeral transit key is just used for the particular instance of the app, in this case, to sign the `authRequest`. + +The app stores the ephemeral transit key during request generation. The public portion of the transit key is passed in the `authRequest` token. The authenticator uses the public portion of the key to encrypt an app private key which is returned via the `authResponse`. + +The authenticator generates the app private key from the user's identity address private key and the app's domain. The app private key serves three functions: + +{% stepper %} +{% step %} +### It is used to create credentials that give the app access to a storage bucket in the user's Gaia hub + +This allows the app to access the user's app-specific storage in their Gaia hub. +{% endstep %} + +{% step %} +### It is used in the end-to-end encryption of files stored for the app in the user's Gaia storage + +This key is used to encrypt files so only the app (with the derived key) can decrypt them. +{% endstep %} + +{% step %} +### It serves as a cryptographic secret that apps can use to perform other cryptographic functions + +Apps can use this deterministic secret for additional cryptographic operations tied to the user's identity and domain. +{% endstep %} +{% endstepper %} + +Finally, the app private key is deterministic, meaning that the same private key will always be generated for a given Stacks address and domain. + +### Key pairs + +Authentication with Stacks makes extensive use of public key cryptography generally and ECDSA with the `secp256k1` curve in particular. + +The following sections describe the three public-private key pairs used, including how they're generated, where they're used and to whom private keys are disclosed. + +#### Transit private key + +The transit private key is an ephemeral key that is used to encrypt secrets that need to be passed from the authenticator to the app during the authentication process. It is randomly generated by the app at the beginning of the authentication response. + +The public key that corresponds to the transit private key is stored in a single element array in the `public_keys` key of the authentication request token. The authenticator encrypts secret data such as the app private key using this public key and sends it back to the app when the user signs in to the app. The transit private key signs the app authentication request. + +#### Identity address private key + +The identity address private key is derived from the user's keychain phrase and is the private key of the Stacks username that the user chooses to use to sign in to the app. It is a secret owned by the user and never leaves the user's instance of the authenticator. + +This private key signs the authentication response token for an app to indicate that the user approves sign in to that app. + +#### App private key + +The app private key is an app-specific private key that is generated from the user's identity address private key using the `domain_name` as input. + +The app private key is securely shared with the app on each authentication, encrypted by the authenticator with the transit public key. Because the transit key is only stored on the client side, this prevents a man-in-the-middle attack where a server or internet provider could potentially snoop on the app private key. diff --git a/docs/learn/network-fundamentals/bitcoin-name-system.md b/docs/learn/network-fundamentals/bitcoin-name-system.md new file mode 100644 index 0000000000..e7d33aeda5 --- /dev/null +++ b/docs/learn/network-fundamentals/bitcoin-name-system.md @@ -0,0 +1,210 @@ +# Bitcoin Name System + +

source: Hiro blog

+ +Bitcoin Name System (BNS) is a network system that binds Stacks usernames to off-chain state without relying on any central points of control. + +The Stacks V1 blockchain implemented BNS through first-order name operations. In Stacks V2, BNS is instead implemented through a smart-contract loaded during the genesis block. + +Names in BNS have three properties: + +* **Names are globally unique.** The protocol does not allow name collisions, and all well-behaved nodes resolve a given name to the same state. +* **Names are human-meaningful.** Each name is chosen by its creator. +* **Names are strongly owned.** Only the name's owner can change the state it resolves to. Specifically, a name is owned by one or more ECDSA private keys. + +The Stacks blockchain ensures that each node's BNS view is synchronized to all of the other nodes in the world, so queries on one node will be the same on other nodes. Stacks blockchain nodes allow a name's owner to bind up to 40Kb of off-chain state to their name, which will be replicated to all other Stacks blockchain nodes via a P2P network. + +The biggest consequence for developers is that in BNS, reading name state is fast and cheap but writing name state is slow and expensive. This is because registering and modifying names requires one or more transactions to be sent to the underlying blockchain, and BNS nodes will not process them until they are sufficiently confirmed. Users and developers need to acquire and spend the requisite cryptocurrency (STX) to send BNS transactions. + +### Motivation behind name systems + +We rely on name systems in everyday life, and they play a critical role in many different applications. For example, when you look up a friend on social media, you are using the platform's name system to resolve their name to their profile. When you look up a website, you are using the Domain Name Service to resolve the hostname to its host's IP address. When you check out a Git branch, you are using your Git client to resolve the branch name to a commit hash. When you look up someone's PGP key on a keyserver, you are resolving their key ID to their public key. + +What kinds of things do we want to be true about names? In BNS, names are globally unique, names are human-meaningful, and names are strongly owned. However, if you look at these examples, you'll see that each of them only guarantees two of these properties. This limits how useful they can be. + +* In DNS and social media, names are globally unique and human-readable, but not strongly owned. The system operator has the final say as to what each name resolves to. + * Problem: Clients must trust the system to make the right choice in what a given name resolves to. This includes trusting that no one but the system administrators can make these changes. +* In Git, branch names are human-meaningful and strongly owned, but not globally unique. Two different Git nodes may resolve the same branch name to different unrelated repository states. + * Problem: Since names can refer to conflicting state, developers have to figure out some other mechanism to resolve ambiguities. +* In PGP, names are key IDs. They are globally unique and cryptographically owned, but not human-readable. PGP key IDs are derived from the keys they reference. + * Problem: These names are difficult for most users to remember since they do not carry semantic information relating to their use in the system. + +BNS names have all three properties, and none of these problems. This makes it a powerful tool for building all kinds of network applications. With BNS, we can do the following and more: + +* Build domain name services where hostnames can't be hijacked. +* Build social media platforms where user names can't be stolen by phishers. +* Build version control systems where repository branches do not conflict. +* Build public-key infrastructure where it's easy for users to discover and remember each other's keys. + +### Organization of BNS + +BNS names are organized into a global name hierarchy. There are three different layers in this hierarchy related to naming: + +* **Namespaces.** These are the top-level names in the hierarchy. An analogy to BNS namespaces are DNS top-level domains. Existing BNS namespaces include `.id`, `.podcast`, and `.helloworld`. All other names belong to exactly one namespace. Anyone can create a namespace, but in order for the namespace to be persisted, it must be _launched_ so that anyone can register names in it. Namespaces are not owned by their creators. +* **BNS names.** These are names whose records are stored directly on the blockchain. The ownership and state of these names are controlled by sending blockchain transactions. Example names include `verified.podcast` and `muneeb.id`. Anyone can create a BNS name, as long as the namespace that contains it exists already. +* **BNS subdomains.** These are names whose records are stored off-chain, but are collectively anchored to the blockchain. The ownership and state for these names lives within the P2P network data. While BNS subdomains are owned by separate private keys, a BNS name owner must broadcast their subdomain state. Example subdomains include `jude.personal.id` and `podsaveamerica.verified.podcast`. Unlike BNS namespaces and names, the state of BNS subdomains is _not_ part of the blockchain consensus rules. + +A feature comparison matrix summarizing the similarities and differences between these name objects: + +| Feature | **Namespaces** | **BNS names** | **BNS Subdomains** | +| -------------------------------------- | -------------- | ------------- | ------------------ | +| Globally unique | X | X | X | +| Human-meaningful | X | X | X | +| Owned by a private key | | X | X | +| Anyone can create | X | X | \[1] | +| Owner can update | | X | \[1] | +| State hosted on-chain | X | X | | +| State hosted off-chain | | X | X | +| Behavior controlled by consensus rules | X | X | | +| May have an expiration date | | X | | + +\[1] Requires the cooperation of a BNS name owner to broadcast its transactions + +### Namespaces + +Namespaces are the top-level name objects in BNS. They control a few properties about the names within them: + +* How expensive they are to register +* How long they last before they have to be renewed +* Who (if anyone) receives the name registration fees +* Who is allowed to seed the namespace with its initial names + +At the time of this writing, by far the largest BNS namespace is the `.id` namespace. Names in the `.id` namespace are meant for resolving user identities. Short names in `.id` are more expensive than long names, and have to be renewed by their owners every two years. Name registration fees are not paid to anyone in particular—they are instead sent to a "black hole" where they are rendered non-spendable (the intention is to discourage ID squatters). + +Unlike DNS, anyone can create a namespace and set its properties. Namespaces are created on a first-come first-serve basis, and once created, they last forever. + +However, creating a namespace is not free. The namespace creator must burn cryptocurrency to do so. The shorter the namespace, the more cryptocurrency must be burned (that is, short namespaces are more valuable than long namespaces). For example, it cost Blockstack PBC 40 BTC to create the `.id` namespace in 2015 (in transaction `5f00b8e609821edd6f3369ee4ee86e03ea34b890e242236cdb66ef6c9c6a1b281`). + +Namespaces can be between 1 and 19 characters long, and are composed of the characters `a-z`, `0-9`, `-`, and `_`. + +### Subdomains + +BNS names are strongly owned because the owner of its private key can generate valid transactions that update its zone file hash and owner. However, this comes at the cost of requiring a name owner to pay for the underlying transaction in the blockchain. Moreover, this approach limits the rate of BNS name registrations and operations to the underlying blockchain's transaction bandwidth. + +BNS overcomes this with subdomains. A **BNS subdomain** is a type of BNS name whose state and owner are stored outside of the blockchain, but whose existence and operation history are anchored to the blockchain. Like their on-chain counterparts, subdomains are globally unique, strongly owned, and human-readable. BNS gives them their own name state and public keys. Unlike on-chain names, subdomains can be created and managed cheaply, because they are broadcast to the BNS network in batches. A single blockchain transaction can send up to 120 subdomain operations. + +This is achieved by storing subdomain records in the BNS name zone files. An on-chain name owner broadcasts subdomain operations by encoding them as `TXT` records within a DNS zone file. To broadcast the zone file, the name owner sets the new zone file hash with a `NAME_UPDATE` transaction and replicates the zone file. This, in turn, replicates all subdomain operations it contains, and anchors the set of subdomain operations to an on-chain transaction. The BNS node's consensus rules ensure that only valid subdomain operations from valid `NAME_UPDATE` transactions will ever be stored. + +For example, the name `verified.podcast` once wrote the zone file hash `247121450ca0e9af45e85a82e61cd525cd7ba023`, which is the hash of the following zone file: + +```bash +$TTL 3600 +1yeardaily TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxeWVhcmRhaWx5CiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMXllYXJkYWlseS9oZWFkLmpzb24iCg==" +2dopequeens TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAyZG9wZXF1ZWVucwokVFRMIDM2MDAKX2h0dHAuX3RjcCBVUkkgMTAgMSAiaHR0cHM6Ly9waC5kb3Rwb2RjYXN0LmNvLzJkb3BlcXVlZW5zL2hlYWQuanNvbiIK" +10happier TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxMGhhcHBpZXIKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8xMGhhcHBpZXIvaGVhZC5qc29uIgo=" +31thoughts TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzMXRob3VnaHRzCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzF0aG91Z2h0cy9oZWFkLmpzb24iCg==" +359 TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzNTkKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8zNTkvaGVhZC5qc29uIgo=" +30for30 TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzMGZvcjMwCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzBmb3IzMC9oZWFkLmpzb24iCg==" +onea TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiBvbmVhCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vb25lYS9oZWFkLmpzb24iCg==" +10minuteteacher TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxMG1pbnV0ZXRlYWNoZXIKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8xMG1pbnV0ZXRlYWNoZXIvaGVhZC5qc29uIgo=" +36questionsthepodcastmusical TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzNnF1ZXN0aW9uc3RoZXBvZGNhc3RtdXNpY2FsCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzZxdWVzdGlvbnN0aGVwb2RjYXN0bXVzaWNhbC9oZWFkLmpzb24iCg==" +_http._tcp URI 10 1 "https://dotpodcast.co/" +``` + +Each `TXT` record in this zone file encodes a subdomain-creation. For example, `1yeardaily.verified.podcast` resolves to: + +```json +{ + "address": "1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH", + "blockchain": "bitcoin", + "last_txid": "d87a22ebab3455b7399bfef8a41791935f94bc97aee55967edd5a87f22cce339", + "status": "registered_subdomain", + "zonefile_hash": "e7acc97fd42c48ed94fd4d41f674eddbee5557e3", + "zonefile_txt": "$ORIGIN 1yeardaily\n$TTL 3600\n_http._tcp URI 10 1 \"https://ph.dotpodcast.co/1yeardaily/head.json\"\n" +} +``` + +This information was extracted from the `1yeardaily` `TXT` resource record in the zone file for `verified.podcast`. + +Subdomain lifecycle + +{% stepper %} +{% step %} +### Creation + +A subdomain-creation operation is created by the subdomain owner and encoded into a `TXT` record in an on-chain name owner's zone file. The on-chain name owner broadcasts the zone file by issuing a `NAME_UPDATE` transaction, which anchors the subdomain-creation on-chain. +{% endstep %} + +{% step %} +### Update + +Subdomain updates are done off-chain by creating signed operations from the subdomain owner's private key. Any on-chain name owner can include these signed operations in their zone file and broadcast via `NAME_UPDATE`. Operations are ordered by a sequence number and require a valid signature that links to the previous operation's public key. +{% endstep %} + +{% step %} +### Transfer + +To change the address (public key hash) owning a subdomain, the subdomain owner signs a subdomain-transfer operation and asks an on-chain name owner (typically the one who created the subdomain) to broadcast it via `NAME_UPDATE`. The broadcasting on-chain name owner's zone file must be present in the Atlas network to prove absence of conflicting operations. +{% endstep %} +{% endstepper %} + +Sequence and validation rules + +* Subdomain operations are ordered by sequence number, starting at 0. Each new operation must include: + * The next sequence number + * The public key that hashes to the previous subdomain transaction's address + * A signature from the corresponding private key over the entire subdomain operation +* If two correctly signed but conflicting operations have the same sequence number, the one earlier in blockchain history is accepted. Invalid operations are ignored. + +Subdomain creation and management rules + +* A subdomain-creation transaction can only be processed by the owner of the on-chain name that shares its suffix (e.g., only the owner of `res_publica.id` can broadcast creations for `*.res_publica.id`). +* A subdomain-transfer transaction can only be broadcast by the owner of the on-chain name that created it. +* To send a subdomain-creation or subdomain-transfer, all of an on-chain name owner's zone files must be present in the Atlas network. This allows proving the absence of conflicting operations. +* A subdomain update can be broadcast by any on-chain name owner, but the subdomain owner needs to find a cooperating on-chain name owner to include and broadcast it. + +To create a subdomain, the subdomain owner generates the creation operation and gives it to the on-chain name owner. Once created, the subdomain owner can use any on-chain name owner to broadcast updates by providing signed operations packaged into zone files. + +Subdomain registrars + +Because subdomain names are cheap, developers may run subdomain registrars for their applications. For example, the name `personal.id` is used to register usernames without requiring users to spend Bitcoin. + +A reference implementation is available: https://github.com/stacks-network/subdomain-registrar. Users still own their subdomain names; the registrar helps developers broadcast subdomain operations. + +### BNS and DID Standards + +BNS names are compliant with the emerging Decentralized Identity Foundation (DIF) protocol specification for decentralized identifiers (DIDs): http://identity.foundation + +Each name in BNS has an associated DID. The DID format for BNS is: + +```bash +did:stack:v0:{address}-{index} +``` + +Where: + +* `{address}` is an on-chain public key hash (for example a Bitcoin address). +* `{index}` refers to the `nth` name this address created. + +Examples: + +* `personal.id` → `did:stack:v0:1dARRtzHPAFRNE7Yup2Md9w18XEQAtLiV-0` (first name created by that address) +* `jude.id` → `did:stack:v0:16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg-1` (the address had created one earlier name before this one) + +Purpose: a DID provides an eternal identifier for a public key. The public key may change, but the DID will not. + +For a DID to be resolvable, all of the following must be true for a name: + +* The name must exist +* The name's zone file hash must be the hash of a well-formed DNS zone file +* The DNS zone file must be present in the Stacks node's data +* The DNS zone file must contain a `URI` resource record that points to a signed JSON Web Token +* The public key that signed the JSON Web Token (and is included with it) must hash to the address that owns the name + +Not all names will have DIDs that resolve to public keys. Names created by standard tooling will have DIDs that do. + +A RESTful API is under development. + +### DID Encoding for Subdomains + +Every name and subdomain in BNS has a DID. Encoding differs so software can determine which code-path to take. + +* For on-chain BNS names, the `{address}` is the same as the Bitcoin address that owns the name. Currently, both version byte 0 and version byte 5 addresses are supported (addresses starting with `1` or `3`, meaning `p2pkh` and `p2sh` addresses). +* For off-chain BNS subdomains, the `{address}` has version byte 63 for subdomains owned by a single private key, and version byte 50 for subdomains owned by an m-of-n set of private keys. That is, subdomain DID addresses start with `S` or `M`, respectively. + +The `{index}` field for a subdomain's DID is distinct from the `{index}` field for a BNS name's DID, even if the same address created both names and subdomains. Example: + +* The name `abcdefgh123456.id` → `did:stack:v0:16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg-0` (first name created by that address) +* The subdomain `jude.statism.id` created by the same address → `did:stack:v0:SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i-0` + +Note: The address `SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i` encodes the same public key hash as `16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg`—the difference is base58check version byte (63 vs 0). diff --git a/docs/learn/network-fundamentals/mainnet-and-testnets.md b/docs/learn/network-fundamentals/mainnet-and-testnets.md new file mode 100644 index 0000000000..0753ce0395 --- /dev/null +++ b/docs/learn/network-fundamentals/mainnet-and-testnets.md @@ -0,0 +1,64 @@ +# Mainnet and Testnets + +

source: Hiro blog

+ +Stacks has both a mainnet and a few different testnets for different purposes. Mainnet and testnet are two completely different networks and tokens cannot be transferred between one or the other. + +### Mainnet + +Stacks mainnet is directly connected to Bitcoin mainnet and is the network where tokens have actual monetary worth. This is the production network and should be treated as such. + +You can view mainnet activity using [Hiro's block explorer](https://explorer.hiro.so/). + +### Primary and Nakamoto Testnets + +There are some notable differences between Primary Testnet and Nakamoto Testnet; this table can guide you in selecting the one most aligned with your needs. + +
AttributesNakamoto TestnetPrimary Testnet
Stacking Cycle Length3 days1 week
DescriptionBleeding edge, more frequent upgrades with Release Candidates.Stable release updates ONLY, the last step before Mainnet.
Usage Recommendations
  • Use this if you don’t mind frequent resets and would like to test the latest features as they’re released
  • Use this if you prefer faster feedback loops to test various stacking-signer scenarios
  • Use this if you prefer more stable releases and don’t want frequent resets and updates
  • Use this if you don't need to be among the first to test new features
  • Use this if you prefer longer Stacking cycles
LifespanNakamoto Testnet will remain available until sBTC goes live on MainnetThe Primary Testnet will exist and be maintained forever.
ExplorerNakamoto ExplorerPrimary Explorer
+ +### Important notes on the Primary Testnet + +* Core devs are working on a BTC Regtest Explorer. In the meantime, Wallet, Explorer, and API links to BTC transactions will lead you nowhere. This is expected and will be addressed. All STX transactions are available to track on the [Explorer](https://explorer.hiro.so/?chain=testnet). +* You can start onboarding your Signer, deploy contracts and test your Apps. All functionality from the previous testnet is available. +* Old testnet data is archived and will remain [available](https://explorer.hiro.so/?chain=testnet\&api=https://api.old.testnet.hiro.so) until the end of June 2024 +* Faucet and tSTX: + * The [Faucet address](https://explorer.hiro.so/address/ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2?chain=testnet) and limits stay the same. + * If you need more tSTX than the current daily limit to onboard your Signer on Primary Testnet, please reach out to your main point of contact in the ecosystem. + +### About Testnet + +The testnet is a separate blockchain from the Stacks mainnet analogous to a staging environnement. It's a network used by developers to test their apps, smart contracts, or changes to the protocol in a production-like environment. + +It produces blocks at roughly the same rate as mainnet; about 1 block every 10 minutes on average. The Stacks testnet is rarely reset. + +#### Faucets + +Testnet faucets provide you with free Stacks Token (STX) to test with. These are not the same as STX on mainnet and have no value. There are a couple of different options for getting testnet STX. + +{% tabs %} +{% tab title="Hiro" %} +You can get STX from the Hiro faucet on the [Hiro Explorer Sandbox](https://explorer.hiro.so/sandbox/faucet?chain=testnet), or using the [API](https://docs.hiro.so/api#tag/Faucets). + +To get STX tokens from within the Explorer Sandbox, navigate to the "Faucet" tab on the left and click "Request STX" button. + +You can also try out Stacking by clicking on `I want to stack`. + +{% hint style="info" %} +The Explorer Sandbox requires you to login with a Stacks wallet +{% endhint %} +{% endtab %} + +{% tab title="LearnWeb3" %} +Alternatively, you can use the [LearnWeb3 faucet](https://learnweb3.io/faucets). + +
+{% endtab %} +{% endtabs %} + +#### Testnet API + +The hosted Stacks Blockchain API for the testnet is available at this base URL: + +```shell +https://api.testnet.hiro.so/ +``` diff --git a/docs/learn/network-fundamentals/network-basics.md b/docs/learn/network-fundamentals/network-basics.md new file mode 100644 index 0000000000..449266d54b --- /dev/null +++ b/docs/learn/network-fundamentals/network-basics.md @@ -0,0 +1,97 @@ +# Network Basics + +

source: Hiro blog

+ +### Tokens + +Stacks (STX) tokens are the native tokens on the Stacks blockchain. The smallest fraction is one micro-STX. 1,000,000 micro-STX make one Stacks (STX). + +STX amounts should be stored as integers (8 bytes long), and represent the amount of micro-STX. + +### Fees + +Fees are used to incentivize miners to confirm transactions on the Stacks blockchain. The fee is calculated based on the estimate fee rate and the size of the raw transaction in bytes. The fee rate is a market determined variable. For the testnet, it is set to 1 micro-STX. + +Fee estimates can obtained through the [`GET /v2/fees/transfer`](https://docs.hiro.so/api#operation/get_fee_transfer) endpoint of the API. + +{% hint style="info" %} +Note that this example uses an external tool, [Hiro's Stacks API](https://www.hiro.so/stacks-api). You can also use the native [Stacks API ](https://app.gitbook.com/u/ZrQItu6D9bMKmf1HfsLTnGc05WZ2)if you would rather run your own node or connect to one. +{% endhint %} + +{% code title="terminal" %} +```bash +# for mainnet, replace `testnet` with `mainnet` +curl 'https://api.testnet.hiro.so/v2/fees/transfer' +``` +{% endcode %} + +The API will respond with the fee rate (as integer): + +```json +1 +``` + +[The Stacks Transactions JS library](https://github.com/hirosystems/stacks.js/tree/master/packages/transactions) supports fee estimation for: + +* token transfers (`estimateTransfer`) +* contract deploys (`estimateContractDeploy`) +* non read-only contract calls (`estimateContractFunctionCall`) + +{% hint style="info" %} +For an implementation using a different language than JavaScript, please review [this reference implementation](https://github.com/hirosystems/stacks.js/blob/master/packages/transactions/src/builders.ts#L97). +{% endhint %} + +### Nonces + +Every account carries a [nonce property](https://en.wikipedia.org/wiki/Cryptographic_nonce) that indicates the number of transactions processed for the given account. Nonces are one-time codes, starting at `0` for new accounts, and incremented by 1 on every transaction. + +Nonces are added to all transactions and help identify them in order to ensure transactions are processed in order and to avoid duplicated processing. + +{% hint style="info" %} +The consensus mechanism also ensures that transactions aren't "replayed" in two ways. First, nodes query its unspent transaction outputs (UTXOs) in order to satisfy their spending conditions in a new transaction. Second, messages sent between nodes review sequence numbers. +{% endhint %} + +When a new token transfer transaction is constructed, the most recent nonce of the account needs to be fetched and set. + +{% hint style="info" %} +The API provides an endpoint to [simplify nonce handling](https://docs.hiro.so/get-started/stacks-blockchain-api#nonce-handling). +{% endhint %} + +### Querying + +Stacks network details can be queried using the [Stacks Blockchain API](https://docs.hiro.so/get-started/stacks-blockchain-api). + +#### Health check + +The [status checker](https://status.stacks.org/) is a service that provides a user interface to quickly review the health of the Stacks blockchain. + +#### Network info + +The network information can be obtained using the [`GET /v2/info`](https://docs.hiro.so/api#operation/get_core_api_info) endpoint: + +{% code title="curl (testnet)" %} +```bash +# for mainnet, replace `testnet` with `mainnet` +curl 'https://api.testnet.hiro.so/v2/info' +``` +{% endcode %} + +Sample response: + +```js +{ + "peer_version": 385875968, + "burn_consensus": "826401d65cf3671210a3fb135d827d549c0b4d37", + "burn_block_height": 1972, + "stable_burn_consensus": "e27ea23f199076bc41a729d76a813e125b725f64", + "stable_burn_block_height": 1971, + "server_version": "blockstack-core 0.0.1 => 23.0.0.0 (master:bdd042242+, release build, linux [x86_64]", + "network_id": 2147483648, + "parent_network_id": 3669344250, + "stacks_tip_height": 933, + "stacks_tip": "1f601823fbcc5b6b2215b2ff59d2818fba61ee4a3cea426d8bc3dbb268005d8f", + "stacks_tip_burn_block": "54c56a9685545c45accf42b5dcb2787c97eda8185a1c794daf9b5a59d4807abc", + "unanchored_tip": "71948ee211dac3b241eb65d881637f649d0d49ac08ee4a41c29217d3026d7aae", + "exit_at_block_height": 28160 +} +``` diff --git a/docs/learn/network-fundamentals/sips.md b/docs/learn/network-fundamentals/sips.md new file mode 100644 index 0000000000..3c36e99eae --- /dev/null +++ b/docs/learn/network-fundamentals/sips.md @@ -0,0 +1,56 @@ +# SIPs + +

source: Hiro blog

+ +### Stacks Improvement Proposals (SIPs) + +Stacks improvement proposals (SIPs) are aimed at describing the implementation of the Stacks blockchain, as well as proposing improvements. + +The SIP process [(SIP-000)](https://github.com/stacksgov/sips/blob/main/sips/sip-000/sip-000-stacks-improvement-proposal-process.md) describes how to make a SIP and get it ratified. + +They should contain concise technical specifications of features or standards and the rationale behind it. SIPs are intended to be the primary medium for proposing new features, for collecting community input on a system-wide issue, and for documenting design decisions. + +The SIPs are located in the [stacksgov/sips](https://github.com/stacksgov/sips) repository as part of the [Stacks Community Governance organization](https://github.com/stacksgov). + +Anyone in the Stacks community can submit a SIP. + +{% hint style="info" %} +Stacks Improvement Proposals Community Calls Add the [weekly community SIP call](https://www.addevent.com/event/wS15955379) to your calendar. + +SIP Meeting calls are recorded and available [here](https://www.youtube.com/playlist?list=PLg717Ri_rTnx5kuaWqp3cUAtwQk_yzslT) + +More details of the meetings are available [here](https://github.com/stacksgov/sips/issues/79) +{% endhint %} + +### Ratified SIPSs + +* [x] [SIP 000: Improvement Proposal Process](https://github.com/stacksgov/sips/blob/main/sips/sip-000/sip-000-stacks-improvement-proposal-process.md) +* [x] [SIP 001: Burn Election](https://github.com/stacksgov/sips/blob/main/sips/sip-001/sip-001-burn-election.md) +* [x] [SIP 002: Clarity, a language for predictable smart contracts](https://github.com/stacksgov/sips/blob/main/sips/sip-002/sip-002-smart-contract-language.md) +* [x] [SIP 003: Peer Network](https://github.com/stacksgov/sips/blob/main/sips/sip-003/sip-003-peer-network.md) +* [x] [SIP 004: Cryptographic Commitment to Materialized Views](https://github.com/stacksgov/sips/blob/main/sips/sip-004/sip-004-materialized-view.md) +* [x] [SIP 005: Blocks, Transactions, and Accounts](https://github.com/stacksgov/sips/blob/main/sips/sip-005/sip-005-blocks-and-transactions.md) +* [x] [SIP 006: Clarity Execution Cost Assessment](https://github.com/stacksgov/sips/blob/main/sips/sip-006/sip-006-runtime-cost-assessment.md) +* [x] [SIP 007: Stacking Consensus](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md) +* [x] [SIP 008: Clarity Parsing and Analysis Cost Assessment](https://github.com/stacksgov/sips/blob/main/sips/sip-008/sip-008-analysis-cost-assessment.md) +* [x] [SIP 009: Standard Trait Definition for Non-Fungible Tokens](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md) +* [x] [SIP 010: Standard Trait Definition for Fungible Tokens](https://github.com/stacksgov/sips/blob/main/sips/sip-010/sip-010-fungible-token-standard.md) +* [x] [SIP 012: Burn Height Selection for a Network Upgrade to Introduce New Cost-Limits](https://github.com/stacksgov/sips/blob/main/sips/sip-012/sip-012-cost-limits-network-upgrade.md) +* [x] [SIP 013: Standard Trait Definition for Semi-Fungible Tokens](https://github.com/stacksgov/sips/blob/main/sips/sip-013/sip-013-semi-fungible-token-standard.md) +* [x] [SIP-015: Stacks Upgrade of Proof-of-Transfer and Clarity](https://github.com/stacksgov/sips/blob/main/sips/sip-015/sip-015-network-upgrade.md) +* [x] [SIP-016: Metadata for Tokens](https://github.com/stacksgov/sips/blob/main/sips/sip-016/sip-016-token-metadata.md) +* [x] [SIP-018: Signed Structured Data](https://github.com/stacksgov/sips/blob/main/sips/sip-018/sip-018-signed-structured-data.md) +* [x] [SIP-019: Notifications for Token Metadata Updates](https://github.com/stacksgov/sips/blob/main/sips/sip-019/sip-019-token-metadata-update-notifications.md) +* [x] [SIP-020: Bitwise Operations in Clarity](https://github.com/stacksgov/sips/blob/main/sips/sip-020/sip-020-bitwise-ops.md) +* [x] [SIP-022: Emergency Fix to PoX Stacking Increases](https://github.com/stacksgov/sips/blob/main/sips/sip-022/sip-022-emergency-pox-fix.md) +* [x] [SIP-023: Emergency Fix to Trait Invocation Behavior](https://github.com/stacksgov/sips/blob/main/sips/sip-023/sip-023-emergency-fix-traits.md) +* [x] [SIP-024: Emergency Fix to Data Validation and Serialization Behavior](https://github.com/stacksgov/sips/blob/main/sips/sip-024/sip-024-least-supertype-fix.md) + +### How to Get Involved + +There are several ways you can get involved with the SIP process: + +* **Join the weekly SIP Meeting call** listed [here](https://community.stacks.org/events). +* **SIP Editor**. SIP editors help SIP authors make sure their SIPs are well-formed and follow the right process. They help get SIPs ready for deep review by advancing it them from Draft to Accepted status. If you want to become a SIP editor, open an issue with your name and email to ask to be added to the list of SIP editors. +* **Join a CAB** (Consideration Advisory Board). SIPs fall under the purview of one or more considerations. A full list is in [this github](https://github.com/stacksgov/sips/tree/main/considerations) directory. Currently they are: Diversity, Economics, Ethics, Governance and Technical. Members of SIP consideration advisory boards use their domain expertise to give Accepted SIPs a deep read, and give the authors any/all feedback to help make the SIP workable. If you want to join a board, reach out to the board's chairperson via the listed contact information. +* **Steering Committee**. The Steering Committee organizes the consideration advisory boards and votes to advance Recommended SIPs to Activation-in-Progress status, and then to either Ratified or Rejected status. Once they are in the process of being activated, they use a SIP's Activation section to determine whether or not the Stacks ecosystem has ratified or rejected the SIP. Joining this committee requires the consent of the Stacks Foundation board. diff --git a/docs/learn/network-fundamentals/technical-specifications/README.md b/docs/learn/network-fundamentals/technical-specifications/README.md new file mode 100644 index 0000000000..625f21733a --- /dev/null +++ b/docs/learn/network-fundamentals/technical-specifications/README.md @@ -0,0 +1,95 @@ +# Technical Specifications + +### Consensus + +* Proof of Transfer (PoX) as described in [SIP-007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md) +* Network will transition to Proof of Burn (PoB) as described in [SIP-001](https://github.com/stacksgov/sips/blob/main/sips/sip-001/sip-001-burn-election.md) after 10 years. [Learn more about Proof-of-Burn in SIP-001](https://github.com/stacksgov/sips/blob/main/sips/sip-001/sip-001-burn-election.md). +* Threat model + * 51% of malicious Bitcoin mining power can reorg the Stacks chain or perform a double-spend attack + * Chain can halt if Stackers cannot meet 70% consensus on block validity +* Different actors and their roles + * Stacks Miners package transactions into blocks and propose them to stackers + * Stacks Holders may alter the calculation of block limits (subject to a miner veto) and may vote to disable Proof-of-Transfer rewards for a reward cycle. + * Stackers validate and append blocks to the chain and validate sBTC deposit and withdrawal transactions + +### Proof of Transfer Mining + +* Coinbase reward schedule: + * 1000 STX/block for first 4 years + * 500 STX/block for following 4 years + * 250 STX/block for subsequent 4 years + * 125 STX/block in perpetuity after that +* Coinbase rewards accumulate for "missed sortitions": If a Bitcoin block has no sortition (at height N), then any Stacks block mined in a subsequent sortition that builds off of any Stacks chain tip that existed at the penultimate sortition (at height N-1) may claim its coinbase. This encourages miners to keep mining even if Bitcoin fees are high. +* Initial mining bonus: This is a special case of the above to incentivize early miners. Coinbase for all burnchain blocks between the first burn block height (to be chosen by independent miners as part of the Stacks 2.0 launch) and the first sortition winner accumulate and are distributed to miners over a fixed window (to be determined). For instance, say burn block height is 10,000 and first sortition is at block 10500 and distribution window is 100 blocks, then coinbase for the first 500 blocks (10,500 - 10,000) will be distributed evenly to miners who win sortition over the subsequent 100 blocks. +* Reward maturity window: 100 blocks, meaning leaders will earn the coinbase reward 100 blocks after the block they successfully mine. +* Block interval: Stacks blockchain produces fast blocks roughly every 10 seconds with a miner tenure change occurring every Bitcoin block +* BTC commitment: Miners must commit at least 11,000 satoshis (5,500 sats / [UTXO output](https://learnmeabitcoin.com/technical/utxo)); 2 outputs / block) to avoid "dust." +* For more details, see Block Production. + +### Stacking + +{% stepper %} +{% step %} +### Prepare phase + +An "anchor block" is chosen. The qualifying set of addresses ("reward set") is determined based on the snapshot of the chain at the anchor block. Length of prepare phase is 100 blocks. Stacking commitments need to be confirmed before this phase starts. +{% endstep %} + +{% step %} +### Reward phase + +Miner BTC commitments are distributed amongst the reward set. Reward cycle length is 2000 BTC blocks (\~2 weeks). +{% endstep %} +{% endstepper %} + +* Two reward addresses / block, for a total of 4000 addresses every reward cycle. The addresses are chosen using a VRF (verifiable random function), so each node can deterministically arrive at the same reward addresses for a given block. +* Stacking threshold: 0.025% of the participating amount of STX when participation is between 25% and 100% and when participation is below 25%, the threshold level is always 0.00625 of the liquid supply of STX. +* Delegation: An STX address can designate another address to participate in Stacking on its behalf. [Relevant section in SIP-007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md#stacker-delegation). +* Pooling: STX holders that individually do not meet the Stacking threshold can pool together their holdings to participate in Stacking. To do this, STX holders must set the (optional) reward address to the "delegate address." For more details, see [this reference](https://docs.stacks.co/references/stacking-contract#delegate-stx). +* Legacy, SegWit, Native Segwit, and Taproot addresses are supported + +### Accounts and Addresses + +* Transactions in the Stacks blockchain originate from, are paid for by, and execute under the authority of accounts +* An account is fully specified by its address + nonce + assets +* Address contains 2 or 3 fields: 1 byte version, 20 byte public key hash (RIPEMD160(SHA256(input))), optional name (variable length, max 128 bytes) +* Two types of accounts: standard accounts are owned by one or more private keys; contract accounts are materialized when a smart-contract is instantiated (specified by the optional name field above) +* Nonce counts number of times an account has authorized a transaction. Starts at 0, valid authorization must include the _next_ nonce value. +* Assets are a map of all asset types -- STX, any on-chain assets specified by a Clarity contract (for example NFTs) -- to quantities owned by that account. +* Accounts need not be explicit "created" or registered; all accounts implicitly exist and are instantiated on first-use. + +### Transactions + +* Transaction types: coinbase, token-transfer, contract-deploy, contract-call, tenure-change. +* Only standard accounts (not contracts) can pay transaction fees. +* Transaction execution is governed by: + +{% stepper %} +{% step %} +### Originating account + +The account that creates, authorizes and sends the transaction. +{% endstep %} + +{% step %} +### Paying account + +The account that is billed by the leader for the cost of validating and executing the transaction. +{% endstep %} + +{% step %} +### Sending account + +The account that identifies who is currently executing the transaction: this can change as a transaction executes via the `as-contract` Clarity function. +{% endstep %} +{% endstepper %} + +* Transactions can be batched or streamed into blocks. The behavior can be controlled by the anchor mode of a transaction. With streaming (microblocks), a faster confirmation time is possible. +* Two types of authorizations: standard authorization is where originating account is the same as paying account. _Sponsored_ authorization is where originating account and paying account are distinct. For instance, developers or service providers could pay for users to call their smart-contracts. +* For sponsored authorization, first a user signs with the originating account and then a sponsor signs with the paying account. +* Mempool limit for concurrent pending transactions is 25 per account +* Pending mempool transactions will be garbage-collected [256 blocks after receipt](https://github.com/stacks-network/stacks-blockchain/blob/master/src/core/mempool.rs#L62). With 10 minutes target block time, this would equal \~42 hours +* [Learn more about transaction encoding in SIP-005](https://github.com/stacksgov/sips/blob/main/sips/sip-005/sip-005-blocks-and-transactions.md#transaction-encoding) +* [Transaction signing and verification are described in SIP-005](https://github.com/stacksgov/sips/blob/main/sips/sip-005/sip-005-blocks-and-transactions.md#transaction-signing-and-verifying) +* All transactions impacting account balance are atomic, a transfer operation can not increment one account’s balance without decrementing another’s. However, transactions that perform multiple account actions (for example, transferring from multiple accounts) may partially complete. +* Transactions can include a memo string (max 34 bytes) diff --git a/docs/learn/network-fundamentals/technical-specifications/audits.md b/docs/learn/network-fundamentals/technical-specifications/audits.md new file mode 100644 index 0000000000..a95d7d9c6c --- /dev/null +++ b/docs/learn/network-fundamentals/technical-specifications/audits.md @@ -0,0 +1,59 @@ +# Audits + +

source: Hiro blog

+ +_All 'high' or 'critical' issues listed in audits have either been mitigated or otherwise made obsolete, even if the report states otherwise._ + +#### sBTC + +{% file src="../../.gitbook/assets/Clarity Alliance - sBTC.pdf" %} + +{% file src="../../.gitbook/assets/CoinFabrik - WSTS.pdf" %} + +{% file src="../../.gitbook/assets/Ottersec - sBTC Withdrawal.pdf" %} + +{% file src="../../.gitbook/assets/Ottersec - WSTS.pdf" %} + + + +#### Stacks Core + +{% file src="../../.gitbook/assets/CoinFabrik - Signer Binary.pdf" %} + +{% file src="../../.gitbook/assets/CoinFabrik - StackerDB.pdf" %} + +{% file src="../../.gitbook/assets/CoinFabrik - Stacks LibSigner.pdf" %} + +{% file src="../../.gitbook/assets/Coinfabrik - Stacks PoX.pdf" %} + +{% file src="../../.gitbook/assets/CoinFabrik - Stacks Signer Audit.pdf" %} + +{% file src="../../.gitbook/assets/Quantstamp - Network State Machine.pdf" %} + + + +#### Audits are just part of the story + +For any project, layers of security are crucial. Audits represent one layer, while core developers and contributors collaborate to provide many more. Notable security programs, designs, and partners beyond audits include: + +* Embedded security researchers [via Asymmetric Research](https://stacks.org/asymmetric-joins-stacks-ecosystem) +* Attackathon programs in partnership with Immunefi +* sBTC’s decentralized [network of validators/signers](https://www.stacks.co/sbtc) (removing the need to entrust a single entity and mitigating counterparty risk) +* Stacks’ underlying design that offers 100% Bitcoin finality, securing sBTC at the consensus level of a $2.5 billion network. +* Support at the app layer via [Hypernative](https://hackernoon.com/hypernative-bolsters-bitcoin-l2-security-as-stacks-ecosystem-gets-real-time-protection) +* Bitcoin L2 Labs' [whitehat security program](https://bitcoinl2-labs.github.io/2024/06/04/orange-hats.html) +* Stacks Foundation's partnership with Staking Defense League +* Stacks Founation's ongoing [Immunefi bug bounty program](https://immunefi.com/bug-bounty/stacks/information/) +* Dedicated Stacks Foundation Residents focused exclusively on fuzz and penetration testing (created [Rendezvous](https://stacks-network.github.io/rendezvous/)) + +#### Other audits + +{% file src="../../.gitbook/assets/Clarity Alliance - sBTC Rewards Program.pdf" %} + +{% file src="../../.gitbook/assets/Blockstack_Desktop_Wallet_Pentest_Report_11-12-2020.pdf" %} + +{% file src="../../.gitbook/assets/NCC_Group_Stacks_Blockchain_Audit_Report_2020-11-23_v1.0.pdf" %} + +{% file src="../../.gitbook/assets/NCC_Group_Stacks_Wallet_Report_2020-11-17_v1.0.pdf" %} + +Trail of Bits Report, Stacks Blockchain (No PDF, [Github Issues List provided](https://github.com/diwakergupta/stacks-blockchain-tob-audit/issues)) diff --git a/docs/learn/sbtc/README.md b/docs/learn/sbtc/README.md new file mode 100644 index 0000000000..e7c1d9c4fa --- /dev/null +++ b/docs/learn/sbtc/README.md @@ -0,0 +1,52 @@ +# sBTC + +
+ +### Introduction to sBTC + +sBTC is a SIP-010 token on the Stacks blockchain that represents Bitcoin (BTC) in a 1:1 ratio. It enables Bitcoin holders to participate in DeFi applications and other smart contract functionalities while maintaining a peg to the underlying Bitcoin. + +#### Purpose + +The primary purpose of sBTC is to bridge Bitcoin to DeFi via the Stacks blockchain, providing Bitcoin holders with access to the rich functionality of smart contracts without sacrificing the security and value of their BTC holdings. + +#### Key Benefits + +1. **Bitcoin Compatibility**: Allows Bitcoin holders to participate in the Stacks ecosystem without selling their BTC. +2. **Quick Conversions**: Facilitates rapid movement between BTC and sBTC (within 3 Bitcoin blocks for deposit, 6 for withdrawal). +3. **Decentralized Management**: Initially utilizes a set of 15 community-chosen signers for maintaining the peg wallet. +4. **Community Governance**: Involves the community in key decisions, such as selecting the initial signing set. + +### Key Concepts + +Understanding sBTC requires familiarity with several key concepts: + +#### sBTC + +sBTC is a [SIP-010](https://github.com/stacksgov/sips/blob/main/sips/sip-010/sip-010-fungible-token-standard.md) token on the Stacks Blockchain that can be converted back to BTC on the Bitcoin Blockchain. The key property of sBTC is its 1:1 peg to Bitcoin, meaning 1 sBTC is always equivalent to 1 BTC. + +#### sBTC UTXO + +The sBTC UTXO is the single unspent transaction output (UTXO) on the Bitcoin blockchain that holds the entire BTC balance pegged into sBTC. This UTXO is managed and maintained by the set of sBTC Signers. + +#### sBTC Signer + +In sBTC, the sBTC Signer is a signer entity separate from the Stacks Nakamoto signer. sBTC signer responsibilities include: + +* Signing sBTC operations +* Communicating with the sBTC contracts on the Stacks chain +* Managing the sBTC UTXO + +#### sBTC Signer Set + +The sBTC Signer Set is the group of all sBTC signers. This set has full democratic access to the sBTC UTXO and is responsible for maintaining the security of the peg wallet. The signers also have the ability to rotate their private keys for enhanced security. + +#### Emily API + +Emily is an API that helps facilitate and supervise the sBTC Bridge in addition to serving as a programmatic liaison between sBTC users and signers. + +#### SIP-010 Token + +sBTC adheres to the [SIP-010](https://github.com/stacksgov/sips/blob/main/sips/sip-010/sip-010-fungible-token-standard.md) standard for fungible tokens on the Stacks blockchain. This ensures compatibility with wallets and applications that support the SIP-010 standard. + +Understanding these concepts is crucial for grasping the overall architecture and functionality of sBTC. In the following sections, we'll explore how these concepts come together to create sBTC. diff --git a/docs/learn/sbtc/auxiliary-features/README.md b/docs/learn/sbtc/auxiliary-features/README.md new file mode 100644 index 0000000000..b0e7df2d63 --- /dev/null +++ b/docs/learn/sbtc/auxiliary-features/README.md @@ -0,0 +1,17 @@ +# Auxiliary Features + +This section covers additional features that enhance the functionality and security of the sBTC system. These auxiliary features contribute to the overall robustness and user-friendliness of the sBTC ecosystem. + +{% stepper %} +{% step %} +### Transaction Fee Sponsorship + +Allowing sBTC transactions to be sponsored. +{% endstep %} + +{% step %} +### Signer Wallet Rotation + +Enabling secure key rotation for sBTC Signers. +{% endstep %} +{% endstepper %} diff --git a/docs/learn/sbtc/auxiliary-features/signer-wallet-rotation.md b/docs/learn/sbtc/auxiliary-features/signer-wallet-rotation.md new file mode 100644 index 0000000000..fd4ec92eea --- /dev/null +++ b/docs/learn/sbtc/auxiliary-features/signer-wallet-rotation.md @@ -0,0 +1,53 @@ +# Signer Wallet Rotation + +Signer wallet rotation allows sBTC signers to update their private keys and modify the signer set composition. This mechanism is how the network maintains security over time and adapts to changing participants. + +## How it works + +The sBTC system uses a multi-signature wallet on Bitcoin to custody BTC deposits. When the system needs to change who controls this wallet—either by rotating keys or changing the signer set—it uses the rotation mechanism. + +As of v1.1.0, the system supports: + +* Adding new signers to the set +* Removing existing signers +* Replacing specific signers +* Rotating keys for current signers + +When signers agree on a new configuration, the system automatically runs a Distributed Key Generation (DKG) protocol to create new signing shares for the updated group. Once complete, control of the sBTC wallet transfers to the new configuration. + +## The rotation process + +{% stepper %} +{% step %} +### Signers coordinate off-chain + +Signers agree on the new signer set. +{% endstep %} + +{% step %} +### Update configuration + +Each signer operator updates their configuration with the newly decided set. +{% endstep %} + +{% step %} +### DKG runs automatically + +Once all signers have configured the exact same set of signers, DKG occurs automatically to generate new signing shares. +{% endstep %} + +{% step %} +### New signer set takes control + +The new signer set takes control of the sBTC wallet. +{% endstep %} +{% endstepper %} + +The Bitcoin UTXOs remain under continuous control throughout this process—there's no moment where funds are unsecured. + +## When rotation occurs + +Key rotation typically happens when: + +* **Signer changes**: When someone leaves the signer set or new participants join, the configuration must be updated to reflect the new membership. +* **Security events**: If a key might be compromised, an emergency rotation can be initiated to secure the system. diff --git a/docs/learn/sbtc/auxiliary-features/transaction-fee-sponsorship.md b/docs/learn/sbtc/auxiliary-features/transaction-fee-sponsorship.md new file mode 100644 index 0000000000..18bd63f127 --- /dev/null +++ b/docs/learn/sbtc/auxiliary-features/transaction-fee-sponsorship.md @@ -0,0 +1,56 @@ +# Transaction Fee Sponsorship + +Transaction Fee Sponsorship is a feature in sBTC that allows users to pay for Stacks transaction fees using sBTC instead of STX. + +## Overview + +* sBTC transactions on Stacks can be sponsored in return for some sBTC. +* This feature improves user experience by allowing sBTC holders to use their tokens for gas fees. + +## Implementation + +The fee sponsorship system is implemented using the approach suggested in [stacks-network/stacks-core#4235](https://github.com/stacks-network/stacks-core/issues/4235). + +{% stepper %} +{% step %} +### Sponsor support for fees + +sBTC users can get support from existing STX holders for transaction fees. +{% endstep %} + +{% step %} +### Sponsor receives sBTC + +The sponsor pays the STX fee and receives sBTC in return. +{% endstep %} +{% endstepper %} + +## User Experience + +From a user's perspective: + +{% stepper %} +{% step %} +### Opt into fee sponsorship + +When initiating an sBTC transaction, they can opt for fee sponsorship. +{% endstep %} + +{% step %} +### Agree to sponsorship terms + +The user agrees to pay a small amount of sBTC for the sponsorship. +{% endstep %} + +{% step %} +### Transaction processed + +The transaction is then processed with the fees paid in STX by the sponsor. +{% endstep %} +{% endstepper %} + +## Benefits + +* Improved UX: Users don't need to hold STX to use sBTC. +* Lower Barrier to Entry: New users can start using sBTC without first acquiring STX. +* Flexibility: Provides an additional option for handling transaction fees. diff --git a/docs/learn/sbtc/clarity-contracts/README.md b/docs/learn/sbtc/clarity-contracts/README.md new file mode 100644 index 0000000000..678d4c8e89 --- /dev/null +++ b/docs/learn/sbtc/clarity-contracts/README.md @@ -0,0 +1,45 @@ +# Clarity Contracts + +### Deployed Mainnet Contracts + +* [sbtc-token](https://explorer.hiro.so/txid/SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token?chain=mainnet) +* [sbtc-registry](https://explorer.hiro.so/txid/SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry?chain=mainnet) +* [sbtc-deposit](https://explorer.hiro.so/txid/SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-deposit?chain=mainnet) +* [sbtc-withdrawal](https://explorer.hiro.so/txid/SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-withdrawal?chain=mainnet) +* [sbtc-bootstrap-signers](https://explorer.hiro.so/txid/SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-bootstrap-signers?chain=mainnet) + +This graph summarizes the Clarity portion of the sBTC protocol. + +
+ +### sBTC Clarity Contracts + +At a high level, the sBTC Clarity contracts are responsible for the following: + +#### sbtc-bootstrap signers + +Core contract for meta signer functionality such as registration & the rotation process. + +#### sbtc-deposit + +Processing contract called by the signers to record a consumed Bitcoin transaction & mint some amount of sBTC to a principal contained in the payload. + +#### sbtc-registry + +State storage for maintaining upgradability across protocol. + +#### sbtc-withdrawal + +Interaction points for users and signers to update withdrawal request state. + +### User Types + +In addition to the contracts themselves, there are two main user types that will interact with these contracts. + +#### Signer + +A signer that is part of the current sBTC signer set. More information on signers and their role in sBTC can be found in the [Signer Process Walkthrough](../walkthroughs/signer-process-walkthrough.md). + +#### Wallet + +A participant in the Stacks/Bitcoin ecosystem that wants to deposit/withdraw/use sbtc. diff --git a/docs/learn/sbtc/clarity-contracts/sbtc-bootstrap-signers.md b/docs/learn/sbtc/clarity-contracts/sbtc-bootstrap-signers.md new file mode 100644 index 0000000000..d7bb7cb743 --- /dev/null +++ b/docs/learn/sbtc/clarity-contracts/sbtc-bootstrap-signers.md @@ -0,0 +1,89 @@ +--- +hidden: true +--- + +# sbtc bootstrap signers + +I can convert and optimize that page for GitBook — please paste the full page content (or provide the page URL) you want imported. + +If you don't have it ready, here are two quick options I can produce now to get started — tell me which you want or paste the content: + +1. Empty GitBook-ready markdown template for "sBTC Bootstrap Signers" you can fill in. +2. Full conversion if you paste the original page content or URL. + +Template (choose 1 or paste content): + +*** + +## sBTC Bootstrap Signers + +Short description: Briefly describe the purpose of this page and what a bootstrap signer is. + +### Signer list + +Provide signer identities, keys, and any relevant metadata here. + +* Signer name + * Public key: + * Contact: + * Notes: + +(Repeat for each signer) + +### Setup / Installation + +Use a stepper for multi-step setup instructions. + +{% stepper %} +{% step %} +### Prepare environment + +Describe prerequisites and environment setup. +{% endstep %} + +{% step %} +### Install dependencies + +Commands and configuration. +{% endstep %} + +{% step %} +### Start the signer + +Service start commands, verification steps. +{% endstep %} +{% endstepper %} + +### Configuration examples + +Include config file examples and code blocks. + +{% code title="signer-config.toml" %} +```toml +# Example config +[signer] +key = "-----BEGIN PUBLIC KEY-----..." +``` +{% endcode %} + +### Security and key handling + +Best practices for key generation, storage, and rotation. + +{% hint style="warning" %} +Never share private keys. Use hardware security modules (HSMs) or secure enclaves where possible. +{% endhint %} + +### FAQ + +
+ +How do I rotate a signer key? + +Describe key rotation steps. + +
+ +*** + +If you paste the original content, I'll convert it exactly into GitBook-optimized markdown (tabs, steppers, expandables, etc.) preserving all links and URLs. Which would you like? diff --git a/docs/learn/sbtc/clarity-contracts/sbtc-deposit.md b/docs/learn/sbtc/clarity-contracts/sbtc-deposit.md new file mode 100644 index 0000000000..42e127fe84 --- /dev/null +++ b/docs/learn/sbtc/clarity-contracts/sbtc-deposit.md @@ -0,0 +1,130 @@ +# sBTC Deposit + +## Overview + +The [sBTC Deposit contract](https://github.com/stacks-network/sbtc/blob/main/contracts/contracts/sbtc-deposit.clar) (`sbtc-deposit.clar`) manages the deposit process for the sBTC system. It handles the validation and minting of sBTC tokens when users deposit Bitcoin, and interacts with the sBTC Registry contract to update the protocol state. + +## Constants + +* `txid-length`: The required length of a transaction ID (32 bytes). +* `dust-limit`: The minimum amount for a valid deposit (546 satoshis). + +## Error Constants + +* `ERR_TXID_LEN` (u300): Indicates that the provided transaction ID is not the correct length. +* `ERR_DEPOSIT_REPLAY` (u301): Signifies an attempt to replay a deposit that has already been completed. +* `ERR_LOWER_THAN_DUST` (u302): Indicates that the deposit amount is below the dust limit. +* `ERR_DEPOSIT_INDEX_PREFIX`: Used as a prefix for deposit-related errors in batch processing. +* `ERR_DEPOSIT` (u303): General deposit error. +* `ERR_INVALID_CALLER` (u304): Indicates that the caller is not authorized to perform the operation. + +*** + +## Public Functions + +### complete-deposit-wrapper + +Processes a single deposit request. + +* Parameters: + * `txid`: `(buff 32)` - The Bitcoin transaction ID + * `vout-index`: `uint` - The output index of the deposit transaction + * `amount`: `uint` - The amount of sBTC to mint (in satoshis) + * `recipient`: `principal` - The Stacks address to receive the minted sBTC +* Returns: `(response bool uint)` + +{% stepper %} +{% step %} +### Validation and authorization + +1. Verifies that the caller is the current signer principal. +2. Checks that the deposit amount is above the dust limit. +3. Validates the transaction ID length. +{% endstep %} + +{% step %} +### Replay protection + +4. Ensures the deposit hasn't been processed before (prevents replay). +{% endstep %} + +{% step %} +### Execution + +5. Mints sBTC tokens to the recipient via `.sbtc-token`'s `protocol-mint`. +6. Updates the deposit state in the sBTC Registry contract via `.sbtc-registry`'s `complete-deposit`. +{% endstep %} +{% endstepper %} + +*** + +### complete-deposits-wrapper + +Processes multiple deposit requests in a single transaction. + +* Parameters: + * `deposits`: `(list 650 {txid: (buff 32), vout-index: uint, amount: uint, recipient: principal})` - List of deposit data +* Returns: `(response uint uint)` + +{% stepper %} +{% step %} +### Authorization + +1. Verifies that the caller is the current signer principal. +{% endstep %} + +{% step %} +### Batch processing + +2. Iterates through the list of deposits, processing each one using the `complete-individual-deposits-helper` function. +{% endstep %} +{% endstepper %} + +*** + +## Private Functions + +### complete-individual-deposits-helper + +Helper function to process individual deposits within the batch operation. + +* Parameters: + * `deposit`: `{txid: (buff 32), vout-index: uint, amount: uint, recipient: principal}` - Single deposit data + * `helper-response`: `(response uint uint)` - Accumulator for tracking processed deposits +* Returns: `(response uint uint)` + +{% stepper %} +{% step %} +### Call deposit wrapper + +1. Calls `complete-deposit-wrapper` for the individual deposit. +{% endstep %} + +{% step %} +### Success handling + +2. If successful, increments the processed deposit count. +{% endstep %} + +{% step %} +### Error handling + +3. If an error occurs, it's propagated with additional index information (using `ERR_DEPOSIT_INDEX_PREFIX` or related error constants). +{% endstep %} +{% endstepper %} + +*** + +## Interactions with Other Contracts + +* `.sbtc-registry`: Calls `get-current-signer-data`, `get-completed-deposit`, and `complete-deposit` to manage deposit state. +* `.sbtc-token`: Calls `protocol-mint` to create new sBTC tokens. + +*** + +## Security Considerations + +1. Access Control: Only the current signer principal can call the deposit completion functions. +2. Replay Prevention: The contract checks for previously processed deposits to prevent replay attacks. +3. Dust Limit: Enforces a minimum deposit amount to prevent spam and ensure economic viability. +4. Transaction ID Validation: Ensures the provided transaction ID is the correct length. diff --git a/docs/learn/sbtc/clarity-contracts/sbtc-registry.md b/docs/learn/sbtc/clarity-contracts/sbtc-registry.md new file mode 100644 index 0000000000..39934e27f7 --- /dev/null +++ b/docs/learn/sbtc/clarity-contracts/sbtc-registry.md @@ -0,0 +1,224 @@ +# sBTC Registry + +## Overview + +The [sBTC Registry contract](https://github.com/stacks-network/sbtc/blob/main/contracts/contracts/sbtc-registry.clar) (`sbtc-registry.clar`) serves as the central registry for the sBTC system. It manages withdrawal requests, completed deposits, and the current signer set. This contract is crucial for maintaining the state and coordinating operations within the sBTC ecosystem. + +## Error Constants + +* `ERR_UNAUTHORIZED` (u400): Indicates unauthorized access. +* `ERR_INVALID_REQUEST_ID` (u401): Signifies an invalid withdrawal request ID. +* `ERR_AGG_PUBKEY_REPLAY` (u402): Indicates an attempt to replay an aggregate public key. +* `ERR_MULTI_SIG_REPLAY` (u403): Signifies an attempt to replay a multi-signature address. + +## State Variables + +* `last-withdrawal-request-id`: Tracks the latest withdrawal request ID. +* `current-signature-threshold`: Stores the current threshold for required signatures. +* `current-signer-set`: Maintains a list of current signer public keys. +* `current-aggregate-pubkey`: Holds the current aggregate public key. +* `current-signer-principal`: Stores the current signer's principal address. + +## Data Maps + +### withdrawal-requests + +Stores withdrawal request details indexed by request ID. + +* Fields: + * `amount`: Amount of sBTC being withdrawn (in sats) + * `max-fee`: Maximum fee for the withdrawal + * `sender`: Principal of the sender + * `recipient`: BTC recipient address (version and hashbytes) + * `block-height`: Burn block height where the request was created + +### withdrawal-status + +Tracks the status of withdrawal requests indexed by request ID. + +* Value: `bool` (true if accepted, false if rejected, none if pending) + +### completed-deposits + +Records completed deposit transactions to prevent replay attacks. + +* Key: `{txid: (buff 32), vout-index: uint}` +* Value: `{amount: uint, recipient: principal}` + +### aggregate-pubkeys + +Tracks used aggregate public keys to prevent replay attacks. + +* Key: `(buff 33)` (aggregate public key) +* Value: `bool` + +### multi-sig-address + +Tracks used multi-signature addresses to prevent replay attacks. + +* Key: `principal` (multi-sig address) +* Value: `bool` + +### protocol-contracts + +Stores authorized protocol contract addresses. + +* Key: `principal` (contract address) +* Value: `bool` + +## Read-only Functions + +### get-withdrawal-request + +Retrieves a withdrawal request by its ID. + +* Parameters: + * `id`: `uint` +* Returns: `(optional {amount: uint, max-fee: uint, sender: principal, recipient: {version: (buff 1), hashbytes: (buff 32)}, block-height: uint, status: (optional bool)})` + +### get-completed-deposit + +Fetches a completed deposit by transaction ID and output index. + +* Parameters: + * `txid`: `(buff 32)` + * `vout-index`: `uint` +* Returns: `(optional {amount: uint, recipient: principal})` + +### get-current-signer-data + +Returns current signer set information. + +* Returns: `{current-signer-set: (list 128 (buff 33)), current-aggregate-pubkey: (buff 33), current-signer-principal: principal, current-signature-threshold: uint}` + +### get-current-aggregate-pubkey + +Returns the current aggregate public key. + +* Returns: `(buff 33)` + +### get-current-signer-principal + +Returns the current signer's principal. + +* Returns: `principal` + +### get-current-signer-set + +Returns the current set of signer public keys. + +* Returns: `(list 128 (buff 33))` + +## Public Functions + +### create-withdrawal-request + +Creates a new withdrawal request. Only callable by protocol contracts. + +* Parameters: + * `amount`: `uint` + * `max-fee`: `uint` + * `sender`: `principal` + * `recipient`: `{version: (buff 1), hashbytes: (buff 32)}` + * `height`: `uint` +* Returns: `(response uint uint)` + +### complete-withdrawal-accept + +Marks a withdrawal request as accepted. + +* Parameters: + * `request-id`: `uint` + * `bitcoin-txid`: `(buff 32)` + * `output-index`: `uint` + * `signer-bitmap`: `uint` + * `fee`: `uint` +* Returns: `(response bool uint)` + +### complete-withdrawal-reject + +Marks a withdrawal request as rejected. + +* Parameters: + * `request-id`: `uint` + * `signer-bitmap`: `uint` +* Returns: `(response bool uint)` + +### complete-deposit + +Records a completed deposit transaction. + +* Parameters: + * `txid`: `(buff 32)` + * `vout-index`: `uint` + * `amount`: `uint` + * `recipient`: `principal` +* Returns: `(response bool uint)` + +### rotate-keys + +Updates the signer set, multi-sig principal, and aggregate public key. + +* Parameters: + * `new-keys`: `(list 128 (buff 33))` + * `new-address`: `principal` + * `new-aggregate-pubkey`: `(buff 33)` + * `new-signature-threshold`: `uint` +* Returns: `(response (buff 33) uint)` + +## Private Functions + +### increment-last-withdrawal-request-id + +Increments and returns the next withdrawal request ID. + +* Returns: `uint` + +### is-protocol-caller + +Checks if the caller is an authorized protocol contract. + +* Returns: `(response bool uint)` + +### validate-protocol-caller + +Validates if a given principal is an authorized protocol contract. + +* Parameters: + * `caller`: `principal` +* Returns: `(response bool uint)` + +## Events + +The contract emits events (via `print`) for important actions: + +* Withdrawal request creation: "withdrawal-create" +* Withdrawal acceptance: "withdrawal-accept" +* Withdrawal rejection: "withdrawal-reject" +* Deposit completion: "completed-deposit" + +{% hint style="info" %} +Events are emitted via `print` statements in the contract for the actions listed above. +{% endhint %} + +## Security Considerations + +{% stepper %} +{% step %} +### Access Control + +Only authorized protocol contracts can call certain functions. +{% endstep %} + +{% step %} +### Replay Prevention + +The contract prevents replay attacks on deposits, aggregate public keys, and multi-signature addresses. +{% endstep %} + +{% step %} +### State Management + +The contract carefully manages the state of withdrawals and the current signer set. +{% endstep %} +{% endstepper %} diff --git a/docs/learn/sbtc/clarity-contracts/sbtc-signers.md b/docs/learn/sbtc/clarity-contracts/sbtc-signers.md new file mode 100644 index 0000000000..5b8cce5065 --- /dev/null +++ b/docs/learn/sbtc/clarity-contracts/sbtc-signers.md @@ -0,0 +1,148 @@ +# sBTC Signers + +Overview + +The [sBTC Signers contract](https://github.com/stacks-network/sbtc/blob/main/contracts/contracts/sbtc-bootstrap-signers.clar) (`sbtc-bootstrap-signers.clar`) manages the signer set for the sBTC system. It handles rotation of signer keys and provides utilities for generating multisig addresses. + +Constants + +* `key-size`: The required length of public keys (33 bytes). + +Error Constants + +* `ERR_KEY_SIZE_PREFIX`: Prefix for key size errors in batch processing. +* `ERR_KEY_SIZE` (u200): Indicates that a provided key is not the correct length. +* `ERR_INVALID_CALLER` (u201): Signifies that the function caller is not the current signer principal. +* `ERR_SIGNATURE_THRESHOLD` (u202): Indicates an invalid signature threshold (must be >50% and ≤100% of total signer keys). + +Public Functions + +rotate-keys-wrapper + +Rotates the keys of the signers. Called when the signer set is updated. + +* Parameters: + * `new-keys`: `(list 128 (buff 33))` - List of new signer public keys + * `new-aggregate-pubkey`: `(buff 33)` - New aggregate public key + * `new-signature-threshold`: `uint` - New signature threshold +* Returns: `(response (buff 33) uint)` + +Function flow: + +{% stepper %} +{% step %} +### Validate signature threshold + +Ensure the new signature threshold is valid (must be >50% and ≤100% of total signer keys). +{% endstep %} + +{% step %} +### Verify caller + +Verify that the caller is the current signer principal. +{% endstep %} + +{% step %} +### Validate keys + +Check the length of each new key and the aggregate public key (must be 33 bytes). +{% endstep %} + +{% step %} +### Update registry + +Call the sBTC Registry contract to update the keys and address. +{% endstep %} +{% endstepper %} + +Read-only Functions + +pubkeys-to-spend-script + +Generates the p2sh redeem script for a multisig. + +* Parameters: + * `pubkeys`: `(list 128 (buff 33))` - List of public keys + * `m`: `uint` - Number of required signatures +* Returns: `(buff 1024)` - The p2sh redeem script + +pubkeys-to-hash + +Computes the hash160 of the p2sh redeem script. + +* Parameters: + * `pubkeys`: `(list 128 (buff 33))` - List of public keys + * `m`: `uint` - Number of required signatures +* Returns: `(buff 20)` - The hash160 of the redeem script + +pubkeys-to-principal + +Generates a principal (Stacks address) from a set of pubkeys and an m-of-n threshold. + +* Parameters: + * `pubkeys`: `(list 128 (buff 33))` - List of public keys + * `m`: `uint` - Number of required signatures +* Returns: `principal` - The generated Stacks address + +pubkeys-to-bytes + +Concatenates a list of pubkeys into a buffer with length prefixes. + +* Parameters: + * `pubkeys`: `(list 128 (buff 33))` - List of public keys +* Returns: `(buff 510)` - Concatenated pubkeys with length prefixes + +concat-pubkeys-fold + +Concatenates a pubkey buffer with a length prefix. + +* Parameters: + * `pubkey`: `(buff 33)` - A single public key + * `iterator`: `(buff 510)` - Accumulator for concatenation +* Returns: `(buff 510)` - Updated concatenated buffer + +bytes-len + +Returns the length of a byte buffer as a single byte. + +* Parameters: + * `bytes`: `(buff 33)` - Input byte buffer +* Returns: `(buff 1)` - Length as a single byte + +uint-to-byte + +Converts a uint to a single byte. + +* Parameters: + * `n`: `uint` - Input number +* Returns: `(buff 1)` - Number as a single byte + +Private Functions + +signer-key-length-check + +Checks that the length of each key is exactly 33 bytes. + +* Parameters: + * `current-key`: `(buff 33)` - Public key to check + * `helper-response`: `(response uint uint)` - Accumulator for error handling +* Returns: `(response uint uint)` - Updated accumulator or error + +Constants + +BUFF\_TO\_BYTE + +A constant list mapping uint values (0-255) to their corresponding byte representations. + +Interactions with Other Contracts + +* `.sbtc-registry`: Calls `get-current-signer-data` and `rotate-keys` to manage signer data. + +Security Considerations + +{% hint style="warning" %} +* Access Control: Only the current signer principal can call the key rotation function. +* Key Validation: Ensures all provided keys are the correct length. +* Signature Threshold: Enforces a minimum threshold of over 50% of signers and a maximum of 100%. +* Multisig Generation: Provides utilities for secure generation of multisig addresses. +{% endhint %} diff --git a/docs/learn/sbtc/clarity-contracts/sbtc-token.md b/docs/learn/sbtc/clarity-contracts/sbtc-token.md new file mode 100644 index 0000000000..e7e84f0c28 --- /dev/null +++ b/docs/learn/sbtc/clarity-contracts/sbtc-token.md @@ -0,0 +1,158 @@ +# sBTC Token + +## Overview + +The [sBTC Token contract](https://github.com/stacks-network/sbtc/blob/main/contracts/contracts/sbtc-token.clar) (`sbtc-token.clar`) implements the fungible token functionality for sBTC. It manages both unlocked and locked sBTC tokens and provides functions for minting, burning, transferring, and querying token information. sBTC is a SIP-010 standard fungible token. + +## Constants + +* `ERR_NOT_OWNER` (u4): Error when the sender tries to move a token they don't own. +* `ERR_NOT_AUTH` (u5): Error when the caller is not an authorized protocol caller. +* `token-decimals` (u8): The number of decimal places for the token. + +## Fungible Tokens + +* `sbtc-token`: The main sBTC fungible token. +* `sbtc-token-locked`: Represents locked sBTC tokens. + +## Data Variables + +* `token-name`: The name of the token (default: "sBTC"). +* `token-symbol`: The symbol of the token (default: "sBTC"). +* `token-uri`: An optional URI for token metadata. + +## Protocol Functions + +These functions can only be called by authorized protocol contracts: + +### protocol-transfer + +* Parameters: `amount: uint`, `sender: principal`, `recipient: principal` +* Returns: `(response bool uint)` + +### protocol-lock + +* Parameters: `amount: uint`, `owner: principal` +* Returns: `(response bool uint)` + +### protocol-unlock + +* Parameters: `amount: uint`, `owner: principal` +* Returns: `(response bool uint)` + +### protocol-mint + +* Parameters: `amount: uint`, `recipient: principal` +* Returns: `(response bool uint)` + +### protocol-burn + +* Parameters: `amount: uint`, `owner: principal` +* Returns: `(response bool uint)` + +### protocol-burn-locked + +* Parameters: `amount: uint`, `owner: principal` +* Returns: `(response bool uint)` + +### protocol-set-name + +* Parameters: `new-name: (string-ascii 32)` +* Returns: `(response bool uint)` + +### protocol-set-symbol + +* Parameters: `new-symbol: (string-ascii 10)` +* Returns: `(response bool uint)` + +### protocol-set-token-uri + +* Parameters: `new-uri: (optional (string-utf8 256))` +* Returns: `(response bool uint)` + +### protocol-mint-many + +* Parameters: `recipients: (list 200 {amount: uint, recipient: principal})` +* Returns: `(response (list 200 (response bool uint)) uint)` + +## Public Functions (SIP-010 Trait) + +### transfer + +* Parameters: `amount: uint`, `sender: principal`, `recipient: principal`, `memo: (optional (buff 34))` +* Returns: `(response bool uint)` + +### get-name + +* Returns: `(response (string-ascii 32) uint)` + +### get-symbol + +* Returns: `(response (string-ascii 10) uint)` + +### get-decimals + +* Returns: `(response uint uint)` + +### get-balance + +Returns the total balance (locked + unlocked) for a principal. + +* Parameters: `who: principal` +* Returns: `(response uint uint)` + +### get-balance-available + +Returns the available (unlocked) balance for a principal. + +* Parameters: `who: principal` +* Returns: `(response uint uint)` + +### get-balance-locked + +Returns the locked balance for a principal. + +* Parameters: `who: principal` +* Returns: `(response uint uint)` + +### get-total-supply + +* Returns: `(response uint uint)` + +### get-token-uri + +* Returns: `(response (optional (string-utf8 256)) uint)` + +## Private Functions + +### protocol-mint-many-iter + +* Helper function for minting tokens to multiple recipients. +* Parameters: `item: {amount: uint, recipient: principal}` +* Returns: `(response bool uint)` + +## Security Considerations + +{% stepper %} +{% step %} +### Access Control + +Protocol functions can only be called by authorized contracts, enforced through the `sbtc-registry` contract. +{% endstep %} + +{% step %} +### Ownership Verification + +The `transfer` function checks that the sender owns the tokens being transferred. +{% endstep %} + +{% step %} +### Separate Token Tracking + +The contract maintains separate tracking for locked and unlocked tokens, ensuring proper accounting. +{% endstep %} +{% endstepper %} + +## Interactions with Other Contracts + +* `.sbtc-registry`: Used to validate protocol callers for privileged operations. diff --git a/docs/learn/sbtc/clarity-contracts/sbtc-withdrawal.md b/docs/learn/sbtc/clarity-contracts/sbtc-withdrawal.md new file mode 100644 index 0000000000..0cb5eda628 --- /dev/null +++ b/docs/learn/sbtc/clarity-contracts/sbtc-withdrawal.md @@ -0,0 +1,141 @@ +# sBTC Withdrawal + +## Overview + +The [sBTC Withdrawal contract](https://github.com/stacks-network/sbtc/blob/main/contracts/contracts/sbtc-withdrawal.clar) (`sbtc-withdrawal.clar`) manages the withdrawal process for the sBTC system. It handles the initiation, acceptance, and rejection of withdrawal requests, ensuring proper validation and interaction with other sBTC contracts. + +## Constants + +### Error Codes + +* `ERR_INVALID_ADDR_VERSION` (u500): Invalid address version. +* `ERR_INVALID_ADDR_HASHBYTES` (u501): Invalid address hashbytes. +* `ERR_DUST_LIMIT` (u502): Withdrawal amount below dust limit. +* `ERR_INVALID_REQUEST` (u503): Invalid withdrawal request ID. +* `ERR_INVALID_CALLER` (u504): Caller is not the current signer principal. +* `ERR_ALREADY_PROCESSED` (u505): Withdrawal request already processed. +* `ERR_FEE_TOO_HIGH` (u505): Paid fee higher than requested. +* `ERR_WITHDRAWAL_INDEX_PREFIX`: Prefix for withdrawal index errors. +* `ERR_WITHDRAWAL_INDEX` (u506): General withdrawal index error. + +### Other Constants + +* `MAX_ADDRESS_VERSION` (u6): Maximum value of an address version. +* `MAX_ADDRESS_VERSION_BUFF_20` (u4): Maximum version for 20-byte hashbytes. +* `MAX_ADDRESS_VERSION_BUFF_32` (u6): Maximum version for 32-byte hashbytes. +* `DUST_LIMIT` (u546): Minimum amount of sBTC for withdrawal. + +## Public Functions + +### initiate-withdrawal-request + +Initiates a new withdrawal request. + +* Parameters: + * `amount`: `uint` - Amount of sBTC to withdraw + * `recipient`: `{ version: (buff 1), hashbytes: (buff 32) }` - Bitcoin address details + * `max-fee`: `uint` - Maximum fee for the withdrawal +* Returns: `(response uint uint)` + +### accept-withdrawal-request + +Accepts a withdrawal request. + +* Parameters: + * `request-id`: `uint` - Withdrawal request ID + * `bitcoin-txid`: `(buff 32)` - Bitcoin transaction ID + * `signer-bitmap`: `uint` - Bitmap of signers + * `output-index`: `uint` - Output index in the Bitcoin transaction + * `fee`: `uint` - Actual fee paid +* Returns: `(response bool uint)` + +### reject-withdrawal-request + +Rejects a withdrawal request. + +* Parameters: + * `request-id`: `uint` - Withdrawal request ID + * `signer-bitmap`: `uint` - Bitmap of signers +* Returns: `(response bool uint)` + +### complete-withdrawals + +Processes multiple withdrawal requests (accept or reject). + +* Parameters: + * `withdrawals`: `(list 600 {...})` - List of withdrawal details +* Returns: `(response uint uint)` + +## Read-only Functions + +### validate-recipient + +Validates the recipient's Bitcoin address format. + +* Parameters: + * `recipient`: `{ version: (buff 1), hashbytes: (buff 32) }` - Bitcoin address details +* Returns: `(response bool uint)` + +## Private Functions + +### complete-individual-withdrawal-helper + +Helper function to process individual withdrawals in the batch operation. + +* Parameters: + * `withdrawal`: `{...}` - Individual withdrawal details + * `helper-response`: `(response uint uint)` - Accumulator for processing +* Returns: `(response uint uint)` + +## Interactions with Other Contracts + +* `.sbtc-token`: Calls `protocol-lock`, `protocol-burn-locked`, `protocol-mint`, and `protocol-unlock` for token operations. +* `.sbtc-registry`: Calls `create-withdrawal-request`, `get-withdrawal-request`, `get-current-signer-data`, `complete-withdrawal-accept`, and `complete-withdrawal-reject` for managing withdrawal requests and signer data. + +## Security Considerations + +{% stepper %} +{% step %} +### Access Control + +Only the current signer principal can accept or reject withdrawal requests. +{% endstep %} + +{% step %} +### Dust Limit + +Enforces a minimum withdrawal amount to prevent spam and ensure economic viability. +{% endstep %} + +{% step %} +### Fee Management + +Ensures that the actual fee doesn't exceed the maximum fee set by the user. +{% endstep %} + +{% step %} +### Address Validation + +Implements thorough validation of Bitcoin address formats. +{% endstep %} + +{% step %} +### State Management + +Prevents double-processing of withdrawal requests. +{% endstep %} +{% endstepper %} + +## Bitcoin Address Types + +The contract supports various Bitcoin address types, including: + +* P2PKH (Pay-to-Public-Key-Hash) +* P2SH (Pay-to-Script-Hash) +* P2SH-P2WPKH (P2SH nested P2WPKH) +* P2SH-P2WSH (P2SH nested P2WSH) +* P2WPKH (Pay-to-Witness-Public-Key-Hash) +* P2WSH (Pay-to-Witness-Script-Hash) +* P2TR (Pay-to-Taproot) + +Each address type is represented by a specific version byte and hashbytes format in the recipient structure. diff --git a/docs/learn/sbtc/core-features.md b/docs/learn/sbtc/core-features.md new file mode 100644 index 0000000000..8336ae30d5 --- /dev/null +++ b/docs/learn/sbtc/core-features.md @@ -0,0 +1,50 @@ +# Core Features + +sBTC offers several core features that make it a powerful trust-minimized Bitcoin bridge between Stacks and Bitcoin: + +{% stepper %} +{% step %} +### 1:1 Bitcoin Backing + +Each sBTC token is backed by an equivalent amount of Bitcoin in the peg wallet. This ensures that sBTC maintains a stable value relative to BTC. +{% endstep %} + +{% step %} +### Decentralized Management + +The sBTC peg wallet is maintained and managed by a set of sBTC signers. This decentralized approach enhances security and reduces single points of failure. +{% endstep %} + +{% step %} +### Quick Conversions + +sBTC facilitates rapid movement between BTC and sBTC: + +* BTC to sBTC conversion can be completed within 3 Bitcoin blocks +* sBTC to BTC conversion can be completed within 6 Bitcoin blocks +{% endstep %} + +{% step %} +### SIP-010 Compatibility + +sBTC adheres to the SIP-010 fungible token standard on the Stacks blockchain. This ensures wide compatibility with Stacks wallets and applications. +{% endstep %} + +{% step %} +### Community Governance + +The initial sBTC signing set is determined by a community vote, weighted by STX holdings. This approach ensures that the community has a say in the management of the sBTC system. +{% endstep %} + +{% step %} +### Signer Key Rotation + +sBTC signers have the ability to rotate their private keys, enhancing long-term security of the system. +{% endstep %} + +{% step %} +### Transaction Fee Sponsorship + +sBTC transactions on Stacks can be sponsored, allowing users to pay transaction fees in sBTC instead of STX. +{% endstep %} +{% endstepper %} diff --git a/docs/learn/sbtc/emily-api.md b/docs/learn/sbtc/emily-api.md new file mode 100644 index 0000000000..a459ab91ea --- /dev/null +++ b/docs/learn/sbtc/emily-api.md @@ -0,0 +1,138 @@ +# Emily API + +[Emily](https://github.com/stacks-network/sbtc/tree/main/emily) is an API that helps facilitate and supervise the sBTC Bridge, serving as a programmatic liaison between sBTC users and signers. + +## Overview + +The Emily API is designed to track deposits and withdrawals, providing information about the status of in-flight sBTC operations. It serves two primary user groups: sBTC users and sBTC app developers. + +## Why Emily? + +The Emily API is given an indirect name because it handles more than just Deposits and Withdrawals; it can detect the health of the system and will likely be extended to handle more as user requirements mature. It was once called the “Revealer API”, which stopped making sense after a few design changes, and then “Deposit API” which also stopped making sense after a few changes. The most obvious choice “sBTC API” gives the wrong impression of what the API is responsible for as well, since the API itself isn’t managing the entirety of the protocol. + +Large companies name their APIs after something loosely related but ambiguous enough that extensions of the API don’t make the original name of the API misleading. Following this, we chose “Emily” after Emily Warren Roebling who was the liaison between the builders and chief engineer, her husband, of the Brooklyn bridge. She was, in effect, the supervisor of the bridge’s construction; similarly, the Emily API supervises the sBTC bridge and liaises between the users of the protocol and the sBTC signers. + +## Key Features + +* Track Deposits: Monitor the process of converting BTC to sBTC. +* Track Withdrawals: Monitor the process of converting sBTC back to BTC. +* Provide Operation Status: Offer real-time status updates for ongoing sBTC operations. +* Retrieve Historical Data: Allow querying of past sBTC operations. + +## Core Concepts + +### sBTC Operations + +sBTC operations are the fundamental processes tracked by Emily: + +* Deposits: Converting BTC to sBTC +* Withdrawals: Converting sBTC back to BTC + +### Operation States + +Each sBTC operation goes through several states: + +* PENDING: The operation has been initiated. +* ACCEPTED: The operation has been approved by the signers. +* CONFIRMED: The operation has been completed and confirmed on the blockchain. +* FAILED: The operation could not be completed. + +## Interaction Flows + +### Deposit Flow + +{% stepper %} +{% step %} +### User creates a deposit transaction on Bitcoin + +User creates a deposit transaction on Bitcoin. +{% endstep %} + +{% step %} +### User submits proof of deposit to the Deposit API + +User submits proof of deposit to the Deposit API. +{% endstep %} + +{% step %} +### Emily records the deposit as PENDING + +Emily records the deposit as PENDING. +{% endstep %} + +{% step %} +### Signers validate and vote on the deposit + +Signers validate and vote on the deposit. +{% endstep %} + +{% step %} +### If accepted, Emily updates status to ACCEPTED + +If accepted, Emily updates status to ACCEPTED. +{% endstep %} + +{% step %} +### Signers process the Bitcoin transaction + +Signers process the Bitcoin transaction. +{% endstep %} + +{% step %} +### Signers mint sBTC on Stacks + +Signers mint sBTC on Stacks. +{% endstep %} + +{% step %} +### Emily updates the deposit status to CONFIRMED + +Emily updates the deposit status to CONFIRMED. +{% endstep %} +{% endstepper %} + +### Withdrawal Flow + +{% stepper %} +{% step %} +### User initiates withdrawal through the sBTC Clarity contract + +User initiates withdrawal through the sBTC Clarity contract. +{% endstep %} + +{% step %} +### Emily records the withdrawal as PENDING + +Emily records the withdrawal as PENDING. +{% endstep %} + +{% step %} +### Signers decide to accept or reject the withdrawal + +Signers decide to accept or reject the withdrawal. +{% endstep %} + +{% step %} +### If accepted, Emily updates status to ACCEPTED + +If accepted, Emily updates status to ACCEPTED. +{% endstep %} + +{% step %} +### Signers process the Bitcoin transaction + +Signers process the Bitcoin transaction. +{% endstep %} + +{% step %} +### Signers burn sBTC on Stacks + +Signers burn sBTC on Stacks. +{% endstep %} + +{% step %} +### Emily updates the withdrawal status to CONFIRMED + +Emily updates the withdrawal status to CONFIRMED. +{% endstep %} +{% endstepper %} diff --git a/docs/learn/sbtc/peg-wallet-utxo.md b/docs/learn/sbtc/peg-wallet-utxo.md new file mode 100644 index 0000000000..7cbb7765ed --- /dev/null +++ b/docs/learn/sbtc/peg-wallet-utxo.md @@ -0,0 +1,60 @@ +# Peg Wallet UTXO + +The Peg Wallet UTXO is a fundamental element of the sBTC system, serving as the Bitcoin backing for all sBTC tokens in circulation. The system uses a Single UTXO Model: the sBTC peg wallet is consistently represented as a single Unspent Transaction Output (UTXO) on the Bitcoin blockchain. This design offers simplicity and improved efficiency in managing the peg wallet. + +## Overview + +* Single UTXO Model: the peg wallet is always a single UTXO. +* Responsibility: UTXO management is performed by the Signer set. +* Purpose: simplify tracking and management, reduce Bitcoin transactions required for sBTC operations, and centralize funds in a single, well-secured output. + +## How the Single UTXO is maintained + +{% stepper %} +{% step %} +### Constructing the new UTXO + +A Signer coordinator constructs the UTXO by creating a new Bitcoin output that will represent the peg wallet going forward. +{% endstep %} + +{% step %} +### Consolidating requests into a batch + +The Signer set collectively consolidates all deposit and withdrawal requests and creates optimized batches that can be processed within a single UTXO. +{% endstep %} + +{% step %} +### Creating the new UTXO from the previous UTXO + +The new UTXO is created by: + +* spending the amount from the previous UTXO, +* adding confirmed deposits, +* subtracting confirmed withdrawals. +{% endstep %} + +{% step %} +### Optimizing batching with approval sets + +When multiple sBTC operation requests are present, the Signer coordinator groups them by approval sets. If differing approval sets exist across active operations, the coordinator batches deposit UTXOs into groups with the maximum size per approval set to preserve the single UTXO invariant while maximizing batch efficiency. +{% endstep %} +{% endstepper %} + +## Benefits + +* Simplified tracking and management of peg funds. +* Fewer Bitcoin transactions for sBTC operations. +* Centralized funds in a single, well-secured output improves operational efficiency. + +{% hint style="info" %} +The Single UTXO Model is designed to balance simplicity and operational efficiency for the sBTC peg wallet. +{% endhint %} + +## Security considerations + +* The single UTXO is managed by the sBTC Bootstrap Signer Set, which requires a threshold of signers to approve any spending (multi-signature). +* Regular audits and continuous monitoring are essential to ensure the UTXO accurately represents the total sBTC in circulation at all times. + +{% hint style="warning" %} +Security is paramount: multi-signature approval, audits, and monitoring are core controls to protect the peg wallet. +{% endhint %} diff --git a/docs/learn/sbtc/sbtc-audits.md b/docs/learn/sbtc/sbtc-audits.md new file mode 100644 index 0000000000..c7ef6b4f76 --- /dev/null +++ b/docs/learn/sbtc/sbtc-audits.md @@ -0,0 +1,11 @@ +# sBTC Audits + +Several third-party security audits have been conducted on the sBTC system and can be referenced here. + +{% file src="../.gitbook/assets/Ottersec - stacks_wsts_audit_final.pdf" %} + +{% file src="../.gitbook/assets/Ottersec - stacks_sbtc_audit_final.pdf" %} + +{% file src="../.gitbook/assets/CoinFabrik_WSTS.pdf" %} + +{% file src="../.gitbook/assets/Clarity Alliance - sBTC-2.pdf" %} diff --git a/docs/learn/sbtc/sbtc-faq.md b/docs/learn/sbtc/sbtc-faq.md new file mode 100644 index 0000000000..ac60f12fca --- /dev/null +++ b/docs/learn/sbtc/sbtc-faq.md @@ -0,0 +1,262 @@ +# sBTC FAQ + +### sBTC Basics + +
+ +What is sBTC? + +sBTC is a decentralizedl 1:1 Bitcoin-backed asset on the Stacks Bitcoin Layer. Read more about Stacks [here](https://www.stacks.co/) and sBTC [here](https://www.stacks.co/sbtc). + +
+ +
+ +How does sBTC work? + +sBTC as a SIP-010 tokensBTC is a SIP-010 token on the Stacks blockchain that represents Bitcoin (BTC) in a 1:1 ratio. sBTC is always backed 1:1 against BTC.Peg wallet and signersThe sBTC peg wallet is maintained and managed by a set of sBTC signers. This decentralized approach enhances security and reduces single points of failure. Read more about Stacker Signing here. + +
+ +
+ +What is Bitcoin Finality, and why is it important? + +Stacks and sBTC state automatically fork with Bitcoin. As such, all transactions settle to Bitcoin with 100% Bitcoin Finality. This protects users against attacks to sBTC via a hard fork. This is a critical security measure that aligns sBTC security with Bitcoin. Read more in [the Stacks Documentation](https://docs.stacks.co/concepts/block-production/bitcoin-finality). + +
+ +
+ +How does the Stacks Signer network improve security? + +Signers are responsible for approving all sBTC deposit and withdrawal operations, ensuring the integrity of the system. With a requirement of 70% consensus for transaction approval, Signers maintain the protocol's liveness and security. + +To launch sBTC, the Stacks community approved [SIP-028](https://github.com/stacksgov/sips/blob/69d40a5f4f0ad98eb448ba44e7c31ca054820aa3/sips/sip-028-sbtc_peg.md), defining the criteria for selecting signers based on factors such as technical expertise, reliability, performance, and decentralization. An initial group of 15 institutional Signers has been chosen for Phase 1 to maintain simplicity and reduce operational risks. This group will expand over time as the protocol matures. + +The list of sBTC signers is public and listed [here](https://bitcoinl2labs.com/sbtc-rollout#sbtc-signers). + +
+ +
+ +What security measures have been put in place to ensure sBTC is safe? + +sBTC is always backed 1:1 against BTC, and it's verifiably secure through threshold cryptography. sBTC removes the need for 3rd party custodian or trusted setup. Instead, BTC is secured by a decentralized signer set. + +Partnerships with top-tier security experts have been established to ensure the protocol is fortified at every level: + +Asymmetric ResearchAsymmetric Research is a core security contributor. Known for their rigorous research and protocol audits, Asymmetric brings security expertise to sBTC to identify and mitigate potential vulnerabilities.ImmuneFiA robust bug bounty program incentivizes ethical hackers to uncover and address potential issues, adding an additional layer of defense.3rd Party AuditsSeveral third-party security audits have been conducted on the sBTC system and can be referenced on the sBTC Audits page. + +
+ +
+ +What sets sBTC apart? + +Here are the main differentiating characteristics of sBTC: + +* sBTC is a true Bitcoin native product +* sBTC is backed by respected leaders in the Bitcoin community (signer network) +* sBTC's security is provided by a decentralized network of validators/signers rather than a single custodian, removing the need to trust a single entity or exchange +* sBTC leverages 100% Bitcoin finality +* sBTC's technology offers optimal UX and DevEx for an L2 +* sBTC is a fully transparent project/product working in the open with public code + +
+ +
+ +Where can I learn more about the sBTC signers? + +Read the "[Selection of sBTC Signer Set](https://github.com/stacks-network/sbtc/discussions/624)" post for more information about each signer and their qualifications. + +
+ +### sBTC Rewards Program + +
+ +Where does the yield paid in BTC come from? + +The sBTC Rewards Program is powered by a group of Stackers "Stacking" STX to a designated reward address, contributing their BTC rewards to the program. + +When Stacking STX, Stackers receive BTC through Stack's [Proof-of-Transfer](https://docs.stacks.co/concepts/stacks-101/proof-of-transfer) (PoX) consensus mechanism. For example, over a given 2-week period, the Stacks protocol has historically [distributed around 10% APY to Stackers](https://www.stacking-tracker.com/), paid in BTC. + +To enable the sBTC Rewards Program, these stackers contribute the corresponding Proof of Transfer BTC rewards to the sBTC incentive pool. This BTC from the incentive pool is directly deposited into a smart contract that bridges the BTC to sBTC and distributes the rewards pro rata to sBTC holders. + +The program is designed to increase sBTC liquidity and drive early usage of the protocol. + +Here's a handy illustration to show the sBTC incentives design: + +
+ +
+ +How are rewards distributed? + +sBTC is automatically distributed every two weeks to the STX address used to enroll in your non-custodial wallet. + +
+ +
+ +What do I have to do to be eligible for rewards? + +To be eligible, you must enroll in the rewards program at bitcoinismore.org. + +
+ +
+ +Do I need to re-enroll in the sBTC Rewards Program if I previously enrolled in the sBTC Rewards Program and have received additional sBTC? + +No re-enrollment is needed. The Yield smart contract will automatically calculate enrolled users updated balance, as long as the sBTC contract address remains the same. + +
+ +
+ +What level of rewards should I expect? + +The level of rewards users can expect will vary based on the amount of STX in the rewards pool, the PoX yield rate, and the amount of sBTC that has been minted. + +
+ +
+ +What is the difference between PoX Rewards and the sBTC Rewards Program? + +PoX Bitcoin rewards are earned by Stackers who lock up their STX tokens to secure the Stacks network, a process that has been ongoing since the launch of Stacks. + +The sBTC Rewards Program, on the other hand, offers additional BTC rewards specifically for early adopters who hold sBTC without requiring them to participate in network consensus or lock up any tokens. + +
+ +### Using sBTC + +
+ +When will sBTC be available? + +sBTC deposits first went live on December 16, 2024, quickly hitting the 1,000 BTC cap. The second cap will go live on February 25th, 2025, quickly hitting the 3,000 BTC cap. Withdrawals went live on April 30, 2025. + +Full decentralization of the Signer set will follow in [a subsequent phase](https://bitcoinl2labs.com/sbtc-rollout), gradually expanding beyond the initial 15 community-elected signers. + +
+ +
+ +What wallets are supported for sBTC? + +[Xverse](https://www.xverse.app/) and [Leather](https://leather.io/) wallets are supported — two leading wallets with seamless integrations designed for Bitcoin and Stacks users. + +In addition, [Ledger](https://www.ledger.com/) and [Asigna](https://www.asigna.io/) support sBTC. + +We are actively working with institutional custodians, staking providers, and other 3rd party wallets to support sBTC. More will be announced. + +
+ +
+ +Why is there a .01 BTC minimum for BTC to sBTC deposits? + +A .01 BTC minimum is imposed for BTC to sBTC deposits to ensure the system does not get spammed by many smaller transactions. We are exploring reducing the deposit minimum for future phases. + +
+ +
+ +What are the steps to use the sBTC Bridge and earn rewards? + +In the Stacks Documentation, find a [video](https://www.youtube.com/watch?v=XZruuDgTo4k\&t=1s) and a [more detailed walkthrough](https://docs.stacks.co/guides-and-tutorials/sbtc/how-to-use-the-sbtc-bridge). + +Ensure BTC is accessible in a supported walletEnsure BTC is accessible via one of the following non-custodial wallets: Xverse, Leather, Ledger, or Asigna.Connect your wallet to the sBTC appTo interact with the sBTC protocol and mint sBTC, head to app.stacks.co and connect your non-custodial wallet with BTC ready to deposit.Enter BTC amountEnter the BTC amount to convert to sBTC (app.stacks.co will guide you through this step).Provide your Stacks receiving addressEnter your Stacks receiving address to initiate the transfer (app.stacks.co will guide you through this step).Enroll in the rewards programAfter your sBTC has been minted to your wallet, visit the rewards program site at bitcoinismore.org and connect your wallet. Then click the 'Earn Rewards' button. Read more in the Stacks Documentation.Start earningSeamlessly start earning sBTC rewards. sBTC is automatically paid every two weeks to the STX address used to enroll in your non-custodial wallet.Note: There is an initial lock-up period until withdrawals are activated in March. Following the lock-up period, sBTC can always be withdrawn. + +
+ +
+ +How long will it take for my BTC deposit to confirm? + +sBTC facilitates rapid movement between BTC and sBTC. + +BTC to sBTCBTC to sBTC conversion can be completed within 3 Bitcoin blocks (under an hour).sBTC to BTCsBTC to BTC conversion can be completed within 6 Bitcoin blocks (Approximately two hours) + +Read more in the [Stacks Documentation](https://docs.stacks.co/concepts/sbtc/operations/deposit-withdrawal-times). + +
+ +
+ +Why is there a cap on the total BTC pegged in? + +A BTC cap will be implemented to ensure a smooth rollout process with a focus on security. + +In addition, the BTC cap will give developers the time to focus on the sBTC user experience and integration with DeFi applications across the Stacks ecosystem prior to opening sBTC for all users. + +
+ +
+ +Are there any associated fees with minting sBTC? + +There are two transaction fees required to mint your sBTC. The first is set by the user manually when they initiate the deposit transaction within their wallet. + +The second is a fee used to consolidate the deposit UTXOs into the single signer UTXO. This separate transaction fee happens automatically and is set to a max of 80k sats. This is automatically deducted from your minted sBTC. This is not a signer fee but a regular Bitcoin transaction fee. + +
+ +
+ +Are there multi-signature solutions for sBTC? + +Yes. [Asigna](https://www.asigna.io/) provides a multi-signature solution for sBTC users. + +
+ +
+ +Are custodians available to support sBTC? + +At the moment, there is no custodian support for sBTC. However, we are actively working with institutional custodians to support sBTC. + +Copper and BitGo already support Stacks and Stacking; however, we are working to prioritize SIP-10 and sBTC integration. + +
+ +### sBTC Troubleshooting + +
+ +My Bitcoin transaction confirmed, but I'm not seeing the sBTC token in my wallet. + +You may need to enable the display of the sBTC token within your wallet by clicking on 'Manage Tokens' and enabling sBTC. + +
+ +
+ +I received an "Errors.Invalid_Transaction" error when using an Xverse Wallet + +If you received a "Errors.Invalid\_Transaction" error when using an Xverse Wallet, you may be using a "Nested SegWit" wallet. To resolve the issue, change your Xverse wallet to use the "Native SegWit". + +
+ +
+ +sBTC still isn't showing up in wallet after 3 Bitcoin blocks. How much longer do I have to wait? + +BTC to sBTC conversions are typically completed within 3 Bitcoin blocks. Due to the speed of Bitcoin blocks, deposits can take up to two hours to see sBTC in your wallet. + +However, there may be a lag with your Leather or Xverse wallet where the sBTC will take another 20 minutes to show up in the wallet. + +
+ +
+ +I didn't receive a confirmation that I enrolled in the rewards program. How can I ensure I'm enrolled? + +Visit [bitcoinismore.org](https://bitcoinismore.org/). On the enroll page, when your wallet is linked, it will say enrolled if you are enrolled in the program. + +
diff --git a/docs/learn/sbtc/sbtc-operations/README.md b/docs/learn/sbtc/sbtc-operations/README.md new file mode 100644 index 0000000000..6c13733b17 --- /dev/null +++ b/docs/learn/sbtc/sbtc-operations/README.md @@ -0,0 +1,17 @@ +# sBTC Operations + +This section covers the main operations in the sBTC system. These operations form the core functionality of sBTC, allowing users to move value between the Bitcoin and Stacks ecosystems. + +{% stepper %} +{% step %} +### Deposit + +Converting BTC to sBTC. +{% endstep %} + +{% step %} +### Withdrawal + +Converting sBTC back to BTC. +{% endstep %} +{% endstepper %} diff --git a/docs/learn/sbtc/sbtc-operations/deposit-vs-withdrawal-times.md b/docs/learn/sbtc/sbtc-operations/deposit-vs-withdrawal-times.md new file mode 100644 index 0000000000..ba2e3379e5 --- /dev/null +++ b/docs/learn/sbtc/sbtc-operations/deposit-vs-withdrawal-times.md @@ -0,0 +1,37 @@ +# Deposit vs Withdrawal Times + +## Why are Deposits So Fast and Withdrawals So Slow? + +sBTC allows users to use their BTC on the Stacks L2 by using a wrapped token called sBTC. Moving sBTC onto the Stacks L2 can take as little time as 1 Bitcoin block, but moving sBTC off the Stacks L2 into the native Bitcoin blockchain takes 6 Bitcoin blocks. Why is that? + +To understand why moving onto the Stacks layer can be so fast and yet moving off must be so slow, we need to first understand the consensus mechanism of the Stacks blockchain. + +The Stacks blockchain uses a consensus mechanism called proof of transfer, or PoX, in order to mint new blocks. On each Bitcoin block miners on the Stacks blockchain each sacrifice some amount of Bitcoin in a bid to win the right to make the next few Stacks blocks, where they retain the right to keep making Stacks blocks until the next Bitcoin block occurs and the latest bidding round elects a new Miner. Signers (validators equivalents for the Stacks Blockchain) look at the Bitcoin blocks and approve new Stacks blocks based on which miner currently has the right to make Stacks blocks, and they only approve new blocks from the Signers that won the most recent bid on the Bitcoin block within the fork that they collectively consider to be the “best”. The Stacks blockchain can only have new blocks added if the Signers agree that the miner who proposed it is the winner of the bid on the Bitcoin blockchain, and all the Signers are voting on which block should be added, effectively collectively deciding which Bitcoin fork is the best one. + +Here’s an important part: if the Signers believe that there’s a new and better Bitcoin fork that differs from the one that the last several Stacks blocks had been mined on, they’ll then only approve new Stacks blocks that build off of Stacks blocks that are tied to that new fork. As in, every Stacks block that was built on Bitcoin Blocks in the other Bitcoin fork that aren’t in this new fork are considered invalid; thus the Stacks blockchain forks too. + +A simple way to say this: “The Stacks blockchain forks with the Bitcoin blockchain.” + +Now that we understand this forking mechanism, let's take a look at why moving off the Stacks layer must be so slow. + +sBTC exists on the Stacks layer as a token that smart contracts can interact with. To move sBTC over from the Stacks layer to the Bitcoin layer the owner of the sBTC calls a smart contract to initiate what we call the “withdrawal” sequence. This lets the “sBTC Signers” (these are different from the earlier Signers mentioned) know that they need to create a transaction on the Bitcoin blockchain to distribute the BTC back to the user. + +If the signers create a Bitcoin transaction to enact the withdrawal they can’t take it back, and it will be valid on every fork of the Bitcoin blockchain. So what happens if, say, the bitcoin blockchain forks and the withdrawal on the Stacks layer got reorganized out? Then there’s an irretrievable withdrawal transaction on the Bitcoin blockchain giving precious BTC to a user who never withdrew their sBTC on the Stacks layer. + +
+ +Can the Signers that maintain the original chain force miners to replay all previously confirmed transactions? + +The Stacks blockchain is a true Layer 2 on top of Bitcoin, and you can write a smart contract to have different behavior based on observations of the Bitcoin blockchain underneath. You can, for example, write a Stacks contract that says “Pay to Jeff if the latest Bitcoin block hash ends in an even hex digit, and pay to Abigail if it’s an odd hex digit.” Now when there’s a reorg of the Bitcoin blockchain you can replay this transaction which originally paid to Jeff, but it now pays to Abigail, and what happens if this contract was giving out sBTC, and further what happens if Jeff then immediately executed a withdrawal? + +
+ +So in the end, to process a withdrawal safely you need to be sufficiently sure it won’t get reorganized out. That means it can only be processed 6 Bitcoin blocks (the finality criteria the Signers are comfortable with) after the withdrawal was made on the Stacks blockchain. + +But then, why can deposits be done in one Bitcoin block at its fastest? + +Remember how Stacks forks with Bitcoin? Let's say someone makes a deposit on the Bitcoin blockchain in an attempt to mint sBTC, and then lets say the sBTC Signers immediately mint sBTC. What happens if the Bitcoin chain forks causing the Stacks blockchain to fork? The mint gets reorganized out! Sure, the deposit is no longer on the Bitcoin blockchain, but it’s not on the Stacks blockchain either. If that deposit doesn’t ever arrive on the Bitcoin blockchain the sBTC signers will never mint sBTC, so there’s nothing to take back! + +So all in all, for movements of sBTC from the Stacks layer into the Bitcoin layer the protocol needs to wait for Bitcoin to be sufficiently final, but movements from the Bitcoin layer to the Stacks layer don’t need to wait for finality to mint because the Stacks layer will just reorganize itself if the Bitcoin layer reorganizes too. + +But then conceptually remember, the mint call on the Stacks blockchain is just as final as the Bitcoin block that contains the deposit of BTC onto the Stacks layer. If you’re minting sBTC on the Stacks layer and you want to wait for it to be final you’ll need to wait a suitable number of Bitcoin blocks to consider it finally minted, but that’s up to you and not the sBTC Signers. diff --git a/docs/learn/sbtc/sbtc-operations/deposit.md b/docs/learn/sbtc/sbtc-operations/deposit.md new file mode 100644 index 0000000000..0fa87a9a5e --- /dev/null +++ b/docs/learn/sbtc/sbtc-operations/deposit.md @@ -0,0 +1,43 @@ +# Deposit + +The deposit operation enables users to mint sBTC, anchored to the BTC they have placed in the threshold wallet on the Bitcoin chain. This process can be completed within a single Bitcoin block, streamlining the user experience. + +## Process Overview + +![deposit diagram](<../../.gitbook/assets/AD_4nXdlY8MKm4IEls6XieRtpunfge6KTNSw2HT_o9iD8FgIL3RCJuzKa781Ft oXNCEn_rIqMqu0_hqD5 GPrF9cT6rFXdnA1BASFoU3Uy6VgR2ARfp 0FnLgrM7GH7hdx Ia2m_DpdonZmlwqTMd1sQe0XqgX4>) + +The deposit process begins when a user initiates a specific Bitcoin transaction that has two outputs. The depositor (usually through the application they are using to deposit) then initiates an API call referencing that Bitcoin transaction. This call triggers the Emily API, which relays deposit information to the sBTC Signers. These signers verify and process the deposit. Once verified, an equivalent amount of sBTC is minted on the Stacks blockchain. + +{% stepper %} +{% step %} +### Script output + +A script that lets the signers spend the funds. +{% endstep %} + +{% step %} +### Time-locked output + +A time lock that allows the depositor to reclaim the funds if necessary. +{% endstep %} +{% endstepper %} + +The deposit is usually completed within a single Bitcoin block, but is guaranteed to be completed within 3. For more information on deposit and withdrawal confirmation times and why deposits can be so fast, check out the [Deposit and Withdrawal Times](deposit-vs-withdrawal-times.md) doc. + +## Bitcoin Deposit Requirements + +For a deposit to be considered valid, it must adhere to specific requirements: + +* The deposit must be made to a taproot address. +* The output must be spendable by a consensus threshold of signers. +* The deposit must follow a format that prevents short-term clawbacks, ensuring the security and integrity of the system. + +## User Experience + +From a user's perspective, the deposit process is straightforward: + +1. Initiate a BTC transaction to the specified address. +2. Wait for the transaction to be confirmed on the Bitcoin blockchain. +3. Receive the equivalent amount of sBTC in the Stacks wallet once the deposit is verified and processed. + +To enhance the user experience, an sBTC bridge web application is currently in development which will provide an intuitive interface for users to track the status of their deposit operations, allowing users to stay informed throughout the process from initiation to completion. diff --git a/docs/learn/sbtc/sbtc-operations/withdrawal.md b/docs/learn/sbtc/sbtc-operations/withdrawal.md new file mode 100644 index 0000000000..36ff4fbf42 --- /dev/null +++ b/docs/learn/sbtc/sbtc-operations/withdrawal.md @@ -0,0 +1,73 @@ +# Withdrawal + +The sBTC withdrawal operation enables users to convert their sBTC back to BTC. This process involves burning sBTC on the Stacks blockchain and releasing an equivalent amount of BTC on the Bitcoin blockchain. + +## Process Overview + +
+ +
+ +{% stepper %} +{% step %} +### Initiate withdrawal + +A user initiates a Clarity contract call (via a Stacks wallet or dApp) specifying: + +* the amount of sBTC to withdraw +* the destination Bitcoin address +{% endstep %} + +{% step %} +### Stacks transaction finality + +The Stacks transaction must reach finality. The protocol requires six Bitcoin block confirmations before proceeding to the next step. +{% endstep %} + +{% step %} +### Signer verification and BTC release + +After confirmations, sBTC Signers verify the withdrawal request and create the withdrawal transaction on the Bitcoin network, releasing the equivalent BTC to the specified Bitcoin address. +{% endstep %} +{% endstepper %} + +The withdrawal process requires six Bitcoin block confirmations to complete. After these confirmations, sBTC Signers create the withdrawal transaction on the Bitcoin network. + +## Withdrawal Confirmation + +The six-block confirmation requirement serves multiple purposes: + +* Ensures finality of the Stacks transaction and prevents potential reversals or conflicts. +* Mitigates issues from potential Bitcoin forks by allowing time for network stability. +* Gives sBTC Signers sufficient time to verify and process the withdrawal request accurately. + +For more information on deposit and withdrawal confirmation times and why deposits can be faster than withdrawals, see the [Deposit and Withdrawal Times](https://app.gitbook.com/u/ZrQItu6D9bMKmf1HfsLTnGc05WZ2) doc. + +## Failure Cases + +Some withdrawal failures can be identified and resolved before the six confirmations are complete. Other failures may only become apparent after the sBTC Bootstrap Signer attempts to create the withdrawal transaction on the Bitcoin network. These delays stem from the complexity of cross-chain operations and the need for thorough verification at each step. + +
+ +More about failure detection timing + +Because cross-chain operations involve verification on both Stacks and Bitcoin, certain issues (for example: insufficient signer consensus, malformed Bitcoin transaction construction, or Bitcoin network conditions) may only be detectable when the signer attempts to broadcast the Bitcoin transaction. This can cause failure detection to occur after confirmations on Stacks are already complete. + +
+ +## Security Considerations + +{% hint style="info" %} +The multi-block confirmation process is a critical security measure to help prevent double-spending attempts. Requiring multiple block confirmations ensures the withdrawal request is valid and final before processing on the Bitcoin network. Additionally, sBTC Signers perform verification of each withdrawal request prior to creating the Bitcoin transaction, providing an extra security layer. +{% endhint %} + +## User Experience + +From a user's perspective: + +* Initiate a withdrawal through a Stacks wallet or dApp. +* Specify the sBTC amount and destination Bitcoin address. +* Wait for the required six Bitcoin blocks to confirm. +* Once confirmations complete and signers process the request, BTC is sent to the specified Bitcoin address. + +The sBTC bridge web application offers a user-friendly interface that lets users track the status of their withdrawal operations in real time, providing updates at each stage so users can understand progress and estimate when they will receive BTC. diff --git a/docs/learn/sbtc/walkthroughs/README.md b/docs/learn/sbtc/walkthroughs/README.md new file mode 100644 index 0000000000..b3862d07e7 --- /dev/null +++ b/docs/learn/sbtc/walkthroughs/README.md @@ -0,0 +1,5 @@ +# Walkthroughs + +These walkthroughs describe at a high level exactly how users and signers can expect to interact with the sBTC system. + +Read these to get a firm understanding of what is actually happening under the hood of the sBTC system. diff --git a/docs/learn/sbtc/walkthroughs/sbtc-transaction-walkthrough.md b/docs/learn/sbtc/walkthroughs/sbtc-transaction-walkthrough.md new file mode 100644 index 0000000000..274116f343 --- /dev/null +++ b/docs/learn/sbtc/walkthroughs/sbtc-transaction-walkthrough.md @@ -0,0 +1,145 @@ +# sBTC Transaction Walkthrough + +Let's follow the journey of 1 BTC as it moves through the sBTC system, from initial deposit to final withdrawal. + +## Part 1: Deposit (BTC → sBTC) + +{% stepper %} +{% step %} +### Initiation + +* Alice decides to convert 1 BTC to sBTC to participate in Stacks DeFi. +* Alice creates a deposit transaction on the Bitcoin network (typically via a UI such as the sBTC bridge or a DeFi application). +* The transaction enters the Bitcoin mempool. +{% endstep %} + +{% step %} +### Proof Submission + +* Alice submits proof of her deposit to the Deposit API (usually via the application's UI). +* The Deposit API sets the deposit status to PENDING. +{% endstep %} + +{% step %} +### Signer Validation + +The sBTC Signer Set: + +* Detects the deposit. +* Validates the UTXO format. +* Votes on the deposit. + +If the deposit is rejected: + +* Signers notify the API of the rejection. +* The Deposit API updates the status to FAILED. + +If the deposit is accepted: + +* The Deposit API updates the status to ACCEPTED. +{% endstep %} + +{% step %} +### Bitcoin Transaction + +If accepted, the sBTC Signer Set: + +* Creates a new Bitcoin transaction consuming Alice's deposited BTC. +* Broadcasts this transaction to the Bitcoin network. + +If this transaction fails: + +* Signers notify the API of the failure. +* The Deposit API updates the status to FAILED. +{% endstep %} + +{% step %} +### sBTC Minting + +Upon successful Bitcoin transaction: + +* The sBTC Signer Set interacts with the Stacks blockchain. +* They fulfill the deposit by minting 1 sBTC to Alice's Stacks address. +{% endstep %} + +{% step %} +### Confirmation + +* The Deposit API updates the deposit status to CONFIRMED. +* Alice now has 1 sBTC in her Stacks wallet. +{% endstep %} +{% endstepper %} + +*** + +## Part 2: sBTC Usage + +Alice can now use her 1 sBTC in the Stacks ecosystem: + +* Transfer it to other users via the `sbtc-token` contract (typically via an application UI). +* Participate in DeFi applications. +* Use it in any application that supports SIP-010 tokens. + +*** + +## Part 3: Withdrawal (sBTC → BTC) + +{% stepper %} +{% step %} +### Initiation + +* Alice initiates a withdrawal by interacting with the Clarity contract on the Stacks blockchain. +* She specifies her Bitcoin address for the withdrawal. +* If successful, the contract locks her sBTC and the withdrawal status is set to PENDING. +* If the transaction fails, no withdrawal occurs. +{% endstep %} + +{% step %} +### Signer Validation + +The sBTC Signer Set: + +* Detects the withdrawal request. +* Decides whether to accept or reject the withdrawal. + +If the withdrawal is rejected: + +* Signers unlock the sBTC. +* The withdrawal status is updated to FAILED. + +If the withdrawal is accepted: + +* The withdrawal status is updated to ACCEPTED. +* Signers wait for 6 Bitcoin block confirmations (for security purposes). +{% endstep %} + +{% step %} +### Bitcoin Transaction + +After the waiting period, if accepted: + +* The sBTC Signer Set creates a new Bitcoin transaction fulfilling Alice's withdrawal. +* They broadcast this transaction to the Bitcoin network. + +If this transaction fails: + +* Signers unlock the sBTC. +* The withdrawal status is updated to FAILED. +{% endstep %} + +{% step %} +### sBTC Burning and Confirmation + +Upon successful Bitcoin transaction: + +* The sBTC Signer Set burns the locked 1 sBTC on the Stacks blockchain. +* The withdrawal status is updated to CONFIRMED. +{% endstep %} + +{% step %} +### Completion + +* Alice now has her 1 BTC back in her specified Bitcoin address. +* The withdrawn sBTC has been permanently removed from circulation. +{% endstep %} +{% endstepper %} diff --git a/docs/learn/sbtc/walkthroughs/signer-process-walkthrough.md b/docs/learn/sbtc/walkthroughs/signer-process-walkthrough.md new file mode 100644 index 0000000000..6c2f81824a --- /dev/null +++ b/docs/learn/sbtc/walkthroughs/signer-process-walkthrough.md @@ -0,0 +1,76 @@ +# Signer Process Walkthrough + +## Introduction + +This document provides a detailed overview of the sBTC system, focusing on the operations of an sBTC signer node. We'll explore the automated processes and software interactions that occur in the sBTC ecosystem. + +A step-by-step guide for setting up and running a sBTC signer node is in the works. This is a conceptual guide to help signers understand what their role looks like in the sBTC system. + +## Signer Node Setup + +As an sBTC signer, your primary responsibility is to run and maintain a signer node. Here's what that entails: + +{% stepper %} +{% step %} +### Hardware setup + +Ensure your node has sufficient computational power and storage. +{% endstep %} + +{% step %} +### Software installation + +Install the sBTC signer node software and its dependencies. +{% endstep %} + +{% step %} +### Key management + +The node software securely generates and stores the Bitcoin private key and corresponding public key. +{% endstep %} + +{% step %} +### Node registration + +Upon first run, the node automatically registers its public key with the sBTC Registry contract on the Stacks blockchain. +{% endstep %} +{% endstepper %} + +## Day-to-Day Operations + +Once set up, your signer node operates autonomously, performing the following tasks: + +{% stepper %} +{% step %} +### Monitoring Deposit Requests + +Your node continuously monitors for sBTC minting requests: + +* The node connects to the Bitcoin network and the Stacks blockchain. +* It watches for Bitcoin transactions sent to the sBTC UTXO address. +* When a deposit is detected, the node verifies the transaction details. +{% endstep %} + +{% step %} +### Processing Mint Requests + +Upon confirming a deposit: + +* The node automatically prepares a signature for the mint operation using its private key. +* It submits this signature to the sBTC Deposit contract on the Stacks blockchain. +* The contract verifies the signature and combines it with signatures from other signer nodes. +* Once enough valid signatures are collected, the contract mints the corresponding amount of sBTC. +{% endstep %} + +{% step %} +### Handling Withdrawal Requests + +For sBTC withdrawal requests: + +* The node monitors the sBTC Withdrawal contract for new requests. +* Upon detecting a request, it verifies the user's sBTC balance and the request's validity. +* The node automatically signs the withdrawal operation and submits its signature. +* Once enough signatures are collected and the sBTC is burned, the node participates in creating and signing a Bitcoin transaction to fulfill the withdrawal. +* The signed Bitcoin transaction is broadcast to the Bitcoin network. +{% endstep %} +{% endstepper %} diff --git a/docs/learn/stacks-101/README.md b/docs/learn/stacks-101/README.md new file mode 100644 index 0000000000..22bd03150b --- /dev/null +++ b/docs/learn/stacks-101/README.md @@ -0,0 +1,7 @@ +# Stacks 101 + +Stacks has a very unique technical model in the blockchain world. This section will help you get a high-level overview of the essential components to understand how Stacks works. + +We'll cover the basics of what Stacks is and how it works from both a philosophical and technical level, and you can dive into the further sections for more details. + +First up, let's get an overview of exactly what Stacks is. diff --git a/docs/learn/stacks-101/bitcoin-connection.md b/docs/learn/stacks-101/bitcoin-connection.md new file mode 100644 index 0000000000..a397f5a8fa --- /dev/null +++ b/docs/learn/stacks-101/bitcoin-connection.md @@ -0,0 +1,165 @@ +# The Bitcoin Connection + +

source: Hiro Blog

+ +In the previous section, we described Stacks as bringing smart contract functionality to Bitcoin, without modifying Bitcoin itself, and explained a bit about how the chain works. + +That's a big promise, but how does Stacks actually deliver on it? And what makes Stacks unique among other Bitcoin layers and other blockchains like Ethereum? + +Before we get into the technical details of how Stacks works, it's important to get a high-level overview of the problem it's solving and how it actually does that. We'll dive deeper into some of these topics as we go through the docs, but it's good to get a high-level picture to bring everything together. + +This topic is a bit of a rabbit hole, and this section is pretty long, but it will give you an in-depth understanding of exactly the problem Stacks is looking to solve, and how it solves it. + +Let's get into it. + +### Is Stacks a Bitcoin L2? + +Stacks is a Bitcoin layer for smart contracts. The classification as a layer-1 (L1) or layer-2 (L2) or sidechain really depends on the definition used. With that said, generally speaking L1 chains are sovereign meaning that (a) they have their own security budget, and (b) they can survive without the need for any other L1 chain. L2 chains typically do not have their own security budget and share the security of the underlying L1 chain, and they cannot live without the underlying L1 chain. There are many different design mechanisms that L2s can use, and we cover several of them and how Stacks compares in the [Stacks Among Other Bitcoin Layers](stacks-among-other-layers.md) section. + +The initial release of Stacks in early 2021 had a separate security budget from Bitcoin L1. Even though the Stacks layer could not function without Bitcoin L1, the developers working on the project described it as a different system that does not fit neatly into existing classifications, sometimes using the term layer 1.5 (see [this Decrypt article](https://decrypt.co/82019/bitcoin-defi-thing-says-stacks-founder-muneeb-ali) for example). + +The upcoming planned release of Stacks, called the Nakamoto release, will no longer have a separate security budget from Bitcoin. Instead, a 100% of Bitcoin hashpower will determine finality on Stacks layer. After the next upgrade, to reorg Stacks blocks/transactions the attacker will need to reorg Bitcoin L1 itself (which is very hard to do and therefore a great security property for a Bitcoin layer to have). More details in the [Stacks paper](https://stacks.co/stacks.pdf). + +The definition of [L2 used in Ethereum](https://ethereum.org/en/layer-2/) and other newer ecosystems is different and focuses on the ability to withdraw assets using only L1 security and L1 miners. According to that definition Stacks layer is not a clear L2, given the set of peg-out signers determine if users can withdraw sBTC. Bitcoin cannot support such verification without changes to Bitcoin L1 (which may happen in the future). The Ethereum L2 definition also does not apply that cleanly to Bitcoin L2s, given new assets are issued on L2s when it comes to Bitcoin and not issued on L1 (only BTC is the L1 asset). Therefore, using the definition of security of withdrawing assets is not directly applicable given assets are defined and used on L2s and not withdrawn out to Bitcoin L1 anyway (with the exception of BTC itself). Rather, what becomes more important is "settlement on Bitcoin" i.e., is contract data and state secured by 100% of Bitcoin's hashpower or not. + +Remember that L2s on Bitcoin also have to serve the additional purpose of expanding both functionality and scalability, which means L2s accomplish fundamentally different goals depending on the functionality of the L1. + +Users and developers organically call Stacks a Bitcoin L2, since it is a simpler concept to understand. There are certain properties of Stacks layer that also help the concept of Stacks as a Bitcoin L2: + +{% stepper %} +{% step %} +### Bitcoin finality + +100% of the Bitcoin hashpower decides block ordering and transaction finality. +{% endstep %} + +{% step %} +### Consensus runs on Bitcoin L1 + +Stacks consensus runs on Bitcoin L1, and Stacks L2 cannot operate or survive without Bitcoin L1. +{% endstep %} + +{% step %} +### sBTC and economic unit + +With the upcoming decentralized Bitcoin peg, called sBTC, most of the economy on Stacks layer will likely use BTC as the unit of economy. It is expected that most users will simply use Bitcoin in wallets and apps and then peg out their BTC to Bitcoin L1. +{% endstep %} + +{% step %} +### Data hashed and stored on Bitcoin L1 + +All data and transactions on Stacks are automatically hashed and permanently stored on Bitcoin L1 on every Bitcoin block. Anyone can verify that some data on Stacks is valid by checking the corresponding hash on Bitcoin L1. This compact storage of hashes on L1 is somewhat similar to rollups (although there are other differences). You can read more about this process in the [Block Production](../block-production/) section. +{% endstep %} + +{% step %} +### Contracts can read Bitcoin L1 + +Contracts on Stacks layer can read Bitcoin L1 transactions and respond to them. Assets on Stacks layer can be moved simply through Bitcoin L1 transactions. +{% endstep %} +{% endstepper %} + +Given all the details above, why would some people think that Stacks is not a Bitcoin L2? There are a couple of reasons this question comes up often: + +{% stepper %} +{% step %} +### Old security-budget material + +The initial version of Stacks (released early 2021) had a separate security budget which changed to following 100% Bitcoin hashpower with the Nakamoto release. There is old material and blog posts floating around that still talk about the initial Stacks version. The old materials will likely get updated with time. +{% endstep %} + +{% step %} +### Ethereum L2 withdrawal definition doesn't map cleanly + +According to the Ethereum definition of L2s a user should be able to withdraw their base-layer assets purely by doing an L1 transaction and relying only on L1 security (this is true for Lightning for example). This definition does not apply cleanly to Bitcoin L2s because assets are not defined at Bitcoin L1 but are defined in L2s instead. The only asset where this matters is the pegged BTC asset from Bitcoin L1, given all other assets are native to L2s anyway. In the upcoming Stacks release, users can withdraw their BTC by sending just a Bitcoin L1 transaction but Bitcoin L1 cannot validate that complex transaction and a majority of peg-out signers will need to sign on the peg-out request. In an ideal world Bitcoin miners can validate such transactions but that would require a change to Bitcoin L1. Therefore, Stacks design optimizes for a method that is decentralized and can be deployed without any changes to Bitcoin L1. If in the future it is possible to make changes to Bitcoin L1 then Stacks layer security can benefit from that as well. +{% endstep %} + +{% step %} +### Healthy Bitcoin skepticism + +Bitcoin community members are generally skeptical of claims and on the lookout for people making any false marketing claims. This is generally a healthy thing for the Bitcoin ecosystem and builds up the immune system. Some such community members might be skeptical about Stacks as a Bitcoin layer or L2 until they fully read the technical details and reasoning. There is a good [Twitter thread](https://twitter.com/lopp/status/1623756872976158722?s=20) about this topic as well. +{% endstep %} +{% endstepper %} + +Why don't we use the term 'sidechain' for Stacks then? Sidechains in Bitcoin typically have a different security budget from Bitcoin L1, typically as a subset of Bitcoin miners who participate in the sidechain (they don't follow 100% Bitcoin finality), their consensus runs on the sidechain (vs running on Bitcoin L1), and they don't publish their data/hashes on Bitcoin L1. The Stacks layer does not fit that definition cleanly given the consensus runs on Bitcoin L1, it follows Bitcoin finality, and publishes data/hashes on L1. + +Can Stacks layer work with rollups? + +Yes! There is already an active R\&D effort to integrate rollups with the Stacks layer. Both with the Stacks layer and sovereign rollups the technically challenging part is how to get BTC in and out of the Stacks layer or the sovereign rollup. The decentralized BTC peg, [sBTC](../sbtc/), applies to both the Stacks layer and sovereign rollups. Without modifying Bitcoin L1, an sBTC-like design with a decentralized open-membership group of signers is the most trust-minimized way to move BTC in and out of Bitcoin layers. Once the necessary upgrades to Bitcoin L1 can be made to enable validity rollups i.e., Bitcoin L1 can enforce BTC withdrawal from a layer, then the Stacks layer can also upgrade to benefit from it. + +Given a trust-minimized asset like sBTC is needed for sovereign rollups, with the launch of sBTC such sovereign rollups become even more interesting to deploy. The Stacks layer can potentially provide the decentralized group of signers for a trust-minimized BTC asset that can be used in a sovereign rollup, and DA comes directly from Bitcoin L1 e.g., with Ordinals. + +### Why Does Stacks Need a Token? + +This brings us to a central philosophical conversation in the world of crypto and Bitcoin, whether or not blockchains need tokens. + +Let's start by looking at the fundamental reason why tokens exist: to fund the maintenance and forward progress of a blockchain. + +Bitcoin is a token. It is a cryptocurrency that is used to incentivize miners to add new blocks to the chain. In Bitcoin's case, mining rewards are set on a predefined schedule, and once those mining rewards run out, the chain will need to survive on transaction fees alone. + +The purpose of a blockchain is to have a permanent historical record of every transaction that has ever occurred on the chain. Blockchains are basically ledgers. The token aspect is used as an incentive mechanism to secure and maintain the chain. + +This is why networks like Lightning and other P2P networks don't need tokens, they don't need to maintain a historical record. Channel-based solutions like Lightning rely on users opening 2-of-2 multisigs with each other. Once those channels are closed, the state disappears. When we are talking about a system that is supposed to maintain a global financial system, it is important for the maintenance of that system to be incentivized correctly. + +Let's look at this concept in the context of Stacks and its goals. Stacks seeks to provide smart contract functionality to Bitcoin, to serve as the programming rails for building a decentralized economy on top of Bitcoin. + +Many Bitcoin community members are skeptical of new tokens and rightly so. There are countless projects out there that force the use of a token on their project and in many cases a token is actually not needed. The Stacks project was started by Bitcoin builders who have a long history of building apps & protocols on Bitcoin L1 without any token (e.g., BNS launched in 2015 on Bitcoin L1 which was one of the largest protocols using OP\_RETURN on Bitcoin L1). So why did a bunch of Bitcoin builders decide to have a separate token for Stacks L2? Great question! Let's dig into the details. + +The Stacks token (STX) is primarily meant to be used for two things: + +{% stepper %} +{% step %} +### Incentives for Stacks L2 miners + +Newly minted STX are used to incentivize decentralized block production on Stacks L2. +{% endstep %} + +{% step %} +### Incentives for peg-out signers + +Signers participating in peg-out operations receive incentives in STX to economically align them with protocol rules. +{% endstep %} +{% endstepper %} + +The only way to remove the token is to build Stacks as a federated network like Liquid. In a federation the pre-selected group of companies control the mining and block production and a pre-selected group of companies need to be trusted for peg-out transactions. + +Stacks developers wanted to design an open and permissionless system. The only way to have a decentralized mining process is through incentives. As mentioned above, this is how Bitcoin works as well, where newly minted BTC are used as incentives to mine new blocks and anyone in the world can decide to become a miner. Anyone with BTC can mine the Stacks L2 chain, it is open and permissionless. + +Similarly, the way sBTC is designed is that the group of signers is open and permissionless (unlike a federation). These signers have economic incentives to correctly follow the protocol for peg-out requests. In a federation, users need to blindly trust the pre-set federation members to get their BTC out of the federation and back on Bitcoin L1. Stacks developers wanted to have an open, permissionless, and decentralized way to move BTC from Bitcoin L1 to Stacks L2 and back. This is made possible through economic incentives i.e., need for a token. + +{% hint style="info" %} +With more and more Bitcoin layers emerging, there is some nuance in this federated vs open network design. Some protocols like Botanix's Spiderchain offer an open network but have different incentive mechanisms. We dig into these in detail in the [Stacks Among Other Layers](stacks-among-other-layers.md) section. +{% endhint %} + +Other than these two reasons, STX is also used to pay gas fees for transactions. However, once the upcoming sBTC peg is live most of the economy of Stacks L2 is expected to follow a Bitcoin standard and work using BTC as the economic unit. It is expected that users will mostly interact just with Bitcoin and use BTC in wallets and apps (gas fees can be paid with BTC using atomic swaps in the background). It is important to note that BTC cannot be used for mining incentives on Stacks L2 because the only way to incentivize decentralized block production is through newly minted assets by the protocol (similar to how Bitcoin works itself) i.e., need for a token. + +### The Symbiotic Relationship Between Stacks and Bitcoin + +Stacks and Bitcoin complement each other. Stacks leverages the extreme decentralization of Bitcoin, its PoW consensus mechanism, and its value as a cryptocurrency. + +But Stacks also complements Bitcoin by unlocking additional use cases, thereby increasing its value over time. This also helps to solve the additional problem of the future maintainability of Bitcoin after the coinbase rewards are gone and Bitcoin has to function on transaction fees alone. + +If Bitcoin is seen as only a store of value, the economic density, meaning how much value is being exchanged, of each transaction will be minimal. But if Bitcoin is the underlying foundation for an entire decentralized economy, those [transactions become much more valuable](https://twitter.com/muneeb/status/1506976317618728963), increasing transaction fees. This is a crucial incentive for miners to continue securing the network as coinbase rewards drop. + +### Reading from Bitcoin State + +One of the things that gives the Stacks chain its superpowers in connecting with Bitcoin is not only how it connects to Bitcoin at a protocol level, discussed above, but also how we can utilize that Bitcoin at a programmatic level. + +That's where Clarity comes in. Clarity is the smart contract language for Stacks, and is how we actually build out a lot of the functionality we are talking about here. + +One of the often-touted features of Clarity is that it has access to the state of the Bitcoin chain built in, but how does it actually do that? Because of Stacks' PoX mechanism, every Stacks block is connected to a Bitcoin block, and can query Bitcoin block header hashes with the [`get-burn-block-info?` function](https://github.com/stacksgov/sips/blob/feat/sip-015/sips/sip-015/sip-015-network-upgrade.md#new-method-get-burn-block-info). + +This function allows us to pass in a Bitcoin block height and get back the header hash. The [`burn-block-height` Clarity keyword](https://docs.stacks.co/docs/write-smart-contracts/clarity-language/language-keywords#burn-block-height) will give us the current block height of the Bitcoin chain. + +However, `get-burn-block-info?` only returns data of the Bitcoin block at that height if it has already been processed and was created after the launch of the Stacks chain. So if we want to evaluate whether or not something happened on Bitcoin, we have to wait at least one block later to do so. + +This is step 1 of Clarity contracts being able to serve as the programming layer for Bitcoin. When a BTC transaction is initiated, the first thing that needs to happen is that a Clarity contract needs to become aware of it. This can happen manually by utilizing Clarity functions discussed above with the [BTC library](https://explorer.stacks.co/txid/0x8b112f2b50c1fa864997b7496aaad1e3940700309a3fdcc6c07f1c6f8b9cfb7b?chain=mainnet), as [Catamaran Swaps](https://docs.catamaranswaps.org/en/latest/catamaran.html) do. + +{% hint style="info" %} +Note that this process is made easier by the additional Clarity functions added in 2.1, like the `get-burn-block-info?` function we looked at above. +{% endhint %} + +Or we can automate (albeit at a cost of some centralization in our dapp) using an event-based architecture using something like Hiro's [chainhooks](https://www.hiro.so/blog/meet-4-new-features-in-clarinet#setting-up-trigger-actions-with-chainhooks), which will allow us to automatically trigger a Clarity contract call when a certain BTC transaction is initiated. + +This is the first component of using Stacks to build Bitcoin dapps, the read access to Bitcoin chain. + +Next up, let's dig a bit deeper into how exactly Stacks is "built on Bitcoin" by taking a look at Stacks' block production mechanism, Proof of Transfer. diff --git a/docs/learn/stacks-101/financial-incentive-and-security-budget.md b/docs/learn/stacks-101/financial-incentive-and-security-budget.md new file mode 100644 index 0000000000..7d0742bfde --- /dev/null +++ b/docs/learn/stacks-101/financial-incentive-and-security-budget.md @@ -0,0 +1,11 @@ +# Financial Incentive And Security Budget + +

source: Hiro blog

+ +In order to reorg the Stacks chain, someone must take control of at least 70% of the STX that are currently Stacked and conduct a 51% attack on Bitcoin itself. If acquired at market prices, then at the time of this writing, that amounts to spending nearly $1 billion USD in only the STX stacked. + +In addition to this, because of how Stacks achieves Bitcoin finality by not allowing forks, Stacks security budget reaches 51% of Bitcoin's mining power because in order to reverse the chain state you would need to reverse the Bitcoin chain state as well. + +Stackers have the new-found power to sign blocks in order to append them to the Stacks chain. However, some of them could refuse to sign, and ensure that no block ever reaches the 70% signature threshold. While this can happen by accident, this is not economically rational behavior -- if they stall the chain for too long, their STX loses their value, and furthermore, they cannot re-stack or liquidate their STX or activate PoX to earn BTC. Also, miners will stop mining if no blocks are getting confirmed, which eliminates their ongoing PoX payouts. + +The technical details of how this all works are discussed in the [Block Production](../block-production/) section. diff --git a/docs/learn/stacks-101/proof-of-transfer.md b/docs/learn/stacks-101/proof-of-transfer.md new file mode 100644 index 0000000000..9ef4a7cb13 --- /dev/null +++ b/docs/learn/stacks-101/proof-of-transfer.md @@ -0,0 +1,61 @@ +# Proof of Transfer (PoX) + +

source: Hiro blog

+ +In the previous sections, we took a look at the vision and ethos of Stacks and talked a lot about it being connected to Bitcoin and how it enables expanding functionality without modifying Bitcoin itself. + +In this section, we'll run through the block production mechanism that makes that happen, Proof of Transfer. + +This section will be a conceptual overview of Proof of Transfer. For more details on exactly how block production happens at a technical level, check out the section on [Block Production](../block-production/). + +Consensus algorithms for blockchains require compute or financial resources to secure the blockchain. The general practice of decentralized consensus is to make it practically infeasible for any single malicious actor to have enough computing power or ownership stake to attack the network. + +Popular consensus mechanisms in modern blockchains include proof of work, in which nodes dedicate computing resources, and proof of stake, in which nodes dedicate financial resources to secure the network. + +Proof of burn is another, less-frequently used consensus mechanism where miners compete by ‘burning’ (destroying) a proof of work cryptocurrency as a proxy for computing resources. + +Proof of Transfer (PoX) is an extension of the proof of burn mechanism. PoX uses the proof of work cryptocurrency of an established blockchain (Bitcoin in this case) to secure a new blockchain. However, unlike proof of burn, rather than burning the cryptocurrency, miners transfer the committed cryptocurrency to some other participants in the network (Stackers in this case). + +
+ +This allows network participants to secure the PoX cryptocurrency network and earn a reward in the base cryptocurrency (BTC). Thus, PoX blockchains are anchored on their chosen PoW chain. Stacks uses Bitcoin as its anchor chain. + +
+ +### Why Bitcoin? + +There are a number of reasons that Stacks chose Bitcoin as the blockchain to power consensus. It's the oldest blockchain protocol, having launched in 2009, and has become a recognized asset outside of the cryptocurrency community. BTC has held the highest market capitalization of any cryptocurrency for the past decade. + +Bitcoin champions simplicity and stability, and has stood the test of time. Influencing or attacking the network is infeasible or impractical for any potential hackers. It's one of the only cryptocurrencies to capture public attention. Bitcoin is a household name, and is recognized as an asset by governments, large corporations, and legacy banking institutions. Lastly, Bitcoin is largely considered a reliable store of value, and provides extensive infrastructure to support the PoX consensus mechanism. + +SIP-001 provides a full [list of reasons why Bitcoin was chosen to secure Stacks](https://github.com/stacksgov/sips/blob/main/sips/sip-001/sip-001-burn-election.md). + +{% hint style="info" %} +By the way, SIP stands for Stacks Improvement Proposal, and it's the process by which community members agree on making changes to the network. Reading the SIPs in detail is an excellent way to familiarize yourself with Stacks at the implementation level. All of the SIPs are available in the [SIPs section](../network-fundamentals/sips.md) of the docs. +{% endhint %} + +### Unlocking Bitcoin capital + +In the previous section we talked about Stacks being able to allow us to build a decentralized economy on top of Bitcoin and that PoX was a key piece of being able to do that. + +The reason is two-fold. First, as a part of this PoX mining process we have covered here, a hash of each Stacks block is recorded to the OP\_RETURN opcode of a Bitcoin transaction. If you aren't familiar, the OP\_RETURN opcode allows us to store up to 40 bytes of arbitrary data in a Bitcoin transaction. + +{% hint style="info" %} +This [Stack Exchange answer](https://bitcoin.stackexchange.com/questions/29554/explanation-of-what-an-op-return-transaction-looks-like) gives a good overview of the reasoning and history of this opcode. +{% endhint %} + +This is the first part of how Stacks inherits Bitcoin's security: its history is anchored block-by-block to the Bitcoin chain. Anyone can use merkle roots to verify these hashes to determine if the history is correct. + +Additionally, after the Nakamoto Upgrade, Stacks no longer forks on its own. Miners are required at a protocol level to build atop the last mined Stacks blocks, meaning that **Stacks is secured by both 100% of Bitcoin's hashrate in addition to the Stacks security budget from its miners.** We'll get into this process in more detail in the [Block Production](../block-production/) section. + +Additionally, part of this PoX process involves each Stacks block also knowing which Bitcoin block it is anchored to. Clarity, Stacks' smart contract language, has built-in functions for reading this data, such as [`get-block-info`](https://docs.stacks.co/docs/write-smart-contracts/clarity-language/language-functions#get-block-info), which returns, among other things, a field called `burnchain-header-hash`, which gives us the hash of the Bitcoin header corresponding to this Stacks block. + +This allows us to do really interesting things like trigger certain things to happen in a Clarity contract by watching the chain and verifying whether or not certain transactions occurred. You can see this in action in [Catamaran Swaps](https://docs.catamaranswaps.org/en/latest/catamaran.html), with other interesting projects like [Zest](https://www.zestprotocol.com/) seeking to expand on this functionality. + +The ultimate goal of all this is to enable the vision of web3, building a decentralized economy and enabling true user ownership of assets and data, on top of Bitcoin as a settlement layer, and using Bitcoin as a base decentralized money. + +### Proof of Transfer Contracts and Technical Details + +The Proof of Transfer functionality is implemented on the Stacks chain via a [Clarity smart contract](https://explorer.hiro.so/txid/0xc6d6e6ec82cabb2d7a9f4b85fcc298778d01186cabaee01685537aca390cdb46?chain=mainnet). An overview of this contract is available in the docs. + +You can see the original design for stacking and proof of transfer by reading the relevant SIP, [SIP-007](https://github.com/stacksgov/sips/blob/main/sips/sip-007/sip-007-stacking-consensus.md). You can also utilize [Hiro's API](https://docs.hiro.so/api#tag/Info/operation/get_pox_info) to get proof of transfer details including the relevant contract address. diff --git a/docs/learn/stacks-101/stacks-among-other-layers.md b/docs/learn/stacks-101/stacks-among-other-layers.md new file mode 100644 index 0000000000..dd072d1237 --- /dev/null +++ b/docs/learn/stacks-101/stacks-among-other-layers.md @@ -0,0 +1,81 @@ +# Stacks Among Other Layers + +

source: Hiro blog

+ +Recently, we have seen a flurry of new "Bitcoin layers" popping up across the ecosystem as the market has finally woken up to the idea. + +However, not all Bitcoin layers are made equal. While a large chunk of these projects are vaporware riding the hype train, there are several projects that are making a good faith effort to grow the Bitcoin economy and build on top of Bitcooin using various approaches. + +The [Bitcoin Layers project](https://www.bitcoinlayers.org/) is an excellent place to begin learning about these different layers. In addition, here we've broken down how Stacks compares to some of the most promising Bitcoin L2 solutions so you can begin to learn about them all and make an educated decision on which to use. + +### What is a Bitcoin Layer? + +It's important to define terms, especially in a new and evolving ecosystem like web3, and an even newer and more rapidly evolving subecosystem like Bitcoin layers. + +For the purpose of this document and comparison, we can use the following definition of a Bitcoin layer: A Bitcoin layer is a separate distributed computing system built either alongside or on top of Bitcoin for the purpose of enhancing its scalability, functionality, or both. + +That definition is intentionally general, and encompasses many different projects like L2s, sidechains, federated, open network, etc. + +#### Technical vs Economic Considerations + +It's important to understand that when designing blockchains, especially layer 2 systems, we have to consider both technical and economic factors. Since a core component of a blockchain system is money, we need to make sure that our systems are both technically robust and economically efficient. And we need to accomplish both of these things while maintaining decentralization. + +While it is trivial to create a trusted bridge to bridge BTC from the L1 to a L2, that defeats the purpose of blockchain technology in general, since the goal should be to create permissionless, trust-minimized systems. + +At the same time, a great technical solution that doesn't consider the economic incentives of the decentralized actors running the network will not have a sustainable path to long-term adoption and viability. + +This balance is why Stacks has chosen the design it has, to balance both achieving the technical features of a Bitcoin L2 like security inheritance and a trust-minimized BTC peg with the economic incentives for the participants in the ecosystem to maintain it in the long term. + +As an example of this, Galaxy recently [conducted research](https://www.galaxy.com/insights/research/exploring-bitcoin-for-data-availability/) on this topic and found that a Bitcoin rollup "will need to generate approximately between $1.9m and $9.63m in revenue from L2 transaction fees per month." That is a significant number and again highlights the need to consider both technical and economic factors when designing Bitcoin layers. + +### Popular Bitcoin Layers Compared + +#### Lightning + +Lightning is probably the most well-known Bitcoin layer, and is primarily designed to address scalability issues. Lightning functions as a separate P2P network from Bitcoin, allowing participants to move their BTC from the main chain to Lightning, conduct multiple transactions on Lightning, and then send the final result to the BTC chain where it is finalized. + +This is actually a completely separate problem from what Stacks is trying to address. Where Lightning takes the existing functionality of Bitcoin and makes it much more scalable, Stacks is seeking to expand Bitcoin's functionality to do things you can't do now. + +Crucially, Lightning is ephemeral, meaning it has no state management. There is no continuous record of what has happened on the Lightning network, only current channels. Once users close their channel and their transactions are written back to the Bitcoin chain, they are gone. + +A key component of fully-expressive smart contracts is that they maintain a permanent historical record of all transactions that have happened on the chain. + +Bitcoin does this now, but its scripting language is very limited. So where Lightning seeks to make existing Bitcoin functionality happen faster, Stacks seeks to add new functionality. + +#### RSK + +Like Stacks, [RSK](https://www.rsk.co/) seeks to add additional functionality to Bitcoin, but it goes about that process differently than Stacks. + +RSK is a merge-mined chain, meaning that it is mined concurrently with Bitcoin. Stacks has its own miners and mining process, and its own economic value and security that is a function of that token value, more on this below. + +There are multiple perspectives to look at this from. Because RSK is merge-mined, Bitcoin miners are also the ones mining RSK blocks, and RSK does not have its own token. + +RSK can only exist with opt-in from Bitcoin miners and mining rewards are highly dependent on transaction volume. + +This also opens up a wider discussion on the costs and benefits of having a separate token, which we'll get into below a bit when we discuss rollups. + +RSK is also EVM-compatible, where Stacks uses Clarity and the Clarity VM. + +#### Liquid + +[Liquid](https://liquid.net/) is a federated network focused on unlocking more advanced financial capabilities with Bitcoin. Being federated, Liquid is not an open network, and thus not decentralized. + +The Liquid consensus mechanism is managed by 15 functionaries, who handle the transaction processing and validating. Liquid also does not support general-purpose applications, but is solely focused on financial applications. + +For another perspective, Hiro wrote an [excellent post](https://www.hiro.so/blog/building-on-bitcoin-project-comparison) comparing Stacks with other Bitcoin projects. + +#### Bitcoin Rollups + +Rollups are an exciting development for scaling decentralized applications. There are many different types of rollups; they're broadly divided into ZK rollups and Optimistic rollups, although other classifications are also there (see [this overview](https://era.zksync.io/docs/dev/fundamentals/rollups.html#what-are-rollups)). + +Rollups are generally considered layer-2 (L2) technology that runs on top of a layer-1 blockchain like Bitcoin or Ethereum. A critical aspect of rollups is the trustless nature where logic running on the L1 chain can determine whether something that happened on the rollup was valid. This is not true for all types of rollups, and there is some fuzziness around exact definitions. [Sovereign rollups](https://blog.celestia.org/sovereign-rollup-chains/), for example, only use the underlying L1 for data availability (DA) and not for consensus. + +Most of the rollups work on Ethereum uses Ethereum L1 both as a data availability layer, and for consensus, i.e., the validity of rollup transactions is determined by logic running on Ethereum L1. Newer systems, [like Celestia](https://celestia.org/), are taking a more modular approach and are separating DA from consensus. One interesting aspect of separating DA is that more established and durable chains like Bitcoin can be used for DA as well. Below is an interesting comparison of sidechains and two types of rollups possible on Bitcoin (John Light posted this [on Twitter](https://twitter.com/lightcoin/status/1630301411962388481?s=20)): + +
+ +This image broadly means developers can build sovereign rollups on Bitcoin today, but you'll need a "trusted" setup for moving BTC in and out of the rollup. In fact, people are already doing this -- see the recent [Rollkit announcement](https://rollkit.dev/blog/sovereign-rollups-on-bitcoin/). To build validity rollups, meaning Bitcoin L1 enforces BTC withdrawals from the rollup, you'll need modifications to Bitcoin L1. See [this overview](https://bitcoinrollups.org/) for more details. + +One important nuance here is the cost required to effectively run a rollup on Bitcoin as discussed in the Galaxy research report linked in the first section. + +Now we have a solid grasp of how Stacks works and how it fits in among other layers. Let's begin to dig into some of the technical implementation details and see how Stacks actually works. diff --git a/docs/learn/stacks-101/what-is-stacks.md b/docs/learn/stacks-101/what-is-stacks.md new file mode 100644 index 0000000000..8f02e001d9 --- /dev/null +++ b/docs/learn/stacks-101/what-is-stacks.md @@ -0,0 +1,74 @@ +# What Is Stacks? + +

source: Hiro Blog

+ +We can get an idea of the goal and ethos behind Stacks by looking at [how Satoshi envisioned generalizing Bitcoin](https://satoshi.nakamotoinstitute.org/posts/bitcointalk/threads/244/#222) back in 2010: + +> "...to be a completely separate network and separate block chain, yet share CPU power with Bitcoin...all networks in the world would share combined CPU power, increasing the total strength." + +This is a major theme in the design decisions for Stacks. A bit of a contradiction in the Bitcoin world, the Stacks network is a Bitcoin L2, but it does have its own token. + +This is an intentional and critical design decision primarily for the purpose of maintaining decentralization, rather than needing to rely on a federation. + +If that's confusing or you are skeptical, that's understandable — we'll be diving deeper into these ideas as we go through the docs. + +### Stacks and the Purpose of Blockchain Technology + +When evaluating new blockchain technologies, it's important to keep the original intent and purpose of them intact. If we go back to Bitcoin, it was originally designed to be: + +* Decentralized +* Immutable +* Secure + +You've likely heard of the blockchain trilemma — the problem of trying to balance decentralization, scalability, and security of a blockchain network. + +Stacks takes the approach of solving this trilemma by separating out chains into layers. + +So at the bottom, you have the foundational layer: Bitcoin. + +Bitcoin is the most decentralized, most secure, and most immutable blockchain network. However, that comes with a few tradeoffs. + +* Bitcoin is very slow compared to other networks. Bitcoin only has a new block written once every \~10 minutes, making its throughput negligible compared to networks designed for speed like Solana. +* Bitcoin is also "boring". Ethereum came along after Bitcoin and sought to do the same thing for software that Bitcoin did for money. Ethereum's goal is to be a decentralized supercomputer of sorts, serving as a global compute environment for smart contracts (code that is written to a blockchain). +* Bitcoin is not scalable. Because every new block must propagate to every node on the network, Bitcoin can only run as fast as the slowest node in the network. + +Now we are seeing the rise of modular blockchain networks like Cosmos that are designed to make it easy for people to spin up their own blockchain networks. + +While most new blockchain protocols popping up these days see these properties as negatives and seek to eliminate them, the Stacks community sees things differently. + +### The Stacks Way + +Stacks takes a layered approach: the foundational settlement layer is Bitcoin, and scalability and functionality are added on top of that using layers. + +There are many different types of L2s and different ways they can be built. They all come with [different tradeoffs](stacks-among-other-layers.md) and have their own way of accomplishing the goals of scalability or functionality. + +By taking this layered approach, we are able to have all of the same functionality as chains like Ethereum, but built on Bitcoin. + +So Stacks is a Bitcoin layer 2 with some unique properties, like having its own token, that acts as an incentive mechanism to maintain a historical ledger of all of its transactions and operate with its own security budget (in addition to Bitcoin's security budget — more on this in the next section). + +This is one of the things that separates Stacks from other Bitcoin layers like Lightning. + +* Lightning doesn't add any additional functionality to Bitcoin; it simply helps to scale functionality Bitcoin already has and helps it operate faster. Lightning is also ephemeral — it has no permanent state — and so is unsuitable for things like smart contracts that need to keep track of data and maintain state. +* Contrast this to Stacks, which adds additional functionality to Bitcoin but still ultimately settles to Bitcoin (we'll cover this in the next section as well). + +The benefit is that we can maintain a separation of concerns and keep Bitcoin simple and sturdy, chugging along producing blocks, while adding additional layers for functionality and speed. If those other layers were compromised, the foundational layer would remain unaffected. + +This is important when building systems intended to be a global decentralized money (Bitcoin) and a decentralized economy built on top of that money (Stacks). + +The STX token is a separate token used to incentivize honest block production. It does not represent pegged Bitcoin (there is a separate Bitcoin peg called [sBTC](../sbtc/) for that purpose). While this may ruffle some feathers among parts of the Bitcoin community, it has several advantages. + +By implementing a token into the Stacks chain, we provide additional economic incentive for miners to produce Stacks blocks honestly. + +This token provides additional incentive as a way to grow the chain. Rather than relying on altruism to produce blocks and grow the chain, we can incentivize builders, token-holders, and investors all at the same time by having a token. + +The ICO scams of 2017 put a bad taste in many people's mouths, which has justifiably made a lot of people skeptical of every new blockchain project that pops up with a new token. + +But the problem with many of those projects was they had no actual value, weren't anchored to anything else of value, and provided no real utility. + +With a project like Stacks, we have real utility in the sense of serving as a way to utilize Bitcoin and make it a productive asset in a decentralized way. This is a key point: currently the only common ways to make Bitcoin productive are to give it to a custodial service or transfer it off the Bitcoin chain via something like wBTC on Ethereum. + +Stacks allows us to do this while ultimately still settling to the Bitcoin chain. + +In addition, Stacks allows us to build decentralized and censorship-resistant software utilizing Bitcoin as the foundational settlement layer. Eventually, the goal is to build a network of financial systems and decentralized software products that all utilize Bitcoin as their money. + +With that context, let's dive into exactly how Stacks is connected to Bitcoin. diff --git a/docs/learn/transactions/README.md b/docs/learn/transactions/README.md new file mode 100644 index 0000000000..8e77cb2089 --- /dev/null +++ b/docs/learn/transactions/README.md @@ -0,0 +1,3 @@ +# Transactions + +Transactions are a key component of the Stacks chain and are the primary way users will interact with it. In this section, we'll cover how transactions work and give an introduction to post conditions, an additional security feature of Stacks that allows client-side developers to enforce certain conditions to protect users from interacting with malicious contracts. diff --git a/docs/learn/transactions/how-transactions-work.md b/docs/learn/transactions/how-transactions-work.md new file mode 100644 index 0000000000..f63758f6aa --- /dev/null +++ b/docs/learn/transactions/how-transactions-work.md @@ -0,0 +1,74 @@ +# How Transactions Work + +

source: Hiro blog

+ +### Introduction + +Transactions are the fundamental unit of execution in the Stacks blockchain. Each transaction is originated from a Stacks account, and is retained in the Stacks blockchain history for eternity. This guide helps you understand Stacks transactions. + +### Lifecycle + +Transactions go through phases before being finally confirmed, and available for all, on the Stacks 2.0 network. + +
+ +{% stepper %} +{% step %} +### Generate + +Transactions are assembled according to the encoding specification. +{% endstep %} + +{% step %} +### Validate and sign + +Transactions are validated to confirm they are well-formed. Required signatures are filled in. +{% endstep %} + +{% step %} +### Broadcast + +Transactions are sent to a node. +{% endstep %} + +{% step %} +### Register + +A miner receives transactions, verifies, and adds them to the ["mempool,"](https://academy.binance.com/en/glossary/mempool) a holding area for all the pending transactions. +{% endstep %} + +{% step %} +### Process + +Miners review the mempool and select transactions for the next block to be mined. Depending on the transaction type, different actions can happen during this step. For example, post-conditions could be verified for a token transfer, smart-contract defined tokens could be minted, or an attempt to call an existing smart contract method could be made. +{% endstep %} + +{% step %} +### Confirm + +Miners successfully propose blocks with a set of transactions. The transactions inside are successfully propagated to the network when the stackers approve them. +{% endstep %} +{% endstepper %} + +{% hint style="info" %} +A transaction can have one of three states once it is registered: `pending`, `success`, or `failed`. +{% endhint %} + +### Types + +Stacks supports a set of different transaction types: + +| **Type** | **Value** | **Description** | +| ------------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Tenure change | `TenureChange` | A tenure change is an event in the existing Stacks blockchain when one miner assumes responsibility for creating new stacks blocks from another miner. A change in tenure occurs when a Stacks block is discovered from a cryptographic sortition. Carried out by stackers. | +| Tenure change block found | `TenureChange-BlockFound` | A `TenureChange-BlockFound` transaction is induced by a winning sortition. This causes the new miner to start producing blocks, and stops the current miner from producing more blocks. | +| Tenure change extend | `TenureChange-Extend` | A `TenureChange-Extend`, which is induced by Stackers, resets the current tenure's ongoing execution budget, thereby allowing the miner to continue producing blocks. | +| Token transfer | `token_transfer` | Asset transfer from a sender to a recipient | +| Contract deploy | `smart_contract` | Contract instantiation | +| Contract call | `contract_call` | Contract call for a public, non read-only function | + +A sample of each transaction type can be found in the [Stacks Blockchain API response definition for transactions](https://docs.hiro.so/stacks/api/transactions/get-transaction). + +{% hint style="info" %} +Read-only contract call calls do **not** require transactions. Read more about it in the network guide. +{% endhint %} diff --git a/docs/learn/transactions/post-conditions.md b/docs/learn/transactions/post-conditions.md new file mode 100644 index 0000000000..91edcaca66 --- /dev/null +++ b/docs/learn/transactions/post-conditions.md @@ -0,0 +1,37 @@ +# Post Conditions + +

source: Hiro blog

+ +Post conditions are one of the most interesting and unique aspects of Stacks. + +From the beginning, safety and security has been at the heart of the Stacks ethos and formed the foundation of architecture decisions when building it. + +Like Clarity, Stacks' smart contract programming language, post conditions were specifically built and designed to solve the problem of user safety when interacting with blockchain applications. + +So what are they and how do they work? + +### How Post Conditions Work + +Post conditions are conditions that are set on the client side to ensure that a smart contract does not perform any unexpected behavior. + +Let's look at an example to make this more concrete. + +Let's say a user is on an NFT marketplace and is expecting to purchase an NFT for 100 STX. Using post conditions, the developer who is building the frontend of the application can add in post conditions to ensure that this is in fact what happens when the user initiates the transaction. + +If it does not, the transaction will abort and the user won't be out anything except the transaction fee. + +It's important to note that post conditions do not live in smart contracts. They are designed to be an extra layer of security on top of smart contracts. + +The problem they help address is a user interacting with a malicious smart contract that attempts to do something the user does not expect. + +But rather than simply being a UI feature of a wallet, these post conditions are built into the Stacks blockchain itself and are enforced at the protocol level. + +When you use a Stacks wallet like the Hiro web wallet and initiate a transaction, the wallet will display the post conditions set by the developer and tell the user exactly what is going to happen. If the action taken by the smart contract matches, the transaction goes through fine, otherwise it aborts. + +Here's what that looks like: + +
+ +In this example, if the smart contract does not transfer one fabulous-frog NFT and and take 50 STX from the user, the transaction will abort. + +You can learn more about how post conditions work in [SIP-005](https://github.com/stacksgov/sips/blob/main/sips/sip-005/sip-005-blocks-and-transactions.md#transaction-post-conditions) and how to utilize them in your applications in Hiro's excellent [post conditions tutorial](https://docs.hiro.so/stacks/stacks.js/guides/post-conditions). diff --git a/docs/operate/.gitbook.yaml b/docs/operate/.gitbook.yaml new file mode 100644 index 0000000000..9c25db3cbb --- /dev/null +++ b/docs/operate/.gitbook.yaml @@ -0,0 +1,31 @@ +root: ./ + +redirects: + + # nodes-and-miners to run-a-node redirects + guides-and-tutorials/nodes-and-miners: README.md + guides-and-tutorials/nodes-and-miners/run-a-node-with-docker: run-a-node/run-a-node-with-docker.md + guides-and-tutorials/nodes-and-miners/run-a-node-with-digital-ocean: run-a-node/run-a-node-with-digital-ocean.md + + # guides-and-tutorials/run-a-miner to operate/run-a-miner redirects + guides-and-tutorials/run-a-miner/mine-mainnet-stacks-tokens: run-a-miner/mine-mainnet-stacks-tokens.md + guides-and-tutorials/run-a-miner/mine-testnet-stacks-tokens: run-a-miner/mine-testnet-stacks-tokens.md + guides-and-tutorials/run-a-miner/miner-costs-and-fees: run-a-miner/miner-costs-and-fees.md + guides-and-tutorials/run-a-miner/miner-prerequisites: run-a-miner/miner-prerequisites.md + guides-and-tutorials/run-a-miner/verify-miner: run-a-miner/verify-miner.md + + # guides-and-tutorials/running-a-signer to operate/run-a-signer redirects + guides-and-tutorials/running-a-signer/best-practices-to-run-a-signer: run-a-signer/best-practices-to-run-a-signer.md + guides-and-tutorials/running-a-signer/how-to-monitor-signer: run-a-signer/how-to-monitor-signer.md + guides-and-tutorials/running-a-signer/how-to-read-signer-logs: run-a-signer/how-to-read-signer-logs.md + guides-and-tutorials/running-a-signer/opsec-best-practices: run-a-signer/opsec-best-practices.md + guides-and-tutorials/running-a-signer/signer-quickstart: run-a-signer/signer-quickstart.md + + guides-and-tutorials/best-practices-to-snapshot-the-chainstate: snapshot-the-chainstate.md + + # guides-and-tutorials/stack-stx to operate/stacking-stx redirects + guides-and-tutorials/stack-stx/increase-stacking: stacking-stx/increase-stacked-position.md + guides-and-tutorials/stack-stx/operate-a-pool: stacking-stx/operate-a-stacking-pool.md + guides-and-tutorials/stack-stx/stack-with-a-pool: stacking-stx/stack-with-a-pool.md + guides-and-tutorials/stack-stx/stacking-flow: stacking-stx/solo-stack.md + guides-and-tutorials/stack-stx/stop-stacking: stacking-stx/stop-stacking.md diff --git a/docs/operate/.gitbook/assets/Frame 316126262 (1).jpg b/docs/operate/.gitbook/assets/Frame 316126262 (1).jpg new file mode 100644 index 0000000000..b80d52e8b7 Binary files /dev/null and b/docs/operate/.gitbook/assets/Frame 316126262 (1).jpg differ diff --git a/docs/operate/.gitbook/assets/Frame 316126262.jpg b/docs/operate/.gitbook/assets/Frame 316126262.jpg new file mode 100644 index 0000000000..b80d52e8b7 Binary files /dev/null and b/docs/operate/.gitbook/assets/Frame 316126262.jpg differ diff --git a/docs/operate/README.md b/docs/operate/README.md new file mode 100644 index 0000000000..5396379e37 --- /dev/null +++ b/docs/operate/README.md @@ -0,0 +1,20 @@ +# Run a Node + +
+ +This section walks through the technical setup steps required to run Stacks Blockchain nodes and miners. There are multiple options available for running a node, including Docker, Digital Ocean, and Render. + +Running your own Stacks node is a great way to increase the decentralization of the ecosystem and avoid relying on third-party centralized providers. + +## Minimum viable requirements + +While you can run a node using these specs, it's recommended to assign more than the minimum for better performance. + +{% hint style="warning" %} +* ⚠️ docker-compose version `2.2.2` or greater is **required** — https://docs.docker.com/compose/install/ +* **8 GB memory** if running only a Stacks node +* **16 GB memory** if running Stacks + Bitcoin node +* **2 vCPU** +* **1 TB disk** for Stacks node +* **1 TB disk** for Bitcoin node +{% endhint %} diff --git a/docs/operate/SUMMARY.md b/docs/operate/SUMMARY.md new file mode 100644 index 0000000000..968520db00 --- /dev/null +++ b/docs/operate/SUMMARY.md @@ -0,0 +1,28 @@ +# Table of contents + +* [Run a Node](README.md) + * [Run a Node with Docker](run-a-node/run-a-node-with-docker.md) + * [Run a Node with Digital Ocean](run-a-node/run-a-node-with-digital-ocean.md) + * [Run a Node with a Hosted Provider](run-a-node/run-a-node-with-a-hosted-provider.md) + * [Run a Node with Quicknode](run-a-node/run-a-node-with-quicknode.md) + * [Run a Bitcoin Node](run-a-node/run-a-bitcoin-node.md) + * [Run a Pruned Bitcoin Node](run-a-node/run-a-pruned-bitcoin-node.md) +* [Run a Miner](run-a-miner/README.md) + * [Miner Prerequisites](run-a-miner/miner-prerequisites.md) + * [Miner Costs and Fees](run-a-miner/miner-costs-and-fees.md) + * [Mine Testnet Stacks Tokens](run-a-miner/mine-testnet-stacks-tokens.md) + * [Mine Mainnet Stacks Tokens](run-a-miner/mine-mainnet-stacks-tokens.md) + * [Verify Miner](run-a-miner/verify-miner.md) +* [Run a Signer](run-a-signer/README.md) + * [Signer Quickstart](run-a-signer/signer-quickstart.md) + * [How to Read Signer Logs](run-a-signer/how-to-read-signer-logs.md) + * [How to Monitor Signer](run-a-signer/how-to-monitor-signer.md) + * [Best Practices to Run a Signer](run-a-signer/best-practices-to-run-a-signer.md) + * [OpSec Best Practices](run-a-signer/opsec-best-practices.md) +* [Snapshot the Chainstate](snapshot-the-chainstate.md) +* [Stacking STX](stacking-stx/README.md) + * [Solo Stack](stacking-stx/solo-stack.md) + * [Operate a Stacking Pool](stacking-stx/operate-a-stacking-pool.md) + * [Stack with a Pool](stacking-stx/stack-with-a-pool.md) + * [Increase Stacked Position](stacking-stx/increase-stacked-position.md) + * [Stop Stacking](stacking-stx/stop-stacking.md) diff --git a/docs/operate/run-a-miner/README.md b/docs/operate/run-a-miner/README.md new file mode 100644 index 0000000000..f0da72f8a3 --- /dev/null +++ b/docs/operate/run-a-miner/README.md @@ -0,0 +1,5 @@ +# Run a Miner + +If you are interested in running a Stacks miner, there are a few things you'll need to understand. Running a miner is similar to running a node, but you'll need to set up some additional configuration. + +These guides will help you get up and running with both a testnet and mainnet Stacks miner. diff --git a/docs/operate/run-a-miner/mine-mainnet-stacks-tokens.md b/docs/operate/run-a-miner/mine-mainnet-stacks-tokens.md new file mode 100644 index 0000000000..8f1df5a046 --- /dev/null +++ b/docs/operate/run-a-miner/mine-mainnet-stacks-tokens.md @@ -0,0 +1,302 @@ +# Mine Mainnet Stacks Tokens + +### Introduction + +For more on the technical details of mining, please review the mining guide. + +The following is an abridged version of the [walkthrough here](https://github.com/stacksfoundation/miner-docs), written for a Linux system. If you're on Windows or MacOS, there will be some slight modifications needed (PR's welcome!). + +If you're interested in mining on the Stacks mainnet, you can find instructions on how to do that here: + +### Running a Bitcoin Mainnet Full Node + +To participate as a miner on mainnet, you must have access to a mainnet bitcoin node with a wallet (and the wallet's private key). One way to accomplish this is to run bitcoin locally. + +* [Ensure your computer meets the minimum hardware requirements before continuing.](https://bitcoin.org/en/bitcoin-core/features/requirements#system-requirements) + +First, download a [bitcoin binary](https://bitcoin.org/en/download), or [build from source](https://github.com/stacksfoundation/miner-docs/blob/main/bitcoin.md#source-install) (_there may be some extra requirements to building,_ [_defined here_](https://github.com/stacksfoundation/miner-docs/blob/main/prerequisites.md#install-required-packages)). + +If you want to learn more about the technical details of mining, please review the mining guide: + +{% hint style="info" %} +**Tip:** It is recommended to use a persistent location for the chainstate, in the steps below we're using `/bitcoin`. +{% endhint %} + +#### Update the Bitcoin Configuration File + +Next, update the bitcoin configuration: + +* **Optional, but recommended:** Use a persistent directory to store the Bitcoin chainstate, i.e. `datadir=/bitcoin`. +* **Optional, but recommended:** Update the `rpcallowip` value to only allow `127.0.0.1`, or the stacks miner IPv4. +* Modify the `rpcuser` and `rpcpassword` values from the defaults below. +* Store the following configuration somewhere on your filesystem (ex: `$HOME/bitcoin.conf`). + +```toml +server=1 +disablewallet=0 +datadir=/bitcoin +rpcuser=btcuser +rpcpassword=btcpass +rpcallowip=0.0.0.0/0 +bind=0.0.0.0:8333 +rpcbind=0.0.0.0:8332 +dbcache=512 +banscore=1 +rpcthreads=256 +rpcworkqueue=256 +rpctimeout=100 +txindex=1 +``` + +#### Start Bitcoin + +Finally, start `bitcoind` as follows (adjust the `conf` path to where it was created in the previous step, i.e. `$HOME/bitcoin.conf`): + +```bash +bitcoind -conf=$HOME/bitcoin.conf +``` + +{% hint style="info" %} +**Note:** It will take a few hours for the node to synchronize with Bitcoin Mainnet. +{% endhint %} + +While it's syncing, you can track the progress with `bitcoin-cli` or the logfile (will be located where the chainstate is stored, i.e. `/bitcoin/debug.log`): + +```bash +$ bitcoin-cli \ + -rpcconnect=127.0.0.1 \ + -rpcport=8332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpass \ +getblockchaininfo | jq .blocks +836745 +``` + +### Running a Stacks Blockchain miner + +First, download the latest tagged [stacks blockchain binary](https://github.com/stacks-network/stacks-blockchain/releases/latest), or [build from source](https://github.com/stacksfoundation/miner-docs/blob/main/stacks-blockchain.md#build-and-install-stacks-blockchain-from-source) (_there may be some extra requirements to building,_ [_defined here_](https://github.com/stacksfoundation/miner-docs/blob/main/prerequisites.md#install-required-packages)). + +{% hint style="info" %} +**Tip:** It is recommended to use a persistent location for the chainstate, in the steps below we're using `/stacks-blockchain`. +{% endhint %} + +#### Generate a keychain + +First, a keychain needs to be generated. With this keychain, we'll purchase some BTC from a cryptocurrency exchange, and then use that BTC to start mining. + +To create a keychain, the simplest way is to use the [stacks-cli](https://docs.hiro.so/references/stacks-cli) with the `make_keychain` command. + +```bash +npx @stacks/cli make_keychain 2>/dev/null | jq -r +``` + +After this runs, you should see some JSON printed to the screen that looks like this: + +```json +{ + "mnemonic": "spare decade dog ghost luxury churn flat lizard inch nephew nut drop huge divert mother soccer father zebra resist later twin vocal slender detail", + "keyInfo": { + "privateKey": "ooxeemeitar4ahw0ca8anu4thae7aephahshae1pahtae5oocahthahho4ahn7eici", + "address": "SPTXOG3AIHOHNAEH5AU6IEX9OOTOH8SEIWEI5IJ9", + "btcAddress": "Ook6goo1Jee5ZuPualeiqu9RiN8wooshoo", + "wif": "rohCie2ein2chaed9kaiyoo6zo1aeQu1yae4phooShov2oosh4ox", + "index": 0 + } +} +``` + +{% hint style="danger" %} +**Do not lose this information** - we'll need to use the `privateKey`, `btcAddress` and `wif` fields in later steps. +{% endhint %} + +The above `wif` (`Kyk49jsPGen5C1ThhyJJH4CndLk8yLESuQJVGsbbTV3FFF9CRTJG`) will then need to be imported into the bitcoin mainnet network. + +Next, a bitcoin wallet is created: + +```bash +bitcoin-cli \ + -rpcconnect=127.0.0.1 \ + -rpcport=8332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpass \ + createwallet \ + wallet_name="miner" \ + disable_private_keys=false \ + blank=false \ + passphrase="" \ + avoid_reuse=false \ + descriptors=false \ + load_on_startup=true +``` + +Now, import your wif (bitcoin private key) inside the newly created wallet. + +{% hint style="info" %} +**Note:** Be sure to replace `` with the wif value in the `Generate a keychain` step. +{% endhint %} + +```bash +bitcoin-cli \ + -rpcport=8332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpassword \ + importprivkey +``` + +{% hint style="info" %} +**Note:** The import may take a while, because a wallet rescan is triggered. After the import has completed successfully, you can check that the address is imported with `getaddressinfo`. +{% endhint %} + +```bash +bitcoin-cli \ + -rpcconnect=127.0.0.1 \ + -rpcport=8332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpass \ + getaddressinfo +``` + +Once imported, we need to get some BTC to that address. You should be able to transfer BTC to this address using a cryptocurrency exchange such as [Coinbase](https://www.coinbase.com/), [Binance](https://www.binance.com/), or [Kraken](https://www.kraken.com/). + +#### Update the Stacks Blockchain Configuration File + +Now, we need to configure our node to use this Bitcoin keychain. Copy the [sample mainnet miner config](https://raw.githubusercontent.com/stacks-network/stacks-blockchain/master/testnet/stacks-node/conf/mainnet-miner-conf.toml) to your local machine in a _memorable_ location like `$HOME/mainnet-miner-conf.toml`. + +Next, update the stacks configuration: + +* **Optional, but recommended:** Use a persistent directory to store the Stacks chainstate, i.e. `working_dir = "/stacks-blockchain"` +* From the `make_keychain` step, modify the `seed` and `mining_key` values with `privatekey` +* Store the following configuration somewhere on your filesystem (ex: `$HOME/mainnet-miner-conf.toml`) + +```toml +[node] +working_dir = "/stacks-blockchain" +rpc_bind = "0.0.0.0:20443" +p2p_bind = "0.0.0.0:20444" +seed = "" +miner = true +bootstrap_node = "02196f005965cebe6ddc3901b7b1cc1aa7a88f305bb8c5893456b8f9a605923893@seed.mainnet.hiro.so:20444,02539449ad94e6e6392d8c1deb2b4e61f80ae2a18964349bc14336d8b903c46a8c@cet.stacksnodes.org:20444,02ececc8ce79b8adf813f13a0255f8ae58d4357309ba0cedd523d9f1a306fcfb79@sgt.stacksnodes.org:20444,0303144ba518fe7a0fb56a8a7d488f950307a4330f146e1e1458fc63fb33defe96@est.stacksnodes.org:20444" +mine_microblocks = false + +[burnchain] +wallet_name = "miner" +chain = "bitcoin" +mode = "mainnet" +peer_host = "127.0.0.1" +username = "" +password = "" +rpc_port = 8332 +peer_port = 8333 +satoshis_per_byte = 100 +burn_fee_cap = 450000 + +[miner] +mining_key = "" +activated_vrf_key_path = "/stacks-blockchain/saved_vrf_key.json" + +[connection_options] +private_neighbors = false +``` + +#### Start the Stacks Blockchain + +To run your miner, run this in the command line: + +```bash +stacks-node start --config $HOME/mainnet-miner-conf.toml +``` + +Your node should start. It will take some time to sync, and then your miner will be running. + +#### Enable Debug Logging + +In case you are running into issues or would like to see verbose logging, you can run your node with debug logging enabled. In the command line, run: + +```bash +STACKS_LOG_DEBUG=1 stacks-node start --config $HOME/mainnet-miner-conf.toml +``` + +### Optional: Running a Stacks Blockchain miner with Docker + +Alternatively, you can run a Stacks mainnet miner with Docker. + +{% hint style="warning" %} +Ensure you have [Docker](https://docs.docker.com/get-docker/) installed. +{% endhint %} + +#### Generate a Keychain and Get Some Tokens + +Generate a keychain: + +```bash +docker run -i node:20-alpine npx @stacks/cli make_keychain 2>/dev/null | jq -r +``` + +We need to get some BTC to that address. You should be able to transfer BTC to this address using a cryptocurrency exchange such as [Coinbase](https://www.coinbase.com/), [Binance](https://www.binance.com/), or [Kraken](https://www.kraken.com/). + +#### Update Stacks Blockchain Docker Configuration File + +Use the steps outlined above to create the configuration file. + +#### Start the Stacks Blockchain miner with Docker + +{% hint style="info" %} +**Info:** The ENV VARS `RUST_BACKTRACE` and `STACKS_LOG_DEBUG` are optional. If removed, debug logs will be disabled. +{% endhint %} + +```bash +docker run -d \ + --name stacks_miner \ + --rm \ + --network host \ + -e RUST_BACKTRACE="full" \ + -e STACKS_LOG_DEBUG="1" \ + -v "$HOME/mainnet-miner-conf.toml:/src/stacks-node/mainnet-miner-conf.toml" \ + -v "/stacks-blockchain:/stacks-blockchain" \ + -p 20443:20443 \ + -p 20444:20444 \ + blockstack/stacks-blockchain:latest \ +/bin/stacks-node start --config /src/stacks-node/mainnet-miner-conf.toml +``` + +You can review the node logs with this command: + +```bash +docker logs -f stacks_miner +``` + +### Optional: Running in Kubernetes with Helm + +In addition, you're also able to run a Stacks miner in a Kubernetes cluster using the [stacks-blockchain Helm chart](https://github.com/stacks-network/stacks-blockchain/tree/master/deployment/helm/stacks-blockchain). + +Ensure you have the following prerequisites installed: + +* [Docker](https://docs.docker.com/get-docker/) +* [minikube](https://minikube.sigs.k8s.io/docs/start/) (Only needed if standing up a local Kubernetes cluster) +* [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) +* [helm](https://helm.sh/docs/intro/install/) + +#### Generate keychain and get some tokens + +Use the steps outlined above + +#### Install the chart and run the miner + +To install the chart with the release name `my-release` and run the node as a miner: + +```bash +minikube start # Only run this if standing up a local Kubernetes cluster +helm repo add blockstack https://charts.blockstack.xyz +helm install my-release blockstack/stacks-blockchain \ + --set config.node.miner=true \ + --set config.node.seed="your-privateKey-from-generate-keychain-step" \ + --set config.burnchain.mode="mainnet" +``` + +You can review the node logs with this command: + +```bash +kubectl logs -l app.kubernetes.io/name=stacks-blockchain +``` + +For more information on the Helm chart and configuration options, please refer to the [chart's homepage](https://github.com/stacks-network/stacks-blockchain/tree/master/deployment/helm/stacks-blockchain). diff --git a/docs/operate/run-a-miner/mine-testnet-stacks-tokens.md b/docs/operate/run-a-miner/mine-testnet-stacks-tokens.md new file mode 100644 index 0000000000..5053833301 --- /dev/null +++ b/docs/operate/run-a-miner/mine-testnet-stacks-tokens.md @@ -0,0 +1,316 @@ +# Mine Testnet Stacks Tokens + +### Introduction + +For more on the technical details of mining, please review the mining guide. + +The following is an abridged version of the [walkthrough here](https://github.com/stacksfoundation/miner-docs/tree/testnet), written for a Linux system. If you're on Windows or MacOS, there will be some slight modifications needed (PR's welcome!). + +If you're interested in mining on the Stacks testnet, you can find instructions on how to do that here: + +### Running a Bitcoin Testnet Full Node + +To participate as a miner on testnet, you must have access to a testnet bitcoin node with a wallet (and the wallet's private key). One way to accomplish this is to run bitcoin locally. + +* [Ensure your computer meets the minimum hardware requirements before continuing.](https://bitcoin.org/en/bitcoin-core/features/requirements#system-requirements) + +First, download a [bitcoin binary](https://bitcoin.org/en/download), or [build from source](https://github.com/stacksfoundation/miner-docs/blob/testnet/bitcoin.md#source-install) (_there may be some extra requirements to building,_ [_defined here_](https://github.com/stacksfoundation/miner-docs/blob/testnet/prerequisites.md#install-required-packages)). + +{% hint style="info" %} +Tip: It is recommended to use a persistent location for the chainstate, in the steps below we're using `/bitcoin`. +{% endhint %} + +#### Update the Bitcoin Configuration File + +Next, update the bitcoin configuration: + +* Optional, but recommended: Use a persistent directory to store the Bitcoin chainstate, i.e. `datadir=/bitcoin`. +* Optional, but recommended: Update the `rpcallowip` value to only allow `127.0.0.1`, or the stacks miner IPv4. +* Modify the `rpcuser` and `rpcpassword` values from the defaults below. +* Store the following configuration somewhere on your filesystem (ex: `$HOME/bitcoin.conf`). + +```toml +server=1 +testnet=1 +disablewallet=0 +datadir=/bitcoin +rpcuser=btcuser +rpcpassword=btcpass +rpcallowip=0.0.0.0/0 +dbcache=512 +banscore=1 +rpcthreads=256 +rpcworkqueue=256 +rpctimeout=100 +txindex=1 + +[test] +bind=0.0.0.0:18333 +rpcbind=0.0.0.0:18332 +rpcport=18332 +``` + +#### Start Bitcoin + +Finally, start `bitcoind` as follows (adjust the `conf` path to where it was created in the previous step, i.e. `$HOME/bitcoin.conf`): + +```bash +bitcoind -conf=$HOME/bitcoin.conf +``` + +{% hint style="info" %} +Note: It will take a few hours for the node to synchronize with Bitcoin Testnet. +{% endhint %} + +While it's syncing, you can track the progress with `bitcoin-cli` or the logfile (will be located where the chainstate is stored, i.e. `/bitcoin/testnet3/debug.log`): + +```bash +$ bitcoin-cli \ + -rpcconnect=127.0.0.1 \ + -rpcport=18332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpass \ +getblockchaininfo | jq .blocks +2583513 +``` + +*** + +### Running a Stacks Blockchain miner + +First, download the latest tagged [stacks blockchain binary](https://github.com/stacks-network/stacks-blockchain/releases/latest), or [build from source](https://github.com/stacksfoundation/miner-docs/blob/testnet/stacks-blockchain.md#build-and-install-stacks-blockchain-from-source) (_there may be some extra requirements to building,_ [_defined here_](https://github.com/stacksfoundation/miner-docs/blob/testnet/prerequisites.md#install-required-packages)). + +{% hint style="info" %} +Tip: It is recommended to use a persistent location for the chainstate, in the steps below we're using `/stacks-blockchain`. +{% endhint %} + +#### Generate a keychain + +First, a keychain needs to be generated. With this keychain, we'll get some testnet BTC from a faucet, and then use that BTC to start mining. + +To create a keychain, the simplest way is to use the [stacks-cli](https://docs.hiro.so/references/stacks-cli) with the `make_keychain` command. + +```bash +npx @stacks/cli make_keychain -t 2>/dev/null | jq -r +``` + +After this runs, you should see some JSON printed to the screen that looks like this: + +```json +{ + "mnemonic": "spare decade dog ghost luxury churn flat lizard inch nephew nut drop huge divert mother soccer father zebra resist later twin vocal slender detail", + "keyInfo": { + "privateKey": "ooxeemeitar4ahw0ca8anu4thae7aephahshae1pahtae5oocahthahho4ahn7eici", + "address": "STTXOG3AIHOHNAEH5AU6IEX9OOTOH8SEIWEI5IJ9", + "btcAddress": "Ook6goo1Jee5ZuPualeiqu9RiN8wooshoo", + "wif": "rohCie2ein2chaed9kaiyoo6zo1aeQu1yae4phooShov2oosh4ox", + "index": 0 + } +} +``` + +{% hint style="danger" %} +Do not lose this information - we'll need to use the `privateKey`, `btcAddress` and `wif` fields in later steps. +{% endhint %} + +The above `wif` (`cPdTdMgww2njhnekUZmHmFNKsWAjVdCR4cfvD2Y4UQhFzMmwoW33`) will then need to be imported into the bitcoin testnet network. + +Next, a bitcoin wallet is created: + +```bash +bitcoin-cli \ + -rpcconnect=127.0.0.1 \ + -rpcport=18332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpass \ + createwallet "miner" \ + false \ + false \ + "" \ + false \ + false \ + true +``` + +Now, import your wif (bitcoin private key) inside the newly created wallet. + +{% hint style="info" %} +Note: Be sure to replace `` with the wif value in the `Generate a keychain` step. +{% endhint %} + +```bash +bitcoin-cli \ + -rpcport=18332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpassword \ + importprivkey +``` + +{% hint style="info" %} +Note: The import may take a while, because a wallet rescan is triggered. After the import has completed successfully, you can check that the address is imported with `getaddressinfo`. +{% endhint %} + +```bash +bitcoin-cli \ + -rpcconnect=127.0.0.1 \ + -rpcport=18332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpass \ + getaddressinfo +``` + +Once imported, we need to get some testnet BTC to that address. Grab the `btcAddress` field, and paste it into [this Bitcoin testnet faucet](https://tbtc.bitaps.com/). You'll be sent `0.01` testnet BTC to that address. + +#### Update the Stacks Blockchain Configuration File + +Now, we need to configure our node to use this Bitcoin keychain. Copy the [sample testnet miner config](https://raw.githubusercontent.com/stacks-network/stacks-blockchain/master/testnet/stacks-node/conf/testnet-miner-conf.toml) to your local machine in a memorable location like `$HOME/testnet-miner-conf.toml`. + +Next, update the stacks configuration: + +* Optional, but recommended: Use a persistent directory to store the Stacks chainstate, i.e. `working_dir = "/stacks-blockchain"` +* From the `make_keychain` step, modify the `seed` value with `privatekey` +* Store the following configuration somewhere on your filesystem (ex: `$HOME/testnet-miner-conf.toml`) + +```toml +[node] +working_dir = "/stacks-blockchain" +rpc_bind = "0.0.0.0:20443" +p2p_bind = "0.0.0.0:20444" +seed = "" +miner = true +bootstrap_node = "029266faff4c8e0ca4f934f34996a96af481df94a89b0c9bd515f3536a95682ddc@seed.testnet.hiro.so:30444" +mine_microblocks = false +wait_time_for_microblocks = 10000 + +[burnchain] +wallet_name = "miner" +chain = "bitcoin" +mode = "xenon" +peer_host = "127.0.0.1" +username = "" +password = "" +rpc_port = 18332 +peer_port = 18333 + +[[ustx_balance]] +address = "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST319CF5WV77KYR1H3GT0GZ7B8Q4AQPY42ETP1VPF" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST221Z6TDTC5E0BYR2V624Q2ST6R0Q71T78WTAX6H" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST2TFVBMRPS5SSNP98DQKQ5JNB2B6NZM91C4K3P7B" +amount = 10000000000000000 +``` + +#### Start the Stacks Blockchain + +To run your miner, run this in the command line: + +```bash +stacks-node start --config $HOME/testnet-miner-conf.toml +``` + +Your node should start. It will take some time to sync, and then your miner will be running. + +#### Enable Debug Logging + +In case you are running into issues or would like to see verbose logging, you can run your node with debug logging enabled. In the command line, run: + +```bash +STACKS_LOG_DEBUG=1 stacks-node start --config $HOME/testnet-miner-conf.toml +``` + +*** + +### Optional: Running a Stacks Blockchain miner with Docker + +Alternatively, you can run a Stacks testnet miner with Docker. + +{% hint style="warning" %} +Ensure you have [Docker](https://docs.docker.com/get-docker/) installed. +{% endhint %} + +#### Generate a Keychain and Get Some Tokens + +Generate a keychain: + +```bash +docker run -i node:20-alpine npx @stacks/cli make_keychain 2>/dev/null | jq -r +``` + +Now, we need to get some tBTC. Grab the `btcAddress` field, and paste it into [this Bitcoin testnet faucet](https://tbtc.bitaps.com/). You'll be sent `0.01` tBTC to that address. + +#### Update Stacks Blockchain Docker Configuration File + +Use the steps outlined above to create the configuration file. + +#### Start the Stacks Blockchain miner with Docker + +{% hint style="info" %} +Info: The ENV VARS `RUST_BACKTRACE` and `STACKS_LOG_DEBUG` are optional. If removed, debug logs will be disabled. +{% endhint %} + +```bash +docker run -d \ + --name stacks_miner \ + --rm \ + --network host \ + -e RUST_BACKTRACE="full" \ + -e STACKS_LOG_DEBUG="1" \ + -v "$HOME/testnet-miner-conf.toml:/src/stacks-node/testnet-miner-conf.toml" \ + -v "/stacks-blockchain:/stacks-blockchain" \ + -p 20443:20443 \ + -p 20444:20444 \ + blockstack/stacks-blockchain:latest \ +/bin/stacks-node start --config /src/stacks-node/testnet-miner-conf.toml +``` + +You can review the node logs with this command: + +```bash +docker logs -f stacks_miner +``` + +*** + +### Optional: Running in Kubernetes with Helm + +In addition, you're also able to run a Stacks miner in a Kubernetes cluster using the [stacks-blockchain Helm chart](https://github.com/stacks-network/stacks-blockchain/tree/master/deployment/helm/stacks-blockchain). + +Ensure you have the following prerequisites installed: + +* [Docker](https://docs.docker.com/get-docker/) +* [minikube](https://minikube.sigs.k8s.io/docs/start/) (Only needed if standing up a local Kubernetes cluster) +* [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) +* [helm](https://helm.sh/docs/intro/install/) + +#### Generate keychain and get some tokens + +Use the steps outlined above + +#### Install the chart and run the miner + +To install the chart with the release name `my-release` and run the node as a miner: + +```bash +minikube start # Only run this if standing up a local Kubernetes cluster +helm repo add blockstack https://charts.blockstack.xyz +helm install my-release blockstack/stacks-blockchain \ + --set config.node.miner=true \ + --set config.node.seed="privateKey-from-generate-keychain-step" \ +``` + +You can review the node logs with this command: + +```bash +kubectl logs -l app.kubernetes.io/name=stacks-blockchain +``` + +For more information on the Helm chart and configuration options, please refer to the [chart's homepage](https://github.com/stacks-network/stacks-blockchain/tree/master/deployment/helm/stacks-blockchain). diff --git a/docs/operate/run-a-miner/miner-costs-and-fees.md b/docs/operate/run-a-miner/miner-costs-and-fees.md new file mode 100644 index 0000000000..291a94e499 --- /dev/null +++ b/docs/operate/run-a-miner/miner-costs-and-fees.md @@ -0,0 +1,36 @@ +# Miner Costs and Fees + +### Configuring Cost and Fee Estimation + +{% code title="config.toml" %} +```toml +[fee_estimation] +cost_estimator = naive_pessimistic +fee_estimator = fuzzed_weighted_median_fee_rate +fee_rate_fuzzer_fraction = 0.1 +fee_rate_window_size = 5 +cost_metric = proportion_dot_product +log_error = true +enabled = true +``` +{% endcode %} + +Fee and cost estimators observe transactions on the network and use the observed costs of those transactions to build estimates for viable fee rates and expected execution costs. Estimators and metrics can be selected using the configuration fields above (the defaults shown are currently the only options). `log_error` controls whether the INFO logger will display information about cost estimator accuracy as new costs are observed. Setting `enabled = false` turns off the cost estimators. + +{% hint style="info" %} +Cost estimators are not consensus-critical components — they are intended for miners to rank mempool transactions or for clients to pick appropriate fee rates before broadcasting. +{% endhint %} + +The `fuzzed_weighted_median_fee_rate` estimator: + +* uses a median estimate from a window of the fees paid in the last `fee_rate_window_size` blocks, and +* then applies a uniform random "fuzz" up to `fee_rate_fuzzer_fraction` of the base estimate. + +
+ +Mining calculator (external) + +There is a mining calculator that can help with this process: https://friedger.github.io/mining-calculator/\ +Source code: https://github.com/friedger/mining-calculator + +
diff --git a/docs/operate/run-a-miner/miner-prerequisites.md b/docs/operate/run-a-miner/miner-prerequisites.md new file mode 100644 index 0000000000..a75cefa59c --- /dev/null +++ b/docs/operate/run-a-miner/miner-prerequisites.md @@ -0,0 +1,109 @@ +# Miner Prerequisites + +## Prerequisites + +### VM setup + +The VM will not need a lot of resources to run a miner - the most resources will be consumed during the blockchain syncs (for both Bitcoin and Stacks). For this example, we'll assume a [**Debian**](https://www.debian.org/) host with `x86_64` architecture (_commands may also work on any Debian-derived distribution_). + +A single CPU system with at least 4GB of memory and 1TB of disk space should be considered the minimum required specs to run the miner. + +#### VM Specs + +* Minimum CPU: `1 vCPU` +* Minimum Memory: `4GB` +* Minimum Storage: `1TB Disk` to allow for chainstate growth + * as of **July 2022**: + * Bitcoin chainstate is roughly `420GB` + * Stacks chainstate is roughly `45GB` + +#### Disk Configuration + +Two options here — either are fine but it's recommended to mount the chainstate from a separate disk that only contains the chainstate (see the first option). + +{% stepper %} +{% step %} +### Separate disks for chainstate(s) and OS + +* mount a dedicated disk for bitcoin at `/bitcoin` of 1TB +* mount a dedicated disk for stacks-blockchain at `/stacks-blockchain` of at least 100GB +* root volume `/` of at least 25GB +{% endstep %} + +{% step %} +### Combined Disk for all data + +* root volume `/` of at least 1TB +{% endstep %} +{% endstepper %} + +Create the required directories: + +{% code title="Create directories" %} +```bash +$ sudo mkdir -p /bitcoin +$ sudo mkdir -p /stacks-blockchain +$ sudo mkdir -p /etc/bitcoin +$ sudo mkdir -p /etc/stacks-blockchain +``` +{% endcode %} + +If using mounted disks: mount the disks to each filesystem created above — edit `/etc/fstab` to automount these disks at boot. + +Example `/etc/fstab` entries: + +``` +/dev/xvdb1 /bitcoin xfs rw,relatime,attr2,inode64,noquota +/dev/xvdc1 /stacks-blockchain xfs rw,relatime,attr2,inode64,noquota +``` + +Mount the disks: + +```bash +sudo mount -a +``` + +### Scripted install + +You can use the scripts/prerequisites.sh to install everything: + +```bash +curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/stacksfoundation/miner-docs/main/scripts/prerequisites.sh | bash +``` + +### Install required packages + +The following packages are required and used by the rest of these docs: + +{% code title="Install packages" %} +```bash +$ curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash - +$ sudo apt-get update -y && sudo apt-get install -y \ + build-essential \ + jq \ + netcat \ + nodejs \ + git \ + autoconf \ + libboost-system-dev \ + libboost-filesystem-dev \ + libboost-thread-dev \ + libboost-chrono-dev \ + libevent-dev \ + libzmq5 \ + libtool \ + m4 \ + automake \ + pkg-config \ + libtool \ + libboost-system-dev \ + libboost-filesystem-dev \ + libboost-chrono-dev \ + libboost-program-options-dev \ + libboost-test-dev \ + libboost-thread-dev \ + libboost-iostreams-dev +$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh && source $HOME/.cargo/env +$ sudo npm install -g @stacks/cli rimraf shx +``` +{% endcode %} diff --git a/docs/operate/run-a-miner/verify-miner.md b/docs/operate/run-a-miner/verify-miner.md new file mode 100644 index 0000000000..47907ccf11 --- /dev/null +++ b/docs/operate/run-a-miner/verify-miner.md @@ -0,0 +1,21 @@ +# Verify Miner + +## Verify Configuration + +You can verify that your node is operating as a miner by checking its log output to verify that it was able to find its Bitcoin UTXOs: + +{% code title="logs" %} +```bash +$ head -n 1000 /path/to/your/node/logs | grep -i utxo +INFO [1630127492.031042] [testnet/stacks-node/src/run_loop/neon.rs:146] [main] Miner node: checking UTXOs at address: +INFO [1630127492.062652] [testnet/stacks-node/src/run_loop/neon.rs:164] [main] UTXOs found - will run as a Miner node +``` +{% endcode %} + +## Verify Operations + +The first transaction of the miner is a registration transaction on Bitcoin. It just contains an `OP_RETURN` utxo. + +Thereafter, the miner creates for each block one transaction on Bitcoin with one data output, and two commit outputs to the stackers. The amount is half the value of the configured `burn_fee_cap` property. + +If the miner won a sortition, the corresponding Stacks address will create a tenure change transaction and a coinbase transaction. The block rewards will be awarded 100 blocks later if mining was successful. diff --git a/docs/operate/run-a-node/run-a-bitcoin-node.md b/docs/operate/run-a-node/run-a-bitcoin-node.md new file mode 100644 index 0000000000..b935cf30e2 --- /dev/null +++ b/docs/operate/run-a-node/run-a-bitcoin-node.md @@ -0,0 +1,236 @@ +# Run a Bitcoin Node + +{% stepper %} +{% step %} +### Requirements + +This guide is written for a Unix based system. It's reasonable to expect some modifications will be required for other operating systems. + +When started, the Bitcoin node will take several days to reach chain tip. + +* Bitcoin Core >= v25.0 + * https://github.com/bitcoin/bitcoin + * https://bitcoincore.org/en/download/ +* Host with a minimum of: + * 2 vCPU (a single dedicated cpu for the bitcoind process) + * 4GB Memory (during sync, more available memory will improve sync time) + * 1TB free disk space +* User account: `bitcoin:bitcoin` +* Chainstate directory located at: `/bitcoin/mainnet` + * `bitcoin` user must have read/write access. +* Config directory located at: `/etc/bitcoin` + * `bitcoin` user must have at least read access +{% endstep %} + +{% step %} +### Add bitcoin user and set file ownership + +Run the following commands: + +{% code title="Create directories and add user" %} +```shell +$ sudo mkdir -p /bitcoin/mainnet +$ sudo mkdir /etc/bitcoin +$ sudo useradd bitcoin -d /bitcoin +$ sudo chown -R bitcoin:bitcoin /bitcoin /etc/bitcoin/ +``` +{% endcode %} +{% endstep %} + +{% step %} +### Bitcoin config + +Below is a sample config used to sync a bitcoin node - feel free to adjust as needed. + +{% hint style="info" %} +If using the [systemd unit below](run-a-bitcoin-node.md#systemd-unit-file), save this file as `/etc/bitcoin/bitcoin.conf` +{% endhint %} + +* `btuser:btcpass` is hardcoded as an rpcauth user/password ([generated using this script](https://github.com/bitcoin/bitcoin/tree/master/share/rpcauth)). +* Only localhost access is allowed (`127.0.0.1`) on the standard mainnet ports. +* `dbcache` is set to the maximum of 16GB. +* Wallet (and wallet rpc calls) are disabled. + +{% code title="Sample /etc/bitcoin/bitcoin.conf" %} +``` +## [rpc] + +# Accept command line and JSON-RPC commands. +server=1 + +# Allow JSON-RPC connections from specified source. +rpcallowip=127.0.0.1/0 + +# Bind to given address to listen for JSON-RPC connections. +rpcbind=127.0.0.1:8332 + +# Username and HMAC-SHA-256 hashed password for JSON-RPC connections. + +# Use the script at https://github.com/bitcoin/bitcoin/tree/master/share/rpcauth to generate + +# Note: may be specified multiple times for different users. +rpcauth=btcuser:18857b4df4b1f0f5e6b1d7884617ab39$de6e02e1da8ee138ee702e13e0637e24679d844756216b066c3aeac4bcac5e10 # btuser:btcpass + +# Optional: rpcwhitelist can restrict listed RPC calls to specific rpcauth users. + +# Uncomment the below the restrict the `limited` user to a small subset of `get` commands + +# rpcauth=limited:350c91a60895b567c4662c27e63e9a41$25188b0f51f2f974dcdc75c1e0d41174e8f7ae19fb96927abf68ac5bc1e8897b # limited:limited + +# rpcwhitelist=limited:getblockchaininfo,getblock,getblockcount,getblockhash,getblockheader,getnetworkinfo + +# rpcwhitelistdefault=0 + +## [core] + +# Specify data directory +datadir=/bitcoin/mainnet + +# Do not keep transactions in the mempool longer than hours (default: 336) +mempoolexpiry=24 + +# Bind to given address and always listen on it (default: 0.0.0.0) +bind=127.0.0.1:8333 + +# Maximum database cache size MiB (4 to 16384, default: 450). In addition, unused mempool memory is shared for this cache +dbcache=16384 + +# Maintain a full transaction index, used by the getrawtransaction rpc call +txindex=1 + +## [wallet] + +# Do not load the wallet and disable wallet RPC calls +disablewallet=1 +``` +{% endcode %} +{% endstep %} + +{% step %} +### Systemd unit file + +Reference: https://github.com/bitcoin/bitcoin/blob/master/contrib/init/bitcoind.service + +Save the following as your systemd unit (for example `/etc/systemd/system/bitcoin.service`): + +{% code title="bitcoind.service" %} +``` +[Unit] +Description=Bitcoin daemon +Documentation=https://github.com/bitcoin/bitcoin/blob/master/doc/init.md + +# https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget/ +After=network-online.target +Wants=network-online.target + +[Service] +ExecStart=/usr/bin/bitcoind -pid=/run/bitcoind/bitcoind.pid \ + -conf=/etc/bitcoin/bitcoin.conf \ + -startupnotify='systemd-notify --ready' \ + -shutdownnotify='systemd-notify --stopping' + +# Make sure the config directory is readable by the service user +PermissionsStartOnly=true +ExecStartPre=/bin/chgrp bitcoin /etc/bitcoin + +# Process management +#################### + +Type=notify +NotifyAccess=all +PIDFile=/run/bitcoind/bitcoind.pid + +Restart=on-failure +TimeoutStartSec=infinity +TimeoutStopSec=600 + +# Directory creation and permissions +#################################### + +# Run as bitcoin:bitcoin +User=bitcoin +Group=bitcoin + +# /run/bitcoind +RuntimeDirectory=bitcoind +RuntimeDirectoryMode=0710 + +# /etc/bitcoin +ConfigurationDirectory=bitcoin +ConfigurationDirectoryMode=0710 + +# /var/lib/bitcoind +StateDirectory=bitcoind +StateDirectoryMode=0710 + +# Hardening measures +#################### + +# Provide a private /tmp and /var/tmp. +PrivateTmp=true + +# Mount /usr, /boot/ and /etc read-only for the process. +ProtectSystem=full + +# Deny access to /home, /root and /run/user +ProtectHome=true + +# Disallow the process and all of its children to gain + +# new privileges through execve(). +NoNewPrivileges=true + +# Use a new /dev namespace only populated with API pseudo devices + +# such as /dev/null, /dev/zero and /dev/random. +PrivateDevices=true + +# Deny the creation of writable and executable memory mappings. +MemoryDenyWriteExecute=true + +# Restrict ABIs to help ensure MemoryDenyWriteExecute is enforced +SystemCallArchitectures=native + +[Install] +WantedBy=multi-user.target +``` +{% endcode %} +{% endstep %} + +{% step %} +### Enable and start the Bitcoin service + +Run: + +{% code title="Enable and start service" %} +```shell +$ sudo systemctl daemon-reload +$ sudo systemctl enable bitcoin.service +$ sudo systemctl start bitcoin.service +``` +{% endcode %} +{% endstep %} + +{% step %} +### Track sync progress + +{% hint style="info" %} +Once started, you may track the sync progress: +{% endhint %} + +{% code title="Tail debug log and query RPC" %} +``` +$ sudo tail -f /bitcoin/mainnet/debug.log +2024-12-05T19:35:31Z UpdateTip: new best=00000000000000000058990a84cc8f8eab25dbbd572f123f9190cea7256d7349 height=509258 version=0x20000000 log2_work=88.128280 tx=299522737 date='2018-02-15T03:42:14Z' progress=0.295203 cache=43.5MiB(172740txo) +... +$ bitcoin-cli \ + -rpcconnect=127.0.0.1 \ + -rpcport=8332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpass \ + getblockcount +509016 +``` +{% endcode %} +{% endstep %} +{% endstepper %} diff --git a/docs/operate/run-a-node/run-a-node-with-a-hosted-provider.md b/docs/operate/run-a-node/run-a-node-with-a-hosted-provider.md new file mode 100644 index 0000000000..5a10fe2d30 --- /dev/null +++ b/docs/operate/run-a-node/run-a-node-with-a-hosted-provider.md @@ -0,0 +1,19 @@ +# Run a Node with a Hosted Provider + +We're always looking for new hosting providers that enable anyone to run the Stacks Blockchain. Below, you'll find some examples of the current providers that are known to support running a node. + +### Quicknode + +Please refer to the Quicknode Section for instructions on launching an instance with Quicknode. + +### Stacks on Render + +The [render-stacks](https://github.com/stacksfoundation/render-stacks) GitHub repo has instructions so anyone can deploy a Stacks node to the hosted [Render](https://render.com/) service in one click. + +{% hint style="warning" %} +While it is possible to use the free plan with some modifications, it is recommended to run this on a paid plan. +{% endhint %} + +### Stacks on Fly + +The [fly-stacks](https://github.com/stacksfoundation/fly-stacks) GitHub repo has instructions so anyone can deploy a Stacks node to the hosted [Fly](https://fly.io/) service. diff --git a/docs/operate/run-a-node/run-a-node-with-digital-ocean.md b/docs/operate/run-a-node/run-a-node-with-digital-ocean.md new file mode 100644 index 0000000000..5f125b0caa --- /dev/null +++ b/docs/operate/run-a-node/run-a-node-with-digital-ocean.md @@ -0,0 +1,104 @@ +# Run a Node with Digital Ocean + +### Introduction + +This is a step by step guide to deploy the [Stacks Blockchain](https://github.com/stacks-network/stacks-blockchain) on [DigitalOcean](https://digitalocean.com/). + +Build code is hosted on this [Github repository](https://github.com/stacksfoundation/stacks-machine-images) using the [methods from here](https://github.com/stacks-network/stacks-blockchain-docker). + +### Steps + +{% stepper %} +{% step %} +### Create the Droplet from the Marketplace + +Go to the [Stacks Blockchain page](https://marketplace.digitalocean.com/apps/stacks-blockchain) in DigitalOcean's marketplace. Click on `Create Stacks Blockchain Droplet`. +{% endstep %} + +{% step %} +### Choose plan and region + +Choose a plan (it will only allow you to select a droplet that meets the minimum requirements) and your preferred datacenter region. +{% endstep %} + +{% step %} +### Authentication + +Enter a root password or [enable SSH keys](https://docs.digitalocean.com/products/droplets/how-to/add-ssh-keys/) if you prefer. +{% endstep %} + +{% step %} +### Create the Droplet + +You can leave the rest of the options as they are and click on `Create Droplet`. +{% endstep %} + +{% step %} +### Wait for creation + +You will need to wait a few seconds for the droplet to get created. Once created click on it to see more information. +{% endstep %} + +{% step %} +### Access the Droplet + +Congratulations! You are now running the Stacks Blockchain. You can click on `Console` for a terminal window to open or login using SSH to the public IP you've been assigned to with user `root`. +{% endstep %} +{% endstepper %} + +### Getting started after deploying Stacks Blockchain + +Once the droplet is launched, the initial startup can take several minutes while BNS data is imported (this is a one time operation) and the Bitcoin headers are synced. + +To keep track of the progress, you can SSH to the host and view logs: + +```bash +ssh root@your_droplet_public_ipv4 +/opt/stacks-blockchain-docker/manage.sh -n mainnet -a logs +``` + +After the stacks blockchain finishes the initial header sync and starts to sync with its peers, the application ports will open (`20443` and `3999`) and HTTP port `80` will now start proxying requests. + +Use `http://your_droplet_public_ipv4` to access the data directly, with output being similar to: + +```json +{ + "server_version": "stacks-blockchain-api v6.2.3 (master:77ab3ae2)", + "status": "ready", + "chain_tip": { + "block_height": 91820, + "block_hash": "0x06b276e85f238151414616618ae0adaf5eeda4eac6cad5bbefceeb37948ab275", + "index_block_hash": "0x4d7c075d7ab0f90b1dbc175f5c42b7344265d00cfef202dd9681d95388eeed8c", + "microblock_hash": "0xcf4f9037cc10696b2812b617ca105885be625c6acf8ad67e71bb4c09fa6ebb21", + "microblock_sequence": 4 + } +} +``` + +{% hint style="info" %} +For the full list of API endpoints for the Stacks Blockchain, consult the [Hiro API Docs](https://docs.hiro.so/api) +{% endhint %} + +All services are managed by a [systemd unit file](https://github.com/stacksfoundation/stacks-machine-images/blob/master/files/etc/systemd/system/stacks.service) that is set to start on boot. + +Manual control is also possible via the [manage.sh script](https://github.com/stacks-network/stacks-blockchain-docker/blob/master/manage.sh) at `/opt/stacks-blockchain-docker/manage.sh` on the host. + +Full details on how to use the manage.sh script is [available here](https://github.com/stacks-network/stacks-blockchain-docker/blob/master/docs/usage.md). + +### Launching a Droplet using the DigitalOcean API + +In addition to creating a Droplet from the Stacks Blockchain 1-Click App via the control panel, you can also use the [DigitalOcean API](https://digitalocean.com/docs/api). + +As an example, to create a 4GB Stacks Blockchain Droplet in the SFO2 region, you can use the following curl command. You’ll need to either save your [API access token](https://docs.digitalocean.com/reference/api/create-personal-access-token/) to an environment variable or substitute it into the command below. + +{% hint style="info" %} +The `name`, `region` and `size` values below are hardcoded, so adjust as desired. +{% endhint %} + +```bash +$ export TOKEN= +$ curl -X POST -H 'Content-Type: application/json' \ + -H 'Authorization: Bearer '$TOKEN'' -d \ + '{"name":"stacks-blockchain","region":"sfo2","size":"s-2vcpu-4gb","image":"stacksfoundation-stacksblockchain"}' \ + "https://api.digitalocean.com/v2/droplets" +``` diff --git a/docs/operate/run-a-node/run-a-node-with-docker.md b/docs/operate/run-a-node/run-a-node-with-docker.md new file mode 100644 index 0000000000..defe4be9b4 --- /dev/null +++ b/docs/operate/run-a-node/run-a-node-with-docker.md @@ -0,0 +1,119 @@ +# Run a Node with Docker + +### Stacks Blockchain with Docker + +Run your own Stacks Blockchain node using [docker-compose](https://docs.docker.com/compose/) with just a few commands using [stacks-blockchain-docker](https://github.com/stacks-network/stacks-blockchain-docker) + +### Requirements + +The minimum viable requirements are listed below. + +While you _can_ run a node using these specs, it's _recommended_ to assign more than the minimum for better performance. + +* ⚠️ [docker-compose](https://docs.docker.com/compose/install/) version `2.2.2` or greater is **required** +* **8GB memory if running only a Stacks node** +* **16 GB memory if running Stacks + Bitcoin node** +* **1 Vcpu** ( _minimum of 2 Vcpu is recommended_ ) +* **500GB disk for Stacks node** +* **1TB disk space for Bitcoin node** + +{% hint style="warning" %} +MacOS with an ARM (M-series chip) processor is NOT recommended + +The way Docker for Mac on an Arm CPU is designed makes the I/O incredibly slow, and blockchains are _**very**_ heavy on I/O. This only seems to affect MacOS with the M-series chip, other Arm based systems like Raspberry Pi work as expected. +{% endhint %} + +### Quickstart + +The `` placeholder used below can be replaced with one of: + +* mainnet +* testnet +* mocknet + +{% stepper %} +{% step %} +### Clone the repository + +Clone the stacks-blockchain-docker repository locally and change into the directory: + +{% code title="Clone repository" %} +```bash +git clone https://github.com/stacks-network/stacks-blockchain-docker && cd stacks-blockchain-docker +``` +{% endcode %} +{% endstep %} + +{% step %} +### Start the services + +Start the docker-compose services for the chosen network: + +{% code title="Start services" %} +```bash +./manage.sh -n -a start +``` +{% endcode %} + +{% hint style="info" %} +With an optional HTTP proxy on port 80: + +{% code title="Start with proxy" %} +```bash +./manage.sh -n -a start -f proxy +``` +{% endcode %} +{% endhint %} +{% endstep %} +{% endstepper %} + +### Accessing the services + +{% hint style="info" %} +For networks other than `mocknet`, downloading the initial headers can take several minutes. Until the headers are downloaded, the `/v2/info` endpoints won't return any data. + +Follow the logs to track the sync progress: + +{% code title="Follow logs" %} +```bash +./manage.sh -n -a logs +``` +{% endcode %} +{% endhint %} + +stacks-blockchain: + +* Ports `20443-20444` are exposed on `localhost` + +{% code title="Check stacks-blockchain /v2/info" %} +```bash +curl -sL localhost:20443/v2/info | jq -r +``` +{% endcode %} + +stacks-blockchain-api: + +* Port `3999` is exposed on `localhost` + +{% code title="Check stacks-blockchain-api" %} +```bash +curl -sL localhost:3999 | jq -r +``` +{% endcode %} + +proxy: + +* Port `80` is exposed on `localhost` + +{% code title="Check proxy" %} +```bash +curl -sL localhost/v2/info | jq -r +curl -sL localhost | jq -r +``` +{% endcode %} + +### Upgrades + +{% hint style="warning" %} +For schema-breaking upgrades to running instances of this repo, you'll need to [run an event-replay](https://github.com/stacks-network/stacks-blockchain-docker/blob/master/docs/upgrade.md). +{% endhint %} diff --git a/docs/operate/run-a-node/run-a-node-with-quicknode.md b/docs/operate/run-a-node/run-a-node-with-quicknode.md new file mode 100644 index 0000000000..aa06a7868b --- /dev/null +++ b/docs/operate/run-a-node/run-a-node-with-quicknode.md @@ -0,0 +1,63 @@ +# Run a Node with Quicknode + +[QuickNode](https://www.quicknode.com/) is a service for rapidly getting set up with a Stacks node. As an easy and fast alternative to running your own node, you can utilize QuickNode to serve as an API. + +{% stepper %} +{% step %} +### Create a QuickNode account + +Sign up on QuickNode: https://www.quicknode.com/signup +{% endstep %} + +{% step %} +### Create an endpoint + +Once signed in, click "Create an endpoint". Select: + +* Stacks +* your desired network (e.g., mainnet or testnet) +* your desired QuickNode plan level + +After that you'll have an API endpoint URL you can use to connect to Stacks. +{% endstep %} + +{% step %} +### Install the Stacks network package + +Install the `@stacks/network` package in your frontend project. +{% endstep %} + +{% step %} +### Import the network class + +In your frontend code, import the network class: + +{% code title="example.js" %} +```javascript +import { StacksTestnet } from "@stacks/network"; +``` +{% endcode %} +{% endstep %} + +{% step %} +### Configure the network with your QuickNode endpoint + +Create the network instance using your QuickNode endpoint URL: + +{% code title="example.js" %} +```javascript +const network = new StacksTestnet({ url: "" }); +``` +{% endcode %} + +Replace \ with the full endpoint URL provided by QuickNode. +{% endstep %} + +{% step %} +### Use with @stacks/transactions + +You can now call transactions and other Stacks RPC methods as you normally would using the `@stacks/transactions` library, passing the `network` instance where required. + +For an example integration and walkthrough, refer to the Hello Stacks tutorial. +{% endstep %} +{% endstepper %} diff --git a/docs/operate/run-a-node/run-a-pruned-bitcoin-node.md b/docs/operate/run-a-node/run-a-pruned-bitcoin-node.md new file mode 100644 index 0000000000..e5b5c22356 --- /dev/null +++ b/docs/operate/run-a-node/run-a-pruned-bitcoin-node.md @@ -0,0 +1,255 @@ +# Run a Pruned Bitcoin Node + +This guide is written for a Unix based system. It's reasonable to expect some modifications will be required for other operating systems. + +When started, the pruned Bitcoin node will take roughly \~24 hours to reach chain tip. + +{% hint style="warning" %} +While bitcoin is syncing, it's recommended to keep a stacks-blockchain node at chain tip, or [use a stacks chainstate archive](https://docs.hiro.so/stacks/archive/guides/stacks-blockchain). +{% endhint %} + +Requirements: + +* Bitcoin Core >= v25.0 + * https://github.com/bitcoin/bitcoin + * https://bitcoincore.org/en/download/ +* Host with a minimum of: + * 2 vCPU (a single dedicated cpu for the bitcoind process) + * 4GB Memory (during sync, more available memory will improve sync time) + * 50GB free disk space (actual usage is closer to 20GB) +* User account: `bitcoin:bitcoin` +* Chainstate directory located at: `/bitcoin/mainnet` + * `bitcoin` user must have read/write access. +* Config directory located at: `/etc/bitcoin` + * `bitcoin` user must have at least read access + +Caveats + +[BIP-0159](https://github.com/bitcoin/bips/blob/master/bip-0159.mediawiki) + +In short, this BIP specifies that pruned nodes will advertise the service bit `NODE_NETWORK_LIMITED`, which restricts syncing blocks older than 288 blocks (\~2 days). + +What this means is that in practice, a stacks-blockchain node: + +* Cannot sync from genesis using a pruned node. +* Must not be offline or otherwise down for longer than \~2 days (or 288 Bitcoin blocks). + +{% stepper %} +{% step %} +### Add bitcoin user and set file ownership + +```shell +$ sudo mkdir -p /bitcoin/mainnet +$ sudo mkdir /etc/bitcoin +$ sudo useradd bitcoin -d /bitcoin +$ sudo chown -R bitcoin:bitcoin /bitcoin /etc/bitcoin/ +``` +{% endstep %} + +{% step %} +### Bitcoin Config + +Below is a sample config used to sync a pruned bitcoin node - feel free to adjust as needed. + +{% hint style="info" %} +If using the [systemd unit below](run-a-pruned-bitcoin-node.md#systemd-unit-file), save this file as `/etc/bitcoin/bitcoin.conf` +{% endhint %} + +Notes: + +* `btuser:btcpass` is hardcoded as an rpcauth user/password ([generated using this script](https://github.com/bitcoin/bitcoin/tree/master/share/rpcauth)). +* Only localhost access is allowed (`127.0.0.1`) on the standard mainnet ports. +* Pruning is set to be small, storing only the last 1GB of blocks (for p2p traffic, this is more than enough). +* `dbcache` is set to the maximum of 16GB. +* Wallet (and wallet rpc calls) are disabled. + +``` +## [rpc] + +# Accept command line and JSON-RPC commands. +server=1 + +# Allow JSON-RPC connections from specified source. +rpcallowip=127.0.0.1/0 + +# Bind to given address to listen for JSON-RPC connections. +rpcbind=127.0.0.1:8332 + +# Username and HMAC-SHA-256 hashed password for JSON-RPC connections. + +# Use the script at https://github.com/bitcoin/bitcoin/tree/master/share/rpcauth to generate + +# Note: may be specified multiple times for different users. +rpcauth=btcuser:18857b4df4b1f0f5e6b1d7884617ab39$de6e02e1da8ee138ee702e13e0637e24679d844756216b066c3aeac4bcac5e10 # btuser:btcpass + +# Optional: rpcwhitelist can restrict listed RPC calls to specific rpcauth users. + +# Uncomment the below the restrict the `limited` user to a small subset of `get` commands + +# rpcauth=limited:350c91a60895b567c4662c27e63e9a41$25188b0f51f2f974dcdc75c1e0d41174e8f7ae19fb96927abf68ac5bc1e8897b # limited:limited + +# rpcwhitelist=limited:getblockchaininfo,getblock,getblockcount,getblockhash,getblockheader,getnetworkinfo + +# rpcwhitelistdefault=0 + +## [core] + +# Specify data directory +datadir=/bitcoin/mainnet + +# Do not keep transactions in the mempool longer than hours (default: 336) +mempoolexpiry=24 + +# Bind to given address and always listen on it (default: 0.0.0.0) +bind=127.0.0.1:8333 + +# Maximum database cache size MiB (4 to 16384, default: 450). In addition, unused mempool memory is shared for this cache +dbcache=16384 + +# Maintain a full transaction index, used by the getrawtransaction rpc call (**Running a pruned node requires that this option is disabled**) +txindex=0 + +# Reduce storage requirements by enabling pruning (deleting) of old + +# blocks. This allows the pruneblockchain RPC to be called to + +# delete specific blocks and enables automatic pruning of old + +# blocks if a target size in MiB is provided. This mode is + +# incompatible with -txindex. Warning: Reverting this setting + +# requires re-downloading the entire blockchain. (default: 0 = + +# disable pruning blocks, 1 = allow manual pruning via RPC, >=550 = + +# automatically prune block files to stay under the specified + +# target size in MiB) +prune=1024 # 1GB of chainstate is enough for p2p block data (if using the RPC,this can be adjusted higher to store more blocks) + +## [wallet] + +# Do not load the wallet and disable wallet RPC calls +disablewallet=1 +``` +{% endstep %} + +{% step %} +### Systemd unit file + +ref: https://github.com/bitcoin/bitcoin/blob/master/contrib/init/bitcoind.service + +``` +[Unit] +Description=Bitcoin daemon +Documentation=https://github.com/bitcoin/bitcoin/blob/master/doc/init.md + +# https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget/ +After=network-online.target +Wants=network-online.target + +[Service] +ExecStart=/usr/bin/bitcoind -pid=/run/bitcoind/bitcoind.pid \ + -conf=/etc/bitcoin/bitcoin.conf \ + -startupnotify='systemd-notify --ready' \ + -shutdownnotify='systemd-notify --stopping' + +# Make sure the config directory is readable by the service user +PermissionsStartOnly=true +ExecStartPre=/bin/chgrp bitcoin /etc/bitcoin + +# Process management +#################### + +Type=notify +NotifyAccess=all +PIDFile=/run/bitcoind/bitcoind.pid + +Restart=on-failure +TimeoutStartSec=infinity +TimeoutStopSec=600 + +# Directory creation and permissions +#################################### + +# Run as bitcoin:bitcoin +User=bitcoin +Group=bitcoin + +# /run/bitcoind +RuntimeDirectory=bitcoind +RuntimeDirectoryMode=0710 + +# /etc/bitcoin +ConfigurationDirectory=bitcoin +ConfigurationDirectoryMode=0710 + +# /var/lib/bitcoind +StateDirectory=bitcoind +StateDirectoryMode=0710 + +# Hardening measures +#################### + +# Provide a private /tmp and /var/tmp. +PrivateTmp=true + +# Mount /usr, /boot/ and /etc read-only for the process. +ProtectSystem=full + +# Deny access to /home, /root and /run/user +ProtectHome=true + +# Disallow the process and all of its children to gain + +# new privileges through execve(). +NoNewPrivileges=true + +# Use a new /dev namespace only populated with API pseudo devices + +# such as /dev/null, /dev/zero and /dev/random. +PrivateDevices=true + +# Deny the creation of writable and executable memory mappings. +MemoryDenyWriteExecute=true + +# Restrict ABIs to help ensure MemoryDenyWriteExecute is enforced +SystemCallArchitectures=native + +[Install] +WantedBy=multi-user.target +``` +{% endstep %} + +{% step %} +### Enable and start the Bitcoin service + +```shell +$ sudo systemctl daemon-reload +$ sudo systemctl enable bitcoin.service +$ sudo systemctl start bitcoin.service +``` +{% endstep %} + +{% step %} +### Track sync progress + +{% hint style="info" %} +Once started, you may track the sync progress: + +``` +$ sudo tail -f /bitcoin/mainnet/debug.log +2024-12-05T19:35:31Z UpdateTip: new best=00000000000000000058990a84cc8f8eab25dbbd572f123f9190cea7256d7349 height=509258 version=0x20000000 log2_work=88.128280 tx=299522737 date='2018-02-15T03:42:14Z' progress=0.295203 cache=43.5MiB(172740txo) +... +$ bitcoin-cli \ + -rpcconnect=127.0.0.1 \ + -rpcport=8332 \ + -rpcuser=btcuser \ + -rpcpassword=btcpass \ + getblockcount +509016 +``` +{% endhint %} +{% endstep %} +{% endstepper %} diff --git a/docs/operate/run-a-signer/README.md b/docs/operate/run-a-signer/README.md new file mode 100644 index 0000000000..ed8aad2fd6 --- /dev/null +++ b/docs/operate/run-a-signer/README.md @@ -0,0 +1,361 @@ +# Run a Signer + +### How to Use This Guide + +If you are not familiar with the concept of signing and stacking, and how they work together, be sure to check out the [Stackers and Signing concept guide](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/signing). + +This guide is a step-by-step walkthrough for setting up and running a signer. If you need to troubleshoot your signer setup, see the Signer Troubleshooting section. If you need to Stack your STX, or have questions about how that process works, check out the Stack STX guide. + +### Background and High-Level Process + +To run a signer you'll run a signer and a Stacks node side-by-side. Specifically, run a follower node. The signer monitors events from the Stacks node and uses the generated account (see Preflight Setup) to sign incoming Stacks blocks sent from the Stacks node. + +This doc provides instructions to set up both using either Docker or the release binaries available in the [stacks core releases](https://github.com/stacks-network/stacks-core/releases) repository, and how to configure them so the signer and Stacks node communicate correctly. + +### Knowledge Prerequisites + +* Docker and basic knowledge of pulling and running images +* Basic knowledge of [Stacks accounts](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/network-fundamentals/accounts) +* Basic knowledge of [stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) and the stacking flow + +{% stepper %} +{% step %} +### Signer Checklist — Pre-Launch Setup + +Quick reference of major setup steps prior to launching a signer. + +* Ensure your system meets the [minimum system requirements](./#minimum-system-requirements). +* Acquire Docker and basic knowledge of Stacks accounts, stacking, and the Nakamoto stacking flow (links above). +{% endstep %} + +{% step %} +### Signer Checklist — Preflight Setup + +* Generate a new private key using stacks-cli (see Preflight Setup). +* Save the generated account information securely. +{% endstep %} + +{% step %} +### Signer Checklist — Configuration Setup + +* Create a `signer-config.toml` file with necessary configurations: + * node\_host + * endpoint + * network + * db\_path + * auth\_password + * stacks\_private\_key +* Store `signer-config.toml` securely and note down the values used. +{% endstep %} + +{% step %} +### Signer Checklist — Running the Signer + +* Decide whether to run the signer using Docker (recommended) or as a binary. +* If using Docker: + * Set up the necessary ports and volumes. + * Run the Docker container with the appropriate settings. +* If running as a binary: + * Build `stacks-core` from source or download the pre-built binary. + * Run the signer using: `stacks-signer run --config `. +{% endstep %} + +{% step %} +### Signer Checklist — Verify Signer Operation + +* Check that the signer is listening on its configured endpoint. +* Confirm that there are no errors and that the system is ready for connections. +{% endstep %} + +{% step %} +### Signer Checklist — Setting Up the Stacks Node + +* Create a `node-config.toml`, include: + * connection\_options.sauth\_token + * events\_observer.endpoint (matching signer config) +* Decide whether to run the Stacks node using Docker or as a binary and follow the respective run steps. +{% endstep %} + +{% step %} +### Signer Checklist — Verify Stacks Node Operation + +* Check Stacks node logs for successful connection to the signer. +* Confirm the node is syncing Bitcoin headers properly. +{% endstep %} + +{% step %} +### Signer Checklist — Setup Stacks Accounts + +* Set up a pool operator wallet in a Stacks wallet (e.g., Leather or Xverse). +* Fund the pool operator wallet with sufficient STX for transaction fees. +* Share the pool operator wallet’s STX address with delegating parties. +* Fund your signer's STX wallet with enough STX to cover transaction fees (recommend at least 100–200 STX). +{% endstep %} +{% endstepper %} + +### Minimum System Requirements + +These are the minimum required specs to run a node and signer. More resources are recommended for optimal performance. + +#### Signer, Stacks node and Bitcoin node + +* 4 vCPU +* 8 GB memory if running only a Stacks node and signer +* 16 GB memory if running Stacks + Bitcoin node + signer +* 1.5+ TB storage (1 TB for Bitcoin node, 500 GB for Stacks node, and 50 GB for signer) + +*** + +## Preflight Setup + +Before you get your signer set up, you'll need to [generate a new private key](https://docs.stacks.co/stacks-101/accounts#creation). The `stacks-cli` provides a mechanism for quickly generating a new account keychain via a simple CLI interface. The linked guide shows how to create one of those accounts on testnet. + +Save the generated account information securely; you'll need it later. + +{% hint style="info" %} +What should the networking setup look like? + +Signers are intended to work with a local node. The node<->signer connection is not run over SSL, which means you can be exposed to a man-in-the-middle attack if your signer and node are hosted on separate machines. Ensure your signer isn't allowing requests from the public internet. We recommend having the signer and node running locally on the same machine or using internal networking between them. +{% endhint %} + +*** + +## Create a Configuration File + +Create a file named `signer-config.toml`. Populate it with the example signer config file contents from the [Sample Configuration Files](https://app.gitbook.com/s/GVj1Z9vMuEOMe7oH7Wnq/signer-configuration) page. Each field is described on that page. + +*** + +## Running the Signer + +Two options: Docker (recommended) or binary. Binaries are available on the [Stacks Core releases page](https://github.com/stacks-network/stacks-core/releases). + +### Running the Signer with Docker + +You can run the signer as a Docker container using the `blockstack/stacks-signer:3.1.0.0.5.0` image. + +Requirements when running the container: + +* The port configured as the `endpoint` (example: 30000) must be exposed to your Stacks node (endpoint should not be public). +* A volume with at least a few GB available that contains the folder specified by your `db_path` (example: `/var`). +* Mount your `signer-config.toml` file as a volume. + +Example docker run command: + +```bash +IMG="blockstack/stacks-signer" +VER="3.1.0.0.5.0" +STX_SIGNER_PATH="./" +STX_SIGNER_DATA="$STX_SIGNER_PATH/data" +STX_SIGNER_CONFIG="$STX_SIGNER_PATH/signer-config.toml" + +docker run -d \ + -v $STX_SIGNER_CONFIG:/config.toml \ + -v $STX_SIGNER_DATA:/var/stacks \ + -p 30000:30000 \ + -e RUST_BACKTRACE=full \ + -e BLOCKSTACK_DEBUG=0 \ + --name stacks-signer \ + $IMG:$VER \ + stacks-signer run \ + --config /config.toml +``` + +Hint about platform mismatch: + +{% hint style="info" %} +If you get an error about the manifest not found or the image platform not matching the host platform, you probably are running on an architecture other than x64. Add `--platform=linux/amd64` to the command (for example, on M1 Mac). +{% endhint %} + +Or, using a custom Dockerfile: + +```docker +FROM blockstack/stacks-signer:3.1.0.0.5.0 +COPY signer-config.toml /config.toml +EXPOSE 30000 +CMD ["stacks-signer", "run", "--config", "/config.toml"] +``` + +### Running the Signer as a Binary + +Download the pre-built binaries from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases), unzip the archive for your architecture — inside is a `stacks-signer` binary. + +Run the signer: + +```bash +stacks-signer run --config ../signer-config.toml +``` + +(Replace `../signer-config.toml` with the actual path to your config.) + +*** + +## Verify the Signer is Running + +List running containers: + +```bash +docker ps +``` + +Check the container logs: + +```bash +docker logs +``` + +You should see: + +Signer spawned successfully. Waiting for messages to process... + +You may also see a warning like: + +``` +WARN [1712003997.160121] [stacks-signer/src/runloop.rs:247] [signer_runloop] Signer is not registered for reward cycle 556. Waiting for confirmed registration... +``` + +This means your signer is running and awaiting registration; proceed to set up the Stacks node and begin stacking. + +{% hint style="warning" %} +Even after you Stack, you may still see messages saying the signer is not registered for the current or next reward cycle. This is normal until the prepare phase for your chosen reward cycle; assuming you meet the stacking minimum, the signer will be registered during that phase. +{% endhint %} + +*** + +## Set Up Your Bitcoin Node + +Optional but recommended to improve signer health and performance. + +Guides: + +* Run a full Bitcoin node: https://docs.stacks.co/guides-and-tutorials/nodes-and-miners/run-a-bitcoin-node +* Run a pruned Bitcoin node: https://docs.stacks.co/guides-and-tutorials/nodes-and-miners/run-a-pruned-bitcoin-node + +*** + +## Set Up Your Stacks Node + +Start the Stacks node after the signer is running — the node will not run unless it can send events to the signer. + +### Stacks Node Configuration + +Create `node-config.toml`. See the [Sample Configuration Files](https://app.gitbook.com/s/GVj1Z9vMuEOMe7oH7Wnq/signer-configuration) page for the full contents. + +Important fields to change: + +* `working_dir`: directory where the node persists data +* `auth_token`: authentication token used by signer (must match signer `auth_password`) +* `events_observer.endpoint`: host and port where your signer listens (example: `127.0.0.1:30000` or `stacks-signer.local:30000`) + +### Start with an archive + +Starting from an archive snapshot is much faster than syncing from genesis. Archives are at https://archive.hiro.so. + +Example to download and extract the latest mainnet snapshot: + +```bash +curl -# https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz -o stacks-snapshot.tar.gz +tar -zxvf stacks-snapshot.tar.gz +``` + +This creates a `mainnet` folder where downloaded. Set `working_dir` to the parent directory containing `mainnet`. + +See best practices for snapshots: ../best-practices-to-snapshot-the-chainstate.md + +### Run a Stacks Node with Docker + +Use the `blockstack/stacks-core` image (example tag: `3.1.0.0.13`). + +When running the container: + +* Expose the port configured for `p2p_bind` to the internet. +* Make the port configured for `rpc_bind` accessible by your signer. +* `working_dir` needs 500 GB–1 TB storage. +* Include your `node-config.toml`. + +Example docker run: + +```bash +IMG="blockstack/stacks-core" +VER="3.1.0.0.13" +STX_NODE_CONFIG="./node-config.toml" + +docker run -d \ + -v $STX_NODE_CONFIG:/config.toml \ + -v /var/stacks \ + -p 20443:20443 \ + -p 20444:20444 \ + -e RUST_BACKTRACE=full \ + --name stacks-node \ + $IMG:$VER \ + stacks-node start \ + --config /config.toml +``` + +Or with a custom Dockerfile: + +```docker +FROM blockstack/stacks-core:3.1.0.0.13 +COPY node-config.toml /config.toml +EXPOSE 20444 +EXPOSE 20443 +CMD ["stacks-node", "start", "--config", "/config.toml"] +``` + +If you get connection refused errors, you may need to point `events_observer.endpoint` to the Docker signer container. If using default Docker bridge mode, `localhost` inside the container is not the host — point the endpoint to the Docker host or the signer container hostname accordingly. + +### Run a Stacks Node with a Binary + +Download the pre-built `stacks-node` binary from the [Stacks Core releases](https://github.com/stacks-network/stacks-core/releases). + +Start the node: + +```bash +./stacks-node start --config node-config.toml +``` + +### Verify Stacks Node is Running + +Typical startup logs: + +```bash +Mar 6 19:35:08.212848 INFO stacks-node 0.1.0 +Mar 6 19:35:08.213084 INFO Loading config at path ./Stacks-config.toml +Mar 6 19:35:08.216674 INFO Registering event observer at: localhost:30000 +Mar 6 19:35:08.221603 INFO Migrating sortition DB to the latest schema version +Mar 6 19:35:08.224082 INFO Migrating chainstate DB to the latest schema version +Mar 6 19:35:08.227404 INFO Start syncing Bitcoin headers, feel free to grab a cup of coffee, this can take a while +``` + +Ensure you see the `Registering event observer at XXX` log with your signer endpoint. Once Bitcoin headers are synced, you can GET `/v2/info` on the node RPC endpoint (default port 20443). + +You may see many logs while syncing; refer to How to Read the Signer Logs if concerned. + +*** + +## Setup Your Stacks Accounts + +{% hint style="info" %} +For more on stacking and signing relationship, see the [Stack STX](broken-reference) guide. +{% endhint %} + +As a signer you’ll manage two Stacks accounts: + +1. A “pool operator” wallet, which commits delegated STX to your signer +2. Your signer’s wallet + +{% hint style="warning" %} +For testing, make sure you are using testnet (not mainnet). Testnet STX can be [requested from a faucet](https://explorer.hiro.so/sandbox/faucet?chain=testnet). +{% endhint %} + +### Setup Your Pool Operator Wallet + +Set up a pool operator wallet using any Stacks wallet, such as [Leather](https://leather.io/) or [Xverse](https://www.xverse.app/). You may generate a new account or use an existing one. Leather supports Ledger hardware wallets if you prefer. + +Fund the wallet with enough STX to cover transaction fees (testnet: faucet at https://explorer.hiro.so/sandbox/faucet?chain=testnet). + +Share this wallet’s STX address with parties that will delegate STX to you. For improved UX, you might use the helper contract allowing a BTC address for stackers ([pox4-pools](https://explorer.hiro.so/txid/SP001SFSMC2ZY76PD4M68P3WGX154XCH7NE3TYMX.pox4-pools?chain=mainnet)) and add your pool to [earn.leather.io](https://earn.leather.io/). + +*** + +If you need more detailed troubleshooting or further setup examples (config snippets, sample signer-config.toml or node-config.toml), let me know which files or examples you'd like converted or added. diff --git a/docs/operate/run-a-signer/best-practices-to-run-a-signer.md b/docs/operate/run-a-signer/best-practices-to-run-a-signer.md new file mode 100644 index 0000000000..96b30092c8 --- /dev/null +++ b/docs/operate/run-a-signer/best-practices-to-run-a-signer.md @@ -0,0 +1,101 @@ +# Best Practices to Run a Signer + +{% hint style="info" %} +**Intended audience**: solo Stackers or Stacking pool operators. +{% endhint %} + +The following best practices suggest how to create a resilient setup for running your Signer. + +{% hint style="info" %} +tl;dr: avoid single point of failures, introduce redundancy, monitor things. +{% endhint %} + +### Monitor your Signer and collect logs + +* See [here](how-to-monitor-signer.md) on how to set up monitoring. +* Retain at least 1 week of logs for both the Signer and the Stacks node. + +### Downstream components + +* Run a dedicated Bitcoin node and Stacks node per Signer. + * Ensure the nodes are provisioned with the minimum hardware requirements described [here](https://docs.stacks.co/guides-and-tutorials/running-a-signer#minimum-system-requirements). + * Nodes should be exclusively dedicated to serve the Signer. Avoid re-using them to serve other clients as that may negatively affect performance (no mock-signing, no Stacks API nodes). +* If running dedicated nodes is not possible, then ensure that the Bitcoin / Stacks nodes do not become single points of failure for multiple signers depending on them. + * Introduce redundancy, load balancing, rely on a robust Bitcoin RPC provider, etc. + +### Split voting power across multiple Signers + +* Distribute your voting power across multiple, distinct Signer public keys to mitigate the risk of loss or downtime from a single compromised key. +* Each Signer should also limit voting power to a maximum amount and invite Stackers to use a different Signer when the limit is reached. + +### Monitor new software releases + +* Stay up-to-date with new releases, patches, and security advisories (e.g., GitHub, mailing lists, Discord). +* Apply updates as quickly as possible, especially those addressing a security vulnerability. + +### Upgrade procedures + +{% stepper %} +{% step %} +### Test one instance first + +Upgrade one Signer instance at a time. Test the update on a single instance and verify functionality before proceeding to others. +{% endstep %} + +{% step %} +### Roll out gradually + +If the test is successful, proceed to upgrade the remaining instances one-by-one. +{% endstep %} + +{% step %} +### Minimize downtime + +While a Signer is offline for upgrades, it won't sign any blocks. Ensure that the downtime is as short as possible. +{% endstep %} +{% endstepper %} + +### Diversified hosting + +* Use different provider / configuration where feasible (e.g., a self-hosted instance and one in the cloud, or in two different data center regions, etc.). +* Ensure each host has redundant power supply and network connectivity. + +### Fall-back deployments + +* Deploy additional Stacks nodes and Bitcoin nodes to be used as fall-back. + * Use the same configuration as your active instances. + * For the Stacks node, comment out the `event_observer` section. +* Prepare a backup Signer (same configuration) to be quickly activated, but do not run it. + * At all times, there should be exactly one Signer instance running for each Signer private key. +* These fall-back instances should be hosted on separate physical hosts (see diversified hosting) from the instances usually active in operations (serving each Signer). + +To switch to the fall-back configuration quickly if an active instance fails, follow these steps: + +{% stepper %} +{% step %} +### Run the backup Signer + +Start the prepared backup Signer instance. +{% endstep %} + +{% step %} +### Enable event observer + +Enable the `event_observer` section of the Stacks node configuration. +{% endstep %} + +{% step %} +### Restart the node + +Restart the Stacks node so it runs with the enabled `event_observer`. +{% endstep %} +{% endstepper %} + +### Redundancy in operations + +* Ensure that multiple, trusted users can manage and maintain signer instances. +* Where feasible, users should span different timezones. + +### Backup signer keys in cold-storage + +* Keep an offline, secure backup of all Signer private keys (e.g., hardware security modules or encrypted storage devices). diff --git a/docs/operate/run-a-signer/how-to-monitor-signer.md b/docs/operate/run-a-signer/how-to-monitor-signer.md new file mode 100644 index 0000000000..baad96143a --- /dev/null +++ b/docs/operate/run-a-signer/how-to-monitor-signer.md @@ -0,0 +1,229 @@ +# How to Monitor Signer + +We will use [Grafana Cloud](https://grafana.com/) to observe and monitor both the Signer and its corresponding Stacks node. + +## Requirements + +Grafana's application observability docs have a [great quick-start](https://grafana.com/docs/grafana-cloud/monitor-applications/application-observability/). We will use: + +* Grafana Cloud to collect metrics and visualize them. +* Grafana Alloy, on the Signer host, to push the metrics. + +### Creating a Grafana Cloud account + +Before we begin, create a [Grafana Cloud](https://grafana.com/docs/grafana-cloud/monitor-applications/application-observability/grafana-cloud/) account (they offer a free tier that you can use). + +Once done, access your dashboard and follow these steps: + +{% stepper %} +{% step %} +### Add a new connection + +Click on "Connections", then "Add new connection". +{% endstep %} + +{% step %} +### Select Hosted Prometheus metrics + +Select "Hosted Prometheus metrics". +{% endstep %} + +{% step %} +### Choose via Grafana Alloy + +Select "Via Grafana Alloy", then on step 2 choose "Run Grafana Alloy" to generate an API token. +{% endstep %} +{% endstepper %} + +Note the token `GCLOUD_RW_API_KEY` and the parameters `GCLOUD_HOSTED_METRICS_URL` and `GCLOUD_HOSTED_METRICS_ID`; we will use them later. + +### Configuring the Signer and the Stacks node + +Ensure both your Signer configuration and your node configuration include the following lines: + +```toml +# signer-config.toml + +# ... + +# Adjust to 0.0.0.0:30001 if running in Docker. +metrics_endpoint = "127.0.0.1:30001" +``` + +```toml +# node-config.toml +[node] + +# ... + +# Adjust to 0.0.0.0:9153 if running in Docker. +prometheus_bind = "127.0.0.1:9153" +``` + +The pre-compiled binaries already include the monitoring feature. However, if you are compiling the application binaries yourself, remember to enable the Cargo feature `monitoring_prom` while building them, for example: + +```bash +cargo build --features monitoring_prom,slog_json --release +``` + +Once both binaries are running with the updated configuration, you can peek at the metrics being exposed: + +```bash +curl 127.0.0.1:30001/metrics + +# HELP stacks_signer_current_reward_cycle The current reward cycle + +# TYPE stacks_signer_current_reward_cycle gauge +stacks_signer_current_reward_cycle 95 + +# HELP stacks_signer_node_rpc_call_latencies_histogram Time (seconds) measuring round-trip RPC call latency to the Stacks node + +# TYPE stacks_signer_node_rpc_call_latencies_histogram histogram +... +stacks_signer_node_rpc_call_latencies_histogram_bucket{path="/v2/info",le="0.005"} 0 +stacks_signer_node_rpc_call_latencies_histogram_bucket{path="/v2/info",le="0.01"} 0 +stacks_signer_node_rpc_call_latencies_histogram_bucket{path="/v2/info",le="0.025"} 0 +stacks_signer_node_rpc_call_latencies_histogram_bucket{path="/v2/info",le="0.05"} 985 +stacks_signer_node_rpc_call_latencies_histogram_bucket{path="/v2/info",le="0.1"} 1194 +... +``` + +Also, you'll have a `/info` endpoint on the same port: + +```bash +curl 127.0.0.1:30001/info +``` + +### Install Alloy + +Follow these instructions to install [Grafana Alloy](https://grafana.com/docs/alloy/latest/set-up/install/linux/). + +On Debian-based distributions: + +```bash +sudo apt install gpg +sudo mkdir -p /etc/apt/keyrings/ +wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null +echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list +sudo apt-get update +sudo apt-get install alloy +``` + +### Configure Alloy + +Edit the file `/etc/alloy/config.alloy` as follows, replacing the placeholders related to the `prometheus` endpoint with the parameters obtained when creating a Grafana Cloud account: + +* `GCLOUD_HOSTED_METRICS_URL` +* `GCLOUD_HOSTED_METRICS_ID` +* `GCLOUD_RW_API_KEY` + +```conf +// For a full configuration reference, see https://grafana.com/docs/alloy +// For a default configuration, integrating all environmental variables from Grafana Cloud +// see https://storage.googleapis.com/cloud-onboarding/alloy/config/config.alloy + +logging { + level = "warn" +} + +prometheus.exporter.unix "default" { + include_exporter_metrics = true + disable_collectors = ["mdadm"] +} + +prometheus.scrape "default" { + targets = array.concat( + prometheus.exporter.unix.default.targets, + [ + { + // Self-collect metrics + job = "alloy", + __address__ = "127.0.0.1:12345", + }, + { + // stacks-signer + job = "stacks-signer", + __address__ = "127.0.0.1:30001", + }, + { + // stacks-node + job = "stacks-node", + __address__ = "127.0.0.1:9153", + }, + ], + ) + + forward_to = [prometheus.remote_write.metrics_service.receiver] +} + +prometheus.remote_write "metrics_service" { + external_labels = {"instance" = constants.hostname} + endpoint { + # TODO: Edit the URL below with your Grafana production URL. + # should end with /api/prom/push + url = "" + + # TODO: Edit with your Grafana Cloud ID and Token + basic_auth { + username = "" + password = "" + } + } +} +``` + +Enable and start Alloy: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable alloy.service +sudo systemctl start alloy.service +``` + +Metrics from your Signer and node will now start being pushed to Grafana Cloud. + +## Visualizing the metrics + +You can now start building a dashboard to visualize the metrics. + +1. Log in to Grafana Cloud and create a new Dashboard. +2. Pick the Prometheus instance you created before as the data source. +3. Create a new panel and pick `stacks_signer_current_reward_cycle` from the metrics. + +You should now be able to see Stacks' current reward cycle, as measured by the Signer, in the dashboard. + +Grafana comes with powerful data visualization tools. You can read about how to query and transform data [here](https://grafana.com/docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/), and find examples on how to build [Prometheus queries](https://prometheus.io/docs/prometheus/latest/querying/basics/). + +[This template](https://grafana.com/grafana/dashboards/22137-stacks-signer-template/) will kick-start your dashboard. + +![A screenshot of the Grafana dashboard instantiated from the template](https://grafana.com/api/dashboards/22137/images/17368/image) + +## Bonus: monitoring the host + +Since we are here, we can also monitor the host itself. Debian-based distributions make it very easy for us by using [`node_exporter`](https://github.com/prometheus/node_exporter/tree/master). + +```bash +sudo apt install prometheus-node-exporter +sudo systemctl enable prometheus-node-exporter +sudo systemctl start prometheus-node-exporter +``` + +This will expose metrics on port `9100` of `localhost`. + +We can now configure `alloy` to push them to Grafana. Edit your `/etc/alloy/config.alloy` file and add the following scrape target to the `prometheus.scrape "default"` targets list: + +```conf +{ + job = "node_exporter", + __address__ = "127.0.0.1:9100", +} +``` + +Now reload `alloy` and check its status: + +```bash +sudo systemctl reload alloy +sudo systemctl status alloy +``` + +`node_exporter` provides a lot of metrics. Explore them through the Grafana Explorer or use one of the many prepared dashboards (e.g., [this one](https://grafana.com/grafana/dashboards/1860-node-exporter-full/)) to see comprehensive information. Once you have a dashboard ready, you can also use it to configure alerts (e.g., on disk space, etc.). diff --git a/docs/operate/run-a-signer/how-to-read-signer-logs.md b/docs/operate/run-a-signer/how-to-read-signer-logs.md new file mode 100644 index 0000000000..2f74b2fd13 --- /dev/null +++ b/docs/operate/run-a-signer/how-to-read-signer-logs.md @@ -0,0 +1,63 @@ +# How to Read Signer Logs + +There are a lot of different messages you can get in the logs when running a signer. Getting a good grasp on what some of these logs mean can help you troubleshoot effectively and determine if your signer is running successfully or not. + +There are three types of log messages you should be aware of: + +* Successful +* Informational +* Errors + +Successful log messages indicate that you are on track and everything is working as expected. However, there are various success stages depending on several factors including your stacking status and the timing of the current reward cycle. + +There are also several informational/warning logs that you don't necessarily need to take action on, but they provide useful context about the network or the signer. + +Finally, error logs indicate something has gone wrong and you need to take action. + +Below are some common log messages you might see, what they mean, and what action (if any) you should take. + +{% hint style="info" %} +Successful / informational / error categories — general guidance: + +* Successful: nothing to do unless the message indicates a different stage of operation that requires action (e.g., registration needed). +* Informational: often safe to ignore, but useful for context. +* Errors: require investigation and remediation. +{% endhint %} + +## Successful + +### Signer uninitialized or not registered + +If you get a message saying your signer is uninitialized, it means your signer is not registered for the current or upcoming reward cycle (or the burnchain block height is not yet at the second block in the prepare phase) so the signer cannot determine registration status yet. This does not mean the signer process itself has failed — it is running successfully, but the signer cannot act until registration/delegation occurs. + +Example log: + +`Signer spawned successfully. Waiting for messages to process... INFO [1711088054.872542] [stacks-signer/src/runloop.rs:278] [signer_runloop] Running one pass for signer ID# 0. Current state: Uninitialized` + +You may also see a warning like: + +``` +WARN [1712003997.160121] [stacks-signer/src/runloop.rs:247] [signer_runloop] Signer is not registered for reward cycle 556. Waiting for confirmed registration... +``` + +Action: + +* If you want the signer to participate, either delegate to it or stack on your own for an upcoming reward cycle. +* For more details on stacking and registration, see the How to Stack doc: ../stack-stx/stacking-flow.md + +## Informational + +### Peer not connecting + +If you see a message about a peer not connecting, for example: + +``` +INFO [1711988555.021567] [stackslib/src/net/neighbors/walk.rs:1015] [p2p-(0.0.0.0:20444,0.0.0.0:20443)] local.80000000://(bind=0.0.0.0:20444)(pub=Some(10.0.19.16:20444)): Failed to connect to facade0b+80000000://172.16.60.18:20444: PeerNotConnected +``` + +This means your node attempted to connect to another node on the network but was unable to. This can happen for many reasons (network connectivity, remote node offline, NAT/firewall, etc.). + +Action: + +* Usually not a cause for concern and does not impact whether your signer is running correctly. +* If you see many such messages or persistent connectivity issues, investigate network connectivity, firewall/NAT rules, or peer configuration. diff --git a/docs/operate/run-a-signer/opsec-best-practices.md b/docs/operate/run-a-signer/opsec-best-practices.md new file mode 100644 index 0000000000..bd1b1361a2 --- /dev/null +++ b/docs/operate/run-a-signer/opsec-best-practices.md @@ -0,0 +1,113 @@ +# OpSec Best Practices + +#### Threat Modeling + +A threat actor that is able to compromise > 70% of Signers (by stake weight) would be able to successfully propose Stacks blocks that would otherwise be considered invalid. + +Some potential vectors for signer key compromise are as follows: + +* stacks-signer node is compromised and key is exfiltrated from the filesystem +* Signer key is compromised during generation or deployment +* Signer key is accidentally checked into SCM (eg Github or Gitlab) +* Social engineering attack against Signer community: eg a malicious link is posted to social media that harvests key material +* An undisclosed backdoor is discovered in the Signer binary. +* Supply chain attack against stacks-signer source code: threat actor compromises upstream dependencies of stacks-signer + +#### Countermeasures + +What can Signers do to mitigate the threat vectors identified above? Let's identify countermeasures in response to each of the threats identified above, starting with the first vector: stacks-signer node compromise and key exfiltration. + +{% stepper %} +{% step %} +### Run the stacks-signer on a separate system from the stacks-node + +This reduces discoverability of the signer. Systems running the stacks-node participate in the peer-to-peer network and are more easily enumerated. If an attacker can't find your stacks-signer, they can't attack it directly. + +Best practice: ensure stacks-node and stacks-signer communicate only over trusted networks, ideally using localhost (127.0.0.1) or a secure private subnet. + +Note: Running the stacks-signer on a separate system is an option, but not strictly necessary. Running both on the same virtual machine within a private network, with traffic firewalled to allow only incoming P2P connections (port 20444), provides a secure and easier setup while minimizing exposure. +{% endstep %} + +{% step %} +### Run the stacks-signer as a separate user from the stacks-node + +When resource constraints prevent separate workloads, run the stacks-signer under a distinct unprivileged user account from the stacks-node. Ensure exclusive ownership and restrictive permissions for each user's configuration files. + +Example: the user running the signer binary (e.g., signer) should own the signer’s config file and set permissions to prevent other users from reading it. The same principle applies to the stacks-node user. This ensures only appropriate processes can access sensitive configuration details. +{% endstep %} + +{% step %} +### Harden the systemd configuration for the stacks-signer + +Hardening systemd can reduce blast radius if an attacker gains control of the stacks-signer process. An example stacks-signer.service systemd unit is shown below. This unit prevents certain filesystem writes and otherwise restricts the process. + +{% code title="stacks-signer.service" %} +```ini +[Unit] +Description=Stacks Signer +After=network.target +StartLimitBurst=3 +StartLimitIntervalSec=300 +ConditionFileIsExecutable=/usr/local/bin/stacks-signer +ConditionPathExists=/etc/stacks/signer +ConditionFileNotEmpty=/etc/stacks/signer/signer-config.toml + +[Service] +ExecStart=/usr/local/bin/stacks-signer run --config /home/etc/stacks/signer/signer-config.toml +User={{ svc_user }} +Group={{ svc_user }} +Type=simple +Restart=on-failure +TimeoutStopSec=600 +KillSignal=SIGTERM +#KillSignal=SIGINT + +# Provide a private /tmp and /var/tmp. +PrivateTmp=true + +# Mount /usr, /boot/ and /etc read-only for the process. +ProtectSystem=full + +# Deny access to /home, /root and /run/user +ProtectHome=true + +# Disallow the process and all of its children to gain + +# new privileges through execve(). +NoNewPrivileges=true + +# Use a new /dev namespace only populated with API pseudo devices + +# such as /dev/null, /dev/zero and /dev/random. +PrivateDevices=true + +[Install] +WantedBy=multi-user.target +``` +{% endcode %} + +Read more about systemd hardening: https://www.ctrl.blog/entry/systemd-service-hardening.html +{% endstep %} + +{% step %} +### Restrict access to unnecessary ports and protocols + +The stacks-signer requires outbound TCP access to the stacks-node, but typically no other inbound network exposure is needed (except for OS updates and administrative access). Restrict network access to the minimum required for operation. +{% endstep %} + +{% step %} +### Harden the operating system + +A few practical OS hardening measures: + +* Run stacks-signer as an unprivileged user (not root). +* Set permissions on the stacks-signer key/config to be readable only by the user running the stacks-signer process, e.g.: + * sudo chmod 600 signer/signer-config.toml +* Require public-key authentication for SSH and disable SSH root login. +* Consider running sshd on a non-standard port to reduce noise from port scanners and credential-stuffing attacks. +{% endstep %} +{% endstepper %} + +This post outlines essential operational security best practices for Stacks Signers, key actors in the Nakamoto architecture. + +By implementing these strategies, signer operators can effectively mitigate risks and maintain the security and reliability of the Stacks network. diff --git a/docs/operate/run-a-signer/signer-quickstart.md b/docs/operate/run-a-signer/signer-quickstart.md new file mode 100644 index 0000000000..6de12f7c56 --- /dev/null +++ b/docs/operate/run-a-signer/signer-quickstart.md @@ -0,0 +1,461 @@ +# Signer Quickstart + +{% hint style="info" %} +**Current Signer and Stacks Node Versions** + +**Stacks Signer - latest** + +* [Docker Image](https://hub.docker.com/layers/blockstack/stacks-signer/latest) +* [GitHub Release](https://github.com/stacks-network/stacks-core/releases/latest) + +**Stacks Node - latest** + +* [Docker Image](https://hub.docker.com/layers/blockstack/stacks-core/latest) +* [GitHub Release](https://github.com/stacks-network/stacks-core/releases/latest) +{% endhint %} + +If you want to get up and running as an active signer as quickly as possible, here is a list of the commands you need to run and actions to take. + +If you are not familiar with how signing works yet, be sure to check out the [Signing concept guide](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/signing). + +If you would like a more detailed walkthrough of all of these steps, take a look at the [Running a Signer](file:///) guide. + +{% hint style="danger" %} +The CLI examples below may show outdated release versions. For the latest releases, always refer to the links above in the top info block. +{% endhint %} + +{% stepper %} +{% step %} +### Prerequisites + +{% tabs %} +{% tab title="Mainnet" %} +```bash +# Create the required directories +mkdir -p ~/stacks-signer/data +mkdir -p ~/stacks-node/data + +# Install needed packages +sudo apt install -y npm wget unzip jq tar + +# Install Stacks CLI globally +npm install --global @stacks/cli + +# Generate a new account and store details in a file +stx make_keychain | jq > ~/stacks-signer/keychain.json +``` +{% endtab %} + +{% tab title="Testnet" %} +```bash +# Create the required directories +mkdir -p ~/stacks-signer/data +mkdir -p ~/stacks-node/data + +# Install needed packages +sudo apt install -y npm wget unzip jq tar + +# Install Stacks CLI globally +npm install --global @stacks/cli + +# Generate a new account and store details in a file + +# '-t' option makes this a testnet account +stx make_keychain -t | jq > ~/stacks-signer/keychain.json +``` +{% endtab %} +{% endtabs %} + +The account file previously created looks like this: + +```json +{ + "mnemonic": "aaa bbb ccc ddd ...", + "keyInfo": { + "privateKey": "65f3...", + "publicKey": "03a3...", + "address": "SP1G...", + "btcAddress": "19tg...", + "wif": "Kzdt...", + "index": 0 + } +} +``` + +From this file, you'll need the `privateKey` value. +{% endstep %} + +{% step %} +### Set Up Your Stacks Signer + +#### Download the stacks-signer binary + +Official binaries are available from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases). Each release includes pre-built binaries. Download the [latest signer release ZIP file](https://github.com/stacks-network/stacks-core/releases/latest) for your server’s architecture and decompress it. Inside of that folder is a `stacks-signer` binary. + +Assuming a `Linux x64 glibc` machine, the commands to download and uncompress the signer binary look like this: + +```bash +# The CLI examples below may show outdated release versions. +# Enter the signer directory +cd ~/stacks-signer + +# Download the signer binary zip +wget https://github.com/stacks-network/stacks-core/releases/latest/download/linux-glibc-x64.zip + +# Unzip the signer binary archive +unzip linux-glibc-x64.zip +``` + +#### Create the configuration file + +Create the configuration file required to start the signer (be sure to replace `` and `` with your auth token and private key values): + +{% tabs %} +{% tab title="Mainnet" %} +```bash +# The CLI examples below may show outdated release versions. +# Set environment variables +AUTH_TOKEN= # Used for signer-node authentication +PRIVATE_KEY= # privateKey from Step 1, this is the signer's private key + +# Create the signer's configuration file +cat < ~/stacks-signer/signer-config.toml +node_host = "127.0.0.1:20443" +endpoint = "127.0.0.1:30000" +network = "mainnet" +db_path = "$HOME/stacks-signer/data/signer.sqlite" +auth_password = "$AUTH_TOKEN" +stacks_private_key = "$PRIVATE_KEY" +metrics_endpoint = "127.0.0.1:9154" +block_proposal_timeout_ms = 180000 +tenure_idle_timeout_secs = 120 +EOF +``` +{% endtab %} + +{% tab title="Testnet" %} +```bash +# Set environment variables +AUTH_TOKEN= # Used for signer-node authentication +PRIVATE_KEY= # privateKey from Step 1, this is the signer's private key + +# Create the signer's configuration file +cat < ~/stacks-signer/signer-config.toml +node_host = "127.0.0.1:20443" +endpoint = "127.0.0.1:30000" +network = "testnet" +db_path = "$HOME/stacks-signer/data/signer.sqlite" +auth_password = "$AUTH_TOKEN" +stacks_private_key = "$PRIVATE_KEY" +metrics_endpoint = "127.0.0.1:9154" +block_proposal_timeout_ms = 180000 +EOF +``` +{% endtab %} +{% endtabs %} + +#### Verify the setup + +To ensure the signer has been set up correctly, you can run the following commands: + +```bash +# The CLI examples below may show outdated release versions. +# Verify the signer's version +~/stacks-signer/stacks-signer --version + +# Output: +stacks-signer stacks-signer signer-3.1.0.0.5.0 (release/signer-3.1.0.0.5.0:513dbc5, release build, linux [x86_64]) + +# Verify the config file +~/stacks-signer/stacks-signer check-config -c ~/stacks-signer/signer-config.toml + +# Output: +Config: +Stacks node host: 127.0.0.1:20443 +Signer endpoint: 127.0.0.1:30000 +Stacks address: SP1G... # address from keychain file +Public key: 03a3... # publicKey from keychain file +Network: mainnet # or testnet +Chain ID: 0x1 # or 0x80000000 for testnet +Database path: /home/admin/stacks-signer/data/signer.sqlite +Metrics endpoint: 127.0.0.1:9154 +``` + +#### Start the signer + +If the outputs of the previous commands are correct, you can proceed and start the signer: + +```bash +~/stacks-signer/stacks-signer run -c ~/stacks-signer/signer-config.toml +``` +{% endstep %} + +{% step %} +### Optional: Set up a Bitcoin node (strongly recommended) + +In order to optimize signer health and performance, we highly recommend setting up your own Bitcoin node rather than relying on a third-party node. + +We have created guides for running both a [full Bitcoin node](../run-a-node/run-a-bitcoin-node.md) and a [pruned Bitcoin node](../run-a-node/run-a-pruned-bitcoin-node.md) you can follow. +{% endstep %} + +{% step %} +### Set Up Your Stacks Node + +#### Download the stacks-node binary + +Official binaries are available from the [Stacks Core releases page on Github](https://github.com/stacks-network/stacks-core/releases). Each release includes pre-built binaries. Download the [latest node release ZIP file](https://github.com/stacks-network/stacks-core/releases/latest) for your server’s architecture and decompress it. Inside of that folder is a `stacks-node` binary. + +Assuming a `Linux x64 glibc` machine, the commands to download and uncompress the node binary look like this: + +```bash +# The CLI examples below may show outdated release versions. +# Enter the node directory +cd ~/stacks-node + +# Download the node binary zip +wget https://github.com/stacks-network/stacks-core/releases/latest/download/linux-glibc-x64.zip + +# Unzip the node binary archive +unzip linux-glibc-x64.zip +``` + +#### Create the configuration file + +Create the configuration file required to start the node (be sure to replace `` with your auth token value): + +{% tabs %} +{% tab title="Mainnet" %} +{% hint style="warning" %} +For mainnet, we strongly recommended that you run your own bitcoin node (you can follow guides on how to run a [full Bitcoin node](https://docs.stacks.co/guides-and-tutorials/nodes-and-miners/run-a-bitcoin-node) or a [pruned Bitcoin node](https://docs.stacks.co/guides-and-tutorials/nodes-and-miners/run-a-pruned-bitcoin-node)) in order to ensure you have no connection issues when downloading bitcoin blocks. A hosted bitcoin node may cause your stacks node to fall behind tip and remain unsynced. + +If you run your own bitcoin node, you'll have to update `peer_host` and optionally add `rpc_port`, `peer_port`, `username` and `password` fields under the `[burnchain]` section of the node's configuration file. +{% endhint %} + +```bash +# The CLI examples below may show outdated release versions. +# Set environment variables +AUTH_TOKEN= # Used for signer-node authentication, same token as the one set up in the signer configuration + +# Create the node's configuration file +cat < ~/stacks-node/node-config.toml +[node] +working_dir = "$HOME/stacks-node/data" +rpc_bind = "127.0.0.1:20443" +p2p_bind = "0.0.0.0:20444" +prometheus_bind = "127.0.0.1:9153" +bootstrap_node = "02196f005965cebe6ddc3901b7b1cc1aa7a88f305bb8c5893456b8f9a605923893@seed.mainnet.hiro.so:20444,02539449ad94e6e6392d8c1deb2b4e61f80ae2a18964349bc14336d8b903c46a8c@cet.stacksnodes.org:20444,02ececc8ce79b8adf813f13a0255f8ae58d4357309ba0cedd523d9f1a306fcfb79@sgt.stacksnodes.org:20444,0303144ba518fe7a0fb56a8a7d488f950307a4330f146e1e1458fc63fb33defe96@est.stacksnodes.org:20444" +stacker = true + +[burnchain] +chain = "bitcoin" +mode = "mainnet" +peer_host = "bitcoin.mainnet.stacks.org" + +[connection_options] +auth_token = "$AUTH_TOKEN" + +[[events_observer]] +endpoint = "127.0.0.1:30000" +events_keys = ["stackerdb", "block_proposal", "burn_blocks"] +EOF +``` +{% endtab %} + +{% tab title="Testnet" %} +```bash +# Set environment variables +AUTH_TOKEN= # Used for signer-node authentication, same token as the one set up in the signer configuration + +# Create the node's configuration file +cat < ~/stacks-node/node-config.toml +[node] +working_dir = "$HOME/stacks-node/data" +rpc_bind = "127.0.0.1:20443" +p2p_bind = "0.0.0.0:20444" +bootstrap_node = "029266faff4c8e0ca4f934f34996a96af481df94a89b0c9bd515f3536a95682ddc@seed.testnet.hiro.so:30444" +prometheus_bind = "127.0.0.1:9153" +stacker = true +pox_sync_sample_secs = 30 +always_use_affirmation_maps = true +require_affirmed_anchor_blocks = true + +[burnchain] +mode = "krypton" +peer_host = "bitcoin.regtest.hiro.so" +peer_port = 18444 +pox_prepare_length = 100 +pox_reward_length = 900 + +[connection_options] +auth_token = "$AUTH_TOKEN" +private_neighbors = false + +[[events_observer]] +endpoint = "127.0.0.1:30000" +events_keys = ["stackerdb", "block_proposal", "burn_blocks"] + +[[ustx_balance]] +address = "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST319CF5WV77KYR1H3GT0GZ7B8Q4AQPY42ETP1VPF" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST221Z6TDTC5E0BYR2V624Q2ST6R0Q71T78WTAX6H" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST2TFVBMRPS5SSNP98DQKQ5JNB2B6NZM91C4K3P7B" +amount = 10000000000000000 + +[[burnchain.epochs]] +epoch_name = "1.0" +start_height = 0 + +[[burnchain.epochs]] +epoch_name = "2.0" +start_height = 0 + +[[burnchain.epochs]] +epoch_name = "2.05" +start_height = 1 + +[[burnchain.epochs]] +epoch_name = "2.1" +start_height = 2 + +[[burnchain.epochs]] +epoch_name = "2.2" +start_height = 3 + +[[burnchain.epochs]] +epoch_name = "2.3" +start_height = 4 + +[[burnchain.epochs]] +epoch_name = "2.4" +start_height = 5 + +[[burnchain.epochs]] +epoch_name = "2.5" +start_height = 6 + +[[burnchain.epochs]] +epoch_name = "3.0" +start_height = 1_900 + +[[burnchain.epochs]] +epoch_name = "3.1" +start_height = 2_000 +EOF +``` +{% endtab %} +{% endtabs %} + +#### Optional: Start the node with a data archive + +You can [download a chainstate archive](https://archive.hiro.so/) in order to quickly sync your node, otherwise it will take a long time to get up-to-date with the other nodes. + +{% tabs %} +{% tab title="Mainnet" %} +```bash +# Enter the node's datadir +cd ~/stacks-node/data + +# Download the archive +wget https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz + +# Decompress the archive +tar -xvf mainnet-stacks-blockchain-latest.tar.gz + +# Remove the archive +rm mainnet-stacks-blockchain-latest.tar.gz +``` +{% endtab %} + +{% tab title="Testnet" %} +```bash +# Enter the node's datadir +cd ~/stacks-node/data + +# Download the archive +wget https://archive.hiro.so/testnet/stacks-blockchain/testnet-stacks-blockchain-latest.tar.gz + +# Decompress the archive +tar -xvf testnet-stacks-blockchain-latest.tar.gz + +# Remove the archive +rm testnet-stacks-blockchain-latest.tar.gz +``` +{% endtab %} +{% endtabs %} + +#### Verify the setup + +To ensure the node has been set up correctly, you can run the following commands: + +```bash +# The CLI examples below may show outdated release versions. +# Verify the node's version +~/stacks-node/stacks-node version + +# Output: +INFO [1738695915.769633] [testnet/stacks-node/src/main.rs:278] [main] stacks-node 3.1.0.0.5 (release/3.1.0.0.5:513dbc5, release build, linux [x86_64]) +stacks-node 3.1.0.0.5 (release/3.1.0.0.5:513dbc5, release build, linux [x86_64]) + +# Verify the node's config +~/stacks-node/stacks-node check-config --config ~/stacks-node/node-config.toml + +# Output: +INFO [1738695915.769633] [testnet/stacks-node/src/main.rs:278] [main] stacks-node 3.1.0.0.5 (release/3.1.0.0.5:513dbc5, release build, linux [x86_64]) +INFO [1729788064.913175] [testnet/stacks-node/src/main.rs:318] [main] Loading config at path /home/admin/stacks-node/node-config.toml +INFO [1729788064.969551] [testnet/stacks-node/src/main.rs:331] [main] Loaded config! +``` + +#### Start the node + +If the outputs of the previous commands are correct, you can proceed and start the node: + +```bash +~/stacks-node/stacks-node start --config ~/stacks-node/node-config.toml +``` +{% endstep %} + +{% step %} +### Generate your signer signature + +In order to stack, you'll need your signer signature. The fields required are further explained in the [Generate a signer key signature](https://docs.stacks.co/guides-and-tutorials/stack-stx/stacking-flow#step-2-generate-a-signer-key-signature) guide. + +The command to generate a signature looks like this: + +```bash +~/stacks-signer/stacks-signer generate-stacking-signature \ + --method stack-stx \ + --max-amount 1000000000000 \ + --auth-id 195591226970828652622091037492597751808 \ + --period 12 \ + --reward-cycle 100 \ + --pox-address 19tg... \ + --config ~/stacks-signer/signer-config.toml \ + --json +``` + +The generated JSON can be then copy-pasted directly in the [Leather Earn](https://earn.leather.io/) website mentioned in the next step. +{% endstep %} + +{% step %} +### Start stacking + +The simplest route is to solo stack. You can do that by using [Leather Earn](https://earn.leather.io/). Click on the 'Stack Independently' button and follow the instructions there. + +If you would like to learn more about solo stacking or running a pool operator, take a look at the [Stack STX](https://docs.stacks.co/guides-and-tutorials/stack-stx) guide. +{% endstep %} + +{% step %} +### Monitoring + +If you would like to learn more about monitoring your signer and its corresponding node, you can check the [How to Monitor a Signer](https://docs.stacks.co/guides-and-tutorials/running-a-signer/how-to-monitor-signer) guide. +{% endstep %} +{% endstepper %} diff --git a/docs/operate/snapshot-the-chainstate.md b/docs/operate/snapshot-the-chainstate.md new file mode 100644 index 0000000000..9486830d0c --- /dev/null +++ b/docs/operate/snapshot-the-chainstate.md @@ -0,0 +1,222 @@ +# Snapshot the Chainstate + +{% hint style="info" %} +**Intended audience**: Solo Stackers, Stacking pool operators, and node operators who need to create reliable chainstate backups. +{% endhint %} + +Regular snapshots of your Stacks chainstate help you recover quickly when things go wrong. This guide shows you how to create and manage chainstate snapshots properly. + +{% hint style="warning" %} +**Critical**: Always shut down your Stacks node properly before creating a snapshot. Creating snapshots while the node is running will result in corrupted chainstate data. +{% endhint %} + +### Shutdown Procedure + +To produce a valid chainstate backup, the node should be stopped gracefully before making a copy. The following steps will correctly shutdown the Stacks node: + +{% stepper %} +{% step %} +### Check node status before shutdown + +```bash +# Verify if the node is responsive +curl http://localhost:20443/v2/info +``` +{% endstep %} + +{% step %} +### Initiate graceful shutdown + +* For Docker: `docker stop stacks-node` (allows at least 10 seconds for graceful shutdown) +* For systemd: `systemctl stop stacks-node` +* For manual processes: + +```bash +kill $(ps aux | grep stacks-node | grep -v grep | awk '{print $2}') +``` +{% endstep %} + +{% step %} +### Verify complete shutdown + +```bash +# Ensure no stacks-node processes are running +ps aux | grep stacks-node +``` +{% endstep %} +{% endstepper %} + +### Overview of Snapshot Methods + +There are two primary approaches for creating Stacks chainstate snapshots: + +1. **File-based snapshots** - compress up the chainstate folder +2. **Volume snapshots** - snapshot the entire disk/volume + +Each method has its advantages depending on your infrastructure setup and recovery requirements. + +### File-Based Snapshots + +This method involves compressing the chainstate directory and storing it locally, or uploading to a cloud storage service. + +#### Steps (see [Example Automation Code section](snapshot-the-chainstate.md#example-automation-code) below) + +1. **Stop the Stacks node gracefully** +2. **Create compressed archive** +3. **Upload to cloud storage or save it locally** +4. **Restart the Stacks node** + +### Volume-Based Snapshots + +This method creates block-level snapshots of the entire storage volume containing the chainstate. Different filesystems have different tools: + +* **ZFS**: Use `zfs snapshot` - [OpenZFS documentation](https://openzfs.github.io/openzfs-docs/man/v2.3/8/zfs-snapshot.8.html) +* **XFS**: Use `xfsdump` - [XFS documentation](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/7/html/storage_administration_guide/xfsbackuprestore) +* **ext4**: Use LVM snapshots - [LVM guide](https://kerneltalks.com/disk-management/how-to-guide-lvm-snapshot/) + +You can also use cloud provider snapshot tools (AWS EBS, Azure Disk, GCP Persistent Disk). + +#### Steps + +1. **Stop the Stacks node gracefully** +2. **Create volume snapshot** using ZFS or cloud provider tools +3. **Restart the Stacks node** + +### How to Restore + +After restoring the chainstate, you can check for corruption by waiting for a few blocks to download and ensuring the node syncs correctly. + +#### From File Snapshots + +1. Stop the Stacks node +2. Download and extract the snapshot +3. Replace the chainstate directory +4. Restart the node + +#### From Volume Snapshots + +1. Stop the Stacks node +2. Create a new volume from the snapshot +3. Attach the volume to your instance +4. Update mount points if necessary +5. Restart the node + +### Example Automation Code + +Here's a simple script that handles both file and volume snapshots on AWS. + +{% code title="snapshot.sh" %} +``` +#!/bin/bash +set -euo pipefail + +# Configuration variables - modify these for your setup +SERVICE_NAME="stacks-node" # systemd service name +SNAPSHOT_DIR="/var/stacks/mainnet" # path to chainstate directory +SNAPSHOT_BASE="/tmp" # temporary directory for archives +EBS_VOLUME_ID="vol-1234567890abcdef0" # EBS volume ID containing chainstate +S3_BUCKET="s3://my-stacks-snapshots" # S3 bucket for archive storage +SNAPSHOT_TYPE="archive" # Options: ebs, archive, or both + +# Stop the Stacks node service gracefully +stop_service() { + echo "Stopping $SERVICE_NAME..." + sudo systemctl stop "$SERVICE_NAME" +} + +# Start the Stacks node service +start_service() { + echo "Starting $SERVICE_NAME..." + sudo systemctl start "$SERVICE_NAME" +} + +# Create compressed archive and upload to S3 +snapshot_archive() { + echo "Creating archive snapshot..." + + # Generate timestamp and version info for filename + TIMESTAMP=$(date +"%Y%m%d") + DIR_NAME=$(basename "$SNAPSHOT_DIR") + VERSION=$(stacks-node version 2>&1 | tail -n 1 | awk '{print $2}') + DEST="$SNAPSHOT_BASE/$DIR_NAME-$VERSION-$TIMESTAMP.tar.zst" + + # Create compressed archive (using zstd for better compression) + tar -cf - -C "$(dirname $SNAPSHOT_DIR)" "$(basename $SNAPSHOT_DIR)" | pzstd -o "$DEST" + echo "Archive created at: $DEST" + + # Upload to S3 + echo "Uploading to S3..." + aws s3 cp "$DEST" "$S3_BUCKET/" + echo "S3 upload complete: $S3_BUCKET/$(basename "$DEST")" + + # Clean up local archive + rm "$DEST" +} + +# Create EBS volume snapshot +snapshot_ebs() { + echo "Creating EBS snapshot of $EBS_VOLUME_ID..." + + # Generate description with timestamp + TIMESTAMP=$(date +"%Y%m%d") + DESC="Stacks Node Snapshot - $TIMESTAMP" + + # Create snapshot with tags + SNAPSHOT_ID=$(aws ec2 create-snapshot \ + --volume-id "$EBS_VOLUME_ID" \ + --description "$DESC" \ + --tag-specifications "ResourceType=snapshot,Tags=[{Key=Name,Value=Stacks Snapshot},{Key=type,Value=chainstate}]" \ + --query 'SnapshotId' --output text) + + echo "EBS Snapshot ID: $SNAPSHOT_ID" +} + +# Main execution function +main() { + case "$SNAPSHOT_TYPE" in + ebs) + stop_service + snapshot_ebs + start_service + ;; + archive) + stop_service + snapshot_archive + start_service + ;; + both) + stop_service + snapshot_archive # Create archive first + snapshot_ebs # Then EBS snapshot + start_service + ;; + *) + echo "Invalid snapshot type: $SNAPSHOT_TYPE. Available options: ebs, archive, or both." + exit 1 + ;; + esac + + echo "Snapshot process completed successfully!" +} + +# Execute main function +main +``` +{% endcode %} + +#### How to Use + +1. **Edit the variables** at the top of the script for your setup +2. **Make it executable**: `chmod +x snapshot.sh` +3. **Run it**: `./snapshot.sh` +4. **Schedule it with cron** for daily backups: + + ``` + # Daily snapshot at 2 AM + 0 2 * * * /path/to/snapshot.sh + ``` + +#### What You Need + +* AWS CLI set up with the right permissions +* `pzstd` installed (comes with the zstd package) diff --git a/docs/operate/stacking-stx/README.md b/docs/operate/stacking-stx/README.md new file mode 100644 index 0000000000..e6c3a1167c --- /dev/null +++ b/docs/operate/stacking-stx/README.md @@ -0,0 +1,128 @@ +# Stacking STX + +Stacking is an essential component of Stacks. + +There are three different ways you can potentially stack your STX tokens and we have a dedicated guide for each of these scenarios. + +If you aren't familiar with how stacking works, especially as it relates to signing after the Nakamoto upgrade, be sure to check out the following concept guides: + +* [Stackers and signing](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/signing) +* [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) + +In Nakamoto, stacking flows have significant changes in comparison to previous versions of Stacks. Because Nakamoto requires stackers to run a signer, which validates blocks produced by Stacks miners, stackers need to provide new information when making Stacking transactions. + +These changes affect both solo Stacking and delegated Stacking. This document outlines the new flows for solo stacking. The next doc outlines the flow and steps for operating a pool. + +The following sections will walk you through how to begin operating as a solo stacker. + +Stacking utilizes the `pox-4` contract. There is a detailed [walkthrough of the stacking contract](https://app.gitbook.com/s/GVj1Z9vMuEOMe7oH7Wnq/clarity/example-contracts/stacking) that you can look at to see what functions are being called at each phase and some common errors you may encounter. This will be especially useful for pool operators who need to call these functions. + +This doc is also useful if you run into errors when calling stacking functions, as it both walks through several common error scenarios and walks through each function call so you can more easily trace what might be happening. + +Before we get into the step-by-step of how to actually stack, it's important to make sure you have an understanding of the different roles, processes and functions involved in Stacking. + +### Definitions and Roles + +* **Stacker**: an entity locking their STX to receive PoX rewards. This is a broad term including solo Stackers and Stackers who use pools. +* **Solo stacker**: an entity that locks their own STX and runs a signer. They don’t receive delegation. +* **Delegator**: a stacker who locks their STX by delegating to a pool operator that runs a signer. They don’t run the signer. +* **Pool operator**: an entity that runs a Signer and allows others to delegate their STX to them. A pool operator doesn’t need to Stack their own STX, but they can. They will also run a signer, but the pool operator and signer address may be different +* **Signer**: an entity that runs the stacks-signer software and participates in block validation. This can be either a solo Stacker or an entity receiving delegated STX. Depending on context, this may also refer to the signer software that validates blocks. + +{% hint style="info" %} +It's important to understand that in the context of the pool operator and signer, these are likely the same _entity_ but may not be the same Stacks address. + +This distinction will be discussed further as we cover the step-by-step process below. +{% endhint %} + +Below are the primary ways you can stack: + +{% stepper %} +{% step %} +### Solo stacking + +If you meet the minimum and want to [solo stack](solo-stack.md), you will either need to run a signer, collaborate with an existing one, or use [stacking.tools](https://stacking.tools/). This guide will walk you through all options. +{% endstep %} + +{% step %} +### Operate a pool + +You can also [operate a pool](operate-a-stacking-pool.md) and have others delegate their STX to you. If you are a pool operator, you will need to run a signer, collaborate with an existing one, or use [stacking.tools](https://stacking.tools/). +{% endstep %} + +{% step %} +### Stack with a pool + +If you do not meet the minimum amount of STX to solo stack, you can [delegate your STX to a pool operator ](stack-with-a-pool.md)and have them stack on your behalf. The minimum stacking amount is dynamic and can be found by visiting the https://api.hiro.so/v2/pox endpoint and looking at the `min_threshold_ustx` field. Note it is denoted in uSTX (1 STX = 1,000,000 uSTX). This is the most common stacking scenario. +{% endstep %} +{% endstepper %} + +As you read through these, it may be helpful to follow along with the functions in the [pox-4 contract](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) to get an idea of what each function is doing. + +### Relationship between manual stacking transactions and the running signer + +This section describes the various transactions that signer entities need to make in order to be registered as a signer for a certain reward cycle. The order of operations between the automated signer and the stacking transactions that need to be done “manually” is important for ensuring that a signer is fully set up for a certain reward cycle. + +#### Prerequisite: ensure the signer is hosted and running + +It's important to emphasize the importance of getting the signer running in a hosted environment before making Stacking transactions. If the signer doesn’t do that, they run the risk of being registered as a signer without their signer software being ready to run DKG and other important consensus mechanisms. + +Some of the important things to double check to ensure the signer is “running” are: + +* The signer software is configured with a private key that the user can access (either through SSH or other means). This is important because their signer needs to utilize this private key to generate signer key signatures that are used in Stacking transactions. +* The signer software is properly configured to make RPC calls to a Stacks node. This refers to the `endpoint` signer configuration field. If properly configured, there should be logs in the Stacks node that show the RPC calls being made from the signer. +* The stacks node is properly configured to send events to the signer. This refers to the \[`event_observers`] field in the Stacks Node’s configuration. If properly configured, the signer should have logs indicating that it’s receiving events from the Stacks node. + +### How a signer becomes registered in the signer set + +Each of the stacking transactions described above are done “manually”. More specifically, this means that none of these transactions are executed automatically by the signer software. The transactions must be done “out of band”. + +In order for a signer to actually be registered in a reward cycle, there need to be manual transactions made in the `pox-4` contract. While the signer software is running, it is continually polling the Stacks node and asking “am I a signer in reward cycle N?”. + +If these manual transactions are confirmed, and the signer has enough STX associated with the signer’s public key, the signer will be registered as a signer in the signer set. + +#### Solo stacking + +The workflow for solo stackers is simpler, because there are less stacking transactions that need to be made. + +For solo stacking, the only transaction that needs to be made is `stack-stx`. Included in this transaction’s payload is the signer’s public key. + +In order for the signer to be registered in reward cycle N+1, the `stack-stx` transaction must be confirmed during the first 2000 blocks of reward cycle N. The last 100 blocks of cycle N (the “prepare phase”) is where DKG occurs. + +The start of the prepare phase is when Stacks nodes determine the official signer set of the next reward cycle. + +#### Delegated Stacking + +The workflow for delegated signers is more complex, because it requires more transactions. + +This workflow is explained more in detail in the [operate a pool](operate-a-stacking-pool.md) guide, but the high-level workflow is: + +{% stepper %} +{% step %} +### Stackers delegate their STX to a pool operator + +Stackers delegate their STX to a pool operator. +{% endstep %} + +{% step %} +### The pool operator approves specific stackers + +The pool operator makes `delegate-stack-stx` transactions to “approve” specific stackers. This needs to be called for every individual stacker that delegates to them. +{% endstep %} + +{% step %} +### The pool operator commits delegated STX + +The pool operator makes a `stack-aggregation-commit` transaction to “commit” all of its delegated STX up to this point. +{% endstep %} +{% endstepper %} + +Similar to solo stacking, these steps must be made before the prepare phase of an upcoming reward cycle. + +### Once a signer is registered in the signer set + +During the prepare phase before a reward cycle, Stacks nodes automatically determine the signer set for the upcoming cycle. When this occurs, the Stacks nodes make an “internal” transaction to update the `.signers` contract with the list of signers. + +The signer software is continuously polling the Stacks node to see if it is registered for a cycle. If the signer software finds that it is registered (by matching its public key to the signers stored in the `signers` contract) it begins performing its duties as a signer. + +During the prepare phase, the signers perform DKG through StackerDB messages. Once an aggregate public key is determined, the signer automatically makes a `vote-for-aggregate-key` transaction. No out-of-band action is needed to be taken for this to occur. diff --git a/docs/operate/stacking-stx/increase-stacked-position.md b/docs/operate/stacking-stx/increase-stacked-position.md new file mode 100644 index 0000000000..6ba205b031 --- /dev/null +++ b/docs/operate/stacking-stx/increase-stacked-position.md @@ -0,0 +1,495 @@ +# Increase Stacked Position + +This guide explains how to increase your stacked STX position. The process depends on your role: + +* **Solo Stackers** use the `stack-increase` function. +* **Delegators** must first revoke their current delegation using `revoke-delegate-stx` and then re-delegate with a higher amount to the same pool operator using `delegate-stx`. +* **Pool Operators** increase their delegators' locked amount by calling `delegate-stack-increase` and then stacking the increased amount with either `stack-aggregation-commit-indexed` (if not already committed) or `stack-aggregation-increase` (if the commit has already been made). + +## Solo Stackers + +Solo stackers can add more STX to their active stacking position by calling the `stack-increase` function. The new amount takes effect from the next stacking cycle. + +The `stack-increase` function locks an additional amount of STX from your account. Your account must be actively stacking and not delegating, and you must have enough unlocked STX to cover the increase. + +
+ +Function source code + +```clojure +;; Increase the number of STX locked. +;; *New in Stacks 2.1* +;; This method locks up an additional amount of STX from `tx-sender`'s, indicated +;; by `increase-by`. The `tx-sender` must already be Stacking & must not be +;; straddling more than one signer-key for the cycles effected. +;; Refer to `verify-signer-key-sig` for more information on the authorization parameters +;; included here. +(define-public (stack-increase + (increase-by uint) + (signer-sig (optional (buff 65))) + (signer-key (buff 33)) + (max-amount uint) + (auth-id uint)) + (let ((stacker-info (stx-account tx-sender)) + (amount-stacked (get locked stacker-info)) + (amount-unlocked (get unlocked stacker-info)) + (unlock-height (get unlock-height stacker-info)) + (cur-cycle (current-pox-reward-cycle)) + (first-increased-cycle (+ cur-cycle u1)) + (stacker-state (unwrap! (map-get? stacking-state + { stacker: tx-sender }) + (err ERR_STACK_INCREASE_NOT_LOCKED))) + (cur-pox-addr (get pox-addr stacker-state)) + (cur-period (get lock-period stacker-state))) + ;; tx-sender must be currently locked + (asserts! (> amount-stacked u0) + (err ERR_STACK_INCREASE_NOT_LOCKED)) + ;; must be called with positive `increase-by` + (asserts! (>= increase-by u1) + (err ERR_STACKING_INVALID_AMOUNT)) + ;; stacker must have enough stx to lock + (asserts! (>= amount-unlocked increase-by) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + ;; stacker must be directly stacking + (asserts! (> (len (get reward-set-indexes stacker-state)) u0) + (err ERR_STACKING_IS_DELEGATED)) + ;; stacker must not be delegating + (asserts! (is-none (get delegated-to stacker-state)) + (err ERR_STACKING_IS_DELEGATED)) + + ;; Validate that amount is less than or equal to `max-amount` + (asserts! (>= max-amount (+ increase-by amount-stacked)) (err ERR_SIGNER_AUTH_AMOUNT_TOO_HIGH)) + + ;; Verify signature from delegate that allows this sender for this cycle + (try! (consume-signer-key-authorization cur-pox-addr cur-cycle "stack-increase" cur-period signer-sig signer-key increase-by max-amount auth-id)) + + ;; update reward cycle amounts + (asserts! (is-some (fold increase-reward-cycle-entry + (get reward-set-indexes stacker-state) + (some { first-cycle: first-increased-cycle, + reward-cycle: (get first-reward-cycle stacker-state), + stacker: tx-sender, + add-amount: increase-by, + signer-key: signer-key }))) + (err ERR_INVALID_INCREASE)) + ;; NOTE: stacking-state map is unchanged: it does not track amount-stacked in PoX-4 + (ok { stacker: tx-sender, total-locked: (+ amount-stacked increase-by)}))) +``` + +
+ +Arguments: + +* Increase by: the amount of uSTX to add to your lock amount. +* Signer public key: the public key used for signing. This can stay the same, or you can use a new key. +* Signer signature: a signature proving control of your signing key. +* Max Amount: used to validate the signer signature provided. It represents the maximum number of uSTX (1 STX = 1,000,000 uSTX) that can be stacked in this transaction. +* Auth Id: used to validate the signer signature provided. It is a random integer that prevents re-use of this particular signer signature. + +## Delegators + +Delegators have to increase their delegated amount in two steps. + +{% stepper %} +{% step %} +### Revoke Your Current Delegation + +Before increasing your delegation, cancel your current delegation through the `revoke-delegate-stx` function, so that you can delegate an increased amount of STX afterwards. + +
+ +Function source code + +```clojure +;; Revokes the delegation to the current stacking pool. +;; New in pox-4: Fails if the delegation was already revoked. +;; Returns the last delegation state. +(define-public (revoke-delegate-stx) + (let ((last-delegation-state (get-check-delegation tx-sender))) + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + (asserts! (is-some last-delegation-state) (err ERR_DELEGATION_ALREADY_REVOKED)) + (asserts! (map-delete delegation-state { stacker: tx-sender }) (err ERR_DELEGATION_ALREADY_REVOKED)) + (ok last-delegation-state))) +``` + +
+{% endstep %} + +{% step %} +### Delegate with a Higher Amount + +After revoking, call the `delegate-stx` function with your new, higher amount. This function does not directly delegate the STX, but rather allows the pool operator to issue the stacking lock on behalf of the user calling this function. + +
+ +Function source code + +```clojure +;; Delegate to `delegate-to` the ability to stack from a given address. +;; This method _does not_ lock the funds, rather, it allows the delegate +;; to issue the stacking lock. +;; The caller specifies: +;; * amount-ustx: the total amount of ustx the delegate may be allowed to lock +;; * until-burn-ht: an optional burn height at which this delegation expires +;; * pox-addr: an optional address to which any rewards *must* be sent +(define-public (delegate-stx (amount-ustx uint) + (delegate-to principal) + (until-burn-ht (optional uint)) + (pox-addr (optional { version: (buff 1), hashbytes: (buff 32) }))) + + (begin + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; delegate-stx no longer requires the delegator to not currently + ;; be stacking. + ;; delegate-stack-* functions assert that + ;; 1. users can't swim in two pools at the same time. + ;; 2. users can't switch pools without cool down cycle. + ;; Other pool admins can't increase or extend. + ;; 3. users can't join a pool while already directly stacking. + + ;; pox-addr, if given, must be valid + (match pox-addr + address + (asserts! (check-pox-addr-version (get version address)) + (err ERR_STACKING_INVALID_POX_ADDRESS)) + true) + + ;; tx-sender must not be delegating + (asserts! (is-none (get-check-delegation tx-sender)) + (err ERR_STACKING_ALREADY_DELEGATED)) + + ;; add delegation record + (map-set delegation-state + { stacker: tx-sender } + { amount-ustx: amount-ustx, + delegated-to: delegate-to, + until-burn-ht: until-burn-ht, + pox-addr: pox-addr }) + + (ok true))) +``` + +
+ +Arguments: + +* Amount: Denoted in uSTX (1 STX = 1,000,000 uSTX). +* Delegate to: the STX address of the pool operator they're delegating to. +* Until burn height: optional BTC block height when the delegation expires. +* Pox Address: optional BTC address that, if specified, the signer must use to accept this delegation. + +{% hint style="info" %} +Make sure the revocation is successful before initiating a new delegation. Otherwise, the `delegate-stx` transaction will fail. +{% endhint %} +{% endstep %} +{% endstepper %} + +## Pool Operators + +Pool operators can increase the total stacking amount through a two-step process. First, update the delegation's locked amount with `delegate-stack-increase`. Then, stack the increased amount by committing it in a future cycle, or increasing an already committed position. + +### Increase the Locked Amount + +The `delegate-stack-increase` function allows a pool operator to add more STX to the existing locked position for a given delegator. It performs necessary checks and updates the delegation state with the increased amount. + +
+ +Function source code + +```clojure +;; As a delegator, increase an active Stacking lock, issuing a "partial commitment" for the +;; increased cycles. +;; *New in Stacks 2.1* +;; This method increases `stacker`'s current lockup and partially commits the additional +;; STX to `pox-addr` +(define-public (delegate-stack-increase + (stacker principal) + (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (increase-by uint)) + (let ((stacker-info (stx-account stacker)) + (existing-lock (get locked stacker-info)) + (available-stx (get unlocked stacker-info)) + (unlock-height (get unlock-height stacker-info))) + + ;; must be called with positive `increase-by` + (asserts! (>= increase-by u1) + (err ERR_STACKING_INVALID_AMOUNT)) + + (let ((unlock-in-cycle (burn-height-to-reward-cycle unlock-height)) + (cur-cycle (current-pox-reward-cycle)) + (first-increase-cycle (+ cur-cycle u1)) + (last-increase-cycle (- unlock-in-cycle u1)) + (cycle-count (try! (if (<= first-increase-cycle last-increase-cycle) + (ok (+ u1 (- last-increase-cycle first-increase-cycle))) + (err ERR_STACKING_INVALID_LOCK_PERIOD)))) + (new-total-locked (+ increase-by existing-lock)) + (stacker-state + (unwrap! (map-get? stacking-state { stacker: stacker }) + (err ERR_STACK_INCREASE_NOT_LOCKED)))) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; stacker must not be directly stacking + (asserts! (is-eq (len (get reward-set-indexes stacker-state)) u0) + (err ERR_STACKING_NOT_DELEGATED)) + + ;; stacker must be delegated to tx-sender + (asserts! (is-eq (unwrap! (get delegated-to stacker-state) + (err ERR_STACKING_NOT_DELEGATED)) + tx-sender) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; stacker must be currently locked + (asserts! (> existing-lock u0) + (err ERR_STACK_INCREASE_NOT_LOCKED)) + + ;; stacker must have enough stx to lock + (asserts! (>= available-stx increase-by) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + + ;; stacker must have delegated to the caller + (let ((delegation-info (unwrap! (get-check-delegation stacker) (err ERR_STACKING_PERMISSION_DENIED))) + (delegated-to (get delegated-to delegation-info)) + (delegated-amount (get amount-ustx delegation-info)) + (delegated-pox-addr (get pox-addr delegation-info)) + (delegated-until (get until-burn-ht delegation-info))) + ;; must have delegated to tx-sender + (asserts! (is-eq delegated-to tx-sender) + (err ERR_STACKING_PERMISSION_DENIED)) + ;; must have delegated enough stx + (asserts! (>= delegated-amount new-total-locked) + (err ERR_DELEGATION_TOO_MUCH_LOCKED)) + ;; if pox-addr is set, must be equal to pox-addr + (asserts! (match delegated-pox-addr + specified-pox-addr (is-eq pox-addr specified-pox-addr) + true) + (err ERR_DELEGATION_POX_ADDR_REQUIRED)) + ;; delegation must not expire before lock period + (asserts! (match delegated-until + until-burn-ht + (>= until-burn-ht unlock-height) + true) + (err ERR_DELEGATION_EXPIRES_DURING_LOCK))) + + ;; delegate stacking does minimal-can-stack-stx + (try! (minimal-can-stack-stx pox-addr new-total-locked first-increase-cycle (+ u1 (- last-increase-cycle first-increase-cycle)))) + + ;; register the PoX address with the amount stacked via partial stacking + ;; before it can be included in the reward set, this must be committed! + (add-pox-partial-stacked pox-addr first-increase-cycle cycle-count increase-by) + + ;; stacking-state is unchanged, so no need to update + + ;; return the lock-up information, so the node can actually carry out the lock. + (ok { stacker: stacker, total-locked: new-total-locked})))) +``` + +
+ +Arguments: + +* Stacker: the STX address of the delegator. +* Pox Address: the BTC address of the pool operator where they will receive the BTC rewards. If the delegator has set his own BTC address in the `delegate-stx` call, this address will have to be the same one. +* Increase by: the amount of uSTX to add to the delegator's locked amount. + + + +## Stack the Increased Amount + +Once the locked amount is updated, the operator must commit the change. There are two functions that can be used to stack the increased amount: + +{% stepper %} +{% step %} +#### If the Commit Has Not Yet Been Made: stack-aggregation-commit-indexed + +This function stacks the total locked amount for an upcoming reward cycle. Note that the `stack-aggregation-commit-indexed` function wraps the `inner-stack-aggregation-commit` function. The wrapped inner function is included here. + +
+ +Function source code + +```clojure +;; Commit partially stacked STX and allocate a new PoX reward address slot. +;; This allows a stacker/delegate to lock fewer STX than the minimal threshold in multiple transactions, +;; so long as: 1. The pox-addr is the same. +;; 2. This "commit" transaction is called _before_ the PoX anchor block. +;; This ensures that each entry in the reward set returned to the stacks-node is greater than the threshold, +;; but does not require it be all locked up within a single transaction +;; +;; Returns (ok uint) on success, where the given uint is the reward address's index in the list of reward +;; addresses allocated in this reward cycle. This index can then be passed to `stack-aggregation-increase` +;; to later increment the STX this PoX address represents, in amounts less than the stacking minimum. +;; +;; *New in Stacks 2.1.* +(define-private (inner-stack-aggregation-commit (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (reward-cycle uint) + (signer-sig (optional (buff 65))) + (signer-key (buff 33)) + (max-amount uint) + (auth-id uint)) + (let ((partial-stacked + ;; fetch the partial commitments + (unwrap! (map-get? partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (err ERR_STACKING_NO_SUCH_PRINCIPAL)))) + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + (let ((amount-ustx (get stacked-amount partial-stacked))) + (try! (consume-signer-key-authorization pox-addr reward-cycle "agg-commit" u1 signer-sig signer-key amount-ustx max-amount auth-id)) + (try! (can-stack-stx pox-addr amount-ustx reward-cycle u1)) + ;; Add the pox addr to the reward cycle, and extract the index of the PoX address + ;; so the delegator can later use it to call stack-aggregation-increase. + (let ((add-pox-addr-info + (add-pox-addr-to-ith-reward-cycle + u0 + { pox-addr: pox-addr, + first-reward-cycle: reward-cycle, + num-cycles: u1, + reward-set-indexes: (list), + stacker: none, + signer: signer-key, + amount-ustx: amount-ustx, + i: u0 })) + (pox-addr-index (unwrap-panic + (element-at (get reward-set-indexes add-pox-addr-info) u0)))) + + ;; don't update the stacking-state map, + ;; because it _already has_ this stacker's state + ;; don't lock the STX, because the STX is already locked + ;; + ;; clear the partial-stacked state, and log it + (map-delete partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (map-set logged-partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle } partial-stacked) + (ok pox-addr-index))))) +``` + +
+ +Arguments: + +* Pox Address: the BTC address to receive rewards. +* Reward-cycle: a reward cycle in the future. +* Signer public key: the public key of your signer. +* Signer signature: a signature proving control of your signing key. +* Max Amount: used to validate the signer signature provided. +* Auth Id: used to validate the signer signature provided. +{% endstep %} + +{% step %} +#### If the Commit Has Already Been Made: stack-aggregation-increase + +If you have previously committed an amount, you can further increase the stacked position by calling `stack-aggregation-increase`. This function adds an additional amount of STX to the already committed delegation. + +
+ +Function source code + +```clojure +;; Commit partially stacked STX to a PoX address which has already received some STX (more than the Stacking min). +;; This allows a delegator to lock up marginally more STX from new delegates, even if they collectively do not +;; exceed the Stacking minimum, so long as the target PoX address already represents at least as many STX as the +;; Stacking minimum. +;; +;; The `reward-cycle-index` is emitted as a contract event from `stack-aggregation-commit` when the initial STX are +;; locked up by this delegator. It must be passed here to add more STX behind this PoX address. If the delegator +;; called `stack-aggregation-commit` multiple times for the same PoX address, then any such `reward-cycle-index` will +;; work here. +;; +;; *New in Stacks 2.1* +;; +(define-public (stack-aggregation-increase (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (reward-cycle uint) + (reward-cycle-index uint)) + (let ((partial-stacked + ;; fetch the partial commitments + (unwrap! (map-get? partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (err ERR_STACKING_NO_SUCH_PRINCIPAL)))) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; reward-cycle must be in the future + (asserts! (> reward-cycle (current-pox-reward-cycle)) + (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + (let ((amount-ustx (get stacked-amount partial-stacked)) + ;; reward-cycle must point to an existing record in reward-cycle-total-stacked + ;; infallible; getting something from partial-stacked-by-cycle succeeded so this must succeed + (existing-total (unwrap-panic (map-get? reward-cycle-total-stacked { reward-cycle: reward-cycle }))) + ;; reward-cycle and reward-cycle-index must point to an existing record in reward-cycle-pox-address-list + (existing-entry (unwrap! (map-get? reward-cycle-pox-address-list { reward-cycle: reward-cycle, index: reward-cycle-index }) + (err ERR_DELEGATION_NO_REWARD_SLOT))) + (increased-ustx (+ (get total-ustx existing-entry) amount-ustx)) + (total-ustx (+ (get total-ustx existing-total) amount-ustx))) + + ;; must be stackable + (try! (minimal-can-stack-stx pox-addr total-ustx reward-cycle u1)) + + ;; new total must exceed the stacking minimum + (asserts! (<= (get-stacking-minimum) total-ustx) + (err ERR_STACKING_THRESHOLD_NOT_MET)) + + ;; there must *not* be a stacker entry (since this is a delegator) + (asserts! (is-none (get stacker existing-entry)) + (err ERR_DELEGATION_WRONG_REWARD_SLOT)) + + ;; the given PoX address must match the one on record + (asserts! (is-eq pox-addr (get pox-addr existing-entry)) + (err ERR_DELEGATION_WRONG_REWARD_SLOT)) + + ;; update the pox-address list -- bump the total-ustx + (map-set reward-cycle-pox-address-list + { reward-cycle: reward-cycle, index: reward-cycle-index } + { pox-addr: pox-addr, + total-ustx: increased-ustx, + stacker: none, + ;; TODO: this must be authorized with a signature, or tx-sender allowance! + signer: (get signer existing-entry) }) + + ;; update the total ustx in this cycle + (map-set reward-cycle-total-stacked + { reward-cycle: reward-cycle } + { total-ustx: total-ustx }) + + ;; don't update the stacking-state map, + ;; because it _already has_ this stacker's state + ;; don't lock the STX, because the STX is already locked + ;; + ;; clear the partial-stacked state, and log it + (map-delete partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (map-set logged-partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle } partial-stacked) + (ok true)))) +``` + +
+ +Arguments: + +* Pox Address: the BTC address to receive rewards. +* Reward Cycle: a reward cycle in the future. +* Reward Cycle Index: the index returned by `stack-aggregation-commit-indexed`. +* Signer signature: a signature proving control of your signing key. +* Signer public key: the public key of your signer. +* Max Amount: used to validate the signer signature provided. +* Auth Id: used to validate the signer signature provided. +{% endstep %} +{% endstepper %} + +{% hint style="warning" %} +* Sequential Process: First call `delegate-stack-increase` to update the locked amount of a delegation. Then, commit the change: + * Use `stack-aggregation-commit-indexed` if this is the first commit in the given cycle. + * Use `stack-aggregation-increase` if you have already committed in the cycle you want to increase. + +Failing to commit (or properly increase after a commit) will result in the increased delegation not taking effect in upcoming stacking cycles. +{% endhint %} diff --git a/docs/operate/stacking-stx/operate-a-stacking-pool.md b/docs/operate/stacking-stx/operate-a-stacking-pool.md new file mode 100644 index 0000000000..8633a40328 --- /dev/null +++ b/docs/operate/stacking-stx/operate-a-stacking-pool.md @@ -0,0 +1,589 @@ +# Operate a Stacking Pool + +This doc assumes you are familiar with stacking at a conceptual level. If not, you may want to read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) concept guide. + +The guide below applies to those who want to operate a pool, meaning they want to have stackers delegate STX tokens to them. If you choose to operate a pool you will either need to run your own signer or collaborate with one. + +### Pool Operator Stacking Flow + +For pool operators, the flow is a bit different than solo stacking. Remember that as a pool operator, other stackers are delegating their STX to you to stack on behalf of them. This additional role adds a couple of extra steps to your stacking flow if operating as a pool operator. + +Similar to the changes to solo Stacking, the big difference for delegation flows is the inclusion of signer keys and signatures. Because pool operators need to make transactions to “finalize” a delegation, these new arguments add new complexities to the stacking flow. + +#### Delegator initiates delegation + +{% hint style="info" %} +This step is not required to apply to pool operators/signers. It is included here to illustrate the end-to-end flow, but if you are operating as a pool operator/signer you will not have to perform this step. Instead, users delegate their STX to you as the pool operator. +{% endhint %} + +The first step, where the delegator sets up their delegation to a pool operator, is to call `delegate-stx`. This function does not directly delegate the STX, but rather allows the pool operator to issue the stacking lock on behalf of the user calling this function. You can think of calling this function as the delegator giving permission to the pool operator to stack on their behalf. + +
+ +Function source code + +```clojure +;; Delegate to `delegate-to` the ability to stack from a given address. +;; This method _does not_ lock the funds, rather, it allows the delegate +;; to issue the stacking lock. +;; The caller specifies: +;; * amount-ustx: the total amount of ustx the delegate may be allowed to lock +;; * until-burn-ht: an optional burn height at which this delegation expires +;; * pox-addr: an optional address to which any rewards *must* be sent +(define-public (delegate-stx (amount-ustx uint) + (delegate-to principal) + (until-burn-ht (optional uint)) + (pox-addr (optional { version: (buff 1), hashbytes: (buff 32) }))) + + (begin + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; delegate-stx no longer requires the delegator to not currently + ;; be stacking. + ;; delegate-stack-* functions assert that + ;; 1. users can't swim in two pools at the same time. + ;; 2. users can't switch pools without cool down cycle. + ;; Other pool admins can't increase or extend. + ;; 3. users can't join a pool while already directly stacking. + + ;; pox-addr, if given, must be valid + (match pox-addr + address + (asserts! (check-pox-addr-version (get version address)) + (err ERR_STACKING_INVALID_POX_ADDRESS)) + true) + + ;; tx-sender must not be delegating + (asserts! (is-none (get-check-delegation tx-sender)) + (err ERR_STACKING_ALREADY_DELEGATED)) + + ;; add delegation record + (map-set delegation-state + { stacker: tx-sender } + { amount-ustx: amount-ustx, + delegated-to: delegate-to, + until-burn-ht: until-burn-ht, + pox-addr: pox-addr }) + + (ok true))) +``` + +
+ +The arguments here are unchanged from previous versions of PoX: + +* Amount: Denoted in uSTX (1 STX = 1,000,000 uSTX) +* Delegate to: the STX address of the pool operator they're delegating to. Note that this is different from the “signer key” used. Instead, this is the STX address that is used to make PoX transactions. +* Until burn height: an optional argument representing the BTC block height when the delegation expires. If none is used, the delegation permission expires only when explicitly revoked. +* Pox Address: an optional BTC address that, if specified, the signer must use to accept this delegation + +#### Pool operator “activates” the delegation + +Just as in the older PoX contract, after a delegator calls the `delegate-stx` function, the pool operator calls `delegate-stack-stx` to commit the delegator’s STX. + +
+ +Function source code + +```clojure +;; As a delegate, stack the given principal's STX using partial-stacked-by-cycle +;; Once the delegate has stacked > minimum, the delegate should call stack-aggregation-commit +(define-public (delegate-stack-stx (stacker principal) + (amount-ustx uint) + (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (start-burn-ht uint) + (lock-period uint)) + ;; this stacker's first reward cycle is the _next_ reward cycle + (let ((first-reward-cycle (+ u1 (current-pox-reward-cycle))) + (specified-reward-cycle (+ u1 (burn-height-to-reward-cycle start-burn-ht))) + (unlock-burn-height (reward-cycle-to-burn-height (+ (current-pox-reward-cycle) u1 lock-period)))) + ;; the start-burn-ht must result in the next reward cycle, do not allow stackers + ;; to "post-date" their `stack-stx` transaction + (asserts! (is-eq first-reward-cycle specified-reward-cycle) + (err ERR_INVALID_START_BURN_HEIGHT)) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; stacker must have delegated to the caller + (let ((delegation-info (unwrap! (get-check-delegation stacker) (err ERR_STACKING_PERMISSION_DENIED)))) + ;; must have delegated to tx-sender + (asserts! (is-eq (get delegated-to delegation-info) tx-sender) + (err ERR_STACKING_PERMISSION_DENIED)) + ;; must have delegated enough stx + (asserts! (>= (get amount-ustx delegation-info) amount-ustx) + (err ERR_DELEGATION_TOO_MUCH_LOCKED)) + ;; if pox-addr is set, must be equal to pox-addr + (asserts! (match (get pox-addr delegation-info) + specified-pox-addr (is-eq pox-addr specified-pox-addr) + true) + (err ERR_DELEGATION_POX_ADDR_REQUIRED)) + ;; delegation must not expire before lock period + (asserts! (match (get until-burn-ht delegation-info) + until-burn-ht (>= until-burn-ht + unlock-burn-height) + true) + (err ERR_DELEGATION_EXPIRES_DURING_LOCK)) + ) + + ;; stacker principal must not be stacking + (asserts! (is-none (get-stacker-info stacker)) + (err ERR_STACKING_ALREADY_STACKED)) + + ;; the Stacker must have sufficient unlocked funds + (asserts! (>= (stx-get-balance stacker) amount-ustx) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + + ;; ensure that stacking can be performed + (try! (minimal-can-stack-stx pox-addr amount-ustx first-reward-cycle lock-period)) + + ;; register the PoX address with the amount stacked via partial stacking + ;; before it can be included in the reward set, this must be committed! + (add-pox-partial-stacked pox-addr first-reward-cycle lock-period amount-ustx) + + ;; add stacker record + (map-set stacking-state + { stacker: stacker } + { pox-addr: pox-addr, + first-reward-cycle: first-reward-cycle, + reward-set-indexes: (list), + lock-period: lock-period, + delegated-to: (some tx-sender) }) + + ;; return the lock-up information, so the node can actually carry out the lock. + (ok { stacker: stacker, + lock-amount: amount-ustx, + unlock-burn-height: unlock-burn-height }))) +``` + +
+ +The arguments are: + +* Stacker: the STX address of the delegator +* Amount: denoted in uSTX (1 STX = 1,000,000 uSTX) +* Pox Address: The BTC address of the pool operator where they will receive the BTC rewards. If the delegator has set his own BTC address in the `delegate-stx` call, this address will have to be the same one, otherwise the contract call will fail. +* Start burn height: The BTC block height in which delegation can begin. This field is used to ensure that an old transaction intended for an earlier cycle will fail, and also prevents callers from "post-dating" the call to a future cycle. The best option here is to add 1 or 2 to the current BTC block height when you initiate this transaction. Note that the delegation will not actively be stacked at this block height, but whatever reward cycle is passed in the aggregation commit function (explained below). +* Lock period: number of cycles to lock for. If the delegator provided the until burn height argument, then the end of these cycles cannot be past the expiration provided. The maximum lock period that a pool operator can provide in this function call is 12. + +This step also allows the pool operator to proactively choose which Stackers they’ll accept delegation from. For “closed” pools, the pool operator will only call this function for approved Stackers. It is up to each pool operator who runs a closed pool to implement this process. + +This step can be repeated for multiple Stackers before going to the next step. + +{% hint style="info" %} +If you look at the function source code, you'll see that the `delegate-stack-stx` function sets the stacker's first reward cycle to be the _next_ reward cycle. + +When generating your signature and your `stack-aggregation-commit-indexed` transaction, you'll want to make sure that the reward cycles match. + +So if you are in cycle 557 when you call the `delegate-stack-stx` function, you'll want to pass in cycle 558 or higher when you generate your signature and your `stack-aggregation-commit-indexed` transaction. + +With `stack-aggregation-commit-indexed`, the `reward-cycle` arg is saying "I'm committing these stacks to be stacked in cycle N". But the `delegate-stack-stx` transaction gets you setup for next cycles, aka 558 and higher. + +Also make sure that, when you generate your signature, you use 558 or higher as the reward cycle. In solo stacking methods, you use the current reward cycle in the signature, but not for `stack-aggregation-commit-indexed`. This is because with `stack-aggregation-commit-indexed` you can commit stacks for future cycles, not just the N+1 cycle. +{% endhint %} + +#### Pool operator “commits” delegated STX + +The next step is for the pool operator to call `stack-aggregation-commit-indexed`. + +{% hint style="info" %} +In the contract source code, you'll notice a similarly named function called `stack-aggregation-commit`. This is a legacy function that makes it difficult to increase the stacking amount, as it does not return the reward index of the stacking slot, which is required in order to call the `stack-aggregation-increase` function. We recommend using `stack-aggregation-commit-indexed`. +{% endhint %} + +
+ +Function source code + +Note that the `stack-aggregation-commit-indexed` function wraps the `inner-stack-aggregation-commit` function. The wrapped inner function is included here. + +Check out the [deployed contract](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) to see the flow of contract calls. + +```clojure +;; Commit partially stacked STX and allocate a new PoX reward address slot. +;; This allows a stacker/delegate to lock fewer STX than the minimal threshold in multiple transactions, +;; so long as: 1. The pox-addr is the same. +;; 2. This "commit" transaction is called _before_ the PoX anchor block. +;; This ensures that each entry in the reward set returned to the stacks-node is greater than the threshold, +;; but does not require it be all locked up within a single transaction +;; +;; Returns (ok uint) on success, where the given uint is the reward address's index in the list of reward +;; addresses allocated in this reward cycle. This index can then be passed to `stack-aggregation-increase` +;; to later increment the STX this PoX address represents, in amounts less than the stacking minimum. +;; +;; *New in Stacks 2.1.* +(define-private (inner-stack-aggregation-commit (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (reward-cycle uint) + (signer-sig (optional (buff 65))) + (signer-key (buff 33)) + (max-amount uint) + (auth-id uint)) + (let ((partial-stacked + ;; fetch the partial commitments + (unwrap! (map-get? partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (err ERR_STACKING_NO_SUCH_PRINCIPAL)))) + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + (let ((amount-ustx (get stacked-amount partial-stacked))) + (try! (consume-signer-key-authorization pox-addr reward-cycle "agg-commit" u1 signer-sig signer-key amount-ustx max-amount auth-id)) + (try! (can-stack-stx pox-addr amount-ustx reward-cycle u1)) + ;; Add the pox addr to the reward cycle, and extract the index of the PoX address + ;; so the delegator can later use it to call stack-aggregation-increase. + (let ((add-pox-addr-info + (add-pox-addr-to-ith-reward-cycle + u0 + { pox-addr: pox-addr, + first-reward-cycle: reward-cycle, + num-cycles: u1, + reward-set-indexes: (list), + stacker: none, + signer: signer-key, + amount-ustx: amount-ustx, + i: u0 })) + (pox-addr-index (unwrap-panic + (element-at (get reward-set-indexes add-pox-addr-info) u0)))) + + ;; don't update the stacking-state map, + ;; because it _already has_ this stacker's state + ;; don't lock the STX, because the STX is already locked + ;; + ;; clear the partial-stacked state, and log it + (map-delete partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (map-set logged-partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle } partial-stacked) + (ok pox-addr-index))))) +``` + +
+ +At this point, the STX are committed to the pool operator, and the pool operator has some “aggregate balance” of committed STX. The pool operator is not actually eligible for reward slots and signer initialization until this step is finished. + +The pool operator cannot call this function until the total number of STX committed is larger than the minimum threshold required to Stack. This minimum stacking threshold is a function of the total number of STX stacked divided by the available number of reward slots. + +{% hint style="info" %} +This number varies and can be found by visiting the pox endpoint of Hiro's API at [https://api.hiro.so/v2/pox](https://api.hiro.so/v2/pox) and looking at the `min_threshold_ustx` field. (1 STX = 1,000,000 uSTX). +{% endhint %} + +Once the threshold is reached, the pool operator calls `stack-aggregation-commit-indexed`. This is the point where you as the pool operator must provide your signer key and signer key signature. The arguments are: + +* Pox Address: the BTC address to receive rewards +* Reward-cycle: a reward cycle in the future (see the note above on passing the correct reward cycle) +* Signer public key: the public key of your signer (remember that this may be different than the address you are using to operate the pool, but this step is how you associate the two together) +* Signer signature: A signature proving control of your signing key (details on how to do this are below) +* Max Amount: This parameter is used to validate the signer signature provided. It represents the maximum number of uSTX (1 STX = 1,000,000 uSTX) that can be stacked in this transaction. +* Auth Id: This parameter is used to validate the signer signature provided. It is a random integer that prevents the re-use of this particular signer signature. + +{% hint style="info" %} +In the Definitions and Roles section in the previous document, we described how the pool operator and signer may be the same entity, but not necessarily have the same address. + +Signers who are also pool operators and wish to have STX delegated to them should have a separate keychain associated with their pool operator account in order to make Stacking transactions such as `delegate-stack-stx` and `stack-aggregation-commit-indexed`. + +So, as a signing entity operating a pool, you should have two accounts: + +* Your pool operator account, which you will use to conduct all of the stacking operations we have covered here. +* Your signer account, which is what you used to set up your signer. This signer public key is what you will pass in to the above aggregation commit function, and is also the key you will use when generating your signer signature. + +If you are operating as a signer and a pool operator, you should have a separate key because you might need to rotate your signer key when necessary. + +The PoX contract is designed to support rotating the signer key without needing your Stackers to un-stack and re-stack later. You cannot rotate a pool operator key without needing to wait for all delegated Stackers to un-stack and finally re-stack to the new pool operator address. + +Because of this limitation of being unable to rotate pool operator addresses, it’s highly recommended to have a separate pool operator key. The benefit of a separate pool operator key is that it can easily be used in existing wallets, including hardware wallets. +{% endhint %} + +Once this succeeds, the signer is eligible for reward slots. The number of reward slots depends on the amount of STX committed to this signer. Even if the signer commits more than the “global minimum”, the minimum amount to receive a slot depends on the amount of STX locked for each cycle. + +To act as a signer, each step up to this point must be taken before the prepare phase of the next cycle begins. It is crucial that the signer software is running. + +#### Pool operator increases amount committed + +Even after the signer commits to a certain amount of STX in the previous step, the signer can increase this amount once more delegations are received. The initial steps must be taken for each Stacker (`delegate-stx` and then `delegate-stack-stx`), and then `stack-aggregation-increase` can be called with the index returned from the first `stack-aggregation-commit-indexed` call and a new signature. + +
+ +Function source code + +``` +;; Commit partially stacked STX to a PoX address which has already received some STX (more than the Stacking min). +;; This allows a delegator to lock up marginally more STX from new delegates, even if they collectively do not +;; exceed the Stacking minimum, so long as the target PoX address already represents at least as many STX as the +;; Stacking minimum. +;; +;; The `reward-cycle-index` is emitted as a contract event from `stack-aggregation-commit` when the initial STX are +;; locked up by this delegator. It must be passed here to add more STX behind this PoX address. If the delegator +;; called `stack-aggregation-commit` multiple times for the same PoX address, then any such `reward-cycle-index` will +;; work here. +;; +;; *New in Stacks 2.1* +;; +(define-public (stack-aggregation-increase (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (reward-cycle uint) + (reward-cycle-index uint)) + (let ((partial-stacked + ;; fetch the partial commitments + (unwrap! (map-get? partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (err ERR_STACKING_NO_SUCH_PRINCIPAL)))) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; reward-cycle must be in the future + (asserts! (> reward-cycle (current-pox-reward-cycle)) + (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + (let ((amount-ustx (get stacked-amount partial-stacked)) + ;; reward-cycle must point to an existing record in reward-cycle-total-stacked + ;; infallible; getting something from partial-stacked-by-cycle succeeded so this must succeed + (existing-total (unwrap-panic (map-get? reward-cycle-total-stacked { reward-cycle: reward-cycle }))) + ;; reward-cycle and reward-cycle-index must point to an existing record in reward-cycle-pox-address-list + (existing-entry (unwrap! (map-get? reward-cycle-pox-address-list { reward-cycle: reward-cycle, index: reward-cycle-index }) + (err ERR_DELEGATION_NO_REWARD_SLOT))) + (increased-ustx (+ (get total-ustx existing-entry) amount-ustx)) + (total-ustx (+ (get total-ustx existing-total) amount-ustx))) + + ;; must be stackable + (try! (minimal-can-stack-stx pox-addr total-ustx reward-cycle u1)) + + ;; new total must exceed the stacking minimum + (asserts! (<= (get-stacking-minimum) total-ustx) + (err ERR_STACKING_THRESHOLD_NOT_MET)) + + ;; there must *not* be a stacker entry (since this is a delegator) + (asserts! (is-none (get stacker existing-entry)) + (err ERR_DELEGATION_WRONG_REWARD_SLOT)) + + ;; the given PoX address must match the one on record + (asserts! (is-eq pox-addr (get pox-addr existing-entry)) + (err ERR_DELEGATION_WRONG_REWARD_SLOT)) + + ;; update the pox-address list -- bump the total-ustx + (map-set reward-cycle-pox-address-list + { reward-cycle: reward-cycle, index: reward-cycle-index } + { pox-addr: pox-addr, + total-ustx: increased-ustx, + stacker: none, + ;; TODO: this must be authorized with a signature, or tx-sender allowance! + signer: (get signer existing-entry) }) + + ;; update the total ustx in this cycle + (map-set reward-cycle-total-stacked + { reward-cycle: reward-cycle } + { total-ustx: total-ustx }) + + ;; don't update the stacking-state map, + ;; because it _already has_ this stacker's state + ;; don't lock the STX, because the STX is already locked + ;; + ;; clear the partial-stacked state, and log it + (map-delete partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (map-set logged-partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle } partial-stacked) + (ok true)))) +``` + +
+ +*** + +## Step by Step Stacking Guide + +Now that you are familiar with the overall stacking flow and the different roles played, let's dive into the step-by-step guide for actually conducting the stacking process as a pool operator. + +{% hint style="info" %} +There are several ways you can go about stacking. This guide will cover using [Leather Earn](https://earn.leather.io/), which is a stacking web application and the simplest option. + +Additionally, you can choose to call the stacking functions directly from the [deployed contract](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) in the explorer. + +The fields and process will be the same, but the UI will differ. + +Finally, you can stack using JS and the [@stacks/stacking](https://github.com/hirosystems/stacks.js/tree/main/packages/stacking) package if you prefer. Again, the functions and parameters will be the same, you will just be writing your JS code directly instead of using a UI. + +If you are interested in using this method, you'll want to follow the [stacking guide](https://docs.hiro.so/stacks.js/guides/how-to-integrate-stacking) created by Hiro, and be sure to integrate the new parameters described in [Hiro's Nakamoto update doc](https://docs.hiro.so/nakamoto/stacks-js). +{% endhint %} + +### Step 1: Run or work with a signer + +This is a necessary prerequisite to stacking as a pool operator. You will either need to run your own signer or work with one and have them conduct step 2 on your behalf and give you their signer signature. + +Running a signer involves setting up a hosted production environment that includes both a Stacks Node and the Stacks Signer. For more information, refer to the [running a signer doc](../run-a-signer/signer-quickstart.md). + +Once the signer software is running, you'll to keep track of the `stacks_private_key` that you used when configuring your signer software. This will be used in subsequent Stacking transactions. + +{% hint style="info" %} +In the note above about pool operator vs signer keys, this corresponds to your signer key, not your pool operator wallet +{% endhint %} + +### Step 2: Generate a signer key signature + +#### Overview of signer keys and signatures + +The main difference with Stacking in Nakamoto is that the Signer needs to include new parameters in their Stacking transactions. These are Clarity transactions that pool operators will call when interacting with the `pox-4.clar` contract. Interacting with that contract is how you as a pool operator actually stack the delegated STX tokens. + +{% hint style="info" %} +The current pox-4 contract address can be found by visiting the following endpoint of the Hiro API: [https://api.hiro.so/v2/pox](https://api.hiro.so/v2/pox). + +You can then visit the [Stacks Explorer](https://explorer.hiro.so/) to view the contract and pass in the contract id. + +You may want to review this contract to familiarize yourself with it. +{% endhint %} + +Here is an overview of the new fields you will need to pass in some of your stacking transactions: + +1. `signer-key`: the public key that corresponds to the `stacks_private_key` your signer is using. +2. `signer-signature`: a signature that demonstrates that you actually control your `signer-key`. Because signer keys need to be unique, this is also a safety check to ensure that other Stackers can’t use someone else’s signer key. +3. `max-amount`: The maximum amount of uSTX (1 STX = 1,000,000 uSTX) that can be locked in the transaction that uses this signature. For example, if calling `stack-aggregation-increase`, then this parameter dictates the maximum amount of uSTX that can be used to add more locked STX to the already committed position. +4. `auth-id`: a random integer that prevents re-use of a particular signature, similar to how nonces are used with transactions. + +Signer signatures are signatures created using a particular signer key. They demonstrate that the controller of that signer key is allowing a Stacker to use their signer's public key. The signer signature’s message hash is created using the following data: + +* `method`: a string that indicates the Stacking method that is allowed to utilize this signature. The valid options are `stack-stx`, `stack-extend`, `stack-increase`, `agg-commit` (for `stack-aggregation-commit-indexed`) and `agg-increase` (for `stack-aggregation-increase`). +* `max-amount`: described above. +* `auth-id`: described above. +* `period`: a value between 1 and 12, which indicates how long the Stacker is allowed to lock their STX for in this particular Stacking transaction. For `agg-commit`, this must be equal to 1. +* `reward-cycle`: This represents the reward cycle in which the Stacking transaction can be confirmed (for `stack-aggregation-commit-indexed`, this has to be set to 1). +* `pox-address`: The Bitcoin address that is allowed to be used for receiving rewards. This corresponds to the Bitcoin address associated with your signer +* `config`: This represents the signer configuration file path where the `stacks_private_key` is located, and it is used for signing the generated signature. + +Now that we have an overview of role and contents of signatures, let's see how to actually generate them. You have several options available. + +Generating your signature with Degen Lab's stacks.tools + +* Degen Lab has a signature generation tool that will generate a signature using their signer. This is the quickest and simplest option. To generate a signature using this method, all you need to do is visit their [signature tool](https://signature.stacking.tools/) and pass in the relevant information as covered on the page. + +Generating your signature with stacks.js + +* The [@stacks/stacking](https://www.npmjs.com/package/@stacks/stacking) NPM package provides interfaces to generate and use signer signatures. +* There is a function called `signPoxSignature` that will allow you to generate this signature and pass it in to the stacking function. +* More information and code samples can be found on [Hiro's Nakamoto docs](https://docs.hiro.so/nakamoto/stacks-js). + +Generating your signature using the stacks-signer CLI + +* If you already have your signer configured and set up, you can use the `stacks-signer` CLI to generate this signature. You can either SSH into your running signer or use the `stacks-signer` CLI locally. If using the CLI locally, you will need to ensure you have a matching configuration file located on your filesystem. Having a matching configuration file is important to ensure that the signer public key you make in Stacking transactions is the same as in your hosted signer. + +The CLI command is: + +``` +``` + +These arguments match those described in section [Overview of signer keys and signatures](solo-stack.md#overview-of-signer-keys-and-signatures), with the addition of: + +* `--json`, to optionally output the resulting signature in JSON. + +You can use the following command to generate a random `32` bit integer as `auth-id`: + +``` +``` + +Once the `generate-stacking-signature` command is run, the CLI will output a JSON: + +```json +{"authId":"12345","maxAmount":"1234","method":"agg-commit","period":1,"poxAddress":"bc1...","rewardCycle":100,"signerKey":"aaaaaaaa","signerSignature":"bbbbbbbbbbb"} +``` + +You will use the JSON when calling Stacking transactions from your pool operator address as outlined above. Remember that this may be different than your signer address. + +Generating your signature with Leather Earn + +* Leather Earn is a web application that provides an easy-to-use interface for stacking and generating signatures. We'll cover using Leather Earn for stacking at the end of this document, here we will cover how to use it to generate a signature. + +{% hint style="info" %} +At the time of writing, this has only been tested using the [Leather](https://leather.io/) wallet. +{% endhint %} + +You can visit [earn.leather.io](https://earn.leather.io/) to generate a signer key signature. Make sure you’re connected to the correct network.\ +To generate a signer key signature, it’s important that you’ve logged in Leather with the same secret key that was used to generate your signer key, not the account that will serve as your pool operator address. Once you’ve setup that account on Leather, you can log in to Leather Earn.\ +Click the link “Signer key signature” at the bottom of the page. This will open the “generate a signer key signature” page. + +The fields are: + +* Reward cycle: + * For all solo stacking transactions, this must equal the current reward cycle, not the cycle in which they will start stacking. The field defaults to the current reward cycle. + * For stack-aggregation-commit-indexed, this field must equal the cycle used in that function’s “reward cycle” argument. Typically, that equates to current\_cycle + 1. +* Bitcoin address: the PoX reward address that can be used +* Topic: the stacking function that will use this signature +* Max amount: max amount of STX that can be used. Defaults to “max possible amount” +* Auth ID: defaults to random int +* Duration: must match the number of cycles used in the stacking transaction. For `stack-aggregation-commit-indexed`, use “1”. + +{% hint style="warning" %} +Each of these fields must be exactly matched in order for the Stacking transaction to work. Future updates to Leather Earn will verify the signature before the transaction is made. +{% endhint %} + +Click the “generate signature” button to popup a Leather page where you can generate the signature. Once you submit that popup, Leather Earn will have the signer key and signature you generated. + +After you sign that message, you'll see the information you can use in your Stacking transactions, including the signer public key and signature. + +You can click the “copy” icon next to “signer details to share with stackers”. This will copy a JSON string, which can be directly pasted into the Leather Earn page where you make your Stacking transaction. Alternatively, this information can be entered manually. + +We'll cover the Leather Earn pages for actually making those transactions in the next section of this document. + +#### Using a hardware or software wallet to generate signatures + +When the signer is configured with a `stacks_private_key`, the signer may want to be able to use that key in a wallet to make stacking signatures. + +If the signer uses a tool like [@stacks/cli](https://docs.hiro.so/get-started/command-line-interface) to generate the key, the CLI also outputs a mnemonic (aka “seed phrase”) that can be imported into a wallet. Because the Stacks CLI uses the standard derivation path for generating Stacks keys, any Stacks wallet will default to having that same private key when the wallet is imported from a derivation path. Similarly, if a hardware wallet is setup with that mnemonic, then the Signer can use a wallet like Leather to make stacking signatures. + +Use the following stepper for the recommended workflow for setting up a wallet to generate signatures: + +{% stepper %} +{% step %} +### Generate the keypair and configure signer + +1. Use @stacks/cli to generate the keychain and private key. + * Typically, when using a hardware wallet, it’s better to generate the mnemonic on the hardware wallet. For this use case, however, the signer software needs the private key, and hardware wallets (by design) don’t allow exporting private keys. +2. Take the `privateKey` from the CLI output and add it to your signer’s configuration. +3. Take the mnemonic (24 words) and either: + * Setup a new hardware wallet with this mnemonic + * Store it somewhere securely, like a password manager. When the signer needs to generate signatures for Stacking transactions, they can import it into either Leather or XVerse. +{% endstep %} + +{% step %} +### When you need to generate signatures + +1. Setup your wallet with your signer key’s private key. Either: + * Setup your Leather wallet with a Ledger hardware wallet + * Import your mnemonic into Leather, XVerse, or another Stacks wallet +2. Open an app that has stacking signature functionality built-in +3. Connect your wallet to the app (aka sign in) +4. In the app, enter your PoX address and “submit” + * The app will popup a window in your wallet that prompts you to sign the information + * The app will show clear information about what you’re signing +5. Create the signature + * If using a Ledger, confirm on your device +6. The app will display two results: + * Your signer key, which is the public key associated with your signer’s key + * Your signer signature +7. Finally, make a Stacking transaction using the signer key and signer signature. +{% endstep %} +{% endstepper %} + +Now that you have your signer signature generated, it's time to start stacking. This process will vary depending on your chosen method. We've included instructions for pool stacking using [Leather Earn](https://earn.leather.io/) below. + +### Step 3: Stack as a pool operator + +The first step with delegated stacking involves a stacker delegating their Stacks to a specific pool operator. Stackers can do this by visiting the “Stack in a pool” page on Leather Earn. + +As the pool operator, you must provide a STX address (a “pool admin address”) that will manage delegations. As discussed in previous sections, this is a separate address from the signer’s private key, and this can be any address. This address is what will be used when making transactions to confirm and aggregate delegated STX. + +Pool operators can log in to Leather Earn and visit [https://earn.leather.io/pool-admin](https://earn.leather.io/pool-admin) to make pool management transactions. + +#### delegate-stack-stx + +Once a user has delegated to a pool operator, the pool operator must call `delegate-stack-stx` for each individual stacker. + +#### stack-aggregation-commit + +Once a pool has enough delegated STX to become a signer, the pool admin needs to visit the `Stack Aggregation Commit` page on Leather Earn. The pool operator enters the following information: + +* Reward cycle: the reward cycle where the operator is “committing” delegated STX. This must be done for every individual reward cycle where the pool will be acting as a signer. +* BTC address +* New fields: + * Signer public key + * Signer key signature (generated in a previous step using the signer key) + * Auth ID + * Max amount + +Once this transaction has been confirmed, the pool operator is eligible to be a signer for an upcoming reward cycle. + +For more on the relationship between automated signing and manual stacking transactions, be sure to check out the main [Stack STX](file:///) doc. diff --git a/docs/operate/stacking-stx/solo-stack.md b/docs/operate/stacking-stx/solo-stack.md new file mode 100644 index 0000000000..f98bb796f3 --- /dev/null +++ b/docs/operate/stacking-stx/solo-stack.md @@ -0,0 +1,614 @@ +# Solo Stack + +This doc assumes you are familiar with stacking at a conceptual level. If not, you may want to read the [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) concept guide. + +The guide below applies to those who want to solo stack, meaning they meet the minimum stacking requirement and need to either run a signer or collaborate with a signer. + +{% hint style="info" %} +There is a dapp created by Degen Lab for [solo stacking](https://solo.stacking.tools/) without needing a signer, as Degen Lab operates a signer. This is likely the easiest option for solo stacking. We'll cover this option below. +{% endhint %} + +If you prefer to participate in a pool by delegating your STX, you do not need to operate a signer either. If you fall into this category, check out the [Stack with a Pool](stack-with-a-pool.md) guide. + +### Solo Stacker Flow + +{% hint style="info" %} +Note that in order to solo stack, you need to have the minimum number of STX tokens. This number can be found by visiting the pox endpoint of Hiro's API at [https://api.mainnet.hiro.so/v2/pox](https://api.mainnet.hiro.so/v2/pox) and looking at the `min_threshold_ustx` field. (1 STX = 1,000,000 uSTX) +{% endhint %} + +#### Call the function `stack-stx` + +
+ +Function source code + +```clojure +;; Lock up some uSTX for stacking! Note that the given amount here is in micro-STX (uSTX). +;; The STX will be locked for the given number of reward cycles (lock-period). +;; This is the self-service interface. tx-sender will be the Stacker. +;; +;; * The given stacker cannot currently be stacking. +;; * You will need the minimum uSTX threshold. This will be determined by (get-stacking-minimum) +;; at the time this method is called. +;; * You may need to increase the amount of uSTX locked up later, since the minimum uSTX threshold +;; may increase between reward cycles. +;; * You need to provide a signer key to be used in the signer DKG process. +;; * The Stacker will receive rewards in the reward cycle following `start-burn-ht`. +;; Importantly, `start-burn-ht` may not be further into the future than the next reward cycle, +;; and in most cases should be set to the current burn block height. +;; +;; To ensure that the Stacker is authorized to use the provided `signer-key`, the stacker +;; must provide either a signature have an authorization already saved. Refer to +;; `verify-signer-key-sig` for more information. +;; +;; The tokens will unlock and be returned to the Stacker (tx-sender) automatically. +(define-public (stack-stx (amount-ustx uint) + (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + (start-burn-ht uint) + (lock-period uint) + (signer-sig (optional (buff 65))) + (signer-key (buff 33)) + (max-amount uint) + (auth-id uint)) + ;; this stacker's first reward cycle is the _next_ reward cycle + (let ((first-reward-cycle (+ u1 (current-pox-reward-cycle))) + (specified-reward-cycle (+ u1 (burn-height-to-reward-cycle start-burn-ht)))) + ;; the start-burn-ht must result in the next reward cycle, do not allow stackers + ;; to "post-date" their `stack-stx` transaction + (asserts! (is-eq first-reward-cycle specified-reward-cycle) + (err ERR_INVALID_START_BURN_HEIGHT)) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; tx-sender principal must not be stacking + (asserts! (is-none (get-stacker-info tx-sender)) + (err ERR_STACKING_ALREADY_STACKED)) + + ;; tx-sender must not be delegating + (asserts! (is-none (get-check-delegation tx-sender)) + (err ERR_STACKING_ALREADY_DELEGATED)) + + ;; the Stacker must have sufficient unlocked funds + (asserts! (>= (stx-get-balance tx-sender) amount-ustx) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + + ;; Validate ownership of the given signer key + (try! (consume-signer-key-authorization pox-addr (- first-reward-cycle u1) "stack-stx" lock-period signer-sig signer-key amount-ustx max-amount auth-id)) + + ;; ensure that stacking can be performed + (try! (can-stack-stx pox-addr amount-ustx first-reward-cycle lock-period)) + + ;; register the PoX address with the amount stacked + (let ((reward-set-indexes (try! (add-pox-addr-to-reward-cycles pox-addr first-reward-cycle lock-period amount-ustx tx-sender signer-key)))) + ;; add stacker record + (map-set stacking-state + { stacker: tx-sender } + { pox-addr: pox-addr, + reward-set-indexes: reward-set-indexes, + first-reward-cycle: first-reward-cycle, + lock-period: lock-period, + delegated-to: none }) + + ;; return the lock-up information, so the node can actually carry out the lock. + (ok { stacker: tx-sender, lock-amount: amount-ustx, signer-key: signer-key, unlock-burn-height: (reward-cycle-to-burn-height (+ first-reward-cycle lock-period)) })))) +``` + +
+ +The first thing solo stackers will need to do is call the `stack-stx` function. + +Just like in previous versions of PoX, Stackers call `stack-stx`, but with the new arguments added in Nakamoto. The arguments are: + +* Amount: Denoted in uSTX (1 STX = 1,000,000 uSTX) +* PoX Address: the BTC wallet address where to receive Stacking rewards +* Start burn height: the current BTC block height +* Lock period: the number of cycles to lock for (between 1 and 12) +* Signer key: the public key that your signer is using +* Signer signature: the signature that proves control of this signer key +* Max Amount: This parameter is used to validate the signer signature provided. It represents the maximum number of uSTX that can be stacked in this transaction. +* Auth Id: This parameter is used to validate the signer signature provided. It is a random integer that prevents the re-use of this particular signer signature. + +Solo stackers have two choices when it comes to operating as a signer. They can choose to run a signer themselves or work with a signer on their behalf. + +#### Option 1: Act as a signer + +In the “prepare phase” before the next stacking cycle (last 100 blocks), the exact set of Stackers will be selected based on the amount of STX stacked. Just like in previous versions of PoX, each Stacker will have some number of reward slots based on the amount of STX locked. + +It is critical that solo stackers have their signer running during this period. The prepare phase is when distributed key generation (DKG) occurs, which is used when validating blocks by signers. + +In general, you don’t need to do anything actively during this period, other than monitoring your signer software to ensure it’s running properly. + +#### Option 2: Work with a signer + +If you do not want to operate a signer on your own, you can work with another signer. To do this, you will need to collaborate with them to get their signer key and signer signature (details in the following sections), which will have to be passed when calling the stacking functions. + +Rather than needing to find a signer to collaborate with, you can use the solo stacking dapp created by Degen Lab in order to use their signer to solo stack. They've created a UI that makes this process really simple. + +They also have a tool for you to generate a signer signature if you prefer to call the stacking functions yourself. + +#### Extending the stacking period + +Just like in the current version of PoX, you can extend your lock period while still Stacking. The function called is `stack-extend`. + +
+ +Function source code + +```clojure +;; Extend an active Stacking lock. +;; *New in Stacks 2.1* +;; This method extends the `tx-sender`'s current lockup for an additional `extend-count` +;; and associates `pox-addr` with the rewards, The `signer-key` will be the key +;; used for signing. The `tx-sender` can thus decide to change the key when extending. +;; +;; Because no additional STX are locked in this function, the `amount` field used +;; to verify the signer key authorization is zero. Refer to `verify-signer-key-sig` for more information. +(define-public (stack-extend (extend-count uint) + (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (signer-sig (optional (buff 65))) + (signer-key (buff 33)) + (max-amount uint) + (auth-id uint)) + (let ((stacker-info (stx-account tx-sender)) + ;; to extend, there must already be an etry in the stacking-state + (stacker-state (unwrap! (get-stacker-info tx-sender) (err ERR_STACK_EXTEND_NOT_LOCKED))) + (amount-ustx (get locked stacker-info)) + (unlock-height (get unlock-height stacker-info)) + (cur-cycle (current-pox-reward-cycle)) + ;; first-extend-cycle will be the cycle in which tx-sender *would have* unlocked + (first-extend-cycle (burn-height-to-reward-cycle unlock-height)) + ;; new first cycle should be max(cur-cycle, stacker-state.first-reward-cycle) + (cur-first-reward-cycle (get first-reward-cycle stacker-state)) + (first-reward-cycle (if (> cur-cycle cur-first-reward-cycle) cur-cycle cur-first-reward-cycle))) + + ;; must be called with positive extend-count + (asserts! (>= extend-count u1) + (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + ;; stacker must be directly stacking + (asserts! (> (len (get reward-set-indexes stacker-state)) u0) + (err ERR_STACKING_IS_DELEGATED)) + + ;; stacker must not be delegating + (asserts! (is-none (get delegated-to stacker-state)) + (err ERR_STACKING_IS_DELEGATED)) + + ;; Verify signature from delegate that allows this sender for this cycle + (try! (consume-signer-key-authorization pox-addr cur-cycle "stack-extend" extend-count signer-sig signer-key u0 max-amount auth-id)) + + ;; TODO: add more assertions to sanity check the `stacker-info` values with + ;; the `stacker-state` values + + (let ((last-extend-cycle (- (+ first-extend-cycle extend-count) u1)) + (lock-period (+ u1 (- last-extend-cycle first-reward-cycle))) + (new-unlock-ht (reward-cycle-to-burn-height (+ u1 last-extend-cycle)))) + + ;; first cycle must be after the current cycle + (asserts! (> first-extend-cycle cur-cycle) (err ERR_STACKING_INVALID_LOCK_PERIOD)) + ;; lock period must be positive + (asserts! (> lock-period u0) (err ERR_STACKING_INVALID_LOCK_PERIOD)) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; tx-sender must be locked + (asserts! (> amount-ustx u0) + (err ERR_STACK_EXTEND_NOT_LOCKED)) + + ;; tx-sender must not be delegating + (asserts! (is-none (get-check-delegation tx-sender)) + (err ERR_STACKING_ALREADY_DELEGATED)) + + ;; standard can-stack-stx checks + (try! (can-stack-stx pox-addr amount-ustx first-extend-cycle lock-period)) + + ;; register the PoX address with the amount stacked + ;; for the new cycles + (let ((extended-reward-set-indexes (try! (add-pox-addr-to-reward-cycles pox-addr first-extend-cycle extend-count amount-ustx tx-sender signer-key))) + (reward-set-indexes + ;; use the active stacker state and extend the existing reward-set-indexes + (let ((cur-cycle-index (- first-reward-cycle (get first-reward-cycle stacker-state))) + (old-indexes (get reward-set-indexes stacker-state)) + ;; build index list by taking the old-indexes starting from cur cycle + ;; and adding the new indexes to it. this way, the index is valid starting from the current cycle + (new-list (concat (default-to (list) (slice? old-indexes cur-cycle-index (len old-indexes))) + extended-reward-set-indexes))) + (unwrap-panic (as-max-len? new-list u12))))) + ;; update stacker record + (map-set stacking-state + { stacker: tx-sender } + { pox-addr: pox-addr, + reward-set-indexes: reward-set-indexes, + first-reward-cycle: first-reward-cycle, + lock-period: lock-period, + delegated-to: none }) + + ;; return lock-up information + (ok { stacker: tx-sender, unlock-burn-height: new-unlock-ht }))))) +``` + +
+ +You can “rotate” your signing key when extending your lock period. + +The arguments are: + +* Extend count: the number of cycles to add to your lock period. The resulting lock period cannot be larger than 12. For example, if you're currently locked with 6 cycles remaining, the maximum number you can extend is 6. +* Pox Address: the BTC address to receive rewards +* Signer public key: the public key used for signing. This can stay the same, or you can use a new key. +* Signer signature: a signature proving control of your signing key +* Max Amount: This parameter is used to validate the signer signature provided. It represents the maximum number of uSTX (1 stx = 1,000,000 uSTX) that can be stacked in this transaction. +* Auth Id: This parameter is used to validate the signer signature provided. It is a random integer that prevents the re-use of this particular signer signature. + +#### Increasing the stacking amount + +The stacking amount can also be increased while actively Stacking. The increased position will take effect starting with the next Stacking cycle. The function called is `stack-increase`. + +
+ +Function source code + +```clojure +;; Increase the number of STX locked. +;; *New in Stacks 2.1* +;; This method locks up an additional amount of STX from `tx-sender`'s, indicated +;; by `increase-by`. The `tx-sender` must already be Stacking & must not be +;; straddling more than one signer-key for the cycles effected. +;; Refer to `verify-signer-key-sig` for more information on the authorization parameters +;; included here. +(define-public (stack-increase + (increase-by uint) + (signer-sig (optional (buff 65))) + (signer-key (buff 33)) + (max-amount uint) + (auth-id uint)) + (let ((stacker-info (stx-account tx-sender)) + (amount-stacked (get locked stacker-info)) + (amount-unlocked (get unlocked stacker-info)) + (unlock-height (get unlock-height stacker-info)) + (cur-cycle (current-pox-reward-cycle)) + (first-increased-cycle (+ cur-cycle u1)) + (stacker-state (unwrap! (map-get? stacking-state + { stacker: tx-sender }) + (err ERR_STACK_INCREASE_NOT_LOCKED))) + (cur-pox-addr (get pox-addr stacker-state)) + (cur-period (get lock-period stacker-state))) + ;; tx-sender must be currently locked + (asserts! (> amount-stacked u0) + (err ERR_STACK_INCREASE_NOT_LOCKED)) + ;; must be called with positive `increase-by` + (asserts! (>= increase-by u1) + (err ERR_STACKING_INVALID_AMOUNT)) + ;; stacker must have enough stx to lock + (asserts! (>= amount-unlocked increase-by) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + ;; stacker must be directly stacking + (asserts! (> (len (get reward-set-indexes stacker-state)) u0) + (err ERR_STACKING_IS_DELEGATED)) + ;; stacker must not be delegating + (asserts! (is-none (get delegated-to stacker-state)) + (err ERR_STACKING_IS_DELEGATED)) + + ;; Validate that amount is less than or equal to `max-amount` + (asserts! (>= max-amount (+ increase-by amount-stacked)) (err ERR_SIGNER_AUTH_AMOUNT_TOO_HIGH)) + + ;; Verify signature from delegate that allows this sender for this cycle + (try! (consume-signer-key-authorization cur-pox-addr cur-cycle "stack-increase" cur-period signer-sig signer-key increase-by max-amount auth-id)) + + ;; update reward cycle amounts + (asserts! (is-some (fold increase-reward-cycle-entry + (get reward-set-indexes stacker-state) + (some { first-cycle: first-increased-cycle, + reward-cycle: (get first-reward-cycle stacker-state), + stacker: tx-sender, + add-amount: increase-by, + signer-key: signer-key }))) + (err ERR_INVALID_INCREASE)) + ;; NOTE: stacking-state map is unchanged: it does not track amount-stacked in PoX-4 + (ok { stacker: tx-sender, total-locked: (+ amount-stacked increase-by)}))) +``` + +
+ +The arguments are: + +* Increase by: the amount of uSTX to add to your lock amount. +* Signer public key: the public key used for signing. This can stay the same, or you can use a new key. +* Signer signature: a signature proving control of your signing key +* Max Amount: This parameter is used to validate the signer signature provided. It represents the maximum number of uSTX (1 stx = 1,000,000 uSTX) that can be stacked in this transaction. +* Auth Id: This parameter is used to validate the signer signature provided. It is a random integer that prevents the re-use of this particular signer signature. + +### Step by Step Stacking Guide + +Now that you are familiar with the overall stacking flow and the different roles played, let's dive into the step-by-step guide for actually conducting the stacking process. + +{% hint style="info" %} +There are several ways you can go about stacking. This guide will cover using Leather Earn, which is a stacking web application and the simplest option. + +Additionally, you can choose to call the stacking functions directly from the [deployed contract](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) in the explorer. + +The fields and process will be the same, but the UI will differ. + +Finally, you can stack using JS and the [@stacks/stacking](https://github.com/hirosystems/stacks.js/tree/main/packages/stacking) package if you prefer. Again, the functions and parameters will be the same, you will just be writing your JS code directly instead of using a UI. + +If you are interested in using this method, you'll want to follow the [stacking guide](https://docs.hiro.so/stacks.js/guides/how-to-integrate-stacking) created by Hiro, and be sure to integrate the new parameters described in [Hiro's Nakamoto update doc](https://docs.hiro.so/nakamoto/stacks-js). +{% endhint %} + +### Step 1: Run or work with a signer + +This is a necessary prerequisite to stacking as a solo stacker or pool operator. + +Running a signer involves setting up a hosted production environment that includes both a Stacks Node and the Stacks Signer. For more information, refer to the [running a signer doc](../run-a-signer/). + +Once the signer software is running, you'll have to keep track of the `stacks_private_key` that you used when configuring your signer software. This will be used in subsequent Stacking transactions. + +{% hint style="info" %} +In the note above about pool operator vs signer keys, this corresponds to your signer key, not your pool operator wallet +{% endhint %} + +Alternatively, you can work with a signer and have them perform step 2 below on your behalf. + +### Step 2: Generate a signer key signature + +#### Overview of signer keys and signatures + +The main difference with Stacking in Nakamoto is that the Signer (either solo Stacker or signer running a pool) needs to include new parameters in their Stacking transactions. These are Clarity transactions that Stackers will call when interacting with the `pox-4.clar` contract. Interacting with this contract is how you as a Stacker actually stack your STX tokens. + +{% hint style="info" %} +The current pox-4 contract address can be found by visiting the following endpoint of the Hiro API: [https://api.mainnet.hiro.so/v2/pox](https://api.mainnet.hiro.so/v2/pox). + +You can then visit the [Explorer](https://explorer.hiro.so/?chain=mainnet) to view the contract and pass in the contract id. + +You may want to review this contract to familiarize yourself with it. +{% endhint %} + +Here is an overview of the fields you will need to pass. We'll cover how to get and pass these fields as we dive further into this doc: + +{% stepper %} +{% step %} +### signer-key + +The public key that corresponds to the `stacks_private_key` your signer is using. +{% endstep %} + +{% step %} +### signer-signature + +A signature that demonstrates that you actually control your `signer-key`. Because signer keys need to be unique, this is also a safety check to ensure that other Stackers can’t use someone else’s public key. +{% endstep %} + +{% step %} +### max-amount + +The maximum amount of uSTX (1 STX = 1,000,000 uSTX) that can be locked in the transaction that uses this signature. For example, if calling `stack-increase`, this parameter dictates the maximum amount of uSTX that can be used to add more locked STX. +{% endstep %} + +{% step %} +### auth-id + +A random integer that prevents the re-use of a particular signature, similar to how nonces are used with transactions. Must be less than 14 characters. +{% endstep %} +{% endstepper %} + +Signer signatures are signatures created using a particular signer key. They demonstrate that the controller of that signer key is allowing a Stacker to use their signing key. The signer signature’s message hash is created using the following data: + +* `method`: a string that indicates the Stacking method that is allowed to utilize this signature. The valid options are `stack-increase`, `stack-stx`, `stack-extend`, `agg-commit` (for stack-aggregation-commit-indexed), or `agg-increase` (for stack-aggregation-increase) +* `max-amount`: described above +* `auth-id`: described above +* `period`: a value between 1 and 12, which indicates the number of cycles that the Stacker is allowed to lock their STX for in this particular Stacking transaction. For `agg-commit`, this must be equal to 1 +* `reward-cycle`: This represents the reward cycle in which the Stacking transaction can be confirmed. For solo stacking operations (`stack-stx`, `stack-extend` and `stack-increase`), this has to be set as the current cycle. +* `pox-address`: The Bitcoin address that is allowed to be used for receiving rewards. This can be set to any Bitcoin address that you have access to. +* `config`: This represents the signer configuration file path where the `stacks_private_key` is located, and it is used for signing the generated signature. + +Now that we have an overview of role and contents of signatures, let's see how to actually generate them. You have several options available. + +Generating your signature with Degen Lab's stacks.tools + +Degen Lab has a signature generation tool that will generate a signature using their signer. This is the quickest and simplest option. To generate a signature using this method, all you need to do is visit their [signature tool](https://signature.stacking.tools/) and pass in the relevant information as covered on this page. + +#### Generating your signature with stacks.js + +The [@stacks/stacking](https://www.npmjs.com/package/@stacks/stacking) NPM package provides interfaces to generate and use signer signatures. + +There is a function called `signPoxSignature` that will allow you to generate this signature and pass it in to the stacking function. + +More information and code samples can be found on [Hiro's Nakamoto docs](https://docs.hiro.so/nakamoto/stacks-js). + +#### Generating your signature using the stacks-signer CLI + +If you already have your signer configured and set up, you can use the `stacks-signer` CLI to generate this signature. You can either SSH into your running signer or use the `stacks-signer` CLI locally. If using the CLI locally, you will need to ensure you have a matching configuration file located on your filesystem. Having a matching configuration file is important to ensure that the signer public key you make in Stacking transactions is the same as in your hosted signer. + +The CLI command is: + +```bash +# Max Amount equivallent to 1M STX + +# Auth Id should be a random string less than 14 characters +stacks-signer generate-stacking-signature \ + --method stack-stx \ + --max-amount 1000000000000 \ + --auth-id 71948271489 \ + --period 1 \ + --reward-cycle 100 \ + --pox-address bc1... \ + --config ./config.toml \ + --json +``` + +These arguments match those described in section [Overview of signer keys and signatures](solo-stack.md#overview-of-signer-keys-and-signatures), with the addition of: + +* `--json`, to optionally output the resulting signature in JSON + +You can use the following command to generate a random `32` bit integer as `auth-id`: + +```bash +python3 -c 'import secrets; print(secrets.randbits(32))' +``` + +Once the `generate-stacking-signature` command is run, the CLI will output a JSON: + +```json +{"authId":"1234","maxAmount":"12345","method":"stack-stx","period":1,"poxAddress":"bc1...","rewardCycle":100,"signerKey":"aaaaaaaa","signerSignature":"bbbbbbbbbbb"} +``` + +You will use the JSON when calling Stacking transactions from your Stacker address as outlined above. Remember that this may be different than your signer address. + +#### Generating your signature with Leather Earn + +Leather Earn is a web application that provides an easy-to-use interface for stacking and generating signatures. We'll cover using Leather Earn for stacking at the end of this document, here we will cover how to use it to generate a signature. + +{% hint style="info" %} +At the time of writing, this has only been tested using the [Leather](https://leather.io/) wallet. +{% endhint %} + +You can visit [earn.leather.io](https://earn.leather.io/) to generate a signer key signature. Make sure you’re connected to the correct network.\ +To generate a signer key signature, it’s important that you’ve logged in Leather with the same secret key that was used to [generate your signer key](broken-reference), not the account that will serve as your pool operator address. Once you’ve setup that account on Leather, you can log in to Leather Earn.\ +Click the link “Signer key signature” at the bottom of the page. This will open the “generate a signer key signature” page. + +The fields are: + +* Reward cycle: + * For all solo stacking transactions, this must equal the current reward cycle, not the cycle in which they will start stacking. The field defaults to the current reward cycle. + * For stack-aggregation-commit-indexed, this field must equal the cycle used in that function’s “reward cycle” argument. Typically, that equates to current\_cycle + 1. +* Bitcoin address: the PoX reward address that can be used +* Topic: the stacking function that will use this signature +* Max amount: max amount of STX that can be used. Defaults to “max possible amount” +* Auth ID: defaults to random int +* Duration: must match the number of cycles used in the stacking transaction. For stack-aggregation-commit-indexed, use “1”. + +{% hint style="warning" %} +Each of these fields must be exactly matched in order for the Stacking transaction to work. Future updates to Leather Earn will verify the signature before the transaction is made. +{% endhint %} + +Click the “generate signature” button to popup a Leather page where you can generate the signature. Once you submit that popup, Leather Earn will have the signer key and signature you generated. + +After you sign that message, you'll see the information you can use in your Stacking transactions, including the signer public key and signature. + +You can click the “copy” icon next to “signer details to share with stackers”. This will copy a JSON string, which can be directly pasted into the Leather Earn page where you make your Stacking transaction. Alternatively, this information can be entered manually. + +We'll cover the Leather Earn pages for actually making those transactions in the next section of this document. + +#### Using a hardware or software wallet to generate signatures + +When the signer is configured with a `stacks_private_key`, the signer may want to be able to use that key in a wallet to make stacking signatures. + +If the signer uses a tool like [@stacks/cli](https://docs.hiro.so/get-started/command-line-interface) to generate the key, the CLI also outputs a mnemonic (aka “seed phrase”) that can be imported into a wallet. Because the Stacks CLI uses the standard derivation path for generating Stacks keys, any Stacks wallet will default to having that same private key when the wallet is imported from a derivation path. Similarly, if a hardware wallet is setup with that mnemonic, then the Signer can use a wallet like Leather to make stacking signatures. + +The workflow for setting up a wallet to generate signatures: + +{% stepper %} +{% step %} +Use @stacks/cli to generate the keychain and private key. + +* Typically, when using a hardware wallet, it’s better to generate the mnemonic on the hardware wallet. For this use case, however, the signer software needs the private key, and hardware wallets (by design) don’t allow exporting private keys. +{% endstep %} + +{% step %} +Take the `privateKey` from the CLI output and add it to your signer’s configuration. +{% endstep %} + +{% step %} +Take the mnemonic (24 words) and either: + +* Setup a new hardware wallet with this mnemonic, or +* Store it somewhere securely, like a password manager. When the signer needs to generate signatures for Stacking transactions, they can import it into either Leather or XVerse. +{% endstep %} +{% endstepper %} + +When the user needs to generate signatures: + +{% stepper %} +{% step %} +Set up your wallet with your signer key’s private key. Either: + +* Setup your Leather wallet with a Ledger hardware wallet, or +* Import your mnemonic into Leather, XVerse, or another Stacks wallet. +{% endstep %} + +{% step %} +Open an app that has stacking signature functionality built-in. +{% endstep %} + +{% step %} +Connect your wallet to the app (aka sign in). +{% endstep %} + +{% step %} +In the app, enter your PoX address and “submit”. + +* The app will popup a window in your wallet that prompts you to sign the information and will show clear information about what you’re signing. +{% endstep %} + +{% step %} +Create the signature. + +* If using a Ledger, confirm on your device. +{% endstep %} + +{% step %} +The app will display two results: + +* Your signer key, which is the public key associated with your signer’s key. +* Your signer signature. +{% endstep %} + +{% step %} +Finally, make a Stacking transaction using the signer key and signer signature. +{% endstep %} +{% endstepper %} + +Now that you have your signer signature generated, it's time to start stacking. This process will vary depending on your chosen method. We've included instructions for solo stacking using [Leather Earn](https://earn.leather.io/) below. + +### Step 3: Stack your STX + +#### stack-stx + +To start, you'll visit [Leather Earn](https://earn.leather.io/) and click the “Stack independently” button on the home page. + +This page will allow you to input the following input: + +* The amount of STX you want to lock +* The duration (in number of cycles) to lock for +* Your BTC address where you will receive Stacking rewards +* New fields: + * Your signer public key + * Your signer key signature (this is what you generated in the previous step) + * Auth ID + * Max amount + +#### stack-extend + +If you want to extend the time that your STX will be locked for, you can use the stack-extend page on Leather Earn. + +If you’re already stacking, the home page will provide a link to “view stacking details”. From there, you can choose to extend. + +On this page are the following fields: + +* The number of cycles you want to extend for +* Your BTC address to receive rewards +* New fields: + * Signer public key + * Signer key signature + * Auth ID + * Max amount + +#### stack-increase + +If you want to increase the amount of STX locked, you can use the stack-increase page on Leather Earn. + +If you’re already stacking, the home page will provide a link to “view stacking details”. From there, you can choose to increase. + +On this page are the following fields: + +* The amount of STX you want to increase by +* New fields: + * Signer public key + * Signer key signature + * Auth ID + * Max amount diff --git a/docs/operate/stacking-stx/stack-with-a-pool.md b/docs/operate/stacking-stx/stack-with-a-pool.md new file mode 100644 index 0000000000..138c8f2984 --- /dev/null +++ b/docs/operate/stacking-stx/stack-with-a-pool.md @@ -0,0 +1,65 @@ +# Stack with a Pool + +The most common stacking scenario is to be an individual stacker that does not meet the stacking minimum and to stack with a pool. + +This is also the least complex version and is very easy to accomplish. + +Be sure you are familiar with the [concept of stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking) before digging into this. + +The dynamic minimum required to stack STX changes based on the total liquid supply of STX in the network and can be found by looking at the `pox` endpoint of the Hiro API: [https://api.hiro.so/v2/pox](https://api.hiro.so/v2/pox). + +If you do not meet this minimum, you'll need to delegate your STX to a pool operator. + +This is a non-custodial delegation, meaning that your STX do not actually leave your wallet. + +{% hint style="info" %} +Pool operators have a lot of control over exactly how they implement stacking. Usually users will be interacting with a wrapper contract that the pool operator has created to utilize the core stacking contract. +{% endhint %} + +Delegating your STX to a pool operator involves a few steps: + +{% stepper %} +{% step %} +### Find a pool + +The first step is to find a stacking pool you would like to stack with. Pool operators have a lot of control over exactly how they implement stacking and reward distribution, and they all do things a bit differently, so it's important to do your research. The Stacks website has a great [page on stacking](https://www.stacks.co/learn/stacking) that links to several pool operators for you to research. +{% endstep %} + +{% step %} +### Use the pool's UI to call the delegate function + +After you've chosen your pool operator, you'll need to get set up with a [Stacks-compatible wallet](https://www.stacks.co/explore/ecosystem?category=All+Teams#wallets) like Leather, Xverse, or Asigna. + +Then you'll use your chosen pool operator's UI to call their delegation function. You may need to pass a couple of parameters here like how long you want to grant delegation permission for. +{% endstep %} + +{% step %} +### Pool operator stacks tokens + +Once you grant permission for the pool operator to delegate, they will take over and call a separate function in the stacking contract to actually stack those delegated tokens. At this point your STX will be locked. + +Depending on how the pool operator handles things, they may offer the option to automatically continue to stack your STX for up to 12 continuous cycles. +{% endstep %} + +{% step %} +### Pool operator allocates rewards + +Next, the pool operator will track what proportion of rewards you should earn based on the proportion of STX you delegated. If distributing rewards directly in Bitcoin, the pool operator will need to take custody of the Bitcoin and manually distribute it. + +This is why it is important to do your research and stack with a pool operator whose reward distribution mechanism you trust. Different operators have different methods to make this process transparent and as trust-minimized as possible. +{% endstep %} + +{% step %} +### Pool operator distributes rewards + +Finally, the pool operator will distribute those rewards to you in either BTC or STX, depending on the model they use. +{% endstep %} +{% endstepper %} + +If you want to learn more about the specific functions and the contract that handles the stacking process, be sure to take a look at the [stacking contract walkthrough](https://app.gitbook.com/s/GVj1Z9vMuEOMe7oH7Wnq/clarity/example-contracts/stacking). + +### Liquid Stacking + +Liquid stacking is when you delegate your STX tokens to a liquid stacking provider and they issue you a new token such as stSTX that you can then use in the ecosystem. This makes it so that stackers can still use their STX to participate in DeFi protocols and other apps even while their STX are locked. + +This works a bit differently than traditional stacking and links to liquid stacking providers for you to research can be found on the [Stacks website](https://www.stacks.co/learn/stacking). diff --git a/docs/operate/stacking-stx/stop-stacking.md b/docs/operate/stacking-stx/stop-stacking.md new file mode 100644 index 0000000000..fcca8c5d8f --- /dev/null +++ b/docs/operate/stacking-stx/stop-stacking.md @@ -0,0 +1,67 @@ +# Stop Stacking + +When you decide it's time to stop stacking your STX tokens, the process depends on whether you are stacking solo or delegating your tokens to a pool operator. This guide explains the steps for both scenarios. + +*** + +## Stopping Solo Stacking + +When stacking solo using the `stack-stx` function, your STX is locked for a fixed period (the lock period) defined when you initiated stacking or when you extended the lock period. **No additional action is required to stop stacking**, you simply have to wait until the lock period expires. + +{% hint style="info" %} +In solo stacking, both the `stack-stx` and `stack-extend` functions emits an event that includes the `unlock-burn-height` field. This is the burn block height at which your tokens will be automatically unlocked. +{% endhint %} + +## Stopping Pooled Stacking + +If you're stacking with a pool (where you delegate your STX via the `delegate-stx` function), the process to stop stacking requires one extra step before your STX is eventually unlocked. + +{% stepper %} +{% step %} +### Revoke Delegation + +Before your STX can be unlocked, you must cancel the delegation with the pool operator. This is done by calling the `revoke-delegate-stx` function through the pool's interface, or within the [pox-4](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet) contract. + +
+ +Function source code + +```clojure +;; Revokes the delegation to the current stacking pool. +;; New in pox-4: Fails if the delegation was already revoked. +;; Returns the last delegation state. +(define-public (revoke-delegate-stx) + (let ((last-delegation-state (get-check-delegation tx-sender))) + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + (asserts! (is-some last-delegation-state) (err ERR_DELEGATION_ALREADY_REVOKED)) + (asserts! (map-delete delegation-state { stacker: tx-sender }) (err ERR_DELEGATION_ALREADY_REVOKED)) + (ok last-delegation-state))) +``` + +
+ +Calling `revoke-delegate-stx` cancels your STX delegation, revoking the pool operator's access to further lock/stack your funds. Even after revoking the delegation, your STX will remain locked until the end of the last stacking cycle chosen by the pool (can be at most 12 cycles in the future). + +{% hint style="warning" %} +Failing to revoke your delegation will mean that you continue to allow the pool to stack your STX until the reach of the burn block height mentioned in the delegate function (`delegate-stx`). Ensure that you have successfully called `revoke-delegate-stx` if you want to stop stacking sooner. +{% endhint %} +{% endstep %} + +{% step %} +### Wait for Funds to Unlock + +After revoking your delegation, your STX tokens will still remain locked until the last stacking cycle chosen by the pool operator completes. The unlock occurs automatically at the predefined unlock burn height for that cycle. + +{% hint style="info" %} +Even in pooled stacking, the unlocking mechanism follows the same blockchain timing as solo stacking. Revoking delegation only stops future stacking actions, it does not immediately unlock your tokens. +{% endhint %} +{% endstep %} +{% endstepper %} + +## Considerations + +* Monitor Your Stacking Status: Use your wallet's interface or the [Hiro Explorer](https://explorer.hiro.so/?chain=mainnet) to track the status of your lock period and confirm when your tokens are available. +* Using the API: Hiro's API offers an endpoint to [Get account STX balance](https://docs.hiro.so/stacks/api/accounts/stx-balances), which contains the `burnchain_unlock_height` height, representing the burn block height where your STX unlocks. +* Plan Ahead: Since the unlocking is bound to cycle's timing, plan your stacking period or revocation accordingly to minimize delays in accessing your funds. diff --git a/docs/press-and-reports/.gitbook.yaml b/docs/press-and-reports/.gitbook.yaml new file mode 100644 index 0000000000..77d87e4766 --- /dev/null +++ b/docs/press-and-reports/.gitbook.yaml @@ -0,0 +1,30 @@ +root: ./ + +redirects: + bitcoin-theses-and-reports/bitcoin-theses: README.md + bitcoin-theses-and-reports/bitcoin-reports: bitcoin-theses-and-reports/bitcoin-reports.md + + # 2024 press-and-top-links redirects + press-and-top-links/2024/january-2024: press-and-top-links/2024/january-2024.md + press-and-top-links/2024/february-2024: press-and-top-links/2024/february-2024.md + press-and-top-links/2024/march-2024: press-and-top-links/2024/march-2024.md + press-and-top-links/2024/april-2024: press-and-top-links/2024/april-2024.md + press-and-top-links/2024/may-2024: press-and-top-links/2024/may-2024.md + press-and-top-links/2024/june-2024: press-and-top-links/2024/june-2024.md + press-and-top-links/2024/july-2024: press-and-top-links/2024/july-2024.md + press-and-top-links/2024/august-2024: press-and-top-links/2024/august-2024.md + press-and-top-links/2024/september-2024: press-and-top-links/2024/september-2024.md + press-and-top-links/2024/october-2024: press-and-top-links/2024/october-2024.md + press-and-top-links/2024/november-2024: press-and-top-links/2024/november-2024.md + press-and-top-links/2024/december-2024: press-and-top-links/2024/december-2024.md + + # 2025 press-and-top-links redirects + press-and-top-links/2025/january-2025: press-and-top-links/2025/january-2025.md + press-and-top-links/2025/february-2025: press-and-top-links/2025/february-2025.md + press-and-top-links/2025/march-2025: press-and-top-links/2025/march-2025.md + press-and-top-links/2025/april-2025: press-and-top-links/2025/april-2025.md + press-and-top-links/2025/may-2025: press-and-top-links/2025/may-2025.md + press-and-top-links/2025/june-2025: press-and-top-links/2025/june-2025.md + press-and-top-links/2025/july-2025: press-and-top-links/2025/july-2025.md + press-and-top-links/2025/august-2025: press-and-top-links/2025/august-2025.md + press-and-top-links/2025/september-2025: press-and-top-links/2025/september-2025.md diff --git a/docs/press-and-reports/.gitbook/assets/theses.svg b/docs/press-and-reports/.gitbook/assets/theses.svg new file mode 100644 index 0000000000..779857b113 --- /dev/null +++ b/docs/press-and-reports/.gitbook/assets/theses.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/press-and-reports/README.md b/docs/press-and-reports/README.md new file mode 100644 index 0000000000..d057fa03ee --- /dev/null +++ b/docs/press-and-reports/README.md @@ -0,0 +1,26 @@ +--- +description: >- + This list will be updated monthly and capture notable investor theses or + industry commentary on Stacks and the Bitcoin ecosystem. +cover: .gitbook/assets/theses.svg +coverY: 0 +--- + +# Bitcoin Theses + +| Title | Outlet / Author | Date | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ------------: | +| [TradFi Tomorrow: DeFi and the Rise of Extensible Finance](https://www.paradigm.xyz/2025/03/tradfi-tomorrow-defi-and-the-rise-of-extensible-finance) | Paradigm | March 2025 | +| 💭 [It's time to make your It’s time to make your BTC productive again](https://medium.com/@aspendigitalAMP/its-time-to-make-your-btc-productive-again-7532ea788a32) | Aspen Digital | March 2025 | +| 💭 [The Bitcoin Renaissance: Unlocking Trillions in Value](https://www.forbes.com/sites/leeorshimron/2024/08/13/the-bitcoin-renaissance-unlocking-trillions-in-value/) | Leeor Shimron, Forbes | August 2024 | +| 💭 [My journey with the Blockchain Ecosystem and why do I like Stacks?](https://www.linkedin.com/pulse/my-journey-blockchain-ecosystem-why-do-i-like-stacks-ali-farid-khwaja-wkybf?ref=stacksblog) | Ali Farid Khwaja | July 2024 | +| 🖋️ [Bitcoin and Future Infracon Highlights](https://arkstreamcapital.medium.com/arkstream-capital-bitcoin-and-future-infracon-highlights-b9b3ac4777cd) | Arkstream Capital | June 2024 | +| 📊 [The Build on Bitcoin Era is Here](https://mythofmoney.substack.com/p/build-on-bitcoin-era-is-here) | Myth of Money | February 2024 | +| 📊 [2024 Bitcoin Halving: This Time It's Different](https://www.grayscale.com/research/reports/2024-halving-this-time-its-actually-different) | Grayscale | February 2024 | +| 🌱 [The Year Ahead](https://panteracapital.com/blockchain-letter/the-year-ahead-2024/) | Pantera | January 2024 | +| 🌱 [State of Bitcoin Q4 2023](https://messari.io/report/state-of-bitcoin-q4-2023) | Messari | January 2024 | +| 🖋️ [Notable Moments for Bitcoin in 2024](https://trustmachines.co/blog/notable-moments-for-bitcoin-in-2024/?ref=stacksblog) | Trust Machines | January 2024 | +| 📙 [Bitcoin Layers: Tapestry of a Trustless Financial Era](https://bitcoinlayersreport.com/) | Spartan Group | December 2023 | +| 🧪 [A Technical History of Blockchain Design, Innovation, and Narratives](https://foundationcapital.com/a-technical-history-of-blockchain-design-innovation-and-narratives-part-i/) | Foundation Capital | December 2023 | +| 📊 [2024 Crypto Market Outlook](https://www.coinbase.com/nl/institutional/research-insights/research/market-intelligence/2024-crypto-market-outlook) | Coinbase | December 2023 | +| 👀 [STX Thesis Update](https://medium.com/@halp1120/stx-thesis-update-cd09b7f2cce8) | Hal Press | December 2023 | diff --git a/docs/press-and-reports/SUMMARY.md b/docs/press-and-reports/SUMMARY.md new file mode 100644 index 0000000000..d80ddfa89b --- /dev/null +++ b/docs/press-and-reports/SUMMARY.md @@ -0,0 +1,32 @@ +# Table of contents + +## Bitcoin Theses & Reports + +* [Bitcoin Theses](README.md) +* [Bitcoin Reports](bitcoin-theses-and-reports/bitcoin-reports.md) + +## Press & Top Links + +* [2024](press-and-top-links/2024/README.md) + * [January 2024](press-and-top-links/2024/january-2024.md) + * [February 2024](press-and-top-links/2024/february-2024.md) + * [March 2024](press-and-top-links/2024/march-2024.md) + * [April 2024](press-and-top-links/2024/april-2024.md) + * [May 2024](press-and-top-links/2024/may-2024.md) + * [June 2024](press-and-top-links/2024/june-2024.md) + * [July 2024](press-and-top-links/2024/july-2024.md) + * [August 2024](press-and-top-links/2024/august-2024.md) + * [September 2024](press-and-top-links/2024/september-2024.md) + * [October 2024](press-and-top-links/2024/october-2024.md) + * [November 2024](press-and-top-links/2024/november-2024.md) + * [December 2024](press-and-top-links/2024/december-2024.md) +* [2025](press-and-top-links/2025/README.md) + * [January 2025](press-and-top-links/2025/january-2025.md) + * [February 2025](press-and-top-links/2025/february-2025.md) + * [March 2025](press-and-top-links/2025/march-2025.md) + * [April 2025](press-and-top-links/2025/april-2025.md) + * [May 2025](press-and-top-links/2025/may-2025.md) + * [June 2025](press-and-top-links/2025/june-2025.md) + * [July 2025](press-and-top-links/2025/july-2025.md) + * [August 2025](press-and-top-links/2025/august-2025.md) + * [September 2025](press-and-top-links/2025/september-2025.md) diff --git a/docs/press-and-reports/bitcoin-theses-and-reports/bitcoin-reports.md b/docs/press-and-reports/bitcoin-theses-and-reports/bitcoin-reports.md new file mode 100644 index 0000000000..83f5cb18cf --- /dev/null +++ b/docs/press-and-reports/bitcoin-theses-and-reports/bitcoin-reports.md @@ -0,0 +1,31 @@ +--- +description: >- + This list will be updated monthly and capture notable investor theses or + industry commentary on Stacks and the Bitcoin ecosystem. +--- + +# Bitcoin Reports + +| Title | Outlet / Author | Date | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -------------- | +| 🧪 [Unlocking Bitcoin \[Presentation\]: The Bitcoin Tipping Point](https://syphercapital.substack.com/p/unlocking-bitcoin-presentation-the) | Sypher Capital | March 2025 | +| 📊 [State of Crypto: 2025 Market Outlet](https://www.globalxetfs.com.au/state-of-crypto-2025-market-outlook/) | Global X | February 2025 | +| 🟧 [Stacks Q4 2024 Brief](https://messari.io/report/stacks-q4-2024-brief) | Messari | February 2025 | +| 📈 [Top 'Made in USA' Coins by MarketCap](https://x.com/Stacks/status/1881640502149390473?utm_source=stackssnacks.com\&utm_medium=newsletter\&utm_campaign=stacks-thrives-amid-regulatory-changes-stacking-dao-unveils-sbtc-product&_bhlid=42f17e5c11b1222a7ddd2cf3de42eb1c121496bc) | CoinGecko | January 2025 | +| 📙 [GTM in Asia Report: The Driving Force Behind Crypto Market Growth](https://www.theblock.co/post/333733/foresight-ventures-and-primitive-ventures-unveil-game-changing-apac-crypto-go-to-market-insights) | Primitive Ventures | January 2025 | +| 📈 [Top 10 Digital Assets Market Predictions](https://x.com/AspenDigitalAMP/status/1877289981166731741) | Aspen Digital | January 2025 | +| 📙 [State of Tokenized BTC: A $1 Trillion Opportunity](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/sbtc/sbtc-operations/deposit) | Bitcoin Builders Association | December 2024 | +| 🟧 [Our Thesis on Stacks](https://candidcontemplation.substack.com/p/our-thesis-on-stx) | Portal Ventures | December 2024 | +| 📊 [Scaling Bitcoin](https://www.gsr.io/reports/scaling-bitcoin/) | GSR | November 2024 | +| 📊 [Bitcoin L2s: A Modular Future](https://www.galaxy.com/insights/research/bitcoin-layer-2-modular-future/) | Galaxy Digital | November 2024 | +| 📊 [Crypto Sectors in Q4 2024](https://www.grayscale.com/research/market-commentary/grayscale-research-insights-crypto-sectors-in-q4-2024) | Grayscale | September 2024 | +| 📈 [Stacks August 2024 Snapshot](https://x.com/signal21btc/status/1833117479024963728) | Signal21 | August 2024 | +| 🟧 [Building Block: Stacks](https://www.grayscale.com/research/reports/building-block-stacks?ref=stacksblog) | Grayscale | August 2024 | +| 📙 [Bitcoin Layer 2 Ecosystems](https://unhashed.aarna.ai/p/bitcoin-layer2-ecosystems?ref=stacksblog) | Alpha Unhashed | June 2024 | +| 📙 [Stacks Snapshot June 2024](https://app.signal21.io/reports/stacks-snapshot-june-2024) | Signal21 | June 2024 | +| 🧪 [The Future of Bitcoin #3: Scaling Bitcoin](https://www.binance.com/en/research/analysis/the-future-of-bitcoin-3-scaling-bitcoin?ref=stacksblog) | Binance Research | May 2024 | +| 🟧 [Q4 2023: No Denying Demand For Bitcoin L2s](https://newsletters.stacks.org/p/q4-2023) | Stacks Foundation | January 2024 | +| 📊 [Stacks established developers up 51%](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL3R3aXR0ZXIuY29tL0tlblRoZVJvZ2Vycy9zdGF0dXMvMTc0ODA2MDU5OTcwMjE5MjYyNj9zPTIwJnJlZj1zdGFja3NibG9nJnV0bV9zb3VyY2U9c3RhY2tzc25hY2tzLmNvbSZ1dG1fbWVkaXVtPXJlZmVycmFsJnV0bV9jYW1wYWlnbj1kaXNjb3Zlci1zdGFja3MtaW4tc3BhcnRhbi1ncm91cC1iaXRjb2luLWwyLXJlcG9ydC1lbGVjdHJpYy1jYXBpdGFsLWRldmVsb3Blci1yZXBvcnQiLCJwb3N0X2lkIjoiZTdmYWFkODEtZmI3Ni00MjBmLTk3YWItNGJjMjdmM2RiYjdiIiwicHVibGljYXRpb25faWQiOiIzY2ZhYmZjYy0xNDU5LTQ0NTAtODI3MC1iOGJmM2RkMmFiOTciLCJ2aXNpdF90b2tlbiI6ImQxNDQwNWJiLWMxOWEtNDJiYi05NGQxLTY4MmRiNzZkOWQ3ZSIsImlhdCI6MTcwODA5NzQyMSwiaXNzIjoib3JjaGlkIn0.t0UBYmIXoMyGWCemN_n36kslOn35rIcv2v7LQR3nMLs) | Electric Capital | January 2024 | +| 📙 [Binance Research: Top 10 Narratives to Follow in 2024](https://public.bnbstatic.com/static/files/research/top-10-narratives.pdf) | Binance Research | December 2023 | +| 📙 [Bitcoin Layers Report: Tapestry of a Trustless Financial Era](https://bitcoinlayersreport.com/) | Spartan Group | December 2023 | + diff --git a/docs/press-and-reports/press-and-top-links/2024/README.md b/docs/press-and-reports/press-and-top-links/2024/README.md new file mode 100644 index 0000000000..c3aad38838 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/README.md @@ -0,0 +1,57 @@ +--- +description: >- + This page indexes top stories, press, and reports related to Stacks on a + monthly basis. +--- + +# 2024 + +For weekly stories delivered to your inbox, subscribe to [Stacks Snacks](https://stackssnacks.com/). For quarterly ecosystem recaps, subscribe to the [Stacks Foundation newsletter](https://newsletters.stacks.org/). + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} + +{% content-ref url="broken-reference" %} +[Broken link](broken-reference) +{% endcontent-ref %} diff --git a/docs/press-and-reports/press-and-top-links/2024/april-2024.md b/docs/press-and-reports/press-and-top-links/2024/april-2024.md new file mode 100644 index 0000000000..345d3349e1 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/april-2024.md @@ -0,0 +1,20 @@ +# April 2024 + +| Title | Outlet/Author | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | +| 👀 [Disruptor To Disruptee: Bitcoin Institutionalization Needs L2 Shakeup](https://www.forbes.com/sites/nimrodlehavi/2024/04/11/disruptor-to-disruptee-bitcoin-institutionalization-needs-l2-shakeup/?sh=1745f76c629b) | Forbes | +| 🟪 [Q\&A: What will the Bitcoin halving mean for Bitcoin L2s?](https://blockworks.co/news/bitcoin-halving-layer-2-impact-stacks) | Blockworks | +| [Bitcoin Layer 2 Stacks Prepares for Nakamoto Upgrade, its Largest Hard-Fork Ever](https://thedefiant.io/news/blockchains/bitcoin-layer-2-stacks-prepares-for-nakamoto-upgrade-its-largest-hard-fork-ever) | The Defiant | +| 🟧 [Stacks, Bitcoin Layers, and the Nakamoto Upgrade: Here’s What’s Going On](https://decrypt.co/225801/stacks-stx-nakamoto-upgrade-bitvm-rollups-defi) | Decrypt | +| 📈 [BTCFi is an ‘enormous opportunity’ to make Bitcoin a productive asset — Stacks](https://cointelegraph.com/news/btcfi-opportunity-make-bitcoin-productive-asset) | Cointelegraph | +| ₿ [‘Real opportunity’ for Bitcoin Runes will come after first wave of investor hype](https://cointelegraph.com/news/real-opportunity-bitcoin-runes-after-first-wave-investor-hype) | Cointelegraph | +| 🟧 [OG Bitcoin L2 Stacks Is Getting a Major Overhaul](https://www.coindesk.com/tech/2024/04/16/og-bitcoin-l2-stacks-is-getting-a-major-overhaul/) | Coindesk | +| 📻 [Bitcoin L2 with Muneeb Ali of Stacks and Andy Fajar Handika of Loka Mining](https://www.charlieshrem.com/bitcoin-l2-with-muneeb-ali-of-stacks-and-andy-fajar-handika-of-loka-mining/) | Charlie Shrem Show | +| 📧 [April 15th Newsletter](https://milkroad.com/daily/what-happened-to-prices-this-weekend/?ref=stacksblog) | Milroad | +| 🗞️ [TEAMZ Web3/AI Summit2024 Day 1 has ended successfully!](https://prtimes.jp/main/html/rd/p/000000143.000031083.html?ref=stacksblog) | PR Times (Japan) | +| 👀 [The Bitcoin Halving's Degen Bets](https://www.bankless.com/the-bitcoin-halvings-degen-bets) | Bankless | +| 🌱 [Top 5 Pioneering Bitcoin Projects Poised For Growth Post-2024 Halving](https://cryptodaily.co.uk/2024/04/top-5-pioneering-bitcoin-projects-poised-for-growth-post-2024-halving) | Crypto Daily UK | +| 💰 [Spartan Capital leads $10 million strategic funding round for Bitcoin DeFi developer ALEX](https://www.theblock.co/post/284556/spartan-capital-leads-10-million-funding-round-for-bitcoin-defi-developer-alex) | The Block | +| ₿ [‘Bitcoin has as many functionalities as other blockchains’: Trust Machines member weighs in Bitcoin DeFi](https://cryptobriefing.com/bitcoin-functionality-other-blockchains/?ref=stacksblog) | Crypto Briefing | +| 🗞️ [A Look into The Upcoming Bitcoin Halving & Bitcoin Layer 2s](https://figment.io/insights/a-look-into-the-upcoming-bitcoin-halving-bitcoin-layer-2s/?ref=stacksblog) | Figment | +| 📕 [Bitcoin could gain new smart-contract superpowers](https://links.coinbase.com/e/evib?_t=3aca56371967418192255878e9689713&_m=fde47e8c40c24182b7cab3a6b10c9d3a&_e=DT1gXnNeiWLsEoKsYDSIAUCWVJr21XSvYbLTlg_uJ63W2CLdvG0Q8MPxsoVG5vCKM9SLV_5n-owzOqH_yPS9iQ%3D%3D\&utm_source=stackssnacks.com\&utm_medium=referral\&utm_campaign=stacks-apps-celebrate-all-time-tvl-high-as-new-defi-protocols-emerge) with the Lightning Network and Stacks. | Coinbase Bytes | diff --git a/docs/press-and-reports/press-and-top-links/2024/august-2024.md b/docs/press-and-reports/press-and-top-links/2024/august-2024.md new file mode 100644 index 0000000000..4e40c6dfe7 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/august-2024.md @@ -0,0 +1,20 @@ +# August 2024 + +| Title | Outlet/Author | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | +| 🧩 [Deploy Stacks nodes on AWS with the AWS Blockchain Node Runners](https://aws.amazon.com/blogs/database/how-to-deploy-stacks-blockchain-nodes-on-aws-with-the-aws-blockchain-node-runners-stacks-blueprint/?ref=stacksblog) | Amazon Web Services | +| 🗞️ [Bitflow and Leather Wallet Join Forces to Simplify Bitcoin L2 Asset Swaps](https://hackernoon.com/bitflow-and-leather-wallet-join-forces-to-simplify-bitcoin-l2-asset-swaps) | Hackernoon | +| 🤝 [Aptos and Stacks Forge New Partnership for Bitcoin Innovation](https://www.altcoinbuzz.io/cryptocurrency-news/aptos-and-stacks-forge-new-partnership-for-bitcoin-innovation/) | Altcoin Buzz | +| 🖊 [Millions of Dollars Worth of BTC Earned by New Institutional Signers Since Nakamoto Instantiation](https://stacks.org/institutional-signers-earn-millions) | Stacks Foundation | +| 💲[Liquidium Raises $2.5M, Accelerating L1 Borrowing and Lending](https://subscribe.bitcoinbuildersassociation.com/p/liquidium-raises-25m-accelerating?ref=stacksblog) | Bitcoin Builders Association | +| 🧡[Bitcoin Network Stacks Begins Rollout of Speed-Boosting Nakamoto Upgrade](https://decrypt.co/246543/bitcoin-stacks-rollout-speed-boosting-nakamoto-upgrade) | Decrypt | +| 📙[Bitcoin Network Stacks Devs 'Can See the Finish Line' With Nakamoto Upgrade](https://decrypt.co/247247/bitcoin-network-stacks-devs-see-finish-line-nakamoto-upgrade) | Decrypt | +| 📙[Stacks co-creator on how the Nakamoto upgrade will drive a $70bn market for Bitcoin DeFi](https://www.dlnews.com/articles/defi/stacks-nakamoto-upgrade-brings-bitcoin-defi-with-sbtc-token/?utm_source=telegram\&utm_medium=organic_social\&utm_campaign=) | DL News | +| 🗞️[Bitcoin Layer-2 Network Stacks Begins Nakamoto Upgrade](https://www.coindesk.com/tech/2024/08/28/bitcoin-layer-2-network-stacks-begins-nakamoto-upgrade/amp/) | Coindesk | +| 🗞️[Bitcoin Layer 2 Stacks readies for Nakamoto upgrade activation](https://crypto.news/bitcoin-layer-2-stacks-readies-for-nakamoto-upgrade-activation/) | Crypto News | +| [Stacks (STX) prepares for Nakamoto upgrade: here’s what to expect](https://coinjournal.net/news/stacks-stx-prepares-for-nakamoto-upgrade-heres-what-to-expect/) 🗞️ | Coin Journal | +| 🗞️[Stacks (STX) poised for recovery as game-changer Nakamoto upgrade approaches](https://invezz.com/news/2024/08/26/stacks-stx-poised-for-recovery-as-game-changer-nakamoto-upgrade-approaches/) | Invezz | +| 🗞️[Bitcoin Layer-2 Stacks Set to Receive Its Nakamoto Upgrade, Will Enhance DeFi on Bitcoin](https://www.livebitcoinnews.com/bitcoin-layer-2-stacks-set-to-receive-its-nakamoto-upgrade-will-enhance-defi-on-bitcoin/) | Live Bitcoin News | +| 🗞️[Nakamoto Activation Begins: Leading L2 Stacks Sets the Stage for a Bitcoin-Led Future](https://markets.businessinsider.com/news/currencies/nakamoto-activation-begins-leading-l2-stacks-sets-the-stage-for-a-bitcoin-led-future-1033729689) | Markets Insider | +| 🧡 [Bitcoin L2s Are Eating the World](https://hackernoon.com/bitcoin-l2s-are-eating-the-world) | Hackernoon | +| 💰 [The Bitcoin Renaissance: Unlocking Trillions in Value](https://www.forbes.com/sites/leeorshimron/2024/08/13/the-bitcoin-renaissance-unlocking-trillions-in-value/?ref=stacksblog) | Forbes | diff --git a/docs/press-and-reports/press-and-top-links/2024/december-2024.md b/docs/press-and-reports/press-and-top-links/2024/december-2024.md new file mode 100644 index 0000000000..cfd6bdf387 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/december-2024.md @@ -0,0 +1,20 @@ +# December 2024 + +| Article | Outlet / Author | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| 🧡 [Portal Ventures, the Leading Pre-seed VC Firm and the First to Call the Bitcoin Thesis, to Back sBTC](https://www.theblock.co/post/329292/portal-ventures-the-leading-pre-seed-vc-firm-and-the-first-to-call-the-bitcoin-thesis-to-back-sbtc?ref=stacksblog) | The Block | +| 🧡 [LearnWeb3, the Largest Educational Platform for Web3, Set to Onboard New Wave of sBTC Developers](https://www.theblock.co/post/330037/learnweb3-the-largest-educational-platform-for-web3-set-to-onboard-new-wave-of-sbtc-developers) | The Block | +| 🧡 [Xverse, Leading Bitcoin Ecosystem Wallet, Adopts sBTC as Preferred Scaling Solution for the Bitcoin Economy](https://www.theblock.co/post/330649/xverse-leading-bitcoin-ecosystem-wallet-adopts-sbtc-as-preferred-scaling-solution-for-the-bitcoin-economy) | The Block | +| 🧡 [Hex Trust Expands Collaboration with Stacks Asia Foundation to Bolster sBTC Adoption](https://blockchainreporter.net/hex-trust-expands-collaboration-with-stacks-asia-foundation-to-bolster-sbtc-adoption/) | Blockchain Reporter | +| 🧡 [Fordefi, the First MPC Wallet to Fully Support Bitcoin DeFi, Joins Cohort of sBTC Backers](https://www.theblock.co/post/331016/fordefi-the-first-mpc-wallet-to-fully-support-bitcoin-defi-joins-cohort-of-sbtc-backers) | The Block | +| 🧡 [Travala, The #1 Bitcoin and Crypto Travel Booking Portal, Announces Support for sBTC and STX](https://www.theblock.co/post/331020/travala-the-1-bitcoin-and-crypto-travel-booking-portal-announces-support-for-sbtc-and-stx) | The Block | +| 🚀 [Double-dipping with sBTC on Stacks](https://blockworks.co/news/stacks-sbtc-double-dipping) | Blockworks | +| 🚀 [Bitcoin Gets DeFi Upgrade: Stacks Launches Bitcoin-Backed sBTC for Smart Contracts](https://hackernoon.com/bitcoin-gets-defi-upgrade-stacks-launches-bitcoin-backed-sbtc-for-smart-contracts) | Hackernoon | +| 🚀 [sBTC Launches on Stacks Mainnet, Bringing Bitcoin DeFi to Life](https://beincrypto.com/sbtc-launches-on-stacks-mainnet/) | BeInCrypto | +| 🚀 [sBTC Launches on Stacks Mainnet With Deposit-Only Functionality](https://cryptopotato.com/sbtc-launches-on-stacks-mainnet-with-deposit-only-functionality/?amp) | Crypto Potato | +| 🚀 [Stacks Launches sBTC on Mainnet with 1,000 BTC Cap, Offering 5% Yield and Up to 60% APY Staking](https://thedefiant.io/news/blockchains/stacks-launches-sbtc-on-mainnet-1000-btc-cap-offering-5-yield-up-to-60-apy-b70deae1) | The Defiant | +| 🚀 [sBTC Kicks Off on Stacks Mainnet: Details](https://u.today/sbtc-kicks-off-on-stacks-mainnet-details) | Crypto Economy | +| 🟧 [Bitcoin's Memecoin-Like 'Runes' Get a Boost With AMM Launch on Stacks](https://www.coindesk.com/tech/2024/12/18/bitcoins-memecoin-like-runes-get-a-boost-with-amm-launch-on-stacks) | Coindesk | +| 🧡 [Ankr, the #1 Provider of Bitcoin-Secured, Physical Infrastructure, Becomes Signer for Stacks as sBTC Launches](https://www.theblock.co/post/331411/ankr-the-1-provider-of-bitcoin-secured-physical-infrastructure-becomes-signer-for-stacks-as-sbtc-launches) | The Block | +| 📙 [New Report Finds Tokenized BTC Landscape Worth $1T (18 Dec)](https://coinmarketcap.com/community/articles/6762f62b09984e48933a1ec1/) | CoinMarketCap | +| 📙 [New Report Finds Tokenized BTC Landscape Worth $1T (18 Dec)](https://www.binance.com/en/square/post/17739664161346) | Binance | diff --git a/docs/press-and-reports/press-and-top-links/2024/february-2024.md b/docs/press-and-reports/press-and-top-links/2024/february-2024.md new file mode 100644 index 0000000000..a2a259bca6 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/february-2024.md @@ -0,0 +1,33 @@ +# February 2024 + +_For weekly stories delivered to your inbox, subscribe to_ [Stacks Snacks](https://stackssnacks.com/). _For quarterly ecosystem recaps, subscribe to the_ [Stacks Foundation newsletter](https://newsletters.stacks.org/). + +
+ +📊 Messari's Q4 2023 'State of Stacks' Report + +[https://messari.io/report/state-of-stacks-q4-2023](https://messari.io/report/state-of-stacks-q4-2023) + +**Key Insights** + +* **Stacks revenue (USD) increased 3,386% QoQ and 3,028% YoY to $637,000.** Much of this revenue was driven by inscription protocol STX20. +* **STX’s market cap increased 203% QoQ and 598% YoY to $2.0 billion.** STX’s growth outpaced BTC and the overall crypto market. +* **DeFi TVL (USD) increased 363% QoQ and 763% YoY to $61 million.** ALEX firmly remained the leader in TVL, but Arkadiko and StackingDAO considerably increased their own TVL dominance in Q3 and Q4. +* **Average daily miner revenue increased 1,015% YoY to $78,000.** STX’s price increase and Stacks’ increased revenue made it significantly more profitable for Bitcoin miners to participate in Stacks’ consensus. +* **The Nakamoto upgrade is expected in April 2024.** This update will enable faster blocks, give transactions 100% Bitcoin finality, reduce MEV, and eliminate forking on the Stacks layer to set the stage for the upcoming sBTC release. + +
+ +| Title | Outlet/Author | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | +| 🌱 [The Year Ahead](https://panteracapital.com/blockchain-letter/the-year-ahead-2024/) | Pantera Blockchain Letter | +| 📊 [Q1 2024 Bitcoin ecosystem map](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL3R3aXR0ZXIuY29tL3NvcmFfdmVudHVyZXMvc3RhdHVzLzE3NTQ2ODc4ODg3NTgwODM5MjI_dXRtX3NvdXJjZT1zdGFja3NzbmFja3MuY29tJnV0bV9tZWRpdW09cmVmZXJyYWwmdXRtX2NhbXBhaWduPWFsZXgtZ292ZXJuYW5jZS1wcm9wb3NhbC1mb3IteGxuay1zdGFja2luZy1kYW8taGl0cy0zNW0taW4tdHZsIiwicG9zdF9pZCI6IjQ1NTY3YzAzLWFkZDUtNGQzNi1iZGM2LTk4Y2YwYzkyMDA5YyIsInB1YmxpY2F0aW9uX2lkIjoiM2NmYWJmY2MtMTQ1OS00NDUwLTgyNzAtYjhiZjNkZDJhYjk3IiwidmlzaXRfdG9rZW4iOiJkMTQ0MDViYi1jMTlhLTQyYmItOTRkMS02ODJkYjc2ZDlkN2UiLCJpYXQiOjE3MDgwOTcxMjksImlzcyI6Im9yY2hpZCJ9.Ga_H469MIGKuovy5WVHfd3Fit9x6IiqAr0qhNCW575E) | Sora Ventures | +| 🪴 [Peak Total Value Locked (TVL) and Rising Developer Engagement on Stacks](https://twitter.com/HouseofChimera/status/1757792122692911207?ref=stacksblog) | House of Chimera | +| 📊 [40% of Bitcoin developers are working on Bitcoin L2s](https://twitter.com/MohamedFFouda/status/1752407779640295480?s=20\&utm_source=stackssnacks.com\&utm_medium=referral\&utm_campaign=nakamoto-release-launch-date-velar-raises-3-5m-in-funding-round) | Mohamed Fouda | +| 📈 [Bitcoin is 25% below its record high -- but Layer 2 Stacks is even closer](https://blockworks.co/news/layer-2-stacks-approaching-bitcoin?ref=stacksblog) | Blockworks | +| 👛 Stacks L2 DeFi protocol [Velar raises $3.5M](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL3d3dy5jb2luZGVzay5jb20vYnVzaW5lc3MvMjAyNC8wMi8wMS9jcnlwdG8tc3RhcnR1cC12ZWxhci1wbGFucy1wZXJwZXR1YWwtc3dhcHMtZXhjaGFuZ2UtZm9yLWJpdGNvaW4tZGVmaS1hZnRlci1yYWlzaW5nLTMtNW0vPXR1dG1fc291cmNlPXN0YWNrc3NuYWNrcy5jb20mdXRtX21lZGl1bT1yZWZlcnJhbCZ1dG1fY2FtcGFpZ249bmFrYW1vdG8tcmVsZWFzZS1sYXVuY2gtZGF0ZS12ZWxhci1yYWlzZXMtMy01bS1pbi1mdW5kaW5nLXJvdW5kIiwicG9zdF9pZCI6ImYxODVmMmM4LTM0ZTQtNDM0My1hMWFkLTM4YmRlODAyYzY0OCIsInB1YmxpY2F0aW9uX2lkIjoiM2NmYWJmY2MtMTQ1OS00NDUwLTgyNzAtYjhiZjNkZDJhYjk3IiwidmlzaXRfdG9rZW4iOiJkMTQ0MDViYi1jMTlhLTQyYmItOTRkMS02ODJkYjc2ZDlkN2UiLCJpYXQiOjE3MDgwOTcyNDUsImlzcyI6Im9yY2hpZCJ9.FjGMmkbPat9qWNoUR5SJsfnwDWqfUQutXW-ScvveCjc) | Coindesk | +| 📊 Report: [Stacks could present a $90 Billion opportunity](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL3R3aXR0ZXIuY29tL1RhbmdDaGFuMHgvc3RhdHVzLzE3NTM0MTcyNDMxMzUwMTMwMTA_dD05N19DMkItaTJqZi11OGgwZEpWV2NBJnM9MzMmdXRtX3NvdXJjZT1zdGFja3NzbmFja3MuY29tJnV0bV9tZWRpdW09cmVmZXJyYWwmdXRtX2NhbXBhaWduPW5ha2Ftb3RvLXJlbGVhc2UtbGF1bmNoLWRhdGUtdmVsYXItcmFpc2VzLTMtNW0taW4tZnVuZGluZy1yb3VuZCIsInBvc3RfaWQiOiJmMTg1ZjJjOC0zNGU0LTQzNDMtYTFhZC0zOGJkZTgwMmM2NDgiLCJwdWJsaWNhdGlvbl9pZCI6IjNjZmFiZmNjLTE0NTktNDQ1MC04MjcwLWI4YmYzZGQyYWI5NyIsInZpc2l0X3Rva2VuIjoiZDE0NDA1YmItYzE5YS00MmJiLTk0ZDEtNjgyZGI3NmQ5ZDdlIiwiaWF0IjoxNzA4MDk3MjQ1LCJpc3MiOiJvcmNoaWQifQ.dons_3VaS_GyWvr5OZs-StFlDP-PX2ToMqIVB9pGVCQ) | Tang Chan | +| 📊 [ABCDE 2024 BTC Recap](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL3R3aXR0ZXIuY29tL0FCQ0RFTGFicy9zdGF0dXMvMTc0MDYyNzc3NTg2MDcyNzkxOT90PUo1ODlrWGtMNGlOblFJRXZyRW55TXcmcz0zMyZ1dG1fc291cmNlPXN0YWNrc3NuYWNrcy5jb20mdXRtX21lZGl1bT1yZWZlcnJhbCZ1dG1fY2FtcGFpZ249bmFrYW1vdG8tcmVsZWFzZS1sYXVuY2gtZGF0ZS12ZWxhci1yYWlzZXMtMy01bS1pbi1mdW5kaW5nLXJvdW5kIiwicG9zdF9pZCI6ImYxODVmMmM4LTM0ZTQtNDM0My1hMWFkLTM4YmRlODAyYzY0OCIsInB1YmxpY2F0aW9uX2lkIjoiM2NmYWJmY2MtMTQ1OS00NDUwLTgyNzAtYjhiZjNkZDJhYjk3IiwidmlzaXRfdG9rZW4iOiJkMTQ0MDViYi1jMTlhLTQyYmItOTRkMS02ODJkYjc2ZDlkN2UiLCJpYXQiOjE3MDgwOTcyNDUsImlzcyI6Im9yY2hpZCJ9.qhu0w9pHUMhY5ASnL74i7ixBbVw7LRrF3MJGJDbcoAs) | ABCDE Labs | +| 👀 [2024 Halving: This Time It’s Actually Different](https://www.grayscale.com/research/reports/2024-halving-this-time-its-actually-different?ref=stacksblog) | Grayscale | +| 📈 [While everyone theorizes about when $BTC will make new highs, $STX...](https://x.com/cburniske/status/1757951005654978872?t=_cLI0sby6lmp9V1Yj6-6kQ\&s=33\&ref=stacksblog) | Chris Burniske | +| [What Are Bitcoin Layer 2 Networks?](https://academy.binance.com/en/articles/what-are-bitcoin-layer-2-networks?viastacksblog\&ref=stacksblog) | Binance Academy | diff --git a/docs/press-and-reports/press-and-top-links/2024/january-2024.md b/docs/press-and-reports/press-and-top-links/2024/january-2024.md new file mode 100644 index 0000000000..773d8ffad9 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/january-2024.md @@ -0,0 +1,15 @@ +# January 2024 + +For weekly stories delivered to your inbox, subscribe to [Stacks Snacks](https://stackssnacks.com/). For quarterly ecosystem recaps, subscribe to the [Stacks Foundation newsletter](https://newsletters.stacks.org/). + +
+ +📙 Bitcoin Layers Report by Spartan Group + +**Tapestry of a Trustless Financial Era** + +Diving deep into the layers of Bitcoin's blossoming ecosystem, the first edition of the Bitcoin Layers Report unveils the emerging reality of a financial world where trust is embedded in technology rather than institutions. As Bitcoin evolves beyond a Store of Value, we stand on the cusp of a revolution in trustless finance and a new era of economic possibilities. [https://bitcoinlayersreport.com/](https://bitcoinlayersreport.com/) + +
+ +
TitleOutlet/Author
🌱 The Year Ahead | Pantera Blockchain LetterPantera
🟧 Q4 2023: No Denying Demand For Bitcoin L2sStacks Foundation
📙 Binance Research: Top 10 Narratives to Follow in 2024Binance Research
📕 The Bankless Guide to StacksBankless
📊 Electric Capital Report: Stacks established developers up 51%Electric Capital
📰 Bitcoin Could See Growth in Layer-2 Ecosystem, Drawing on Ethereum's ExperienceCoindesk, Yahoo Finance
🔮 Paul Veradittakit of Pantera Capital Lists Stacks In 6 Predictions Crypto 2024Coindesk
🏛️ Franklin Templeton: Ordinals & Layer 2 solutions on Bitcoin shouldn’t be ignoredFranklin Templeton
🔉 Podcast: Muneeb Ali joins us in The BlueprintThe Blueprint
🎨 Jack Butcher: Onboarding to BitcoinJack Butcher
👀 2024 Bitcoin Outlook: Ecosystem Beyond ETFsFour Pillars
Ⓜ️ This Bitcoin ecosystem is having its momentCosmo Jiang, Pantera Capital
🧪 MT Capital Research Report: Stacks Nakamoto is about to be upgraded to inject new impetus into the Bitcoin ecosystemMT Capital via Gate.io
📊 House of Chimera Analyzes Rapid Growth in Stacks TransactionsHouse of Chimera
👛 Ryder to operate FAST Pool, become signer in Stacks’ Nakamoto upgradeCrypto.news
🔶 Bitcoin 2024 Outlook (featuring Stacks and ALEX)OKX
💰 Bitcoin DEX Bitflow raises $1.3M in pre-seed fundingBitflow
😈 DeFi Researcher Ignas: “Degen Playbook for Stacks”Ignas via TLDR
📺 Why DeFi on Stacks is the Next Bitcoin BullBlock Runner
📊 All time high Total Value Locked (TVL) for Bitcoin L2 StacksDefiLlama
📊 sOrdinals: 100k Inscriptions in less than 24 hourssOrdinals
📊 STX20 Protocol Surpasses 1M Transactions in 1 MonthSTX20
📊 StackingDAO celebrates $20M in TVL one month inStackingDAO
📙 Bitcoin Layers Report: Tapestry of a Trustless Financial EraSpartan Group
diff --git a/docs/press-and-reports/press-and-top-links/2024/july-2024.md b/docs/press-and-reports/press-and-top-links/2024/july-2024.md new file mode 100644 index 0000000000..9738d92a0d --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/july-2024.md @@ -0,0 +1,21 @@ +--- +description: >- + Bitcoin Summer continued in July: BitGo stepped up to join the Stacks signer + network, Stacks teams Bitflow and Hermetica landed their own media and + builders everywhere convened in Nashville. +--- + +# July 2024 + +| Title | Outlet / Author | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| 📈 [All Time High in Monthly Active Accounts for Stacks](https://app.signal21.io/stacks?utm_source=stackssnacks.com\&utm_medium=referral\&utm_campaign=all-time-high-in-monthly-active-accounts-for-stacks) | Signal21 | +| 💎 [Bitflow Unveils Liquidity Hub Upgrade, Enabling Functionality Like Ethereum DeFi](https://blockchainreporter.net/bitflow-unveils-liquidity-hub-upgrade-enabling-functionality-like-ethereum-defi/) | Blockchain Reporter | +| 💎 [Bitflow’s Liquidity Hub Elevates Bitcoin DeFi to Ethereum DeFi Ecosystem Standard](https://coinmarketcap.com/community/articles/667c6188dd97c85264ba1fc1/) | CoinMarketCap | +| [Hermetica's Synthetic Dollar Sparks DeFi Revolution](https://hackernoon.com/hermeticas-synthetic-dollar-sparks-defi-revolution?ref=stacksblog) | Hackernoon | +| 🛡️ [Hypernative Bolsters Bitcoin L2 Security as Stacks Ecosystem Gets Real-Time Protection](https://hackernoon.com/hypernative-bolsters-bitcoin-l2-security-as-stacks-ecosystem-gets-real-time-protection) | Hackernoon | +| 🎫 [Bitcoin Builders Conference Set to Spotlight Innovation and the Future of the Bitcoin Economy](https://decrypt.co/239086/bitcoin-builders-conference-set-to-spotlight-innovation-and-the-future-of-the-bitcoin-economy) | Decrypt | +| 🪙 [Bitcoin Developers Launch BTC-Backed Stablecoin As Rune Token](https://decrypt.co/239925/bitcoin-developers-launch-btc-backed-stablecoin-as-rune-token) | Decrypt | +| 📰 [BitGo integrates Stacks for Bitcoin rewards, following institutional Bitcoin demand](https://cointelegraph.com/news/bitgo-stacks-bitcoin-rewards-institutional-bitcoin-demand) | CoinTelegraph | +| 📰 [BitGo Launches Support for Bitcoin L2 Stacks and sBTC](https://cryptopotato.com/bitgo-launches-support-for-bitcoin-l2-stacks-and-sbtc/?amp) | Crypto Potato | +| 🟧 [Protocol Village: Bitrue Ventures Launches $40M Fund for 'Nascent Web3 Companies'](https://www.coindesk.com/tech/2024/07/17/protocol-village/) | Coindesk | diff --git a/docs/press-and-reports/press-and-top-links/2024/june-2024.md b/docs/press-and-reports/press-and-top-links/2024/june-2024.md new file mode 100644 index 0000000000..35069d5129 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/june-2024.md @@ -0,0 +1,13 @@ +# June 2024 + +| Title | Outlet/Author | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | +| 📊 [Layer-2 Networks Mark The Dawn Of A New Golden Age For Bitcoin](https://www.analyticsinsight.net/cryptocurrency-analytics-insight/layer-2-networks-mark-the-dawn-of-a-new-golden-age-for-bitcoin?ref=stacksblog) | Analytics Insight | +| 📈 [Stacks Layer 2 for Bitcoin already hosts projects, STX token among top gainers](https://www.cryptopolitan.com/stacks-layer-2-bitcoin-stx-token-top-gainers/?ref=stacksblog) | Cryptopolitan | +| 📖 [Top Bitcoin Layer 2 Projects & Coins in 2024](https://cryptonews.com/cryptocurrency/bitcoin-layer-2-projects/?ref=stacksblog/) | Cryptonews | +| 📖 [Layer-2 Networks Mark The Dawn Of A New Golden Age For Bitcoin](https://www.analyticsinsight.net/cryptocurrency-analytics-insight/layer-2-networks-mark-the-dawn-of-a-new-golden-age-for-bitcoin) | Analytics Insight | +| 🧩 [Haruko Integrates Stacks to Deliver Institutional Asset Management on Bitcoin L2](https://www.financemagnates.com/thought-leadership/haruko-integrates-stacks-to-deliver-institutional-asset-management-on-bitcoin-l2/) | Finance Magnates | +| 🧩 [Haruko to enhance digital asset management with Stacks integration](https://finbold.com/haruko-to-enhance-digital-asset-management-with-stacks-integration/) | Finbold | +| 🧩 [Haruko to Streamline Bitcoin Asset Management With Stacks Integration](https://u.today/haruko-to-streamline-bitcoin-asset-management-with-stacks-integration) | U Today | +| 💲 [Stacking DAO Bi-Weekly Update: $560k in Stacking rewards over two cycles](https://medium.com/@stackingdao/stacking-dao-bi-weekly-update-560k-in-stacking-rewards-over-two-cycles-c012256c1622) | Medium | +| 🧩 [Kiln: We're thrilled to unveil our latest integration](https://x.com/Kiln_finance/status/1797612820537729354?ref=stacksblog) | Kiln | diff --git a/docs/press-and-reports/press-and-top-links/2024/march-2024.md b/docs/press-and-reports/press-and-top-links/2024/march-2024.md new file mode 100644 index 0000000000..c135319f4c --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/march-2024.md @@ -0,0 +1,9 @@ +# March 2024 + +* 🔏 [Bitcoin layer-2 Stacks partners with eight companies ahead of its Nakamoto upgrade](https://cryptobriefing.com/stacks-network-expansion-new-signers/) — Crypto Briefing +* 🤝 [Stacks expands with Blockdaemon, Near Foundation amid Bitcoin surge](https://cointelegraph.com/news/stacks-welcomes-new-signers-blockdaemon-near-foundation-bitcoin-surge) — Cointelegraph +* 📈 [More Validation for Bitcoin Builders: Industry Leaders to Integrate Stacks, the Leading Bitcoin L2](https://www.benzinga.com/pressreleases/24/03/37487866/more-validation-for-bitcoin-builders-industry-leaders-to-integrate-stacks-the-leading-bitcoin-l2) — Benzinga +* 💼 [Bitcoin layer-2 Stacks partners with eight companies ahead of its Nakamoto upgrade](https://www.coindesk.com/tech/2024/04/16/og-bitcoin-l2-stacks-is-getting-a-major-overhaul/) — Coindesk +* 🔏 [Stacks L2 Bolsters Network Security with 8 New Signers](https://www.cryptotimes.io/2024/03/06/stacks-l2-bolsters-network-security-with-8-new-signers/) — The Crypto Times +* 📈 [Stacks: The Nakamoto Upgrade](https://twitter.com/FTI_DA/status/1771166481880944693?utm_source=stackssnacks.com\&utm_medium=referral\&utm_campaign=franklin-templeton-highlights-the-nakamoto-release-velar-amm-mainnet) — Franklin Templeton +* 🪴 [Stacks reaches 1,000,000 unique wallets](https://flight.beehiiv.net/v2/clicks/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwczovL3R3aXR0ZXIuY29tL1N0YWNrcy9zdGF0dXMvMTc2NTg2NTQzOTc2OTMwNTU3MT91dG1fc291cmNlPXN0YWNrc3NuYWNrcy5jb20mdXRtX21lZGl1bT1yZWZlcnJhbCZ1dG1fY2FtcGFpZ249bmFrYW1vdG8tcmVsZWFzZS12b3RpbmctcGVyaW9kLWVuZHMtY2VsZWJyYXRpbmctb25lLW1pbGxpb24tdW5pcXVlLXdhbGxldHMiLCJwb3N0X2lkIjoiYTY0NTBlYTAtY2U4MC00ZjRlLTg0YjMtNjNmNWFhNzRiMTY1IiwicHVibGljYXRpb25faWQiOiIzY2ZhYmZjYy0xNDU5LTQ0NTAtODI3MC1iOGJmM2RkMmFiOTciLCJ2aXNpdF90b2tlbiI6ImRhZTNjOGUxLWVkZTUtNDQxMC1hNWFkLTU3MTc2NmYyMGQ3OSIsImlhdCI6MTcxMzcyNTM5MiwiaXNzIjoib3JjaGlkIn0.allLoCyKRwaGltRK4_zwV80JFbHr6s8SGxr7zPwSZ44) — Signal21 diff --git a/docs/press-and-reports/press-and-top-links/2024/may-2024.md b/docs/press-and-reports/press-and-top-links/2024/may-2024.md new file mode 100644 index 0000000000..393a1a7ef7 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/may-2024.md @@ -0,0 +1,20 @@ +# May 2024 + +| Title | Outlet/Author | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| [👀 Stacks active accounts reach record high amid growing interest in Bitcoin DeFi](https://cointelegraph.com/news/bitcoin-defi-surge-stacks-l2-record-users) | Cointelegraph | +| [🗞️ Newsletter: BTC’s Streak Is Coming To An End](https://milkroad.com/daily/btcs-streak-is-coming-to-an-end-%EF%B8%8F/?ref=stacksblog) | Milkroad | +| [📕 The Imperative for Bitcoin Layers](https://chorus.one/articles/the-imperative-for-bitcoin-layers-2?ref=stacksblog) | Chorus One | +| [First Bitcoin-backed synthetic dollar to launch with 25% yield](https://cointelegraph.com/news/hermetica-usdh-bitcoin-backed-synthetic-dollar) | Cointelegraph | +| [🔗 Stacks, Moonriver, Hedera Network and Iron Fish Join Axelar’s Interchain Amplifier](https://cryptonews.com/news/stacks-hedera-network-and-iron-fish-join-axelar-interchain.htm) | Crypto News | +| [🔗 Axelar Integrates With Stacks To Bridge Bitcoin Across Over 65 Blockchains](https://cryptodaily.co.uk/news-in-crypto/coincodex:axelar-integrates-with-stacks-to-bridge-bitcoin-across-over-65-blockchains) | Crypto Daily UK | +| 🌱 [LunarCrush Unveils AI-Driven Web3 Platform for Creators](https://www.altcoinbuzz.io/cryptocurrency-news/lunarcrush-unveils-ai-driven-web3-platform-for-creators/) | Altcoin Buzz | +| [Despite Bitcoin price volatility, factors point to BTC’s long-term success](https://cointelegraph.com/news/bitcoin-price-volatility-btc-success) | CoinTelegraph | +| [Satoshi’s Vision Or Not, Bitcoin DeFi Is Here To Stay](https://www.thestreet.com/crypto/markets/satoshis-vision-or-not-bitcoin-defi-is-here-to-stay-) | The Street | +| [📣 Stacks Foundation joins Uphold to drive Bitcoin adoption](https://www.binance.com/en/square/post/8093584158561) | Binance Square | +| [Stacks & Uphold Team Up to Boost Bitcoin Beyond Just a Store of Value](https://coinpaper.com/4190/stacks-and-uphold-team-up-to-boost-bitcoin-beyond-just-a-store-of-value) | Coinpaper | +| [Stacks and Uphold Partner Up to Boost Bitcoin Adoption](https://coinmarketcap.com/community/articles/66437419d7905c7145a4c38e/) | CoinMarketCap | +| [Stacks & Uphold Team Up to Boost Bitcoin Beyond Just a Store of Value](https://coinstats.app/news/e879f032aa90aad3a51213254a35691ddc897bbfc7200d3d95b95ff87bb4ca0e_Stacks-%26-Uphold-Team-Up-to-Boost-Bitcoin-Beyond-Just-a-Store-of-Value/) | CoinStats | +| [Stacks Foundation joins Uphold to drive Bitcoin adoption](https://www.coinlive.com/id/news-flash/514530) | CoinLive | +| [Stacks Partners With Uphold To Further Increase Bitcoin Adoption](https://www.investingcube.com/stacks-partners-with-uphold-to-further-increase-bitcoin-adoption/) | InvestingCube | +| [Stacks and Uphold Partner Up to Boost Bitcoin Adoption](https://www.cryptotimes.io/2024/05/14/stacks-and-uphold-partner-up-to-boost-bitcoin-adoption/) | Crypto Times | diff --git a/docs/press-and-reports/press-and-top-links/2024/november-2024.md b/docs/press-and-reports/press-and-top-links/2024/november-2024.md new file mode 100644 index 0000000000..feb35d0bdf --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/november-2024.md @@ -0,0 +1,8 @@ +# November 2024 + +| Article | Outlet / Author | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| 🧡 [CoinFlip, the #1 Global Bitcoin ATM Network Is Making Programmable Bitcoin More Accessible with Stacks, the Leading Bitcoin L2](https://www.theblock.co/post/327328/coinflip-the-1-global-bitcoin-atm-network-is-making-programmable-bitcoin-more-accessible-with-stacks-the-leading-bitcoin-l2) | TheBlock | +| 🧡 [Leading Crowdsourced Security Platform Immunefi Teams Up with Asymmetric Research & Bitcoin L2 Labs to Bolster sBTC Security](https://www.theblock.co/post/326835/leading-crowdsourced-security-platform-immunefi-teams-up-with-asymmetric-research-bitcoin-l2-labs-to-bolster-sbtc-security) | TheBlock | +| 🧡 [CoinFlip, the #1 Global Bitcoin ATM Network Is Making Programmable Bitcoin More Accessible with Stacks, the Leading Bitcoin L2](https://coinmarketcap.com/community/articles/673e0069c291c94bd18e68fb/) | CoinMarketCap | +| 🧡 [Bitcoin Frontier Fund, Home of the Top Bitcoin Accelerator, To Invest in Teams Built on sBTC](https://www.theblock.co/post/328240/bitcoin-frontier-fund-home-of-the-top-bitcoin-accelerator-to-invest-in-teams-built-on-sbtc) | TheBlock | diff --git a/docs/press-and-reports/press-and-top-links/2024/october-2024.md b/docs/press-and-reports/press-and-top-links/2024/october-2024.md new file mode 100644 index 0000000000..9997e79848 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/october-2024.md @@ -0,0 +1,11 @@ +# October 2024 + +| Article | Outlet/Author | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------- | +| 🚀 [Stacks fortifies Bitcoin ties with Nakamoto upgrade](https://blockworks.co/news/stacks-sbtc-bitcoin-alignment-nakamoto?ref=stacksblog) | Blockworks | +| 🚀 [Stacks, Prominent Bitcoin Layer-2 Project, Activates Long-Awaited 'Nakamoto' Upgrade](https://www.coindesk.com/tech/2024/10/29/stacks-prominent-bitcoin-layer-2-project-activates-long-awaited-nakamoto-upgrade/?ref=stacksblog) | Coindesk | +| 🤝 [Asymmetric Research Joins Stacks Ecosystem as Security Contributor to Bitcoin L2](https://hackernoon.com/asymmetric-research-joins-stacks-ecosystem-as-security-contributor-to-bitcoin-l2) | Hackernoon | +| 🟧 [A Beginner's Guide to Bitcoin Layers](https://www.hiro.so/blog/read-a-beginners-guide-to-bitcoin-layers?ref=stacksblog) | Hiro | +| 🌱 [Bitcoin-backed stablecoin developer Hermetica raises $1.7M in seed funding](https://www.theblock.co/post/321141/bitcoin-backed-stablecoin-developer-hermetica-raises-1-7-million-in-seed-funding?ref=stacksblog) | The Block | +| 🌱 [Blockstream raises $210M to accelerate Bitcoin adoption](https://subscribe.bitcoinbuildersassociation.com/p/blockstream-raises-210m-to-accelerate?ref=stacksblog) | Bitcoin Builders Association | +| 🟧 [BoostVC, Draper Associates, and Thesis Announce BitcoinFi Accelerator](https://subscribe.bitcoinbuildersassociation.com/p/boostvc-draper-associates-and-thesis?ref=stacksblog) | Bitcoin Builders Association | diff --git a/docs/press-and-reports/press-and-top-links/2024/september-2024.md b/docs/press-and-reports/press-and-top-links/2024/september-2024.md new file mode 100644 index 0000000000..571e6f2805 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2024/september-2024.md @@ -0,0 +1,16 @@ +# September 2024 + +| Article | Outlet / Author | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | +| 📈 [New Milestone for Bitcoin DeFi: Over 1,400 Smart Contracts Deployed on Stacks](https://coinchapter.com/new-milestone-for-bitcoin-defi-over-1400-smart-contracts-deployed-on-stacks-even-before-major-upgrade/) | Coin Chapter | +| 📈 [Bitcoin layer-2 Stacks witnessed 1,400 smart contract deployments month over month](https://www.livebitcoinnews.com/stacks-registers-unseen-smart-contract-deployment-days-away-from-its-nakamoto-upgrade/) | Live Bitcoin News | +| 🗞️ [Stacks' smart contracts reach record high ahead of Nakamoto upgrade](https://cointelegraph.com/news/stacks-record-smart-contracts-nakamoto-upgrade?ref=stacksblog) | Cointelegraph | +| 🤝🏻 [Anchorage Digital Announces Custody Support for Stacks](https://x.com/Stacks/status/1831335327300309174?utm_source=stackssnacks.com\&utm_medium=referral\&utm_campaign=anchorage-digital-supporting-stacks-btc-bash-and-other-highlights) | X / Anchorage Digital | +| 🚀 [Hermetica Labs Launches USDh, the first Bitcoin-native synthetic USD](https://cryptobriefing.com/bitcoin-synthetic-dollar-25-percent-yield/?ref=stacksblog) | Crypto Briefing | +| 💡 [What is sBTC? A Guide to the Non-Custodial Native Bitcoin DeFi](https://www.xverse.app/blog/what-is-sbtc?ref=stacksblog) | Xverse | +| ₿ [Over $1.5B worth of BTC is now locked in Bitcoin Layers](https://subscribe.bitcoinbuildersassociation.com/p/over-15b-worth-of-btc-is-now-locked?ref=stacksblog) | Bitcoin Builders Association | +| 🤝 [Stacks x Aptos Foundations Join Forces to Bring Bitcoin to Aptos Network via sBTC](https://decrypt.co/249825/bitcoin-stacks-l2-brings-its-sbtc-to-the-aptos-network) | Decrypt | +| 🖼️ [Gamma's United Bitcoin Ordinals and Stacks Platform Enters Beta](https://nftinsider.io/gamma-bitcoin-beta/?ref=stacksblog) | NFT Insider | +| 🤝 [Tokensoft partners with Stacks Foundation and Bitcoin Frontier Fund to Accelerate Bitcoin Builders](https://cryptobriefing.com/bitcoin-builders-acceleration-partnership/?ref=stacksblog) | Crypto Briefing | +| 🗞️ [Stacks Asia Foundation Launches with $15M in Funding to Boost Bitcoin Layer-2 Adoption](https://coinmarketcap.com/community/articles/66e2998ae0c16b2dea22b4f1/?ref=stacksblog) | CoinMarketCap | +| 🚀 [Zest Introduces BTCz, leveraging Babylon and Stacks](https://subscribe.bitcoinbuildersassociation.com/p/zest-introduces-btcz-leveraging-babylon?ref=stacksblog) | Zest | diff --git a/docs/press-and-reports/press-and-top-links/2025/README.md b/docs/press-and-reports/press-and-top-links/2025/README.md new file mode 100644 index 0000000000..9c23c326da --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2025/README.md @@ -0,0 +1,5 @@ +# 2025 + +For weekly stories delivered to your inbox, subscribe to [Stacks Snacks](https://stackssnacks.com/). For quarterly ecosystem recaps, subscribe to the [Stacks Foundation newsletter](https://newsletters.stacks.org/). + +
IssueTarget
january-2024.mdBroken link
february-2025.mdBroken link
march-2025.mdBroken link
march-2025-1.mdBroken link
march-2025-2.mdBroken link
june-2025.mdBroken link
june-2025-1.mdBroken link
june-2025-2.mdBroken link
june-2025-2-1.mdBroken link
diff --git a/docs/press-and-reports/press-and-top-links/2025/april-2025.md b/docs/press-and-reports/press-and-top-links/2025/april-2025.md new file mode 100644 index 0000000000..eaf41ee051 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2025/april-2025.md @@ -0,0 +1,12 @@ +# April 2025 + +| Article Title & Link | Media Outlet | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | +| [🧡 Experts: Gold’s Rise Doesn’t Undermine Bitcoin’s Digital Gold Status](https://news.bitcoin.com/experts-golds-rise-doesnt-undermine-bitcoins-digital-gold-status/) | Bitcoin.com | +| [🗞️ Veteran crypto exchange Bitfinex bets big on Bitcoin-based DeFi, deepens integration with Stacks](https://u.today/bitfinex-lists-stx-token-by-stacks-becomes-network-signer) | U Today | +| [🟧 Bitfinex Throws Its Full Weight Behind Bitcoin’s Leading Layer-2 Stacks](https://www.coincarp.com/learn/bitfinex-throws-its-full-weight-behind-bitcoins-leading-layer-2-stacks/) | Coincarp | +| [🚀 Hex Trust Expands Institutional Support for Stacks (STX) and sBTC Amid Growing Adoption](https://www.dlnews.com/research/external/hex-trust-expands-institutional-support-for-stacks-stx-and-sbtc-amid-growing-adoption/) | DL News | +| [🟧 Hermetica brings 5% yield to Bitcoin traders on Velar PerpDEX](https://crypto.news/hermetica-brings-5-yield-to-bitcoin-traders-on-velar-perpdex/) | Crypto.News | +| [🗞️ BitGo Launches Institutional Support for SBTC, Expanding Bitcoin DeFi Accessibility (22 Apr)](https://www.binance.com/en/square/post/23264351434353) | Binance | +| [🪙 Stacks' STX Is Week's Best Performer as Bitgo Link Seen Boosting Institutional Use](https://www.coindesk.com/markets/2025/04/25/stacks-stx-is-week-s-best-performer-as-bitgo-link-seen-boosting-institutional-use) | Coindesk | +| [🚀 Stacks Asia bets big on Middle East Bitcoin boom with Abu Dhabi partnership](https://cointelegraph.com/news/stacks-asia-adgm-partnership-boosts-bitcoin-adoption-middle-east) | Cointelegraph | diff --git a/docs/press-and-reports/press-and-top-links/2025/august-2025.md b/docs/press-and-reports/press-and-top-links/2025/august-2025.md new file mode 100644 index 0000000000..3ae0a4769c --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2025/august-2025.md @@ -0,0 +1,5 @@ +# August 2025 + +| Article Title & Link | Outlet | +| ----------------------------------------------------------------------------------------------------------------------------- | -------------- | +| [BTCFi #2: Inside the Infrastructure Layer of BTCFi](https://x.com/Tiger_Research_/status/1957703731308556340?ref=stacksblog) | Tiger Research | diff --git a/docs/press-and-reports/press-and-top-links/2025/february-2025.md b/docs/press-and-reports/press-and-top-links/2025/february-2025.md new file mode 100644 index 0000000000..87753f567f --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2025/february-2025.md @@ -0,0 +1,14 @@ +# February 2025 + +| Article | Publication / Link | +| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 🧡 Bitcoin's Correlation With Markets Grows, Challenging 'Safe Haven' Narrative | [Bitcoin.com](http://bitcoin.com/) | +| 🪙 How sBTC Unlocks Bitcoin DeFi – And Why It Matters | [Coincu.com](http://coincu.com/) | +| 🟧 sBTC Is Fast Emerging As The Missing Link In Bitcoin’s Cross-Chain Evolution | [Bitcoinist](https://bitcoinist.com/sbtc-is-fast-emerging-as-the-missing-link-in-bitcoins-cross-chain-evolution/) | +| 🧡 The Overlooked Bitcoin Layer-2 Sector: Why and What to Expect Moving Forward | [Binance](https://www.binance.com/en-IN/square/post/20381520919281) | +| 🟧 Bitcoin L2 ‘honeymoon phase’ is over, most projects will fail — Muneeb Ali | [CoinTelegraph](https://cointelegraph.com/news/bitcoin-layer2-projects-fade-stacks-muneeb-ali) | +| 🗞️ Blockchair Launches Stacks Explorer, Enhancing Bitcoin Layer 2 Visibility and sBTC Support | [DL News](https://www.dlnews.com/research/external/blockchair-launches-stacks-explorer-enhancing-bitcoin-layer-2-visibility-and-sbtc-support/) | +| 🗞️ Blockchair Launches Stacks Explorer, Enhancing Bitcoin Layer 2 Visibility and sBTC Support | [CoinMarketCap](https://coinmarketcap.com/community/articles/67bf2560ae03156d954313b6/) | +| 🟧 SNZ, UTXO Capital, Jump Crypto Among Leaders to Deposit Early in sBTC, Unlocking Bitcoin DeFi Utility | [Decrypt](https://decrypt.co/307910/snz-utxo-capital-jump-crypto-among-leaders-to-deposit-early-in-sbtc-unlocking-bitcoin-defi-utility) | +| 🚀 Stacks’ sBTC Sees Rapid Adoption as Second Cap Hits 3,000 BTC Limit | [Bitcoinist](https://bitcoinist.com/stacks-sbtc-sees-rapid-adoption-as-second-cap-hits-3000-btc-limit/) | +| 🚀 SNZ, UTXO Capital, Jump Crypto Among Leaders to Deposit Early in sBTC, Unlocking Bitcoin DeFi Utility | [DL News](https://www.dlnews.com/research/external/snz-utxo-capital-jump-crypto-among-leaders-to-deposit-early-in-sbtc-unlocking-bitcoin-defi-utility/) | diff --git a/docs/press-and-reports/press-and-top-links/2025/january-2025.md b/docs/press-and-reports/press-and-top-links/2025/january-2025.md new file mode 100644 index 0000000000..785cbb435f --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2025/january-2025.md @@ -0,0 +1,18 @@ +# January 2025 + +For weekly stories delivered to your inbox, subscribe to [Stacks Snacks](https://stackssnacks.com/). For quarterly ecosystem recaps, subscribe to the [Stacks Foundation newsletter](https://newsletters.stacks.org/). + +| Article | Outlet/Publication | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| [🧡 Stacks’ Muneeb Ali: Let the Bitcoin L2s Bloom](https://www.coindesk.com/consensus-hong-kong-2025-coverage/2025/01/13/stacks-muneeb-ali-let-the-bitcoin-l2s-bloom) | Coindesk | +| [🚀 Why Stacks Is Leading the Bitcoin Layer 2 Revolution](https://www.crypto-news-flash.com/why-stacks-is-leading-the-bitcoin-layer-2-revolution/) | Crypto News Flash | +| [🗞️ Bitcoin-Based Stablecoin USDh Secures $3M in Liquidity](https://www.coindesk.com/tech/2025/01/22/bitcoin-based-stablecoin-usdh-secures-usd3m-in-liquidity) | Coindesk | +| [🗞️ Hermetica's Bold Move to Dominate Stacks DeFi With its USDh Stablecoin and sBTC Yield Trade Program](https://hackernoon.com/hermeticas-bold-move-to-dominate-stacks-defi-with-its-usdh-stablecoin-and-sbtc-yield-trade-program) | Hackernoon | +| [🪙 Hermetica Launches New sBTC Yield Product, USDh Liquidity Faces Boost](https://u.today/hermetica-launches-new-sbtc-yield-product-usdh-liquidity-faces-boost) | U Today | +| [🪙 Hermetica Unveils $sBTC for Yield-Bearing $USDh in Partnership with Zest Protocol](https://blockchainreporter.net/hermetica-unveils-sbtc-for-yield-bearing-usdh-in-partnership-with-zest-protocol/) | Blockchain Reporter | +| [🪙 USDh set to become the leading stablecoin on Stacks as Hermetica introduces sBTC yield product](https://invezz.com/news/2025/01/23/usdh-set-to-become-the-leading-stablecoin-on-stacks-as-hermetica-introduces-sbtc-yield-product/) | Invezz | +| [🧡 Making Bitcoin Go Further: How sBTC Is Expanding the Possibilities of DeFi](https://bitcoinist.com/making-bitcoin-go-further-how-sbtc-is-expanding-the-possibilities-of-defi/) | Bitcoinist | +| [🚀 Bitcoin DeFi: The Most Pivotal Innovation On Bitcoin’s Evolutionary Path](https://www.cryptopolitan.com/bitcoin-defi-the-most-pivotal-innovation-on-bitcoins-evolutionary-path/) | Cryptopolitan | +| [🟧 Stacks Bridges the Gap to Bitcoin as Strategic US Reserve Materializes](https://www.financemagnates.com/thought-leadership/stacks-bridges-the-gap-to-bitcoin-as-strategic-us-reserve-materializes/) | Finance Magnates | +| [🗞️ Bitcoin DeFi Protocol Velar Unveils .BTC Name Grant Program for Unified Identities on Stacks](https://cryptopotato.com/bitcoin-defi-protocol-velar-unveils-btc-name-grant-program-for-unified-identities-on-stacks/) | CryptoPotato | +| [🚀](https://www.crypto-news-flash.com/why-stacks-is-leading-the-bitcoin-layer-2-revolution/) [Bitcoin DeFi: The Most Pivotal Innovation On Bitcoin’s Evolutionary Path](https://www.cryptopolitan.com/bitcoin-defi-the-most-pivotal-innovation-on-bitcoins-evolutionary-path/?ref=stacksblog) | Cryptopolitan | diff --git a/docs/press-and-reports/press-and-top-links/2025/july-2025.md b/docs/press-and-reports/press-and-top-links/2025/july-2025.md new file mode 100644 index 0000000000..8e4874aea3 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2025/july-2025.md @@ -0,0 +1,6 @@ +# July 2025 + +| Article Title & Link | Outlet | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | +| 📰 [$1.5B Stacks sBTC and STX Adopt Wormhole NTT Standard to Expand Multichain](https://wormhole.com/blog/usd1-5b-stacks-sbtc-and-stx-adopt-wormhole-ntt-standard-to-expand-multichain?ref=stacksblog) | Wormhole Blog | +| [Top Interop Protocol Wormhole Adds Stacks to Bridge Bitcoin to Multi-Chain DeFi](https://thedefiant.io/news/defi/wormhole-integrates-stacks-tokens-for-multi-chain-bitcoin-defi) | The Defiant | diff --git a/docs/press-and-reports/press-and-top-links/2025/june-2025.md b/docs/press-and-reports/press-and-top-links/2025/june-2025.md new file mode 100644 index 0000000000..147803e46b --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2025/june-2025.md @@ -0,0 +1,7 @@ +# June 2025 + +| Article Title & Link | Outlet | +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | +| 📰 [Stacks to be Showcased at Blockchain and Digital Assets Virtual Investor Conference on June 5](https://www.globenewswire.com/news-release/2025/06/04/3093569/0/en/Stacks-to-be-Showcased-at-Blockchain-and-Digital-Assets-Virtual-Investor-Conference-on-June-5th.html) | Globe Newswire | +| 🤝 [Copper Launches Support for sBTC](https://www.crowdfundinsider.com/2025/06/241658-digital-assets-copper-introduces-sbtc-stacking-functionality/) | Crowdfund Insider | +| 🧩 [Hex Trust Adds sBTC Support and SIP-010 Integration](https://bitcolumnist.com/release/hex-trust-integrates-sbtc-via-sip-010-to-enable-institutional-bitcoin-defi-access/) | BitColumnist | diff --git a/docs/press-and-reports/press-and-top-links/2025/march-2025.md b/docs/press-and-reports/press-and-top-links/2025/march-2025.md new file mode 100644 index 0000000000..23b8215866 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2025/march-2025.md @@ -0,0 +1,7 @@ +# March 2025 + +* 🟧 [America’s Crypto Renaissance: Why Stacks and Bitcoin Layers Are Poised for Growth](https://www.cryptopolitan.com/americas-crypto-renaissance-why-stacks-and-bitcoin-layers-are-poised-for-growth/) — Cryptopolitan +* 🧡 [Bitcoin Layers and the Path to Universal Utility](https://www.google.com/url?q=https://cfc-stmoritz.com/industry-insights/bitcoin-layers-and-the-path-to-universal-utility?utm_source%3DCfC%2BSt.%2BMoritz%2B-%2BGlobal%2BMailing%2BList%26utm_campaign%3D61bc405f9f-EMAIL_CAMPAIGN_2024_04_03_02_27_COPY_01%26utm_medium%3Demail%26utm_term%3D0_-4a9e5b2e5d-296312470\&sa=D\&source=editors\&ust=1743526707117175\&usg=AOvVaw2GQInrk1FYUbGiwB0hw6Jt) — Cfc Moritz (Kyle Ellicott) +* 🚀 [Velar PerpDex Launches on Stacks as First Bitcoin-Native Perpetual DEX](https://www.dlnews.com/research/external/velar-perpdex-launches-on-stacks-as-first-bitcoin-native-perpetual-dex/) — DL News +* 🧡 [Stacks Reaches New All-Time High TVL](https://x.com/signal21btc/status/1902742256068514010) — Signal21 + diff --git a/docs/press-and-reports/press-and-top-links/2025/may-2025.md b/docs/press-and-reports/press-and-top-links/2025/may-2025.md new file mode 100644 index 0000000000..d833dcc6ea --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2025/may-2025.md @@ -0,0 +1,8 @@ +# May 2025 + +| Article Title & Link | Media Outlet | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------- | +| 🚀 [Stacks roadmap update and STX surge](https://blockchain.news/flashnews/stacks-announces-new-roadmap-after-nakamoto-and-sbtc-launch-key-updates-for-crypto-traders-in-2025) | Blockchain News | +| 🤝 [Stacks Asia partners with HEX Trust](https://cointelegraph.com/news/stacks-asia-hex-trust-bitcoin-defi-opportunity) | Cointelegraph | +| 📈 [STX rallies 30% ahead of mainnet upgrade](https://cointelegraph.com/news/stacks-stx-makes-30-gain-as-mainnet-upgrade-and-stablecoin-launch-approach) | Cointelegraph | +| 💡 [Tech Expert Predicts $1 Million Bitcoin — 'Only One More 10x Left'](https://www.binance.com/en-IN/square/post/24043258936257) | Binance | diff --git a/docs/press-and-reports/press-and-top-links/2025/september-2025.md b/docs/press-and-reports/press-and-top-links/2025/september-2025.md new file mode 100644 index 0000000000..40690b8583 --- /dev/null +++ b/docs/press-and-reports/press-and-top-links/2025/september-2025.md @@ -0,0 +1,6 @@ +# September 2025 + +| Article Title & Link | Outlet | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | +| [LAB: Africa’s First Bitcoin Layer 2 Development Hub & Incubator](https://stacks.org/let-africa-build?ref=stacksblog) | Stacks Foundation Blog | +| [Unlocking Bitcoin's value - Will the BTCFi narrative push Stacks to $20?](https://eng.ambcrypto.com/unlocking-bitcoins-value-will-the-btcfi-narrative-push-stacks-stx-to-20/) | AMB Crypto | diff --git a/docs/reference/.gitbook.yaml b/docs/reference/.gitbook.yaml new file mode 100644 index 0000000000..b7d427cf03 --- /dev/null +++ b/docs/reference/.gitbook.yaml @@ -0,0 +1,17 @@ +root: ./ + +redirects: + + reference/api: rpc-api.md + reference/the-stack: README.md + reference/functions: clarity/functions.md + reference/keywords: clarity/keywords.md + reference/types: clarity/types.md + reference/stacks-node-configuration: stacks-node-configuration.md + reference/sample-configuration-files: signer-configuration.md + + # example-contracts redirects + example-contracts/audited-starter-contracts: clarity/example-contracts/audited-starter-contracts.md + example-contracts/bns: clarity/example-contracts/bns.md + example-contracts/multi-send: clarity/example-contracts/multi-send.md + example-contracts/stacking: clarity/example-contracts/stacking.md diff --git a/docs/reference/.gitbook/assets/Frame 316124591.jpg b/docs/reference/.gitbook/assets/Frame 316124591.jpg new file mode 100644 index 0000000000..1dabccaed3 Binary files /dev/null and b/docs/reference/.gitbook/assets/Frame 316124591.jpg differ diff --git a/docs/reference/.gitbook/assets/image (1).png b/docs/reference/.gitbook/assets/image (1).png new file mode 100644 index 0000000000..cbae820d7a Binary files /dev/null and b/docs/reference/.gitbook/assets/image (1).png differ diff --git a/docs/reference/.gitbook/assets/image (2).png b/docs/reference/.gitbook/assets/image (2).png new file mode 100644 index 0000000000..0f36a116df Binary files /dev/null and b/docs/reference/.gitbook/assets/image (2).png differ diff --git a/docs/reference/.gitbook/assets/image (3).png b/docs/reference/.gitbook/assets/image (3).png new file mode 100644 index 0000000000..62cf4ac9cd Binary files /dev/null and b/docs/reference/.gitbook/assets/image (3).png differ diff --git a/docs/reference/.gitbook/assets/image.png b/docs/reference/.gitbook/assets/image.png new file mode 100644 index 0000000000..3ce68c0c16 Binary files /dev/null and b/docs/reference/.gitbook/assets/image.png differ diff --git a/docs/reference/README.md b/docs/reference/README.md new file mode 100644 index 0000000000..9cf93e581d --- /dev/null +++ b/docs/reference/README.md @@ -0,0 +1,99 @@ +# Developer Stack + +

source: Hiro Youtube

+ +New to developing on Stacks or looking for a quick reference guide for all the important components and links? You're in the right place. + +We'll go over all the building blocks you need to be aware of to build high-quality Stacks dapps. This page exists to serve as a reference to the Stacks developer's tool chest. In addition to the tools below, stacks.co houses an index of [apps, services, and other integrations available on Stacks](https://www.stacks.co/explore/ecosystem?category=All+Teams#tools). + +### Building Blocks + +#### Clarity + +[Clarity](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/clarity/overview) is the smart contract language on Stacks. If you want to build the next decentralized social network, DeFi protocol, or any other Stacks dapp, you'll need to know Clarity. + +#### Post Conditions + +[Post conditions](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/transactions/post-conditions) are a cool feature of the Stacks blockchain that allow you to verify the legitimacy of a transaction on the client side before it is executed. This adds an additional layer of defense against malicious smart contracts. + +#### Proof of Transfer + +[PoX](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/stacks-101/proof-of-transfer) is the unique consensus mechanism of Stacks that facilitates new block production and also allows Stackers to earn real Bitcoin yield by participating in locking their STX tokens. + +#### Stacking + +Speaking of [Stacking](https://app.gitbook.com/s/H74xqoobupBWwBsVMJhK/block-production/stacking), it's the mechanism that helps to secure the Stacks chain and allows Stackers to earn real Bitcoin yield transferred by miners. + +#### SIP-009 and SIP-010 Tokens + +Fungible and non-fungible tokens in Clarity are defined by [SIP-009](file:///) and [SIP-010](https://github.com/stacksgov/sips/blob/main/sips/sip-010/sip-010-fungible-token-standard.md) standards. You can learn more about how to work with these tokens in the Clarity book. + +### Tools + +#### Wallets + +Wallets are a key tool in any web3 ecosystem, and Stacks is no different. There are several options available including: + +* [Leather](https://leather.io/) +* [Xverse](https://www.xverse.app/) +* [Asigna](https://asigna.io/) + +#### Platform + +The [Hiro Platform](https://www.hiro.so/platform) is your all-in-one cloud development environment for Stacks development, and is integrated with most of the tools listed below. + +It's by far the easiest way to get up and running quickly. Plus, they have SSH integration. + +#### Explorer + +Every developer needs a block explorer to take a look at information about blocks and transactions being submitted to the chain. You have two choices here: the [Hiro Explorer](https://explorer.hiro.so/) and [STXScan](https://stxscan.co/). + +#### API + +If you want to interact with or read data from the chain, there's a good chance the [Hiro API](https://docs.hiro.so/stacks-blockchain-api) has an endpoint for that. + +#### Stacks.js + +[Stacks.js](https://www.hiro.so/stacks-js) is the de-facto JavaScript library for the Stacks ecosystem. There are several packages here that will help you build robust frontends for your applications. + +#### Clarinet + +All good developer tooling needs a robust, easy-to-use development environment. Enter [Clarinet](https://www.hiro.so/clarinet). Clarinet provides everything you need to write, test, and deploy Clarity smart contracts, including a fully-featured local devnet blockchain. + +#### Chainhook + +One of the key use cases for Stacks is being able to directly interact with the Bitcoin chain. Hiro's [Chainhook](https://docs.hiro.so/chainhook) makes this easier by providing an IFTTT system for responding and reacting to events on both the Bitcoin and Stacks chains. + +#### Stacking Tools + +The Degen Lab team has created a [suite of tools](https://stacking.tools/) to make stacking significantly easier including a signer signature generator, a solo stacking dapp to stack without needing to run a signer, and a TypeScript library for mocking stacking functions. + +#### Oracles and Price Feeds + +DIA and Pyth provide oracle services for the Stacks layer. Find [documentation for DIA here](https://docs.diadata.org/use-nexus-product/nexus/data-delivery-usage/integrated-blockchains/stacks-price-oracles) and learn more about the [developer release of Pyth here](https://www.pyth.network/blog/developer-release-pyth-on-stacks). + +### Educational Resources + +#### Docs + +These docs you are currently looking at are a great place to get a comprehensive view of all things in the Stacks ecosystem, as well as providing some links out to additional resources you'll find helpful. + +#### Hiro Docs + +Hiro is a key player in the Stacks ecosystem, providing several developer tools to make your life easier. They also publish excellent [guides and docs](https://docs.hiro.so/) to make using these tools a breeze. + +#### Clarity Book + +The [Clarity Book](https://book.clarity-lang.org/) is the go-to resource for learning how to be a Clarity developer. In it you'll not only get the basics of Clarity but go through several practice projects and learn best practices. + +#### LearnWeb3 + +LearnWeb3 is one of the best education providers in the game. They have recently begun publishing courses as part of their [Stacks Developer Degree](https://learnweb3.io/degrees/stacks-developer-degree/). LearnWeb3 courses will teach you everything you need to know about building Stacks Dapps. + +#### EasyA + +[EasyA](https://www.easya.io/) is a mobile app with a Stacks course built in. The EasyA app allows you to learn on the go and is a great way to learn the basics of Stacks and Clarity development all directly in their app. + +#### Bitcoin Primer + +If you're new to Bitcoin, interested in how it works, and how you can build Stacks dapps that interact with it, the [Bitcoin Primer](https://start.bitcoinprimer.dev/) is a great place to start. diff --git a/docs/reference/SUMMARY.md b/docs/reference/SUMMARY.md new file mode 100644 index 0000000000..641f3d10b0 --- /dev/null +++ b/docs/reference/SUMMARY.md @@ -0,0 +1,91 @@ +# Table of contents + +* [Developer Stack](README.md) + +## Clarity + +* [Functions](clarity/functions.md) +* [Keywords](clarity/keywords.md) +* [Types](clarity/types.md) +* [Example Contracts](clarity/example-contracts/README.md) + * [multi send](clarity/example-contracts/multi-send.md) + * [audited starter contracts](clarity/example-contracts/audited-starter-contracts.md) + * [stacking](clarity/example-contracts/stacking.md) + * [bns](clarity/example-contracts/bns.md) + +## Clarinet + +* [Overview](clarinet/overview.md) +* [Quickstart](clarinet/quickstart.md) +* [Project Structure](clarinet/project-structure.md) +* [Project Development](clarinet/project-development.md) +* [Contract Interaction](clarinet/contract-interaction.md) +* [Validation and Analysis](clarinet/validation-and-analysis.md) +* [Clarity Formatter](clarinet/clarity-formatter.md) +* [Local Blockchain Development](clarinet/local-blockchain-development.md) +* [Contract Deployment](clarinet/contract-deployment.md) +* [CLI Reference](clarinet/cli-reference.md) +* [FAQ](clarinet/faq.md) + +## Clarinet JS SDK + +* [Overview](clarinet-js-sdk/overview.md) +* [Unit Testing](clarinet-js-sdk/unit-testing.md) +* [Integration Testing](clarinet-js-sdk/integration-testing.md) +* [Mainnet Execution Simulation](clarinet-js-sdk/mainnet-execution-simulation.md) +* [SDK Reference](clarinet-js-sdk/sdk-reference.md) +* [Browser SDK Reference](clarinet-js-sdk/browser-sdk-reference.md) + +## Clarinet Integrations + +* [Pyth Oracle Integration](clarinet-integrations/pyth-oracle-integration.md) +* [Chainhook Integration](clarinet-integrations/chainhook-integration.md) +* [Stacks.js Integration](clarinet-integrations/stacks.js-integration.md) +* [sBTC Integration](clarinet-integrations/sbtc-integration.md) +* [VSCode Extension](clarinet-integrations/vscode-extension.md) + +## Stacks.js + +* [Overview](stacks.js/overview.md) +* [Accounts & Addresses](stacks.js/accounts-and-addresses.md) +* [Private Keys](stacks.js/private-keys.md) +* [Networks](stacks.js/networks.md) +* [Read Only Calls](stacks.js/read-only-calls.md) +* [Build Transactions](stacks.js/build-transactions.md) +* [Contract Calls](stacks.js/contract-calls.md) +* [Contract Deployment](stacks.js/contract-deployment.md) +* [Address Validation](stacks.js/address-validation.md) +* [Encoding & Decoding](stacks.js/encoding-and-decoding.md) +* [Network Configuration](stacks.js/network-configuration.md) +* [Unit Conversion](stacks.js/unit-conversion.md) + +## Stacks Connect + +* [Connect Wallet](stacks-connect/connect-wallet.md) +* [Broadcast Transactions](stacks-connect/broadcast-transactions.md) +* [Message Signing](stacks-connect/message-signing.md) +* [Migration Guide](stacks-connect/migration-guide.md) +* [Wallet Support](stacks-connect/wallet-support.md) + +## Post-Conditions + +* [Overview](post-conditions/overview.md) +* [Implementing Post Conditions](post-conditions/implementing-post-conditions.md) + +## Stacks.js Integrations + +* [Pyth Oracle Integration](stacks.js-integrations/pyth-oracle-integration.md) +* [React Native Integration](stacks.js-integrations/react-native-integration.md) + +## Stacks.js References + +* [@stacks/network](stacks.js-references/stacks-network.md) +* [@stacks/connect](stacks.js-references/stacks-connect.md) +* [@stacks/transactions](stacks.js-references/stacks-transactions.md) +* [sbtc](stacks.js-references/sbtc.md) + +*** + +* [RPC-API](rpc-api.md) +* [Stacks Node Configuration](stacks-node-configuration.md) +* [Signer Configuration](signer-configuration.md) diff --git a/docs/reference/clarinet-integrations/chainhook-integration.md b/docs/reference/clarinet-integrations/chainhook-integration.md new file mode 100644 index 0000000000..ea73e10808 --- /dev/null +++ b/docs/reference/clarinet-integrations/chainhook-integration.md @@ -0,0 +1,160 @@ +# Chainhook Integration + +Learn how to register Chainhooks on Clarinet devnet so you can monitor smart contract events during local development. + +## What you'll learn + +* Create Chainhook predicate files for event monitoring +* Register Chainhooks with Clarinet devnet +* Monitor contract calls and blockchain events +* Set up webhooks for real-time notifications + +{% hint style="info" %} +Prerequisites + +* Clarinet `2.1.0` or later (`clarinet --version`) +* Node.js `16` or later (`node --version`) +{% endhint %} + +## Quickstart + +{% stepper %} +{% step %} +### Create your Chainhook predicates + +Create predicate files in a `chainhooks/` directory alongside your contracts: + +* contracts/ + * counter.clar +* chainhooks/ + * increment.json + * decrement.json +* tests/ + * counter.test.ts +* Clarinet.toml + +Example predicate for monitoring increment events: + +{% code title="chainhooks/increment.json" %} +```json +{ + "chain": "stacks", + "uuid": "increment-hook", + "name": "Increment Counter Hook", + "version": 1, + "networks": { + "devnet": { + "if_this": { + "scope": "contract_call", + "contract_identifier": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter", + "method": "increment" + }, + "then_that": { + "http_post": { + "url": "http://localhost:3000/api/increment", + "authorization_header": "Bearer my-secret" + } + } + } + } +} +``` +{% endcode %} +{% endstep %} + +{% step %} +### Start devnet with Chainhooks + +From your project root, start devnet. Clarinet registers every predicate automatically: + +{% code title="Start devnet" %} +```bash +clarinet devnet start +``` +{% endcode %} + +Check the logs for a confirmation message such as: + +{% code title="Clarinet log" %} +``` +INFO Feb 5 15:20:07.233382 2 chainhooks registered +``` +{% endcode %} +{% endstep %} + +{% step %} +### Monitor Chainhook activity + +Trigger contract actions and watch for Chainhook alerts: + +{% code title="Clarinet log" %} +``` +INFO Feb 5 15:21:07.233382 1 hooks triggered +``` +{% endcode %} + +Verify the payload based on your `then_that` configuration: + +* `http_post` – confirm your endpoint received the POST request +* `file_append` – ensure the file was created or updated +{% endstep %} +{% endstepper %} + +## Common patterns + +### Contract deployment hook + +Monitor when specific contracts are deployed: + +{% code title="chainhooks/deploy.json" %} +```json +{ + "chain": "stacks", + "uuid": "deploy-hook", + "name": "Contract Deploy Monitor", + "version": 1, + "networks": { + "devnet": { + "if_this": { + "scope": "contract_deployment", + "deployer": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }, + "then_that": { + "file_append": { + "path": "./deployments.log" + } + } + } + } +} +``` +{% endcode %} + +### STX transfer monitoring + +Track STX transfers above a certain threshold: + +{% code title="chainhooks/stx-transfer.json" %} +```json +{ + "chain": "stacks", + "uuid": "stx-transfer-hook", + "name": "Large STX Transfer Monitor", + "version": 1, + "networks": { + "devnet": { + "if_this": { + "scope": "stx_event", + "actions": ["transfer"], + "amount_upper_bound": "1000000000000" + }, + "then_that": { + "http_post": { + "url": "http://localhost:3000/api/large-transfer" + } + } + } + } +} +``` +{% endcode %} diff --git a/docs/reference/clarinet-integrations/pyth-oracle-integration.md b/docs/reference/clarinet-integrations/pyth-oracle-integration.md new file mode 100644 index 0000000000..fbb78fb06e --- /dev/null +++ b/docs/reference/clarinet-integrations/pyth-oracle-integration.md @@ -0,0 +1,328 @@ +# Pyth Oracle Integration + +### What you'll learn + +* Set up mainnet contract dependencies +* Use mainnet transaction execution (MXS) for oracle testing +* Create mock price feeds for unit tests +* Write integration tests with real oracle data + +### Prerequisites + +* Clarinet version 2.1.0 or higher with MXS support +* Understanding of [Pyth oracle contracts](pyth-oracle-integration.md#) + +### Quickstart + +#### Configure mainnet dependencies + +Add Pyth oracle contracts to your `Clarinet.toml` requirements: + +```toml +[project] +name = "my-oracle-project" + +[[project.requirements]] +contract_id = "SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3" + +[[project.requirements]] +contract_id = "SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3" + +[[project.requirements]] +contract_id = "SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-pnau-decoder-v2" + +[[project.requirements]] +contract_id = "SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.wormhole-core-v3" +``` + +#### Create a mock oracle for unit tests + +Use a lightweight mock contract for fast tests: + +```clarity +(define-map price-feeds + (buff 32) + { + price: int, + conf: uint, + expo: int, + ema-price: int, + ema-conf: uint, + publish-time: uint, + prev-publish-time: uint + } +) + +(define-public (set-mock-price (feed-id (buff 32)) (price int) (expo int)) + (ok (map-set price-feeds feed-id { + price: price, + conf: u1000000, + expo: expo, + ema-price: price, + ema-conf: u1000000, + publish-time: block-height, + prev-publish-time: (- block-height u1) + }))) +) + +(define-read-only (get-price (feed-id (buff 32)) (storage-contract principal)) + (ok (unwrap! (map-get? price-feeds feed-id) (err u404))) +) + +(define-public (verify-and-update-price-feeds (vaa (buff 8192)) (params (tuple))) + (ok u1) +) +``` + +#### Write unit tests with mocked prices + +Example Vitest suite using the mock oracle: + +```ts +import { Cl } from '@stacks/transactions'; +import { describe, expect, it } from 'vitest'; + +const accounts = simnet.getAccounts(); +const deployer = accounts.get('deployer')!; +const wallet1 = accounts.get('wallet_1')!; + +describe('Benjamin Club with Mock Oracle', () => { + it('sets up mock BTC price', () => { + const btcFeedId = '0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43'; + const mockPrice = Cl.int(10_000_000_000_000); + const expo = Cl.int(-8); + + const response = simnet.callPublicFn( + 'mock-pyth-oracle', + 'set-mock-price', + [Cl.bufferFromHex(btcFeedId), mockPrice, expo], + deployer + ); + + expect(response.result).toBeOk(Cl.uint(1)); + }); + + it('calculates required sBTC amount for $100', () => { + const response = simnet.callReadOnlyFn( + 'benjamin-club', + 'get-required-sbtc-amount', + [], + wallet1 + ); + + expect(response.result).toBeOk(Cl.uint(100_000)); + }); + + it('mints NFT when user has sufficient sBTC', () => { + const mockVaa = Cl.bufferFromHex('00'.repeat(100)); + + simnet.callPublicFn( + 'sbtc-token', + 'mint', + [Cl.uint(100_000_000), Cl.principal(wallet1)], + deployer + ); + + const response = simnet.callPublicFn( + 'benjamin-club', + 'mint-for-hundred-dollars', + [mockVaa], + wallet1 + ); + + expect(response.result).toBeOk(Cl.uint(1)); + }); +}); +``` + +#### Set up mainnet execution tests + +Use mainnet execution (MXS) with live Pyth data: + +```ts +import { buildDevnetNetworkOrchestrator } from '@hirosystems/clarinet-sdk'; +import { Cl } from '@stacks/transactions'; +import { PriceServiceConnection } from '@pythnetwork/price-service-client'; +import { describe, expect, it } from 'vitest'; + +describe('Benjamin Club with Mainnet Oracle', () => { + it('interacts with the real Pyth oracle', async () => { + const orchestrator = buildDevnetNetworkOrchestrator({ + manifest: './Clarinet.toml', + networkId: 1, + }); + + const client = new PriceServiceConnection( + 'https://hermes.pyth.network', + { priceFeedRequestConfig: { binary: true } } + ); + + const btcFeedId = 'e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43'; + const [vaa] = await client.getLatestVaas([btcFeedId]); + const vaaHex = Buffer.from(vaa, 'base64').toString('hex'); + + const response = await orchestrator.callPublicFn( + 'benjamin-club', + 'get-current-btc-price', + [Cl.bufferFromHex(vaaHex)], + 'wallet_1' + ); + + expect(response.result).toBeOk(); + }); +}); +``` + +### Common patterns + +#### Testing different price scenarios + +Create helper functions to test various market conditions: + +```ts +export function setupPriceScenario( + scenario: 'bull' | 'bear' | 'stable', + simnet: any +) { + const prices = { + bull: { + btc: 15000000000000, // $150,000 + stx: 500000000, // $5.00 + eth: 500000000000 // $5,000 + }, + bear: { + btc: 2000000000000, // $20,000 + stx: 50000000, // $0.50 + eth: 100000000000 // $1,000 + }, + stable: { + btc: 10000000000000, // $100,000 + stx: 200000000, // $2.00 + eth: 300000000000 // $3,000 + } + }; + + const selectedPrices = prices[scenario]; + + // Set up all price feeds + Object.entries(selectedPrices).forEach(([asset, price]) => { + const feedId = getFeedId(asset); + simnet.callPublicFn( + "mock-pyth-oracle", + "set-mock-price", + [Cl.bufferFromHex(feedId), Cl.int(price), Cl.int(-8)], + "deployer" + ); + }); +} +``` + +#### Testing price staleness + +Ensure your contract handles stale prices correctly: + +```ts +describe("Price Staleness Handling", () => { + it("should reject stale prices", () => { + // Set a price with old timestamp + const oldTimestamp = simnet.getBlockHeight() - 1000; + + simnet.callPublicFn( + "mock-pyth-oracle", + "set-price-with-timestamp", + [ + Cl.bufferFromHex(BTC_FEED_ID), + Cl.int(10000000000000), + Cl.int(-8), + Cl.uint(oldTimestamp) + ], + deployer + ); + + const response = simnet.callPublicFn( + "benjamin-club", + "mint-for-hundred-dollars", + [mockVaa], + wallet1 + ); + + expect(response.result).toBeErr(Cl.uint(ERR_STALE_PRICE)); + }); +}); +``` + +#### Deployment testing + +Create deployment tests to ensure proper configuration: + +```yaml +--- +id: 0 +name: "Mainnet deployment" +network: mainnet +stacks-node: "https://api.mainnet.hiro.so" +bitcoin-node: "https://blockstream.info/api/" +plan: + batches: + - id: 0 + transactions: + - contract-call: + contract-id: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R.benjamin-club + method: initialize + parameters: + - "'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3" + - "'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3" +``` + +#### Gas estimation + +Test gas consumption for oracle operations: + +```ts +describe("Gas Estimation", () => { + it("should estimate gas for oracle update + mint", () => { + const txCost = simnet.getTransactionCost( + "benjamin-club", + "mint-for-hundred-dollars", + [Cl.bufferFromHex("00".repeat(1000))], // Typical VAA size + wallet1 + ); + + console.log("Estimated gas cost:", txCost); + + // Ensure gas cost is reasonable + expect(txCost.write_length).toBeLessThan(20000); + expect(txCost.runtime).toBeLessThan(50000000); + }); +}); +``` + +### CI/CD integration + +Set up GitHub Actions for automated testing: + +```yaml +name: Test Oracle Integration + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install Clarinet + run: | + curl -L https://github.com/hirosystems/clarinet/releases/download/v2.1.0/clarinet-linux-x64.tar.gz | tar xz + chmod +x ./clarinet + sudo mv ./clarinet /usr/local/bin + + - name: Run unit tests + run: clarinet test --coverage + + - name: Run mainnet fork tests + run: clarinet test --mainnet-fork + env: + MAINNET_API_KEY: ${{ secrets.MAINNET_API_KEY }} +``` diff --git a/docs/reference/clarinet-integrations/sbtc-integration.md b/docs/reference/clarinet-integrations/sbtc-integration.md new file mode 100644 index 0000000000..2163a56442 --- /dev/null +++ b/docs/reference/clarinet-integrations/sbtc-integration.md @@ -0,0 +1,223 @@ +# sBTC Integration + +Clarinet can automatically wire up the official sBTC contracts so you can build and test SIP-010 flows locally. + +## What you'll learn + +* Add sBTC smart contracts to your Clarinet project +* Test contracts with automatic sBTC funding in devnet +* Work with sBTC as a SIP-010 fungible token +* Deploy sBTC contracts to testnet and mainnet + +## Prerequisites + +* Clarinet 2.15.0 or later required for automatic sBTC integration. + +## Quickstart + +{% stepper %} +{% step %} +### Add sBTC to your project + +Add the sBTC contracts to your project requirements: + +```bash +clarinet requirements add SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-deposit +``` + +This pulls in: + +* `sbtc-token` – SIP-010 fungible token contract +* `sbtc-registry` – configuration registry +* `sbtc-deposit` – deposit and withdrawal logic + +Clarinet auto-funds devnet wallets with sBTC when these are present. +{% endstep %} + +{% step %} +### Create an sBTC-enabled contract + +Example NFT marketplace that accepts sBTC payments: + +```clarity +(define-non-fungible-token marketplace-nft uint) +(define-data-var mint-price uint u100) +(define-data-var next-id uint u0) + +(define-public (mint-with-sbtc) + (begin + (try! (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token transfer + (var-get mint-price) + tx-sender + (as-contract tx-sender) + none)) + (try! (nft-mint? marketplace-nft (var-get next-id) tx-sender)) + (ok (var-set next-id (+ (var-get next-id) u1))) + ) +) + +(define-read-only (get-sbtc-balance (owner principal)) + (contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token get-balance owner) +) +``` +{% endstep %} + +{% step %} +### Test in the Clarinet console + +Launch the console and try the contract using auto-funded wallets: + +```bash +clarinet console +``` + +```clarity +(contract-call? .nft-marketplace get-sbtc-balance tx-sender) +(contract-call? .nft-marketplace mint-with-sbtc) +(nft-get-owner? .nft-marketplace marketplace-nft u0) +``` +{% endstep %} + +{% step %} +### Write unit tests + +Sample Vitest test for sBTC payments: + +```ts +import { describe, expect, it } from 'vitest'; +import { Cl } from '@stacks/transactions'; + +describe('NFT Marketplace', () => { + it('mints NFT with sBTC payment', () => { + const accounts = simnet.getAccounts(); + const wallet1 = accounts.get('wallet_1')!; + + const initial = simnet.callReadOnlyFn( + 'nft-marketplace', + 'get-sbtc-balance', + [Cl.standardPrincipal(wallet1.address)], + wallet1.address + ); + + const mint = simnet.callPublicFn( + 'nft-marketplace', + 'mint-with-sbtc', + [], + wallet1 + ); + + expect(mint.result).toBeOk(); + + const final = simnet.callReadOnlyFn( + 'nft-marketplace', + 'get-sbtc-balance', + [Cl.standardPrincipal(wallet1.address)], + wallet1.address + ); + + expect(Number(Cl.parse(final.result))).toBeLessThan( + Number(Cl.parse(initial.result)) + ); + }); +}); +``` +{% endstep %} + +{% step %} +### Deploy to testnet + +Generate a plan to confirm remapped addresses for official sBTC contracts: + +```bash +clarinet deployments generate --testnet +``` + +Deploy when ready: + +```bash +clarinet deployments apply --testnet +``` +{% endstep %} +{% endstepper %} + +## Common patterns + +### Working with sBTC addresses + +Clarinet handles sBTC contract address mapping across networks: + +| Network | sBTC Contract Address | +| ------------- | ------------------------------------------------------ | +| Simnet/Devnet | `SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token` | +| Testnet | `ST1F7QA2MDF17S807EPA36TSS8AMEFY4KA9TVGWXT.sbtc-token` | +| Mainnet | Contract address remains unchanged | + +Your contract code always references the simnet address. Clarinet automatically remaps during deployment. + +## Manual sBTC minting in unit tests + +While Clarinet 2.15.0+ automatically funds wallets with sBTC in devnet, you may need to manually mint sBTC in unit tests for specific scenarios. + +### Minting sBTC using the deployer address + +The sBTC token contract allows the deployer (multisig) address to mint tokens. Use this approach in your tests: + +```ts +import { describe, expect, it } from "vitest"; +import { Cl } from "@stacks/transactions"; + +describe("Manual sBTC minting", () => { + it("mints sBTC to custom addresses", () => { + // The sBTC multisig address that can mint + const sbtcDeployer = "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4"; + const customWallet = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM"; + + // Mint 1000 sats to custom wallet + const mintResult = simnet.callPublicFn( + "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token", + "mint", + [ + Cl.uint(1000), // amount in sats + Cl.principal(customWallet) // recipient + ], + sbtcDeployer // sender must be deployer + ); + + expect(mintResult.result).toBeOk(Cl.bool(true)); + + // Verify balance + const balance = simnet.callReadOnlyFn( + "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token", + "get-balance", + [Cl.principal(customWallet)], + customWallet + ); + + expect(balance.result).toBeOk(Cl.uint(1000)); + }); +}); +``` + +### Testing with mainnet execution simulation + +When using mainnet execution simulation, you can mint sBTC using the actual mainnet multisig: + +```ts +const mainnetMultisig = "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4"; +const mainnetWallet = "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR"; + +// Mint sBTC to any mainnet address +simnet.callPublicFn( + "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token", + "mint", + [Cl.uint(100000), Cl.principal(mainnetWallet)], + mainnetMultisig +); +``` + +This approach is useful for: + +* Testing specific sBTC amounts +* Simulating different wallet balances +* Testing edge cases with precise token amounts +* Integration testing with mainnet contracts diff --git a/docs/reference/clarinet-integrations/stacks.js-integration.md b/docs/reference/clarinet-integrations/stacks.js-integration.md new file mode 100644 index 0000000000..89b685dfcc --- /dev/null +++ b/docs/reference/clarinet-integrations/stacks.js-integration.md @@ -0,0 +1,212 @@ +# Stacks.js Integration + +Use Stacks.js to interact with your Clarinet devnet from JavaScript applications. + +## What you'll learn + +* Configure Stacks.js for a local devnet connection +* Make STX transfers between devnet accounts +* Call smart contract functions from JavaScript +* Deploy contracts programmatically + +## Quickstart + +{% stepper %} +{% step %} +### Install Stacks.js packages + +Add the required libraries to your frontend project: + +```bash +npm install @stacks/transactions @stacks/network +``` +{% endstep %} + +{% step %} +### Configure for devnet + +Create a network helper: + +```ts +import { StacksDevnet } from '@stacks/network'; + +export const devnet = new StacksDevnet({ + url: 'http://localhost:3999' +}); + +export const accounts = { + deployer: { + address: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + key: 'cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01' + }, + wallet1: { + address: 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5', + key: '7287ba251d44a4d3fd9276c88ce34c5c52a038955511cccaf77e61068649c17801' + } +}; +``` +{% endstep %} + +{% step %} +### Test STX transfers + +Send a transfer between devnet accounts: + +```ts +import { makeSTXTokenTransfer, broadcastTransaction, AnchorMode } from '@stacks/transactions'; +import { devnet, accounts } from './devnet-config'; + +async function transferSTX() { + const tx = await makeSTXTokenTransfer({ + amount: 1_000_000n, + recipient: accounts.wallet1.address, + senderKey: accounts.deployer.key, + network: devnet, + anchorMode: AnchorMode.Any, + }); + + const result = await broadcastTransaction(tx, devnet); + console.log('Transaction ID:', result.txid); +} + +transferSTX().catch(console.error); +``` + +Run the transfer with `ts-node stx-transfer.ts`. +{% endstep %} + +{% step %} +### Call smart contracts + +Interact with contracts deployed on devnet: + +```ts +import { makeContractCall, broadcastTransaction, AnchorMode } from '@stacks/transactions'; +import { devnet, accounts } from './devnet-config'; + +async function callContract() { + const tx = await makeContractCall({ + contractAddress: accounts.deployer.address, + contractName: 'counter', + functionName: 'increment', + functionArgs: [], + senderKey: accounts.wallet1.key, + network: devnet, + anchorMode: AnchorMode.Any, + }); + + await broadcastTransaction(tx, devnet); +} + +async function readCount() { + const response = await fetch( + `${devnet.coreApiUrl}/v2/contracts/call-read/${accounts.deployer.address}/counter/get-count`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + sender: accounts.wallet1.address, + arguments: [] + }) + } + ); + const data = await response.json(); + console.log(data); +} + +callContract().catch(console.error); +readCount().catch(console.error); +``` +{% endstep %} + +{% step %} +### Deploy contracts programmatically + +Deploy a contract from your application code: + +```ts +import { makeContractDeploy, broadcastTransaction, AnchorMode } from '@stacks/transactions'; +import { devnet, accounts } from './devnet-config'; +import counterContract from './contracts/counter.clar?raw'; + +async function deployCounter() { + const tx = await makeContractDeploy({ + contractName: 'counter', + codeBody: counterContract, + senderKey: accounts.deployer.key, + network: devnet, + anchorMode: AnchorMode.Any, + }); + + return broadcastTransaction(tx, devnet); +} + +deployCounter().catch(console.error); +``` +{% endstep %} +{% endstepper %} + +## Common patterns + +### Watching for transaction confirmation + +Monitor when transactions are confirmed on devnet: + +```ts +async function waitForTransaction(txid: string) { + let attempts = 0; + const maxAttempts = 10; + + while (attempts < maxAttempts) { + const response = await fetch( + `${devnet.coreApiUrl}/extended/v1/tx/${txid}` + ); + + const tx = await response.json(); + + if (tx.tx_status === 'success') { + console.log('Transaction confirmed!'); + return tx; + } + + if (tx.tx_status === 'abort_by_response') { + throw new Error(`Transaction failed: ${tx.tx_result.repr}`); + } + + // Wait for next block + await new Promise(resolve => setTimeout(resolve, 5000)); + attempts++; + } + + throw new Error('Transaction timeout'); +} +``` + +### Post conditions for safety + +Add post conditions to ensure contract calls behave as expected: + +```ts +import { Pc } from '@stacks/transactions'; + +const postConditions = [ + // Ensure sender's balance decreases by exactly the amount + Pc.principal(accounts.wallet1.address) + .willSendEq(1000000n) + .ustx(), + + // Ensure recipient receives the amount + Pc.principal(accounts.deployer.address) + .willReceiveEq(1000000n) + .ustx() +]; + +const tx = await makeSTXTokenTransfer({ + amount: 1000000n, + recipient: accounts.deployer.address, + senderKey: accounts.wallet1.key, + network: devnet, + postConditions, + anchorMode: AnchorMode.Any, +}); +``` diff --git a/docs/reference/clarinet-integrations/vscode-extension.md b/docs/reference/clarinet-integrations/vscode-extension.md new file mode 100644 index 0000000000..ade1bccfba --- /dev/null +++ b/docs/reference/clarinet-integrations/vscode-extension.md @@ -0,0 +1,116 @@ +# VSCode Extension + +## Features + +### Smart auto-completion + +The extension provides intelligent code completion that understands Clarity's context. When you start typing any Clarity function, you get instant suggestions with documentation: + +```clarity +;; Type "stx-tr" and get: +(stx-transfer? amount sender recipient) +;; ^ ^ ^ +;; Placeholders for easy navigation +``` + +Use `Tab` to jump between placeholders and `Escape` to exit placeholder mode. + +### Documentation on hover + +Access comprehensive documentation without leaving your editor. Hover over any Clarity function or keyword to see: + +```clarity +;; Hover over 'map-set' to see: +;; - Function signature +;; - Parameter descriptions +;; - Return type +;; - Usage examples +(map-set my-map {key: "value"} "data") +``` + +### Go-to definition + +Navigate your codebase efficiently with jump-to-definition features: + +* `F12` or `Ctrl+Click` - Go to definition +* `Alt+F12` - Peek definition without leaving current file +* Works across contract files and contract calls + +### Real-time error checking + +The extension validates your code continuously, providing immediate feedback: + +* **Red squiggles** - Syntax errors, unknown keywords +* **Yellow squiggles** - Warnings for potentially unsafe code +* **Error list** - All issues in the Problems panel (`Ctrl+Shift+M`) + +Common errors caught include undefined variables, type mismatches, missing trait implementations, and invalid function signatures. + +### Local contract resolution + +Auto-completion works across your entire project. Reference functions from other contracts in your workspace: + +```clarity +;; Auto-complete local contract calls +(contract-call? .my-token transfer amount sender recipient) +;; ^ +;; Suggests contracts in your project +``` + +### Trait support + +When implementing traits (like SIP-009 NFTs or SIP-010 tokens), the extension verifies: + +* All required functions are implemented +* Function signatures match trait definitions +* Return types are correct + +```clarity +;; Extension warns if missing required trait functions +(impl-trait .sip-010-trait.sip-010-trait) + +;; ⚠️ Warning: Missing required function 'get-balance' +``` + +### Visual debugging + +{% stepper %} +{% step %} +### Set breakpoints + +Set breakpoints by clicking line numbers in the editor. +{% endstep %} + +{% step %} +### Start debugging + +Press `F5` or use Run → Start Debugging to begin a debugging session. +{% endstep %} + +{% step %} +### Step through code + +Step through code line-by-line to follow execution. +{% endstep %} + +{% step %} +### Inspect state + +Inspect variables and stack state while paused at breakpoints. +{% endstep %} +{% endstepper %} + +{% hint style="warning" %} +Visual debugging requires VS Code Desktop and Clarinet installed locally. +{% endhint %} + +## Comparison table + +| Feature | Basic Editor | VS Code Extension | +| --------------------- | --------------- | ----------------------- | +| Syntax highlighting | Limited | Full Clarity support | +| Auto-completion | None | Context-aware with docs | +| Error checking | On deploy only | Real-time validation | +| Documentation | External lookup | Inline hover docs | +| Debugging | Console only | Visual debugger | +| Cross-file navigation | Manual | Jump to definition | diff --git a/docs/reference/clarinet-js-sdk/browser-sdk-reference.md b/docs/reference/clarinet-js-sdk/browser-sdk-reference.md new file mode 100644 index 0000000000..e9f354c611 --- /dev/null +++ b/docs/reference/clarinet-js-sdk/browser-sdk-reference.md @@ -0,0 +1,122 @@ +# Browser SDK Reference + +The browser build of the Clarinet SDK lets you interact with simnet directly from web experiences, so you can run Clarity tests without standing up a Node.js server. + +## Installation + +{% code title="Install" %} +```bash +npm install @hirosystems/clarinet-sdk-browser +``` +{% endcode %} + +## Usage + +The browser SDK implements the same API as the Node.js Clarinet SDK. All methods, properties, and custom matchers work identically. + +### Empty session + +{% code title="Empty session (TypeScript)" %} +```ts +import { initSimnet } from '@hirosystems/clarinet-sdk-browser'; + +const simnet = await initSimnet(); +await simnet.initEmptySession(); + +// Execute Clarity code directly +const result = simnet.runSnippet("(+ 1 2)"); +console.log(result); // 3 +``` +{% endcode %} + +### With a Clarinet project + +For testing with an existing Clarinet project using a virtual file system: + +{% code title="Using a Clarinet project (TypeScript)" %} +```ts +import { initSimnet } from '@hirosystems/clarinet-sdk-browser'; + +const simnet = await initSimnet(); +await simnet.initSession("/project", "Clarinet.toml"); + +// Your contracts are now available +const count = simnet.getDataVar('counter', 'count'); +``` +{% endcode %} + +{% hint style="info" %} +Virtual file system + +Using a Clarinet project in the browser requires setting up a virtual file system. Documentation and examples for this advanced use case are coming soon. +{% endhint %} + +## Common use cases + +### Interactive contract playground + +{% code title="Playground example (TypeScript)" %} +```ts +import { initSimnet } from '@hirosystems/clarinet-sdk-browser'; +import { Cl } from '@stacks/transactions'; + +// Initialize simnet +const simnet = await initSimnet(); +await simnet.initEmptySession(); + +// Deploy a simple contract +const sourceCode = ` +(define-data-var counter uint u0) + +(define-public (increment) + (ok (var-set counter (+ (var-get counter) u1)))) + +(define-read-only (get-counter) + (ok (var-get counter))) +`; + +simnet.deployContract('counter', sourceCode, null, simnet.deployer); + +// Interact with the contract +simnet.callPublicFn('counter', 'increment', [], simnet.deployer); +const count = simnet.callReadOnlyFn('counter', 'get-counter', [], simnet.deployer); +console.log(count.result); // (ok u1) +``` +{% endcode %} + +### Testing in browser-based IDEs + +{% code title="Browser test example (TypeScript + Vitest)" %} +```ts +import { initSimnet } from '@hirosystems/clarinet-sdk-browser'; +import { expect } from 'vitest'; + +const simnet = await initSimnet(); +await simnet.initEmptySession(); + +// Run tests directly in the browser +test('counter increments correctly', () => { + simnet.deployContract('counter', counterCode, null, simnet.deployer); + + const result = simnet.callPublicFn('counter', 'increment', [], simnet.deployer); + expect(result.result).toBeOk(Cl.uint(1)); + + const count = simnet.getDataVar('counter', 'counter'); + expect(count).toBeUint(1); +}); +``` +{% endcode %} + +## Browser compatibility + +The browser SDK works in all modern browsers that support: + +* ES2020+ JavaScript features +* WebAssembly +* Dynamic imports + +Tested browsers include: + +* Chrome/Edge 90+ +* Firefox 89+ +* Safari 15+ diff --git a/docs/reference/clarinet-js-sdk/integration-testing.md b/docs/reference/clarinet-js-sdk/integration-testing.md new file mode 100644 index 0000000000..c40ea69d32 --- /dev/null +++ b/docs/reference/clarinet-js-sdk/integration-testing.md @@ -0,0 +1,207 @@ +# Integration Testing + +Integration testing connects multiple contracts and workflows to ensure the entire system behaves correctly. The Clarinet JS SDK lets you simulate complex scenarios and interactions across contracts. + +## What you'll learn + +* Set up integration tests for multi-step workflows +* Test contract interactions and dependencies +* Simulate real-world scenarios +* Verify system-wide state changes + +## Set up your project + +Create a new Clarinet project and install dependencies: + +```bash +clarinet new stx-defi +cd stx-defi +npm install +``` + +## Create the enhanced contract + +We'll use an enhanced DeFi contract that supports both deposits and borrowing. Create the contract: + +```bash +clarinet contract new defi +``` + +Replace `contracts/defi.clar` with this enhanced version: + +```clarity +;; Error constants +(define-constant err-overborrow (err u300)) + +;; Interest rate (10%) +(define-data-var loan-interest-rate uint u10) + +;; Total deposits in the contract +(define-data-var total-deposits uint u0) + +;; User deposits +(define-map deposits { owner: principal } { amount: uint }) + +;; User loans +(define-map loans principal { amount: uint, last-interaction-block: uint }) + +;; Deposit STX into the contract +(define-public (deposit (amount uint)) + (let + ( + (current-balance (default-to u0 (get amount (map-get? deposits { owner: tx-sender })))) + ) + (try! (stx-transfer? amount tx-sender (as-contract tx-sender))) + (map-set deposits { owner: tx-sender } { amount: (+ current-balance amount) }) + (var-set total-deposits (+ (var-get total-deposits) amount)) + (ok true) + ) +) + +;; Borrow STX based on deposits (up to 50% of deposit) +(define-public (borrow (amount uint)) + (let + ( + (user-deposit (default-to u0 (get amount (map-get? deposits { owner: tx-sender })))) + (allowed-borrow (/ user-deposit u2)) + (current-loan-details (default-to { amount: u0, last-interaction-block: u0 } + (map-get? loans tx-sender))) + (accrued-interest (calculate-accrued-interest + (get amount current-loan-details) + (get last-interaction-block current-loan-details))) + (total-due (+ (get amount current-loan-details) (unwrap-panic accrued-interest))) + (new-loan (+ amount)) + ) + (asserts! (<= new-loan allowed-borrow) err-overborrow) + (try! (as-contract (stx-transfer? amount tx-sender tx-sender))) + (map-set loans tx-sender { amount: new-loan, last-interaction-block: block-height }) + (ok true) + ) +) + +;; Get user's balance +(define-read-only (get-balance-by-sender) + (ok (map-get? deposits { owner: tx-sender })) +) + +;; Get amount owed including interest +(define-read-only (get-amount-owed) + (let + ( + (current-loan-details (default-to { amount: u0, last-interaction-block: u0 } + (map-get? loans tx-sender))) + (accrued-interest (calculate-accrued-interest + (get amount current-loan-details) + (get last-interaction-block current-loan-details))) + (total-due (+ (get amount current-loan-details) (unwrap-panic accrued-interest))) + ) + (ok total-due) + ) +) + +;; Calculate interest +(define-private (calculate-accrued-interest (principal uint) (start-block uint)) + (let + ( + (elapsed-blocks (- block-height start-block)) + (interest (/ (* principal (var-get loan-interest-rate) elapsed-blocks) u10000)) + ) + (asserts! (not (is-eq start-block u0)) (ok u0)) + (ok interest) + ) +) +``` + +Run `clarinet check` to ensure your contract is valid: + +```bash +clarinet check +``` + +## Write integration tests + +Create a test that simulates a complete user workflow - depositing and then borrowing: + +```ts +import { describe, it, expect } from 'vitest'; +import { Cl } from '@stacks/transactions'; + +const accounts = simnet.getAccounts(); +const wallet1 = accounts.get('wallet_1')!; + +describe('stx-defi integration', () => { + it('allows deposit and borrow workflow', () => { + // Step 1: User deposits STX + const depositResult = simnet.callPublicFn( + 'defi', + 'deposit', + [Cl.uint(1000)], + wallet1 + ); + expect(depositResult.result).toBeOk(Cl.bool(true)); + + // Verify deposit was recorded + const totalDeposits = simnet.getDataVar('defi', 'total-deposits'); + expect(totalDeposits).toBeUint(1000); + + // Step 2: User borrows against deposit + const borrowResult = simnet.callPublicFn( + 'defi', + 'borrow', + [Cl.uint(400)], // Borrowing 40% of deposit + wallet1 + ); + expect(borrowResult.result).toBeOk(Cl.bool(true)); + + // Step 3: Check amount owed + const { result } = simnet.callReadOnlyFn( + 'defi', + 'get-amount-owed', + [], + wallet1 + ); + expect(result).toBeOk(Cl.uint(400)); // No interest yet at same block + }); + + it('prevents over-borrowing', () => { + // Setup: deposit first + simnet.callPublicFn('defi', 'deposit', [Cl.uint(1000)], wallet1); + + // Attempt to borrow more than allowed (>50%) + const borrowResult = simnet.callPublicFn( + 'defi', + 'borrow', + [Cl.uint(600)], + wallet1 + ); + + // Should fail with err-overborrow + expect(borrowResult.result).toBeErr(Cl.uint(300)); + }); +}); +``` + +Key aspects used in the test: + +* callPublicFn - Simulates calling public functions just as on the actual blockchain +* getDataVar - Retrieves the value of contract data variables +* callReadOnlyFn - Calls read-only functions without modifying state +* Custom matchers - toBeOk() and toBeErr() validate Clarity response types + +## Try it out + +Run your integration tests: + +```bash +npm run test +``` + +## Common issues + +| Issue | Solution | +| ----------------------------- | ------------------------------------------------------ | +| State pollution between tests | Each test runs in isolation - state doesn't carry over | +| Timing issues | Use `simnet.mineEmptyBlocks()` to advance block height | +| Complex assertions | Break down into smaller, focused tests | + +## diff --git a/docs/reference/clarinet-js-sdk/mainnet-execution-simulation.md b/docs/reference/clarinet-js-sdk/mainnet-execution-simulation.md new file mode 100644 index 0000000000..0dc79b6f6a --- /dev/null +++ b/docs/reference/clarinet-js-sdk/mainnet-execution-simulation.md @@ -0,0 +1,157 @@ +# Mainnet Execution Simulation + +Mainnet execution simulation (MXS) lets you test your Clarity contracts against real mainnet data without deploying experimental code. You can reproduce historical state, validate complex integrations, and debug edge cases while keeping the speed of local development. + +## What you'll learn + +* Set up MXS in a Clarinet project +* Write tests that interact with mainnet contracts +* Simulate historical transactions +* Understand MXS limitations + +## What is Mainnet execution simulation? + +Testing smart contracts in realistic conditions is essential. Simnet offers an isolated environment but lacks the live Stacks mainnet's complexity and history. + +MXS fills this gap by enabling unit tests with the Clarinet JS SDK and Vitest to simulate the Stacks mainnet state at a specific block height. This allows you to: + +* **Validate contract logic with real data:** Directly test mainnet contracts or data within your tests. +* **(Re)simulate transactions:** Analyze mainnet transactions' results, execution, or costs without deploying or using actual STX. + +## Enable MXS in your project + +Add the following configuration to your `Clarinet.toml` file: + +```toml +[repl.remote_data] + +# Enable mainnet execution simulation +enabled = true + +# Specify the Stacks block height to fork from +initial_height = 522000 + +# API URL (optional, defaults to https://api.hiro.so) +api_url = 'https://api.hiro.so' +``` + +{% hint style="info" %} +Pro tip + +Set a specific `initial_height` to keep tests reproducible. +{% endhint %} + +## Using mainnet addresses + +When testing contracts that check or require mainnet addresses, set `use_mainnet_wallets = true`. This enables your simnet tests to use mainnet addresses (SP/SM) instead of testnet addresses (ST). + +```toml +[repl.remote_data] +enabled = true +initial_height = 522000 +use_mainnet_wallets = true # !mark +``` + +This is particularly useful when: + +* Testing against mainnet-only contracts like DEX protocols +* Your contract includes [`(is-standard standard-or-contract-principal)`](mainnet-execution-simulation.md#) validation +* Simulating transactions that require mainnet address formats + +## Configure API access + +While MXS works without an API key, you may encounter rate limits. Set up an API key for reliable access: + +```bash +export HIRO_API_KEY="" +``` + +## Write tests with mainnet data + +Once MXS is enabled, your tests automatically operate against the mainnet state snapshot. Here's an example testing against the mainnet `pox-4` contract: + +```ts +import { describe, it, expect } from "vitest"; +import { Cl } from "@stacks/transactions"; + +const accounts = simnet.getAccounts(); +const deployer = accounts.get("deployer")!; + +describe("pox-4 mainnet interaction", () => { + it("reads current reward cycle from mainnet", () => { + // Call the mainnet pox-4 contract + const call = simnet.callReadOnlyFn( + "SP000000000000000000002Q6VF78.pox-4", // Mainnet contract + "current-pox-reward-cycle", + [], + deployer + ); + + // Assert the result (adjust based on your initial_height) + expect(call.result).toBeUint(109); + + console.log("Current POX reward cycle:", Cl.prettyPrint(call.result)); + }); +}); +``` + +The test uses `simnet.callReadOnlyFn` just like in standard unit tests, but because MXS is enabled, it targets the actual `pox-4` contract state at the specified block height. + +## Try it out + +Run your test to see MXS in action: + +```bash +npm run test +``` + +## Common issues + +
+ +Rate limit errors + +Solution: Set up the `HIRO_API_KEY` environment variable. + +```bash +export HIRO_API_KEY="" +``` + +
+ +
+ +Inconsistent results + +Solution: Fix `initial_height` in configuration so tests are run against a reproducible block snapshot. + +
+ +
+ +Function not found + +Solution: Check the contract exists at your block height. + +
+ +## Testing in the playground + +{% stepper %} +{% step %} +### Visit the playground + +Go to: https://play.hiro.so/?remote\_data=true +{% endstep %} + +{% step %} +### Run a mainnet contract call + +Example Clarity call: + +```clarity +> contract-call? 'SP000000000000000000002Q6VF78.pox-4 current-pox-reward-cycle +``` +{% endstep %} +{% endstepper %} + diff --git a/docs/reference/clarinet-js-sdk/overview.md b/docs/reference/clarinet-js-sdk/overview.md new file mode 100644 index 0000000000..3cde0dd562 --- /dev/null +++ b/docs/reference/clarinet-js-sdk/overview.md @@ -0,0 +1,274 @@ +--- +description: Practical guide to testing smart contracts with the Clarinet JS SDK. +--- + +# Overview + +

source: Hiro blog

+ +The Clarinet JS SDK provides a powerful testing framework for Clarity smart contracts. It integrates with Vitest to let you run comprehensive tests against a simulated blockchain environment. + +## Initial setup + +Create a new Node.js project (or reuse an existing one): + +```bash +npm init -y +``` + +Install the Clarinet JS SDK and its dependencies: + +```bash +npm install @hirosystems/clarinet-sdk vitest @stacks/transactions +``` + +## Project structure + +Organize your project so contracts and tests live together: + +``` +- my-project/ + - contracts/ + - counter.clar + - tests/ + - counter.test.ts + - Clarinet.toml + - package.json + - tsconfig.json + - vitest.config.js +``` + +## Simple test + +Create `tests/counter.test.ts` to verify the contract: + +{% code title="tests/counter.test.ts" %} +```ts +import { describe, expect, it } from "vitest"; +import { Cl } from "@stacks/transactions"; + +const accounts = simnet.getAccounts(); +const wallet = accounts.get("wallet_1")!; + +describe("counter contract", () => { + it("increments the count", () => { + const countUpCall = simnet.callPublicFn("counter", "count-up", [], wallet); + expect(countUpCall.result).toBeOk(Cl.bool(true)); + }); +}); +``` +{% endcode %} + +The `simnet` object is automatically available and exposes a simulated Stacks blockchain. + +## Configuration options + +### Clarinet configuration + +Define your contracts in `Clarinet.toml`: + +```toml +[project] +name = "my-project" + +[contracts.counter] +path = "contracts/counter.clar" +``` + +### TypeScript setup + +Configure TypeScript for the SDK: + +```json +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "types": ["vitest/globals"] + }, + "include": ["tests/**/*.ts"], + "exclude": ["node_modules"] +} +``` + +### Vitest configuration + +Set up Vitest so the SDK can bootstrap the testing environment: + +```js +import { defineConfig } from "vitest/config"; +import { vitestSetupFilePath } from "@hirosystems/clarinet-sdk/vitest"; + +export default defineConfig({ + test: { + environment: "node", + globals: true, + setupFiles: [vitestSetupFilePath], + }, +}); +``` + +{% hint style="info" %} +Include `vitestSetupFilePath` in `setupFiles` so the SDK can prepare the `simnet` instance before tests run. +{% endhint %} + +### Package scripts + +Add convenient test scripts to `package.json`: + +```json +"scripts": { + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage" +} +``` + +## Common patterns + +### Testing read-only functions + +Use `callReadOnlyFn` for functions that do not modify state: + +```ts +const getCountCall = simnet.callReadOnlyFn( + "counter", + "get-count", + [Cl.principal(wallet)], + wallet +); +expect(getCountCall.result).toBeUint(1); +``` + +### Testing public functions with parameters + +Pass parameters with the appropriate Clarity helpers: + +```ts +const depositCall = simnet.callPublicFn( + "defi", + "deposit", + [Cl.uint(1000)], + wallet +); +expect(depositCall.result).toBeOk(Cl.bool(true)); +``` + +### Accessing contract state + +Inspect data variables and maps directly: + +```ts +const totalDeposits = simnet.getDataVar("defi", "total-deposits"); +expect(totalDeposits).toBeUint(1000); + +const balance = simnet.getMapEntry("defi", "balances", Cl.principal(wallet)); +expect(balance).toBeUint(1000); +``` + +## Examples + +### Testing contract deployment + +Ensure the contract was deployed: + +```ts +it("ensures the contract is deployed", () => { + const contractSource = simnet.getContractSource("counter"); + expect(contractSource).toBeDefined(); +}); +``` + +### Testing error conditions + +Verify error handling logic: + +```ts +it("fails when borrowing too much", () => { + const borrowCall = simnet.callPublicFn( + "defi", + "borrow", + [Cl.uint(10000)], // Amount exceeds allowed + wallet + ); + expect(borrowCall.result).toBeErr(Cl.uint(300)); // err-overborrow +}); +``` + +### Testing with multiple accounts + +Simulate cross-account interactions: + +```ts +const wallet1 = accounts.get("wallet_1")!; +const wallet2 = accounts.get("wallet_2")!; + +// Wallet 1 deposits +simnet.callPublicFn("defi", "deposit", [Cl.uint(1000)], wallet1); + +// Wallet 2 tries to withdraw wallet 1's funds (should fail) +const withdrawCall = simnet.callPublicFn( + "defi", + "withdraw", + [Cl.uint(1000)], + wallet2 +); +expect(withdrawCall.result).toBeErr(Cl.uint(401)); // err-unauthorized +``` + +## Running tests + +Execute the full suite: + +```bash +npm test +``` + +Generate coverage reports: + +```bash +npm run test:coverage +``` + +Coverage output includes cost analysis for your contract functions. + +## Advanced usage + +### Using the SDK in existing projects + +If your project has a custom structure, keep contracts and tests together and point the SDK to the manifest: + +``` +- my-app/ + - blockchain/ + - contracts/ + - token.clar + - tests/ + - token.test.ts + - Clarinet.toml + - frontend/ + - App.tsx + - package.json + - vitest.config.js +``` + +Update `vitest.config.js` to reference the correct manifest path: + +```js +export default defineConfig({ + test: { + environment: "node", + globals: true, + setupFiles: [vitestSetupFilePath], + env: { + CLARINET_MANIFEST_PATH: "./blockchain/Clarinet.toml" + } + }, +}); +``` + +## diff --git a/docs/reference/clarinet-js-sdk/sdk-reference.md b/docs/reference/clarinet-js-sdk/sdk-reference.md new file mode 100644 index 0000000000..d743be66d3 --- /dev/null +++ b/docs/reference/clarinet-js-sdk/sdk-reference.md @@ -0,0 +1,614 @@ +# SDK Reference + +The Clarinet JS SDK provides a comprehensive suite of helpers for testing and interacting with Clarity smart contracts. From simnet initialization to contract deployment, the SDK streamlines your entire testing workflow. + +* Initialize a simulated network: `initSimnet` +* Manage contract state: `getDataVar`, `getMapEntry` +* Call contract functions: `callReadOnlyFn`, `callPublicFn` +* Transfer STX: `transferSTX` +* Deploy contracts: `deployContract` +* Mine blocks: `mineBlock`, `mineEmptyBlock` +* Custom assertions: `toBeOk`, `toBeErr` + +## Installation + +```bash +npm install @hirosystems/clarinet-sdk +``` + +## Initialize simulated network + +### initSimnet + +`initSimnet` initializes a simulated network for testing your smart contracts. + +Usage: + +``` +initSimnet(manifestPath?: string): Promise +``` + +```ts +import { initSimnet } from '@hirosystems/clarinet-sdk'; + +const simnet = await initSimnet(); +``` + +| Parameter | Type | Description | +| -------------- | -------- | -------------------------------------------- | +| `manifestPath` | `string` | Optional path to Clarinet.toml manifest file | + +## Simnet properties + +### blockHeight + +Returns the current block height of simnet. + +```ts +const currentBlockHeight = simnet.blockHeight; +// Returns: 1 +``` + +### deployer + +Returns the default deployer address as defined in the project file. + +```ts +const deployerAddress = simnet.deployer; +// Returns: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM +``` + +You can also update the deployer: + +```ts +simnet.deployer = 'ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5'; +``` + +### currentEpoch + +Returns the current epoch of simnet (e.g., 2.5 for Stacks 2.5). + +```ts +const epoch = simnet.currentEpoch; +// Returns: 2.5 +``` + +## Account management + +### getAccounts + +`getAccounts` retrieves all configured Stacks addresses including wallets, deployers, and faucets. + +Usage: + +``` +getAccounts(): Map +``` + +```ts +const accounts = simnet.getAccounts(); +const wallet1 = accounts.get('wallet_1')!; +// Returns: ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +``` + +## Asset balances + +### getAssetsMap + +`getAssetsMap` retrieves asset balances for all addresses, including STX, fungible, and non-fungible tokens. + +Usage: + +``` +getAssetsMap(): Map> +``` + +```ts +const assets = simnet.getAssetsMap(); +const stxBalances = assets.get('STX')!; +const deployerBalance = stxBalances.get(simnet.deployer)!; +// Returns: 100000000000000n +``` + +## Read contract state + +### getDataVar + +`getDataVar` retrieves the value of a data variable from a contract. + +Usage: + +``` +getDataVar(contract: string, dataVar: string): ClarityValue +``` + +```ts +const count = simnet.getDataVar('counter', 'count'); +// Returns: { type: 1, value: 1n } +``` + +| Parameter | Type | Description | +| ---------- | -------- | ------------------------- | +| `contract` | `string` | Contract identifier | +| `dataVar` | `string` | Name of the data variable | + +### getMapEntry + +`getMapEntry` retrieves a value from a contract map by its key. + +Usage: + +``` +getMapEntry(contract: string, mapName: string, mapKey: ClarityValue): ClarityValue +``` + +```ts +import { Cl } from '@stacks/transactions'; + +const hasParticipated = simnet.getMapEntry( + "pool", + "Participants", + Cl.standardPrincipal(wallet) +); +// Returns: { type: 10, value: { type: 3 } } +``` + +| Parameter | Type | Description | +| ---------- | -------------- | ------------------- | +| `contract` | `string` | Contract identifier | +| `mapName` | `string` | Name of the map | +| `mapKey` | `ClarityValue` | Key to look up | + +## Call contract functions + +### callReadOnlyFn + +`callReadOnlyFn` calls read-only functions without mining a block. + +Usage: + +``` +callReadOnlyFn( + contract: string, + method: string, + args: ClarityValue[], + sender: string +): ParsedTransactionResult +``` + +```ts +import { Cl } from '@stacks/transactions'; + +const result = simnet.callReadOnlyFn( + 'pool', + 'get-contribution-amount', + [Cl.standardPrincipal(wallet)], + wallet +); +// Returns: { result: { type: 1, value: 42000000n }, events: [] } +``` + +| Parameter | Type | Description | +| ---------- | ---------------- | ------------------- | +| `contract` | `string` | Contract identifier | +| `method` | `string` | Function name | +| `args` | `ClarityValue[]` | Function arguments | +| `sender` | `string` | Sender address | + +### callPublicFn + +`callPublicFn` calls public functions and mines a block. + +Usage: + +``` +callPublicFn( + contract: string, + method: string, + args: ClarityValue[], + sender: string +): ParsedTransactionResult +``` + +```ts +import { Cl } from '@stacks/transactions'; + +const result = simnet.callPublicFn( + 'pool', + 'register-participant', + [Cl.standardPrincipal(wallet)], + wallet +); +// Mines block and returns result +``` + +### callPrivateFn + +`callPrivateFn` calls private functions (testing only) and mines a block. + +Usage: + +``` +callPrivateFn( + contract: string, + method: string, + args: ClarityValue[], + sender: string +): ParsedTransactionResult +``` + +```ts +const result = simnet.callPrivateFn( + "pool", + "reward-participant-points", + [Cl.standardPrincipal(address1)], + wallet +); +``` + +## Transfer STX + +`transferSTX` transfers STX between addresses and mines a block. + +Usage: + +``` +transferSTX( + amount: number | bigint, + recipient: string, + sender: string +): ParsedTransactionResult +``` + +```ts +const transfer = simnet.transferSTX( + 42000000, // 42 STX in microSTX + recipient, + simnet.deployer +); +// Returns transaction result with transfer event +``` + +| Parameter | Type | Description | +| ----------- | ------------------ | ------------------ | +| `amount` | `number \| bigint` | Amount in microSTX | +| `recipient` | `string` | Recipient address | +| `sender` | `string` | Sender address | + +## Deploy contracts + +`deployContract` deploys a new contract to simnet and mines a block. + +Usage: + +``` +deployContract( + name: string, + content: string, + options: DeployContractOptions | null, + sender: string +): ParsedTransactionResult +``` + +```ts +const sourceCode = '(define-read-only (say-hi) (ok "Hello World"))'; + +const contract = simnet.deployContract( + 'hello-world', + sourceCode, + { clarityVersion: 2 }, + simnet.deployer +); +``` + +| Parameter | Type | Description | +| --------- | ---------------- | ------------------- | +| `name` | `string` | Contract name | +| `content` | `string` | Clarity source code | +| `options` | `object \| null` | Deployment options | +| `sender` | `string` | Deployer address | + +## Block mining + +### mineBlock + +`mineBlock` mines a block with multiple transactions. + +Usage: + +``` +mineBlock(txs: Tx[]): ParsedTransactionResult[] +``` + +```ts +import { tx } from '@hirosystems/clarinet-sdk'; +import { Cl } from '@stacks/transactions'; + +const block = simnet.mineBlock([ + tx.callPublicFn("counter", "increment", [], simnet.deployer), + tx.transferSTX(19000000, wallet, simnet.deployer), +]); +``` + +### mineEmptyBlock + +`mineEmptyBlock` mines an empty block and increases block height. + +Usage: + +``` +mineEmptyBlock(): number +``` + +```ts +simnet.mineEmptyBlock(); +const newHeight = simnet.blockHeight; +// Returns: 2 +``` + +### mineEmptyBlocks + +`mineEmptyBlocks` mines multiple empty blocks. + +Usage: + +``` +mineEmptyBlocks(count?: number): number +``` + +```ts +simnet.mineEmptyBlocks(5); +const newHeight = simnet.blockHeight; +// Returns: 6 +``` + +## Utility methods + +### runSnippet + +`runSnippet` executes arbitrary Clarity code without deploying. + +Usage: + +``` +runSnippet(snippet: string): string | ClarityValue +``` + +```ts +const result = simnet.runSnippet('(stx-account tx-sender)'); +// Returns account balance information +``` + +### getContractsInterfaces + +`getContractsInterfaces` returns contract interfaces with function signatures and storage. + +Usage: + +``` +getContractsInterfaces(): Map +``` + +```ts +const interfaces = simnet.getContractsInterfaces(); +const poolInterface = interfaces.get(`${simnet.deployer}.pool`); +// Returns contract interface with functions, maps, variables +``` + +### getContractSource + +`getContractSource` retrieves the source code of a deployed contract. + +Usage: + +``` +getContractSource(contract: string): string | undefined +``` + +```ts +const source = simnet.getContractSource('pool'); +// Returns Clarity source code as string +``` + +### getContractAST + +`getContractAST` returns the Abstract Syntax Tree of a contract. + +Usage: + +``` +getContractAST(contractId: string): ContractAST +``` + +```ts +const ast = simnet.getContractAST('pool'); +// Returns parsed AST structure +``` + +## Custom matchers + +The SDK provides Vitest matchers for Clarity value assertions. + +### Response matchers + +#### toBeOk + +Asserts that a response is `(ok )`. + +```ts +expect(result).toBeOk(Cl.uint(1)); +``` + +#### toBeErr + +Asserts that a response is `(err )`. + +```ts +expect(result).toBeErr(Cl.uint(500)); +``` + +#### toBeSome + +Asserts that a response is `(some )`. + +```ts +expect(result).toBeSome(Cl.bool(true)); +``` + +#### toBeNone + +Asserts that a response is `(none)`. + +```ts +expect(result).toBeNone(); +``` + +### Value matchers + +#### toBeBool + +Asserts a boolean value. + +```ts +expect(result).toBeBool(true); +``` + +#### toBeInt + +Asserts a signed integer value. + +```ts +expect(result).toBeInt(1); // or 1n +``` + +#### toBeUint + +Asserts an unsigned integer value. + +```ts +expect(result).toBeUint(1); // or 1n +``` + +#### toBeAscii + +Asserts a string-ascii value. + +```ts +expect(result).toBeAscii('Hello World'); +``` + +#### toBeUtf8 + +Asserts a string-utf8 value. + +```ts +expect(result).toBeUtf8('Hello World'); +``` + +#### toBePrincipal + +Asserts a principal value. + +```ts +expect(Cl.standardPrincipal(deployer)).toBePrincipal(deployer); +``` + +#### toBeBuff + +Asserts a buffer value. + +```ts +const buffer = Uint8Array.from([1, 2, 3, 4]); +expect(result).toBeBuff(buffer); +``` + +#### toBeList + +Asserts a list of Clarity values. + +```ts +expect(result).toBeList([ + Cl.standardPrincipal('ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM'), + Cl.standardPrincipal('ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5') +]); +``` + +#### toBeTuple + +Asserts a tuple value. + +```ts +expect(result).toBeTuple({ + enrollmentBlock: Cl.some(Cl.uint(1)), + contributionAmount: Cl.some(Cl.uint(19000000)) +}); +``` + +### Type checking + +#### toHaveClarityType + +Checks that a value has the expected Clarity type. + +```ts +expect(result).toHaveClarityType(ClarityType.ResponseOk); +``` + +### Event matchers + +#### toContainEqual + +Asserts that an events array contains a specific event. This is useful for checking transaction events. + +```ts +// STX transfer event +expect(events).toContainEqual({ + event: "stx_transfer_event", + data: { + amount: "1000000", + memo: "", + recipient: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + sender: "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5", + }, +}); + +// Fungible token transfer event +expect(events).toContainEqual({ + event: "ft_transfer_event", + data: { + amount: "1000", + asset_identifier: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token::my-token", + recipient: recipientAddress, + sender: senderAddress, + }, +}); + +// NFT transfer event +expect(events).toContainEqual({ + event: "nft_transfer_event", + data: { + asset_identifier: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.nft::my-nft", + value: Cl.serialize(Cl.uint(1)), + recipient: newOwner, + sender: previousOwner, + }, +}); + +// Print event +expect(events).toContainEqual({ + event: "print_event", + data: { + contract_id: `${deployer}.my-contract`, + value: Cl.serialize(Cl.tuple({ message: Cl.stringAscii("Hello") })), + }, +}); + +// Check only specific properties with objectContaining +expect(events).toContainEqual({ + event: "stx_transfer_event", + data: expect.objectContaining({ + sender: senderAddress, + recipient: recipientAddress, + }), +}); +``` diff --git a/docs/reference/clarinet-js-sdk/unit-testing.md b/docs/reference/clarinet-js-sdk/unit-testing.md new file mode 100644 index 0000000000..cd70286f24 --- /dev/null +++ b/docs/reference/clarinet-js-sdk/unit-testing.md @@ -0,0 +1,195 @@ +# Unit Testing + +Unit testing verifies that individual contract functions behave as expected. The Clarinet JS SDK pairs with Vitest to help you catch bugs early in development. + +## What you'll learn + +* Set up a Clarinet project for unit testing +* Write tests for public and read-only functions +* Handle error conditions and edge cases +* Run tests and generate coverage reports + +{% stepper %} +{% step %} +### Set up your project + +Create a new Clarinet project and install dependencies: + +```bash +clarinet new stx-defi +cd stx-defi +npm install +``` +{% endstep %} + +{% step %} +### Create the contract + +Generate a contract stub: + +```bash +clarinet contract new defi +``` + +Replace `contracts/defi.clar` with the following implementation: + +```clarity +;; Holds the total amount of deposits in the contract +(define-data-var total-deposits uint u0) + +;; Maps a user's principal address to their deposited amount +(define-map deposits { owner: principal } { amount: uint }) + +;; Public function for users to deposit STX into the contract +(define-public (deposit (amount uint)) + (let + ( + ;; Fetch the current balance or default to 0 if none exists + (current-balance (default-to u0 (get amount (map-get? deposits { owner: tx-sender })))) + ) + ;; Transfer the STX from sender to contract + (try! (stx-transfer? amount tx-sender (as-contract tx-sender))) + ;; Update the user's deposit amount in the map + (map-set deposits { owner: tx-sender } { amount: (+ current-balance amount) }) + ;; Update the total deposits variable + (var-set total-deposits (+ (var-get total-deposits) amount)) + ;; Return success + (ok true) + ) +) + +;; Read-only function to get the balance by tx-sender +(define-read-only (get-balance-by-sender) + (ok (map-get? deposits { owner: tx-sender })) +) +``` + +Validate the contract: + +```bash +clarinet check +``` +{% endstep %} + +{% step %} +### Write your unit test + +Create `tests/defi.test.ts` with a basic happy-path test: + +```ts +import { describe, it, expect } from 'vitest'; +import { Cl } from '@stacks/transactions'; + +const accounts = simnet.getAccounts(); +const wallet1 = accounts.get('wallet_1')!; + +describe('stx-defi', () => { + it('allows users to deposit STX', () => { + // Define the amount to deposit + const amount = 1000; + + // Call the deposit function + const deposit = simnet.callPublicFn('defi', 'deposit', [Cl.uint(amount)], wallet1); + + // Assert the deposit was successful + expect(deposit.result).toBeOk(Cl.bool(true)); + + // Verify the contract's total deposits + const totalDeposits = simnet.getDataVar('defi', 'total-deposits'); + expect(totalDeposits).toBeUint(amount); + + // Check the user's balance + const balance = simnet.callReadOnlyFn('defi', 'get-balance-by-sender', [], wallet1); + expect(balance.result).toBeOk( + Cl.some( + Cl.tuple({ + amount: Cl.uint(amount), + }) + ) + ); + }); +}); +``` + +{% hint style="info" %} +Key calls: + +* `simnet.callPublicFn` – executes public functions +* `expect(...).toBeOk()` – asserts a Clarity `ok` response +* `simnet.getDataVar` – reads a contract data variable +* `Cl.*` helpers – build Clarity values in JavaScript +{% endhint %} +{% endstep %} + +{% step %} +### Try it out + +Run the tests: + +```bash +$ npm run test + PASS tests/defi.test.ts + stx-defi + allows users to deposit STX (5 ms) + +Test Files 1 passed (1) + Tests 1 passed (1) +``` +{% endstep %} + +{% step %} +### Generate coverage reports + +```bash +npm run test:report +``` + +This command produces: + +* `lcov.info` – code coverage data +* `costs-reports.json` – gas cost analysis + +View the HTML report (macOS example): + +```bash +$ brew install lcov +$ genhtml lcov.info --branch-coverage -o coverage +$ open coverage/index.html +``` +{% endstep %} +{% endstepper %} + +## Common issues + +| Issue | Solution | +| -------------------------- | --------------------------------------------------------------------- | +| `toBeOk is not a function` | Ensure the Clarinet SDK matchers are loaded via `vitestSetupFilePath` | +| `Contract not found` | Re-run `clarinet check` to compile and register contracts | +| Type errors | Use the `Cl` helpers to construct Clarity values | + +## Advanced testing patterns + +Test error conditions: + +```ts +it('fails when depositing zero', () => { + const deposit = simnet.callPublicFn('defi', 'deposit', [Cl.uint(0)], wallet1); + expect(deposit.result).toBeErr(Cl.uint(100)); // err-invalid-amount +}); +``` + +Test with multiple accounts: + +```ts +const wallet2 = accounts.get('wallet_2')!; + +it('tracks deposits from multiple users', () => { + simnet.callPublicFn('defi', 'deposit', [Cl.uint(1000)], wallet1); + simnet.callPublicFn('defi', 'deposit', [Cl.uint(2000)], wallet2); + + const total = simnet.getDataVar('defi', 'total-deposits'); + expect(total).toBeUint(3000); +}); +``` + +## diff --git a/docs/reference/clarinet/clarity-formatter.md b/docs/reference/clarinet/clarity-formatter.md new file mode 100644 index 0000000000..da22310d10 --- /dev/null +++ b/docs/reference/clarinet/clarity-formatter.md @@ -0,0 +1,151 @@ +# Clarity Formatter + +The Clarity formatter automatically shapes your smart contract code to follow standardized style rules. Consistent formatting improves readability and makes collaboration easier across teams. + +## Formatting philosophy + +The formatter applies an opinionated standard designed to make Clarity code more readable: + +* **Line length** – wraps lines at 80 characters by default +* **Indentation** – uses two spaces for consistency +* **Structure** – enforces consistent patterns for functions, let bindings, and control flow + +You can customize these defaults to match your preferences. + +### Integration points + +{% stepper %} +{% step %} +### Clarity VS Code Extension + +Format directly in the editor. +{% endstep %} + +{% step %} +### Clarinet CLI + +Format via command line, including entire projects. +{% endstep %} +{% endstepper %} + +## Comparison table + +| Aspect | Manual formatting | Clarity formatter | +| ----------------- | ---------------------- | --------------------------- | +| Consistency | Varies by developer | Uniform across the codebase | +| Speed | Time-consuming | Instant | +| Error-prone | Yes | No | +| Team coordination | Requires a style guide | Automatic enforcement | + +## Best practices + +* **Format on save** – enable automatic formatting in VS Code +* **Pre-commit hooks** – ensure code is formatted before commits +* **Team adoption** – share consistent settings with your team + +## Formatting rules in detail + +### Function definitions + +Functions span multiple lines with consistent indentation: + +```clarity +(define-public (my-func + (amount uint) + (sender principal) + ) + (ok true) +) +``` + +Single arguments can remain on the first line: + +```clarity +(define-read-only (get-balance (who principal)) + (ok u0) +) +``` + +### Let expressions + +Bindings are placed on separate lines with consistent indentation: + +```clarity +(let ( + (a u1) + (b u2) +) + (body-expression) +) +``` + +### Control flow (if, match) + +Each branch receives its own line: + +```clarity +(if condition + (then-expression) + (else-expression) +) + +(match optional-value + value (handle-some value) + (handle-none) +) +``` + +### Tuples and maps + +The formatter automatically converts to sugared syntax with proper formatting: + +```clarity +;; Input: (tuple (n1 u1) (n2 u2)) +;; Output: +{ + n1: u1, + n2: u2, +} +``` + +## Usage examples + +### VS Code integration + +```json +// settings.json +"[clarity]": { + "editor.formatOnSave": true +} +``` + +### CLI usage + +```bash +clarinet format --in-place +``` + +Format with custom settings: + +```bash +clarinet format -i 4 -l 120 --in-place +``` + +Check formatting in CI/CD pipelines: + +```bash +clarinet format --check +``` + +The `--check` flag validates that all Clarity files are properly formatted without changing them, which is ideal for continuous integration workflows. + +## Ignoring blocks of code + +Prevent formatting for specific code blocks: + +```clarity +;; @format-ignore +(define-constant something (list + 1 2 3 ;; Preserves custom spacing + 4 5 )) +``` diff --git a/docs/reference/clarinet/cli-reference.md b/docs/reference/clarinet/cli-reference.md new file mode 100644 index 0000000000..60850f5aa7 --- /dev/null +++ b/docs/reference/clarinet/cli-reference.md @@ -0,0 +1,411 @@ +# CLI Reference + +The Clarinet CLI provides a comprehensive suite of tools for Clarity smart contract development. From project initialization to deployment, Clarinet streamlines your entire development workflow. + +* Create a new project: `clarinet new` +* Generate a new smart contract: `clarinet contracts new` +* Validate contract syntax and types: `clarinet check` +* Interactive REPL for testing contracts: `clarinet console` +* Launch a local development network: `clarinet devnet start` +* Manage deployments: `clarinet deployments` + +## Initialize a new project + +### clarinet new + +`clarinet new` creates a new project with all necessary configuration files and directory structure. + +Usage + +``` +clarinet new [OPTIONS] +``` + +```bash +$ clarinet new my-defi-protocol +Create directory my-defi-protocol +Create directory contracts +Create directory settings +Create directory tests +Create file Clarinet.toml +Create file settings/Mainnet.toml +Create file settings/Testnet.toml +Create file settings/Devnet.toml +Create directory .vscode +Create file .vscode/settings.json +Create file .vscode/tasks.json +Create file .gitignore +Create file .gitattributes +Create file package.json +Create file tsconfig.json +Create file vitest.config.js +``` + +| Option | Description | +| --------------------- | --------------------------------------------------------- | +| `--disable-telemetry` | Do not provide developer usage telemetry for this project | + +## Manage your contracts + +### clarinet contracts + +`clarinet contracts` is a subcommand for working with contracts. It has two subcommands: + +| Command | Description | +| ------- | ---------------------------------------------- | +| `new` | Generate files and settings for a new contract | +| `rm` | Remove files and settings for a contract | + +Usage with `new` + +``` +clarinet contracts new +``` + +```bash +$ clarinet contracts new fungible-token +Created file contracts/fungible-token.clar +Created file tests/fungible-token.test.ts +Updated Clarinet.toml +``` + +Usage with `rm` + +``` +clarinet contracts rm +``` + +```bash +$ clarinet contracts rm old-token +Removed file contracts/old-token.clar +Removed file tests/old-token.test.ts +Updated Clarinet.toml +``` + +| Option | Description | +| ------------------------ | --------------------- | +| `--manifest-path ` | Path to Clarinet.toml | + +## Validate your contracts + +### clarinet check + +`clarinet check` checks contracts syntax and performs type checking. + +Usage + +``` +clarinet check [FILE] [OPTIONS] +``` + +```bash +clarinet check +✔ 3 contracts checked +clarinet check contracts/token.clar +✔ contracts/token.clar syntax checks passed +``` + +| Option | Short | Description | +| -------------------------------- | ----- | ------------------------------------------------------------------------------------- | +| `--manifest-path ` | `-m` | Path to Clarinet.toml | +| `--deployment-plan-path ` | `-p` | If specified, use this deployment file | +| `--use-on-disk-deployment-plan` | `-d` | Use on disk deployment plan (prevent updates computing) | +| `--use-computed-deployment-plan` | `-c` | Use computed deployment plan (will overwrite on disk version if any update) | +| `--enable-clarity-wasm` | | Allow the Clarity Wasm preview to run in parallel with the Clarity interpreter (beta) | + +## Interact with your contracts in a local REPL + +### clarinet console + +`clarinet console` loads contracts in a REPL for an interactive session. + +Usage + +``` +clarinet console [OPTIONS] +``` + +```bash +$ clarinet console +clarity-repl v1.0.0 +Enter ".help" for usage hints. +Connected to a transient in-memory database. +``` + +The Clarinet console offers a variety of commands for contract interaction: + +* `::help`: Lists all console commands +* `::functions`: Display all the native functions available in Clarity +* `::keywords`: Display all the native keywords available in Clarity +* `::describe | `: Display documentation for a given native function or keyword +* `::toggle_costs`: Display cost analysis after every expression +* `::toggle_timings`: Display the execution duration +* `::mint_stx `: Mint STX balance for a given principal +* `::set_tx_sender `: Set tx-sender variable to principal +* `::get_assets_maps`: Get assets maps for active accounts +* `::get_contracts`: Get contracts +* `::get_block_height`: Get current block height +* `::advance_chain_tip `: Simulate mining of `` blocks +* `::advance_stacks_chain_tip `: Simulate mining of `` stacks blocks +* `::advance_burn_chain_tip `: Simulate mining of `` burnchain blocks +* `::set_epoch `: Update the current epoch +* `::get_epoch`: Get current epoch +* `::debug `: Start an interactive debug session executing `` +* `::trace `: Generate an execution trace for `` +* `::get_costs `: Display the cost analysis +* `::reload`: Reload the existing contract(s) in the session +* `::read `: Read expressions from a file +* `::encode `: Encode an expression to a Clarity Value bytes representation +* `::decode `: Decode a Clarity Value bytes representation + +| Option | Short | Description | +| --------------------------------------- | ----- | ------------------------------------------------------------------------------------- | +| `--manifest-path ` | `-m` | Path to Clarinet.toml | +| `--deployment-plan-path ` | `-p` | If specified, use this deployment file | +| `--use-on-disk-deployment-plan` | `-d` | Use on disk deployment plan (prevent updates computing) | +| `--use-computed-deployment-plan` | `-c` | Use computed deployment plan (will overwrite on disk version if any update) | +| `--enable-remote-data` | `-r` | Enable remote data fetching from mainnet or a testnet | +| `--remote-data-api-url ` | `-a` | Set a custom Stacks Blockchain API URL for remote data fetching | +| `--remote-data-initial-height ` | `-b` | Initial remote Stacks block height | +| `--enable-clarity-wasm` | | Allow the Clarity Wasm preview to run in parallel with the Clarity interpreter (beta) | + +## Start a local development network + +### clarinet devnet + +`clarinet devnet` is a subcommand for working with Devnet. It has two subcommands: + +| Command | Description | +| --------- | ---------------------------------------------------------------- | +| `start` | Start a local Devnet network for interacting with your contracts | +| `package` | Generate package of all required devnet artifacts | + +Usage with `start` + +``` +clarinet devnet start [OPTIONS] +``` + +```bash +clarinet devnet start +``` + +| Option | Short | Description | +| -------------------------------- | ----- | --------------------------------------------------------------------------- | +| `--manifest-path ` | `-m` | Path to Clarinet.toml | +| `--no-dashboard` | | Display streams of logs instead of terminal UI dashboard | +| `--deployment-plan-path ` | `-p` | If specified, use this deployment file | +| `--use-on-disk-deployment-plan` | `-d` | Use on disk deployment plan (prevent updates computing) | +| `--use-computed-deployment-plan` | `-c` | Use computed deployment plan (will overwrite on disk version if any update) | +| `--package ` | | Path to Package.json produced by 'clarinet devnet package' | + +Usage with `package` + +``` +clarinet devnet package [OPTIONS] +``` + +```bash +$ clarinet devnet package --name my-devnet +Packaging devnet artifacts... +Created file my-devnet.json +``` + +| Option | Short | Description | +| ------------------------ | ----- | --------------------- | +| `--name ` | `-n` | Output json file name | +| `--manifest-path ` | `-m` | Path to Clarinet.toml | + +## Manage your deployments + +### clarinet deployments + +`clarinet deployments` is a subcommand for managing deployments on Devnet/Testnet/Mainnet. + +| Command | Description | +| ---------- | ------------------------ | +| `check` | Check deployments format | +| `generate` | Generate new deployment | +| `apply` | Apply deployment | + +Usage with `check` + +``` +clarinet deployments check [OPTIONS] +``` + +```bash +$ clarinet deployments check +✔ Deployment files are valid +``` + +| Option | Description | +| ------------------------ | --------------------- | +| `--manifest-path ` | Path to Clarinet.toml | + +Usage with `generate` + +``` +clarinet deployments generate [OPTIONS] +``` + +```bash +$ clarinet deployments generate --testnet +Generated deployment plan +Created file deployments/default.testnet-plan.yaml +``` + +| Option | Description | +| ------------------------ | ----------------------------------------------------------------------------- | +| `--simnet` | Generate a deployment file for simnet environments (console, tests) | +| `--devnet` | Generate a deployment file for devnet, using settings/Devnet.toml | +| `--testnet` | Generate a deployment file for testnet, using settings/Testnet.toml | +| `--mainnet` | Generate a deployment file for mainnet, using settings/Mainnet.toml | +| `--manifest-path ` | Path to Clarinet.toml | +| `--no-batch` | Generate a deployment file without trying to batch transactions (simnet only) | +| `--low-cost` | Compute and set cost, using low priority (network connection required) | +| `--medium-cost` | Compute and set cost, using medium priority (network connection required) | +| `--high-cost` | Compute and set cost, using high priority (network connection required) | +| `--manual-cost` | Leave cost estimation manual | + +Usage with `apply` + +``` +clarinet deployments apply [OPTIONS] +``` + +```bash +$ clarinet deployments apply --testnet +Applying deployment to testnet +✔ Broadcasting transaction for token.clar + Transaction ID: 0x3d4f5... + Contract: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token +✔ All contracts deployed successfully +``` + +| Option | Short | Description | +| -------------------------------- | ----- | --------------------------------------------------------------------------- | +| `--devnet` | | Apply default deployment settings/default.devnet-plan.toml | +| `--testnet` | | Apply default deployment settings/default.testnet-plan.toml | +| `--mainnet` | | Apply default deployment settings/default.mainnet-plan.toml | +| `--manifest-path ` | `-m` | Path to Clarinet.toml | +| `--deployment-plan-path ` | `-p` | Apply deployment plan specified | +| `--no-dashboard` | | Display streams of logs instead of terminal UI dashboard | +| `--use-on-disk-deployment-plan` | `-d` | Use on disk deployment plan (prevent updates computing) | +| `--use-computed-deployment-plan` | `-c` | Use computed deployment plan (will overwrite on disk version if any update) | + +## Interact with Mainnet contracts + +### clarinet requirements + +`clarinet requirements` is a subcommand for interacting with Mainnet contracts. + +| Command | Description | +| ------- | ------------------------------------------------------ | +| `add` | Add a mainnet contract as a dependency to your project | + +Usage + +``` +clarinet requirements +``` + +```bash +$ clarinet requirements add SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait +Added requirement SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait +Updated Clarinet.toml +``` + +| Option | Description | +| ------------------------ | --------------------- | +| `--manifest-path ` | Path to Clarinet.toml | + +## Editor Integrations + +### clarinet lsp + +`clarinet lsp` starts the Language Server Protocol service for Clarity, enabling intelligent code completion, error highlighting, and other IDE features in supported editors. + +Usage + +``` +clarinet lsp +``` + +## Debugging + +### clarinet dap + +`clarinet dap` starts the Debug Adapter Protocol service, enabling debugging features like breakpoints, step-through execution, and variable inspection in supported editors. + +Usage + +``` +clarinet dap +``` + +## Format your code + +### clarinet format + +`clarinet format` formats Clarity code files according to standard conventions. + +Usage + +``` +clarinet format [OPTIONS] +``` + +```bash +clarinet format --check +clarinet format --dry-run +clarinet format --file contracts/token.clar --in-place +``` + +| Option | Short | Description | Required | +| ---------------------------- | ----- | ------------------------------------------------------ | -------- | +| `--check` | | Check if code is formatted without modifying files | No | +| `--dry-run` | | Only echo the result of formatting | No | +| `--in-place` | | Replace the contents of a file with the formatted code | No | +| `--manifest-path ` | `-m` | Path to Clarinet.toml | No | +| `--file ` | `-f` | If specified, format only this file | No | +| `--max-line-length ` | `-l` | Maximum line length | No | +| `--indent ` | `-i` | Indentation size, e.g. 2 | No | +| `--tabs` | `-t` | Use tabs instead of spaces | No | + +## Utilities + +### clarinet completions + +`clarinet completions` generates shell completion scripts for your shell. + +Usage + +``` +clarinet completions +``` + +```bash +clarinet completions zsh > ~/.zsh/completions/_clarinet +source ~/.zshrc +``` + +Supported Shells + +* `bash` +* `zsh` +* `fish` +* `powershell` +* `elvish` + +| Option | Short | Description | +| ----------------- | ----- | -------------------------------------------------------- | +| `--shell ` | `-s` | Specify which shell to generation completions script for | + +## Environment Variables + +Clarinet supports environment variables for configuration. All environment variables are prefixed with `CLARINET_`: + +```bash +export CLARINET_MANIFEST_PATH=/path/to/project +``` diff --git a/docs/reference/clarinet/contract-deployment.md b/docs/reference/clarinet/contract-deployment.md new file mode 100644 index 0000000000..99abc06fb0 --- /dev/null +++ b/docs/reference/clarinet/contract-deployment.md @@ -0,0 +1,387 @@ +# Contract Deployment + +Clarinet provides deployment tooling that helps you move from local development to production networks. Whether you're testing on devnet, staging on testnet, or launching on mainnet, Clarinet streamlines the process. + +## Generating deployment plans + +Deployment plans are YAML files that describe how contracts are published or called. Generate a plan for any network: + +```bash +$ clarinet deployments generate --testnet +Analyzing contracts... +Calculating deployment costs... +Generating deployment plan +Created file deployments/default.testnet-plan.yaml +``` + +Example output structure: + +{% code title="deployments/default.devnet-plan.yaml" %} +```yaml +--- +id: 0 +name: Devnet deployment +network: devnet +stacks-node: "http://localhost:20443" +bitcoin-node: "http://devnet:devnet@localhost:18443" +plan: + batches: + - id: 0 + transactions: + - contract-publish: + contract-name: counter + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 6940 + path: contracts/counter.clar + anchor-block-only: true + clarity-version: 2 + epoch: "2.5" +``` +{% endcode %} + +### Deployment plan structure + +| Field | Description | +| -------------- | -------------------------------------------- | +| `id` | Unique identifier for the deployment | +| `name` | Human-readable deployment name | +| `network` | Target network (devnet, testnet, or mainnet) | +| `stacks-node` | RPC endpoint for the Stacks node | +| `bitcoin-node` | RPC endpoint for the Bitcoin node | +| `plan.batches` | Array of deployment batches | + +### Deployment specifications + +Deployment behavior is configured in two places: + +* **Project configuration (`Clarinet.toml`)** – Clarity versions, dependencies, epoch requirements +* **Network configuration (`settings/.toml`)** – Account details, balances, endpoints + +Example network configuration: + +{% code title="settings/Testnet.toml" %} +```toml +[network] +name = "testnet" +deployment_fee_rate = 10 + +[accounts.deployer] +mnemonic = "" +balance = 100_000_000_000_000 +derivation = "m/44'/5757'/0'/0/0" +``` +{% endcode %} + +## Working with contract requirements + +Reference contracts that already exist on-chain—useful for standardized traits. + +### Adding requirements + +```bash +clarinet requirements add SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait +``` + +Clarinet updates your configuration automatically: + +```toml +[project] +name = "my-nft-project" +requirements = [ + { contract_id = "SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait" } +] +``` + +During deployment Clarinet downloads the contract, remaps the principal for devnet, and ensures the requirement is available before your contracts deploy. + +## Deploying to different networks + +### Devnet + +Devnet deploys contracts automatically when it starts: + +```bash +clarinet devnet start +``` + +To deploy manually against a running devnet: + +```bash +clarinet deployments apply --devnet +``` + +See [local development](local-blockchain-development.md) for more devnet configuration tips. + +### Testnet + +{% hint style="info" %} +Prerequisites: + +* Request testnet STX from the faucet +* Configure your account in `settings/Testnet.toml` +* Validate contracts with `clarinet check` +{% endhint %} + +Generate a deployment plan with cost estimation: + +```bash +clarinet deployments generate --testnet --medium-cost +``` + +Deploy to testnet: + +```bash +clarinet deployments apply --testnet +``` + +### Mainnet + +{% hint style="warning" %} +Mainnet checklist +{% endhint %} + +{% stepper %} +{% step %} +Finish thorough testing on testnet +{% endstep %} + +{% step %} +Audit contracts for security +{% endstep %} + +{% step %} +Ensure sufficient STX for fees +{% endstep %} + +{% step %} +Back up deployment keys securely +{% endstep %} + +{% step %} +Prefer a hardware wallet for deployment +{% endstep %} +{% endstepper %} + +Create a mainnet plan: + +```bash +clarinet deployments generate --mainnet --high-cost +``` + +Deploy with confirmation: + +```bash +clarinet deployments apply --mainnet +``` + +## Cost estimation and optimization + +Choose the right fee level for your deployment: + +```bash +clarinet deployments generate --testnet --manual-cost +``` + +Fee options: + +* `--low-cost` – minimize fees, slower confirmation +* `--medium-cost` – balanced approach +* `--high-cost` – priority inclusion +* `--manual-cost` – interactive selection + +Analyze costs before deploying: + +```bash +clarinet deployments generate --testnet --medium-cost +``` + +## Advanced deployment configurations + +### Multi-batch deployments + +Deploy complex systems with batches: + +{% code title="" %} +```yaml +plan: + batches: + - id: 0 + transactions: + - contract-publish: + contract-name: trait-definitions + path: contracts/traits.clar + clarity-version: 2 + - id: 1 + transactions: + - contract-publish: + contract-name: token + path: contracts/token.clar + - contract-publish: + contract-name: oracle + path: contracts/oracle.clar + - id: 2 + transactions: + - contract-publish: + contract-name: defi-pool + path: contracts/defi-pool.clar +``` +{% endcode %} + +Batches guarantee that dependencies deploy first, allow parallel transactions within a batch, and run batches sequentially. + +### Transaction types + +Deployment plans support different transaction types: + +| Transaction type | Description | +| ------------------- | ------------------------------------------------------------------- | +| Contract operations | Publish or call contracts, specifying sender, cost, and source path | +| Asset transfers | Transfer STX or BTC by setting sender, recipient, and amounts | + +**Contract operations** + +```yaml +- contract-publish: + contract-name: my-contract + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + cost: 5960 + path: contracts/my-contract.clar + clarity-version: 2 + +- contract-call: + contract-id: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.my-contract + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + method: initialize + parameters: + - u1000000 + - "Token Name" + cost: 5960 +``` + +**Asset transfers** + +```yaml +- stx-transfer: + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM + recipient: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG + mstx-amount: 1000000 + memo: '0x48656c6c6f' + +- btc-transfer: + expected-sender: mjSrB3wS4xab3kYqFktwBzfTdPg367ZJ2d + recipient: bcrt1qnxknq3wqtphv7sfwy07m7e4sr6ut9yt6ed99jg + sats-amount: 100000000 + sats-per-byte: 10 +``` + +### Manual customization + +You can edit deployment plans for complex scenarios. + +{% hint style="info" %} +Manual edits + +When Clarinet prompts to overwrite your plan, answer `no` to keep custom changes. +{% endhint %} + +Example contract initialization batch: + +```yaml +- id: 3 + transactions: + - contract-call: + contract-id: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token + method: initialize + parameters: + - u1000000 + - { name: "My Token", symbol: "MTK", decimals: u6 } + expected-sender: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM +``` + +## Common issues + +### Insufficient STX balance + +**Error**: “Insufficient STX balance for deployment” + +Solutions: + +{% stepper %} +{% step %} +Request testnet STX from the faucet +{% endstep %} + +{% step %} +Reduce the fee rate with `--low-cost` +{% endstep %} + +{% step %} +Check your balance: + +```bash +clarinet console --testnet +stx-account 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG +``` +{% endstep %} +{% endstepper %} + +### Contract already exists + +**Error**: “Contract `token` already deployed at this address” + +Solutions: + +{% stepper %} +{% step %} +Use a different contract name +{% endstep %} + +{% step %} +Deploy from another address +{% endstep %} + +{% step %} +On testnet, switch to a fresh account +{% endstep %} +{% endstepper %} + +### Network connection failures + +**Error**: “Failed to connect to testnet node” + +Check your network settings: + +```toml +[network] +name = "testnet" +node_rpc_address = "https://stacks-node-api.testnet.stacks.co" +``` + +Alternative endpoints: + +* Hiro: `https://api.testnet.hiro.so` +* Your own node + +Debug the connection: + +```bash +curl -I https://api.testnet.hiro.so/v2/info +``` + +### Invalid deployment plan + +**Common YAML errors** + +* Incorrect indentation +* Missing required fields +* Invalid contract paths + +Validate and regenerate as needed: + +```bash +clarinet deployments check +clarinet deployments generate --testnet +ls contracts/ +``` + +## diff --git a/docs/reference/clarinet/contract-interaction.md b/docs/reference/clarinet/contract-interaction.md new file mode 100644 index 0000000000..f48648fa6e --- /dev/null +++ b/docs/reference/clarinet/contract-interaction.md @@ -0,0 +1,167 @@ +# Contract Interaction + +Clarinet provides powerful tools for interacting with your contracts during development. The console gives you an interactive REPL where you can call functions, inspect state, and debug issues in real time. + +## Starting the console + +Use `clarinet console` to launch an interactive session with your contracts deployed to a local simulated blockchain: + +```bash +clarinet console +``` + +Sample startup output: + +``` +clarity-repl v3.3.0 +Enter "::help" for usage hints. +Connected to a transient in-memory database. +``` + +The console supports several useful flags for different development scenarios: + +| Option | Description | +| --------------------------------------- | ----------------------------------------------------- | +| `--enable-remote-data` | Connect to mainnet or testnet to query real contracts | +| `--deployment-plan-path ` | Use a specific deployment plan | +| `--manifest-path ` | Use an alternate `Clarinet.toml` location | +| `--remote-data-api-url ` | Specify a custom Stacks API endpoint | +| `--remote-data-initial-height ` | Set the starting block height for remote data | + +## Working with remote data + +One of the most powerful features is the ability to interact with real mainnet or testnet contracts from your local console. This lets you test against actual deployed contracts: + +```bash +clarinet console --enable-remote-data +``` + +Example contract calls: + +```clarity +(contract-call? 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token get-decimals) +;; (ok u8) + +(contract-call? 'SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token get-name) +;; (ok "Arkadiko Token") +``` + +These capabilities help you: + +* Test integrations with existing protocols +* Verify contract behavior against live chain state +* Develop contracts that depend on mainnet deployments + +> **Warning: Remote data requirements** +> +> Before using remote data, add the target contract to `Clarinet.toml` with `clarinet requirements add SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token`. + +### Using the Hiro API key + +Avoid rate limits by setting the `HIRO_API_KEY` environment variable before launching the console. Clarinet forwards this key in the `x-api-key` header for all requests: + +```bash +export HIRO_API_KEY=your_api_key_here +clarinet console --enable-remote-data +``` + +You can request a free API key from the Hiro Platform. + +### Working with contracts + +List all available contracts in the session: + +```clarity +::get_contracts +;; +---------------------------------------------------------+----------------------+ +;; | Contract identifier | Public functions | +;; |---------------------------------------------------------+----------------------| +;; | ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter | (count-up) | +;; | | (get-count (who ...))| +;; +---------------------------------------------------------+----------------------+ + +(contract-call? .counter count-up) +;; (ok true) + +(contract-call? .counter get-count tx-sender) +;; u1 +``` + +### Working with different principals + +Switch between the provided test wallets to validate multi-user flows: + +```clarity +::get_assets_maps +;; +-------------------------------------------+-----------------+ +;; | Address | uSTX | +;; |-------------------------------------------+-----------------| +;; | ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM | 100000000000000 | +;; | ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 | 100000000000000 | +;; ... + +::set_tx_sender ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +;; tx-sender switched to ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 +``` + +### Working with block heights + +Advance the chain to test time-dependent logic: + +```clarity +::get_block_height +;; Current block height: 4 + +::advance_chain_tip 100 +;; new burn height: 3 +;; new stacks height: 104 + +::get_block_height +;; Current block height: 104 +``` + +> **Tip: Console reference** +> +> For a complete list of console commands, see the [CLI reference](cli-reference.md). + +## Common issues + +
+ +Contract not found errors + +If you see `use of unresolved contract` errors, the contract may not be deployed or the name might be incorrect: + +```clarity +(contract-call? .missing-contract get-value) +;; error: use of unresolved contract +``` + +Solutions: + +* Check for typos in the contract identifier +* Confirm the contract is deployed in the current session with `::get_contracts` +* Use the correct prefix (`.` for local contracts) + +
+ +
+ +Remote data connection issues + +When you enable remote data, rate limits or connectivity problems can occur: + +```clarity +(contract-call? 'SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token get-name) +;; error: API rate limit exceeded +``` + +**Solutions:** + +1. Set your Hiro API key: `export HIRO_API_KEY=your_key_here` +2. Use a custom API endpoint: `--remote-data-api-url https://your-node.com` +3. Wait for rate limit to reset (usually 1 minute) + +
+ +## diff --git a/docs/reference/clarinet/faq.md b/docs/reference/clarinet/faq.md new file mode 100644 index 0000000000..499d59f398 --- /dev/null +++ b/docs/reference/clarinet/faq.md @@ -0,0 +1,172 @@ +# FAQ + +Common questions and solutions for Clarinet development. + +This page addresses common issues encountered when building with Clarinet, based on community feedback and support interactions. + +
+ +How do I test with sBTC tokens in my development environment? + +To test with sBTC tokens, add the mainnet sBTC contract as a requirement and mint tokens using the deployer address. + +* Add sBTC as a requirement:\ + `clarinet requirements add SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token` +* Mint sBTC in your tests\ + // The sBTC multisig address that can mint\ + `const sbtcDeployer = "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4";`\ + // Mint sBTC to your test wallet\ + `const mintTx = simnet.callPublicFn( "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token", "mint", [Cl.uint(1000000), Cl.principal(wallet1)], sbtcDeployer);`\ + \ + This approach lets you work with sBTC in unit tests without complex Bitcoin transaction simulation. + +
+ +
+ +Why am I getting an error when using mainnet addresses during mainnet simulation? + +When you run mainnet execution simulation, the target contract may expect mainnet addresses instead of the default testnet wallets. As of Clarinet v3.4.0, you can enable mainnet wallets in simnet with `use_mainnet_wallets = true`: + +```toml +[repl.remote_data] +enabled = true +initial_height = 522000 +use_mainnet_wallets = true +``` + +If you prefer to manage addresses manually, skip `simnet.getAccounts()` and use the specific mainnet principals you need: + +```ts +// Instead of using simnet.getAccounts() +const mainnetAddress = "SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY"; + +// Mint STX to any mainnet address +simnet.mintSTX(mainnetAddress, 1000000n); + +// Call functions with a mainnet address +const result = simnet.callReadOnlyFn( + "SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3", + "get-price", + [priceFeed], + mainnetAddress +); +``` + +The simnet accepts any valid Stacks address when mainnet simulation is enabled. + +
+ +
+ +How do I migrate from expectSTXTransferEvent to the new SDK? + +Clarinet v2 relies on standard Vitest matchers instead of the legacy event helpers. + +Old approach (Clarinet v1): + +```ts +block.receipts[0].events.expectSTXTransferEvent( + amount, + sender, + recipient +); +``` + +New approach (Clarinet v2): + +```ts +// Check for an exact event match +expect(events).toContainEqual({ + event: "stx_transfer_event", + data: { + amount: "1000000", + memo: "", + recipient: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", + sender: "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5", + }, +}); + +// Or assert specific properties only +expect(events).toContainEqual({ + event: "stx_transfer_event", + data: expect.objectContaining({ + sender: address1, + recipient: contractAddress, + }), +}); +``` + +For Clarity value assertions, use the built-in matchers: + +```ts +expect(result).toBeOk(Cl.bool(true)); +expect(result).toBeErr(Cl.uint(500)); +``` + +
+ +
+ +Why am I getting "bip39 error" when generating deployment plans? + +Starting with Clarinet 2.15.0, deployment configurations require 24-word mnemonics. Twelve-word mnemonics are no longer supported. + +Update your configuration with a full 24-word phrase: + +```toml +[accounts.deployer] + +# Use a 24-word mnemonic +mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin green tomorrow spring wrist shed math olympic multiply hip blue scout claw" +``` + +Generate a new 24-word mnemonic using a BIP39 generator if needed. The longer phrase improves security for production deployments. + +
+ +
+ +Can I test Bitcoin transaction verification in Clarinet? + +Testing contracts that use `clarity-bitcoin-lib` for Bitcoin transaction verification has limitations in simnet and devnet environments. + +Current limitations: + +* No real Bitcoin blocks or transactions in simnet +* Mock blocks do not contain verifiable Bitcoin transactions +* `get-burn-block-info?` returns mock data unsuitable for verification + +Workarounds: + +* Test Bitcoin verification logic on mainnet or with mainnet execution simulation +* Write unit tests that mock expected behavior instead of full verification +* Consider separating Bitcoin verification logic so it can be tested independently + +The Clarinet team continues to investigate better support for Bitcoin-focused testing. + +
+ +
+ +Why does my devnet freeze at the epoch 3.0 transition? + +The epoch 3.0 transition in devnet can be unstable, with success rates between 50–80% depending on your setup. + +Temporary workarounds: + +* Restart devnet if it freezes around blocks 139–140 +* Try Clarinet 2.14.0, which some users report as more stable +* Watch for the upcoming feature to start devnet directly in epoch 3.0 + +You can also monitor the transition manually: + +``` +# Watch for the transition around these blocks +Block 139: Epoch 2.5 +Block 140: Should transition to 3.0 +``` + +The Clarinet team is working on improving epoch transition stability and plans to allow starting devnet in epoch 3.0. + +
diff --git a/docs/reference/clarinet/local-blockchain-development.md b/docs/reference/clarinet/local-blockchain-development.md new file mode 100644 index 0000000000..a0ad5dad81 --- /dev/null +++ b/docs/reference/clarinet/local-blockchain-development.md @@ -0,0 +1,403 @@ +# Local Blockchain Development + +Clarinet ships with a complete local blockchain environment so you can build, test, and debug smart contracts without deploying to a public network. + +## Starting your local blockchain + +Launch devnet with all required services: + +```bash +clarinet devnet start +``` + +Useful flags: + +| Option | Description | +| -------------------------------- | ------------------------------------------------- | +| `--manifest-path ` | Use an alternate `Clarinet.toml` | +| `--no-dashboard` | Stream logs instead of showing the interactive UI | +| `--deployment-plan-path ` | Apply a specific deployment plan | +| `--use-on-disk-deployment-plan` | Use an existing plan without recomputing | +| `--use-computed-deployment-plan` | Recompute and overwrite the plan | +| `--package ` | Load a packaged devnet configuration | + +{% hint style="info" %} +Prerequisites + +Devnet requires Docker. If you see “clarinet was unable to create network,” ensure Docker Desktop is running or the Docker daemon is started. +{% endhint %} + +By default the dashboard displays service health, recent transactions, block production, contract deployments, and resource usage. Use `--no-dashboard` in CI or when you prefer streaming logs. + +## Core services and features + +Devnet starts these services for you: + +| Service | Port | Purpose | +| ---------------- | ----- | ---------------------------------------- | +| Stacks node | 20443 | Processes transactions and mines blocks | +| Bitcoin node | 18443 | Provides block anchoring in regtest mode | +| Stacks API | 3999 | REST API for blockchain data | +| Postgres | 5432 | Indexes blockchain data | +| Stacks Explorer | 8000 | Browse transactions in a web UI | +| Bitcoin Explorer | 8001 | View the Bitcoin regtest chain | + +Devnet includes pre-funded accounts: + +```clarity +::get_assets_maps +;; +-------------------------------------------+-----------------+ +;; | Address | STX Balance | +;; |-------------------------------------------+-----------------| +;; | ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM | 100000000000000 | +;; | ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5 | 100000000000000 | +;; | ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG | 100000000000000 | +;; | ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC | 100000000000000 | +;; | ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND | 100000000000000 | +;; +-------------------------------------------+-----------------+ +``` + +When devnet starts it automatically deploys your project contracts so you can interact immediately. + +``` +$ clarinet devnet start +Deploying contracts... +Deploying counter.clar ✓ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter +Deploying token.clar ✓ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token +Deploying marketplace.clar ✓ ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.marketplace + +All contracts deployed successfully +``` + +## Configuration and customization + +Devnet behavior is controlled by configuration files in your project. + +### Basic configuration + +`settings/Devnet.toml` defines network settings: + +```toml +[network] +name = "devnet" + +# Service ports +stacks_node_rpc_port = 20443 +stacks_api_port = 3999 +stacks_explorer_port = 8000 +bitcoin_node_rpc_port = 18443 + +[network.devnet] +bitcoin_controller_block_time = 30_000 # 30 seconds + +disable_bitcoin_explorer = false +disable_stacks_explorer = false +disable_stacks_api = false +``` + +### Port configuration + +Avoid local conflicts by customizing ports: + +```toml +stacks_node_rpc_port = 30443 +stacks_api_port = 4999 +postgres_port = 6432 +stacks_explorer_port = 4020 +``` + +### Mining intervals + +Control block production speed: + +```toml +bitcoin_controller_block_time = 1_000 # Fast development (1 second) +bitcoin_controller_block_time = 30_000 # Standard testing (30 seconds) +bitcoin_controller_block_time = 120_000 # Realistic timing (2 minutes) +``` + +### Custom accounts + +Add accounts with specific balances: + +```toml +[accounts.treasury] +mnemonic = "twice kind fence tip hidden tilt action fragile skin nothing glory cousin" +balance = 10_000_000_000_000 + +[accounts.alice] +mnemonic = "female adjust gallery certain visit token during great side clown fitness like" +balance = 5_000_000_000_000 +``` + +## Accessing services + +Devnet exposes several ways to interact with the blockchain. + +### Stacks Explorer + +Visit the explorer to browse transactions, blocks, contract state, and account balances: + +``` +http://localhost:8000 +``` + +### API endpoints + +Query blockchain data with the Stacks API: + +```bash +curl http://localhost:3999/v2/info +``` + +Common endpoints: + +* `/v2/info` – network information +* `/v2/accounts/{address}` – account details +* `/v2/contracts/source/{address}/{name}` – contract source code +* `/extended/v1/tx/{txid}` – transaction details + +### Direct RPC + +Submit transactions directly to the Stacks node: + +```bash +curl -X POST http://localhost:20443/v2/transactions \ + -H "Content-Type: application/json" \ + -d @transaction.json +``` + +Useful RPC endpoints: + +* `/v2/transactions` – broadcast transactions +* `/v2/contracts/call-read` – read-only contract calls +* `/v2/fees/transfer` – fee estimates for STX transfers + +## Advanced configuration + +### Performance optimization + +For faster development cycles: + +{% code title="settings/Devnet.toml" %} +```toml +[network.devnet] +bitcoin_controller_block_time = 1_000 + +disable_bitcoin_explorer = true +disable_stacks_explorer = true +disable_stacks_api = false +``` +{% endcode %} + +### Epoch configuration + +Test different Stacks versions: + +```toml +[epochs] +epoch_2_0 = 0 # Stacks 2.0 from genesis +epoch_2_05 = 0 # Stacks 2.05 from genesis +epoch_2_1 = 0 # Stacks 2.1 from genesis +epoch_2_2 = 0 # Pox-2 from genesis +epoch_2_3 = 0 # Pox-3 from genesis +epoch_2_4 = 0 # Pox-4 from genesis +epoch_3_0 = 101 # Nakamoto activation at block 101 +``` + +### Package deployment + +Create reusable devnet configurations: + +```bash +$ clarinet devnet package --name demo-env +Packaging devnet configuration... +Created demo-env.json +``` + +Use a packaged configuration: + +```bash +$ clarinet devnet start --package demo-env.json +``` + +## Common issues + +
+ +Docker connection errors — “clarinet was unable to create network” + +Follow these steps to fix Docker connection issues: + +Ensure Docker Desktop is running (macOS/Windows).Start the Docker daemon (sudo systemctl start docker) on Linux.Confirm permissions with docker ps.Reset Docker to factory defaults if problems persist. + +Verify Docker status: + +```bash +docker --version +docker ps +``` + +
+ +
+ +Port already in use — “bind: address already in use” + +Find and stop the conflicting process (macOS/Linux): + +```bash +lsof -i :3999 +kill -9 $(lsof -t -i:3999) +``` + +Windows equivalent: + +```bash +netstat -ano | findstr :3999 +taskkill /PID /F +``` + +Or update ports in `settings/Devnet.toml`: + +```toml +stacks_api_port = 4999 +stacks_explorer_port = 4020 +postgres_port = 6432 +``` + +
+ +
+ +High resource usage (slow performance, high CPU or memory) + +Optimizations: + +```toml +disable_bitcoin_explorer = true +disable_stacks_explorer = true +bitcoin_controller_block_time = 60_000 +``` + +Set Docker resource limits: + +```bash +docker update --memory="2g" --cpus="1" +``` + +Clean up old data: + +```bash +clarinet devnet stop +docker system prune -a +rm -rf tmp/devnet +``` + +
+ +
+ +Network already exists — “network with name `.devnet` already exists” + +Remove the orphaned network: + +```bash +docker network rm .devnet +``` + +If you're unsure of the name: + +```bash +docker network ls | grep devnet +docker network rm +``` + +Prevent the issue by stopping devnet with `Ctrl+C` and pruning orphaned networks: + +```bash +docker network prune +``` + +
+ +
+ +Docker stream error during startup — “Fatal: unable to create image: Docker stream error” + +**Error**: "Fatal: unable to create image: Docker stream error" + +This error often occurs when Docker images are corrupted or when explorers fail to start properly. + +**Solution 1 - Disable explorers**: + +If you don't need the web explorers, disable them in `settings/Devnet.toml`: + +``` +disable_bitcoin_explorer = true +disable_stacks_explorer = true +``` + +**Solution 2 - Clean Docker environment**: + +Remove all containers and images, then restart: + +Terminal + +``` +docker stop $(docker ps -a -q) +docker system prune -a +docker volume prune +``` + +**Solution 3 - Full cleanup and restart**: + +Terminal + +``` +docker stop $(docker ps -a -q) +docker network rm .devnet +docker system prune --all --volumes +clarinet devnet start +``` + +This ensures a clean Docker environment for devnet to start fresh. + +
+ +
+ +Contract deployment failures + +Ensure dependencies deploy first in `Clarinet.toml`: + +```toml +[contracts.sip-010-trait] +path = "contracts/sip-010-trait.clar" + +[contracts.token] +path = "contracts/token.clar" +``` + +Validate contracts before deployment: + +```bash +clarinet check +``` + +Check logs: + +```bash +clarinet devnet start --no-dashboard +``` + +Deploy manually if needed: + +```bash +clarinet deployments generate --devnet +clarinet deployments apply --devnet +``` + +
+ +*** diff --git a/docs/reference/clarinet/overview.md b/docs/reference/clarinet/overview.md new file mode 100644 index 0000000000..204fa29c1d --- /dev/null +++ b/docs/reference/clarinet/overview.md @@ -0,0 +1,71 @@ +--- +description: >- + Clarinet is everything you need to write, test, and deploy Clarity smart + contracts on Stacks. +--- + +# Overview + +
+ +Clarinet is the fastest way to build, test, and deploy smart contracts on the Stacks blockchain. It gives you a local devnet, REPL, testing framework, and debugging tools to ship high-quality Clarity code with confidence. + +## Key features + +* [**Leverage a powerful CLI**](cli-reference.md) - Create new projects, manage your smart contracts and their dependencies using clarinet requirements, and interact with your code through the built-in REPL. +* [**Write unit tests with the SDK**](../clarinet-js-sdk/unit-testing.md) - Use the Clarinet SDK to write unit tests in a familiar JS environment and validate contract behavior. +* [**Run a private blockchain environment**](local-blockchain-development.md) - Spin up a local devnet with nodes, miners, and APIs so you can test and integrate your code. +* [**VSCode extension**](../clarinet-integrations/vscode-extension.md) - Linter, step by step debugger, helps writing smart contracts (autocompletion, documentation etc). + +## Installation + +{% tabs %} +{% tab title="Homebrew" %} +```bash +brew install clarinet +``` +{% endtab %} + +{% tab title="Winget" %} +```bash +winget install clarinet +``` +{% endtab %} + +{% tab title="Source" %} +```bash +sudo apt install build-essential pkg-config libssl-dev +git clone https://github.com/hirosystems/clarinet +cd clarinet +cargo clarinet-install +``` +{% endtab %} + +{% tab title="Binary" %} +```bash +wget -nv https://github.com/hirosystems/clarinet/releases/latest/download/clarinet-linux-x64-glibc.tar.gz -O clarinet-linux-x64.tar.gz +tar -xf clarinet-linux-x64.tar.gz +chmod +x ./clarinet +mv ./clarinet /usr/local/bin +``` +{% endtab %} +{% endtabs %} + +## Networks + +Clarinet supports different network types to cater to various development and testing needs: + +| Network | Description | Use case | +| --------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| `simnet` | Optimized for fast feedback loops, introspection, and portability. | Ideal for initial development and unit-testing. | +| `devnet` | Local Stacks and Bitcoin nodes running on Docker for faster feedback loops. | Use for integration tests or local frontend development. | +| `testnet` | A pre-production network that offers a realistic environment for testing. | Ideal for final testing before deploying to Mainnet. | +| `mainnet` | The production network where real transactions occur. | Use when you're ready to deploy your smart contract to production. | + + + +{% hint style="info" %} +Help: Need help building with Clarinet? + +Reach out to us on the **#clarinet** channel on [Discord](https://stacks.chat/) under the Developer Tools section. +{% endhint %} diff --git a/docs/reference/clarinet/project-development.md b/docs/reference/clarinet/project-development.md new file mode 100644 index 0000000000..6c226defca --- /dev/null +++ b/docs/reference/clarinet/project-development.md @@ -0,0 +1,149 @@ +# Project Development + +Clarinet streamlines the entire lifecycle of Clarity smart contract development. From project initialization to contract management and code formatting, you'll have the tools needed for professional workflows. + +## Creating a new project + +The `clarinet new` command creates a complete project structure with all necessary configuration files: + +```bash +$ clarinet new my-defi-app +``` + +| Option | Description | Example | +| --------------------- | ------------------------------- | ----------------------------------------- | +| `--disable-telemetry` | Opt out of telemetry collection | `clarinet new my-app --disable-telemetry` | + +For a deeper look at what Clarinet generates, see the [project structure](project-structure.md) guide. + +## Managing contracts + +### Creating new contracts + +The `clarinet contract new` command generates both a contract file and a matching test file: + +```bash +$ clarinet contract new token +Created file contracts/token.clar +Created file tests/token.test.ts +Updated Clarinet.toml +``` + +The generated contract includes a minimal template: + +```clarity +;; token +;; + +;; constants +;; + +;; data vars +;; + +;; data maps +;; + +;; public functions +;; + +;; read only functions +;; + +;; private functions +;; +``` + +### Removing contracts + +Clean up unused contracts with the `rm` command: + +```bash +$ clarinet contract rm old-token +Removed file contracts/old-token.clar +Removed file tests/old-token.test.ts +Updated Clarinet.toml +``` + +## Checking project contract syntax + +Validate your entire project setup: + +```bash +$ clarinet check +✔ 3 contracts checked +``` + +Check specific contracts: + +```bash +$ clarinet check contracts/token.clar +✔ contracts/token.clar Syntax of contract successfully checked +``` + +## Code formatting + +Clarinet includes a formatter to maintain consistent style across your project. + +Format all contracts in your project: + +```bash +$ clarinet format --in-place +Formatted 5 contracts +``` + +### Formatting options + +Customize formatting to match your team's style guide: + +| Option | Description | Example | +| ------------------- | ------------------------------------------------------ | --------------------------------------- | +| `--dry-run` | Preview changes without modifying files | `clarinet format --dry-run` | +| `--in-place` | Replace file contents (required for actual formatting) | `clarinet format --in-place` | +| `--max-line-length` | Set maximum line length | `clarinet format --max-line-length 100` | +| `--indent` | Set indentation size | `clarinet format --indent 2` | +| `--tabs` | Use tabs instead of spaces | `clarinet format --tabs` | + +### Format single files + +```bash +$ clarinet format contracts/messy-contract.clar --in-place +``` + +Format specific contracts with glob patterns: + +```bash +$ clarinet format contracts/token*.clar --in-place +``` + +## Project configuration + +### Working with requirements + +Add mainnet contracts as dependencies: + +```bash +$ clarinet requirements add SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait +Added requirement SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait +Updated Clarinet.toml +``` + +Clarinet adds the dependency to `Clarinet.toml`: + +```toml +[project] +requirements = [ + { contract_id = "SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait" } +] +``` + +You can now implement traits from mainnet contracts: + +```clarity +(impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait) + +(define-non-fungible-token my-nft uint) +;; ... implement required functions +``` + +## diff --git a/docs/reference/clarinet/project-structure.md b/docs/reference/clarinet/project-structure.md new file mode 100644 index 0000000000..ad02b8bd5e --- /dev/null +++ b/docs/reference/clarinet/project-structure.md @@ -0,0 +1,258 @@ +--- +description: Understand the complete structure and configuration of a Clarinet project. +--- + +# Project Structure + +A Clarinet project follows a carefully designed structure that separates contracts, tests, and configuration. Understanding this structure helps you organize code effectively and configure tools for an efficient development workflow. + +## Core project layout + +Every Clarinet project contains these essential directories and files: + +``` +- my-project/ + - .vscode/ + - contracts/ + - main.clar + - trait.clar + - deployments/ + - settings/ + - Devnet.toml + - Mainnet.toml + - Testnet.toml + - tests/ + - main.test.ts + - .gitignore + - Clarinet.toml + - package.json + - tsconfig.json + - vitest.config.js +``` + +Each component serves a specific purpose in your development workflow. The sections below explain how they work together to create a complete development environment. + +## The project manifest + +### Clarinet.toml + +The **Clarinet.toml** file is the heart of your project. It defines project metadata and tracks all contracts: + +```toml +[project] +name = "counter" +description = "A counter smart contract" + +[contracts.traits] +path = "contracts/traits.clar" +clarity_version = 3 +epoch = "latest" + +[contracts.counter] +path = "contracts/counter.clar" +clarity_version = 3 +epoch = "latest" +``` + +The manifest handles several critical functions: + +* **Contract registration**: Every contract must be listed here +* **Stacks epoch and Clarity version**: Specifies Clarity version and epoch for each contract +* **Boot sequence**: Lists contracts to deploy on `clarinet devnet start` + +### Epoch configuration + +You can specify the epoch in two ways: + +```toml +# Use a specific epoch version +epoch = 3.1 +``` + +```toml +# Use the latest available epoch (default) +epoch = "latest" +``` + +Using `"latest"` ensures your contracts always use the newest Clarity features and optimizations available in your version of Clarinet. + +## Testing infrastructure + +### Package configuration + +The **package.json** defines your testing environment and dependencies: + +```json +{ + "name": "counter-tests", + "version": "1.0.0", + "description": "Run unit tests on this project.", + "type": "module", + "private": true, + "scripts": { + "test": "vitest run", + "test:report": "vitest run -- --coverage --costs", + "test:watch": "chokidar \"tests/**/*.ts\" \"contracts/**/*.clar\" -c \"npm run test:report\"" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@hirosystems/clarinet-sdk": "^3.0.2", + "@stacks/transactions": "^7.0.6", + "@types/node": "^24.0.14", + "chokidar-cli": "^3.0.0", + "vitest": "^3.1.3", + "vitest-environment-clarinet": "^2.3.0" + } +} +``` + +| Package | Purpose | +| ----------------------------- | ------------------------------------------------------- | +| `@hirosystems/clarinet-sdk` | WebAssembly-compiled Clarinet for Node.js | +| `@stacks/transactions` | Clarity value manipulation in TypeScript | +| `vitest` | Modern testing framework with native TypeScript support | +| `vitest-environment-clarinet` | Simnet bootstrapping for tests | + +### Vitest configuration + +The **vitest.config.js** configures the testing framework: + +```js +/// +import { defineConfig } from "vite"; +import { vitestSetupFilePath, getClarinetVitestsArgv } from "@hirosystems/clarinet-sdk/vitest"; + +export default defineConfig({ + test: { + environment: "clarinet", // use vitest-environment-clarinet + pool: "forks", + poolOptions: { + threads: { singleThread: true }, + forks: { singleFork: true }, + }, + setupFiles: [ + vitestSetupFilePath, + // custom setup files can be added here + ], + environmentOptions: { + clarinet: { + ...getClarinetVitestsArgv(), + // add or override options + }, + }, + }, +}); +``` + +This configuration enables: + +* **Clarinet environment**: Automatic `simnet` setup for each test +* **Single fork mode**: Efficient test execution with proper isolation +* **Coverage tracking**: Generate reports in multiple formats +* **Custom setup**: Add project-specific test utilities + +### TypeScript configuration + +The **tsconfig.json** provides TypeScript support: + +```json +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext"], + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + "strict": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": [ + "node_modules/@hirosystems/clarinet-sdk/vitest-helpers/src", + "tests" + ] +} +``` + +Properly setting the `include` property ensures TypeScript picks up the helpers defined in the Clarinet SDK package along with your tests. + +## Network configurations + +### Environment settings + +Each network has its own configuration file in the **settings** directory: + +```toml +[network] +name = "devnet" +deployment_fee_rate = 10 + +[accounts.deployer] +mnemonic = "twice kind fence tip hidden..." +balance = 100_000_000_000_000 + +[accounts.wallet_1] +mnemonic = "sell invite acquire kitten..." +balance = 10_000_000_000_000 +``` + +These settings control: + +* **Network ports**: API, RPC, and explorer endpoints +* **Account configuration**: Test wallets with STX balances +* **Chain parameters**: Network-specific blockchain settings + +{% hint style="warning" %} +Never commit mainnet private keys or mnemonics. Use environment variables for production credentials. +{% endhint %} + +## Common issues + +
+ +Imports failing in tests + +If you're encountering import errors in your tests, update your TypeScript configuration to use Vite's bundler resolution: + +```json +{ + "compilerOptions": { + "moduleResolution": "bundler", + "allowImportingTsExtensions": true + } +} +``` + +This configuration ensures TypeScript understands Vite's module resolution strategy and allows importing `.ts` files directly. + +
+ +
+ +Mismatched versions + +All contracts in your project should use the same Clarity version and epoch to avoid compatibility issues: + +```toml +[contracts.token] +clarity_version = 3 +epoch = "latest" + +[contracts.pool] +clarity_version = 3 +epoch = "latest" +``` + +Mismatched versions can cause deployment failures and unexpected behavior. Always upgrade all contracts together when moving to a new Clarity version. + +
diff --git a/docs/reference/clarinet/quickstart.md b/docs/reference/clarinet/quickstart.md new file mode 100644 index 0000000000..0ade77c804 --- /dev/null +++ b/docs/reference/clarinet/quickstart.md @@ -0,0 +1,142 @@ +--- +description: >- + In this guide, you'll build a simple counter smart contract and interact with + it in a local environment. +--- + +# Quickstart + +## What you'll learn + +* Create a Clarity smart contract project +* Write Clarity code with maps and public functions +* Test and validate your contracts using Clarinet's console + +## Prerequisites + +* Clarinet installed on your machine. Follow the [installation guide](overview.md#installation) if needed. +* A code editor like VS Code for editing Clarity files. + +{% stepper %} +{% step %} +### Create your project + +Let's start by creating a new Clarinet project. The `clarinet new` command sets up everything you need for smart contract development, including a testing framework, deployment configurations, and a local development environment: + +```bash +clarinet new counter +``` + +Clarinet creates a complete project structure for you. Each folder serves a specific purpose in your development workflow: + +``` +- counter/ + - contracts/ + - settings/ + - Devnet.toml + - Mainnet.toml + - Testnet.toml + - tests/ + - Clarinet.toml + - package.json + - vitest.config.js +``` +{% endstep %} + +{% step %} +### Generate your contract + +Now that we have our project structure, let's create a smart contract. Navigate into your project directory and use Clarinet's contract generator: + +```bash +$ cd counter +$ clarinet contract new counter +Created file contracts/counter.clar +Created file tests/counter.test.ts +Updated Clarinet.toml with contract counter +``` + +Clarinet automatically creates both your contract file and a corresponding test file. This follows the best practice of writing tests alongside your contract code: + +| File | Purpose | +| ------------------------ | --------------------------- | +| `contracts/counter.clar` | Your smart contract code | +| `tests/counter.test.ts` | Test file for your contract | + +{% hint style="info" %} +Notice that Clarinet also updated your `Clarinet.toml` file. This configuration file tracks all contracts in your project and their deployment settings. +{% endhint %} +{% endstep %} + +{% step %} +### Write your contract code + +Open `contracts/counter.clar` and replace its contents with our counter implementation. This contract will maintain a separate count for each user who interacts with it: + +{% code title="contracts/counter.clar" %} +```lisp +;; Define a map to store counts for each user +(define-map counters principal uint) + +;; Increment the count for the caller +(define-public (count-up) + (ok (map-set counters tx-sender (+ (get-count tx-sender) u1))) +) + +;; Get the current count for a user +(define-read-only (get-count (who principal)) + (default-to u0 (map-get? counters who)) +) +``` +{% endcode %} + +Let's understand what each part does: + +* `define-map` creates a persistent storage map that associates each user (principal) with their count +* `tx-sender` is a built-in variable that contains the address of whoever calls the function +* `define-public` declares functions that can modify contract state +* `define-read-only` declares functions that only read data without modifying it +{% endstep %} + +{% step %} +### Validate your contract + +Before we can test our contract, let's make sure it's syntactically correct and type-safe. Clarinet's check command analyzes your contract without deploying it: + +```bash +clarinet check +``` + +If you see errors instead, here are the most common issues and how to fix them: + +| Error | Fix | +| --------------------- | ----------------------------------------------------------------------------- | +| `Unknown keyword` | Check spelling of Clarity functions | +| `Type mismatch` | Ensure you're using correct types (uint, principal, etc.) | +| `Unresolved contract` | Verify contract name in `Clarinet.toml` matches the contract name in the file | +{% endstep %} + +{% step %} +### Test in the console + +Now for the exciting part—let's interact with our contract! Clarinet provides an interactive console where you can call functions and see results immediately. Start the console with: + +```bash +clarinet console +``` + +Once the console loads, you can call your contract functions directly. Here are a few examples you can try: + +```lisp +$ (contract-call? .counter count-up) +(ok true) +$ (contract-call? .counter get-count tx-sender) +u1 +$ (contract-call? .counter count-up) +(ok true) +$ (contract-call? .counter get-count tx-sender) +u2 +``` +{% endstep %} +{% endstepper %} + diff --git a/docs/reference/clarinet/validation-and-analysis.md b/docs/reference/clarinet/validation-and-analysis.md new file mode 100644 index 0000000000..7924b3f6e3 --- /dev/null +++ b/docs/reference/clarinet/validation-and-analysis.md @@ -0,0 +1,241 @@ +# Validation and Analysis + +Clarinet provides powerful tools for validating, analyzing, and debugging your smart contracts. From static type checking to real-time cost analysis, you can ensure your contracts are correct and efficient before deployment. + +Contract validation spans static analysis, runtime debugging, and cost optimization. Each discipline helps you gain confidence in contract behavior. + +## Understanding contract validation + +**Static analysis vs. runtime debugging** + +| Static analysis | Runtime debugging | +| --------------------------------------- | -------------------------------------- | +| Catches issues before deployment | Reveals behavior during execution | +| Flags type mismatches and syntax errors | Shows actual execution costs | +| Ensures trait compliance | Exposes state changes and side effects | +| Detects undefined variables | Highlights transaction flow | +| Validates function signatures | Surfaces performance bottlenecks | + +## Static analysis + +Run comprehensive validation with `clarinet check`: + +```bash +clarinet check +``` + +Successful output resembles: + +``` +✔ 3 contracts checked +``` + +When validation fails, Clarinet provides detailed diagnostics: + +``` +✖ 1 error detected + +Error in contracts/token.clar:15:10 + | +15| (ok (+ balance amount)) + | ^^^^^^^ + | + = Type error: expected uint, found (response uint uint) +``` + +{% stepper %} +{% step %} +### Run basic checks + +Use `clarinet check` to validate your contracts and catch type/syntax errors before deployment. + +```bash +clarinet check +``` +{% endstep %} + +{% step %} +### Check a specific contract + +Focus validation during development on a single contract file: + +```bash +clarinet check contracts/nft.clar +``` +{% endstep %} + +{% step %} +### Integrate into CI + +Automate validation in continuous integration pipelines. Example GitHub Actions workflow: + +```yaml +name: Contract Validation +on: [push, pull_request] + +jobs: + sanity-checks: + runs-on: ubuntu-latest + container: hirosystems/clarinet:latest + steps: + - uses: actions/checkout@v4 + - name: Check Clarity contracts + run: clarinet check --use-on-disk-deployment-plan + - name: Check formatting + run: clarinet format --check +``` +{% endstep %} +{% endstepper %} + +**Validation scope** + +Clarinet validates multiple aspects of your contracts: + +| Validation type | What it checks | +| ------------------------ | -------------------------------------------------- | +| **Type safety** | Function parameters, return values, variable types | +| **Trait compliance** | Implementation matches trait definitions | +| **Response consistency** | `ok`/`err` branches return the same types | +| **Variable scope** | Variables defined before use | +| **Function visibility** | Proper use of public, private, and read-only | + +## Runtime analysis + +The Clarinet console offers runtime tools that help you inspect behavior during execution. + +### Cost analysis with `::toggle_costs` + +Enable automatic cost display after every expression: + +```clarity +::toggle_costs +;; Always show costs: true + +(contract-call? .counter count-up) +;; +----------------------+----------+------------+------------+ +;; | | Consumed | Limit | Percentage | +;; |----------------------+----------+------------+------------| +;; | Runtime | 4775 | 5000000000 | 0.00 % | +;; | Read count | 5 | 15000 | 0.03 % | +;; | Read length (bytes) | 268 | 100000000 | 0.00 % | +;; | Write count | 1 | 15000 | 0.01 % | +;; | Write length (bytes) | 41 | 15000000 | 0.00 % | +;; +----------------------+----------+------------+------------+ +;; (ok true) +``` + +### Execution tracing with `::trace` + +Trace function calls to understand execution flow: + +```clarity +::trace (contract-call? .defi-pool swap u100 'token-a 'token-b) +;; (contract-call? .defi-pool swap u100 'token-a 'token-b) +;; ( get-pool-balance 'token-a ) defi-pool:15:8 +;; ↳ args: 'token-a +;; u50000 +;; ( get-pool-balance 'token-b ) defi-pool:16:8 +;; ↳ args: 'token-b +;; u75000 +;; ( calculate-output u100 u50000 u75000 ) defi-pool:18:12 +;; ↳ args: u100, u50000, u75000 +;; u149 +;; (ok u149) +``` + +### Interactive debugging with `::debug` + +Set breakpoints and step through execution: + +```clarity +::debug (contract-call? .complex-contract process-batch) +break validate-input +;; Breakpoint set at validate-input +continue +;; Hit breakpoint at validate-input:23 +``` + +Common navigation commands: + +{% hint style="info" %} +* `step` or `s` – step into subexpressions +* `finish` or `f` – complete the current expression +* `next` or `n` – step over subexpressions +* `continue` or `c` – resume execution +{% endhint %} + +### Using `::get_costs` for targeted analysis + +```clarity +::get_costs (contract-call? .defi-pool add-liquidity u1000 u1000) +;; +----------------------+----------+------------+------------+ +;; | | Consumed | Limit | Percentage | +;; |----------------------+----------+------------+------------| +;; | Runtime | 12250 | 5000000000 | 0.00 % | +;; | Read count | 6 | 15000 | 0.04 % | +;; | Read length (bytes) | 192 | 100000000 | 0.00 % | +;; | Write count | 3 | 15000 | 0.02 % | +;; | Write length (bytes) | 96 | 15000000 | 0.00 % | +;; +----------------------+----------+------------+------------+ +;; (ok {lp-tokens: u1000}) +``` + +### Spotting costly operations with `::trace` + +```clarity +::trace (contract-call? .complex-algo process-large-dataset) +``` + +Review the trace for loops with high iteration counts, nested map/filter operations, repeated contract calls, and large data structure manipulations. + +## Debugging workflows + +Master interactive debugging to identify issues quickly: + +```clarity +::debug (contract-call? .counter count-up) +;; ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter:9:3 +;; 6 +;; 7 ;; Increment the count for the caller +;; 8 (define-public (count-up) +;; ->9 (ok (map-set counters tx-sender (+ (get-count tx-sender) u1))) +;; ^ +;; 10 ) +``` + +### Analyzing failed transactions with `::trace` + +```clarity +::trace (contract-call? .marketplace purchase u999) +;; (contract-call? .marketplace purchase u999) +;; ( get-listing u999 ) marketplace:45:12 +;; ↳ args: u999 +;; none +;; (err u404) # Listing not found +``` + +### Using `::encode` and `::decode` for inspection + +```clarity +::encode { id: u1, active: true } +;; 0c0000000206616374697665030269640100000000000000000000000000000001 + +::decode 0d0000000b48656c6c6f20776f726c64 +;; "Hello world" +``` + +### Testing time-dependent logic + +```clarity +::get_block_height +;; Current block height: 4 + +::advance_chain_tip 100 +;; new burn height: 3 +;; new stacks height: 104 + +(contract-call? .vesting claim) +;; (ok {claimed: u2500, remaining: u7500}) +``` + +## diff --git a/docs/reference/clarity/example-contracts/README.md b/docs/reference/clarity/example-contracts/README.md new file mode 100644 index 0000000000..8bfe0241ca --- /dev/null +++ b/docs/reference/clarity/example-contracts/README.md @@ -0,0 +1,2 @@ +# Example Contracts + diff --git a/docs/reference/clarity/example-contracts/audited-starter-contracts.md b/docs/reference/clarity/example-contracts/audited-starter-contracts.md new file mode 100644 index 0000000000..aed648cfe3 --- /dev/null +++ b/docs/reference/clarity/example-contracts/audited-starter-contracts.md @@ -0,0 +1,10 @@ +# audited starter contracts + +Here's a list of sample contracts to learn Clarity or to serve as a starting point for your next project. All contracts come from the [Clarity book](https://book.clarity-lang.org/) and have been audited by [Coinfabrik](https://www.coinfabrik.com/). + +* [Counter](https://github.com/clarity-lang/book/tree/main/projects/counter) +* [Multisig Vault](https://github.com/clarity-lang/book/tree/main/projects/multisig-vault) +* [Sip-009 NFT](https://github.com/clarity-lang/book/tree/main/projects/sip009-nft) +* [SIP-010 FT](https://github.com/clarity-lang/book/tree/main/projects/sip010-ft) +* [Timelocked Wallet](https://github.com/clarity-lang/book/tree/main/projects/timelocked-wallet) +* [Tiny Market (NFT marketplace)](https://github.com/clarity-lang/book/tree/main/projects/tiny-market) diff --git a/docs/reference/clarity/example-contracts/bns.md b/docs/reference/clarity/example-contracts/bns.md new file mode 100644 index 0000000000..c5498c7099 --- /dev/null +++ b/docs/reference/clarity/example-contracts/bns.md @@ -0,0 +1,423 @@ +# bns + +The Bitcoin Name System (BNS) is implemented as a smart contract using Clarity. + +Below is a list of public and read-only functions as well as error codes that can be returned by those methods. + +*** + +## Public functions + +### name-import + +Signature: + +{% code title="Signature" %} +```clojure +(name-import namespace name beneficiary zonefile-hash) +``` +{% endcode %} + +Input: `(buff 20), (buff 48), principal, (buff 20)`\ +Output: `(response bool int)` + +Description:\ +Imports name to a revealed namespace. Each imported name is given both an owner and some off-chain state. + +*** + +### name-preorder + +Signature: + +{% code title="Signature" %} +```clojure +(name-preorder hashed-salted-fqn stx-to-burn) +``` +{% endcode %} + +Input: `(buff 20), uint`\ +Output: `(response uint int)` + +Description:\ +Preorders a name by telling all BNS nodes the salted hash of the BNS name. It pays the registration fee to the namespace owner's designated address. + +*** + +### name-register + +Signature: + +{% code title="Signature" %} +```clojure +(name-register namespace name salt zonefile-hash) +``` +{% endcode %} + +Input: `(buff 20), (buff 48), (buff 20), (buff 20)`\ +Output: `(response bool int)` + +Description:\ +Reveals the salt and the name to all BNS nodes, and assigns the name an initial public key hash and zone file hash. + +*** + +### name-renewal + +Signature: + +{% code title="Signature" %} +```clojure +(name-renewal namespace name stx-to-burn new-owner zonefile-hash) +``` +{% endcode %} + +Input: `(buff 20), (buff 48), uint, (optional principal), (optional (buff 20))`\ +Output: `(response bool int)` + +Description:\ +Depending on the namespace rules, a name can expire. For example, names in the .id namespace expire after 2 years. You need to send a name renewal every so often to keep your name. + +You will pay the registration cost of your name to the namespace's designated burn address when you renew it. When a name expires, it enters a "grace period". The period is set to 5000 blocks (a month) but can be configured for each namespace. + +It will stop resolving in the grace period, and all of the above operations will cease to be honored by the BNS consensus rules. You may, however, send a NAME\_RENEWAL during this grace period to preserve your name. After the grace period, everybody can register that name again. If your name is in a namespace where names do not expire, then you never need to use this transaction. + +*** + +### name-revoke + +Signature: + +{% code title="Signature" %} +```clojure +(name-revoke namespace name) +``` +{% endcode %} + +Input: `(buff 20), (buff 48)`\ +Output: `(response bool int)` + +Description:\ +Makes a name unresolvable. The BNS consensus rules stipulate that once a name is revoked, no one can change its public key hash or its zone file hash. The name's zone file hash is set to null to prevent it from resolving. You should only do this if your private key is compromised, or if you want to render your name unusable for whatever reason. + +*** + +### name-transfer + +Signature: + +{% code title="Signature" %} +```clojure +(name-transfer namespace name new-owner zonefile-hash) +``` +{% endcode %} + +Input: `(buff 20), (buff 48), principal, (optional (buff 20))`\ +Output: `(response bool int)` + +Description:\ +Changes the name's public key hash. You would send a name transfer transaction if you wanted to: + +* Change your private key +* Send the name to someone else +* Update your zone file + +When transferring a name, you have the option to also clear the name's zone file hash (i.e. set it to null). This is useful for when you send the name to someone else, so the recipient's name does not resolve to your zone file. + +*** + +### name-update + +Signature: + +{% code title="Signature" %} +```clojure +(name-update namespace name zonefile-hash) +``` +{% endcode %} + +Input: `(buff 20), (buff 48), (buff 20)`\ +Output: `(response bool int)` + +Description:\ +Changes the name's zone file hash. You would send a name update transaction if you wanted to change the name's zone file contents. For example, you would do this if you want to deploy your own Gaia hub and want other people to read from it. + +*** + +### namespace-preorder + +Signature: + +{% code title="Signature" %} +```clojure +(namespace-preorder hashed-salted-namespace stx-to-burn) +``` +{% endcode %} + +Input: `(buff 20), uint`\ +Output: `(response uint int)` + +Description:\ +Registers the salted hash of the namespace with BNS nodes, and burns the requisite amount of cryptocurrency. Additionally, this step proves to the BNS nodes that user has honored the BNS consensus rules by including a recent consensus hash in the transaction. Returns pre-order's expiration date (in blocks). + +*** + +### namespace-ready + +Signature: + +{% code title="Signature" %} +```clojure +(namespace-ready namespace) +``` +{% endcode %} + +Input: `(buff 20)`\ +Output: `(response bool int)` + +Description:\ +Launches the namespace and makes it available to the public. Once a namespace is launched, anyone can register a name in it if they pay the appropriate amount of cryptocurrency. + +*** + +### namespace-reveal + +Signature: + +{% code title="Signature" %} +```clojure +(namespace-reveal namespace namespace-salt p-func-base p-func-coeff p-func-b1 p-func-b2 p-func-b3 p-func-b4 p-func-b5 p-func-b6 p-func-b7 p-func-b8 p-func-b9 p-func-b10 p-func-b11 p-func-b12 p-func-b13 p-func-b14 p-func-b15 p-func-b16 p-func-non-alpha-discount p-func-no-vowel-discount lifetime namespace-import) +``` +{% endcode %} + +Input: `(buff 20), (buff 20), uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, uint, principal`\ +Output: `(response bool int)` + +Description:\ +Reveals the salt and the namespace ID (after a namespace preorder). It reveals how long names last in this namespace before they expire or must be renewed, and it sets a price function for the namespace that determines how cheap or expensive names will be. All of the parameters prefixed by `p` make up the price function. These parameters govern the pricing and lifetime of names in the namespace. + +Rules for a namespace: + +* A name can fall into one of 16 buckets, measured by length. Bucket 16 incorporates all names at least 16 characters long. +* The pricing structure applies a multiplicative penalty for having numeric characters, or punctuation characters. +* The price of a name in a bucket is: ((coeff) \* (base) ^ (bucket exponent)) / ((numeric discount multiplier) \* (punctuation discount multiplier)) + +Example parameters: + +* base = 10 +* coeff = 2 +* nonalpha discount: 10 +* no-vowel discount: 10 +* buckets 1, 2: 9 +* buckets 3, 4, 5, 6: 8 +* buckets 7–14: 7 +* buckets 15, 16+: (not specified in source) + +*** + +## Read-only functions + +### can-name-be-registered + +Signature: + +{% code title="Signature" %} +```clojure +(can-name-be-registered namespace name) +``` +{% endcode %} + +Input: `(buff 20), (buff 48)`\ +Output: `(response bool int)` + +Description:\ +Returns true if the provided name can be registered. + +*** + +### can-namespace-be-registered + +Signature: + +{% code title="Signature" %} +```clojure +(can-namespace-be-registered namespace) +``` +{% endcode %} + +Input: `(buff 20)`\ +Output: `(response bool UnknownType)` + +Description:\ +Returns true if the provided namespace is available. + +*** + +### can-receive-name + +Signature: + +{% code title="Signature" %} +```clojure +(can-receive-name owner) +``` +{% endcode %} + +Input: `principal`\ +Output: `(response bool int)` + +Description:\ +Returns true if the provided name can be received. That is, if it is not currently owned, a previous lease is expired, and the name wasn't revoked. + +*** + +### get-name-price + +Signature: + +{% code title="Signature" %} +```clojure +(get-name-price namespace name) +``` +{% endcode %} + +Input: `(buff 20), (buff 48)`\ +Output: `(response uint int)` + +Description:\ +Gets the price for a name. + +*** + +### get-namespace-price + +Signature: + +{% code title="Signature" %} +```clojure +(get-namespace-price namespace) +``` +{% endcode %} + +Input: `(buff 20)`\ +Output: `(response uint int)` + +Description:\ +Gets the price for a namespace. + +*** + +### get-namespace-properties + +Signature: + +{% code title="Signature" %} +```clojure +(get-namespace-properties namespace) +``` +{% endcode %} + +Input: `(buff 20)`\ +Output: `(response (tuple (namespace (buff 20)) (properties (tuple (can-update-price-function bool) (launched-at (optional uint)) (lifetime uint) (namespace-import principal) (price-function (tuple (base uint) (buckets (list 16 uint)) (coeff uint) (no-vowel-discount uint) (nonalpha-discount uint))) (revealed-at uint)))) int)` + +Description:\ +Get namespace properties. + +*** + +### is-name-lease-expired + +Signature: + +{% code title="Signature" %} +```clojure +(is-name-lease-expired namespace name) +``` +{% endcode %} + +Input: `(buff 20), (buff 48)`\ +Output: `(response bool int)` + +Description:\ +Return true if the provided name lease is expired. + +*** + +### name-resolve + +Signature: + +{% code title="Signature" %} +```clojure +(name-resolve namespace name) +``` +{% endcode %} + +Input: `(buff 20), (buff 48)`\ +Output: `(response (tuple (lease-ending-at (optional uint)) (lease-started-at uint) (owner principal) (zonefile-hash (buff 20))) int)` + +Description:\ +Get name registration details. + +*** + +### resolve-principal + +Signature: + +{% code title="Signature" %} +```clojure +(resolve-principal owner) +``` +{% endcode %} + +Input: `principal`\ +Output: `(response (tuple (name (buff 48)) (namespace (buff 20))) (tuple (code int) (name (optional (tuple (name (buff 48)) (namespace (buff 20)))))))` + +Description:\ +Returns the registered name that a principal owns if there is one. A principal can only own one name at a time. + +*** + +## Error codes + +* ERR\_INSUFFICIENT\_FUNDS — type: int, value: 4001 +* ERR\_NAMESPACE\_ALREADY\_EXISTS — type: int, value: 1006 +* ERR\_NAMESPACE\_ALREADY\_LAUNCHED — type: int, value: 1014 +* ERR\_NAMESPACE\_BLANK — type: int, value: 1013 +* ERR\_NAMESPACE\_CHARSET\_INVALID — type: int, value: 1016 +* ERR\_NAMESPACE\_HASH\_MALFORMED — type: int, value: 1015 +* ERR\_NAMESPACE\_NOT\_FOUND — type: int, value: 1005 +* ERR\_NAMESPACE\_NOT\_LAUNCHED — type: int, value: 1007 +* ERR\_NAMESPACE\_OPERATION\_UNAUTHORIZED — type: int, value: 1011 +* ERR\_NAMESPACE\_PREORDER\_ALREADY\_EXISTS — type: int, value: 1003 +* ERR\_NAMESPACE\_PREORDER\_CLAIMABILITY\_EXPIRED — type: int, value: 1009 +* ERR\_NAMESPACE\_PREORDER\_EXPIRED — type: int, value: 1002 +* ERR\_NAMESPACE\_PREORDER\_LAUNCHABILITY\_EXPIRED — type: int, value: 1010 +* ERR\_NAMESPACE\_PREORDER\_NOT\_FOUND — type: int, value: 1001 +* ERR\_NAMESPACE\_PRICE\_FUNCTION\_INVALID — type: int, value: 1008 +* ERR\_NAMESPACE\_STX\_BURNT\_INSUFFICIENT — type: int, value: 1012 +* ERR\_NAMESPACE\_UNAVAILABLE — type: int, value: 1004 +* ERR\_NAME\_ALREADY\_CLAIMED — type: int, value: 2011 +* ERR\_NAME\_BLANK — type: int, value: 2010 +* ERR\_NAME\_CHARSET\_INVALID — type: int, value: 2022 +* ERR\_NAME\_CLAIMABILITY\_EXPIRED — type: int, value: 2012 +* ERR\_NAME\_COULD\_NOT\_BE\_MINTED — type: int, value: 2020 +* ERR\_NAME\_COULD\_NOT\_BE\_TRANSFERRED — type: int, value: 2021 +* ERR\_NAME\_EXPIRED — type: int, value: 2008 +* ERR\_NAME\_GRACE\_PERIOD — type: int, value: 2009 +* ERR\_NAME\_HASH\_MALFORMED — type: int, value: 2017 +* ERR\_NAME\_NOT\_FOUND — type: int, value: 2013 +* ERR\_NAME\_NOT\_RESOLVABLE — type: int, value: 2019 +* ERR\_NAME\_OPERATION\_UNAUTHORIZED — type: int, value: 2006 +* ERR\_NAME\_PREORDERED\_BEFORE\_NAMESPACE\_LAUNCH — type: int, value: 2018 +* ERR\_NAME\_PREORDER\_ALREADY\_EXISTS — type: int, value: 2016 +* ERR\_NAME\_PREORDER\_EXPIRED — type: int, value: 2002 +* ERR\_NAME\_PREORDER\_FUNDS\_INSUFFICIENT — type: int, value: 2003 +* ERR\_NAME\_PREORDER\_NOT\_FOUND — type: int, value: 2001 +* ERR\_NAME\_REVOKED — type: int, value: 2014 +* ERR\_NAME\_STX\_BURNT\_INSUFFICIENT — type: int, value: 2007 +* ERR\_NAME\_TRANSFER\_FAILED — type: int, value: 2015 +* ERR\_NAME\_UNAVAILABLE — type: int, value: 2004 +* ERR\_PANIC — type: int, value: 0 +* ERR\_PRINCIPAL\_ALREADY\_ASSOCIATED — type: int, value: 3001 diff --git a/docs/reference/clarity/example-contracts/multi-send.md b/docs/reference/clarity/example-contracts/multi-send.md new file mode 100644 index 0000000000..a0d2408322 --- /dev/null +++ b/docs/reference/clarity/example-contracts/multi-send.md @@ -0,0 +1,23 @@ +# multi send + +Multi send is a very simple but highly useful utility contract for executing multiple STX transfers in a single transaction. + +It takes in a list of addresses and amounts and folds through them to execute a STX transfer for each one. + +Mainnet contract: https://explorer.hiro.so/txid/0x59665b756dc0fa9efb3fca9e05a28f572c9b14ca894c115fd3e7d81a563e14f8?chain=mainnet + +{% code title="multi-send.clar" %} +```clojure +;; send-many +(define-private (send-stx (recipient { to: principal, ustx: uint })) + (stx-transfer? (get ustx recipient) tx-sender (get to recipient))) +(define-private (check-err (result (response bool uint)) + (prior (response bool uint))) + (match prior ok-value result + err-value (err err-value))) +(define-public (send-many (recipients (list 200 { to: principal, ustx: uint }))) + (fold check-err + (map send-stx recipients) + (ok true))) +``` +{% endcode %} diff --git a/docs/reference/clarity/example-contracts/stacking.md b/docs/reference/clarity/example-contracts/stacking.md new file mode 100644 index 0000000000..9066b6a095 --- /dev/null +++ b/docs/reference/clarity/example-contracts/stacking.md @@ -0,0 +1,428 @@ +# stacking + +Stacking is implemented as a smart contract using Clarity. You can always find the Stacking contract identifier using the Stacks Blockchain API [`v2/pox` endpoint](https://docs.hiro.so/api#operation/get_pox_info). + +Currently, stacking uses the pox-4 contract. The deployed pox-4 contract and included comments can be [viewed in the explorer](https://explorer.hiro.so/txid/SP000000000000000000002Q6VF78.pox-4?chain=mainnet). + +In this walkthrough, we'll cover the entire stacking contract from start to finish, including descriptions of the various functions and errors, and when you might use/encounter them. + +Rather than walking through the contract line by line, which you can do by simply reading the contract code and the comments, we'll instead explore it from the perspective of conducting stacking operations, including solo stacking, delegating, and running a pool. + +At the bottom you will find a list of some errors you may run into and their explanations. + +There are a few utilities that make interacting with this contract easier including [Leather Earn](https://earn.leather.io/) as an UI and the [@stacks/stacking package](https://www.npmjs.com/package/@stacks/stacking) for a JS library. + +Hiro has a [detailed guide](https://docs.hiro.so/stacks.js/guides/how-to-integrate-stacking) available for stacking using this library as well as a [Nakamoto guide](https://docs.hiro.so/nakamoto/stacks-js) specifically for the additions made to work with `pox-4`. + +### Prerequisites + +If you are not familiar with stacking as a concept, it will be useful to [familiarize yourself with that first](broken-reference) before diving into the contract. + +*** + +## Solo Stacking + +Solo stacking is the simplest option, and begins by calling the `stack-stx` function. + +### stack-stx + +This function locks up the given amount of STX for the given lock period (number of reward cycles) for the `tx-sender`. + +Here's the full code for that function, then we'll dive into how it works below that. + +{% code title="pox-4: stack-stx" %} +```clojure +(define-public (stack-stx (amount-ustx uint) + (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32)))) + (start-burn-ht uint) + (lock-period uint) + (signer-sig (optional (buff 65))) + (signer-key (buff 33)) + (max-amount uint) + (auth-id uint)) + ;; this stacker's first reward cycle is the _next_ reward cycle + (let ((first-reward-cycle (+ u1 (current-pox-reward-cycle))) + (specified-reward-cycle (+ u1 (burn-height-to-reward-cycle start-burn-ht)))) + ;; the start-burn-ht must result in the next reward cycle, do not allow stackers + ;; to "post-date" their `stack-stx` transaction + (asserts! (is-eq first-reward-cycle specified-reward-cycle) + (err ERR_INVALID_START_BURN_HEIGHT)) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; tx-sender principal must not be stacking + (asserts! (is-none (get-stacker-info tx-sender)) + (err ERR_STACKING_ALREADY_STACKED)) + + ;; tx-sender must not be delegating + (asserts! (is-none (get-check-delegation tx-sender)) + (err ERR_STACKING_ALREADY_DELEGATED)) + + ;; the Stacker must have sufficient unlocked funds + (asserts! (>= (stx-get-balance tx-sender) amount-ustx) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + + ;; Validate ownership of the given signer key + (try! (consume-signer-key-authorization pox-addr (- first-reward-cycle u1) "stack-stx" lock-period signer-sig signer-key amount-ustx max-amount auth-id)) + + ;; ensure that stacking can be performed + (try! (can-stack-stx pox-addr amount-ustx first-reward-cycle lock-period)) + + ;; register the PoX address with the amount stacked + (let ((reward-set-indexes (try! (add-pox-addr-to-reward-cycles pox-addr first-reward-cycle lock-period amount-ustx tx-sender signer-key)))) + ;; add stacker record + (map-set stacking-state + { stacker: tx-sender } + { pox-addr: pox-addr, + reward-set-indexes: reward-set-indexes, + first-reward-cycle: first-reward-cycle, + lock-period: lock-period, + delegated-to: none }) + + ;; return the lock-up information, so the node can actually carry out the lock. + (ok { stacker: tx-sender, lock-amount: amount-ustx, signer-key: signer-key, unlock-burn-height: (reward-cycle-to-burn-height (+ first-reward-cycle lock-period)) })))) +``` +{% endcode %} + +First let's cover the needed parameters. + +* `amount-ustx` is the amount of STX you would like to lock, denoted in micro-STX, or uSTX (1 STX = 1,000,000 uSTX). +* `pox-addr` is a tuple that encodes the Bitcoin address to be used for the PoX rewards, details below. +* `start-burn-ht` is the Bitcoin block height you would like to begin stacking. You will receive rewards in the reward cycle following `start-burn-ht`. Importantly, `start-burn-ht` may not be further into the future than the current reward cycle, and in most cases should be set to the current burn block height. +* `lock-period` sets the number of reward cycles you would like you lock your STX for, this can be between 1 and 12. +* `signer-sig` is a unique generated signature that proves ownership of this signer. Further details for its role and how to generate it can be found in the [How to Stack](broken-reference) document. +* `signer-key` is the public key of your signer, more details in the [How to Run a Signer](broken-reference) document. +* `max-amount` sets the maximum amount allowed to be stacked during the provided stacking period. +* `auth-id` is a unique string to prevent re-use of this stacking transaction. + +{% hint style="warning" %} +It's important to make sure that these fields match what you pass in to the signer signature generation. If they don't, you will likely get error 35 (`ERR_INVALID_SIGNATURE_PUBKEY`) when trying to submit this transaction as the signer signature will not be valid. +{% endhint %} + +### Supported Reward Address Types + +{% hint style="info" %} +For the `pox-addr` field, the `version` buffer must represent what kind of bitcoin address is being submitted. These are all the possible values you can pass here depending on your address type: + +```clojure +(define-constant ADDRESS_VERSION_P2PKH 0x00) +(define-constant ADDRESS_VERSION_P2SH 0x01) +(define-constant ADDRESS_VERSION_P2WPKH 0x02) +(define-constant ADDRESS_VERSION_P2WSH 0x03) +(define-constant ADDRESS_VERSION_NATIVE_P2WPKH 0x04) +(define-constant ADDRESS_VERSION_NATIVE_P2WSH 0x05) +(define-constant ADDRESS_VERSION_NATIVE_P2TR 0x06) +``` + +The `hashbytes` are the 20 hash bytes of the bitcoin address. You can obtain that from a bitcoin library, for instance using [`bitcoinjs-lib`](https://github.com/bitcoinjs/bitcoinjs-lib): + +```javascript +const btc = require("bitcoinjs-lib"); +console.log( + "0x" + + btc.address + .fromBase58Check("1C56LYirKa3PFXFsvhSESgDy2acEHVAEt6") + .hash.toString("hex") +); +``` +{% endhint %} + +The `stack-stx` function performs several checks including: + +* The `start-burn-ht` results in the next reward cycle +* The function is being called by the `tx-sender` or an allowed contract caller +* The `tx-sender` is not currently stacking or delegating +* The `tx-sender` has enough funds +* The given `signer-key` is valid, proving ownership +* Stacking can be performed (amount meets minimum threshold, lock period and bitcoin address are valid) + +Next the function registers the provided PoX address for the next reward cycle, assigns its specific reward slot, and adds it to the `stacking-state` map, which keeps track of all current stackers per reward cycle. + +Finally it returns the lock-up information so the node can carry out the lock. This step is what actually locks the STX and prevents the stacker from using them on-chain. + +From here, the locked STX tokens will be unlocked automatically at the end of the lock period. The stacker can also call `stack-increase` or `stack-extend` to increase the amount locked or extend the time. + +*** + +## Delegated Stacking + +Delegated stacking is essentially a multi-step process where delegators give pool operators permission to lock STX on their behalf. The typical flow: + +{% stepper %} +{% step %} +### Step: Delegator delegates their STX to a pool operator + +The delegator calls `delegate-stx` to record that they delegate a given amount to a specific pool operator. This does not lock the STX — it only records the delegation permission. +{% endstep %} + +{% step %} +### Step: Pool operator stacks delegated STX (partial) + +The pool operator calls `delegate-stack-stx` for each delegator they will lock on behalf of. This marks those STX as partially stacked (not yet in the official reward set). +{% endstep %} + +{% step %} +### Step: Pool operator commits aggregated locks + +When the pool operator has aggregated enough delegated STX, they call `stack-aggregation-commit-indexed` (wraps `inner-stack-aggregation-commit`) to commit the aggregated stake into the reward set for the reward cycle. +{% endstep %} +{% endstepper %} + +There are also alternative actions like revoking delegation (see contract functions). + +*** + +### delegate-stx + +This function is called by the individual stacker delegating their STX to a pool operator. An individual stacker choosing to delegate does not need to run their own signer. + +This function does not actually lock the STX, but just allows the pool operator to issue the lock. + +{% code title="pox-4: delegate-stx" %} +```clojure +(define-public (delegate-stx (amount-ustx uint) + (delegate-to principal) + (until-burn-ht (optional uint)) + (pox-addr (optional { version: (buff 1), hashbytes: (buff 32) }))) + + (begin + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; delegate-stx no longer requires the delegator to not currently + ;; be stacking. + ;; delegate-stack-* functions assert that + ;; 1. users can't swim in two pools at the same time. + ;; 2. users can't switch pools without cool down cycle. + ;; Other pool admins can't increase or extend. + ;; 3. users can't join a pool while already directly stacking. + + ;; pox-addr, if given, must be valid + (match pox-addr + address + (asserts! (check-pox-addr-version (get version address)) + (err ERR_STACKING_INVALID_POX_ADDRESS)) + true) + + ;; tx-sender must not be delegating + (asserts! (is-none (get-check-delegation tx-sender)) + (err ERR_STACKING_ALREADY_DELEGATED)) + + ;; add delegation record + (map-set delegation-state + { stacker: tx-sender } + { amount-ustx: amount-ustx, + delegated-to: delegate-to, + until-burn-ht: until-burn-ht, + pox-addr: pox-addr }) + + (ok true))) +``` +{% endcode %} + +Parameters: + +* `amount-ustx`: amount delegating (uSTX) +* `delegate-to`: Stacks address of the pool operator +* `until-burn-ht`: optional expiry burn height for the delegation +* `pox-addr`: optional Bitcoin address where this delegator wants rewards sent (if supplied, pool operator must send rewards to this address) + +Checks: caller allowed, `pox-addr` version valid if provided, delegator not already delegating. Updates `delegation-state`. No STX are locked yet — the pool operator must call `delegate-stack-stx`. + +*** + +### delegate-stack-stx + +Called by the pool operator to partially stack a delegator's STX. + +{% code title="pox-4: delegate-stack-stx" %} +```clojure +(define-public (delegate-stack-stx (stacker principal) + (amount-ustx uint) + (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (start-burn-ht uint) + (lock-period uint)) + ;; this stacker's first reward cycle is the _next_ reward cycle + (let ((first-reward-cycle (+ u1 (current-pox-reward-cycle))) + (specified-reward-cycle (+ u1 (burn-height-to-reward-cycle start-burn-ht))) + (unlock-burn-height (reward-cycle-to-burn-height (+ (current-pox-reward-cycle) u1 lock-period)))) + ;; the start-burn-ht must result in the next reward cycle, do not allow stackers + ;; to "post-date" their `stack-stx` transaction + (asserts! (is-eq first-reward-cycle specified-reward-cycle) + (err ERR_INVALID_START_BURN_HEIGHT)) + + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + + ;; stacker must have delegated to the caller + (let ((delegation-info (unwrap! (get-check-delegation stacker) (err ERR_STACKING_PERMISSION_DENIED)))) + ;; must have delegated to tx-sender + (asserts! (is-eq (get delegated-to delegation-info) tx-sender) + (err ERR_STACKING_PERMISSION_DENIED)) + ;; must have delegated enough stx + (asserts! (>= (get amount-ustx delegation-info) amount-ustx) + (err ERR_DELEGATION_TOO_MUCH_LOCKED)) + ;; if pox-addr is set, must be equal to pox-addr + (asserts! (match (get pox-addr delegation-info) + specified-pox-addr (is-eq pox-addr specified-pox-addr) + true) + (err ERR_DELEGATION_POX_ADDR_REQUIRED)) + ;; delegation must not expire before lock period + (asserts! (match (get until-burn-ht delegation-info) + until-burn-ht (>= until-burn-ht + unlock-burn-height) + true) + (err ERR_DELEGATION_EXPIRES_DURING_LOCK)) + ) + + ;; stacker principal must not be stacking + (asserts! (is-none (get-stacker-info stacker)) + (err ERR_STACKING_ALREADY_STACKED)) + + ;; the Stacker must have sufficient unlocked funds + (asserts! (>= (stx-get-balance stacker) amount-ustx) + (err ERR_STACKING_INSUFFICIENT_FUNDS)) + + ;; ensure that stacking can be performed + (try! (minimal-can-stack-stx pox-addr amount-ustx first-reward-cycle lock-period)) + + ;; register the PoX address with the amount stacked via partial stacking + ;; before it can be included in the reward set, this must be committed! + (add-pox-partial-stacked pox-addr first-reward-cycle lock-period amount-ustx) + + ;; add stacker record + (map-set stacking-state + { stacker: stacker } + { pox-addr: pox-addr, + first-reward-cycle: first-reward-cycle, + reward-set-indexes: (list), + lock-period: lock-period, + delegated-to: (some tx-sender) }) + + ;; return the lock-up information, so the node can actually carry out the lock. + (ok { stacker: stacker, + lock-amount: amount-ustx, + unlock-burn-height: unlock-burn-height }))) +``` +{% endcode %} + +This function validates the delegation record, ensures the delegator has the funds and is not already stacking, runs lightweight stacking checks, registers the partial stacked amount, and updates `stacking-state`. The STX remain partially stacked until the operator commits. + +*** + +### stack-aggregation-commit-indexed / inner-stack-aggregation-commit + +The `stack-aggregation-commit-indexed` function wraps the private `inner-stack-aggregation-commit`. The private function commits partially stacked amounts into the reward set so each pox-addr obtains a reward-slot index. + +{% code title="pox-4: inner-stack-aggregation-commit" %} +```clojure +(define-private (inner-stack-aggregation-commit (pox-addr { version: (buff 1), hashbytes: (buff 32) }) + (reward-cycle uint) + (signer-sig (optional (buff 65))) + (signer-key (buff 33)) + (max-amount uint) + (auth-id uint)) + (let ((partial-stacked + ;; fetch the partial commitments + (unwrap! (map-get? partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (err ERR_STACKING_NO_SUCH_PRINCIPAL)))) + ;; must be called directly by the tx-sender or by an allowed contract-caller + (asserts! (check-caller-allowed) + (err ERR_STACKING_PERMISSION_DENIED)) + (let ((amount-ustx (get stacked-amount partial-stacked))) + (try! (consume-signer-key-authorization pox-addr reward-cycle "agg-commit" u1 signer-sig signer-key amount-ustx max-amount auth-id)) + (try! (can-stack-stx pox-addr amount-ustx reward-cycle u1)) + ;; Add the pox addr to the reward cycle, and extract the index of the PoX address + ;; so the delegator can later use it to call stack-aggregation-increase. + (let ((add-pox-addr-info + (add-pox-addr-to-ith-reward-cycle + u0 + { pox-addr: pox-addr, + first-reward-cycle: reward-cycle, + num-cycles: u1, + reward-set-indexes: (list), + stacker: none, + signer: signer-key, + amount-ustx: amount-ustx, + i: u0 })) + (pox-addr-index (unwrap-panic + (element-at (get reward-set-indexes add-pox-addr-info) u0)))) + + ;; don't update the stacking-state map, + ;; because it _already has_ this stacker's state + ;; don't lock the STX, because the STX is already locked + ;; + ;; clear the partial-stacked state, and log it + (map-delete partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle }) + (map-set logged-partial-stacked-by-cycle { pox-addr: pox-addr, sender: tx-sender, reward-cycle: reward-cycle } partial-stacked) + (ok pox-addr-index))))) +``` +{% endcode %} + +Key points: + +* Validates caller and signer signature. +* Validates stacking conditions. +* Adds the aggregated pox-addr to the reward cycle and returns its reward-set index. +* Deletes the partial-stacked entry and logs it. + +*** + +## How Stacking Reward Distribution Works + +All of the above stacking functions take a `pox-addr` field that corresponds to a Bitcoin address where BTC rewards will be sent. It's important to understand how these addresses are used and how reward distribution is handled. + +How Bitcoin rewards are distributed is primarily up to the discretion of the pool operator. Since PoX reward distributions are handled using Bitcoin transactions, there is currently not an effective way to automate their distribution to individual delegated stackers. + +Role of `pox-addr` by function: + +* stack-stx: Bitcoin address for the solo stacker to receive rewards. +* delegate-stx: Optional. If omitted, the pool operator decides where to send this delegator's rewards. If provided, the pool operator must send rewards to that address. Note: if provided, the delegator must have enough STX to meet the minimum stacking amount (each unique `pox-addr` consumes a reward slot). +* delegate-stack-stx and stack-aggregation-commit-indexed: `pox-addr` is where the pool operator will receive BTC rewards for that aggregated stake. Pool operators typically use wrapper contracts or off-chain accounting to distribute BTC to delegators. + +*** + +## Errors + +You may encounter several errors when trying to perform stacking operations. Below are some of the more common errors with explanations and how to resolve them. + +
+ +Error 35 - ERR_INVALID_SIGNATURE_PUBKEY + +This is likely the most common error you will encounter, and you'll usually see it in a failed `stack-stx` or `stack-aggregation-commit` transaction. + +This error occurs in `consume-signer-key-authorization` which is called any time a signer signature is provided. + +Possible causes: + +* The public key you used to generate the signer signature is not the same as the one you are passing in to the `signer-key` field. +* One of the fields you passed in to generate your signer signature does not match the field you passed in to either the `stack-stx` or `stack-aggregation-commit` function. + +How to fix: verify that the signer signature was generated using the exact same signer public key and parameters (amount, pox-addr/reward-cycle, lock period, max-amount, auth-id, etc.) as what you are passing into the contract call. + +
+ +
+ +Error 4 - ERR_STACKING_NO_SUCH_PRINCIPAL + +This error means the contract lookup for a partially stacked entry failed. The stacking contract looks up partially stacked STX (after `delegate-stack-stx`) by the key `(pox-addr, stx-address, reward-cycle)`. If any of those parameters are wrong when generating the signature or calling `stack-aggregation-commit`, the lookup will fail. + +How to fix: check that the `pox-addr`, `stacker` principal (stx address), and `reward-cycle` values match exactly what was used in `delegate-stack-stx` / the signature generation. See the [stacking guide](broken-reference) for delegation flow details. + +
+ +
+ +Error 24 - ERR_INVALID_START_BURN_HEIGHT + +This means the `start-burn-height` parameter parsed was invalid (it corresponded to a past or future cycle rather than the current next reward cycle). You will mostly see this in `stack-stx` or `delegate-stack-stx` failed transactions. + +How to fix: set `start-burn-ht` to the current burn block height corresponding to the next reward cycle (or compute it using node APIs / libraries that map burn height to reward cycles). + +
diff --git a/docs/reference/clarity/functions.md b/docs/reference/clarity/functions.md new file mode 100644 index 0000000000..b3dc7d0d2a --- /dev/null +++ b/docs/reference/clarity/functions.md @@ -0,0 +1,2104 @@ +--- +description: The complete reference guide to all Clarity functions. +--- + +# Functions + +## \* (multiply) + +Introduced in: **Clarity 1** + +**input:** `int, ... | uint, ...`\ +**output:** `int | uint`\ +**signature:** `(* i1 i2...)` + +**description:**\ +Multiplies a variable number of integer inputs and returns the result. In the event of an _overflow_, throws a runtime error. + +**example:** + +```clojure +(* 2 3) ;; Returns 6 +(* 5 2) ;; Returns 10 +(* 2 2 2) ;; Returns 8 +``` + +*** + +## + (add) + +Introduced in: **Clarity 1** + +**input:** `int, ... | uint, ...`\ +**output:** `int | uint`\ +**signature:** `(+ i1 i2...)` + +**description:**\ +Adds a variable number of integer inputs and returns the result. In the event of an _overflow_, throws a runtime error. + +**example:** + +```clojure +(+ 1 2 3) ;; Returns 6 +``` + +*** + +## - (subtract) + +Introduced in: **Clarity 1** + +**input:** `int, ... | uint, ...`\ +**output:** `int | uint`\ +**signature:** `(- i1 i2...)` + +**description:**\ +Subtracts a variable number of integer inputs and returns the result. In the event of an _underflow_, throws a runtime error. + +**example:** + +```clojure +(- 2 1 1) ;; Returns 0 +(- 0 3) ;; Returns -3 +``` + +*** + +## / (divide) + +Introduced in: **Clarity 1** + +**input:** `int, ... | uint, ...`\ +**output:** `int | uint`\ +**signature:** `(/ i1 i2...)` + +**description:**\ +Integer divides a variable number of integer inputs and returns the result. In the event of division by zero, throws a runtime error. + +**example:** + +```clojure +(/ 2 3) ;; Returns 0 +(/ 5 2) ;; Returns 2 +(/ 4 2 2) ;; Returns 1 +``` + +*** + +## < (less than) + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff`\ +**output:** `bool`\ +**signature:** `(< i1 i2)` + +**description:**\ +Compares two integers (or other comparable types), returning `true` if `i1` is less than `i2` and `false` otherwise. i1 and i2 must be of the same type. + +* Starting with Stacks 1.0: comparable types are `int` and `uint`. +* Starting with Stacks 2.1: comparable types also include `string-ascii`, `string-utf8` and `buff`. + +**example:** + +```clojure +(< 1 2) ;; Returns true +(< 5 2) ;; Returns false +(< "aaa" "baa") ;; Returns true +(< "aa" "aaa") ;; Returns true +(< 0x01 0x02) ;; Returns true +(< 5 u2) ;; Throws type error +``` + +*** + +## <= (less than or equal) + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff`\ +**output:** `bool`\ +**signature:** `(<= i1 i2)` + +**description:**\ +Compares two values, returning `true` if `i1` is less than or equal to `i2`. Types must match. Same type support notes as `<`. + +**example:** + +```clojure +(<= 1 1) ;; Returns true +(<= 5 2) ;; Returns false +(<= "aaa" "baa") ;; Returns true +(<= "aa" "aaa") ;; Returns true +(<= 0x01 0x02) ;; Returns true +(<= 5 u2) ;; Throws type error +``` + +*** + +## > (greater than) + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff`\ +**output:** `bool`\ +**signature:** `(> i1 i2)` + +**description:**\ +Compares two values, returning `true` if `i1` is greater than `i2`. Types must match. Same type support notes as `<`. + +**example:** + +```clojure +(> 1 2) ;; Returns false +(> 5 2) ;; Returns true +(> "baa" "aaa") ;; Returns true +(> "aaa" "aa") ;; Returns true +(> 0x02 0x01) ;; Returns true +(> 5 u2) ;; Throws type error +``` + +*** + +## >= (greater than or equal) + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff`\ +**output:** `bool`\ +**signature:** `(>= i1 i2)` + +**description:**\ +Compares two values, returning `true` if `i1` is greater than or equal to `i2`. Types must match. Same type support notes as `<`. + +**example:** + +```clojure +(>= 1 1) ;; Returns true +(>= 5 2) ;; Returns true +(>= "baa" "aaa") ;; Returns true +(>= "aaa" "aa") ;; Returns true +(>= 0x02 0x01) ;; Returns true +(>= 5 u2) ;; Throws type error +``` + +*** + +## and + +Introduced in: **Clarity 1** + +**input:** `bool, ...`\ +**output:** `bool`\ +**signature:** `(and b1 b2 ...)` + +**description:**\ +Returns `true` if all boolean inputs are `true`. Arguments are evaluated in-order and lazily (short-circuits on `false`). + +**example:** + +```clojure +(and true false) ;; Returns false +(and (is-eq (+ 1 2) 1) (is-eq 4 4)) ;; Returns false +(and (is-eq (+ 1 2) 3) (is-eq 4 4)) ;; Returns true +``` + +*** + +## append + +Introduced in: **Clarity 1** + +**input:** `list A, A`\ +**output:** `list`\ +**signature:** `(append (list 1 2 3 4) 5)` + +**description:**\ +Takes a list and a value of the same entry type, and returns a new list with max\_len += 1 (effectively appending the value). + +**example:** + +```clojure +(append (list 1 2 3 4) 5) ;; Returns (1 2 3 4 5) +``` + +*** + +## as-contract + +Introduced in: **Clarity 1** + +**input:** `A`\ +**output:** `A`\ +**signature:** `(as-contract expr)` + +**description:**\ +Executes `expr` with the tx-sender switched to the contract's principal and returns the result. + +**example:** + +```clojure +(as-contract tx-sender) ;; Returns S1G2081040G2081040G2081040G208105NK8PE5.docs-test +``` + +*** + +## as-max-len? + +Introduced in: **Clarity 1** + +**input:** `sequence_A, uint`\ +**output:** `(optional sequence_A)`\ +**signature:** `(as-max-len? sequence max_length)` + +**description:**\ +If the sequence length ≤ max\_length, returns `(some sequence)`, otherwise `none`. Applies to `(list A)`, `buff`, `string-ascii`, `string-utf8`. + +**example:** + +```clojure +(as-max-len? (list 2 2 2) u3) ;; Returns (some (2 2 2)) +(as-max-len? (list 1 2 3) u2) ;; Returns none +(as-max-len? "hello" u10) ;; Returns (some "hello") +(as-max-len? 0x010203 u10) ;; Returns (some 0x010203) +``` + +*** + +## asserts! + +Introduced in: **Clarity 1** + +**input:** `bool, C`\ +**output:** `bool`\ +**signature:** `(asserts! bool-expr thrown-value)` + +**description:**\ +If `bool-expr` is `true`, returns `true` and continues. If `false`, returns `thrown-value` and exits current control-flow. + +**example:** + +```clojure +(asserts! (is-eq 1 1) (err 1)) ;; Returns true +``` + +*** + +## at-block + +Introduced in: **Clarity 1** + +**input:** `(buff 32), A`\ +**output:** `A`\ +**signature:** `(at-block id-block-hash expr)` + +**description:**\ +Evaluates `expr` as if evaluated at the end of the block identified by `id-block-hash`. `expr` must be read-only. The block hash must be from `id-header-hash`. + +**example:** + +```clojure +(define-data-var data int 1) +(at-block 0x0000000000000000000000000000000000000000000000000000000000000000 block-height) ;; Returns u0 +(at-block (get-block-info? id-header-hash 0) (var-get data)) ;; Throws NoSuchDataVariable because `data` wasn't initialized at block height 0 +``` + +*** + +## begin + +Introduced in: **Clarity 1** + +**input:** `AnyType, ... A`\ +**output:** `A`\ +**signature:** `(begin expr1 expr2 expr3 ... expr-last)` + +**description:**\ +Evaluates each expression in order and returns the value of the last expression. Note: intermediary statements returning a response type must be checked. + +**example:** + +```clojure +(begin (+ 1 2) 4 5) ;; Returns 5 +``` + +*** + +## bit-and + +Introduced in: **Clarity 2** + +**input:** `int, ... | uint, ...`\ +**output:** `int | uint`\ +**signature:** `(bit-and i1 i2...)` + +**description:**\ +Bitwise AND across a variable number of integer inputs. + +**example:** + +```clojure +(bit-and 24 16) ;; Returns 16 +(bit-and 28 24 -1) ;; Returns 24 +(bit-and u24 u16) ;; Returns u16 +(bit-and -128 -64) ;; Returns -128 +``` + +*** + +## bit-not + +Introduced in: **Clarity 2** + +**input:** `int | uint`\ +**output:** `int | uint`\ +**signature:** `(bit-not i1)` + +**description:**\ +Returns the one's complement (bitwise NOT) of `i1`. + +**example:** + +```clojure +(bit-not 3) ;; Returns -4 +(bit-not u128) ;; Returns u340282366920938463463374607431768211327 +(bit-not 128) ;; Returns -129 +(bit-not -128) ;; Returns 127 +``` + +*** + +## bit-or + +Introduced in: **Clarity 2** + +**input:** `int, ... | uint, ...`\ +**output:** `int | uint`\ +**signature:** `(bit-or i1 i2...)` + +**description:**\ +Bitwise inclusive OR across a variable number of integer inputs. + +**example:** + +```clojure +(bit-or 4 8) ;; Returns 12 +(bit-or 1 2 4) ;; Returns 7 +(bit-or 64 -32 -16) ;; Returns -16 +(bit-or u2 u4 u32) ;; Returns u38 +``` + +*** + +## bit-shift-left + +Introduced in: **Clarity 2** + +**input:** `int, uint | uint, uint`\ +**output:** `int | uint`\ +**signature:** `(bit-shift-left i1 shamt)` + +**description:**\ +Shifts bits of `i1` left by `shamt` modulo 128. Does not check for arithmetic overflow — use `*`, `/`, `pow` if overflow detection is needed. + +**example:** + +```clojure +(bit-shift-left 2 u1) ;; Returns 4 +(bit-shift-left 16 u2) ;; Returns 64 +(bit-shift-left -64 u1) ;; Returns -128 +(bit-shift-left u4 u2) ;; Returns u16 +(bit-shift-left 123 u9999999999) ;; Returns -170141183460469231731687303715884105728 +(bit-shift-left -1 u7) ;; Returns -128 +(bit-shift-left -1 u128) ;; Returns -1 +``` + +*** + +## bit-shift-right + +Introduced in: **Clarity 2** + +**input:** `int, uint | uint, uint`\ +**output:** `int | uint`\ +**signature:** `(bit-shift-right i1 shamt)` + +**description:**\ +Shifts bits of `i1` right by `shamt` modulo 128. For `uint` fills with zeros; for `int` preserves sign bit. Does not check for arithmetic overflow. + +**example:** + +```clojure +(bit-shift-right 2 u1) ;; Returns 1 +(bit-shift-right 128 u2) ;; Returns 32 +(bit-shift-right -64 u1) ;; Returns -32 +(bit-shift-right u128 u2) ;; Returns u32 +(bit-shift-right 123 u9999999999) ;; Returns 0 +(bit-shift-right -128 u7) ;; Returns -1 +``` + +*** + +## bit-xor + +Introduced in: **Clarity 2** + +**input:** `int, ... | uint, ...`\ +**output:** `int | uint`\ +**signature:** `(bit-xor i1 i2...)` + +**description:**\ +Bitwise exclusive OR across a variable number of integer inputs. + +**example:** + +```clojure +(bit-xor 1 2) ;; Returns 3 +(bit-xor 120 280) ;; Returns 352 +(bit-xor -128 64) ;; Returns -64 +(bit-xor u24 u4) ;; Returns u28 +(bit-xor 1 2 4 -1) ;; Returns -8 +``` + +*** + +## buff-to-int-be + +Introduced in: **Clarity 2** + +**input:** `(buff 16)`\ +**output:** `int`\ +**signature:** `(buff-to-int-be (buff 16))` + +**description:**\ +Converts a buffer to a signed integer using big-endian encoding. Buffer up to 16 bytes; if fewer, it behaves as if left-zero-padded. Available starting Stacks 2.1. + +**example:** + +```clojure +(buff-to-int-be 0x01) ;; Returns 1 +(buff-to-int-be 0x00000000000000000000000000000001) ;; Returns 1 +(buff-to-int-be 0xffffffffffffffffffffffffffffffff) ;; Returns -1 +(buff-to-int-be 0x) ;; Returns 0 +``` + +*** + +## buff-to-int-le + +Introduced in: **Clarity 2** + +**input:** `(buff 16)`\ +**output:** `int`\ +**signature:** `(buff-to-int-le (buff 16))` + +**description:**\ +Converts a buffer to a signed integer using little-endian encoding. Up to 16 bytes; fewer bytes behave as right-zero-padded. Available starting Stacks 2.1. + +**example:** + +```clojure +(buff-to-int-le 0x01) ;; Returns 1 +(buff-to-int-le 0x01000000000000000000000000000000) ;; Returns 1 +(buff-to-int-le 0xffffffffffffffffffffffffffffffff) ;; Returns -1 +(buff-to-int-le 0x) ;; Returns 0 +``` + +*** + +## buff-to-uint-be + +Introduced in: **Clarity 2** + +**input:** `(buff 16)`\ +**output:** `uint`\ +**signature:** `(buff-to-uint-be (buff 16))` + +**description:**\ +Converts a buffer to an unsigned integer using big-endian encoding. Up to 16 bytes; fewer bytes behave as left-zero-padded. Available starting Stacks 2.1. + +**example:** + +```clojure +(buff-to-uint-be 0x01) ;; Returns u1 +(buff-to-uint-be 0x00000000000000000000000000000001) ;; Returns u1 +(buff-to-uint-be 0xffffffffffffffffffffffffffffffff) ;; Returns u340282366920938463463374607431768211455 +(buff-to-uint-be 0x) ;; Returns u0 +``` + +*** + +## buff-to-uint-le + +Introduced in: **Clarity 2** + +**input:** `(buff 16)`\ +**output:** `uint`\ +**signature:** `(buff-to-uint-le (buff 16))` + +**description:**\ +Converts a buffer to an unsigned integer using little-endian encoding. Up to 16 bytes; fewer bytes behave as right-zero-padded. Available starting Stacks 2.1. + +**example:** + +```clojure +(buff-to-uint-le 0x01) ;; Returns u1 +(buff-to-uint-le 0x01000000000000000000000000000000) ;; Returns u1 +(buff-to-uint-le 0xffffffffffffffffffffffffffffffff) ;; Returns u340282366920938463463374607431768211455 +(buff-to-uint-le 0x) ;; Returns u0 +``` + +*** + +## concat + +Introduced in: **Clarity 1** + +**input:** `sequence_A, sequence_A`\ +**output:** `sequence_A`\ +**signature:** `(concat sequence1 sequence2)` + +**description:**\ +Concatenates two sequences of the same type. Applicable to `(list A)`, `buff`, `string-ascii`, `string-utf8`. + +**example:** + +```clojure +(concat (list 1 2) (list 3 4)) ;; Returns (1 2 3 4) +(concat "hello " "world") ;; Returns "hello world" +(concat 0x0102 0x0304) ;; Returns 0x01020304 +``` + +*** + +## contract-call? + +Introduced in: **Clarity 1** + +**input:** `ContractName, PublicFunctionName, Arg0, ...`\ +**output:** `(response A B)`\ +**signature:** `(contract-call? .contract-name function-name arg0 arg1 ...)` + +**description:**\ +Executes a public function on another contract (not the current contract). If that function returns `err`, any DB changes resulting from the call are aborted; if `ok`, DB changes occurred. + +**example:** + +```clojure +;; instantiate the sample-contracts/tokens.clar contract first +(as-contract (contract-call? .tokens mint! u19)) ;; Returns (ok u19) +``` + +*** + +## contract-of + +Introduced in: **Clarity 1** + +**input:** `Trait`\ +**output:** `principal`\ +**signature:** `(contract-of .contract-name)` + +**description:**\ +Returns the principal of the contract implementing the trait. + +**example:** + +```clojure +(use-trait token-a-trait 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF.token-a.token-trait) +(define-public (forward-get-balance (user principal) (contract )) + (begin + (ok (contract-of contract)))) ;; returns the principal of the contract implementing +``` + +*** + +## default-to + +Introduced in: **Clarity 1** + +**input:** `A, (optional A)`\ +**output:** `A`\ +**signature:** `(default-to default-value option-value)` + +**description:**\ +If the second argument is `(some v)`, returns `v`. If it is `none`, returns `default-value`. + +**example:** + +```clojure +(define-map names-map { name: (string-ascii 12) } { id: int }) +(map-set names-map { name: "blockstack" } { id: 1337 }) +(default-to 0 (get id (map-get? names-map (tuple (name "blockstack"))))) ;; Returns 1337 +(default-to 0 (get id (map-get? names-map (tuple (name "non-existant"))))) ;; Returns 0 +``` + +*** + +## define-constant + +Introduced in: **Clarity 1** + +**input:** `MethodSignature, MethodBody`\ +**output:** `Not Applicable`\ +**signature:** `(define-constant name expression)` + +**description:**\ +Defines a private constant evaluated at contract launch. Must be top-level. Be mindful of definition order. + +**example:** + +```clojure +(define-constant four (+ 2 2)) +(+ 4 four) ;; Returns 8 +``` + +*** + +## define-data-var + +Introduced in: **Clarity 1** + +**input:** `VarName, TypeDefinition, Value`\ +**output:** `Not Applicable`\ +**signature:** `(define-data-var var-name type value)` + +**description:**\ +Defines a new persisted variable for the contract. Only modifiable by the contract. Must be top-level. + +**example:** + +```clojure +(define-data-var size int 0) +(define-private (set-size (value int)) + (var-set size value)) +(set-size 1) +(set-size 2) +``` + +*** + +## define-fungible-token + +Introduced in: **Clarity 1** + +**input:** `TokenName, `\ +**output:** `Not Applicable`\ +**signature:** `(define-fungible-token token-name )` + +**description:**\ +Defines a fungible token class in the contract. Optional total supply caps minting. Must be top-level. + +**example:** + +```clojure +(define-fungible-token stacks) +(define-fungible-token limited-supply-stacks u100) +``` + +*** + +## define-map + +Introduced in: **Clarity 1** + +**input:** `MapName, TypeDefinition, TypeDefinition`\ +**output:** `Not Applicable`\ +**signature:** `(define-map map-name key-type value-type)` + +**description:**\ +Defines a data map stored by the contract. Must be top-level. + +**example:** + +```clojure +(define-map squares { x: int } { square: int }) +(define-private (add-entry (x int)) + (map-insert squares { x: 2 } { square: (* x x) })) +(add-entry 1) +(add-entry 2) +``` + +*** + +## define-non-fungible-token + +Introduced in: **Clarity 1** + +**input:** `AssetName, TypeSignature`\ +**output:** `Not Applicable`\ +**signature:** `(define-non-fungible-token asset-name asset-identifier-type)` + +**description:**\ +Defines an NFT class in the contract. Asset identifiers must be unique. Must be top-level. + +**example:** + +```clojure +(define-non-fungible-token names (buff 50)) +``` + +*** + +## define-private + +Introduced in: **Clarity 1** + +**input:** `MethodSignature, MethodBody`\ +**output:** `Not Applicable`\ +**signature:** `(define-private (function-name (arg-name-0 arg-type-0) ...) function-body)` + +**description:**\ +Defines a private function callable only within the contract. Must be top-level. + +**example:** + +```clojure +(define-private (max-of (i1 int) (i2 int)) + (if (> i1 i2) + i1 + i2)) +(max-of 4 6) ;; Returns 6 +``` + +*** + +## define-public + +Introduced in: **Clarity 1** + +**input:** `MethodSignature, MethodBody`\ +**output:** `Not Applicable`\ +**signature:** `(define-public (function-name (arg-name-0 arg-type-0) ...) function-body)` + +**description:**\ +Defines a public transaction function. Must return a ResponseType (`ok` or `err`). DB changes are aborted if `err`. Must be top-level. + +**example:** + +```clojure +(define-public (hello-world (input int)) + (begin + (print (+ 2 input)) + (ok input))) +``` + +*** + +## define-read-only + +Introduced in: **Clarity 1** + +**input:** `MethodSignature, MethodBody`\ +**output:** `Not Applicable`\ +**signature:** `(define-read-only (function-name (arg-name-0 arg-type-0) ...) function-body)` + +**description:**\ +Defines a public read-only function. Cannot modify data maps or call mutating functions. May return any type. Must be top-level. + +**example:** + +```clojure +(define-read-only (just-return-one-hundred) + (* 10 10)) +``` + +*** + +## define-trait + +Introduced in: **Clarity 1** + +**input:** `VarName, [MethodSignature]`\ +**output:** `Not Applicable`\ +**signature:** `(define-trait trait-name ((func1-name (arg1-type ...) (return-type))))` + +**description:**\ +Defines a trait (interface) other contracts can implement. Must be top-level. Notes about Clarity 1 vs Clarity 2 trait usage and implicit casting in Clarity 2 are included. + +**example:** + +```clojure +(define-trait token-trait + ((transfer? (principal principal uint) (response uint uint)) + (get-balance (principal) (response uint uint)))) +``` + +*** + +## element-at + +Introduced in: **Clarity 1** + +**input:** `sequence_A, uint`\ +**output:** `(optional A)`\ +**signature:** `(element-at? sequence index)` + +**description:**\ +Returns the element at `index` in the sequence as an optional. Applicable types: `(list A)`, `buff`, `string-ascii`, `string-utf8`. In Clarity 1 spelled `element-at` (alias). + +**example:** + +```clojure +(element-at? "blockstack" u5) ;; Returns (some "s") +(element-at? (list 1 2 3 4 5) u5) ;; Returns none +(element-at? (list 1 2 3 4 5) (+ u1 u2)) ;; Returns (some 4) +(element-at? "abcd" u1) ;; Returns (some "b") +(element-at? 0xfb01 u1) ;; Returns (some 0x01) +``` + +*** + +## element-at? + +Introduced in: **Clarity 2** + +(Same as element-at; retained as Clarity 2 preferred spelling) + +**example:** (see element-at above) + +*** + +## err + +Introduced in: **Clarity 1** + +**input:** `A`\ +**output:** `(response A B)`\ +**signature:** `(err value)` + +**description:**\ +Constructs an `err` response. Use for returning errors from public functions; indicates DB changes should be rolled back. + +**example:** + +```clojure +(err true) ;; Returns (err true) +``` + +*** + +## filter + +Introduced in: **Clarity 1** + +**input:** `Function(A) -> bool, sequence_A`\ +**output:** `sequence_A`\ +**signature:** `(filter func sequence)` + +**description:**\ +Filters elements of a sequence by applying `func` to each element and keeping those where `func` returns `true`. `func` must be a literal function name. Applies to `(list A)`, `buff`, `string-ascii`, `string-utf8`. + +**example:** + +```clojure +(filter not (list true false true false)) ;; Returns (false false) +(define-private (is-a (char (string-utf8 1))) + (is-eq char u"a")) +(filter is-a u"acabd") ;; Returns u"aa" +``` + +*** + +## fold + +Introduced in: **Clarity 1** + +**input:** `Function(A, B) -> B, sequence_A, B`\ +**output:** `B`\ +**signature:** `(fold func sequence_A initial_B)` + +**description:**\ +Reduces a sequence to a single value by applying `func` cumulatively, starting with `initial_B`. + +**example:** + +```clojure +(fold * (list 2 2 2) 1) ;; Returns 8 +(fold - (list 3 7 11) 2) ;; Returns 5 +``` + +(Examples showing string/buffer concatenation omitted here; see original for fuller set.) + +*** + +## from-consensus-buff? + +Introduced in: **Clarity 2** + +**input:** `type-signature(t), buff`\ +**output:** `(optional t)`\ +**signature:** `(from-consensus-buff? type-signature buffer)` + +**description:**\ +Deserializes a buffer into a Clarity value using SIP-005 consensus serialization. Returns `some` on success, `none` on failure. + +**example:** + +```clojure +(from-consensus-buff? int 0x0000000000000000000000000000000001) ;; Returns (some 1) +(from-consensus-buff? uint 0x0000000000000000000000000000000001) ;; Returns none +(from-consensus-buff? bool 0x03) ;; Returns (some true) +(from-consensus-buff? principal 0x051fa46ff88886c2ef9762d970b4d2c63678835bd39d) ;; Returns (some SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) +``` + +*** + +## ft-burn? + +Introduced in: **Clarity 1** + +**input:** `TokenName, uint, principal`\ +**output:** `(response bool uint)`\ +**signature:** `(ft-burn? token-name amount sender)` + +**description:**\ +Burns (destroys) `amount` of `token-name` from `sender`'s balance. On success returns `(ok true)`. Error `(err u1)` - insufficient balance or non-positive amount. + +**example:** + +```clojure +(define-fungible-token stackaroo) +(ft-mint? stackaroo u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (ok true) +(ft-burn? stackaroo u50 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (ok true) +``` + +*** + +## ft-get-balance + +Introduced in: **Clarity 1** + +**input:** `TokenName, principal`\ +**output:** `uint`\ +**signature:** `(ft-get-balance token-name principal)` + +**description:**\ +Returns the `token-name` balance for `principal`. Token must be defined with `define-fungible-token`. + +**example:** + +```clojure +(define-fungible-token stackaroo) +(ft-mint? stackaroo u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) +(ft-get-balance stackaroo 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) ;; Returns u100 +``` + +*** + +## ft-get-supply + +Introduced in: **Clarity 1** + +**input:** `TokenName`\ +**output:** `uint`\ +**signature:** `(ft-get-supply token-name)` + +**description:**\ +Returns circulating supply for the `token-name`. Token must be defined with `define-fungible-token`. + +**example:** + +```clojure +(define-fungible-token stackaroo) +(ft-mint? stackaroo u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) +(ft-get-supply stackaroo) ;; Returns u100 +``` + +*** + +## ft-mint? + +Introduced in: **Clarity 1** + +**input:** `TokenName, uint, principal`\ +**output:** `(response bool uint)`\ +**signature:** `(ft-mint? token-name amount recipient)` + +**description:**\ +Mints `amount` of `token-name` to `recipient`. Non-positive amount returns `(err 1)`. On success returns `(ok true)`. + +**example:** + +```clojure +(define-fungible-token stackaroo) +(ft-mint? stackaroo u100 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (ok true) +``` + +*** + +## ft-transfer? + +Introduced in: **Clarity 1** + +**input:** `TokenName, uint, principal, principal`\ +**output:** `(response bool uint)`\ +**signature:** `(ft-transfer? token-name amount sender recipient)` + +**description:**\ +Transfers `amount` of `token-name` from `sender` to `recipient` (token must be defined in contract). Anyone can call; proper guards are expected. Returns `(ok true)` on success. Error codes: `(err u1)` insufficient balance, `(err u2)` sender==recipient, `(err u3)` non-positive amount. + +**example:** + +```clojure +(define-fungible-token stackaroo) +(ft-mint? stackaroo u100 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) +(ft-transfer? stackaroo u50 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF) ;; Returns (ok true) +``` + +*** + +## get + +Introduced in: **Clarity 1** + +**input:** `KeyName, (tuple) | (optional (tuple))`\ +**output:** `A`\ +**signature:** `(get key-name tuple)` + +**description:**\ +Fetches value associated with `key-name` from a tuple. If an optional tuple is supplied and is `none`, returns `none`. + +**example:** + +```clojure +(define-map names-map { name: (string-ascii 12) } { id: int }) +(map-insert names-map { name: "blockstack" } { id: 1337 }) +(get id (tuple (name "blockstack") (id 1337))) ;; Returns 1337 +(get id (map-get? names-map (tuple (name "blockstack")))) ;; Returns (some 1337) +(get id (map-get? names-map (tuple (name "non-existent")))) ;; Returns none +``` + +*** + +## get-block-info? + +Introduced in: **Clarity 1** + +**input:** `BlockInfoPropertyName, uint`\ +**output:** `(optional buff) | (optional uint)`\ +**signature:** `(get-block-info? prop-name block-height)` + +**description:**\ +Fetches data for a Stacks block at given block height. If the height doesn't exist prior to current block, returns `none`. Property names and returned types described; newer Clarity versions split this into `get-stacks-block-info?` and `get-tenure-info?`. See original for full list of properties and notes. + +**example:** + +```clojure +(get-block-info? time u0) ;; Returns (some u1557860301) +(get-block-info? header-hash u0) ;; Returns (some 0x3747...) +``` + +*** + +## get-burn-block-info? + +Introduced in: **Clarity 2** + +**input:** `BurnBlockInfoPropertyName, uint`\ +**output:** `(optional buff) | (optional (tuple ...))`\ +**signature:** `(get-burn-block-info? prop-name block-height)` + +**description:**\ +Fetches burnchain block data for the given burnchain height. Valid properties include `header-hash` and `pox-addrs`. See original for full tuple shape of `pox-addrs`. + +**example:** + +```clojure +(get-burn-block-info? header-hash u677050) ;; Returns (some 0xe671...) +(get-burn-block-info? pox-addrs u677050) ;; Returns (some (tuple (addrs (...)) (payout u123))) +``` + +*** + +## get-stacks-block-info? + +Introduced in: **Clarity 3** + +**input:** `StacksBlockInfoPropertyName, uint`\ +**output:** `(optional buff), (optional uint)`\ +**signature:** `(get-stacks-block-info? prop-name stacks-block-height)` + +**description:**\ +Replacement for `get-block-info?` in Clarity 3; fetches Stacks block data for a given height. See original for property list and behavior differences before/after epoch 3.0. + +**example:** + +```clojure +(get-stacks-block-info? time u0) ;; Returns (some u1557860301) +(get-stacks-block-info? header-hash u0) ;; Returns (some 0x3747...) +``` + +*** + +## get-tenure-info? + +Introduced in: **Clarity 3** + +**input:** `TenureInfoPropertyName, uint`\ +**output:** `(optional buff) | (optional uint)`\ +**signature:** `(get-tenure-info? prop-name stacks-block-height)` + +**description:**\ +Fetches tenure-related info at the given block height (burnchain header for tenure, miner address, time, vrf-seed, block reward, miner spend totals). Returns `none` if height is not prior to current block. See original for full notes. + +**example:** + +```clojure +(get-tenure-info? time u0) ;; Returns (some u1557860301) +(get-tenure-info? vrf-seed u0) ;; Returns (some 0xf490...) +``` + +*** + +## hash160 + +Introduced in: **Clarity 1** + +**input:** `buff|uint|int`\ +**output:** `(buff 20)`\ +**signature:** `(hash160 value)` + +**description:**\ +Computes RIPEMD160(SHA256(x)). If input is an integer, it is hashed over its little-endian representation. + +**example:** + +```clojure +(hash160 0) ;; Returns 0xe4352f72... +``` + +*** + +## if + +Introduced in: **Clarity 1** + +**input:** `bool, A, A`\ +**output:** `A`\ +**signature:** `(if bool1 expr1 expr2)` + +**description:**\ +Conditional expression: evaluates and returns `expr1` if `bool1` is true, otherwise `expr2`. Both exprs must return the same type. + +**example:** + +```clojure +(if true 1 2) ;; Returns 1 +(if (> 1 2) 1 2) ;; Returns 2 +``` + +*** + +## impl-trait + +Introduced in: **Clarity 1** + +**input:** `TraitIdentifier`\ +**output:** `Not Applicable`\ +**signature:** `(impl-trait trait-identifier)` + +**description:**\ +Asserts that the contract implements the given trait. Checked at publish time. Must be top-level. + +**example:** + +```clojure +(impl-trait 'SPAXYA5XS51713FDTQ8H94EJ4V579CXMTRNBZKSF.token-a.token-trait) +(define-public (get-balance (account principal)) + (ok u0)) +``` + +*** + +## index-of + +Introduced in: **Clarity 1** + +**input:** `sequence_A, A`\ +**output:** `(optional uint)`\ +**signature:** `(index-of? sequence item)` + +**description:**\ +Returns first index of `item` in sequence using `is-eq`. Returns `none` if not found or if empty string/buffer. Clarity 1 spelling: `index-of` (alias). + +**example:** + +```clojure +(index-of? "blockstack" "b") ;; Returns (some u0) +(index-of? (list 1 2 3 4 5) 6) ;; Returns none +``` + +*** + +## index-of? + +Introduced in: **Clarity 2** + +(Same as index-of; retained for Clarity 2) + +*** + +## int-to-ascii + +Introduced in: **Clarity 2** + +**input:** `int | uint`\ +**output:** `(string-ascii 40)`\ +**signature:** `(int-to-ascii (int|uint))` + +**description:**\ +Converts an integer to its ASCII string representation. Available starting Stacks 2.1. + +**example:** + +```clojure +(int-to-ascii 1) ;; Returns "1" +(int-to-ascii -1) ;; Returns "-1" +``` + +*** + +## int-to-utf8 + +Introduced in: **Clarity 2** + +**input:** `int | uint`\ +**output:** `(string-utf8 40)`\ +**signature:** `(int-to-utf8 (int|uint))` + +**description:**\ +Converts an integer to its UTF-8 string representation. Available starting Stacks 2.1. + +**example:** + +```clojure +(int-to-utf8 1) ;; Returns u"1" +(int-to-utf8 -1) ;; Returns u"-1" +``` + +*** + +## is-eq + +Introduced in: **Clarity 1** + +**input:** `A, A, ...`\ +**output:** `bool`\ +**signature:** `(is-eq v1 v2...)` + +**description:**\ +Returns `true` if all inputs are equal. Unlike `and`, does not short-circuit. All arguments must be the same type. + +**example:** + +```clojure +(is-eq 1 1) ;; Returns true +(is-eq true false) ;; Returns false +(is-eq "abc" "abc") ;; Returns true +``` + +*** + +## is-err / is-ok / is-none / is-some + +Introduced in: **Clarity 1** + +* `(is-err value)` returns `true` if `value` is `(err ...)`. +* `(is-ok value)` returns `true` if `value` is `(ok ...)`. +* `(is-none value)` returns `true` if `value` is `none`. +* `(is-some value)` returns `true` if `value` is `(some ...)`. + +**examples:** + +```clojure +(is-err (err 1)) ;; Returns true +(is-ok (ok 1)) ;; Returns true +(is-none none) ;; Returns true +(is-some (some 1)) ;; Returns true +``` + +*** + +## is-standard + +Introduced in: **Clarity 2** + +**input:** `principal`\ +**output:** `bool`\ +**signature:** `(is-standard standard-or-contract-principal)` + +**description:**\ +Tests whether a principal matches the current network type (mainnet vs testnet) and therefore can spend tokens on that network. Available starting Stacks 2.1. + +**example:** + +```clojure +(is-standard 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6) ;; true on testnet; false on mainnet +``` + +*** + +## keccak256 + +Introduced in: **Clarity 1** + +**input:** `buff|uint|int`\ +**output:** `(buff 32)`\ +**signature:** `(keccak256 value)` + +**description:**\ +Computes KECCAK256(value). If input is an integer, it is hashed over its little-endian representation. + +**example:** + +```clojure +(keccak256 0) ;; Returns 0xf490de29... +``` + +*** + +## len + +Introduced in: **Clarity 1** + +**input:** `sequence_A`\ +**output:** `uint`\ +**signature:** `(len sequence)` + +**description:**\ +Returns length of a sequence. Applies to `(list A)`, `buff`, `string-ascii`, `string-utf8`. + +**example:** + +```clojure +(len "blockstack") ;; Returns u10 +(len (list 1 2 3 4 5)) ;; Returns u5 +(len 0x010203) ;; Returns u3 +``` + +*** + +## let + +Introduced in: **Clarity 1** + +**input:** `((name1 AnyType) ...), AnyType, ... A`\ +**output:** `A`\ +**signature:** `(let ((name1 expr1) ...) expr-body1 ... expr-body-last)` + +**description:**\ +Binds sequential variables then evaluates the body expressions in that context. Returns last body expression's value. + +**example:** + +```clojure +(let ((a 2) (b (+ 5 6 7))) + (print a) + (print b) + (+ a b)) ;; Returns 20 +``` + +*** + +## list + +Introduced in: **Clarity 1** + +**input:** `A, ...`\ +**output:** `(list A)`\ +**signature:** `(list expr1 expr2 expr3 ...)` + +**description:**\ +Constructs a list from supplied values (must be same type). + +**example:** + +```clojure +(list (+ 1 2) 4 5) ;; Returns (3 4 5) +``` + +*** + +## log2 + +Introduced in: **Clarity 1** + +**input:** `int | uint`\ +**output:** `int | uint`\ +**signature:** `(log2 n)` + +**description:**\ +Returns floor(log2(n)). Fails on negative numbers. + +**example:** + +```clojure +(log2 u8) ;; Returns u3 +(log2 8) ;; Returns 3 +``` + +*** + +## map + +Introduced in: **Clarity 1** + +**input:** `Function(A, B, ..., N) -> X, sequence_A, sequence_B, ...`\ +**output:** `(list X)`\ +**signature:** `(map func sequence_A sequence_B ...)` + +**description:**\ +Applies `func` to each corresponding element of input sequences and returns a list of results. `func` must be a literal function name. Output is always a list. + +**example:** + +```clojure +(map + (list 1 2 3) (list 1 2 3) (list 1 2 3)) ;; Returns (3 6 9) +``` + +*** + +## map-delete / map-get? / map-insert / map-set + +Introduced in: **Clarity 1** + +Operations for manipulating contract data maps: + +* `(map-delete map-name key-tuple)` — removes entry; returns `true` if removed, `false` if none existed. +* `(map-get? map-name key-tuple)` — returns `(some value)` or `none`. +* `(map-insert map-name key-tuple value-tuple)` — inserts only if key absent; returns `true` if inserted, `false` if existed. +* `(map-set map-name key-tuple value-tuple)` — blind overwrite; returns `true`. + +Examples exist in the original content (omitted here for brevity). + +*** + +## match + +Introduced in: **Clarity 1** + +**input:** `(optional A) name expression expression | (response A B) name expression name expression`\ +**output:** `C`\ +**signature:** `(match opt-input some-binding-name some-branch none-branch) | (match-resp input ok-binding-name ok-branch err-binding-name err-branch)` + +**description:**\ +Destructures `optional` and `response` types and evaluates only the matching branch. See original for type-checking caveats. + +**example:** + +```clojure +(define-private (add-10 (x (optional int))) + (match x + value (+ 10 value) + 10)) +(add-10 (some 5)) ;; Returns 15 +(add-10 none) ;; Returns 10 +``` + +*** + +## merge + +Introduced in: **Clarity 1** + +**input:** `tuple, tuple`\ +**output:** `tuple`\ +**signature:** `(merge tuple { key1: val1 })` + +**description:**\ +Returns a new tuple combining fields (non-mutating). + +**example:** + +```clojure +(merge user { address: (some 'SPAXYA5X...) }) ;; Returns merged tuple +``` + +*** + +## mod + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff`\ +**output:** `int | uint`\ +**signature:** `(mod i1 i2)` + +**description:**\ +Returns remainder of integer division; division by zero throws runtime error. + +**example:** + +```clojure +(mod 5 2) ;; Returns 1 +``` + +*** + +## nft-burn? / nft-get-owner? / nft-mint? / nft-transfer? + +Introduced in: **Clarity 1** + +NFT operations for assets defined with `define-non-fungible-token`: + +* `(nft-mint? asset-class asset-identifier recipient)` — mint; returns `(ok true)` or `(err u1)` if exists. +* `(nft-get-owner? asset-class asset-identifier)` — returns `(some owner)` or `none`. +* `(nft-transfer? asset-class asset-identifier sender recipient)` — transfer; returns `(ok true)` or errors. +* `(nft-burn? asset-class asset-identifier sender)` — burn; returns `(ok true)` or errors. + +Examples present in original content. + +*** + +## not + +Introduced in: **Clarity 1** + +**input:** `bool`\ +**output:** `bool`\ +**signature:** `(not b1)` + +**description:**\ +Boolean negation. + +**example:** + +```clojure +(not true) ;; Returns false +``` + +*** + +## ok + +Introduced in: **Clarity 1** + +**input:** `A`\ +**output:** `(response A B)`\ +**signature:** `(ok value)` + +**description:**\ +Constructs an `ok` response. Use for successful public function returns. + +**example:** + +```clojure +(ok 1) ;; Returns (ok 1) +``` + +*** + +## or + +Introduced in: **Clarity 1** + +**input:** `bool, ...`\ +**output:** `bool`\ +**signature:** `(or b1 b2 ...)` + +**description:**\ +Returns `true` if any input is `true`. Evaluated in-order and lazily (short-circuits on `true`). + +**example:** + +```clojure +(or true false) ;; Returns true +``` + +*** + +## pow + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff`\ +**output:** `int | uint`\ +**signature:** `(pow i1 i2)` + +**description:**\ +Returns i1^i2. Throws runtime error on overflow. Special-case rules for 0^0, i1==1, etc. Throws runtime error if exponent negative or > u32::MAX. + +**example:** + +```clojure +(pow 2 3) ;; Returns 8 +``` + +*** + +## principal-construct? + +Introduced in: **Clarity 2** + +**input:** `(buff 1), (buff 20), [(string-ascii 40)]`\ +**output:** `(response principal { error_code: uint, principal: (option principal) })`\ +**signature:** `(principal-construct? (buff 1) (buff 20) [(string-ascii 40)])` + +**description:**\ +Constructs a standard or contract principal from version byte and hash bytes, optionally with contract name. Returns `ok` with principal or `err` tuple with error code and optional principal. Available starting Stacks 2.1. + +**example:** (see original for many examples) + +*** + +## principal-destruct? + +Introduced in: **Clarity 2** + +**input:** `principal`\ +**output:** `(response (tuple ...) (tuple ...))`\ +**signature:** `(principal-destruct? principal-address)` + +**description:**\ +Decomposes a principal into `{version, hash-bytes, name}` tuple. Returns `ok` if version matches network, otherwise `err`. Available starting Stacks 2.1. + +**example:** (see original) + +*** + +## principal-of? + +Introduced in: **Clarity 1** + +**input:** `(buff 33)`\ +**output:** `(response principal uint)`\ +**signature:** `(principal-of? public-key)` + +**description:**\ +Derives the principal from a compressed public key. Returns `(err u1)` if invalid. Note: pre-2.1 bug returned testnet principals irrespective of network; fixed in 2.1. + +**example:** + +```clojure +(principal-of? 0x03adb8de4b...) ;; Returns (ok ST1AW6E...) +``` + +*** + +## print + +Introduced in: **Clarity 1** + +**input:** `A`\ +**output:** `A`\ +**signature:** `(print expr)` + +**description:**\ +Evaluates and returns its argument. On dev nodes prints to STDOUT. + +**example:** + +```clojure +(print (+ 1 2 3)) ;; Returns 6 +``` + +*** + +## replace-at? + +Introduced in: **Clarity 2** + +**input:** `sequence_A, uint, A`\ +**output:** `(optional sequence_A)`\ +**signature:** `(replace-at? sequence index element)` + +**description:**\ +Returns a new sequence with element at `index` replaced. Returns `none` if index out of bounds. + +**example:** + +```clojure +(replace-at? u"ab" u1 u"c") ;; Returns (some u"ac") +(replace-at? (list 1 2) u3 4) ;; Returns none +``` + +*** + +## secp256k1-recover? + +Introduced in: **Clarity 1** + +**input:** `(buff 32), (buff 65)`\ +**output:** `(response (buff 33) uint)`\ +**signature:** `(secp256k1-recover? message-hash signature)` + +**description:**\ +Recovers the public key from a signature over message-hash. Returns `(err u1)` if signature doesn't match, `(err u2)` if signature invalid. + +**example:** (see original) + +*** + +## secp256k1-verify + +Introduced in: **Clarity 1** + +**input:** `(buff 32), (buff 64) | (buff 65), (buff 33)`\ +**output:** `bool`\ +**signature:** `(secp256k1-verify message-hash signature public-key)` + +**description:**\ +Verifies that `signature` of `message-hash` was produced by `public-key`. Signature is 64 or 65 bytes. + +**example:** (see original) + +*** + +## sha256 / sha512 / sha512/256 + +Introduced in: **Clarity 1** + +Hash functions: + +* `(sha256 value)` → `(buff 32)` +* `(sha512 value)` → `(buff 64)` +* `(sha512/256 value)` → `(buff 32)` + +If integer supplied, hashed over little-endian representation. + +**examples:** + +```clojure +(sha256 0) ;; Returns 0x374708ff... +(sha512 1) ;; Returns 0x6fcee9a7... +(sha512/256 1) ;; Returns 0x515a7e92... +``` + +*** + +## slice? + +Introduced in: **Clarity 2** + +**input:** `sequence_A, uint, uint`\ +**output:** `(optional sequence_A)`\ +**signature:** `(slice? sequence left-position right-position)` + +**description:**\ +Returns subsequence \[left, right). If left==right returns empty sequence. Returns `none` if out of bounds or right < left. + +**example:** + +```clojure +(slice? "blockstack" u5 u10) ;; Returns (some "stack") +(slice? "abcd" u2 u2) ;; Returns (some "") +(slice? "abcd" u3 u1) ;; Returns none +``` + +*** + +## some + +Introduced in: **Clarity 1** + +**input:** `A`\ +**output:** `(optional A)`\ +**signature:** `(some value)` + +**description:**\ +Constructs `(some value)`. + +**example:** + +```clojure +(some 1) ;; Returns (some 1) +``` + +*** + +## sqrti + +Introduced in: **Clarity 1** + +**input:** `int | uint`\ +**output:** `int | uint`\ +**signature:** `(sqrti n)` + +**description:**\ +Returns floor(sqrt(n)). Fails on negative numbers. + +**example:** + +```clojure +(sqrti u11) ;; Returns u3 +``` + +*** + +## string-to-int? / string-to-uint? + +Introduced in: **Clarity 2** + +**input:** `(string-ascii 1048576) | (string-utf8 262144)`\ +**output:** `(optional int)` / `(optional uint)`\ +**signature:** `(string-to-int? str)` / `(string-to-uint? str)` + +**description:**\ +Parse string to int/uint. Returns `(some value)` on success, `none` on failure. Available starting Stacks 2.1. + +**example:** + +```clojure +(string-to-int? "1") ;; Returns (some 1) +(string-to-uint? "1") ;; Returns (some u1) +(string-to-int? "a") ;; Returns none +``` + +*** + +## stx-account + +Introduced in: **Clarity 2** + +**input:** `principal`\ +**output:** `(tuple (locked uint) (unlock-height uint) (unlocked uint))`\ +**signature:** `(stx-account owner)` + +**description:**\ +Query the STX account for `owner`. Returns locked, unlock-height, and unlocked amounts (microstacks). Available starting Clarity 2. + +**example:** + +```clojure +(stx-account 'SZ2J6ZY48G...) ;; Returns (tuple (locked u0) (unlock-height u0) (unlocked u0)) +``` + +*** + +## stx-burn? + +Introduced in: **Clarity 1** + +**input:** `uint, principal`\ +**output:** `(response bool uint)`\ +**signature:** `(stx-burn? amount sender)` + +**description:**\ +Destroys `amount` of STX from `sender` (microstacks). `sender` must be current `tx-sender`. Returns `(ok true)` on success. Error codes: `(err u1)` insufficient balance, `(err u3)` non-positive amount, `(err u4)` sender not tx-sender. + +**example:** + +```clojure +(as-contract (stx-burn? u60 tx-sender)) ;; Returns (ok true) +``` + +*** + +## stx-get-balance + +Introduced in: **Clarity 1** + +**input:** `principal`\ +**output:** `uint`\ +**signature:** `(stx-get-balance owner)` + +**description:**\ +Returns STX balance (microstacks) of `owner`. If owner not materialized returns 0. + +**example:** + +```clojure +(stx-get-balance (as-contract tx-sender)) ;; Returns u1000 +``` + +*** + +## stx-transfer-memo? + +Introduced in: **Clarity 2** + +**input:** `uint, principal, principal, buff`\ +**output:** `(response bool uint)`\ +**signature:** `(stx-transfer-memo? amount sender recipient memo)` + +**description:**\ +Same as `stx-transfer?` but includes a `memo` buffer. Returns same error codes as `stx-transfer?`. + +**example:** + +```clojure +(as-contract (stx-transfer-memo? u60 tx-sender 'SZ2J6Z... 0x010203)) ;; Returns (ok true) +``` + +*** + +## stx-transfer? + +Introduced in: **Clarity 1** + +**input:** `uint, principal, principal`\ +**output:** `(response bool uint)`\ +**signature:** `(stx-transfer? amount sender recipient)` + +**description:**\ +Transfers STX (microstacks) from `sender` to `recipient`. `sender` must be current `tx-sender`. Returns `(ok true)` or errors: `(err u1)` insufficient funds, `(err u2)` same principal, `(err u3)` non-positive amount, `(err u4)` sender not tx-sender. + +**example:** + +```clojure +(as-contract (stx-transfer? u60 tx-sender 'SZ2J6Z...)) ;; Returns (ok true) +``` + +*** + +## to-consensus-buff? + +Introduced in: **Clarity 2** + +**input:** `any`\ +**output:** `(optional buff)`\ +**signature:** `(to-consensus-buff? value)` + +**description:**\ +Serializes a Clarity value using SIP-005 consensus serialization. If serialized size fits into a buffer, returns `(some buff)`, else `none`. During type checking the maximal possible buffer length is inferred. Available starting Clarity 2. + +**example:** + +```clojure +(to-consensus-buff? 1) ;; Returns (some 0x0000...01) +(to-consensus-buff? true) ;; Returns (some 0x03) +``` + +*** + +## to-int + +Introduced in: **Clarity 1** + +**input:** `uint`\ +**output:** `int`\ +**signature:** `(to-int u)` + +**description:**\ +Converts `uint` to `int`. Runtime error if argument >= 2^127. + +**example:** + +```clojure +(to-int u238) ;; Returns 238 +``` + +*** + +## to-uint + +Introduced in: **Clarity 1** + +**input:** `int`\ +**output:** `uint`\ +**signature:** `(to-uint i)` + +**description:**\ +Converts `int` to `uint`. Runtime error if argument is negative. + +**example:** + +```clojure +(to-uint 238) ;; Returns u238 +``` + +*** + +## try! + +Introduced in: **Clarity 1** + +**input:** `(optional A) | (response A B)`\ +**output:** `A`\ +**signature:** `(try! option-input)` + +**description:**\ +Unpacks `(some v)` or `(ok v)` returning `v`. If input is `none` or `(err ...)`, `try!` returns the current function's `none` or `(err ...)` and exits control-flow. + +**example:** (see original for longer usage) + +*** + +## tuple + +Introduced in: **Clarity 1** + +**input:** `(key-name A), ...`\ +**output:** `(tuple (key-name A) ...)`\ +**signature:** `(tuple (key0 expr0) (key1 expr1) ...)` + +**description:**\ +Constructs a typed tuple. Shorthand using curly braces `{ key: val, ... }` is available. + +**example:** + +```clojure +(tuple (name "blockstack") (id 1337)) +{name: "blockstack", id: 1337} +``` + +*** + +## unwrap! / unwrap-err! / unwrap-err-panic / unwrap-panic + +Introduced in: **Clarity 1** + +Utilities for unpacking optionals and responses with different failure behaviors: + +* `(unwrap! option-or-response thrown-value)` — returns inner value or returns `thrown-value` from current function. +* `(unwrap-err! response-input thrown-value)` — returns err value or returns `thrown-value` if ok. +* `(unwrap-err-panic response-input)` — returns err inner value or throws runtime error if ok. +* `(unwrap-panic option-or-response)` — returns inner value or throws runtime error if none/err. + +**example:** (see original for usage) + +*** + +## use-trait + +Introduced in: **Clarity 1** + +**input:** `VarName, TraitIdentifier`\ +**output:** `Not Applicable`\ +**signature:** `(use-trait trait-alias trait-identifier)` + +**description:**\ +Imports an external trait under an alias for use in the contract (must be top-level). + +**example:** + +```clojure +(use-trait token-a-trait 'SPAXYA5X....token-a.token-trait) +``` + +*** + +## var-get / var-set + +Introduced in: **Clarity 1** + +* `(var-get var-name)` — returns the value of a data var. +* `(var-set var-name expr)` — sets the data var; returns `true`. + +**example:** + +```clojure +(define-data-var cursor int 6) +(var-get cursor) ;; Returns 6 +(var-set cursor (+ (var-get cursor) 1)) ;; Returns true +``` + +*** + +## xor + +Introduced in: **Clarity 1** + +**input:** `int, int | uint, uint | string-ascii, string-ascii | string-utf8, string-utf8 | buff, buff`\ +**output:** `int | uint`\ +**signature:** `(xor i1 i2)` + +**description:**\ +Bitwise exclusive OR of `i1` and `i2`. + +**example:** + +```clojure +(xor 1 2) ;; Returns 3 +``` diff --git a/docs/reference/clarity/keywords.md b/docs/reference/clarity/keywords.md new file mode 100644 index 0000000000..262627fdcd --- /dev/null +++ b/docs/reference/clarity/keywords.md @@ -0,0 +1,282 @@ +--- +description: The complete reference guide to all Clarity keywords. +--- + +# Keywords + +{% hint style="info" %} +The Nakamoto hard fork will introduce a few new Clarity keywords. It's important to note that even with the new block production mechanism, the `block-height` keyword behavior will not change. It will simply correspond to the current tenure height. This means any Clarity contracts using this keyword will be backwards compatible after the Nakamoto Upgrade. +{% endhint %} + +### block-height + +Introduced in: Clarity 1 + +output: `uint` + +description: + +Returns the current block height of the Stacks blockchain in Clarity 1 and 2. Upon activation of epoch 3.0, `block-height` will return the same value as `tenure-height`. In Clarity 3, `block-height` is removed and has been replaced with `stacks-block-height`. + +example: + +```clarity +(> block-height u1000) ;; returns true if the current block-height has passed 1000 blocks. +``` + +*** + +### burn-block-height + +{% hint style="warning" %} +There is a bug in Clarity 3 when `burn-block-height` is used within an `at-block` expression. Normally, keywords executed within an `at-block` expression will return the data for that specified block. This bug causes `burn-block-height` always to return the burn block at the current chain tip, even within an `at-block` expression. This behavior affects any Clarity 3 contracts and will be fixed in a future hard fork. +{% endhint %} + +Introduced in: Clarity 1 + +output: `uint` + +description: + +Returns the current block height of the underlying burn blockchain as a uint + +example: + +```clarity +(> burn-block-height 1000) ;; returns true if the current height of the underlying burn blockchain has passed 1000 blocks. +``` + +*** + +### chain-id + +Introduced in: Clarity 2 + +output: `uint` + +description: + +Returns the 32-bit chain ID of the blockchain running this transaction + +example: + +```clarity +(print chain-id) ;; Will print 'u1' if the code is running on mainnet, and 'u2147483648' on testnet, and other values on different chains. +``` + +*** + +### contract-caller + +Introduced in: Clarity 1 + +output: `principal` + +description: + +Returns the caller of the current contract context. If this contract is the first one called by a signed transaction, the caller will be equal to the signing principal. If `contract-call?` was used to invoke a function from a new contract, `contract-caller` changes to the _calling_ contract's principal. If `as-contract` is used to change the `tx-sender` context, `contract-caller` _also_ changes to the same contract principal. + +example: + +```clarity +(print contract-caller) ;; Will print out a Stacks address of the transaction sender +``` + +{% hint style="warning" %} +Use caution when leveraging all contract calls, particularly tx-sender and contract-caller as based on the design, you can unintentionally introduce attack surface area. [Read more](https://www.setzeus.com/community-blog-posts/clarity-carefully-tx-sender). +{% endhint %} + +*** + +### false + +Introduced in: Clarity 1 + +output: `bool` + +description: + +Boolean false constant. + +example: + +```clarity +(and true false) ;; Evaluates to false +(or false true) ;; Evaluates to true +``` + +*** + +### is-in-mainnet + +Introduced in: Clarity 2 + +output: `bool` + +description: + +Returns a boolean indicating whether or not the code is running on the mainnet + +example: + +```clarity +(print is-in-mainnet) ;; Will print 'true' if the code is running on the mainnet +``` + +*** + +### is-in-regtest + +Introduced in: Clarity 1 + +output: `bool` + +description: + +Returns whether or not the code is running in a regression test + +example: + +```clarity +(print is-in-regtest) ;; Will print 'true' if the code is running in a regression test +``` + +*** + +### none + +Introduced in: Clarity 1 + +output: `(optional ?)` + +description: + +Represents the _none_ option indicating no value for a given optional (analogous to a null value). + +example: + +```clarity +(define-public (only-if-positive (a int)) + (if (> a 0) + (some a) + none)) +(only-if-positive 4) ;; Returns (some 4) +(only-if-positive (- 3)) ;; Returns none +``` + +```clarity +(print stx-liquid-supply) ;; Will print out the total number of liqui +``` + +*** + +### stacks-block-height + +Introduced in: Clarity 3 + +output: `uint` + +description: + +Returns the current Stacks block height. + +example: + +```clarity +(print stacks-block-height) ;; Will print out the current Stacks block height +``` + +*** + +### stx-liquid-supply + +Introduced in: Clarity 1 + +output: `uint` + +description: + +Returns the total number of micro-STX (uSTX) that are liquid in the system as of this block. + +example: + +```clarity +(print stx-liquid-supply) ;; Will print out the total number of liquid uSTX +``` + +*** + +### tenure-height + +Introduced in: Clarity 3 + +output: `uint` + +description: + +Returns the number of tenures that have passed. When the Nakamoto block-processing starts, this will be equal to the chain length. + +example: + +```clarity +(print tenure-height) ;; Will print out the current tenure height +``` + +*** + +### true + +Introduced in: Clarity 1 + +output: `bool` + +description: + +Boolean true constant. + +example: + +```clarity +(and true false) ;; Evaluates to false +(or false true) ;; Evaluates to true +``` + +*** + +### tx-sender + +Introduced in: Clarity 1 + +output: `principal` + +description: + +Returns the original sender of the current transaction, or if `as-contract` was called to modify the sending context, it returns that contract principal. + +example: + +```clarity +(print tx-sender) ;; Will print out a Stacks address of the transaction sender +``` + +{% hint style="warning" %} +Use caution when leveraging all contract calls, particularly tx-sender and contract-caller as based on the design, you can unintentionally introduce attack surface area. [Read more](https://www.setzeus.com/community-blog-posts/clarity-carefully-tx-sender). +{% endhint %} + +*** + +### tx-sponsor? + +Introduced in: Clarity 2 + +output: `optional principal` + +description: + +Returns the sponsor of the current transaction if there is a sponsor, otherwise returns None. + +example: + +```clarity +(print tx-sponsor?) ;; Will print out an optional value containing the Stacks address of the transaction sponsor +``` diff --git a/docs/reference/clarity/types.md b/docs/reference/clarity/types.md new file mode 100644 index 0000000000..ecf04eca23 --- /dev/null +++ b/docs/reference/clarity/types.md @@ -0,0 +1,23 @@ +--- +description: The complete reference guide to all Clarity types. +--- + +# Types + +### Clarity Type System + +The type system contains the following types: + +| Types | Notes | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `int` | signed 128-bit integer | +| `uint` | unsigned 128-bit integer | +| `bool` | boolean value (`true` or `false`) | +| `principal` | object representing a principal (whether a contract principal or standard principal) | +| `(buff max-len)` | byte buffer of maximum length `max-len`. | +| `(string-ascii max-len)` | ASCII string of maximum length `max-len` | +| `(string-utf8 max-len)` | UTF-8 string of maximum length `max-len` (u"A smiley face emoji \u{1F600} as a utf8 string") | +| `(list max-len entry-type)` | list of maximum length `max-len`, with entries of type `entry-type` | +| `{label-0: value-type-0, label-1: value-type-1, ...}` | tuple, group of data values with named fields | +| `(optional some-type)` | an option type for objects that can either be `(some value)` or `none` | +| `(response ok-type err-type)` | object used by public functions to commit their changes or abort. May be returned or used by other functions as well, however, only public functions have the commit/abort behavior. | diff --git a/docs/reference/post-conditions/implementing-post-conditions.md b/docs/reference/post-conditions/implementing-post-conditions.md new file mode 100644 index 0000000000..56b39db343 --- /dev/null +++ b/docs/reference/post-conditions/implementing-post-conditions.md @@ -0,0 +1,227 @@ +# Implementing Post Conditions + +Learn how to add post-conditions to protect your Stacks transactions. + +Post-conditions are a powerful security feature in Stacks that protect users from unexpected transaction outcomes. This tutorial will walk you through implementing post-conditions in your applications to ensure transactions behave exactly as users expect. + +## What you'll learn + +* Construct post-conditions using the Pc helper API +* Add post-conditions to different transaction types +* Configure post-condition modes for transaction security +* Implement post-conditions for STX, fungible tokens, and NFTs +* Handle semi-fungible tokens (SFTs) with post-conditions + +## Prerequisites + +* Basic understanding of Stacks transactions +* Stacks.js library installed (`npm install @stacks/transactions`) +* A development environment set up for Stacks + +## Constructing post-conditions + +The Pc helper in Stacks.js provides a fluent, BDD-inspired API for constructing post-conditions. Start with `Pc.principal()` to specify which address will be verified, then chain methods to define the condition. + +```ts +import { Pc } from '@stacks/transactions'; + +// Basic structure of a post-condition +const postCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendEq(1000) + .ustx(); +``` + +The Pc helper uses method chaining for intuitive condition building. Your IDE will provide auto-completion for available methods at each step. + +## Available transfer methods + +Post-conditions support different comparison operators and asset types. Choose the appropriate method based on your security requirements. + +### STX and fungible token methods + +```ts +// Exact amount +Pc.principal(address).willSendEq(1000).ustx(); + +// Greater than or equal +Pc.principal(address).willSendGte(500).ustx(); + +// Less than +Pc.principal(address).willSendLt(2000).ustx(); +``` + +Comparison methods available: + +* `.willSendEq(amount)` - Exactly equal to amount +* `.willSendGte(amount)` - Greater than or equal to amount +* `.willSendGt(amount)` - Greater than amount +* `.willSendLte(amount)` - Less than or equal to amount +* `.willSendLt(amount)` - Less than amount + +### Asset type methods + +```ts +// STX transfers +.ustx() + +// Fungible token transfers +.ft(contractAddress, tokenName) + +// NFT transfers +.nft(assetIdentifier, tokenId) +``` + +### NFT-specific methods + +```ts +// Ensure NFT is sent +Pc.principal(address).willSendAsset().nft(...); + +// Ensure NFT is NOT sent +Pc.principal(address).willNotSendAsset().nft(...); +``` + +## Setting the post-condition mode + +The post-condition mode determines how the Stacks blockchain handles asset transfers not explicitly covered by your post-conditions. This is a critical security setting. + +```ts +import { PostConditionMode, makeContractCall } from '@stacks/transactions'; + +const tx = await makeContractCall({ + // ... other transaction properties + postConditionMode: PostConditionMode.Deny, // Recommended default + postConditions: [ + // your post-conditions here + ], +}); +``` + +Mode options: + +* PostConditionMode.Deny (default): Transaction fails if any unspecified transfers occur +* PostConditionMode.Allow: Transaction allows transfers beyond specified post-conditions + +## Common implementation patterns + +### STX transfer post-conditions + +Protect STX transfers by specifying exact amounts or ranges. + +```ts +import { Pc, makeSTXTokenTransfer } from '@stacks/transactions'; + +// Exact amount post-condition +const exactAmountCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendEq(1000) + .ustx(); + +// Use in a transaction +const tx = await makeSTXTokenTransfer({ + recipient: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM', + amount: 1000, + postConditions: [exactAmountCondition], + postConditionMode: PostConditionMode.Deny, + // ... other properties +}); +``` + +### Fungible token post-conditions + +Ensure fungible tokens are transferred as expected in contract calls. + +```ts +import { Pc, makeContractCall } from '@stacks/transactions'; + +// Minimum amount condition +const ftCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendGte(500) + .ft('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6.token-ft', 'token'); + +// Use in a contract call +const tx = await makeContractCall({ + contractAddress: 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6', + contractName: 'token-transfer', + functionName: 'transfer', + functionArgs: [ + // ... function arguments + ], + postConditions: [ftCondition], + // ... other properties +}); +``` + +### NFT transfer post-conditions + +Control NFT ownership changes with specific post-conditions. + +```ts +import { Pc, Cl } from '@stacks/transactions'; + +// Ensure NFT is sent +const sendNftCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendAsset() + .nft('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6.nft-contract::nft-name', Cl.uint(1)); + +// Ensure NFT is NOT sent (protection against unwanted transfers) +const keepNftCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willNotSendAsset() + .nft('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6.nft-contract::nft-name', Cl.uint(1)); +``` + +Use `willNotSendAsset()` to protect valuable NFTs from being transferred unexpectedly. + +### Semi-fungible token (SFT) post-conditions + +SFTs require special handling as they have both fungible and non-fungible properties. + +```ts +import { Cl, Pc } from '@stacks/transactions'; + +// SFT as NFT (specific token ID) +const sftNftCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendAsset() + .nft( + 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.sft-contract::sft-id', + Cl.tuple({ + 'token-id': Cl.uint(1), + owner: Cl.principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + }) + ); + +// SFT as FT (amount-based) +const sftFtCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendEq(500) + .ft('ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.sft-contract', 'sft-token'); +``` + +## Multiple post-conditions + +Complex transactions often require multiple post-conditions to fully protect all asset transfers. + +```ts +const tx = await makeContractCall({ + // ... transaction properties + postConditions: [ + // Sender must send exactly 1000 uSTX + Pc.principal(senderAddress).willSendEq(1000).ustx(), + + // Contract must send at least 100 tokens to user + Pc.principal(contractAddress).willSendGte(100) + .ft(contractAddress + '.my-token', 'my-token'), + + // User must not lose their NFT + Pc.principal(senderAddress).willNotSendAsset() + .nft(nftContract + '::my-nft', Cl.uint(1)), + ], + postConditionMode: PostConditionMode.Deny, +}); +``` + diff --git a/docs/reference/post-conditions/overview.md b/docs/reference/post-conditions/overview.md new file mode 100644 index 0000000000..e848ec344f --- /dev/null +++ b/docs/reference/post-conditions/overview.md @@ -0,0 +1,121 @@ +# Overview + +Learn how post-conditions protect users from unexpected transaction outcomes. + +Post-conditions are security features in Stacks that protect users by ensuring transactions execute exactly as expected. They verify that specific asset transfers occur during a transaction, and if the conditions aren't satisfied, the entire transaction fails with no state changes. + +## What are post-conditions? + +Post-conditions act as safeguards that verify asset transfers match your expectations. They can check STX transfers, fungible tokens, and non-fungible token ownership changes. + +```ts +import { Pc } from '@stacks/transactions'; + +const tx = await makeContractCall({ + // ... + postConditions: [ + Pc.principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6').willSendEq(1000).ustx(), + ], +}); +``` + +Post-conditions only verify that assets are sent, not received. They cannot guarantee the final recipient of tokens. + +## Using the Pc helper + +The `Pc` helper provides a fluent API for creating post-conditions with better type safety and readability. + +```ts +import { Pc } from '@stacks/transactions'; + +// STX transfer post-condition +const stxCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendGte(1000) + .ustx(); + +// Fungible token post-condition +const ftCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendEq(50) + .ft('SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.my-token', 'my-token'); + +// NFT post-condition +const nftCondition = Pc + .principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6') + .willSendAsset() + .nft('SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.my-nft::my-asset', Cl.uint(1)); +``` + +## Manual creation + +Create post-conditions manually using type definitions when building conditions dynamically. + +```ts +import { + StxPostCondition, + FungiblePostCondition, + NonFungiblePostCondition +} from '@stacks/transactions'; + +// STX post-condition +const stxPostCondition: StxPostCondition = { + type: 'stx-postcondition', + address: 'SP2JXKMSH007NPYAQHKJPQMAQYAD90NQGTVJVQ02B', + condition: 'gte', // 'eq' | 'gt' | 'gte' | 'lt' | 'lte' + amount: '100', +}; +``` + +Available condition types: + +* `eq`: Exactly equal to amount +* `gt`: Greater than amount +* `gte`: Greater than or equal to amount +* `lt`: Less than amount +* `lte`: Less than or equal to amount + +### Fungible tokens + +```ts +const ftPostCondition: FungiblePostCondition = { + type: 'ft-postcondition', + address: 'SP2JXKMSH007NPYAQHKJPQMAQYAD90NQGTVJVQ02B', + condition: 'eq', + amount: '100', + asset: 'SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.my-ft-token::my-token', +}; +``` + +### Non-fungible tokens + +```ts +const nftPostCondition: NonFungiblePostCondition = { + type: 'nft-postcondition', + address: 'SP2JXKMSH007NPYAQHKJPQMAQYAD90NQGTVJVQ02B', + condition: 'sent', // 'sent' | 'not-sent' + asset: 'SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.my-nft::my-asset', + assetId: Cl.uint(602), +}; +``` + +## Post-condition mode + +Control how unspecified asset transfers are handled with post-condition mode. + +```ts +import { PostConditionMode } from '@stacks/transactions'; + +const tx = await makeContractCall({ + // ... + postConditionMode: PostConditionMode.Deny, + postConditions: [ + // your conditions + ], +}); +``` + +{% hint style="warning" %} +Post-condition mode\ +Always use `Deny` mode unless you have a specific reason to allow additional transfers. This provides maximum security for users. +{% endhint %} diff --git a/docs/reference/rpc-api.md b/docs/reference/rpc-api.md new file mode 100644 index 0000000000..7eea2d8aa1 --- /dev/null +++ b/docs/reference/rpc-api.md @@ -0,0 +1,134 @@ +# RPC-API + +### Introduction + +The Stacks Blockchain API allows you to query the Stacks blockchain and interact with smart contracts. It was built to maintain pageable materialized views of the Stacks Blockchain. + +Note that the [Stacks Node RPC API](https://github.com/stacks-network/stacks-blockchain/) and the [Hiro Stacks API](https://www.hiro.so/stacks-api) are two different things. The Hiro API is a centralized service run by Hiro, a developer tooling company, that makes it easy to get onboarded and begin interacting with the Stacks blockchain in a RESTful way. You can also [run your own API server](https://docs.hiro.so/get-started/running-api-node). + +The Hiro Stacks API is a proxy for the Stacks Node API that makes it a bit easier to work with by providing additional functionality. + +The RPC API is generated by every Stacks node and allows developers to self-host their own node and API for a more decentralized architecture. + +The RPC API can be used without any authorization. The basepath for the API is: + +```bash +# for mainnet, replace `testnet` with `mainnet` +https://api.testnet.hiro.so/ +``` + +{% hint style="warning" %} +If you run a local node, it exposes an HTTP server on port `20443`. The info endpoint would be `localhost:20443/v2/info`. +{% endhint %} + +*** + +### Stacks Node RPC API endpoints + +The Stacks 2.0 Blockchain RPC API is exposed by every running Stacks node. Below is a non-exhaustive list of common RPC endpoints (each entry links to the OpenAPI spec used by the Stacks node): + +* GET `/v2/contracts/interface/{contract_address}/{contract_name}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* POST `/v2/transactions`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* POST `/v2/map_entry/{contract_address}/{contract_name}/{map_name}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* POST `/v3/blocks/upload`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* POST `/v2/mempool/query`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* POST `/v2/blocks/upload/{consensus_hash}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/transactions/unconfirmed/{txid}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/tenures/tip/{consensus_hash}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/tenures/fork_info/{start}/{stop}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/neighbors`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/blocks/{block_id}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/headers/{quantity}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/data_var/{principal}/{contract_name}/{var_name}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/stackerdb/{principal}/{contract_name}/replicas`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* POST `/v2/stackerdb/{principal}/{contract_name}/chunks`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/stackerdb/{principal}/{contract_name}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/stackerdb/{principal}/{contract_name}/{slot_id}/{slot_version}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/stackerdb/{principal}/{contract_name}/{slot_id}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* POST `/v2/microblocks`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/microblocks/unconfirmed/{block_id}/{seq}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/microblocks/{microblock_id}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/microblocks/confirmed/{block_id}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/attachments/inv`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/attachments/{hash}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/health`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/transaction/{txid}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/signer/{signer_pubkey}/{cycle_number}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/sortitions/burn_height/{height}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/sortitions/burn/{burn_header_hash}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/sortitions/consensus/{consensus_hash}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/sortitions/latest_and_last`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/sortitions`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/tenures/{block_id}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/tenures/info`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/blocks/height/{block_height}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/blocks/{block_id}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v3/stacker_set/{cycle_number}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* POST `/v3/block_proposal`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/constant_val/{contract_address}/{contract_name}/{constant_name}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/clarity/metadata/{contract_address}/{contract_name}/{clarity_metadata_key}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/clarity/marf/{marf_key_hash}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/traits/{contract_address}/{contract_name}/{trait_contract_address}/{trait_contract_name}/{trait_name}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/pox`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/info`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/fees/transfer`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* POST `/v2/fees/transaction`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/accounts/{principal}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* POST `/v3/contracts/fast-call-read/{contract_address}/{contract_name}/{function_name}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* POST `/v2/contracts/call-read/{contract_address}/{contract_name}/{function_name}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml +* GET `/v2/contracts/source/{contract_address}/{contract_name}`\ + OpenAPI: https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml + +*** + +For details about request/response schemas and additional endpoints, consult the Stacks node OpenAPI spec:\ +https://raw.githubusercontent.com/stacks-network/stacks-core/master/docs/rpc/openapi.yaml diff --git a/docs/reference/signer-configuration.md b/docs/reference/signer-configuration.md new file mode 100644 index 0000000000..d5ee126ff4 --- /dev/null +++ b/docs/reference/signer-configuration.md @@ -0,0 +1,243 @@ +# Signer Configuration + +{% hint style="info" %} +Note that in this version, the Stacks node will not boot if it sees config values that are unused. If your node is not booting, be sure to check your logs for any messages indicating +{% endhint %} + +## Signer Configuration + +#### Signer Configuration File Options + +The signer configuration file is a TOML file that contains the configuration options for your signer. Below are the options you can set in the signer configuration file. + +| Name | Required | Description | +| ---------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| node\_host | ✓ | IP:PORT where your Stacks node can be accessed. The port 20443 is the default RPC endpoint for Stacks nodes. Note that you must use an IP address - DNS hosts are not supported at this time. | +| endpoint | ✓ | IP:PORT where the signer will expose an RPC endpoint for receiving events from your Stacks node. | +| stacks\_private\_key | ✓ | Hex representation of the signer's Stacks private key used for communicating with the Stacks Node, including writing to the Stacker DB instance. | +| network | ✓ | Network to use. One of "mainnet", "testnet" or "mocknet". | +| auth\_password | ✓ | Authorization token for HTTP requests made from the signer to your Stacks node. | +| db\_path | ✓ | Path to the signer's database file | +| block\_proposal\_timeout\_ms | | Specifies the maximum time (in milliseconds) a signer waits after a Bitcoin block for a miner to produce their first Nakamoto block. If the miner exceeds this time, the signer marks their tenure as invalid and rejects subsequent block proposals. Default value of 600\_000 (10 minutes). | +| metrics\_endpoint | | IP:PORT for Prometheus metrics collection. | +| chain\_id | | An optional ChainID, only used for custom networks (like Nakamoto Testnet) | + +#### Example Configs + +Below are sample configuration files for running a Stacks node and signer provided in one place for convenience. You'll need to modify some of these according to the [How to Run a Signer](broken-reference) doc. + +#### Testnet Signer + +```toml +# The IP address and port where your Stacks node can be accessed. +# The port 20443 is the default RPC endpoint for Stacks nodes. +# Note that you must use an IP address - DNS hosts are not supported at this time. +# This should be the IP address accessible via Docker, usually via a network. +node_host = "127.0.0.1:20443" + +# This is the location where the signer will expose an RPC endpoint for +# receiving events from your Stacks node. +endpoint = "127.0.0.1:30000" + +# Either “testnet” or “mainnet” +network = "testnet" + +# this is a file path where your signer will persist data. If using Docker, +# this must be within a volume, so that data can be persisted across restarts +db_path = "/var/stacks/signer.sqlite" + +# an authentication token that is used for some HTTP requests made from the +# signer to your Stacks node. You’ll need to use this later on when configuring +# your Stacks node. You create this field yourself, rather than it being generated +# with your private key. +auth_password = "$your_http_auth_token" + +# This is the privateKey field from the keys you generated in the +# previous step. +stacks_private_key = "$your_stacks_private_key" +``` + +#### Stacks Node Testnet Config + +{% hint style="warning" %} +Note that the `block_proposal_token` field has changed to `auth_token` in the Stacks node configuration file. +{% endhint %} + +This is the configuration you'll need to run a Stacks follower node if you are also running a signer. Be sure to change the commented lines to the appropriate data for your setup. If you are not familiar with the process of setting up a signer, be sure to follow the [How to Run a Signer](broken-reference) guide. + +An overview of all Stacks node configuration options can be found in the [Stacks Node Configuration](broken-reference) doc. + +Additions necessary specifically to run a signer are the `[connection_options]` and `[[events_observer]]` sections and the `stacker = true` line. There are also a few comments detailing other lines that need to change. + +```toml +[node] + +rpc_bind = "0.0.0.0:20443" +p2p_bind = "0.0.0.0:20444" +bootstrap_node = "029266faff4c8e0ca4f934f34996a96af481df94a89b0c9bd515f3536a95682ddc@seed.testnet.hiro.so:30444" +prometheus_bind = "127.0.0.1:9153" +working_dir = "/hirosystems/data" +local_peer_seed = "{{ redacted }}" + +# Required for nodes attached to signers, optional for other nodes +stacker = true + +[burnchain] +chain = "bitcoin" +mode = "krypton" +peer_host = "bitcoin.regtest.hiro.so" +peer_port = 18444 +pox_prepare_length = 100 +pox_reward_length = 900 + +# Set your auth token, which the signer uses +# This should match the auth_password field of your signer config +[connection_options] +auth_token = "12345" + +# Set your signer as an event observer +[[events_observer]] + +# This endpoint is where your signer will communicate with your Stacks node +endpoint = "127.0.0.1:30000" +events_keys = ["stackerdb", "block_proposal", "burn_blocks"] + +[[ustx_balance]] +address = "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST319CF5WV77KYR1H3GT0GZ7B8Q4AQPY42ETP1VPF" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST221Z6TDTC5E0BYR2V624Q2ST6R0Q71T78WTAX6H" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST2TFVBMRPS5SSNP98DQKQ5JNB2B6NZM91C4K3P7B" +amount = 10000000000000000 + +[fee_estimation] +fee_estimator = "fuzzed_weighted_median_fee_rate" + +[[burnchain.epochs]] +epoch_name = "1.0" +start_height = 0 + +[[burnchain.epochs]] +epoch_name = "2.0" +start_height = 0 + +[[burnchain.epochs]] +epoch_name = "2.05" +start_height = 1 + +[[burnchain.epochs]] +epoch_name = "2.1" +start_height = 2 + +[[burnchain.epochs]] +epoch_name = "2.2" +start_height = 3 + +[[burnchain.epochs]] +epoch_name = "2.3" +start_height = 4 + +[[burnchain.epochs]] +epoch_name = "2.4" +start_height = 5 + +[[burnchain.epochs]] +epoch_name = "2.5" +start_height = 6 + +[[burnchain.epochs]] +epoch_name = "3.0" +start_height = 1_900 + +[[burnchain.epochs]] +epoch_name = "3.1" +start_height = 2_000 + +[[burnchain.epochs]] +epoch_name = "3.2" +start_height = 71_525 +``` + +#### Mainnet Signer + +This config is very similar to the testnet config, except the `network` field is changed. + +```toml +# The IP address and port where your Stacks node can be accessed. +# The port 20443 is the default RPC endpoint for Stacks nodes. +# Note that you must use an IP address - DNS hosts are not supported at this time. +# This should be the IP address accessible via Docker, usually via a network. +node_host = "127.0.0.1:20443" + +# This is the location where the signer will expose an RPC endpoint for +# receiving events from your Stacks node. +endpoint = "127.0.0.1:30000" + +# Either “testnet” or “mainnet” +network = "mainnet" + +# this is a file path where your signer will persist data. If using Docker, +# this must be within a volume, so that data can be persisted across restarts +db_path = "/var/stacks/signer.sqlite" + +# an authentication token that is used for some HTTP requests made from the +# signer to your Stacks node. You’ll need to use this later on when configuring +# your Stacks node. You create this field yourself, rather than it being generated +# with your private key. +auth_password = "$your_http_auth_token" + +# This is the privateKey field from the keys you generated in the +# previous step. +stacks_private_key = "$your_stacks_private_key" + +# The IP address and port where prometheus metrics can be accessed. +metrics_endpoint = "127.0.0.1:9154" + +# Determining when a time-based tenure extend will be accepted +tenure_idle_timeout_secs = 120 +``` + +#### Mainnet Stacks Node + +With a mainnet Stacks node config, you'll need to change the bootstrap node field and the burnchain fields. Other than that, the `ustx_balance` fields are not necessary. + +```toml +[node] + +# Set this based on where you downloaded +# the chain state archive as described in the How to Run a Signer guide: +working_dir = "/data-dir-somewhere" +rpc_bind = "0.0.0.0:20443" +p2p_bind = "0.0.0.0:20444" + +# This is the node that your node will use to begin syncing chain state +bootstrap_node = "02196f005965cebe6ddc3901b7b1cc1aa7a88f305bb8c5893456b8f9a605923893@seed.mainnet.hiro.so:20444,02539449ad94e6e6392d8c1deb2b4e61f80ae2a18964349bc14336d8b903c46a8c@cet.stacksnodes.org:20444,02ececc8ce79b8adf813f13a0255f8ae58d4357309ba0cedd523d9f1a306fcfb79@sgt.stacksnodes.org:20444,0303144ba518fe7a0fb56a8a7d488f950307a4330f146e1e1458fc63fb33defe96@est.stacksnodes.org:20444" + +# Required for nodes attached to signers, optional for other nodes +stacker = true + +[burnchain] +chain = "bitcoin" +mode = "mainnet" +peer_host = "bitcoin.mainnet.stacks.org" + +# Set your auth token, which the signer uses +# This should match the auth_password field of your signer config +[connection_options] +auth_token = "12345" + +# Set your signer as an event observer +[[events_observer]] + +# This endpoint is where your signer will communicate with your Stacks node +endpoint = "127.0.0.1:30000" +events_keys = ["stackerdb", "block_proposal", "burn_blocks"] +``` diff --git a/docs/reference/stacks-connect/broadcast-transactions.md b/docs/reference/stacks-connect/broadcast-transactions.md new file mode 100644 index 0000000000..d9a3fea563 --- /dev/null +++ b/docs/reference/stacks-connect/broadcast-transactions.md @@ -0,0 +1,168 @@ +# Broadcast Transactions + +The process of broadcasting transactions is fundamental for interacting with blockchains, whether you're transferring tokens, deploying contracts, or executing contract functions. + +In this guide you will: + +* Install the required packages +* Connect to a user's wallet +* Sign and broadcast different transaction types +* Handle transaction results + +## Setup and installation + +Install the required packages to start building and broadcasting transactions. + +{% tabs %} +{% tab title="npm" %} +```bash +npm install @stacks/connect @stacks/transactions +``` +{% endtab %} + +{% tab title="yarn" %} +```bash +yarn add @stacks/connect @stacks/transactions +``` +{% endtab %} + +{% tab title="pnpm" %} +```bash +pnpm add @stacks/connect @stacks/transactions +``` +{% endtab %} +{% endtabs %} + +## Connect to a user's wallet + +Before signing transactions, users need to connect their wallet to your application. Use the `connect` function to initiate a wallet connection: + +```ts +import { connect, isConnected } from '@stacks/connect'; + +async function connectWallet() { + if (!isConnected()) { + const response = await connect(); + console.log('Connected with addresses:', response); + } +} +``` + +## Sign and broadcast transactions + +There are three common transaction flows you can build: + +{% stepper %} +{% step %} +### STX transfer + +Use `stx_transferStx` to send tokens: + +```ts +import { request } from '@stacks/connect'; + +async function transferStx() { + const response = await request('stx_transferStx', { + recipient: 'ST2EB9WEQNR9P0K28D2DC352TM75YG3K0GT7V13CV', + amount: '100', + memo: 'Reimbursement', + }); + + console.log('Transaction ID:', response.txId); +} +``` +{% endstep %} + +{% step %} +### Contract deployment + +Deploy a contract with `stx_deployContract`: + +```ts +import { request } from '@stacks/connect'; + +async function deployContract() { + const codeBody = '(define-public (say-hi) (ok "hello world"))'; + + const response = await request('stx_deployContract', { + name: 'my-contract', + code: codeBody, + clarityVersion: 3, + }); + + console.log('Transaction ID:', response.txId); +} +``` + +{% hint style="info" %} +Contracts deploy to the Stacks address of the connected wallet. +{% endhint %} +{% endstep %} + +{% step %} +### Contract execution + +Call contract functions with `stx_callContract`: + +```clarity +(define-public (say-hi) + (print "hi") + (ok u0) +) +``` + +```ts +import { request } from '@stacks/connect'; + +async function callContract() { + const response = await request('stx_callContract', { + contractAddress: 'ST22T6ZS7HVWEMZHHFK77H4GTNDTWNPQAX8WZAKHJ', + contractName: 'my-contract', + functionName: 'say-hi', + functionArgs: [], + }); + + console.log('Transaction ID:', response.txId); +} +``` + +When passing arguments, construct Clarity values via `Cl`: + +```ts +import { Cl } from '@stacks/transactions'; + +const functionArgs = [ + Cl.uint(123), + Cl.stringAscii('hello'), + Cl.standardPrincipalCV('ST1X..'), +]; +``` +{% endstep %} +{% endstepper %} + +## Handle transaction results + +When a transaction is signed and broadcast, the `request` method returns a response object containing information about the transaction: + +```ts +interface TransactionResponse { + txId: string; // The transaction ID + txRaw: string; // The raw transaction hex +} +``` + +You can use the transaction ID to create a link to view the transaction in the explorer: + +```ts +import { request } from '@stacks/connect'; + +async function handleTransaction() { + const response = await request('stx_transferStx', { + recipient: 'ST2EB9WEQNR9P0K28D2DC352TM75YG3K0GT7V13CV', + amount: '100', + }); + + const explorerUrl = `https://explorer.stacks.co/txid/${response.txId}`; + console.log('View transaction in explorer:', explorerUrl); +} +``` diff --git a/docs/reference/stacks-connect/connect-wallet.md b/docs/reference/stacks-connect/connect-wallet.md new file mode 100644 index 0000000000..0c220ffd17 --- /dev/null +++ b/docs/reference/stacks-connect/connect-wallet.md @@ -0,0 +1,120 @@ +# Connect Wallet + +Learn how to integrate wallet connections into your Stacks application. Connecting a wallet authenticates users and enables blockchain interactions like transfers and contract calls. + +## What you'll learn + +* Install the `@stacks/connect` package +* Connect to a wallet and authenticate users +* Manage authentication state +* Access user account data + +{% hint style="info" %} +Prerequisites: + +* Node.js installed on your machine +* A web application setup (React, Vue, or vanilla JS) +* Basic understanding of async/await +{% endhint %} + +## Quickstart + +{% stepper %} +{% step %} +### Install package + +Add Stacks Connect to your project: + +{% code title="Install" %} +```bash +npm install @stacks/connect +``` +{% endcode %} +{% endstep %} + +{% step %} +### Connect and authenticate + +Use `connect` to initiate a wallet session and persist user data: + +{% code title="connect.ts" %} +```ts +import { connect, isConnected } from '@stacks/connect'; + +async function connectWallet() { + if (isConnected()) { + console.log('Already authenticated'); + return; + } + + const response = await connect(); + console.log('Connected:', response.addresses); +} +``` +{% endcode %} + +Manage authentication state in your app: + +{% code title="auth.ts" %} +```ts +import { disconnect, isConnected } from '@stacks/connect'; + +const authenticated = isConnected(); + +function logout() { + disconnect(); + console.log('User disconnected'); +} +``` +{% endcode %} +{% endstep %} + +{% step %} +### Access user data + +Read persisted addresses and request full account details: + +{% code title="user-data.ts" %} +```ts +import { getLocalStorage, request } from '@stacks/connect'; + +const userData = getLocalStorage(); +if (userData?.addresses) { + const stxAddress = userData.addresses.stx[0].address; + const btcAddress = userData.addresses.btc[0].address; + console.log('STX:', stxAddress); + console.log('BTC:', btcAddress); +} + +const accounts = await request('stx_getAccounts'); +const account = accounts.addresses[0]; +console.log('Address:', account.address); +console.log('Public key:', account.publicKey); +console.log('Gaia URL:', account.gaiaHubUrl); +``` +{% endcode %} +{% endstep %} + +{% step %} +### Make your first transaction + +Request the wallet to broadcast a transfer: + +{% code title="send-transaction.ts" %} +```ts +import { request } from '@stacks/connect'; + +async function sendTransaction() { + const response = await request('stx_transferStx', { + amount: '1000000', + recipient: 'SP2MF04VAGYHGAZWGTEDW5VYCPDWWSY08Z1QFNDSN', + memo: 'First transfer', + }); + + console.log('Transaction ID:', response.txid); +} +``` +{% endcode %} +{% endstep %} +{% endstepper %} + diff --git a/docs/reference/stacks-connect/message-signing.md b/docs/reference/stacks-connect/message-signing.md new file mode 100644 index 0000000000..437aca5574 --- /dev/null +++ b/docs/reference/stacks-connect/message-signing.md @@ -0,0 +1,191 @@ +# Message Signing + +Learn how to implement message signing in your Stacks application. Message signing allows users to cryptographically prove they control an address without making an on-chain transaction, enabling authentication, authorization, and verifiable statements. + +## What you'll learn + +* Connect to a user's wallet and request message signatures +* Sign both simple text messages and structured data +* Verify signatures to ensure authenticity + +## Prerequisites + +* Node.js installed on your machine +* A code editor like VS Code + +## Installation + +Install the required packages for message signing and verification. + +```bash +npm install @stacks/connect @stacks/encryption +``` + +## Connect to wallet + +Before signing messages, establish a connection to the user's wallet. The connection persists across page reloads. + +```ts +import { connect, isConnected } from '@stacks/connect'; + +async function connectWallet() { + if (!isConnected()) { + const response = await connect(); + console.log('Connected addresses:', response.addresses); + } +} +``` + +Call this function when your app loads or when the user clicks a connect button. + +## Sign text messages + +Request a signature for a simple text message using the `request` method. + +```ts +import { request } from '@stacks/connect'; + +async function signMessage() { + const message = 'Hello World'; + + const response = await request('stx_signMessage', { + message, + }); + + console.log('Signature:', response.signature); + console.log('Public key:', response.publicKey); + + return response; +} +``` + +The wallet will display the message to the user for approval before signing. + +## Sign structured data + +For more complex data, use structured message signing with Clarity values. + +```ts +import { request } from '@stacks/connect'; +import { Cl } from '@stacks/transactions'; + +async function signStructuredMessage() { + const message = Cl.tuple({ + action: Cl.stringAscii('transfer'), + amount: Cl.uint(1000), + recipient: Cl.stringAscii('alice.btc') + }); + + const domain = Cl.tuple({ + name: Cl.stringAscii('My App'), + version: Cl.stringAscii('1.0.0'), + 'chain-id': Cl.uint(1) // 1 for mainnet + }); + + const response = await request('stx_signStructuredMessage', { + message, + domain + }); + + return response; +} +``` + +Structured messages provide better type safety and are easier to parse on-chain. + +## Verify signatures + +Validate signatures to ensure they match the expected message and public key. + +```ts +import { verifyMessageSignatureRsv } from '@stacks/encryption'; + +async function verifySignature( + message: string, + signature: string, + publicKey: string +): Promise { + const isValid = verifyMessageSignatureRsv({ + message, + signature, + publicKey + }); + + if (isValid) { + console.log('✓ Signature verified successfully'); + } else { + console.log('✗ Invalid signature'); + } + + return isValid; +} +``` + +Always verify signatures before trusting the signed data. + +## Complete verification flow + +```ts +async function signAndVerify() { + // Request signature + const message = 'Authorize login at ' + new Date().toISOString(); + const signResponse = await request('stx_signMessage', { message }); + + // Verify immediately + const isValid = await verifySignature( + message, + signResponse.signature, + signResponse.publicKey + ); + + if (isValid) { + // Proceed with authenticated action + console.log('Authentication successful'); + } +} +``` + +## Try it out + +Create a simple authentication system using message signatures. + +```ts +// Generate a unique challenge +function generateChallenge(): string { + const nonce = Math.random().toString(36).substring(7); + const timestamp = Date.now(); + return `Sign this message to authenticate:\nNonce: ${nonce}\nTime: ${timestamp}`; +} + +// Complete auth flow +async function authenticate() { + const challenge = generateChallenge(); + + try { + const response = await request('stx_signMessage', { + message: challenge + }); + + const isValid = verifyMessageSignatureRsv({ + message: challenge, + signature: response.signature, + publicKey: response.publicKey + }); + + if (isValid) { + // Store auth token or session + localStorage.setItem('auth', JSON.stringify({ + publicKey: response.publicKey, + timestamp: Date.now() + })); + + return { success: true }; + } + } catch (error) { + console.error('Authentication failed:', error); + } + + return { success: false }; +} +``` + diff --git a/docs/reference/stacks-connect/migration-guide.md b/docs/reference/stacks-connect/migration-guide.md new file mode 100644 index 0000000000..76ce21d96c --- /dev/null +++ b/docs/reference/stacks-connect/migration-guide.md @@ -0,0 +1,90 @@ +# Migration Guide + +For a while now, the Stacks community has been working on a new standard for wallet-to-dapp communication. Stacks Connect and related projects now use standards like [WBIPs](https://wbips.netlify.app/) and [SIP-030](https://github.com/janniks/sips/blob/main/sips/sip-030/sip-030-wallet-interface.md) to allow wallets to communicate with dapps in a more simplified and flexible way. + +{% hint style="info" %} +Migration status\ +Feel free to continue using Stacks Connect `7.x.x` while things stabilize. The `7.x.x` version may still be better supported by some wallets. + +Legacy installs: + +```bash +npm install @stacks/connect@7.10.1 +``` +{% endhint %} + +## Deprecations + +The following classes, methods, and types are deprecated in favor of the new `request` RPC methods: + +* `show...` and `open...` methods +* `authenticate` method +* `UserSession` class and related functionality +* `AppConfig` class +* `SessionOptions` interface +* `SessionData` interface +* `UserData` interface +* `SessionDataStore` class +* `InstanceDataStore` class +* `LocalStorageStore` class + +{% hint style="info" %} +Backwards compatibility\ +`UserSession` and `AppConfig` remain available in `8.x.x` for caching addresses via `loadUserData`, but consider them temporary helpers while you migrate. +{% endhint %} + +## Migration steps + +{% stepper %} +{% step %} +### Update your @stacks/connect version + +```bash +npm install @stacks/connect@latest +``` +{% endstep %} + +{% step %} +### Replace legacy methods with `request` + +Switch from `showXyz`, `openXyz`, and `doXyz` helpers to the generic `request(method, params)` API. The `request` function is async, so replace `onFinish`/`onCancel` callbacks with `await` or `.then().catch()` chains. + +Examples: + +* `showConnect()`, `authenticate()` → `connect()` +* `useConnect().doContractCall({})` → `request('stx_callContract', {})` +* `openContractDeploy()` → `request('stx_deployContract', {})` +{% endstep %} + +{% step %} +### Use `connect` instead of `showConnect` / `authenticate` + +`connect()` is an alias for `request('getAddresses', { forceWalletSelect: true })` and caches the selected address in local storage by default. +{% endstep %} + +{% step %} +### Update authentication state management + +* Replace `UserSession.isSignedIn()` with `isConnected()` +* Replace `UserSession.signUserOut()` with `disconnect()` +{% endstep %} + +{% step %} +### Remove legacy code + +* Delete references to deprecated helpers (`AppConfig`, `UserSession`, etc.) +* Remove the `@stacks/connect-react` package + * Manually reload components if you rely on local storage updates + * Hooks are no longer required for Stacks Connect +* A new `@stacks/react` package is in development to simplify state tracking (transaction status, network changes, and more) +{% endstep %} +{% endstepper %} + +## Address Access + +Previously, the `UserSession` class was used to access the user's addresses and data, which abstracted away the underlying implementation details. Now, the `request` method is used to directly interact with the wallet, giving developers more explicit control and clarity over what's happening under the hood. This manual approach makes the wallet interaction more transparent and customizable. Developers can manually manage the currently connected user's address in e.g. local storage, jotai, etc. or use the `connect()`/`request()` method to cache the address in local storage. + +{% hint style="warning" %} +Security note\ +`8.x.x` wallets return only the current network's address (previous versions returned both mainnet and testnet). +{% endhint %} diff --git a/docs/reference/stacks-connect/wallet-support.md b/docs/reference/stacks-connect/wallet-support.md new file mode 100644 index 0000000000..12eeb41025 --- /dev/null +++ b/docs/reference/stacks-connect/wallet-support.md @@ -0,0 +1,61 @@ +# Wallet Support + +{% hint style="info" %} +Legend: + +* 🔴 No support (yet) +* 🟡 Partial support +* 🟢 Supported +* 🔵 Compatibility overrides present (may transform/normalize behavior) +{% endhint %} + +## Wallet Support + +This page provides detailed information about which methods and events are supported by different wallet providers in the Stacks ecosystem. + +### Method Compatibility + +| Method | Leather | Xverse-like | +| --------------------------- | --------------------------------------- | ------------------------------------------------------------------- | +| `getAddresses` | 🟡 No support for experimental purposes | 🟡 Use `wallet_connect` instead | +| `sendTransfer` | 🟡 Expects `amount` as string | 🟡 Expects `amount` as number | +| `signPsbt` | 🟡 Uses signing index array only | 🟡 Uses `signInputs` record instead of array | +| `stx_getAddresses` | 🟢 | 🔴 | +| `stx_getAccounts` | 🔴 | 🟢 | +| `stx_getNetworks` | 🔴 | 🔴 | +| `stx_transferStx` | 🟢 | 🟢 | +| `stx_transferSip10Ft` | 🟢 | 🔴 | +| `stx_transferSip9Nft` | 🟢 | 🔴 | +| `stx_callContract` | 🟡 Hex-encoded Clarity values only | 🟡 Hex-encoded Clarity values only, no support for `postConditions` | +| `stx_deployContract` | 🟡 Hex-encoded Clarity values only | 🟡 Hex-encoded Clarity values only, no support for `postConditions` | +| `stx_signTransaction` | 🟡 Hex-encoded Clarity values only | 🟡 Hex-encoded Clarity values only | +| `stx_signMessage` | 🟡 Hex-encoded Clarity values only | 🟡 Hex-encoded Clarity values only | +| `stx_signStructuredMessage` | 🟡 Hex-encoded Clarity values only | 🟡 Hex-encoded Clarity values only | +| `stx_updateProfile` | 🔴 | 🔴 | + +### Event Compatibility + +| Event | Leather | Xverse | +| ------------------- | ------- | ------ | +| `stx_accountChange` | 🔴 | 🔴 | +| `stx_networkChange` | 🔴 | 🔴 | + +### Compatibility Layer + +The `request` method in `@stacks/connect` adds a layer of auto-compatibility for different wallet providers. This helps unify the interface where wallet providers may implement methods and results differently. + +* 🟢 No overrides needed for any wallet +* 🔵 Has compatibility overrides that maintain functionality +* 🟡 Has breaking overrides that may lose some information + +| Method | Status | Notes | +| --------------------------- | ------ | ----------------------------------------------------------------------------------------- | +| `getAddresses` | 🔵 | Maps to `wallet_connect` for Xverse-like wallets | +| `sendTransfer` | 🔵 | Converts `amount` to number for Xverse, string for Leather | +| `signPsbt` | 🟡 | Transforms PSBT format for Leather (base64 to hex) with lossy restructure of `signInputs` | +| `stx_getAddresses` | 🔵 | Maps to `wallet_connect` for Xverse-like wallets | +| `stx_callContract` | 🔵 | Transforms Clarity values to hex-encoded format for compatibility | +| `stx_deployContract` | 🔵 | Transforms Clarity values to hex-encoded format for compatibility | +| `stx_signTransaction` | 🔵 | Transforms Clarity values to hex-encoded format for compatibility | +| `stx_signMessage` | 🔵 | Transforms Clarity values to hex-encoded format for compatibility | +| `stx_signStructuredMessage` | 🔵 | Transforms Clarity values to hex-encoded format for compatibility | diff --git a/docs/reference/stacks-node-configuration.md b/docs/reference/stacks-node-configuration.md new file mode 100644 index 0000000000..708832a45e --- /dev/null +++ b/docs/reference/stacks-node-configuration.md @@ -0,0 +1,235 @@ +# Stacks Node Configuration + +{% hint style="info" %} +Note that these config fields are for a Stacks follower node. If you are running a signer alongside your Stacks node, you'll want to use the sample file found on the [Signer Configuration](broken-reference) page as it contains additional parameters needed for your signer and Stacks node to function properly. +{% endhint %} + +### Usage + +```bash +stacks-node sub-command [--subcommand-option ] +``` + +#### Subcommands + +* `mocknet`: start a mocknet instance using defaults +* `testnet`: start a testnet instance using defaults (chainstate is not persistent) +* `mainnet`: start a mainnet instance using defaults (chainstate is not persistent) +* `start`: combined with `--config`, starts an instance with a specified configuration file +* `version`: displays binary version +* `help`: displays the help message + +### Configuration File Options + +The Stacks Blockchain configuration file has multiple sections under which an option may be placed. + +* node +* events\_observer +* connection\_options +* burnchain +* ustx\_balance +* miner + +For reference, several configuration file examples are [available here](https://github.com/stacks-network/stacks-core/tree/master/sample/conf). + +#### node + +Contains various configuration options for the stacks-node binary. + +| Name | Required | Description | +| ---------------------------- | -------- | ---------------------------------------------------------------------------------------------------------- | +| rpc\_bind | ✓ | IPv4 address and port to open for RPC connections | +| p2p\_bind | ✓ | IPv4 address and port to open for P2P connections | +| working\_dir | | Absolute path to the directory where chainstate data will be stored | +| data\_url | | IPv4 address and port for incoming RPC connections | +| p2p\_address | | IPv4 address and port for incoming P2P connections | +| bootstrap\_node | | Public key, IPv4 address, and port to bootstrap the chainstate | +| wait\_time\_for\_microblocks | | The amount of time in ms to wait before trying to mine a block after catching up to the anchored chain tip | +| seed | | The private key to use for mining. Only needed if `miner` is set to `true` | +| local\_peer\_seed | | The private key to use for signing P2P messages in the networking stack | +| miner | | Determines whether the node is running a follower (`false`) or a miner (`true`). Defaults to `false` | +| mock\_mining | | Simulates running a miner (typically used for debugging) | +| mock\_mining\_output\_dir | | Folder for mock mining data | +| mine\_microblocks | | Determines whether the node will mine microblocks. Will only take effect if `miner` is set to `true` | +| prometheus\_bind | | Address and port for Prometheus metrics collection. | +| deny\_nodes | | List of ip addresses of nodes that should be ignored | +| stacker | | Determines whether the node is running a stacker (`true`) that issues events for signer binary | + +#### events\_observer + +{% hint style="info" %} +This section is _optional_ and not required + +However, if this section is added, **all** fields are required. +{% endhint %} + +Contains options for sending events emitted to the [stacks-blockchain-api](https://github.com/hirosystems/stacks-blockchain-api) service. + +| Name | Required | Description | +| ------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| endpoint | ✓ | Address and port to a [stacks-blockchain-api](https://github.com/hirosystems/stacks-blockchain-api) service | +| events\_keys | ✓ | Event keys for which to watch. The emitted node events can be restricted by account, function name and event type. Asterix ("\*") can be used to emit all events. | + +#### connection\_options + +{% hint style="info" %} +This section is _optional_ and not required. +{% endhint %} + +Specifies configuration options for others connecting to the stacks node. + +| Name | Required | Description | +| ------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| public\_ip\_address | | Public IPv4 to advertise to other nodes | +| download\_interval | | Time (in seconds) between attempts to download blocks | +| walk\_interval | | Time (in seconds) between attempts to walk the list of neighbors | +| private\_neighbors | | If false, this node won't announce or accept neighbors that are behind private networks. Defaults to true. | +| read\_only\_call\_limit\_read\_length | | Total number of bytes allowed to be read by an individual read-only function call | +| read\_only\_call\_limit\_read\_count | | Total number of independent read operations permitted for an individual read-only function call | +| read\_only\_call\_limit\_runtime | | [Runtime cost](https://github.com/stacksgov/sips/blob/main/sips/sip-006/sip-006-runtime-cost-assessment.md) limit for an individual read-only function call | + +#### burnchain + +This section contains configuration options pertaining to the blockchain the stacks-node binds to on the backend for proof-of-transfer (BTC). + +| Name | Required | Description | +| ---------- | -------- | --------------------------------------------------------------------------------------------------------------------- | +| chain | ✓ | The blockchain stacks-node binds to on the backend for proof-of-transfer. Only value supported: `bitcoin` | +| mode | ✓ | The profile or test phase of which to run stacks-node. Valid values are \[ `mocknet`, `testnet`, `xenon`, `mainnet` ] | +| peer\_host | | FQDN of the host running the backend Bitcoin blockchain | +| rpc\_port | | RPC port of `peer_host` | +| peer\_port | | P2P port of `peer_host` | + +**Mining** + +| Name | Required | Description | +| -------------------------------- | -------- | -------------------------------------------------------------------------------------------------- | +| burn\_fee\_cap | ✓ | Maximum amount (in sats) of "burn commitment" to broadcast for the next block's leader election | +| satoshis\_per\_byte | ✓ | [Amount (in sats) per byte](https://bitcoinfees.net/) - Used to calculate the transaction fees | +| commit\_anchor\_block\_within | | Sets the time period (in milliseconds) for commitments. Only used when `mode` is set to `mocknet`. | +| tenure\_extend\_cost\_threshold | | Percentage of block budget that must be used before attempting a time-based tenure extend | +| block\_rejection\_timeout\_steps | | Define the timeout to apply while waiting for signers responses, based on the amount of rejections | + +#### ustx\_balance + +{% hint style="info" %} +This section is only required for the `testnet` and `mocknet` networks. + +However, if this section is added, **all** fields are required. +{% endhint %} + +This section contains configuration options allocating microSTX per address in the genesis block + +This section can repeat multiple times, but each section can only define a single address. + +| Name | Required | Description | +| ------- | -------- | --------------------------------------------------------------------- | +| address | ✓ | Address which maintains a microSTX balance | +| amount | ✓ | The balance of microSTX given to the address at the start of the node | + +### Example Mainnet Follower Configuration + +{% code title="stacks-node-mainnet.toml" %} +```toml +[node] +working_dir = "/stacks-blockchain" +rpc_bind = "0.0.0.0:30443" +p2p_bind = "0.0.0.0:20444" +bootstrap_node = "02196f005965cebe6ddc3901b7b1cc1aa7a88f305bb8c5893456b8f9a605923893@seed.mainnet.hiro.so:20444,02539449ad94e6e6392d8c1deb2b4e61f80ae2a18964349bc14336d8b903c46a8c@cet.stacksnodes.org:20444,02ececc8ce79b8adf813f13a0255f8ae58d4357309ba0cedd523d9f1a306fcfb79@sgt.stacksnodes.org:20444,0303144ba518fe7a0fb56a8a7d488f950307a4330f146e1e1458fc63fb33defe96@est.stacksnodes.org:20444" + +[burnchain] +chain = "bitcoin" +mode = "mainnet" +peer_host = "localhost" +peer_port = 8333 + +[[events_observer]] +endpoint = "localhost:3700" +events_keys = ["*"] +``` +{% endcode %} + +### Example Testnet Follower Configuration + +{% code title="stacks-node-testnet.toml" %} +```toml +[node] + +rpc_bind = "0.0.0.0:20443" +p2p_bind = "0.0.0.0:20444" +bootstrap_node = "029266faff4c8e0ca4f934f34996a96af481df94a89b0c9bd515f3536a95682ddc@seed.testnet.hiro.so:30444" +prometheus_bind = "127.0.0.1:9153" +working_dir = "/stacks-blockchain" + +[burnchain] +chain = "bitcoin" +mode = "krypton" +peer_host = "bitcoin.regtest.hiro.so" +peer_port = 18444 +pox_prepare_length = 100 +pox_reward_length = 900 + +[[ustx_balance]] +address = "ST2QKZ4FKHAH1NQKYKYAYZPY440FEPK7GZ1R5HBP2" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST319CF5WV77KYR1H3GT0GZ7B8Q4AQPY42ETP1VPF" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST221Z6TDTC5E0BYR2V624Q2ST6R0Q71T78WTAX6H" +amount = 10000000000000000 + +[[ustx_balance]] +address = "ST2TFVBMRPS5SSNP98DQKQ5JNB2B6NZM91C4K3P7B" +amount = 10000000000000000 + +[fee_estimation] +fee_estimator = "fuzzed_weighted_median_fee_rate" + +[[burnchain.epochs]] +epoch_name = "1.0" +start_height = 0 + +[[burnchain.epochs]] +epoch_name = "2.0" +start_height = 0 + +[[burnchain.epochs]] +epoch_name = "2.05" +start_height = 1 + +[[burnchain.epochs]] +epoch_name = "2.1" +start_height = 2 + +[[burnchain.epochs]] +epoch_name = "2.2" +start_height = 3 + +[[burnchain.epochs]] +epoch_name = "2.3" +start_height = 4 + +[[burnchain.epochs]] +epoch_name = "2.4" +start_height = 5 + +[[burnchain.epochs]] +epoch_name = "2.5" +start_height = 6 + +[[burnchain.epochs]] +epoch_name = "3.0" +start_height = 1_900 + +[[burnchain.epochs]] +epoch_name = "3.1" +start_height = 2_000 + +[[burnchain.epochs]] +epoch_name = "3.2" +start_height = 71_525 +``` +{% endcode %} diff --git a/docs/reference/stacks.js-integrations/pyth-oracle-integration.md b/docs/reference/stacks.js-integrations/pyth-oracle-integration.md new file mode 100644 index 0000000000..8c11cd1e9f --- /dev/null +++ b/docs/reference/stacks.js-integrations/pyth-oracle-integration.md @@ -0,0 +1,169 @@ +# Pyth Oracle Integration + +This guide shows how to integrate Pyth Network price feeds into a frontend application with Stacks.js. + +## Objectives + +* Install and configure the Pyth SDK +* Fetch VAA messages from the Hermes API +* Build transactions that include oracle data +* Add post-conditions that account for oracle fees + +## Prerequisites + +* A React or Node.js application with Stacks.js installed +* Understanding of [Pyth oracle contracts](pyth-oracle-integration.md#) + +## Quickstart + +{% stepper %} +{% step %} +### Install dependencies + +Install the Pyth SDK alongside your existing Stacks.js packages. + +```bash +npm install @pythnetwork/price-service-client buffer +``` + +`buffer` enables data format conversion in browser environments. +{% endstep %} + +{% step %} +### Set up the Pyth client + +Create a service that fetches Pyth price feed updates. + +```ts +// services/pyth.ts +import { PriceServiceConnection } from '@pythnetwork/price-service-client'; +import { Buffer } from 'buffer'; + +// Price feed IDs +export const PRICE_FEEDS = { + BTC_USD: '0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43', + STX_USD: '0xec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c17', + ETH_USD: '0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace', + USDC_USD: '0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a', +}; + +const pythClient = new PriceServiceConnection('https://hermes.pyth.network', { + priceFeedRequestConfig: { + binary: true, + }, +}); + +export async function fetchPriceUpdateVAA(priceFeedId: string): Promise { + try { + const vaas = await pythClient.getLatestVaas([priceFeedId]); + + if (!vaas || vaas.length === 0) { + throw new Error('No VAA data received'); + } + + const messageBuffer = Buffer.from(vaas[0], 'base64'); + const hexString = messageBuffer.toString('hex'); + + return `0x${hexString}`; + } catch (error) { + console.error('Failed to fetch price VAA:', error); + throw error; + } +} +``` +{% endstep %} + +{% step %} +### Build oracle-enabled transactions + +Use fresh price data inside a contract call. + +```ts +// components/PythTransaction.tsx +import { request } from '@stacks/connect'; +import { Cl } from '@stacks/transactions'; +import { fetchPriceUpdateVAA, PRICE_FEEDS } from '../services/pyth'; +import { useState } from 'react'; + +export function MintWithOraclePrice() { + const [loading, setLoading] = useState(false); + + const handleMint = async () => { + setLoading(true); + try { + const priceVAA = await fetchPriceUpdateVAA(PRICE_FEEDS.BTC_USD); + const vaaBuffer = Cl.bufferFromHex(priceVAA.slice(2)); + + const response = await request('stx_callContract', { + contract: 'SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R.benjamin-club', + functionName: 'mint-for-hundred-dollars', + functionArgs: [vaaBuffer], + postConditionMode: 'deny', + network: 'mainnet', + }); + + console.log('Transaction submitted:', response.txid); + alert(`NFT minted! Transaction ID: ${response.txid}`); + } catch (error) { + console.error('Minting failed:', error); + alert('Failed to mint NFT'); + } finally { + setLoading(false); + } + }; + + return ( + + ); +} +``` +{% endstep %} + +{% step %} +### Add post-conditions for oracle fees + +Include post-conditions that reflect oracle fees and token transfers. + +```ts +// components/MintWithPostConditions.tsx +import { request } from '@stacks/connect'; +import { Cl, Pc } from '@stacks/transactions'; +import { fetchPriceUpdateVAA, PRICE_FEEDS } from '../services/pyth'; + +export function MintWithPostConditions() { + const handleMint = async () => { + const userAddress = 'SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R'; + + const postConditions = [ + Pc.principal(userAddress).willSendLte(1).ustx(), + Pc.principal(userAddress) + .willSendEq(100000) + .ft('SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sbtc-token', 'sbtc'), + ]; + + try { + const priceVAA = await fetchPriceUpdateVAA(PRICE_FEEDS.BTC_USD); + + const response = await request('stx_callContract', { + contract: `${userAddress}.benjamin-club`, + functionName: 'mint-for-hundred-dollars', + functionArgs: [Cl.bufferFromHex(priceVAA.slice(2))], + postConditions, + postConditionMode: 'deny', + network: 'mainnet', + }); + + console.log('Transaction successful:', response.txid); + } catch (error) { + console.error('Transaction failed:', error); + } + }; + + return ; +} +``` +{% endstep %} +{% endstepper %} + diff --git a/docs/reference/stacks.js-integrations/react-native-integration.md b/docs/reference/stacks.js-integrations/react-native-integration.md new file mode 100644 index 0000000000..3d274d4f38 --- /dev/null +++ b/docs/reference/stacks.js-integrations/react-native-integration.md @@ -0,0 +1,290 @@ +# React Native Integration + +Stacks.js can be integrated into React Native applications to bring blockchain functionality to mobile devices. This tutorial walks you through setting up a React Native project with Expo and configuring it to work with Stacks.js libraries. + +## Objectives + +* Set up an Expo project configured for Stacks.js +* Install and configure necessary polyfills for React Native +* Generate wallets and sign transactions in a mobile app +* Handle React Native's JavaScript environment limitations +* Build a working Stacks mobile application + +## Prerequisites + +* Node.js and npm installed on your development machine +* Basic knowledge of React Native and Expo +* Familiarity with Stacks.js concepts +* iOS or Android device or simulator for testing + +## Set up the Expo project + +Start by creating a new Expo project. The latest version of Expo provides the best compatibility with Stacks.js polyfills. + +```bash +npx create-expo-app@latest my-stacks-app +cd my-stacks-app +``` + +The boilerplate project includes everything needed to start building. Test the initial setup by running the development server. + +```bash +npm start +``` + +Connect your mobile device using the Expo Go app and scan the QR code to verify the base project works correctly. + +## Install necessary dependencies + +React Native's JavaScript environment lacks certain Node.js and browser APIs that Stacks.js requires. Install the core Stacks libraries along with necessary polyfills. + +```bash +npm install @stacks/transactions @stacks/wallet-sdk +``` + +Install the polyfill dependencies as dev dependencies to handle missing APIs. + +```bash +npm install --save-dev buffer process react-native-get-random-values \ + text-encoding readable-stream crypto-browserify @peculiar/webcrypto +``` + +These polyfills provide: + +* `buffer` and `process` for Node.js globals +* `react-native-get-random-values` for crypto random values +* `text-encoding` for `TextEncoder` and `TextDecoder` +* `crypto-browserify` and `@peculiar/webcrypto` for cryptographic functions + +## Configure Metro bundler + +Metro bundler needs configuration to properly resolve Node.js modules. Create a custom Metro configuration file. + +```bash +npx expo customize metro.config.js +``` + +Update `metro.config.js` to map Node.js modules to their React Native-compatible versions. + +```ts +const { getDefaultConfig } = require('expo/metro-config'); + +const config = getDefaultConfig(__dirname); + +config.resolver.extraNodeModules = { + stream: require.resolve('readable-stream'), + crypto: require.resolve('crypto-browserify'), +}; + +module.exports = config; +``` + +This configuration ensures that when Stacks.js requests Node.js modules, Metro provides the appropriate polyfills. + +## Set up global polyfills + +Create a polyfill system to make browser and Node.js APIs available in React Native. This requires modifying the app's entry point. + +### Create the polyfill file + +Create `polyfill.js` to initialize the required global objects. + +```ts +import { Buffer } from 'buffer/'; +import process from 'process'; +import 'react-native-get-random-values'; +import { TextDecoder, TextEncoder } from 'text-encoding'; + +global.process = process; +global.Buffer = Buffer; +global.TextEncoder = TextEncoder; +global.TextDecoder = TextDecoder; +``` + +### Create a custom entry point + +Create `index.js` so the app loads polyfills before the UI renders. + +```ts +import './polyfill'; +import { Crypto } from '@peculiar/webcrypto'; + +Object.assign(global.crypto, new Crypto()); + +import 'expo-router/entry'; +``` + +{% hint style="warning" %} +Runtime initialization errors: Polyfills must be loaded in separate files as shown. Loading them in the same file can cause runtime initialization errors. +{% endhint %} + +### Update package.json + +Point the app to use the new entry point. + +```json +{ + "main": "index.js" +} +``` + +## Implement Stacks functionality + +With the environment configured, you can now use Stacks.js in your React Native components. Update the main screen to demonstrate wallet generation and transaction signing. + +### Import Stacks.js modules + +Edit `app/(tabs)/index.tsx` to import the necessary Stacks.js functions. + +```ts +import { + TransactionVersion, + getAddressFromPrivateKey, + makeSTXTokenTransfer, +} from '@stacks/transactions'; +import { Wallet, generateSecretKey, generateWallet } from '@stacks/wallet-sdk'; +import { useState } from 'react'; +import { Button } from 'react-native'; +``` + +### Set up component state + +Create state variables to manage wallet data and user feedback. + +```ts +export default function HomeScreen() { + const [mnemonic, setMnemonic] = useState('Press button to generate'); + const [wallet, setWallet] = useState(null); + const [log, setLog] = useState(''); + + // Component implementation continues... +} +``` + +### Generate a wallet and sign a transaction + +Implement the core functionality to create a wallet and sign a transaction. + +```ts +const generate = async () => { + try { + const mnemonic = generateSecretKey(); + setMnemonic(mnemonic); + + const wallet = await generateWallet({ + secretKey: mnemonic, + password: '', + }); + setWallet(wallet); + + const txOptions = { + amount: 1000, + anchorMode: 'any' as const, + recipient: 'SP3W993D3BRDYB284CY3SBFDEGTC5XEDJPDEA21CN', + senderKey: wallet.accounts[0].stxPrivateKey, + fee: 300, + network: 'testnet' as const, + nonce: 0, + }; + + const transaction = await makeSTXTokenTransfer(txOptions); + setLog('Transaction signed successfully'); + } catch (error) { + setLog(`Error: ${error.message}`); + } +}; +``` + +### Build the user interface + +Show wallet information and trigger wallet generation from the UI. + +```ts +return ( + + Stacks Wallet Demo + + + Seed Phrase + {mnemonic} +