======================== CODE SNIPPETS ======================== TITLE: Install Lumos and Crypto-Browserify DESCRIPTION: Command to install the necessary `@ckb-lumos/lumos` library and `crypto-browserify` dependency using npm for use in a project. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/connect-wallet.mdx#_snippet_3 LANGUAGE: bash CODE: ``` npm install @ckb-lumos/lumos crypto-browserify --save ``` ---------------------------------------- TITLE: Install Spore SDK (Bash) DESCRIPTION: Install the `@spore-sdk/core` package using npm. This SDK provides utilities for interacting with the Spore Protocol and simplifies transaction creation. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/create-new-site.mdx#_snippet_3 LANGUAGE: bash CODE: ``` npm install @spore-sdk/core --save ``` ---------------------------------------- TITLE: Start Development Server (Bash) DESCRIPTION: Starts the local development server for the Next.js application, typically making the application accessible via http://localhost:3000. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/index.mdx#_snippet_0 LANGUAGE: bash CODE: ``` npm run dev ``` ---------------------------------------- TITLE: Installing wagmi and viem (Bash) DESCRIPTION: Use this command to install the necessary libraries `wagmi` and `viem` via npm. These libraries are essential for interacting with Ethereum wallets like MetaMask in your project. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/connect-wallet.mdx#_snippet_0 LANGUAGE: Bash CODE: ``` npm install wagmi viem --save ``` ---------------------------------------- TITLE: Install react-remark for Markdown Rendering (Bash) DESCRIPTION: Installs the react-remark library using npm, which is required to render Markdown content within the React component for displaying blog posts. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/manage-blog-post.mdx#_snippet_4 LANGUAGE: bash CODE: ``` npm install --save react-remark ``` ---------------------------------------- TITLE: Initializing Blog Site Homepage with Spore SDK (TypeScript) DESCRIPTION: This code defines a Next.js page component that fetches and displays details for a specific blog site (represented by a Spore Cluster). It uses the router to get the site ID, queries the CKB indexer for the corresponding Cluster cell using the Spore SDK, unpacks the data, and updates the component state to display the site's name and description. It also includes basic wallet connection logic. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/create-new-site.mdx#_snippet_10 LANGUAGE: TypeScript CODE: ``` import useWallet from '@/hooks/useWallet'; import { Indexer } from '@ckb-lumos/lumos'; import { getSporeScript, unpackToRawClusterData } from '@spore-sdk/core'; import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; import { Site } from '..'; import { config } from '@/config'; export default function SitePage() { const router = useRouter(); const { id } = router.query; const { lock, isConnected, connect } = useWallet(); const [siteInfo, setSiteInfo] = useState(); useEffect(() => { if (!id) { return; } (async () => { const indexer = new Indexer(config.ckbIndexerUrl); const { script } = getSporeScript(config, 'Cluster'); const collector = indexer.collector({ type: { ...script, args: id as string }, }); for await (const cell of collector.collect()) { const unpacked = unpackToRawClusterData(cell.data); setSiteInfo({ id: cell.cellOutput.type!.args, name: unpacked.name, description: unpacked.description, }); } })(); }, [id, lock]); return (

{siteInfo?.name}

{siteInfo?.description}

