UNPKG

bigblocks

Version:

Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React

17 lines 8.29 kB
{ "name": "social-utils-protocol", "type": "registry:component", "dependencies": [ "@bsv/sdk" ], "devDependencies": [], "registryDependencies": [], "files": [ { "path": "components/social/utils/protocol.ts", "type": "registry:component", "content": "// Protocol Utilities - Bitcoin Social Transaction Builders\nimport { Hash, type PrivateKey, Script, Utils } from \"@bsv/sdk\";\nimport {\n\ttype AIPProtocolBuilder,\n\ttype BProtocolBuilder,\n\tDEFAULT_FILENAMES,\n\ttype MAPProtocolBuilder,\n\tPROTOCOL_IDS,\n\ttype ProtocolDataArray,\n\ttype SignedProtocolData,\n\ttype SocialTransactionInput,\n} from \"../types/protocol.js\";\nimport type { ContentType, SocialActionType } from \"../types/social.js\";\n\n// Build B Protocol Data (content layer)\nexport function buildBProtocol(builder: BProtocolBuilder): ProtocolDataArray {\n\tconst { content, contentType, encoding = \"utf-8\", filename } = builder;\n\n\tconst fileName = filename || DEFAULT_FILENAMES[contentType] || \"content.txt\";\n\n\treturn [PROTOCOL_IDS.B, content, contentType, encoding, fileName];\n}\n\n// Build MAP Protocol Data (metadata layer)\nexport function buildMAPProtocol(\n\tbuilder: MAPProtocolBuilder,\n): ProtocolDataArray {\n\tconst { app, type, context, metadata = {} } = builder;\n\n\tconst mapData: string[] = [PROTOCOL_IDS.MAP, \"SET\", \"app\", app, \"type\", type];\n\n\t// Add context if provided\n\tif (context) {\n\t\tmapData.push(\n\t\t\t\"context\",\n\t\t\tcontext.type,\n\t\t\tcontext.type, // Key and value type are the same\n\t\t\tcontext.value,\n\t\t);\n\t}\n\n\t// Add additional metadata\n\tfor (const [key, value] of Object.entries(metadata)) {\n\t\tmapData.push(key, value);\n\t}\n\n\treturn mapData;\n}\n\n// Build complete social transaction data\nexport function buildSocialTransaction(\n\tinput: SocialTransactionInput,\n): ProtocolDataArray {\n\tconst {\n\t\ttype,\n\t\tcontent,\n\t\tcontentType = \"text/plain\",\n\t\tcontext,\n\t\tmetadata,\n\t\tconfig,\n\t} = input;\n\n\tconst dataArrays: ProtocolDataArray[] = [];\n\n\t// Add B protocol if content provided\n\tif (content) {\n\t\tconst bData = buildBProtocol({\n\t\t\tcontent,\n\t\t\tcontentType,\n\t\t});\n\t\tdataArrays.push(bData);\n\t}\n\n\t// Add MAP protocol\n\tconst mapData = buildMAPProtocol({\n\t\tapp: config.app,\n\t\ttype,\n\t\tcontext,\n\t\tmetadata,\n\t});\n\tdataArrays.push(mapData);\n\n\t// Combine with pipe separator\n\tconst combined: string[] = [];\n\tfor (let i = 0; i < dataArrays.length; i++) {\n\t\tconst dataArray = dataArrays[i];\n\t\tif (dataArray) {\n\t\t\tcombined.push(...dataArray);\n\t\t\tif (i < dataArrays.length - 1) {\n\t\t\t\tcombined.push(PROTOCOL_IDS.PIPE);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn combined;\n}\n\n// Sign transaction data with AIP (Authentication & Identity Protocol)\nexport function signWithAIP(builder: AIPProtocolBuilder): SignedProtocolData {\n\tconst { dataToSign, signingKey, address } = builder;\n\n\t// Convert data array to number arrays for signing\n\tconst dataArrays = dataToSign.map((item) => Utils.toArray(item, \"utf8\"));\n\n\t// Combine all data arrays into one\n\tconst combinedData: number[] = [];\n\tfor (const arr of dataArrays) {\n\t\tcombinedData.push(...arr);\n\t}\n\n\t// Sign the hash of the data\n\tconst hashBytes = Hash.sha256(combinedData);\n\tconst signatureResult = signingKey.sign(hashBytes).toString(\"hex\");\n\t// Ensure signature is a string\n\tconst signature =\n\t\ttypeof signatureResult === \"string\"\n\t\t\t? signatureResult\n\t\t\t: Utils.toHex(signatureResult);\n\n\t// Build AIP data\n\tconst aipData: string[] = [\n\t\tPROTOCOL_IDS.AIP,\n\t\t\"BITCOIN_ECDSA\",\n\t\taddress,\n\t\tsignature,\n\t];\n\n\t// Combine original data with AIP signature\n\tconst signedData = [...dataToSign, PROTOCOL_IDS.PIPE, ...aipData];\n\n\treturn {\n\t\tdata: signedData,\n\t\tsignature: signature as string,\n\t\taddress,\n\t\talgorithm: \"BITCOIN_ECDSA\",\n\t} as SignedProtocolData;\n}\n\n// Build OP_RETURN script from signed data\nexport function buildOpReturnScript(signedData: SignedProtocolData): Script {\n\tconst dataArrays = signedData.data.map((item) => Utils.toArray(item, \"utf8\"));\n\n\tconst chunks: Array<{ op: number; data?: number[] }> = [\n\t\t{ op: 0 }, // OP_0 (OP_FALSE)\n\t\t{ op: 106 }, // OP_RETURN\n\t];\n\n\t// Add data chunks with proper op codes\n\tfor (const data of dataArrays) {\n\t\tif (data.length < 76) {\n\t\t\t// For data less than 76 bytes, the op code is the length\n\t\t\tchunks.push({ op: data.length, data });\n\t\t} else {\n\t\t\t// For larger data, we need OP_PUSHDATA opcodes\n\t\t\t// This is a simplified version - real implementation would handle OP_PUSHDATA1/2/4\n\t\t\tchunks.push({ op: 76, data }); // OP_PUSHDATA1\n\t\t}\n\t}\n\n\treturn new Script(chunks);\n}\n\n// Helper function to create specific social action transactions\nexport function createSocialActionTransaction(\n\ttype: SocialActionType,\n\tsigningKey: PrivateKey,\n\toptions: {\n\t\tcontent?: string;\n\t\tcontentType?: ContentType;\n\t\ttargetTxid?: string;\n\t\ttargetIdKey?: string;\n\t\temoji?: string;\n\t\tapp: string;\n\t\tmetadata?: Record<string, string>;\n\t},\n): SignedProtocolData {\n\tconst {\n\t\tcontent,\n\t\tcontentType,\n\t\ttargetTxid,\n\t\ttargetIdKey,\n\t\temoji,\n\t\tapp,\n\t\tmetadata = {},\n\t} = options;\n\n\t// Determine context based on action type and targets\n\tlet context = undefined;\n\tif (targetTxid) {\n\t\tcontext = { type: \"tx\" as const, value: targetTxid };\n\t} else if (targetIdKey) {\n\t\tcontext = { type: \"bapID\" as const, value: targetIdKey }; // Note: keeping 'bapID' for protocol\n\t}\n\n\t// Add emoji to metadata if provided (for reactions)\n\tif (emoji) {\n\t\tmetadata.emoji = emoji;\n\t}\n\n\t// Build transaction data\n\tconst transactionData = buildSocialTransaction({\n\t\ttype,\n\t\tcontent,\n\t\tcontentType,\n\t\tcontext,\n\t\tmetadata,\n\t\tsigningKey,\n\t\tconfig: { app },\n\t});\n\n\t// Sign with AIP\n\treturn signWithAIP({\n\t\tdataToSign: transactionData,\n\t\tsigningKey,\n\t\taddress: signingKey.toAddress().toString(),\n\t});\n}\n\n// Specific helper functions for common social actions\nexport const SocialActions = {\n\tpost: (\n\t\tcontent: string,\n\t\tsigningKey: PrivateKey,\n\t\tapp: string,\n\t\tcontentType: ContentType = \"text/plain\",\n\t) =>\n\t\tcreateSocialActionTransaction(\"post\", signingKey, {\n\t\t\tcontent,\n\t\t\tcontentType,\n\t\t\tapp,\n\t\t}),\n\n\treply: (\n\t\tcontent: string,\n\t\tparentTxid: string,\n\t\tsigningKey: PrivateKey,\n\t\tapp: string,\n\t\tcontentType: ContentType = \"text/plain\",\n\t) =>\n\t\tcreateSocialActionTransaction(\"reply\", signingKey, {\n\t\t\tcontent,\n\t\t\tcontentType,\n\t\t\ttargetTxid: parentTxid,\n\t\t\tapp,\n\t\t}),\n\n\tlike: (txid: string, signingKey: PrivateKey, app: string, emoji = \"\") =>\n\t\tcreateSocialActionTransaction(\"like\", signingKey, {\n\t\t\ttargetTxid: txid,\n\t\t\temoji,\n\t\t\tapp,\n\t\t}),\n\n\tunlike: (txid: string, signingKey: PrivateKey, app: string, emoji = \"\") =>\n\t\tcreateSocialActionTransaction(\"unlike\", signingKey, {\n\t\t\ttargetTxid: txid,\n\t\t\temoji,\n\t\t\tapp,\n\t\t}),\n\n\tfollow: (idKey: string, signingKey: PrivateKey, app: string) =>\n\t\tcreateSocialActionTransaction(\"follow\", signingKey, {\n\t\t\ttargetIdKey: idKey,\n\t\t\tapp,\n\t\t}),\n\n\tunfollow: (idKey: string, signingKey: PrivateKey, app: string) =>\n\t\tcreateSocialActionTransaction(\"unfollow\", signingKey, {\n\t\t\ttargetIdKey: idKey,\n\t\t\tapp,\n\t\t}),\n\n\tfriend: (idKey: string, signingKey: PrivateKey, app: string) =>\n\t\tcreateSocialActionTransaction(\"friend\", signingKey, {\n\t\t\ttargetIdKey: idKey,\n\t\t\tapp,\n\t\t}),\n\n\tunfriend: (idKey: string, signingKey: PrivateKey, app: string) =>\n\t\tcreateSocialActionTransaction(\"unfriend\", signingKey, {\n\t\t\ttargetIdKey: idKey,\n\t\t\tapp,\n\t\t}),\n\n\tmessage: (\n\t\tcontent: string,\n\t\trecipient: string,\n\t\tsigningKey: PrivateKey,\n\t\tapp: string,\n\t\tencrypted = true,\n\t) =>\n\t\tcreateSocialActionTransaction(\"message\", signingKey, {\n\t\t\tcontent,\n\t\t\ttargetIdKey: recipient,\n\t\t\tapp,\n\t\t\tmetadata: { encrypted: encrypted.toString() },\n\t\t}),\n};\n", "target": "<%- config.aliases.components %>/protocol.tsx" } ] }