stacksagent 1.3.4 β†’ 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,179 +1,271 @@
1
- # stacksagent
1
+ <!--
2
+ This README is the single source of truth.
3
+ It's automatically copied to cli/README.md during `npm run build`
4
+ -->
2
5
 
3
- > AI Skill CLI installer for building Stacks blockchain applications
6
+ # Stacks Agent
4
7
 
5
- [![npm version](https://img.shields.io/npm/v/stacksagent.svg)](https://www.npmjs.com/package/stacksagent)
6
- [![npm downloads](https://img.shields.io/npm/dm/stacksagent.svg)](https://www.npmjs.com/package/stacksagent)
7
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ > AI Skill for building Stacks blockchain applications - Bitcoin's most powerful smart contract layer
8
9
 
9
- Transform your AI coding assistant into a Stacks blockchain expert with 170+ searchable knowledge entries.
10
+ An intelligent AI assistant that provides development guidance for Clarity smart contracts, DeFi integration, and blockchain development on Stacks. Works with **Claude Code, Cursor, Windsurf, Antigravity, GitHub Copilot, Kiro, and Codex**.
10
11
 
11
- ## 🎯 Supported AI Platforms
12
+ ![Stacks Agent](https://img.shields.io/npm/v/stacks-agent?color=blue&label=stacks-agent)
13
+ ![License](https://img.shields.io/npm/l/stacks-agent)
14
+ ![Downloads](https://img.shields.io/npm/dm/stacks-agent)
12
15
 
13
- - **Claude Code** - Claude's official CLI
14
- - **Cursor** - AI-first code editor
15
- - **Windsurf** - AI coding assistant
16
- - **Antigravity** - AI development tool
17
- - **GitHub Copilot** - GitHub's AI assistant
18
- - **Kiro** - AI coding helper
19
- - **Codex** - OpenAI Codex integration
16
+ ## ✨ Features
17
+
18
+ **495+ Knowledge Base Entries Across 15 Domains:**
19
+
20
+ ### Core Language & Tools (175 entries)
21
+ - **πŸ”· 61 Clarity Functions** - Complete language reference with examples
22
+ - **βš›οΈ 75 Stacks.js Utilities** - Wallet, transactions, CV constructors, API integration
23
+ - **πŸ“‹ 14 Contract Templates** - SIP-010, SIP-009, DAO, Vault, Marketplace, Stacking
24
+ - **πŸš€ 25 Deployment Steps** - Testnet, mainnet, and devnet workflows
25
+
26
+ ### Domain-Specific Integrations (185 entries)
27
+ - **🌐 21 BNS Operations** - Name registration, resolution (Clarity + JS + API)
28
+ - **πŸ’° 25 Stacking Guides** - PoX stacking and pool delegation (Clarity + JS + API)
29
+ - **πŸ”„ 25 DeFi Protocols** - Alex, Velar, Bitflow, Zest, StackingDAO (Clarity + JS + API)
30
+ - **🎨 30 NFT Operations** - SIP-009, minting, marketplace patterns (Clarity + JS + API)
31
+ - **πŸ’Ž 40 Token Operations** - SIP-010 fungible tokens, DeFi integrations (Clarity + JS + API)
32
+ - **πŸ” 14 Authentication** - Gaia storage, wallet auth, JWT, token-gating (Clarity + JS + API)
33
+ - **πŸ“Š 30 Oracle Integration** - Pyth Network price feeds, VAA handling, Hermes API (Clarity + JS + API)
34
+
35
+ ### Advanced & Specialized (135 entries)
36
+ - **πŸ›‘οΈ 15 Security Patterns** - Common vulnerabilities and secure implementations
37
+ - **🎯 40 Advanced Patterns** - Pagination, SWR, presales, lotteries, vesting, CSV export
38
+ - **πŸ”— 30 Chainhooks** - Event indexing, webhooks, predicates, ordinals tracking
39
+ - **πŸ€– 50 Trading Bots** - Automated trading, wallet SDKs, Privy, bonding curves
40
+
41
+ ## 🎯 Use Cases
42
+
43
+ - Generate Clarity smart contracts from natural language
44
+ - Audit contracts for security vulnerabilities
45
+ - Integrate with DeFi protocols (swaps, liquidity, lending)
46
+ - Integrate Pyth Network oracle for price feeds
47
+ - Deploy to testnet/mainnet
48
+ - Build dApps with Stacks.js
49
+ - Implement stacking and BNS features
20
50
 
21
51
  ## πŸ“¦ Installation
22
52
 
53
+ ### Using CLI (Recommended)
54
+
23
55
  ```bash
56
+ # Install globally
24
57
  npm install -g stacksagent
58
+
59
+ # Go to your project
60
+ cd /path/to/your/project
61
+
62
+ # Install for your AI assistant
63
+ stacksagent init --ai claude # Claude Code
64
+ stacksagent init --ai cursor # Cursor
65
+ stacksagent init --ai windsurf # Windsurf
66
+ stacksagent init --ai antigravity # Antigravity
67
+ stacksagent init --ai copilot # GitHub Copilot
68
+ stacksagent init --ai kiro # Kiro
69
+ stacksagent init --ai codex # OpenAI Codex
70
+ stacksagent init --ai all # All platforms
25
71
  ```
26
72
 
27
- ## πŸš€ Usage
73
+ ### Manual Installation
28
74
 
29
- ### Install for Your AI Platform
75
+ Copy the appropriate folders to your project:
30
76
 
31
- ```bash
32
- # Claude Code
33
- stacksagent init --ai claude
77
+ | AI Assistant | Folders to Copy |
78
+ | --------------- | -------------------------------------------------------- |
79
+ | Claude Code | `.claude/skills/stacks-agent/` |
80
+ | Cursor | `.cursor/commands/stacks-agent.md` + `.shared/stacks-agent/` |
81
+ | Windsurf | `.windsurf/workflows/stacks-agent.md` + `.shared/stacks-agent/` |
82
+ | Antigravity | `.agent/workflows/stacks-agent.md` + `.shared/stacks-agent/` |
83
+ | GitHub Copilot | `.github/prompts/stacks-agent.prompt.md` + `.shared/stacks-agent/` |
84
+ | Kiro | `.kiro/steering/stacks-agent.md` + `.shared/stacks-agent/` |
85
+ | Codex | `.codex/skills/stacks-agent/` |
34
86
 
35
- # Cursor
36
- stacksagent init --ai cursor
87
+ ## πŸš€ Usage
37
88
 
38
- # Windsurf
39
- stacksagent init --ai windsurf
89
+ ### Claude Code
40
90
 
41
- # Antigravity
42
- stacksagent init --ai antigravity
91
+ The skill activates automatically when you request Stacks development work:
43
92
 
44
- # GitHub Copilot
45
- stacksagent init --ai copilot
93
+ ```
94
+ Create a meme token called PEPE with 1 billion supply
95
+ ```
46
96
 
47
- # Kiro
48
- stacksagent init --ai kiro
97
+ ### Cursor / Windsurf / Antigravity
49
98
 
50
- # Codex
51
- stacksagent init --ai codex
99
+ Use the slash command:
52
100
 
53
- # All platforms at once
54
- stacksagent init --ai all
101
+ ```
102
+ /stacks-agent Create a meme token called PEPE with 1 billion supply
55
103
  ```
56
104
 
57
- ### Force Overwrite
105
+ ### GitHub Copilot
58
106
 
59
- ```bash
60
- stacksagent init --ai claude --force
107
+ Reference in chat:
108
+
109
+ ```
110
+ @stacks-agent How do I swap tokens on Alex?
61
111
  ```
62
112
 
63
- ### Check Version
113
+ ## πŸ’‘ Example Prompts
64
114
 
65
- ```bash
66
- stacksagent --version
115
+ ```
116
+ "Create a SIP-010 token with burn mechanism"
117
+ "Build an NFT collection with royalties"
118
+ "Audit this Clarity contract for security issues"
119
+ "Show me how to integrate Alex swap in my dApp"
120
+ "How do I get BTC/USD price from Pyth oracle?"
121
+ "Deploy my contract to testnet"
122
+ "How do I implement stacking in my app?"
123
+ "Generate a DAO contract with proposal voting"
67
124
  ```
68
125
 
69
- ### Update to Latest
126
+ ## πŸ” Knowledge Base Search
127
+
128
+ The skill includes a powerful BM25-based search engine:
70
129
 
71
130
  ```bash
72
- stacksagent update
73
- ```
131
+ # Auto-detect domain
132
+ python3 .claude/skills/stacks-agent/scripts/search.py "define-public"
74
133
 
75
- ### List Available Versions
134
+ # Search specific domain
135
+ python3 .claude/skills/stacks-agent/scripts/search.py "swap tokens" --domain defi
76
136
 
77
- ```bash
78
- stacksagent versions
137
+ # Get more results
138
+ python3 .claude/skills/stacks-agent/scripts/search.py "security" --domain security -n 10
139
+
140
+ # JSON output
141
+ python3 .claude/skills/stacks-agent/scripts/search.py "stx transfer" --domain stacksjs -f json
79
142
  ```
80
143
 
81
- ## πŸ’‘ What Gets Installed
144
+ **Available domains**:
145
+ - `clarity` - Syntax and functions
146
+ - `templates` - Contract templates
147
+ - `security` - Security patterns
148
+ - `defi` - DeFi protocols
149
+ - `stacksjs` - JavaScript snippets
150
+ - `bns` - BNS operations
151
+ - `stacking` - Stacking guides
152
+ - `deployment` - Deployment steps
153
+ - `auto` - Auto-detect (default)
82
154
 
83
- The CLI installs platform-specific skill files in your project:
155
+ ## πŸ“š Knowledge Base Contents
84
156
 
85
- - **Claude Code**: `.claude/skills/stacks-agent/`
86
- - **Cursor**: `.cursor/commands/` + `.shared/stacks-agent/`
87
- - **Windsurf**: `.windsurf/workflows/` + `.shared/stacks-agent/`
88
- - **Antigravity**: `.agent/workflows/` + `.shared/stacks-agent/`
89
- - **Copilot**: `.github/prompts/` + `.shared/stacks-agent/`
90
- - **Kiro**: `.kiro/steering/` + `.shared/stacks-agent/`
91
- - **Codex**: `.codex/skills/stacks-agent/`
157
+ ### Clarity Language (61 entries)
158
+ Types, functions, control flow, arithmetic, comparisons, maps, tokens, STX operations
92
159
 
93
- ## πŸ“š Knowledge Base
160
+ ### Contract Templates (14 entries)
161
+ - **Tokens**: SIP-010 basic, mintable, burnable, capped
162
+ - **NFTs**: SIP-009 basic, mintable, royalties
163
+ - **DeFi**: Vault basic/timelocked, liquidity pool
164
+ - **DAO**: Basic DAO, treasury management
165
+ - **Other**: Marketplace, stacking pool
94
166
 
95
- 170+ entries across 8 domains:
167
+ ### Security Patterns (15 entries)
168
+ - Critical: Access control, unchecked transfers
169
+ - High: Reentrancy, arithmetic safety
170
+ - Medium: Input validation, front-running
171
+ - Low: Code style, gas optimization
96
172
 
97
- - **Clarity** (61) - Language syntax and functions
98
- - **Templates** (14) - Contract templates (SIP-010, SIP-009, DAO, Vault)
99
- - **Security** (15) - Vulnerability patterns and fixes
100
- - **DeFi** (15) - Protocol integrations (Alex, Velar, Bitflow, Zest)
101
- - **Stacks.js** (30) - Frontend integration snippets
102
- - **BNS** (10) - Bitcoin Name System operations
103
- - **Stacking** (15) - PoX stacking guides
104
- - **Deployment** (25) - Testnet/mainnet deployment steps
173
+ ### DeFi Protocols (15 entries)
174
+ Alex, Velar, Bitflow, Zest, StackingDAO, Boost, Faktory integration patterns
105
175
 
106
- ## πŸ” Search Knowledge Base
176
+ ### Stacks.js (30 entries)
177
+ Wallet connection, transactions, Clarity values, API calls, post-conditions
107
178
 
108
- After installation, search the knowledge base:
179
+ ### BNS (10 entries)
180
+ Name resolution, registration, transfer, updates
109
181
 
110
- ```bash
111
- python3 .claude/skills/stacks-agent/scripts/search.py "your query"
182
+ ### Stacking (15 entries)
183
+ Direct stacking, delegation, pools, rewards
112
184
 
113
- # Search specific domain
114
- python3 .claude/skills/stacks-agent/scripts/search.py "swap tokens" --domain defi
115
-
116
- # Get more results
117
- python3 .claude/skills/stacks-agent/scripts/search.py "security" -n 10
185
+ ### Deployment (25 entries)
186
+ Testnet, mainnet, devnet workflows with Clarinet
118
187
 
119
- # JSON output
120
- python3 .claude/skills/stacks-agent/scripts/search.py "stx transfer" -f json
121
- ```
188
+ ## πŸ›‘οΈ Security Best Practices
122
189
 
123
- ## πŸ’¬ Example Prompts
190
+ All generated contracts include:
124
191
 
125
- After installing the skill, ask your AI assistant:
192
+ - βœ… Access control (`tx-sender` validation)
193
+ - βœ… Error handling (`try!`, `unwrap!`)
194
+ - βœ… Input validation (`asserts!`)
195
+ - βœ… Named error constants
196
+ - βœ… Kebab-case naming
197
+ - βœ… Network compatibility checks
126
198
 
127
- - "Create a SIP-010 token with burn mechanism"
128
- - "Build an NFT collection with royalties"
129
- - "Audit this Clarity contract for security issues"
130
- - "Show me how to integrate Alex swap"
131
- - "Deploy my contract to testnet"
132
- - "How do I implement stacking?"
199
+ ## 🌐 Networks
133
200
 
134
- ## πŸ› οΈ Development
201
+ - **Mainnet**: Production (SP... addresses)
202
+ - **Testnet**: Testing (ST... addresses, free STX)
203
+ - **Devnet**: Local development (Clarinet)
135
204
 
136
- ### Requirements
205
+ ## πŸ”§ Prerequisites
137
206
 
138
- - Node.js 18+
139
207
  - Python 3.x (for search functionality)
140
-
141
- ### Build from Source
208
+ - Node.js 18+ (for CLI installation)
142
209
 
143
210
  ```bash
144
- git clone https://github.com/kai-builder/stacksagent.git
145
- cd stacksagent/cli
146
- npm install
147
- npm run build
148
- npm link
211
+ python3 --version
212
+ node --version
149
213
  ```
150
214
 
151
215
  ## πŸ“– Documentation
152
216
 
153
- - [Main Repository](https://github.com/kai-builder/stacksagent)
154
217
  - [Stacks Docs](https://docs.stacks.co)
155
218
  - [Clarity Reference](https://docs.stacks.co/clarity)
156
219
  - [Hiro Platform](https://platform.hiro.so)
220
+ - [Explorer (Mainnet)](https://explorer.hiro.so)
221
+ - [Explorer (Testnet)](https://explorer.hiro.so/?chain=testnet)
222
+
223
+ ## πŸ”— DeFi Resources
224
+
225
+ - [Alex DEX](https://app.alexlab.co)
226
+ - [Velar DEX](https://app.velar.co)
227
+ - [Bitflow DEX](https://app.bitflow.finance)
228
+ - [Zest Protocol](https://www.zestprotocol.com)
157
229
 
158
230
  ## 🀝 Contributing
159
231
 
160
- Contributions welcome! Please see [Contributing Guide](https://github.com/kai-builder/stacksagent/blob/main/CONTRIBUTING.md).
232
+ Contributions are welcome! Please:
233
+
234
+ 1. Fork the repository
235
+ 2. Create a feature branch
236
+ 3. Make your changes
237
+ 4. Add knowledge entries to appropriate CSV files
238
+ 5. Test the search functionality
239
+ 6. Submit a pull request
161
240
 
162
241
  ## πŸ“„ License
163
242
 
164
- MIT License - see [LICENSE](LICENSE) for details.
243
+ MIT License - see [LICENSE](LICENSE) file for details
165
244
 
166
- ## πŸ™ Credits
245
+ ## πŸ™ Acknowledgments
167
246
 
168
247
  Built for the Stacks community with:
169
- - [Stacks.js](https://github.com/hirosystems/stacks.js)
248
+ - [Stacks.js](https://github.com/hirosystems/stacks.js) by Hiro Systems
170
249
  - [Stacks Blockchain](https://www.stacks.co)
171
250
  - [Clarity Language](https://docs.stacks.co/clarity)
172
251
 
173
252
  ## πŸ“ž Support
174
253
 
175
- - **GitHub Issues**: [Report bugs or request features](https://github.com/kai-builder/stacksagent/issues)
176
- - **Stacks Discord**: [Join the community](https://discord.gg/stacks)
254
+ - GitHub Issues: [Report bugs or request features](https://github.com/kai-builder/stacks-agent/issues)
255
+ - Stacks Discord: [Join the community](https://discord.gg/stacks)
256
+ - Twitter: [@kai_builder](https://twitter.com/kai_builder)
257
+
258
+ ## 🎯 Roadmap
259
+
260
+ - [x] Multi-platform AI skill support
261
+ - [x] 170+ knowledge base entries
262
+ - [x] BM25 search engine
263
+ - [x] CLI installer
264
+ - [ ] Web-based search interface
265
+ - [ ] VSCode extension
266
+ - [ ] Real-time contract analysis
267
+ - [ ] Community knowledge contributions
268
+ - [ ] Multi-language support
177
269
 
178
270
  ---
179
271
 
@@ -1,23 +1,15 @@
1
1
  id,language,category,name,description,code,imports_or_contract,notes
2
- 1,javascript,connect,authenticate-user,"Authenticate user with wallet","import { showConnect } from '@stacks/connect'; import { AppConfig, UserSession } from '@stacks/auth'; const appConfig = new AppConfig(['store_write', 'publish_data']); const userSession = new UserSession({ appConfig }); showConnect({ appDetails: { name: 'My App', icon: '/logo.png' }, redirectTo: '/', onFinish: () => window.location.reload(), userSession })","import { showConnect } from '@stacks/connect'; import { AppConfig, UserSession } from '@stacks/auth'","Opens wallet connection modal; redirects after auth"
3
- 2,javascript,connect,check-authentication,"Check if user is authenticated","import { UserSession, AppConfig } from '@stacks/auth'; const appConfig = new AppConfig(['store_write']); const userSession = new UserSession({ appConfig }); if (userSession.isUserSignedIn()) { const userData = userSession.loadUserData(); console.log('User:', userData.profile.stxAddress.mainnet) } else { console.log('Not authenticated') }","import { UserSession, AppConfig } from '@stacks/auth'","Check auth status before protected operations"
4
- 3,javascript,connect,sign-out-user,"Sign out user","import { UserSession, AppConfig } from '@stacks/auth'; const appConfig = new AppConfig(['store_write']); const userSession = new UserSession({ appConfig }); userSession.signUserOut(); window.location.href = '/'","import { UserSession, AppConfig } from '@stacks/auth'","Clears session and redirects to home"
5
- 4,javascript,profile,get-user-profile,"Get user profile data","import { UserSession } from '@stacks/auth'; const userSession = new UserSession(); const userData = userSession.loadUserData(); const profile = { stxAddress: userData.profile.stxAddress.mainnet, btcAddress: userData.profile.btcAddress, username: userData.username, name: userData.profile.name, image: userData.profile.image }","import { UserSession } from '@stacks/auth'","Returns wallet addresses and BNS username"
6
- 5,javascript,signing,sign-message,"Sign message with wallet","import { openSignatureRequestPopup } from '@stacks/connect'; const message = 'Verify ownership of this address'; openSignatureRequestPopup({ message, onFinish: (data) => { console.log('Signature:', data.signature); console.log('Public key:', data.publicKey) }})","import { openSignatureRequestPopup } from '@stacks/connect'","Proves wallet ownership without transaction"
7
- 6,javascript,signing,verify-signature,"Verify signed message","import { verifyMessageSignature } from '@stacks/encryption'; const isValid = verifyMessageSignature({ message: 'Verify ownership', signature: signatureData.signature, publicKey: publicKey }); console.log('Valid:', isValid)","import { verifyMessageSignature } from '@stacks/encryption'","Cryptographically verify message was signed by public key"
8
- 7,javascript,session,persist-session,"Persist auth session to localStorage","import { UserSession } from '@stacks/auth'; const userSession = new UserSession(); if (userSession.isUserSignedIn()) { const userData = userSession.loadUserData(); localStorage.setItem('stacks_session', JSON.stringify({ address: userData.profile.stxAddress.mainnet, username: userData.username, timestamp: Date.now() })) }","import { UserSession } from '@stacks/auth'","Cache session data for faster page loads"
9
- 8,javascript,session,restore-session,"Restore auth session from cache","const cachedSession = localStorage.getItem('stacks_session'); if (cachedSession) { const session = JSON.parse(cachedSession); const hourAgo = Date.now() - 3600000; if (session.timestamp > hourAgo) { console.log('Cached session:', session.address) } else { localStorage.removeItem('stacks_session') }}","None","Validate timestamp to prevent stale sessions"
10
- 9,javascript,permissions,request-permissions,"Request specific permissions","import { AppConfig, UserSession } from '@stacks/auth'; const appConfig = new AppConfig(['store_write', 'publish_data', 'email']); const userSession = new UserSession({ appConfig })","import { AppConfig, UserSession } from '@stacks/auth'","Permissions: store_write, publish_data, email"
11
- 10,javascript,encryption,encrypt-data,"Encrypt data with user key","import { encryptContent } from '@stacks/encryption'; import { UserSession } from '@stacks/auth'; const userSession = new UserSession(); const userData = userSession.loadUserData(); const encrypted = await encryptContent(JSON.stringify(sensitiveData), { publicKey: userData.profile.publicKey })","import { encryptContent } from '@stacks/encryption'; import { UserSession } from '@stacks/auth'","Only user with private key can decrypt"
12
- 11,javascript,encryption,decrypt-data,"Decrypt encrypted data","import { decryptContent } from '@stacks/encryption'; import { UserSession } from '@stacks/auth'; const userSession = new UserSession(); const userData = userSession.loadUserData(); const decrypted = await decryptContent(encryptedData, { privateKey: userData.appPrivateKey })","import { decryptContent } from '@stacks/encryption'; import { UserSession } from '@stacks/auth'","Requires user private key from session"
13
- 12,javascript,auth-hooks,use-auth-hook,"React hook for authentication","import { UserSession } from '@stacks/auth'; import { useState, useEffect } from 'react'; function useAuth() { const [user, setUser] = useState(null); useEffect(() => { const userSession = new UserSession(); if (userSession.isUserSignedIn()) { setUser(userSession.loadUserData()) }}, []); return { user, isAuthenticated: !!user, signOut: () => new UserSession().signUserOut() }}","import { UserSession } from '@stacks/auth'; import { useState, useEffect } from 'react'","Reusable auth state for React apps"
14
- 13,javascript,auth-guards,protected-route,"Protect routes with auth guard","function ProtectedRoute({ children }) { const { user } = useAuth(); const router = useRouter(); useEffect(() => { if (!user) { router.push('/login') }}, [user, router]); return user ? children : <div>Loading...</div> }","import { useAuth } from './hooks'; import { useRouter } from 'next/router'","Redirect to login if not authenticated"
15
- 14,javascript,middleware,auth-middleware,"API route auth middleware","export function withAuth(handler) { return async (req, res) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'Unauthorized' }); try { const verified = verifyJWT(token); req.user = verified; return handler(req, res) } catch { return res.status(401).json({ error: 'Invalid token' }) }}}","None","Protect API routes; verify JWT tokens"
16
- 15,javascript,jwt,create-auth-token,"Create JWT auth token","import jwt from 'jsonwebtoken'; const token = jwt.sign({ address: userData.profile.stxAddress.mainnet, username: userData.username, exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24)}, JWT_SECRET)","import jwt from 'jsonwebtoken'","Token expires in 24 hours; include user identity"
17
- 16,javascript,jwt,verify-auth-token,"Verify JWT auth token","import jwt from 'jsonwebtoken'; try { const decoded = jwt.verify(token, JWT_SECRET); console.log('User:', decoded.address); return decoded } catch (error) { console.error('Invalid token:', error); return null }","import jwt from 'jsonwebtoken'","Throws error if token invalid or expired"
18
- 17,javascript,session-tokens,exchange-session-token,"Exchange auth token for session","async function exchangeAuthToken(authToken) { const response = await fetch('/api/auth/session', { method: 'POST', headers: { 'Authorization': `Bearer ${authToken}` } }); const { sessionToken } = await response.json(); localStorage.setItem('session_token', sessionToken); return sessionToken }","fetch","Backend validates auth token and issues session token"
19
- 18,javascript,oauth,connect-with-oauth,"Authenticate with OAuth provider","import { showConnect } from '@stacks/connect'; showConnect({ authOrigin: 'https://app.blockstack.org', appDetails: { name: 'My App', icon: '/icon.png' }, onFinish: (payload) => { const { authResponse } = payload; fetch('/api/auth/verify', { method: 'POST', body: JSON.stringify({ token: authResponse }) }) }})","import { showConnect } from '@stacks/connect'","OAuth-like flow with Stacks auth"
20
- 19,javascript,social,connect-with-bns,"Resolve BNS to get social links","import { lookupProfile } from '@stacks/auth'; const profile = await lookupProfile('username.btc'); console.log(profile.name, profile.description); const socialLinks = profile.account?.filter(a => a.service === 'twitter' || a.service === 'github')","import { lookupProfile } from '@stacks/auth'","BNS profiles can include social media links"
21
- 20,javascript,access-control,verify-nft-ownership,"Gate content by NFT ownership","async function hasAccessNFT(userAddress, nftContract, nftId) { const response = await fetch(`https://api.hiro.so/extended/v1/tokens/nft/holdings?principal=${nftContract}&token_id=${nftId}`); const data = await response.json(); return data.results[0]?.owner === userAddress }","fetch","Token-gated access pattern"
22
- 21,javascript,access-control,verify-token-balance,"Gate content by token balance","async function hasMinBalance(userAddress, tokenContract, minAmount) { const response = await fetch(`https://api.hiro.so/extended/v1/address/${userAddress}/balances`); const balances = await response.json(); const balance = balances.fungible_tokens[tokenContract]?.balance || '0'; return parseInt(balance) >= minAmount }","fetch","Require minimum token holdings for access"
23
- 22,javascript,webhooks,verify-webhook-signature,"Verify webhook signature","import crypto from 'crypto'; function verifyWebhook(payload, signature, secret) { const hmac = crypto.createHmac('sha256', secret); hmac.update(JSON.stringify(payload)); const digest = hmac.digest('hex'); return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest)) }","import crypto from 'crypto'","Verify webhook came from trusted source"
2
+ 1,javascript,signing,verify-signature,"Verify signed message","import { verifyMessageSignature } from '@stacks/encryption'; const isValid = verifyMessageSignature({ message: 'Verify ownership', signature: signatureData.signature, publicKey: publicKey }); console.log('Valid:', isValid)","import { verifyMessageSignature } from '@stacks/encryption'","Cryptographically verify message was signed by public key"
3
+ 2,javascript,session,restore-session,"Restore auth session from cache","const cachedSession = localStorage.getItem('stacks_session'); if (cachedSession) { const session = JSON.parse(cachedSession); const hourAgo = Date.now() - 3600000; if (session.timestamp > hourAgo) { console.log('Cached session:', session.address) } else { localStorage.removeItem('stacks_session') }}","None","Validate timestamp to prevent stale sessions"
4
+ 3,javascript,encryption,encrypt-data,"Encrypt data with user key","import { encryptContent } from '@stacks/encryption'; const encrypted = await encryptContent(JSON.stringify(sensitiveData), { publicKey: userPublicKey })","import { encryptContent } from '@stacks/encryption'","Only user with private key can decrypt; use wallet publicKey"
5
+ 4,javascript,encryption,decrypt-data,"Decrypt encrypted data","import { decryptContent } from '@stacks/encryption'; const decrypted = await decryptContent(encryptedData, { privateKey: userPrivateKey })","import { decryptContent } from '@stacks/encryption'","Requires user private key; handle securely"
6
+ 5,javascript,auth-guards,protected-route,"Protect routes with auth guard","function ProtectedRoute({ children }) { const { isAuthenticated } = useWallet(); const router = useRouter(); useEffect(() => { if (!isAuthenticated) { router.push('/login') }}, [isAuthenticated, router]); return isAuthenticated ? children : <div>Loading...</div> }","import { useWallet } from './hooks'; import { useRouter } from 'next/router'","Redirect to login if not authenticated; uses useWallet hook from stacks-js-core"
7
+ 6,javascript,middleware,auth-middleware,"API route auth middleware","export function withAuth(handler) { return async (req, res) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'Unauthorized' }); try { const verified = verifyJWT(token); req.user = verified; return handler(req, res) } catch { return res.status(401).json({ error: 'Invalid token' }) }}}","None","Protect API routes; verify JWT tokens"
8
+ 7,javascript,jwt,create-auth-token,"Create JWT auth token","import jwt from 'jsonwebtoken'; const token = jwt.sign({ address: walletAddress, exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24)}, JWT_SECRET)","import jwt from 'jsonwebtoken'","Token expires in 24 hours; include wallet address"
9
+ 8,javascript,jwt,verify-auth-token,"Verify JWT auth token","import jwt from 'jsonwebtoken'; try { const decoded = jwt.verify(token, JWT_SECRET); console.log('User:', decoded.address); return decoded } catch (error) { console.error('Invalid token:', error); return null }","import jwt from 'jsonwebtoken'","Throws error if token invalid or expired"
10
+ 9,javascript,session-tokens,exchange-session-token,"Exchange auth token for session","async function exchangeAuthToken(authToken) { const response = await fetch('/api/auth/session', { method: 'POST', headers: { 'Authorization': `Bearer ${authToken}` } }); const { sessionToken } = await response.json(); localStorage.setItem('session_token', sessionToken); return sessionToken }","fetch","Backend validates auth token and issues session token"
11
+ 10,javascript,backend-auth,wallet-to-jwt,"Connect wallet then get JWT from backend","import { connect } from '@stacks/connect'; const userData = await connect(); const address = userData.addresses.stx[0].address; const response = await fetch('/api/auth/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ address }) }); const { token } = await response.json()","import { connect } from '@stacks/connect'","Connect wallet then verify with backend API to get JWT"
12
+ 11,javascript,bns,lookup-bns-profile,"Resolve BNS to get profile data","import { lookupProfile } from '@stacks/auth'; const profile = await lookupProfile('username.btc'); console.log(profile.name, profile.description); const socialLinks = profile.account?.filter(a => a.service === 'twitter' || a.service === 'github')","import { lookupProfile } from '@stacks/auth'","BNS profiles can include social media links"
13
+ 12,javascript,access-control,verify-nft-ownership,"Gate content by NFT ownership","async function hasAccessNFT(userAddress, nftContract, nftId) { const response = await fetch(`https://api.hiro.so/extended/v1/tokens/nft/holdings?principal=${nftContract}&token_id=${nftId}`); const data = await response.json(); return data.results[0]?.owner === userAddress }","fetch","Token-gated access pattern"
14
+ 13,javascript,access-control,verify-token-balance,"Gate content by token balance","async function hasMinBalance(userAddress, tokenContract, minAmount) { const response = await fetch(`https://api.hiro.so/extended/v1/address/${userAddress}/balances`); const balances = await response.json(); const balance = balances.fungible_tokens[tokenContract]?.balance || '0'; return parseInt(balance) >= minAmount }","fetch","Require minimum token holdings for access"
15
+ 14,javascript,webhooks,verify-webhook-signature,"Verify webhook signature","import crypto from 'crypto'; function verifyWebhook(payload, signature, secret) { const hmac = crypto.createHmac('sha256', secret); hmac.update(JSON.stringify(payload)); const digest = hmac.digest('hex'); return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest)) }","import crypto from 'crypto'","Verify webhook came from trusted source"
@@ -0,0 +1,31 @@
1
+ id,language,oracle,category,name,description,code,imports_or_contract,notes
2
+ 1,clarity,pyth,setup,mainnet-contract,"Pyth Oracle V4 mainnet contract","SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4","Mainnet: SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4","Storage: pyth-storage-v4; Decoder: pyth-pnau-decoder-v3; Wormhole: wormhole-core-v4"
3
+ 2,clarity,pyth,setup,testnet-contract,"Pyth Oracle V4 testnet contract","STR738QQX1PVTM6WTDF833Z18T8R0ZB791TCNEFM.pyth-oracle-v4","Testnet: STR738QQX1PVTM6WTDF833Z18T8R0ZB791TCNEFM.pyth-oracle-v4","Same trait contract names as mainnet (pyth-storage-v4 etc)"
4
+ 3,clarity,pyth,read,decode-price-feeds,"Decode VAA bytes to extract price info","(contract-call? .pyth-oracle-v4 decode-price-feeds vaa-buffer trait-tuple)","SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4","Returns price ema-price conf ema-conf expo publish-time; Read-only no fee"
5
+ 4,clarity,pyth,write,verify-and-update-price-feeds,"Verify VAA signatures and update on-chain prices","(contract-call? .pyth-oracle-v4 verify-and-update-price-feeds vaa-buffer trait-tuple)","SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4","Costs 1 STX fee; Updates stored price feeds; Verifies Wormhole signatures"
6
+ 5,clarity,pyth,read,read-price-feed,"Read stored on-chain price for a feed ID","(contract-call? .pyth-oracle-v4 read-price-feed feed-id-buffer storage-trait)","SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4","Returns stored price data; Must be updated first via verify-and-update"
7
+ 6,clarity,pyth,read,get-price,"Get current price value for a feed","(contract-call? .pyth-oracle-v4 get-price feed-id-buffer storage-trait)","SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4","Returns (ok {price ema-price conf ema-conf expo publish-time prev-publish-time})"
8
+ 7,clarity,pyth,trait,trait-tuple,"Build trait tuple for Pyth calls","(tuple (pyth-storage-contract (contract-principal .pyth-storage-v4)) (pyth-decoder-contract (contract-principal .pyth-pnau-decoder-v3)) (wormhole-core-contract (contract-principal .wormhole-core-v4)))","Trait implementor contracts","Required for all Pyth contract calls; Use same address as oracle contract"
9
+ 8,clarity,pyth,feed-id,btc-usd-feed,"BTC/USD price feed ID","0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43","Pyth mainnet feed ID","Use bufferCV(hexToBuff(feedId)) in contract calls"
10
+ 9,clarity,pyth,feed-id,stx-usd-feed,"STX/USD price feed ID","0xec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c17","Pyth mainnet feed ID","Primary feed for STX price oracle"
11
+ 10,clarity,pyth,feed-id,eth-usd-feed,"ETH/USD price feed ID","0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace","Pyth mainnet feed ID","Standard Ethereum price feed"
12
+ 11,clarity,pyth,feed-id,usdc-usd-feed,"USDC/USD price feed ID","0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a","Pyth mainnet feed ID","Stablecoin price feed"
13
+ 12,javascript,pyth,decode,decode-price-call,"Call decode-price-feeds from JS","const { request } = await import('@stacks/connect'); const vaa = bufferCV(hexToBuff(vaaHex)); const traits = tupleCV({...traitTuple}); await request('stx_callContract' {contract: `${addr}.${name}` functionName: 'decode-price-feeds' functionArgs: [vaa traits].map(cvToHex) network})","import { bufferCV tupleCV cvToHex } from '@stacks/transactions'; import { request } from '@stacks/connect'","Returns decoded price data; Read-only transaction"
14
+ 13,javascript,pyth,update,verify-update-call,"Call verify-and-update-price-feeds from JS","const { request } = await import('@stacks/connect'); const vaa = bufferCV(hexToBuff(vaaHex)); const traits = tupleCV({...traitTuple}); const pc = Pc.principal(userAddr).willSendLte(1000000).ustx(); await request('stx_callContract' {contract: `${addr}.${name}` functionName: 'verify-and-update-price-feeds' functionArgs: [vaa traits].map(cvToHex) postConditions: [pc] network})","import { bufferCV tupleCV cvToHex Pc } from '@stacks/transactions'; import { request } from '@stacks/connect'","Requires 1 STX fee; Add post-condition for safety"
15
+ 14,javascript,pyth,read,read-price-call,"Call read-price-feed from JS","const { request } = await import('@stacks/connect'); const feedId = bufferCV(hexToBuff(feedIdHex)); const storage = contractPrincipalCV(address 'pyth-storage-v4'); await request('stx_callContract' {contract: `${addr}.${name}` functionName: 'read-price-feed' functionArgs: [feedId storage].map(cvToHex) network})","import { bufferCV contractPrincipalCV cvToHex } from '@stacks/transactions'; import { request } from '@stacks/connect'","Read stored price; Must be updated first"
16
+ 15,javascript,pyth,read,get-price-call,"Call get-price from JS","const { request } = await import('@stacks/connect'); const feedId = bufferCV(hexToBuff(feedIdHex)); const storage = contractPrincipalCV(address 'pyth-storage-v4'); await request('stx_callContract' {contract: `${addr}.${name}` functionName: 'get-price' functionArgs: [feedId storage].map(cvToHex) network})","import { bufferCV contractPrincipalCV cvToHex } from '@stacks/transactions'; import { request } from '@stacks/connect'","Returns current price with all metadata"
17
+ 16,javascript,pyth,util,hex-to-buffer,"Convert hex string to buffer for Pyth calls","function hexToBuff(hexWith0x) { const h = hexWith0x.startsWith('0x') ? hexWith0x.slice(2) : hexWith0x; return Buffer.from(h 'hex') }","import { Buffer } from 'buffer'","Required to convert feed IDs and VAA hex to buffers"
18
+ 17,javascript,pyth,util,trait-tuple-builder,"Build trait tuple for contract calls","function traitTuple(network) { const addr = getContractAddress(network); return tupleCV({ 'pyth-storage-contract': contractPrincipalCV(addr 'pyth-storage-v4') 'pyth-decoder-contract': contractPrincipalCV(addr 'pyth-pnau-decoder-v3') 'wormhole-core-contract': contractPrincipalCV(addr 'wormhole-core-v4') }) }","import { tupleCV contractPrincipalCV } from '@stacks/transactions'","Use same address as oracle contract; Required for decode and verify calls"
19
+ 18,api,pyth,hermes,fetch-latest-vaa,"Fetch latest VAA from Hermes API","const feedId = '0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43'; const url = `https://hermes.pyth.network/api/latest_vaas?ids[]=${encodeURIComponent(feedId)}`; const res = await fetch(url); const vaas = await res.json(); const vaaHex = base64ToHex(vaas[0])","fetch or axios","Returns base64-encoded VAA; Convert to hex before using in contract calls"
20
+ 19,api,pyth,hermes,hermes-client-sdk,"Use Hermes client SDK to fetch VAA","import { HermesClient } from '@pythnetwork/hermes-client'; const hermes = new HermesClient('https://hermes.pyth.network'); const vaas = await hermes.getLatestVaas({ids: [feedId]}); const vaaHex = base64ToHex(vaas[0])","npm install @pythnetwork/hermes-client","SDK handles VAA fetching; Still need to convert base64 to hex"
21
+ 20,javascript,pyth,util,base64-to-hex,"Convert base64 VAA to hex","function base64ToHex(b64) { const binStr = atob(b64); let hex = '0x'; for (let i = 0; i < binStr.length; i++) { hex += (binStr.charCodeAt(i) & 0xff).toString(16).padStart(2 '0') } return hex }","Native JavaScript","Required to convert Hermes VAA response to hex for contract calls"
22
+ 21,javascript,pyth,parse,parse-price-result,"Parse price data from contract result","function parsePriceFromResult(repr) { const tupleMatch = repr.match(/\(ok \((?:tuple|list.*tuple) \(([\s\S]*?)\)\)\)/); if (!tupleMatch) return null; const content = tupleMatch[1]; return { price: parseInt(content.match(/price (\d+|u\d+)/)?.[1] || 0) emaPrice: parseInt(content.match(/ema-price (\d+)/)?.[1] || 0) conf: parseInt(content.match(/conf u(\d+)/)?.[1] || 0) emaConf: parseInt(content.match(/ema-conf u(\d+)/)?.[1] || 0) expo: parseInt(content.match(/expo (-?\d+)/)?.[1] || 0) publishTime: parseInt(content.match(/publish-time u(\d+)/)?.[1] || 0) } }","None","Parse transaction result repr string to extract price data"
23
+ 22,javascript,pyth,calc,calculate-actual-price,"Calculate actual price from price data","function calculateActualPrice(priceData) { return priceData.emaPrice / Math.pow(10 Math.abs(priceData.expo)) }","None","Formula: ema-price / 10^|expo|; Expo is typically -8 so divide by 10^8"
24
+ 23,javascript,pyth,parse,parse-update-event,"Parse price from verify-and-update event","function parsePriceUpdateEvent(events) { for (const event of events) { if (event.event_type === 'smart_contract_log' && event.contract_log) { const repr = event.contract_log.value.repr; if (repr.includes('action \"updated\"') && repr.includes('type \"price-feed\"')) { const dataMatch = repr.match(/data \(tuple ([\s\S]*?)\)\)/); if (dataMatch) { return parsePriceFromResult(`(ok (tuple ${dataMatch[1]}))`); } } } } return null }","None","Extract price data from contract log events after verify-and-update"
25
+ 24,api,pyth,hiro,fetch-transaction-result,"Fetch and parse transaction from Hiro API","async function getTxResult(txId network) { const baseUrl = network === 'mainnet' ? 'https://api.mainnet.hiro.so' : 'https://api.testnet.hiro.so'; const res = await fetch(`${baseUrl}/extended/v1/tx/${txId}`); const tx = await res.json(); if (tx.tx_status === 'success' && tx.tx_result?.repr) { return parsePriceFromResult(tx.tx_result.repr) } return null }","fetch or axios","Wait ~10s after tx submit for indexing; Parse result to get price data"
26
+ 25,clarity,pyth,example,full-oracle-integration,"Complete price oracle integration example","(define-read-only (get-btc-price) (let ((feed-id 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43)) (match (contract-call? 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-oracle-v4 get-price feed-id 'SP1CGXWEAMG6P6FT04W66NVGJ7PQWMDAC19R7PJ0Y.pyth-storage-v4) success (ok (get ema-price success)) error (err u404))))","Pyth oracle contract","Read-only function to get BTC price; Returns ema-price or error"
27
+ 26,javascript,pyth,example,full-update-flow,"Complete flow to update price on-chain","// 1. Fetch VAA from Hermes; const hermes = new HermesClient('https://hermes.pyth.network'); const vaas = await hermes.getLatestVaas({ids: [feedId]}); const vaaHex = base64ToHex(vaas[0]); // 2. Update price on-chain; const { request } = await import('@stacks/connect'); const tx = await request('stx_callContract' {...}); // 3. Wait and fetch result; setTimeout(async () => { const result = await getTxResult(tx.txid 'mainnet'); const price = calculateActualPrice(result) } 10000)","Multiple imports","Complete workflow: fetch VAA update on-chain parse result calculate price"
28
+ 27,javascript,pyth,feeds,price-feed-constants,"Common Pyth price feed IDs","const PRICE_FEEDS = { BTC_USD: '0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43' STX_USD: '0xec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c17' ETH_USD: '0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace' USDC_USD: '0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a' BNB_USD: '0x2f95862b045670cd22bee3114c39763a4a08beeb663b145d283c31d7d1101c4f' LINK_USD: '0x8ac0c70fff57e9aefdf5edf44b51d62c2d433653cbb2cf5cc06bb115af04d221' }","None","Mainnet feed IDs; Find more at pyth.network/price-feeds"
29
+ 28,javascript,pyth,wallet,connect-for-oracle,"Connect wallet for oracle transactions","import { connect isConnected getLocalStorage } from '@stacks/connect'; const appDetails = { name: 'My Pyth App' icon: window.location.origin + '/icon.png' }; const response = await connect(); const stxAddress = response.addresses.stx[0].address","npm install @stacks/connect","Required for verify-and-update transactions (read-only calls work without wallet)"
30
+ 29,api,pyth,hermes,hermes-endpoints,"Pyth Hermes API endpoints","// Latest VAA: https://hermes.pyth.network/api/latest_vaas?ids[]={feedId}; // Latest price updates: https://hermes.pyth.network/api/latest_price_feeds?ids[]={feedId}; // All feed IDs: https://hermes.pyth.network/api/price_feed_ids","Hermes REST API","Free API; No authentication required; Mainnet production endpoint"
31
+ 30,clarity,pyth,price-calc,price-calculation,"Price calculation in Clarity","(define-read-only (calculate-price (price-data (tuple (ema-price int) (expo int)))) (let ((ema (get ema-price price-data)) (exp (get expo price-data))) (if (< exp 0) (/ ema (pow u10 (to-uint (- 0 exp)))) (* ema (pow u10 (to-uint exp))))))","None","Convert raw price with exponent; Handle negative exponents (most common)"
@@ -0,0 +1,51 @@
1
+ id,language,category,name,description,code,imports_or_contract,notes
2
+ 1,javascript,wallet-sdk,restore-wallet-from-seed,"Restore wallet from seed phrase","import { generateWallet, generateNewAccount, restoreWalletAccounts, getStxAddress } from '@stacks/wallet-sdk'; import { StacksMainnet, TransactionVersion } from '@stacks/network'; const seedPhrase = process.env.SEED_PHASES; const password = process.env.PASSWORD; let restoredWallet = await generateWallet({ secretKey: seedPhrase, password: password ?? '' }); restoredWallet = generateNewAccount(restoredWallet); const wallet = await restoreWalletAccounts({ wallet: restoredWallet, gaiaHubUrl: 'https://hub.blockstack.org', network: new StacksMainnet() }); const mainnetAddr = getStxAddress({ account: restoredWallet.accounts[0], transactionVersion: TransactionVersion.Mainnet }); const account = wallet.accounts[0]","import { generateWallet, generateNewAccount, restoreWalletAccounts, getStxAddress } from '@stacks/wallet-sdk'; import { StacksMainnet, TransactionVersion } from '@stacks/network'","Use wallet-sdk for automated trading; stores seed in env; generates mainnet address; account.stxPrivateKey for signing"
3
+ 2,javascript,wallet-sdk,get-wallet-addresses,"Get testnet and mainnet addresses","import { getStxAddress } from '@stacks/wallet-sdk'; import { TransactionVersion } from '@stacks/transactions'; const testnetAddr = getStxAddress({ account: wallet.accounts[0], transactionVersion: TransactionVersion.Testnet }); const mainnetAddr = getStxAddress({ account: wallet.accounts[0], transactionVersion: TransactionVersion.Mainnet })","import { getStxAddress } from '@stacks/wallet-sdk'; import { TransactionVersion } from '@stacks/transactions'","Get addresses for both networks; use mainnetAddr for production; testnetAddr for testing"
4
+ 3,javascript,balance,fetch-token-balances,"Fetch all token balances for address","const response = await fetch(`https://api.hiro.so/extended/v1/address/${address}/balances`, { headers: getRandomHeader() }); const data = await response.json(); const tokenIds = Object.keys(data.fungible_tokens); tokenIds.forEach(tokenId => { const balance = Number(data.fungible_tokens[tokenId].balance); console.log(`${tokenId}: ${balance}`) })","fetch","Get all FT holdings; iterate through fungible_tokens; balance is in smallest unit"
5
+ 4,javascript,balance,filter-bonding-tokens,"Filter bonding curve tokens from holdings","const tokenHoldings = []; const tokenIds = Object.keys(data.fungible_tokens); for(const tokenId of tokenIds) { const balance = Number(data.fungible_tokens[tokenId].balance); if (balance > (100 * Math.pow(10, 6)) && (tokenId.toLowerCase().includes('bonding-curve') || tokenId.toLowerCase().includes('stxcity'))) { tokenHoldings.push({ tokenId, balance }) } }","None","Filter by min balance (100 STX worth); filter by contract name pattern; useful for bot automation"
6
+ 5,javascript,balance,calculate-sell-amount,"Calculate percentage of holdings to sell","const holding = 1000000; const sellPercent = 50; const amountToSell = Math.floor(holding * Number(sellPercent)/100)","None","Floor division for integer amounts; sellPercent from API request; prevents dust"
7
+ 6,javascript,txOptions,basic-contract-call,"Build basic txOptions for contract call","import { makeContractCall, principalCV, uintCV, AnchorMode, PostConditionMode } from '@stacks/transactions'; const txOptions = { contractAddress: 'SP2C2YFP...', contractName: 'dex-contract', functionName: 'swap', functionArgs: [ principalCV('SP...token-contract'), uintCV(1000000) ], senderKey: account.stxPrivateKey, validateWithAbi: true, network: new StacksMainnet(), postConditions: [], anchorMode: AnchorMode.OnChainOnly, postConditionMode: PostConditionMode.Deny }","import { makeContractCall, principalCV, uintCV, AnchorMode, PostConditionMode } from '@stacks/transactions'; import { StacksMainnet } from '@stacks/network'","Complete txOptions structure; senderKey from wallet-sdk; validateWithAbi catches errors early"
8
+ 7,javascript,txOptions,set-transaction-fee,"Set custom transaction fee","const txOptions = { ...baseOptions }; const txFee = 0.1; if (txFee !== null && txFee !== undefined) { txOptions.fee = Number(txFee) * Math.pow(10, 6) }","None","Fee in microSTX; 0.1 STX = 100,000 microSTX; optional parameter"
9
+ 8,javascript,txOptions,random-fee-range,"Generate random fee for privacy","const randomFee = (Math.random() * (0.3 - 0.08) + 0.08).toFixed(2); txOptions.fee = Number(randomFee) * Math.pow(10, 6)","None","Random fee between 0.08-0.3 STX; prevents MEV; harder to identify bot transactions"
10
+ 9,javascript,post-conditions,user-sends-ft,"Post-condition: user sends exact FT amount","import { Pc } from '@stacks/transactions'; const contractNameForPC = `${deployer}.${name}` as `${string}.${string}`; const sendFTCondition = Pc.principal(userAddress) .willSendEq(BigInt(amountToSell)).ft(contractNameForPC, symbol); postConditions.push(sendFTCondition)","import { Pc } from '@stacks/transactions'","willSendEq for exact amounts; ft() requires contract.name format; symbol is token identifier"
11
+ 10,javascript,post-conditions,dex-sends-stx,"Post-condition: DEX sends minimum STX","import { Pc } from '@stacks/transactions'; const sendSTXPostCondition = Pc.principal(dexContractId) .willSendGte(100).ustx(); postConditions.push(sendSTXPostCondition)","import { Pc } from '@stacks/transactions'","willSendGte for minimum guarantee; 100 microSTX minimum; protects against zero-return exploits"
12
+ 11,javascript,post-conditions,user-sends-stx,"Post-condition: user sends exact STX amount","import { Pc } from '@stacks/transactions'; const userSendSTXPC = Pc.principal(userAddress) .willSendEq(BigInt(stxAmount)).ustx(); postConditions.push(userSendSTXPC)","import { Pc } from '@stacks/transactions'","willSendEq prevents overspending; amount in microSTX; critical for buy operations"
13
+ 12,javascript,post-conditions,amm-sends-ft,"Post-condition: AMM vault sends FT","import { Pc } from '@stacks/transactions'; const contractNameForPC = `${deployer}.${name}` as `${string}.${string}`; const ammSendsFTPC = Pc.principal('SP...amm-vault-v2-01') .willSendGte(BigInt(1)).ft(contractNameForPC, symbol); postConditions.push(ammSendsFTPC)","import { Pc } from '@stacks/transactions'","AMM vault holds tokens; willSendGte(1) ensures we receive something; critical for swaps"
14
+ 13,javascript,broadcast,broadcast-transaction,"Broadcast transaction to network","import { makeContractCall, broadcastTransaction } from '@stacks/transactions'; const transaction = await makeContractCall(txOptions); const broadcastResponse = await broadcastTransaction(transaction, network); const txId = broadcastResponse.txid; console.log('Transaction ID:', txId)","import { makeContractCall, broadcastTransaction } from '@stacks/transactions'","makeContractCall builds tx; broadcastTransaction sends to network; txid for tracking"
15
+ 14,javascript,broadcast,batch-broadcast,"Broadcast multiple transactions in parallel","const walletPromises = seedPhrasesList.map(async (seedPhrase) => { const wallet = await restoreWallet(seedPhrase); const transaction = await makeContractCall(txOptions); const response = await broadcastTransaction(transaction, network); return response.txid }); const txIds = await Promise.all(walletPromises)","None","Promise.all for parallel execution; multiple wallets for volume; collect all txIds"
16
+ 15,javascript,dex,bonding-curve-sell,"Sell tokens on bonding curve DEX","const txOptions = { contractAddress: dexDeployer, contractName: dexContractName, functionName: 'sell', functionArgs: [ principalCV(tokenContractId), uintCV(amountToSell) ], senderKey: account.stxPrivateKey, validateWithAbi: true, network, postConditions: [sendSTXPostCondition, sendFTCondition], anchorMode: AnchorMode.OnChainOnly, postConditionMode: PostConditionMode.Deny }","import { principalCV, uintCV, AnchorMode, PostConditionMode } from '@stacks/transactions'","Bonding curve sell function; args: token-contract, amount; Deny mode for safety"
17
+ 16,javascript,dex,alex-swap-helper,"Swap on Alex DEX using swap-helper","const txOptions = { contractAddress: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM', contractName: 'amm-pool-v2-01', functionName: 'swap-helper', functionArgs: [ principalCV('SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-wstx-v2'), principalCV('SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC.token-wsbtc'), uintCV(100000000), uintCV(1000000), someCV(uintCV(1)) ], senderKey: account.stxPrivateKey, network, postConditions, anchorMode: AnchorMode.OnChainOnly, postConditionMode: PostConditionMode.Allow }","import { principalCV, uintCV, someCV, AnchorMode, PostConditionMode } from '@stacks/transactions'","Alex swap-helper pattern; args: token-x, token-y, factor, dx, min-dy; someCV for optional values"
18
+ 17,javascript,dex,bitflow-swap,"Swap on Bitflow DEX","const txOptions = { contractAddress: 'SM1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR', contractName: 'xyk-core-v-1-2', functionName: 'swap-y-for-x', functionArgs: [ principalCV('SM1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.xyk-pool-sbtc-stx-v-1-1'), principalCV('SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token'), principalCV('SP1Y5YSTAHZ88XYK1VPDH24GY0HPX5J4JECTMY4A1.wstx'), uintCV(1000000), uintCV(950000) ], senderKey: account.stxPrivateKey, network, postConditions, anchorMode: AnchorMode.OnChainOnly }","import { principalCV, uintCV, AnchorMode } from '@stacks/transactions'","Bitflow XYK swap; args: pool-contract, token-y, token-x, amount-y, min-x-out"
19
+ 18,javascript,quotes,fetch-bitflow-quote,"Get swap quote from Bitflow API","async function getBitflowQuote(amount: number): Promise<number> { const timestamp = Date.now(); const response = await fetch(`https://app.bitflow.finance/api/sdk/quote-for-route?tokenXId=token-stx&tokenYId=token-sbtc&amount=${amount}&timestamp=${timestamp}`); const data = await response.json(); return data.bestRoute.quote }","fetch","Fetch real-time quote; timestamp for cache busting; returns expected output amount"
20
+ 19,javascript,quotes,calculate-slippage,"Calculate min receive with slippage","const quote = await getBitflowQuote(1); const minReceiveAfterSlippage = quote * 0.9; const minReceiveInDecimals = Math.floor(minReceiveAfterSlippage * Math.pow(10, 8))","None","10% slippage = 0.9 multiplier; floor to prevent decimal issues; convert to token decimals"
21
+ 20,javascript,validation,validate-bonding-token,"Validate token is active bonding curve","const tokenContract = tokenId.split('::')[0]; const response = await fetch(`https://stx.city/api/searchTokens?token_contract=${tokenContract}`); const data = await response.json(); if (!data.bonding_curve || data.bonding_curve.length === 0) { console.log('Not a bonding curve token'); return false } const tokenInfo = data.bonding_curve[0]; if (tokenInfo.status !== 'active') { console.log('Token not active'); return false }","fetch","Check if token exists in bonding curve registry; validate active status; prevents trading dead tokens"
22
+ 21,javascript,validation,split-contract-id,"Parse contract identifier","const dexContractId = 'SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.dex-v1'; const dexDeployer = dexContractId.split('.')[0]; const dexContractName = dexContractId.split('.')[1]","None","Split on dot separator; [0] is deployer address; [1] is contract name"
23
+ 22,javascript,validation,extract-token-parts,"Extract token contract and symbol","const tokenId = 'SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.token-bonding-v1::TOKEN'; const tokenContract = tokenId.split('::')[0]; const symbol = tokenId.split('::')[1]","None","Split on :: separator; [0] is full contract id; [1] is token symbol"
24
+ 23,javascript,validation,type-safe-contract-id,"TypeScript type-safe contract ID","function validateAndUseString(inputString: string): `${string}.${string}` { const parts = inputString.split('.'); return inputString as `${string}.${string}` }","None","Type assertion for Clarity contract format; prevents type errors with Pc.ft()"
25
+ 24,javascript,auth,bearer-token-auth,"Protect API endpoint with bearer token","export async function POST(request: Request) { const authHeader = request.headers.get('authorization'); if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) { return new NextResponse('Unauthorized', { status: 401 }) } // Process request... }","import { NextResponse } from 'next/server'","Verify bearer token; use env vars for secrets; return 401 if invalid"
26
+ 25,javascript,auth,production-auth-guard,"Only require auth in production","import { isProd } from '@/lib/dev'; if (isProd()) { const authHeader = request.headers.get('authorization'); if (authHeader !== `Bearer ${process.env.API_KEY}`) { return new NextResponse('Unauthorized', { status: 401 }) } }","None","Skip auth in development; isProd() checks NODE_ENV; easier local testing"
27
+ 26,javascript,error,handle-tx-errors,"Handle transaction errors gracefully","try { const transaction = await makeContractCall(txOptions); const broadcastResponse = await broadcastTransaction(transaction, network); return NextResponse.json({ txId: broadcastResponse.txid }, { status: 201 }) } catch (error) { console.error('Error making transaction:', error); return NextResponse.json({ error: `Transaction failed: ${error}` }, { status: 500 }) }","import { NextResponse } from 'next/server'","Try-catch around tx creation; log full error; return 500 with message"
28
+ 27,javascript,error,skip-failed-tokens,"Continue processing on individual failures","for(const holding of tokenHoldings) { try { const tx = await makeSellTransaction(holding); txIds.push(tx) } catch (error) { console.error(`Failed to sell ${holding.tokenId}:`, error); continue } }","None","Don't stop batch processing; log which token failed; collect successful txIds"
29
+ 28,javascript,patterns,dynamic-route-config,"Force dynamic route in Next.js","export const dynamic = 'force-dynamic'","None","Disable static generation; required for API routes with env vars; prevents build caching"
30
+ 29,javascript,patterns,random-api-key,"Use random API key from pool","function getRandomHeader() { const apiKeys = ['key1', 'key2', 'key3']; const randomApiKey = apiKeys[Math.floor(Math.random() * apiKeys.length)]; return { 'x-hiro-api-key': randomApiKey } }","None","Rotate API keys; avoid rate limits; distribute load across keys"
31
+ 30,javascript,patterns,someCV-optional,"Use someCV for optional Clarity arguments","import { someCV, noneCV, uintCV } from '@stacks/transactions'; const minReceive = hasMinimum ? someCV(uintCV(minAmount)) : noneCV()","import { someCV, noneCV, uintCV } from '@stacks/transactions'","someCV wraps optional values; noneCV for none; matches Clarity (optional uint)"
32
+ 31,javascript,wallet-sdk,multi-wallet-setup,"Set up multiple wallets from seed list","const seedPhrasesList = [seedPhrase1, seedPhrase2, seedPhrase3]; const walletPromises = seedPhrasesList.map(async (currentSeedPhrase) => { let restoredWallet = await generateWallet({ secretKey: currentSeedPhrase, password: password ?? '' }); restoredWallet = generateNewAccount(restoredWallet); return await restoreWalletAccounts({ wallet: restoredWallet, gaiaHubUrl: 'https://hub.blockstack.org', network: new StacksMainnet() }) }); const wallets = await Promise.all(walletPromises)","import { generateWallet, generateNewAccount, restoreWalletAccounts } from '@stacks/wallet-sdk'","Parallel wallet restoration; Promise.all for speed; useful for multi-wallet bots"
33
+ 32,javascript,txOptions,anchor-mode-options,"Set anchor mode for transaction","import { AnchorMode } from '@stacks/transactions'; const txOptions = { ...baseOptions, anchorMode: AnchorMode.OnChainOnly }","import { AnchorMode } from '@stacks/transactions'","OnChainOnly for finality; Any for speed; OffChainOnly for microblocks"
34
+ 33,javascript,txOptions,post-condition-mode,"Set post-condition mode","import { PostConditionMode } from '@stacks/transactions'; const txOptions = { ...baseOptions, postConditionMode: PostConditionMode.Deny }","import { PostConditionMode } from '@stacks/transactions'","Deny rejects unexpected transfers; Allow permits any transfer; Deny recommended for safety"
35
+ 34,javascript,complete,complete-sell-bot,"Complete automated sell bot","import { generateWallet, generateNewAccount, restoreWalletAccounts, getStxAddress } from '@stacks/wallet-sdk'; import { makeContractCall, broadcastTransaction, principalCV, uintCV, Pc, AnchorMode, PostConditionMode, TransactionVersion } from '@stacks/transactions'; import { StacksMainnet } from '@stacks/network'; const wallet = await setupWallet(seedPhrase, password); const account = wallet.accounts[0]; const mainnetAddr = getStxAddress({ account: wallet.accounts[0], transactionVersion: TransactionVersion.Mainnet }); const balance = await fetchTokenBalance(mainnetAddr, tokenContract); const txOptions = { contractAddress: dexDeployer, contractName: dexContractName, functionName: 'sell', functionArgs: [principalCV(tokenContract), uintCV(balance)], senderKey: account.stxPrivateKey, network: new StacksMainnet(), postConditions: [Pc.principal(dexContract).willSendGte(100).ustx(), Pc.principal(mainnetAddr).willSendEq(BigInt(balance)).ft(tokenContract, symbol)], anchorMode: AnchorMode.OnChainOnly, postConditionMode: PostConditionMode.Deny }; const tx = await makeContractCall(txOptions); const result = await broadcastTransaction(tx, new StacksMainnet()); return result.txid","import { generateWallet, generateNewAccount, restoreWalletAccounts, getStxAddress } from '@stacks/wallet-sdk'; import { makeContractCall, broadcastTransaction, principalCV, uintCV, Pc, AnchorMode, PostConditionMode, TransactionVersion } from '@stacks/transactions'; import { StacksMainnet } from '@stacks/network'","Full sell bot pattern; restore wallet, check balance, build tx, broadcast; all safety checks included"
36
+ 35,javascript,complete,complete-buy-bot,"Complete automated buy bot","import { generateWallet, generateNewAccount, restoreWalletAccounts } from '@stacks/wallet-sdk'; import { makeContractCall, broadcastTransaction, principalCV, uintCV, someCV, Pc, AnchorMode, PostConditionMode } from '@stacks/transactions'; import { StacksMainnet } from '@stacks/network'; const wallet = await setupWallet(seedPhrase, password); const account = wallet.accounts[0]; const quote = await getDexQuote(stxAmount); const minReceive = Math.floor(quote * 0.9 * Math.pow(10, tokenDecimals)); const txOptions = { contractAddress: 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM', contractName: 'amm-pool-v2-01', functionName: 'swap-helper', functionArgs: [principalCV(tokenX), principalCV(tokenY), uintCV(factor), uintCV(stxAmount), someCV(uintCV(minReceive))], senderKey: account.stxPrivateKey, network: new StacksMainnet(), postConditions: [Pc.principal(userAddr).willSendEq(BigInt(stxAmount)).ustx(), Pc.principal(ammVault).willSendGte(BigInt(minReceive)).ft(tokenContract, symbol)], anchorMode: AnchorMode.OnChainOnly, postConditionMode: PostConditionMode.Allow }; const tx = await makeContractCall(txOptions); return await broadcastTransaction(tx, new StacksMainnet())","import { generateWallet, generateNewAccount, restoreWalletAccounts } from '@stacks/wallet-sdk'; import { makeContractCall, broadcastTransaction, principalCV, uintCV, someCV, Pc, AnchorMode, PostConditionMode } from '@stacks/transactions'; import { StacksMainnet } from '@stacks/network'","Full buy bot pattern; get quote, calculate slippage, build tx with post-conditions, broadcast"
37
+ 36,javascript,privy,init-privy-client,"Initialize Privy server client","import { PrivyClient } from '@privy-io/server-auth'; const privyAppId = process.env.NEXT_PUBLIC_PRIVY_APP_ID; const privyAppSecret = process.env.PRIVY_APP_SECRET; const authPrivateKey = process.env.QUORUMS_PRIVATE_KEY; const privy = new PrivyClient(privyAppId, privyAppSecret, { walletApi: { authorizationPrivateKey: authPrivateKey } })","import { PrivyClient } from '@privy-io/server-auth'","Singleton pattern recommended; walletApi requires authorization private key; used for server-side wallet operations"
38
+ 37,javascript,privy,verify-privy-token,"Verify Privy auth token","import { getPrivyServerClient } from '@/utils/privy-server-client'; const privy = getPrivyServerClient(); const accessToken = authHeader.replace('Bearer ', ''); try { const verifiedClaims = await privy.verifyAuthToken(accessToken); console.log('User ID:', verifiedClaims.userId) } catch { throw new Error('Invalid or expired token') }","import { getPrivyServerClient } from '@/utils/privy-server-client'","Verify JWT from frontend; extract userId from claims; throws on invalid/expired tokens"
39
+ 38,javascript,privy,create-privy-wallet,"Create Privy embedded wallet","const privy = getPrivyServerClient(); const result = await privy.walletApi.createWallet({ chainType: 'bitcoin-segwit', owner: { userId }, additionalSigners: [{ signerId: process.env.NEXT_PUBLIC_QUORUMS_ID }] }); await privy.walletApi.updateWallet({ id: result.id, additionalSigners: [] })","import { getPrivyServerClient } from '@/utils/privy-server-client'","bitcoin-segwit supports Stacks; additionalSigners for quorum; remove signers after creation for full control"
40
+ 39,javascript,privy,get-privy-wallet,"Get Privy wallet details","const privy = getPrivyServerClient(); const wallet = await privy.walletApi.getWallet({ id: walletId }); const publicKey = wallet.publicKey; const address = publicKeyToAddress(publicKey, 'mainnet')","import { getPrivyServerClient } from '@/utils/privy-server-client'; import { publicKeyToAddress } from '@stacks/transactions'","Fetch wallet by ID; publicKey for unsigned txs; derive Stacks address from publicKey"
41
+ 40,javascript,privy,sign-raw-hash,"Sign hash with Privy wallet","import { generateAuthorizationSignature } from '@privy-io/server-auth/wallet-api'; import { v4 as uuidv4 } from 'uuid'; const idempotencyKey = uuidv4(); const input = { headers: { 'privy-app-id': process.env.NEXT_PUBLIC_PRIVY_APP_ID, 'privy-idempotency-key': idempotencyKey }, method: 'POST', url: `https://api.privy.io/v1/wallets/${walletId}/raw_sign`, version: 1, body: { params: { hash } } }; const signature = generateAuthorizationSignature({ input, authorizationPrivateKey: process.env.QUORUMS_PRIVATE_KEY }); const res = await fetch(`https://api.privy.io/v1/wallets/${walletId}/raw_sign`, { method: 'POST', headers: { ...input.headers, 'Content-Type': 'application/json', 'privy-authorization-signature': signature, Authorization: generateBasicAuthHeader(appId, appSecret) }, body: JSON.stringify(input.body) }); const signData = await res.json(); return signData.data.signature","import { generateAuthorizationSignature } from '@privy-io/server-auth/wallet-api'; import { v4 as uuidv4 } from 'uuid'","Sign pre-hashed data; idempotency key prevents duplicate signs; returns raw signature (r+s)"
42
+ 41,javascript,privy,make-unsigned-contract-call,"Build unsigned contract call with Privy","import { makeUnsignedContractCall, Cl, Pc, PostConditionMode } from '@stacks/transactions'; const txOptions = { publicKey, contractAddress, contractName, functionName, functionArgs: [Cl.address(tokenContract), Cl.uint(amount)], fee: 5000, postConditionMode: PostConditionMode.Deny, postConditions: [Pc.principal(userAddr).willSendEq(amount).ustx()], network: 'mainnet' }; const transaction = await makeUnsignedContractCall(txOptions)","import { makeUnsignedContractCall, Cl, Pc, PostConditionMode } from '@stacks/transactions'","Use publicKey instead of senderKey; transaction unsigned; ready for Privy signing"
43
+ 42,javascript,privy,create-sighash,"Create signature hash for Privy","import { TransactionSigner, sigHashPreSign } from '@stacks/transactions'; const transaction = await makeUnsignedContractCall(txOptions); const signer = new TransactionSigner(transaction); const preSignSigHash = sigHashPreSign(signer.sigHash, transaction.auth.authType, transaction.auth.spendingCondition.fee, transaction.auth.spendingCondition.nonce); const payload = `0x${preSignSigHash}`","import { TransactionSigner, sigHashPreSign } from '@stacks/transactions'","Generate hash before signing; includes auth type, fee, nonce; 0x prefix for Privy"
44
+ 43,javascript,privy,format-signature,"Format Privy signature for Stacks","function formatStacksSignature(signature) { const hexSig = signature.startsWith('0x') ? signature.slice(2) : signature; const r = hexSig.slice(0, 64).padStart(64, '0'); const s = hexSig.slice(64, 128).padStart(64, '0'); return { r, s } }","None","Extract r and s from Privy signature; pad to 64 chars; ready for recovery ID testing"
45
+ 44,javascript,privy,test-recovery-ids,"Test all recovery IDs for signature","import { createMessageSignature } from '@stacks/transactions'; const recoveryIds = ['00', '01', '02', '03']; for (const recoveryId of recoveryIds) { const testTx = await makeUnsignedContractCall(txOptions); testTx.auth.spendingCondition.signature = createMessageSignature(`${recoveryId}${r}${s}`); try { const response = await broadcastTransaction({ transaction: testTx, network: 'mainnet' }); if (!response.error) { console.log(`Success with recovery ID ${recoveryId}`); return { success: true, txid: response.txid, recoveryId } } } catch (err) { continue } }","import { createMessageSignature, broadcastTransaction } from '@stacks/transactions'","Try recovery IDs in order [01,00,02,03]; broadcast each variant; return first successful"
46
+ 45,javascript,privy,broadcast-with-recovery,"Broadcast with automatic recovery ID testing","async function broadcastWithRecoveryTesting(signature, createTransactionFn, broadcastFn) { const hexSig = signature.startsWith('0x') ? signature.slice(2) : signature; const r = hexSig.slice(0, 64).padStart(64, '0'); const s = hexSig.slice(64, 128).padStart(64, '0'); const recoveryIds = ['01', '00', '02', '03']; for (const v of recoveryIds) { const formatted = `${v}${r}${s}`; const tx = await createTransactionFn(formatted); const response = await broadcastFn(tx); if (!response.error || response.reason !== 'SignatureValidation') { return { success: true, response, signatureData: { v, r, s, formatted } } } } return { success: false, response: lastError, signatureData: { v: '01', r, s, formatted: `01${r}${s}` } } }","None","Systematic recovery ID testing; tries priority order; returns working signature or best guess"
47
+ 46,javascript,privy,complete-privy-buy,"Complete buy transaction with Privy","const privy = getPrivyServerClient(); const wallet = await privy.walletApi.getWallet({ id: walletId }); const publicKey = wallet.publicKey; const txOptions = { publicKey, contractAddress: dexAddress, contractName: dexName, functionName: 'buy', functionArgs: [Cl.address(dexContract), Cl.address(tokenContract), Cl.uint(BigInt(amountInMicroSTX))], fee: 5000, postConditionMode: PostConditionMode.Deny, postConditions: [Pc.principal(userAddr).willSendLte(BigInt(amountInMicroSTX)).ustx(), Pc.principal(dexContract).willSendGte(BigInt(100)).ft(tokenContract, symbol)], network: 'mainnet' }; const transaction = await makeUnsignedContractCall(txOptions); const signer = new TransactionSigner(transaction); const preSignSigHash = sigHashPreSign(signer.sigHash, transaction.auth.authType, transaction.auth.spendingCondition.fee, transaction.auth.spendingCondition.nonce); const payload = `0x${preSignSigHash}`; const rawSignData = await signRaw(walletId, payload); const signature = rawSignData.data.signature; const result = await broadcastWithRecoveryTesting(signature, async (recoveryId) => { const testTx = await makeUnsignedContractCall(txOptions); testTx.auth.spendingCondition.signature = createMessageSignature(recoveryId); return testTx }, async (tx) => await broadcastTransaction({ transaction: tx, network: 'mainnet' })); return result.response.txid","import { getPrivyServerClient } from '@/utils/privy-server-client'; import { makeUnsignedContractCall, TransactionSigner, sigHashPreSign, createMessageSignature, broadcastTransaction, Cl, Pc, PostConditionMode } from '@stacks/transactions'; import { signRaw, broadcastWithRecoveryTesting } from '@/utils'","Full Privy buy flow; makeUnsigned -> sign hash -> test recovery IDs -> broadcast; handles signature validation automatically"
48
+ 47,javascript,privy,complete-privy-sell,"Complete sell transaction with Privy","const privy = getPrivyServerClient(); const wallet = await privy.walletApi.getWallet({ id: walletId }); const publicKey = wallet.publicKey; const txOptions = { publicKey, contractAddress: dexAddress, contractName: dexName, functionName: 'sell', functionArgs: [Cl.address(dexContract), Cl.address(tokenContract), Cl.uint(BigInt(tokenAmount))], fee: 5000, postConditionMode: PostConditionMode.Deny, postConditions: [Pc.principal(userAddr).willSendLte(BigInt(tokenAmount)).ft(tokenContract, symbol), Pc.principal(dexContract).willSendGte(BigInt(1)).ustx()], network: 'mainnet' }; const transaction = await makeUnsignedContractCall(txOptions); const signer = new TransactionSigner(transaction); const preSignSigHash = sigHashPreSign(signer.sigHash, transaction.auth.authType, transaction.auth.spendingCondition.fee, transaction.auth.spendingCondition.nonce); const payload = `0x${preSignSigHash}`; const rawSignData = await signRaw(walletId, payload); const signature = rawSignData.data.signature; const result = await broadcastWithRecoveryTesting(signature, async (recoveryId) => { const testTx = await makeUnsignedContractCall(txOptions); testTx.auth.spendingCondition.signature = createMessageSignature(recoveryId); return testTx }, async (tx) => await broadcastTransaction({ transaction: tx, network: 'mainnet' })); return result.response.txid","import { getPrivyServerClient } from '@/utils/privy-server-client'; import { makeUnsignedContractCall, TransactionSigner, sigHashPreSign, createMessageSignature, broadcastTransaction, Cl, Pc, PostConditionMode } from '@stacks/transactions'; import { signRaw, broadcastWithRecoveryTesting } from '@/utils'","Full Privy sell flow; willSendLte for token transfer; automatic recovery ID testing"
49
+ 48,javascript,privy,privy-stx-transfer,"STX transfer with Privy wallet","const txOptions = { recipient: recipientAddress, amount: transferAmount, publicKey, network: 'mainnet', fee: 4000 }; const transaction = await makeUnsignedSTXTokenTransfer(txOptions); const signer = new TransactionSigner(transaction); const preSignSigHash = sigHashPreSign(signer.sigHash, transaction.auth.authType, transaction.auth.spendingCondition.fee, transaction.auth.spendingCondition.nonce); const payload = `0x${preSignSigHash}`; const rawSignData = await signRaw(walletId, payload); const signature = rawSignData.data.signature; const result = await broadcastWithRecoveryTesting(signature, async (recoveryId) => { const testTx = await makeUnsignedSTXTokenTransfer(txOptions); testTx.auth.spendingCondition.signature = createMessageSignature(recoveryId); return testTx }, async (tx) => await broadcastTransaction({ transaction: tx, network: 'mainnet' }))","import { makeUnsignedSTXTokenTransfer, TransactionSigner, sigHashPreSign, createMessageSignature, broadcastTransaction } from '@stacks/transactions'","Simple STX transfer; amount in microSTX; deduct fee if transferring full balance"
50
+ 49,javascript,privy,batch-privy-wallets,"Process multiple Privy wallets in parallel","const results = []; for (const wallet of wallets) { try { const walletId = wallet.id; const privyWallet = await privy.walletApi.getWallet({ id: walletId }); const publicKey = privyWallet.publicKey; const tx = await executeTrade(publicKey, walletId, wallet.amount); results.push({ walletId, status: 'success', txid: tx }) } catch (error) { results.push({ walletId: wallet.id, status: 'failed', error: error.message }); continue } } return { successful: results.filter(r => r.status === 'success').length, failed: results.filter(r => r.status === 'failed').length, results }","None","Sequential processing with error handling; collect all results; continue on individual failures"
51
+ 50,javascript,privy-auth,basic-auth-header,"Generate Basic Auth header for Privy API","function generateBasicAuthHeader(username, password) { const token = Buffer.from(`${username}:${password}`).toString('base64'); return `Basic ${token}` }","None","Username is appId; password is appSecret; required for all Privy API calls"
@@ -23,7 +23,9 @@ DOMAINS = {
23
23
  'tokens': 'fungible-tokens.csv',
24
24
  'auth': 'authentication.csv',
25
25
  'advanced': 'advanced-patterns.csv',
26
- 'chainhooks': 'chainhooks.csv'
26
+ 'chainhooks': 'chainhooks.csv',
27
+ 'trading': 'trading-bots.csv',
28
+ 'oracles': 'oracles.csv'
27
29
  }
28
30
 
29
31
  # Auto-detection keywords
@@ -38,9 +40,11 @@ DOMAIN_KEYWORDS = {
38
40
  'deployment': ['deploy', 'mainnet', 'testnet', 'clarinet', 'devnet'],
39
41
  'nfts': ['nft', 'sip-009', 'mint', 'transfer', 'metadata', 'collection', 'marketplace', 'gamma'],
40
42
  'tokens': ['sip-010', 'fungible', 'token', 'balance', 'allowance', 'transfer-from', 'decimals'],
41
- 'auth': ['authentication', 'session', 'sign', 'verify', 'profile', 'encrypt', 'jwt', 'oauth', 'webhook'],
43
+ 'auth': ['jwt', 'middleware', 'encrypt', 'decrypt', 'access-control', 'token-gate', 'nft-gate', 'protected-route', 'verify-signature'],
42
44
  'advanced': ['pagination', 'swr', 'activity', 'presale', 'lottery', 'portfolio', 'wizard', 'launchpad', 'template', 'milestone', 'vesting', 'export', 'csv', 'modal', 'confirmation'],
43
- 'chainhooks': ['chainhook', 'webhook', 'txid', 'contract-call', 'print-event', 'ft-event', 'nft-event', 'stx-event', 'predicate', 'hiro', 'indexing', 'ordinals']
45
+ 'chainhooks': ['chainhook', 'webhook', 'txid', 'contract-call', 'print-event', 'ft-event', 'nft-event', 'stx-event', 'predicate', 'hiro', 'indexing', 'ordinals'],
46
+ 'trading': ['trading', 'bot', 'automated', 'wallet-sdk', 'privy', 'buy', 'sell', 'swap', 'dex', 'post-condition', 'broadcast', 'signature', 'recovery', 'txOptions', 'makeContractCall', 'bonding'],
47
+ 'oracles': ['oracle', 'pyth', 'price', 'feed', 'vaa', 'hermes', 'wormhole', 'verify', 'update', 'decode']
44
48
  }
45
49
 
46
50
 
package/dist/index.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stacksagent",
3
- "version": "1.3.4",
3
+ "version": "1.5.0",
4
4
  "description": "AI Skill for building Stacks blockchain applications - CLI installer",
5
5
  "bin": {
6
6
  "stacksagent": "./dist/index.js"
@@ -11,7 +11,8 @@
11
11
  "README.md"
12
12
  ],
13
13
  "scripts": {
14
- "build": "tsc && npm run copy-assets",
14
+ "build": "npm run copy-readme && tsc && npm run copy-assets",
15
+ "copy-readme": "cp ../README.md ./README.md",
15
16
  "copy-assets": "cp -r src/assets dist/",
16
17
  "dev": "tsc --watch",
17
18
  "prepublishOnly": "npm run build"
@@ -1,76 +0,0 @@
1
- id,category,name,description,code,imports,notes
2
- 1,wallet,connect-wallet,"Connect to Leather/Xverse wallet","const userData = await connect(); console.log('Connected:' userData.addresses.stx[0].address)","import { connect } from '@stacks/connect'","Returns userData with addresses.stx[0].address for STX and addresses.btc[0].address for BTC"
3
- 2,wallet,get-address,"Get user's STX address from connected wallet","const { isConnected getLocalStorage } = await import('@stacks/connect'); if (!isConnected()) return null; const data = getLocalStorage(); return data?.addresses?.stx?.[0]?.address || null","import { isConnected getLocalStorage } from '@stacks/connect'","Use getLocalStorage() to access stored wallet data; check isConnected() first"
4
- 3,wallet,disconnect,"Disconnect wallet","await disconnect(); localStorage.removeItem('walletAddress')","import { disconnect } from '@stacks/connect'","Disconnects wallet and clears session; also clear localStorage"
5
- 4,wallet,check-connection,"Check if wallet is already connected","if (isConnected()) { console.log('Already authenticated'); return }","import { isConnected } from '@stacks/connect'","Check connection status before calling connect(); prevents duplicate connection prompts"
6
- 5,wallet,connect-wallet-helper,"Complete connect wallet helper with isConnected check","async function connectWallet() { const { connect isConnected getLocalStorage } = await import('@stacks/connect'); if (isConnected()) { return getLocalStorage() }; return connect() }","import { connect isConnected getLocalStorage } from '@stacks/connect'","Production pattern: check if already connected before calling connect(); uses lazy import"
7
- 6,wallet,connect-wallet-button,"Wallet connect button with loading states","function ConnectButton({ onConnect }) { const [connecting setConnecting] = useState(false); const handleConnect = async () => { setConnecting(true); try { const userData = await connect(); const address = userData.addresses.stx[0].address; localStorage.setItem('walletAddress' address); onConnect?.(address); setConnecting(false) } catch(e) { setConnecting(false) }}; return <button onClick={handleConnect} disabled={connecting}>{connecting ? 'Connecting...' : 'Connect Wallet'}</button> }","import { connect } from '@stacks/connect'; import { useState } from 'react'","React component; uses correct connect() API with userData.addresses.stx[0].address"
8
- 7,wallet,use-wallet-hook,"Complete React hook for wallet connection management","function useWallet() { const [address setAddress] = useState(null); const [connected setConnected] = useState(false); useEffect(() => { const saved = localStorage.getItem('walletAddress'); if (saved) { setAddress(saved); setConnected(true) }}, []); const connectWallet = async () => { const userData = await connect(); const addr = userData.addresses.stx[0].address; setAddress(addr); setConnected(true); localStorage.setItem('walletAddress' addr); return userData }; const disconnectWallet = async () => { await disconnect(); setAddress(null); setConnected(false); localStorage.removeItem('walletAddress') }; return { address connected connect: connectWallet disconnect: disconnectWallet }}","import { connect disconnect } from '@stacks/connect'; import { useState useEffect } from 'react'","Complete wallet management with correct userData.addresses.stx[0].address pattern"
9
- 8,wallet,disconnect-wallet-helper,"Complete disconnect helper with cleanup","async function disconnectWallet() { const { disconnect } = await import('@stacks/connect'); disconnect(); localStorage.clear() }","import { disconnect } from '@stacks/connect'","Disconnect and clear all localStorage; use localStorage.clear() or removeItem"
10
- 9,wallet,disconnect-button,"Disconnect wallet with UI feedback and cleanup","function DisconnectButton({ onDisconnect }) { const handleDisconnect = async () => { const { disconnect } = await import('@stacks/connect'); await disconnect(); localStorage.removeItem('walletAddress'); localStorage.removeItem('stacks_wallet'); onDisconnect(); }; return <button onClick={handleDisconnect}>Disconnect</button> }","import { disconnect } from '@stacks/connect'","Cleans up both localStorage and @stacks/connect session"
11
- 10,wallet,is-wallet-connected,"Check wallet connection status","async function isWalletConnected() { const { isConnected } = await import('@stacks/connect'); return isConnected() }","import { isConnected } from '@stacks/connect'","Returns boolean; call before attempting to access wallet data"
12
- 11,wallet,get-wallet-addresses,"Get all wallet addresses (STX BTC) from connected wallet","async function getWalletAddresses() { const { isConnected getLocalStorage } = await import('@stacks/connect'); if (!isConnected()) return null; return getLocalStorage()?.addresses || null }","import { isConnected getLocalStorage } from '@stacks/connect'","Returns addresses object with stx and btc arrays; null if not connected"
13
- 12,wallet,get-btc-address,"Get Bitcoin address from connected wallet","const { isConnected getLocalStorage } = await import('@stacks/connect'); if (!isConnected()) return null; const data = getLocalStorage(); return data?.addresses?.btc?.[0]?.address || null","import { isConnected getLocalStorage } from '@stacks/connect'","Returns BTC address string from addresses.btc[0].address"
14
- 13,wallet,get-stx-and-btc-addresses,"Get both STX and BTC addresses","const { isConnected getLocalStorage } = await import('@stacks/connect'); if (!isConnected()) return null; const data = getLocalStorage(); const stxAddress = data?.addresses?.stx?.[0]?.address; const btcAddress = data?.addresses?.btc?.[0]?.address; return { stx: stxAddress btc: btcAddress }","import { isConnected getLocalStorage } from '@stacks/connect'","Returns object with both addresses; addresses are from first element of arrays"
15
- 14,wallet,resolve-stx-address-helper,"Helper to resolve STX address from connected wallet","async function resolveStxAddress() { const { isConnected getLocalStorage } = await import('@stacks/connect'); if (!isConnected()) return null; const data = getLocalStorage(); return data?.addresses?.stx?.[0]?.address || null }","import { isConnected getLocalStorage } from '@stacks/connect'","Production helper matching sBTC Market pattern; returns STX address string or null"
16
- 15,wallet,addresses-structure,"Understanding getLocalStorage addresses structure","const data = getLocalStorage(); // Returns: { addresses: { stx: [{ address: 'SP...' }] btc: [{ address: 'bc1...' }] } }","import { getLocalStorage } from '@stacks/connect'","Addresses are arrays; stx[0].address for STX, btc[0].address for BTC"
17
- 16,wallet,display-wallet-address,"Display connected wallet with truncated address","function WalletDisplay({ address }) { const truncated = address ? `${address.slice(0 6)}...${address.slice(-4)}` : ''; const handleCopy = () => navigator.clipboard.writeText(address); return <div onClick={handleCopy}>{truncated}</div> }","import { useState } from 'react'","Shows first 6 and last 4 characters; click to copy full address"
18
- 17,wallet,connection-status-badge,"Visual connection status indicator","function ConnectionStatus({ connected }) { return <div style={{ display: 'flex' alignItems: 'center' gap: '8px' }}><div style={{ width: 8 height: 8 borderRadius: '50%' background: connected ? '#10b981' : '#ef4444' }} /><span>{connected ? 'Connected' : 'Disconnected'}</span></div> }","import { React } from 'react'","Green dot for connected, red for disconnected; customizable styling"
19
- 18,wallet,wallet-info-display,"Complete wallet info display with balance","function WalletInfo({ address }) { const [balance setBalance] = useState('0'); useEffect(() => { if (!address) return; fetch(`https://api.hiro.so/extended/v1/address/${address}/balances`).then(r => r.json()).then(data => setBalance((Number(data.stx.balance) / 1000000).toFixed(2))) }, [address]); return <div><div>{formatAddress(address)}</div><div>{balance} STX</div></div> }","import { useState useEffect } from 'react'","Fetches and displays STX balance; combines address display with balance"
20
- 19,transaction,stx-transfer,"Transfer STX","await makeSTXTokenTransfer({ recipient amount senderKey network anchorMode: AnchorMode.Any })","import { makeSTXTokenTransfer AnchorMode } from '@stacks/transactions'","Amount in microSTX (1 STX = 1000000 microSTX)"
21
- 20,transaction,contract-call,"Call contract function","await makeContractCall({ contractAddress contractName functionName functionArgs senderKey network anchorMode: AnchorMode.Any })","import { makeContractCall AnchorMode } from '@stacks/transactions'","Use cv.* constructors for functionArgs"
22
- 21,transaction,deploy-contract,"Deploy Clarity contract","await makeContractDeploy({ contractName codeBody senderKey network anchorMode: AnchorMode.Any })","import { makeContractDeploy AnchorMode } from '@stacks/transactions'","codeBody is Clarity code as string"
23
- 22,transaction,broadcast-tx,"Broadcast transaction","const txid = await broadcastTransaction(transaction network)","import { broadcastTransaction } from '@stacks/transactions'","Returns transaction ID"
24
- 23,clarity-values,uint,"Create Clarity uint","uintCV(100)","import { uintCV } from '@stacks/transactions'","For uint function arguments"
25
- 24,clarity-values,int,"Create Clarity int","intCV(-100)","import { intCV } from '@stacks/transactions'","For int function arguments"
26
- 25,clarity-values,principal,"Create Clarity principal","principalCV('SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR')","import { principalCV } from '@stacks/transactions'","Standard or contract principal"
27
- 26,clarity-values,string-ascii,"Create Clarity ASCII string","stringAsciiCV('hello world')","import { stringAsciiCV } from '@stacks/transactions'","ASCII characters only"
28
- 27,clarity-values,string-utf8,"Create Clarity UTF-8 string","stringUtf8CV('hello δΈ–η•Œ')","import { stringUtf8CV } from '@stacks/transactions'","Supports Unicode characters"
29
- 28,clarity-values,buffer,"Create Clarity buffer","bufferCV(Buffer.from('data'))","import { bufferCV } from '@stacks/transactions'","Node.js Buffer or Uint8Array"
30
- 29,clarity-values,tuple,"Create Clarity tuple","tupleCV({ name: stringAsciiCV('test') value: uintCV(100) })","import { tupleCV stringAsciiCV uintCV } from '@stacks/transactions'","Object with CV values"
31
- 30,clarity-values,list,"Create Clarity list","listCV([uintCV(1) uintCV(2) uintCV(3)])","import { listCV uintCV } from '@stacks/transactions'","Array of CV values of same type"
32
- 31,clarity-values,some,"Create Clarity some value","someCV(uintCV(100))","import { someCV uintCV } from '@stacks/transactions'","Optional value present"
33
- 32,clarity-values,none,"Create Clarity none value","noneCV()","import { noneCV } from '@stacks/transactions'","Optional value absent"
34
- 33,clarity-values,ok-response,"Create Clarity ok response","responseOkCV(uintCV(100))","import { responseOkCV uintCV } from '@stacks/transactions'","Success response"
35
- 34,clarity-values,err-response,"Create Clarity err response","responseErrorCV(uintCV(404))","import { responseErrorCV uintCV } from '@stacks/transactions'","Error response"
36
- 35,api,get-balance,"Get STX balance","const response = await fetch(`https://api.hiro.so/extended/v1/address/${address}/balances`)","fetch or axios","Returns STX balance and all token balances"
37
- 36,api,get-transactions,"Get address transactions","const response = await fetch(`https://api.hiro.so/extended/v1/address/${address}/transactions`)","fetch or axios","Paginated results with limit and offset"
38
- 37,api,get-contract-info,"Get contract info","const response = await fetch(`https://api.hiro.so/v2/contracts/interface/${address}/${contractName}`)","fetch or axios","Returns ABI and source code"
39
- 38,api,call-read-only,"Call read-only function","const result = await callReadOnlyFunction({ contractAddress contractName functionName functionArgs senderAddress network })","import { callReadOnlyFunction } from '@stacks/transactions'","No transaction needed for read-only calls"
40
- 39,api,get-transaction,"Get transaction by ID","const response = await fetch(`https://api.hiro.so/extended/v1/tx/${txid}`)","fetch or axios","Returns transaction details and status"
41
- 40,network,mainnet,"Mainnet config","new StacksMainnet()","import { StacksMainnet } from '@stacks/network'","Production network"
42
- 41,network,testnet,"Testnet config","new StacksTestnet()","import { StacksTestnet } from '@stacks/network'","Testing network"
43
- 42,network,devnet,"Devnet config","new StacksDevnet()","import { StacksDevnet } from '@stacks/network'","Local development"
44
- 43,post-conditions,stx-postcondition,"STX transfer post-condition","makeStandardSTXPostCondition(address FungibleConditionCode.Equal amount)","import { makeStandardSTXPostCondition FungibleConditionCode } from '@stacks/transactions'","Ensures exact STX amount transferred"
45
- 44,post-conditions,ft-postcondition,"FT transfer post-condition","makeStandardFungiblePostCondition(address FungibleConditionCode.Equal amount assetInfo)","import { makeStandardFungiblePostCondition FungibleConditionCode createAssetInfo } from '@stacks/transactions'","Ensures exact token amount transferred"
46
- 45,post-conditions,nft-postcondition,"NFT transfer post-condition","makeStandardNonFungiblePostCondition(address NonFungibleConditionCode.Sends assetInfo tokenId)","import { makeStandardNonFungiblePostCondition NonFungibleConditionCode createAssetInfo } from '@stacks/transactions'","Ensures NFT ownership transfer"
47
- 46,state-management,react-context-provider,"Create React Context for wallet state with persistence","const Context = createContext(undefined); function Provider({ children }) { const [state setState] = useState(defaultState); useEffect(() => { localStorage.setItem(KEY JSON.stringify(state)) } [state]); return <Context.Provider value={state}>{children}</Context.Provider> }","import { createContext useState useEffect } from 'react'","React-specific; includes localStorage persistence and hydration"
48
- 47,authentication,sign-message,"Sign message to prove wallet ownership","const { request } = await import('@stacks/connect'); const response = await request('stx_signMessage' { message: 'Sign to verify ownership' }); return { signature: response.signature publicKey: response.publicKey }","import { request } from '@stacks/connect'","Used for authentication without transfers; returns signature and publicKey"
49
- 48,server-side,server-transaction,"Build and broadcast transaction from server","const tx = await makeContractCall({ contractAddress contractName functionName functionArgs senderKey network nonce anchorMode: AnchorMode.Any }); const result = await broadcastTransaction({ transaction: tx network })","import { makeContractCall broadcastTransaction AnchorMode } from '@stacks/transactions'","Requires senderKey (private key) and nonce management"
50
- 49,server-side,fetch-nonce,"Get current nonce for server-side transactions","const response = await fetch(`https://api.hiro.so/extended/v1/address/${address}/nonces`); const data = await response.json(); return data.possible_next_nonce","fetch or axios","Essential for server-side transaction sequencing"
51
- 50,server-side,wallet-sdk,"Generate wallet from seed phrase for server-side transactions","const wallet = await generateWallet({ secretKey: seedPhrase password }); const account = generateNewAccount(wallet); const address = getStxAddress(account.accounts[0] networkKey)","import { generateWallet generateNewAccount getStxAddress } from '@stacks/wallet-sdk'","For server-side automation; keep seed phrases secure"
52
- 51,transaction,complex-contract-call,"Contract call with multiple arguments and types","const args = [stringUtf8CV(question) uintCV(BigInt(block)) bufferCV(hexToBuff(id)) intCV(BigInt(price)) tupleCV({ field: valueCV })]; await openContractCall(functionName args)","import { stringUtf8CV uintCV bufferCV intCV tupleCV } from '@stacks/transactions'","Shows proper type conversion for complex arguments"
53
- 52,utilities,hex-to-buffer,"Convert hex string to buffer for Clarity","function hexToBuff(hex) { const clean = hex.replace(/^0x/ ''); return Buffer.from(clean 'hex') }","Node.js Buffer or @stacks/common","Essential for buffer arguments like feed IDs"
54
- 53,advanced-api,fetch-readonly,"Call read-only function with fetchCallReadOnlyFunction","const result = await fetchCallReadOnlyFunction({ contractAddress contractName functionName functionArgs: [uintCV(id)] network senderAddress }); const json = cvToJSON(result)","import { fetchCallReadOnlyFunction uintCV cvToJSON } from '@stacks/transactions'","No transaction fee; use for queries"
55
- 54,utilities,unwrap-response,"Unwrap (ok) response from read-only calls","function unwrapResponseOk(cvJson) { if (cvJson.type === 'responseOk') return cvJson.value; if (cvJson.success) return cvJson.value; return null }","None (utility function)","Handles different response formats from cvToJSON"
56
- 55,utilities,parse-cv-response,"Parse and extract values from ClarityValue JSON","const json = cvToJSON(result); const ok = unwrapResponseOk(json); if (!ok) return null; const value = BigInt(ok.value)","import { cvToJSON } from '@stacks/transactions'","Common pattern for processing read-only responses"
57
- 56,utilities,lazy-import,"Lazy load Stacks.js to reduce bundle size","let modulePromise = null; async function getModule() { if (!modulePromise) modulePromise = import('@stacks/connect'); return modulePromise }","Dynamic import()","Reduces initial bundle; cache promise for reuse"
58
- 57,network,custom-fetch-network,"Create network with custom fetch for headers","const customFetch = async (input init) => { const headers = { ...init?.headers 'x-hiro-api-key': key }; return fetch(input { ...init headers }) }; createNetwork({ network: 'mainnet' client: { baseUrl: url fetch: customFetch }})","import { createNetwork } from '@stacks/network'","Useful for API key management and request customization"
59
- 58,hooks,use-price-hook,"React hook for fetching external price data","function useBtcPrice() { const [price setPrice] = useState(0); useEffect(() => { const fetchPrice = async () => { const res = await fetch(url); setPrice(await res.json()) }; fetchPrice(); const interval = setInterval(fetchPrice 60000); return () => clearInterval(interval) } []); return price }","import { useState useEffect } from 'react'","React-specific; shows interval cleanup pattern"
60
- 59,advanced-api,parallel-reads,"Execute multiple read-only calls in parallel","const promises = ids.map(id => fetchCallReadOnlyFunction({ contractAddress contractName functionName functionArgs: [uintCV(id)] network senderAddress })); const results = await Promise.all(promises)","import { fetchCallReadOnlyFunction uintCV } from '@stacks/transactions'","More efficient than sequential calls"
61
- 60,clarity-values,contract-principal,"Create contract principal ClarityValue","const [address name] = contractId.split('.'); contractPrincipalCV(address name)","import { contractPrincipalCV } from '@stacks/transactions'","For passing contract references as arguments"
62
- 61,clarity-values,boolean-cv,"Create boolean ClarityValue from JavaScript boolean","function boolCV(value) { return value ? trueCV() : falseCV() }","import { trueCV falseCV } from '@stacks/transactions'","Helper for converting JS boolean to Clarity"
63
- 62,utilities,cv-to-hex,"Encode ClarityValue to hex for request","const hexArgs = functionArgs.map(arg => cvToHex(arg))","import { cvToHex } from '@stacks/transactions'","Used with request('stx_callContract') for Stacks Connect"
64
- 63,utilities,parse-tuple-response,"Parse tuple fields from read-only response","const json = cvToJSON(result); const ok = unwrapResponseOk(json); const fields = ok.value; return { name: fields.name.value count: BigInt(fields.count.value) }","import { cvToJSON } from '@stacks/transactions'","Common for contract data structures"
65
- 64,network,env-network-config,"Network configuration based on environment","const networkKey = process.env.NETWORK === 'mainnet' ? 'mainnet' : 'testnet'; const rpcUrl = process.env.RPC_URL || defaultRpc[networkKey]; const network = createNetwork({ network: networkKey client: { baseUrl: rpcUrl }})","import { createNetwork } from '@stacks/network'","Standard pattern for multi-environment apps"
66
- 65,transaction,post-condition-mode,"Set post-condition mode for transactions","await makeContractCall({ ...options postConditionMode: PostConditionMode.Allow })","import { makeContractCall PostConditionMode } from '@stacks/transactions'","Allow or Deny; Allow skips post-condition validation"
67
- 66,state-management,vanilla-state-persist,"Persist wallet state with vanilla JavaScript (no React)","function saveWallet(data) { localStorage.setItem('wallet' JSON.stringify(data)) }; function loadWallet() { const raw = localStorage.getItem('wallet'); return raw ? JSON.parse(raw) : null }","None (native browser API)","Framework-agnostic alternative to React Context; works in any JavaScript environment"
68
- 67,utilities,poll-tx-status,"Poll transaction status until confirmed or failed","async function pollTxStatus(txid) { while (true) { const res = await fetch(`https://api.hiro.so/extended/v1/tx/${txid}`); const tx = await res.json(); if (tx.tx_status === 'success' || tx.tx_status === 'abort_by_response') return tx; await new Promise(r => setTimeout(r 3000)) }}","fetch","Polls every 3 seconds; returns when transaction confirms or fails"
69
- 68,transaction,sequential-calls,"Execute multiple contract calls in sequence","async function callSequence(calls network senderKey) { let nonce = await getNonce(address); for (const call of calls) { const tx = await makeContractCall({ ...call senderKey network nonce: nonce++ }); await broadcastTransaction({ transaction: tx network }) }}","import { makeContractCall broadcastTransaction } from '@stacks/transactions'","Manages nonce increments for sequential transactions"
70
- 69,transaction,custom-tx-options,"Override transaction fee and nonce manually","await makeContractCall({ contractAddress contractName functionName functionArgs senderKey network nonce: customNonce fee: BigInt(10000) anchorMode: AnchorMode.Any })","import { makeContractCall AnchorMode } from '@stacks/transactions'","Fee in microSTX; useful for batch transactions or custom nonce management"
71
- 70,utilities,hiro-headers,"Add randomized Hiro API headers for rate limiting","function getHiroHeaders() { const keys = ['key1' 'key2' 'key3']; const randomKey = keys[Math.floor(Math.random() * keys.length)]; return { 'x-hiro-api-key': randomKey 'Content-Type': 'application/json' }}","None","Rotates API keys to distribute rate limits across multiple keys"
72
- 71,utilities,format-balance,"Format STX balance from microSTX to STX with decimals","function formatStx(microStx decimals = 6) { const stx = Number(microStx) / 1000000; return stx.toFixed(decimals) }","None","1 STX = 1,000,000 microSTX; default to 6 decimal places"
73
- 72,transaction,tx-error-handling,"Comprehensive error handling for transaction failures","try { const result = await broadcastTransaction(tx network); return result } catch (error) { if (error.message.includes('ConflictingNonceInMempool')) throw new Error('Nonce conflict wait and retry'); if (error.message.includes('NotEnoughFunds')) throw new Error('Insufficient balance'); throw error }","None (error handling pattern)","Handles common transaction errors with user-friendly messages"
74
- 73,network,switch-network,"Dynamically switch between networks at runtime","function getNetwork(networkName) { const networks = { mainnet: new StacksMainnet() testnet: new StacksTestnet() devnet: new StacksDevnet() }; return networks[networkName] || networks.testnet }","import { StacksMainnet StacksTestnet StacksDevnet } from '@stacks/network'","Factory pattern for network switching; defaults to testnet if unknown"
75
- 74,state-management,persist-wallet-connection,"Save wallet connection state to localStorage","function saveWalletState(address network connected) { const state = { address network connected timestamp: Date.now() }; localStorage.setItem('stacks_wallet' JSON.stringify(state)) }; function loadWalletState() { const raw = localStorage.getItem('stacks_wallet'); if (!raw) return null; const state = JSON.parse(raw); const hourAgo = Date.now() - 3600000; if (state.timestamp < hourAgo) return null; return state }","None (native browser API)","Includes timestamp expiry (1 hour); prevents stale connections"
76
- 75,utilities,format-wallet-address,"Format wallet address for display (truncated)","function formatAddress(address maxLength = 12) { if (!address) return ''; if (address.length <= maxLength) return address; const start = Math.floor((maxLength - 3) / 2); const end = address.length - (maxLength - 3 - start); return `${address.slice(0 start)}...${address.slice(end)}` }","None","Configurable truncation; default shows ~6 chars + ... + ~3 chars"