{isConnected ? ( ) : ( )}
); } ``` ---------------------------------------- TITLE: Install Spore SDK with npm (Shell) DESCRIPTION: Installs the Spore SDK core package using the npm package manager. This command downloads the necessary dependencies and makes the SDK available in your project. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/resources/spore-sdk.mdx#_snippet_0 LANGUAGE: shell CODE: ``` npm i @spore-sdk/core ``` ---------------------------------------- TITLE: Install Node Dependencies with pnpm DESCRIPTION: This command installs all necessary project dependencies using the pnpm package manager. It is the first step required to set up the project for local development. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/README.md#_snippet_0 LANGUAGE: shell CODE: ``` pnpm i ``` ---------------------------------------- TITLE: Install Spore SDK with pnpm (Shell) DESCRIPTION: Installs the Spore SDK core package using the pnpm package manager. This command adds the package to your project's dependencies efficiently. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/resources/spore-sdk.mdx#_snippet_1 LANGUAGE: shell CODE: ``` pnpm add @spore-sdk/core ``` ---------------------------------------- TITLE: Install Spore SDK with yarn (Shell) DESCRIPTION: Installs the Spore SDK core package using the yarn package manager. This command adds the package to your project's dependencies. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/resources/spore-sdk.mdx#_snippet_2 LANGUAGE: shell CODE: ``` yarn add @spore-sdk/core ``` ---------------------------------------- TITLE: Preparing Spore Transaction for Signing (TypeScript) DESCRIPTION: Illustrates the initial steps required to prepare a transaction skeleton (`txSkeleton`) for signing, specifically for the SECP256K1_BLAKE160 lock script. It involves getting the Spore configuration, accessing the script details, and using `prepareSigningEntries` to generate the messages that need to be signed by the wallet. This is a prerequisite step before the actual signing. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-first-spore/explore-code.md#_snippet_8 LANGUAGE: TypeScript CODE: ``` import { SporeConfig, getSporeConfig } from '@spore-sdk/core'; // Generate messages to be signed const config: SporeConfig = getSporeConfig(); const Secp256k1Blake160 = config.lumos.SCRIPTS['SECP256K1_BLAKE160']!; txSkeleton = secp256k1Blake160.prepareSigningEntries(txSkeleton, { config: config.lumos }); // Sign transaction messages with private key from the `wallet` txSkeleton = wallet.signTransaction(txSkeleton); ``` ---------------------------------------- TITLE: Run Spore Docs in Development Mode DESCRIPTION: Execute this command to start the documentation site locally in development mode. This allows you to edit files and see changes reflected immediately in your browser. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/README.md#_snippet_1 LANGUAGE: shell CODE: ``` pnpm run start ``` ---------------------------------------- TITLE: Wallet Interface Definition in TypeScript DESCRIPTION: Defines the structure of the Wallet type used in the example. It includes properties for the CKB lock script and address, and methods for signing messages, transactions, and signing/sending transactions. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-first-spore/explore-code.md#_snippet_1 LANGUAGE: tsx CODE: ``` import { Script, Address, HexString, Hash } from '@ckb-lumos/base'; interface Wallet { lock: Script; address: Address; signMessage(message: HexString): Hash; signTransaction(txSkeleton: helpers.TransactionSkeletonType): helpers.TransactionSkeletonType; signAndSendTransaction(txSkeleton: helpers.TransactionSkeletonType): Promise; } ``` ---------------------------------------- TITLE: Setting up Site Creation Form (React/TypeScript) DESCRIPTION: Initializes state variables for site name and description using React's useState hook. Renders a form allowing users to input these values and a button to submit. Includes basic wallet connection status display and buttons. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/create-new-site.mdx#_snippet_8 LANGUAGE: tsx CODE: ``` import { Indexer, RPC } from '@ckb-lumos/lumos'; import { useEffect, useState } from 'react'; import { createCluster, unpackToRawClusterData, getSporeScript } from '@spore-sdk/core'; import { signTransaction } from '@/utils/transaction'; import useWallet from '@/hooks/useWallet'; export default function Home() { const { address, lock, balance, isConnected, connect, disconnect } = useWallet(); const [siteName, setSiteName] = useState(''); const [siteDescription, setSiteDescription] = useState(''); if (!isConnected) { return ; } return (
CKB Address: {address}
Balance: {balance?.toNumber() ?? 0} CKB

Create Site

