stacksagent 1.3.2 → 1.4.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.
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
id,language,category,name,description,code,imports_or_contract,notes
|
|
2
|
-
1,javascript,
|
|
3
|
-
2,javascript,
|
|
4
|
-
3,javascript,
|
|
5
|
-
4,javascript,
|
|
6
|
-
5,javascript,
|
|
7
|
-
6,javascript,
|
|
8
|
-
7,javascript,
|
|
9
|
-
8,javascript,
|
|
10
|
-
9,javascript,
|
|
11
|
-
10,javascript,
|
|
12
|
-
11,javascript,
|
|
13
|
-
12,javascript,
|
|
14
|
-
13,javascript,
|
|
15
|
-
14,javascript,
|
|
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,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}×tamp=${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,8 @@ 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'
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
# Auto-detection keywords
|
|
@@ -38,9 +39,10 @@ DOMAIN_KEYWORDS = {
|
|
|
38
39
|
'deployment': ['deploy', 'mainnet', 'testnet', 'clarinet', 'devnet'],
|
|
39
40
|
'nfts': ['nft', 'sip-009', 'mint', 'transfer', 'metadata', 'collection', 'marketplace', 'gamma'],
|
|
40
41
|
'tokens': ['sip-010', 'fungible', 'token', 'balance', 'allowance', 'transfer-from', 'decimals'],
|
|
41
|
-
'auth': ['
|
|
42
|
+
'auth': ['jwt', 'middleware', 'encrypt', 'decrypt', 'access-control', 'token-gate', 'nft-gate', 'protected-route', 'verify-signature'],
|
|
42
43
|
'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']
|
|
44
|
+
'chainhooks': ['chainhook', 'webhook', 'txid', 'contract-call', 'print-event', 'ft-event', 'nft-event', 'stx-event', 'predicate', 'hiro', 'indexing', 'ordinals'],
|
|
45
|
+
'trading': ['trading', 'bot', 'automated', 'wallet-sdk', 'privy', 'buy', 'sell', 'swap', 'dex', 'post-condition', 'broadcast', 'signature', 'recovery', 'txOptions', 'makeContractCall', 'bonding']
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
|
package/package.json
CHANGED
|
@@ -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"
|