setSiteName(e.target.value)} />
setSiteDescription(e.target.value)} />
); } ``` ---------------------------------------- TITLE: Initializing Wagmi Configuration (TypeScript/TSX) DESCRIPTION: Include this code in your `src/pages/_app.tsx` file to initialize the Wagmi configuration. It sets up auto-connection and configures a public client using `viem` to interact with the Ethereum mainnet. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/connect-wallet.mdx#_snippet_1 LANGUAGE: TypeScript CODE: ``` import type { AppProps } from 'next/app'; import { WagmiConfig, createConfig, mainnet } from 'wagmi'; import { createPublicClient, http } from 'viem'; const config = createConfig({ autoConnect: true, publicClient: createPublicClient({ chain: mainnet, transport: http(), }), }); export default function App({ Component, pageProps }: AppProps) { return ( ); } ``` ---------------------------------------- TITLE: Setting up DOB Decoder Server (Bash) DESCRIPTION: These commands clone the standalone DOB decoder server repository from GitHub and then run the server using `cargo run`. The `RUST_LOG` environment variable is set to enable debug logging for the server. By default, the server runs on CKB Testnet; configuration needs to be swapped for Mainnet. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/dob/Introduction.mdx#_snippet_2 LANGUAGE: bash CODE: ``` $ git clone https://github.com/sporeprotocol/dob-decoder-standalone-server $ RUST_LOG=dob_decoder_server=debug cargo run ``` ---------------------------------------- TITLE: Configure Next.js for Browser Polyfills DESCRIPTION: Configuration for `next.config.js` to add webpack fallbacks for Node.js modules like `crypto` and `buffer`, making `@ckb-lumos/lumos` compatible with browser environments. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/connect-wallet.mdx#_snippet_4 LANGUAGE: javascript CODE: ``` const webpack = require('webpack'); /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, webpack: (config) => { // <- Add some webpack config config.resolve.fallback = { ...config.resolve.fallback, crypto: require.resolve('crypto-browserify'), buffer: require.resolve('buffer'), encoding: false, path: false, fs: false, stream: false, }; config.plugins = [ ...config.plugins, new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }), ]; return config; }, }; module.exports = nextConfig; ``` ---------------------------------------- TITLE: Import Dependencies for Cluster Creation (TypeScript) DESCRIPTION: Import necessary functions like `createCluster` from `@spore-sdk/core`, the custom `signTransaction` utility, and the application's Spore configuration for use in the site creation logic. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/create-new-site.mdx#_snippet_6 LANGUAGE: tsx CODE: ``` import { createCluster, unpackToRawClusterData } from '@spore-sdk/core'; import { signTransaction } from '@/utils/transaction'; import { config } from '@/config'; ``` ---------------------------------------- TITLE: Importing useWallet Hook in Index Page (TSX) DESCRIPTION: Imports the custom `useWallet` React hook into the `index.tsx` page. This hook encapsulates the wallet connection and address derivation logic, making the page component cleaner. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/create-new-site.mdx#_snippet_1 LANGUAGE: TSX CODE: ``` import useWallet from '@/hooks/useWallet'; ``` ---------------------------------------- TITLE: Creating Immortal Spore with Specific Content Type DESCRIPTION: This example shows how to use `data.contentTypeParameters` alongside a specific `contentType`, such as 'image/jpeg', to create an immortal spore of that type. The parameters are automatically merged into the final `contentType` string. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/recipes/Create/create-immortal-spore.md#_snippet_1 LANGUAGE: TypeScript CODE: ``` await createSpore({ data: { content: CONTENT_AS_BYTES, contentType: 'image/jpeg', contentTypeParameters: { immortal: true, }, }, ... }); ``` ---------------------------------------- TITLE: Set up CKB Balance Query Utility DESCRIPTION: This snippet creates a utility function `getCapacities` in `src/utils/balance.ts` to query the CKB balance for a given address on the Nervos CKB testnet using @ckb-lumos/lumos. It initializes the indexer, sets up a collector for the address, and sums the capacities of the collected cells. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/connect-wallet.mdx#_snippet_7 LANGUAGE: tsx CODE: ``` import { Indexer, config, BI, helpers } from '@ckb-lumos/lumos'; const CKB_RPC_URL = 'https://testnet.ckb.dev/rpc'; const CKB_INDEXER_URL = 'https://testnet.ckb.dev/indexer'; const indexer = new Indexer(CKB_INDEXER_URL, CKB_RPC_URL); export async function getCapacities(address: string): Promise { config.initializeConfig(config.predefined.AGGRON4); const collector = indexer.collector({ lock: helpers.parseAddress(address), type: 'empty', data: '0x', }); let capacities = BI.from(0); for await (const cell of collector.collect()) { capacities = capacities.add(cell.cellOutput.capacity); } return capacities; } ``` ---------------------------------------- TITLE: Encoding DOB DNA Formats (JavaScript) DESCRIPTION: Provides examples of how different DNA formats (string, array, object property) can be encoded into byte groups using the `bytifyRawString` helper function from `@spore-sdk/helpers/buffer` for storage in the DOB content field. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/dob/dob0-protocol.md#_snippet_3 LANGUAGE: javascript CODE: ``` bytifyRawString(JSON.stringify("df4ffcb5e7a283ea7e6f09a504d0e256")) // or bytifyRawString(JSON.stringify(["df4ffcb5e7a283ea7e6f09a504d0e256"])) // or bytifyRawString(JSON.stringify({ "dna": "df4ffcb5e7a283ea7e6f09a504d0e256" })) // or [0, 223, 79, 252, 181, 231, 162, 131, 234, 126, 111, 9, 165, 4, 208, 226, 86] ``` ---------------------------------------- TITLE: Implement CKB Transaction Signing (TypeScript) DESCRIPTION: Define an asynchronous function `signTransaction` that takes a Lumos transaction skeleton, prepares signing entries, signs messages using wagmi, handles ECDSA recovery ID, and packs the signature into the witness for CKB transactions. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/create-new-site.mdx#_snippet_4 LANGUAGE: tsx CODE: ``` import { commons, config, helpers } from '@ckb-lumos/lumos'; import { blockchain } from '@ckb-lumos/base'; import { bytes } from '@ckb-lumos/codec'; import { signMessage } from 'wagmi/actions'; export async function signTransaction( txSkeleton: helpers.TransactionSkeletonType, ) { config.initializeConfig(config.predefined.AGGRON4); let tx = commons.omnilock.prepareSigningEntries(txSkeleton); const signedWitnesses = new Map(); const signingEntries = tx.get('signingEntries')!; for (let i = 0; i < signingEntries.size; i += 1) { const entry = signingEntries.get(i)!; if (entry.type === 'witness_args_lock') { const { message, index } = entry; if (signedWitnesses.has(message)) { const signedWitness = signedWitnesses.get(message)!; tx = tx.update('witnesses', (witnesses) => { return witnesses.set(index, signedWitness); }); continue; } let signature = await signMessage({ message: { raw: message } as any }); // Fix ECDSA recoveryId v parameter // let v = Number.parseInt(signature.slice(-2), 16); if (v >= 27) v -= 27; signature = ('0x' + signature.slice(2, -2) + v.toString(16).padStart(2, '0')) as `0x${string}`; const signedWitness = bytes.hexify( blockchain.WitnessArgs.pack({ lock: commons.omnilock.OmnilockWitnessLock.pack({ signature: bytes.bytify(signature!).buffer, }), }), ); signedWitnesses.set(message, signedWitness); tx = tx.update('witnesses', (witnesses) => { return witnesses.set(index, signedWitness); }); } } const signedTx = helpers.createTransactionFromSkeleton(tx); return signedTx; } ``` ---------------------------------------- TITLE: Configure DOB Pattern with Code Hash Decoders (Simplified) DESCRIPTION: Provides an alternative configuration for the DOB pattern, focusing on using `code_hash` for both decoders. This version is presented without comments and is intended for documentation purposes, demonstrating a simpler setup for defining data fields and their rendering rules. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/dob/dob1-protocol.md#_snippet_1 LANGUAGE: typescript CODE: ``` {\n description: "Collection for docs.spore.pro",\n dob: {\n ver: 1,\n decoders: [\n {\n decoder: {\n type: "code_hash",\n hash: "0x13cac78ad8482202f18f9df4ea707611c35f994375fa03ae79121312dda9925c"\n },\n pattern: [\n [\n "Face",\n "String",\n 0,\n 1,\n "options",\n [\n "Laugh",\n "Smile",\n "Sad",\n "Angry"\n ]\n ],\n [\n "Age",\n "Number",\n 1,\n 1,\n "range",\n [\n 0,\n 100\n ]\n ],\n [\n "BirthMonth",\n "Number",\n 2,\n 1,\n "options",\n [\n 1,\n 2,\n 3,\n 4,\n 5,\n 6,\n 7,\n 8,\n 9,\n 10,\n 11,\n 12\n ]\n ],\n [\n "Score",\n "Number",\n 3,\n 1,\n "rawNumber"\n ]\n ]\n },\n {\n decoder: {\n type: "code_hash",\n hash: "0xda3525549b72970b4c95f5b5749357f20d1293d335710b674f09c32f7d54b6dc"\n },\n pattern: [\n [\n "IMAGE.0",\n "attributes",\n "",\n "raw",\n "xmlns='http://www.w3.org/2000/svg' viewBox='0 0 500 500'"\n ],\n [\n "IMAGE.0",\n "attributes",\n "Face",\n "options",\n [\n [\n "Laugh",\n "fill='#FFFF00'"\n ],\n [\n "Smile",\n "fill='#FF00FF'"\n ],\n [\n "Sad",\n ``` ---------------------------------------- TITLE: Querying and Displaying User Sites (React/TypeScript) DESCRIPTION: Defines a TypeScript type Site for structuring site data. Adds state to store an array of Site objects. Implements a useEffect hook that fetches "Cluster" cells associated with the connected wallet's lock script using the CKB indexer and Spore SDK, updates the sites state, and renders a list of site names. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/create-new-site.mdx#_snippet_9 LANGUAGE: tsx CODE: ``` import { Indexer, RPC } from '@ckb-lumos/lumos'; import { useEffect, useState } from 'react'; import { createCluster, unpackToRawClusterData, getSporeScript } from '@spore-sdk/core'; import { signTransaction } from '@/utils/transaction'; import useWallet from '@/hooks/useWallet'; import { config } from '@/config'; export type Site = { id: string; name: string; description: string; }; export default function Home() { const { address, lock, balance, isConnected, connect, disconnect } = useWallet(); const [siteName, setSiteName] = useState(''); const [siteDescription, setSiteDescription] = useState(''); const [sites, setSites] = useState([]); // ... useEffect(() => { if (!lock) { return; } (async () => { const indexer = new Indexer(config.ckbIndexerUrl); const { script } = getSporeScript(config, 'Cluster'); const collector = indexer.collector({ type: { ...script, args: '0x' }, lock, }); const sites = []; for await (const cell of collector.collect()) { const unpacked = unpackToRawClusterData(cell.data); sites.push({ id: cell.cellOutput.type!.args, name: unpacked.name, description: unpacked.description, }); } setSites(sites); })(); }, [lock]); // ... return (
CKB Address: {address}
Balance: {balance?.toNumber() ?? 0} CKB

Create Site

setSiteName(e.target.value)} />
setSiteDescription(e.target.value)} />

My Sites

    {sites.map((site) => (
  • {site.name}
  • ))}
); } ``` ---------------------------------------- TITLE: Spore Post Content JSON Structure DESCRIPTION: This snippet provides an example of the JSON structure used to store the title and content of a blog post within the Spore cell's `content` field. The `contentType` is set to 'application/json' to indicate this format, allowing the data to be parsed correctly when retrieved from the blockchain. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/manage-blog-post.mdx#_snippet_2 LANGUAGE: JSON CODE: ``` { "title": "My post", "content": "Hello World" } ``` ---------------------------------------- TITLE: Using useWallet Hook in Index Page Component (TSX) DESCRIPTION: Utilizes the `useWallet` hook within the `Home` functional component to access the connected wallet's address, lock script, balance, connection status, and connection/disconnection functions. It replaces previous inline wallet logic. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/create-new-site.mdx#_snippet_2 LANGUAGE: TSX CODE: ``` // highlight-start import { Indexer, RPC } from '@ckb-lumos/lumos'; import { useEffect, useState } from 'react'; // highlight-end import useWallet from '@/hooks/useWallet'; export default function Home() { // highlight-start const { address, lock, balance, isConnected, connect, disconnect } = useWallet(); // highlight-end } ``` ---------------------------------------- TITLE: Refuel Spore Zero-fee Transfer using transferSpore (TSX) DESCRIPTION: Demonstrates how to use the transferSpore function from @spore-sdk/core to add capacity margin to a Spore. This effectively 'refuels' the Zero-fee Transfer feature, allowing the spore to cover more future transaction fees. The example shows setting a capacity margin and disabling the use of the existing margin for the current transaction fee. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/recipes/Transfer/modify-0-fee-transfer.md#_snippet_0 LANGUAGE: TSX CODE: ``` import { transferSpore } from '@spore-sdk/core'; import { BI } from '@ckb-lumos/lumos'; let { txSkeleton } = await transferSpore({ outPoint: SPORE_OUTPOINT, toLock: OWNER_LOCK, fromInfos: [SPONSOR_ADDRESS], // highlight-start capacityMargin: BI.from(1_0000_0000), // Add 1 CKB as margin, default is 0 useCapacityMarginAsFee: false, // Disable the feature // highlight-end }); ``` ---------------------------------------- TITLE: Serve Production Build Locally DESCRIPTION: After building the production version, use this command to run a local server and preview the production build. This is useful for testing features that behave differently in production. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/README.md#_snippet_3 LANGUAGE: shell CODE: ``` pnpm run serve ``` ---------------------------------------- TITLE: Implementing Wallet Connection Hook with Lumos and Wagmi (TSX) DESCRIPTION: Defines a React hook `useWallet` to manage wallet connection state, retrieve the connected Ethereum address, derive the corresponding CKB lock script and address using Lumos, fetch the CKB balance, and provide functions for connecting and disconnecting the wallet. It depends on `@ckb-lumos/lumos`, `react`, `wagmi`, and a local `getCapacities` utility. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/create-new-site.mdx#_snippet_0 LANGUAGE: TSX CODE: ``` import { BI, commons, config, helpers } from '@ckb-lumos/lumos'; import { useEffect, useMemo, useState } from 'react'; import { useAccount, useConnect, useDisconnect } from 'wagmi'; import { InjectedConnector } from 'wagmi/connectors/injected'; import { getCapacities } from '@/utils/balance'; export default function useWallet() { const { address: ethAddress, isConnected } = useAccount(); const { connect } = useConnect({ connector: new InjectedConnector(), }); const { disconnect } = useDisconnect(); const [balance, setBalance] = useState(null); const lock = useMemo(() => { if (!ethAddress) return; return commons.omnilock.createOmnilockScript( { auth: { flag: 'ETHEREUM', content: ethAddress ?? '0x' }, }, { config: config.predefined.AGGRON4 }, ); }, [ethAddress]); const address = useMemo( () => lock ? helpers.encodeToAddress(lock, { config: config.predefined.AGGRON4, }) : undefined, [lock], ); useEffect(() => { if (!address) { return; } getCapacities(address).then((capacities) => { setBalance(capacities.div(10 ** 8)); }); }, [address]); return { address, lock, balance, isConnected, connect, disconnect, }; } ``` ---------------------------------------- TITLE: Export Spore Testnet Configuration (TypeScript) DESCRIPTION: Initialize and export the predefined Spore Testnet configuration from `@spore-sdk/core`. This configuration includes Lumos settings and is used globally for Spore interactions. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/create-new-site.mdx#_snippet_5 LANGUAGE: ts CODE: ``` import { predefinedSporeConfigs, setSporeConfig } from '@spore-sdk/core'; import { config as lumosConfig } from '@ckb-lumos/lumos'; const config = predefinedSporeConfigs.Testnet; lumosConfig.initializeConfig(config.lumos); setSporeConfig(config); export { config, }; ``` ---------------------------------------- TITLE: Connecting and Displaying MetaMask Address (TypeScript/TSX) DESCRIPTION: Add this code to your `src/pages/index.tsx` to implement the wallet connection logic. It uses Wagmi hooks to connect to MetaMask via `InjectedConnector`, displays the connected ETH address, and provides buttons for connecting and disconnecting. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/connect-wallet.mdx#_snippet_2 LANGUAGE: TypeScript CODE: ``` import { useAccount, useConnect, useDisconnect } from 'wagmi'; import { InjectedConnector } from 'wagmi/connectors/injected'; export default function Home() { const { address, isConnected } = useAccount(); const { connect } = useConnect({ connector: new InjectedConnector(), }); const { disconnect } = useDisconnect(); return (
{address}
{isConnected ? ( ) : ( )}
); } ``` ---------------------------------------- TITLE: Build Spore Docs for Production DESCRIPTION: This command builds the documentation site for production deployment. It compiles and optimizes the site assets, preparing them for serving. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/README.md#_snippet_2 LANGUAGE: shell CODE: ``` pnpm run build ``` ---------------------------------------- TITLE: Handle Spore Cluster Creation (TypeScript) DESCRIPTION: Implement an asynchronous function `handleCreateSite` that uses `createCluster` to build a transaction skeleton for creating a Spore Cluster, signs it using the custom `signTransaction` function, and sends the signed transaction to the CKB RPC node. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/create-new-site.mdx#_snippet_7 LANGUAGE: tsx CODE: ``` export default function Home() { // ... // highlight-start const handleCreateSite = async (e: React.FormEvent) => { e.preventDefault(); if (!address || !lock) return; const { txSkeleton } = await createCluster({ data: { name: siteName, description: siteDescription, }, fromInfos: [address], toLock: lock, }); const tx = await signTransaction(txSkeleton); const rpc = new RPC(config.ckbNodeUrl); const hash = await rpc.sendTransaction(tx, 'passthrough'); console.log(hash); }; // highlight-end return (
// ...
); } ``` ---------------------------------------- TITLE: Fetching and Displaying Site Info and Posts using Spore SDK DESCRIPTION: This code snippet, located in `src/pages/site/[id].tsx`, fetches site information (Cluster data) and associated blog posts (Spore data) using the CKB Indexer and Spore SDK. It filters Spore by `contentType` ('application/json') and `clusterId`, parses the content as JSON to extract title and content, and updates the component's state to display the site details and a list of post titles as links. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/manage-blog-post.mdx#_snippet_3 LANGUAGE: TSX CODE: ``` import useWallet from '@/hooks/useWallet'; import { Indexer } from '@ckb-lumos/lumos'; // highlight-start import { getSporeScript, bufferToRawString, unpackToRawClusterData, unpackToRawSporeData, } from '@spore-sdk/core'; // highlight-end import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; import { Site } from '..'; import { config } from '@/config'; // highlight-start import Link from 'next/link'; export type Post = { id: string; title: string; content: string; }; // highlight-end export default function SitePage() { const router = useRouter(); const { id } = router.query; const { lock, isConnected, connect } = useWallet(); const [siteInfo, setSiteInfo] = useState(); // highlight-next-line const [posts, setPosts] = useState([]); useEffect(() => { if (!id) { return; } (async () => { const indexer = new Indexer(config.ckbIndexerUrl); const { script } = getSporeScript(config, 'Cluster'); const collector = indexer.collector({ type: { ...script, args: id as string }, }); for await (const cell of collector.collect()) { const unpacked = unpackToRawClusterData(cell.data); setSiteInfo({ id: cell.cellOutput.type!.args, name: unpacked.name, description: unpacked.description, }); } })(); // highlight-start (async () => { const indexer = new Indexer(config.ckbIndexerUrl); const { script } = getSporeScript(config, 'Spore'); const collector = indexer.collector({ type: { ...script, args: '0x' }, lock, }); const posts = []; for await (const cell of collector.collect()) { const unpacked = unpackToRawSporeData(cell.data); const { contentType } = unpacked; if (contentType !== 'application/json' || unpacked.clusterId !== id) { continue; } const { title, content } = JSON.parse(bufferToRawString(unpacked.content)) ?? {}; if (title && content) { posts.push({ id: cell.cellOutput.type!.args, title, content, }); } } setPosts(posts); })(); // highlight-end }, [id, lock]); // ... return (

{siteInfo?.name}

{siteInfo?.description}

{isConnected ? ( ) : ( )} {/* highlight-start */}

Posts

    {posts.map((post) => (
  • {post.title}
  • ))}
{/* highlight-end */}
); } ``` ---------------------------------------- TITLE: Linking to Blog Site Homepage from Index Page (TypeScript) DESCRIPTION: This code modifies the src/pages/index.tsx file to add navigation links to the individual blog site homepages. It imports the Link component from next/link and wraps each site name in the list with a Link component, dynamically setting the href based on the site's unique ID. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/create-new-site.mdx#_snippet_11 LANGUAGE: TypeScript CODE: ``` // ... // highlight-next-line import Link from 'next/link'; // ... export default function Home() { // ... return (
{/* ... */}

My Sites

    {sites.map((site) => (
  • {/* highlight-start */} {site.name} {/* highlight-end */}
  • ))}
); } ``` ---------------------------------------- TITLE: Integrate and Display CKB Balance in React DESCRIPTION: This snippet demonstrates how to use the `getCapacities` function within a React component (`src/pages/index.tsx`) to fetch and display the CKB balance. It uses wagmi to connect a wallet, derives the CKB address from the connected Ethereum address using Omnilock, fetches the balance using `useEffect`, and displays the address and balance (converted from shannons to CKB) on the page. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/connect-wallet.mdx#_snippet_8 LANGUAGE: tsx CODE: ``` import { useAccount, useConnect, useDisconnect } from 'wagmi'; import { InjectedConnector } from 'wagmi/connectors/injected'; // highlight-start import { BI, commons, config, helpers } from '@ckb-lumos/lumos'; import { useEffect, useMemo, useState } from 'react'; import { getCapacities } from '../utils/balance'; // highlight-end export default function Home() { const { address: ethAddress, isConnected } = useAccount(); const { connect } = useConnect({ connector: new InjectedConnector(), }); const { disconnect } = useDisconnect(); // highlight-next-line const [balance, setBalance] = useState(null); const address = useMemo(() => { if (!ethAddress) return; const lock = commons.omnilock.createOmnilockScript({ auth: { flag: 'ETHEREUM', content: ethAddress ?? '0x' }, }); return helpers.encodeToAddress(lock, { config: config.predefined.AGGRON4 }); }, [ethAddress]); // highlight-start useEffect(() => { if (!address) { return; } getCapacities(address).then((capacities) => { setBalance(capacities.div(10 ** 8)); }); }, [address]); return (
{isConnected ? (
CKB Address: {address}
Balance: {balance?.toNumber() ?? 0} CKB
) : ( )}
); // highlight-end } ``` ---------------------------------------- TITLE: Generate CKB Address from ETH Address (Lumos) DESCRIPTION: React/TypeScript code using `wagmi` and `@ckb-lumos/lumos` to connect to an Ethereum wallet, retrieve the ETH address, and use `useMemo` to generate a corresponding CKB address via the Omnilock script. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/connect-wallet.mdx#_snippet_5 LANGUAGE: tsx CODE: ``` import { useAccount, useConnect, useDisconnect } from 'wagmi'; import { InjectedConnector } from 'wagmi/connectors/injected'; // highlight-start import { commons, config, helpers } from '@ckb-lumos/lumos'; import { useMemo } from 'react'; // highlight-end export default function Home() { // highlight-next-line const { address: ethAddress, isConnected } = useAccount(); const { connect } = useConnect({ connector: new InjectedConnector(), }); const { disconnect } = useDisconnect(); // highlight-start const address = useMemo(() => { if (!ethAddress) return; const lock = commons.omnilock.createOmnilockScript({ auth: { flag: 'ETHEREUM', content: ethAddress ?? '0x' }, }); return helpers.encodeToAddress(lock, { config: config.predefined.AGGRON4 }); }, [ethAddress]); // highlight-end return (
{address}
{isConnected ? ( ) : ( )}
); } ``` ---------------------------------------- TITLE: Constructing Spore Creation Transaction (TypeScript) DESCRIPTION: Demonstrates calling the `createSpore` API to construct a transaction skeleton for minting a new Spore. It includes setting the `data` field with `contentType` ('image/jpeg') and `content` (obtained from `fetchLocalFile`), specifying the owner's lock script in `toLock`, and listing sponsor addresses in `fromInfos` to cover storage and fees. The API returns a `txSkeleton` and the `outputIndex` of the new Spore cell. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-first-spore/explore-code.md#_snippet_6 LANGUAGE: TypeScript CODE: ``` const { txSkeleton, outputIndex } = await createSpore({ data: { contentType: 'image/jpeg', content: await fetchLocalFile('./image.jpg'), }, toLock: wallet.lock, fromInfos: [wallet.address], }); ``` ---------------------------------------- TITLE: Creating DOB/0 Cluster with Spore SDK (JavaScript) DESCRIPTION: Demonstrates how to use the Spore SDK `createCluster` function to mint a Spore Cluster configured for the DOB/0 protocol, embedding the DOB metadata JSON into the cluster's description field. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/dob/dob0-protocol.md#_snippet_1 LANGUAGE: jsx CODE: ``` import { createCluster } from '@spore-sdk/api'; import { bytifyRawString } from '@spore-sdk/helpers/buffer'; const account = ...; const dob_metadata = { description: 'this is the description for cluster', dob: { ver: 0, decoder: { type: 'code_hash', hash: '...' }, pattern: [["Age", "Number", 1, 1, "range", [0, 100]]], } }; const { txSkeleton, outputIndex } = await createCluster({ data: { name: 'My First DOB Cluster', description: bytifyRawString(JSON.strinify(dob_metadata)), }, fromInfos: [account.address], toLock: account.lock }); // sign for txSkeleton ``` ---------------------------------------- TITLE: Display Generated CKB Address DESCRIPTION: Updates the React component to display the generated CKB address instead of the connection button when the wallet is connected, showing the result of the address generation. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/connect-wallet.mdx#_snippet_6 LANGUAGE: tsx CODE: ``` import { useAccount, useConnect, useDisconnect } from 'wagmi'; import { InjectedConnector } from 'wagmi/connectors/injected'; import { commons, config, helpers } from '@ckb-lumos/lumos'; import { useMemo } from 'react'; export default function Home() { const { address: ethAddress, isConnected } = useAccount(); const { connect } = useConnect({ connector: new InjectedConnector(), }); const { disconnect } = useDisconnect(); const address = useMemo(() => { if (!ethAddress) return; const lock = commons.omnilock.createOmnilockScript({ auth: { flag: 'ETHEREUM', content: ethAddress ?? '0x' }, }); return helpers.encodeToAddress(lock, { config: config.predefined.AGGRON4 }); }, [ethAddress]); // highlight-start return (
{isConnected ? (
CKB Address: {address}
) : ( )}
); } // highlight-end ``` ---------------------------------------- TITLE: Minting DOB with Spore SDK (JavaScript) DESCRIPTION: Illustrates the process of minting a Spore (acting as a DOB) using the Spore SDK `createSpore` function, specifying the 'dob/0' content type, embedding the DNA in the content field, and linking it to a pre-configured DOB cluster. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/dob/dob0-protocol.md#_snippet_4 LANGUAGE: jsx CODE: ``` import { createSpore } from '@spore-sdk/api'; import { bytifyRawString } from '@spore-sdk/helpers/buffer'; const account = ...; const dob_cluster_id = ...; const dob_content = { dna: 'df4ffcb5e7a283ea7e6f09a504d0e256' }; const { txSkeleton, outputIndex } = await createSpore({ data: { content_type: 'dob/0', content: bytifyRawString(JSON.strinify(dob_content)), cluster_id: dob_cluster_id }, fromInfos: [account.address], toLock: account.lock }); // sign for txSkeleton ``` ---------------------------------------- TITLE: Create Next.js Page to Display Spore Post (TypeScript/React) DESCRIPTION: Implements a Next.js dynamic route page ([id].tsx) that fetches a Spore Cell by its ID from the URL, unpacks and parses its JSON content (title, content), and renders the title and Markdown content using react-remark. It uses Lumos Indexer to find the cell. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/manage-blog-post.mdx#_snippet_5 LANGUAGE: typescript CODE: ``` import { Indexer } from '@ckb-lumos/lumos'; import { SporeData, bufferToRawString, getSporeScript, } from '@spore-sdk/core'; import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; import { useRemark } from 'react-remark'; import { Post } from '../site/[id]'; import { config } from '@/config'; export default function Post() { const router = useRouter(); const { id } = router.query; const [post, setPost] = useState(); const [reactContent, setMarkdownSource] = useRemark(); useEffect(() => { if (!id) { return; } (async () => { const indexer = new Indexer(config.ckbIndexerUrl); const { script } = getSporeScript(config, 'Spore'); const collector = indexer.collector({ type: { ...script, args: id as string }, }); for await (const cell of collector.collect()) { const unpacked = SporeData.unpack(cell.data); const { title, content } = JSON.parse(bufferToRawString(unpacked.content)) ?? {}; if (title && content) { setPost({ id: cell.cellOutput.type!.args, outPoint: cell.outPoint!, title, content, }); return; } } })(); }, [id]); useEffect(() => { setMarkdownSource(post?.content ?? ''); }, [post, setMarkdownSource]); return (

{post?.title}

{reactContent}
); } ``` ---------------------------------------- TITLE: Importing Spore SDK and Lumos Dependencies DESCRIPTION: Imports necessary modules from `@spore-sdk/core` for cluster creation, `@ckb-lumos/codec` for data encoding, and `@ckb-lumos/lumos` for key derivation and address encoding. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/recipes/Create/create-public-cluster.mdx#_snippet_6 LANGUAGE: TypeScript CODE: ``` import { predefinedSporeConfig, createCluster } from '@spore-sdk/core'; import { bytes, number } from '@ckb-lumos/codec'; import { hd, helpers } from '@ckb-lumos/lumos; ``` ---------------------------------------- TITLE: Implementing Spore Post Deletion Logic (TSX) DESCRIPTION: This snippet implements the `handlePostDelete` asynchronous function. It finds the post by ID, retrieves its `outPoint`, uses `meltSpore` from the Spore SDK to create a transaction skeleton for deleting the Spore cell, signs the transaction, sends it to the CKB network, and then refetches the posts after a short delay. It also includes necessary imports and updates the `Post` type to include `outPoint`. SOURCE: https://github.com/sporeprotocol/spore-docs/blob/main/docs/tutorials/create-on-chain-blog/manage-blog-post.mdx#_snippet_7 LANGUAGE: TSX CODE: ``` import useWallet from '@/hooks/useWallet'; import { Indexer, OutPoint, RPC } from '@ckb-lumos/lumos'; import { meltSpore, getSporeScript, bufferToRawString, unpackToRawClusterData, unpackToRawSporeData, } from '@spore-sdk/core'; import { useRouter } from 'next/router'; import { useCallback, useEffect, useState } from 'react'; import { Site } from '..'; import { config } from '@/config'; import Link from 'next/link'; import { signTransaction } from '@/utils/transaction'; export type Post = { id: string; title: string; content: string; outPoint: OutPoint; }; export default function SitePage() { const router = useRouter(); const { id } = router.query; const { lock, isConnected, connect } = useWallet(); const [siteInfo, setSiteInfo] = useState(); const [posts, setPosts] = useState([]); const { address } = useWallet(); const fetchPosts = useCallback(async () => { const indexer = new Indexer(config.ckbIndexerUrl); const { script } = getSporeScript(config, 'Spore'); const collector = indexer.collector({ type: { ...script, args: '0x' }, lock, }); const posts = []; for await (const cell of collector.collect()) { const unpacked = unpackToRawSporeData(cell.data); const { contentType } = unpacked; if (contentType !== 'application/json' || unpacked.clusterId !== id) { continue; } const { title, content } = JSON.parse(bufferToRawString(unpacked.content)) ?? {}; if (title && content) { posts.push({ id: cell.cellOutput.type!.args, title, content, outPoint: cell.outPoint!, }); } } setPosts(posts); }, [id, lock]); useEffect(() => { if (!id) { return; } (async () => { const indexer = new Indexer(config.ckbIndexerUrl); const { script } = getSporeScript(config, 'Cluster'); const collector = indexer.collector({ type: { ...script, args: id as string }, }); for await (const cell of collector.collect()) { const unpacked = unpackToRawClusterData(cell.data); setSiteInfo({ id: cell.cellOutput.type!.args, name: unpacked.name, description: unpacked.description, }); } })(); fetchPosts(); }, [id, lock, fetchPosts]); const handlePostDelete = async (id: string) => { if (!address) return; const post = posts.find((post) => post.id === id); if (!post) return; const { txSkeleton } = await meltSpore({ outPoint: post.outPoint, fromInfos: [address], }); const tx = await signTransaction(txSkeleton); const rpc = new RPC(config.ckbNodeUrl); const hash = await rpc.sendTransaction(tx, 'passthrough'); setTimeout(() => fetchPosts(), 1000); console.log(hash); }; return ... } ```