├── github ├── FUNDING.yml └── main │ └── content │ └── sponsors │ ├── utxo-dark-mode.svg │ └── sat-ventures-dark-mode.svg ├── example ├── README.md ├── .eslintrc.json ├── lib │ ├── const.ts │ ├── utils.d.ts │ ├── db.ts │ ├── utils.d.ts.map │ ├── github.ts │ ├── utils.ts │ ├── utils.js │ ├── urls.ts │ └── btc.ts ├── app │ ├── favicon.ico │ ├── PxPlus_IBM_VGA8.ttf │ ├── PxPlus_IBM_VGA8.woff │ ├── Windows_Command.ttf │ ├── Windows_Regular.ttf │ ├── PxPlus_IBM_VGA8.woff2 │ ├── page.tsx │ ├── layout.tsx │ ├── api │ │ ├── authorize │ │ │ └── route.ts │ │ └── submit-poll │ │ │ └── route.ts │ └── globals.css ├── .env.example ├── .prettierrc.json ├── next.config.mjs ├── postcss.config.mjs ├── tailwind.config.d.ts.map ├── components │ ├── ui │ │ ├── sonner.d.ts │ │ ├── sonner.d.ts.map │ │ ├── input.tsx │ │ ├── sonner.tsx │ │ ├── checkbox.tsx │ │ ├── tooltip.tsx │ │ ├── badge.tsx │ │ ├── sonner.js │ │ ├── avatar.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── select.tsx │ │ └── chart.tsx │ ├── ClickToCopy.tsx │ ├── PollResults.tsx │ ├── ClickToCopyNpmInstallPill.tsx │ ├── PollCard.tsx │ └── WalletCard.tsx ├── components.json ├── .gitignore ├── public │ ├── vercel.svg │ ├── next.svg │ ├── lasereyes_connected.svg │ └── lasereyes_disconnected.svg ├── types │ └── btc.ts ├── tsconfig.json ├── package.json ├── hooks │ └── useUtxos.ts ├── tailwind.config.d.ts ├── tailwind.config.ts └── tailwind.config.js ├── lasereyes.png ├── src ├── .DS_Store ├── consts │ ├── inscribe.ts │ ├── settings.ts │ ├── wallets.ts │ └── networks.ts ├── config │ └── createConfig.ts ├── index.tsx ├── icons │ ├── index.tsx │ ├── okx.tsx │ ├── xverse.tsx │ ├── oyl.tsx │ ├── walletIcon.tsx │ ├── wizz.tsx │ ├── leather.tsx │ ├── phantom.tsx │ ├── magiceden.tsx │ ├── unisat.tsx │ └── orange.tsx ├── lib │ ├── urls.ts │ └── helpers.ts └── types │ └── index.ts ├── .github └── FUNDING.yml ├── .gitignore ├── README.md ├── tsconfig.json ├── postprocess.js ├── LICENSE ├── package.json └── dist └── index.d.ts /github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Laser Eyes Demo 2 | -------------------------------------------------------------------------------- /example/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /lasereyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnisat/lasereyes/HEAD/lasereyes.png -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnisat/lasereyes/HEAD/src/.DS_Store -------------------------------------------------------------------------------- /example/lib/const.ts: -------------------------------------------------------------------------------- 1 | export const BTC_MESSAGE_TO_SIGN = 'Laser Eyes - Test Message' 2 | -------------------------------------------------------------------------------- /src/consts/inscribe.ts: -------------------------------------------------------------------------------- 1 | export const MIME_TYPE_TEXT = "text/plain;charset=utf-8"; 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [omnisat] 2 | custom: ["https://github.com/sponsors/omnisat/"] 3 | # 4 | -------------------------------------------------------------------------------- /example/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnisat/lasereyes/HEAD/example/app/favicon.ico -------------------------------------------------------------------------------- /example/app/PxPlus_IBM_VGA8.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnisat/lasereyes/HEAD/example/app/PxPlus_IBM_VGA8.ttf -------------------------------------------------------------------------------- /example/app/PxPlus_IBM_VGA8.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnisat/lasereyes/HEAD/example/app/PxPlus_IBM_VGA8.woff -------------------------------------------------------------------------------- /example/app/Windows_Command.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnisat/lasereyes/HEAD/example/app/Windows_Command.ttf -------------------------------------------------------------------------------- /example/app/Windows_Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnisat/lasereyes/HEAD/example/app/Windows_Regular.ttf -------------------------------------------------------------------------------- /example/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_SUPABASE_ANON_KEY= 2 | SERVICE_ROLE= 3 | NEXT_PUBLIC_SUPABASE_URL= 4 | JWT_SECRET= 5 | -------------------------------------------------------------------------------- /example/app/PxPlus_IBM_VGA8.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omnisat/lasereyes/HEAD/example/app/PxPlus_IBM_VGA8.woff2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn.lock 3 | .idea 4 | .DS_Store 5 | src/.DS_Store 6 | example/.env 7 | example/package-lock.json 8 | package-lock.json 9 | -------------------------------------------------------------------------------- /example/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "trailingComma": "es5", 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "useTabs": false 7 | } -------------------------------------------------------------------------------- /example/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | // reactStrictMode: false, 4 | }; 5 | 6 | export default nextConfig; 7 | -------------------------------------------------------------------------------- /src/config/createConfig.ts: -------------------------------------------------------------------------------- 1 | import { Config } from "../types"; 2 | 3 | export const createConfig = (config: Config): Config => { 4 | return { 5 | ...config, 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /example/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /example/lib/utils.d.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue } from 'clsx'; 2 | export declare function cn(...inputs: ClassValue[]): string; 3 | export declare function truncateString(str: string, maxLength: number): string; 4 | //# sourceMappingURL=utils.d.ts.map -------------------------------------------------------------------------------- /example/tailwind.config.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tailwind.config.d.ts","sourceRoot":"","sources":["tailwind.config.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2EM,CAAA;AAElB,eAAe,MAAM,CAAA"} -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | export * from "./consts/networks"; 3 | export * from "./consts/wallets"; 4 | export * from "./config/createConfig"; 5 | export * from "./providers/LaserEyesProvider"; 6 | export * from "./lib/helpers"; 7 | export * from "./icons"; 8 | -------------------------------------------------------------------------------- /example/lib/db.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from '@supabase/supabase-js' 2 | 3 | const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL 4 | const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY 5 | 6 | export const supabase = createClient(String(supabaseUrl), String(supabaseKey)) 7 | -------------------------------------------------------------------------------- /example/lib/utils.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAQ,MAAM,MAAM,CAAA;AAG5C,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAQrE"} -------------------------------------------------------------------------------- /example/components/ui/sonner.d.ts: -------------------------------------------------------------------------------- 1 | import { Toaster as Sonner } from "sonner"; 2 | type ToasterProps = React.ComponentProps; 3 | declare const Toaster: ({ ...props }: ToasterProps) => import("react/jsx-runtime").JSX.Element; 4 | export { Toaster }; 5 | //# sourceMappingURL=sonner.d.ts.map -------------------------------------------------------------------------------- /src/icons/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./oyl"; 2 | export * from "./leather"; 3 | export * from "./phantom"; 4 | export * from "./xverse"; 5 | export * from "./unisat"; 6 | export * from "./wizz"; 7 | export * from "./okx"; 8 | export * from "./magiceden"; 9 | export * from "./walletIcon"; 10 | -------------------------------------------------------------------------------- /example/components/ui/sonner.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sonner.d.ts","sourceRoot":"","sources":["sonner.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE1C,KAAK,YAAY,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,CAAA;AAEvD,QAAA,MAAM,OAAO,iBAAkB,YAAY,4CAqB1C,CAAA;AAED,OAAO,EAAE,OAAO,EAAE,CAAA"} -------------------------------------------------------------------------------- /example/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 | Laser Eyes 6 | 7 | 8 |

9 | 10 | # lasereyes 11 | 12 | The repo has moved! Please visit the new repo at [laseryes-mono](https://github.com/omnisat/lasereyes-mono) 13 | -------------------------------------------------------------------------------- /example/lib/github.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | const owner = 'omnisat' 4 | const repo = 'lasereyes' 5 | const branch = 'main' 6 | 7 | export const getPackageVersion = async () => { 8 | try { 9 | const response = await axios.get( 10 | `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/package.json` 11 | ) 12 | const packageJson = response.data 13 | return packageJson.version 14 | } catch (error) { 15 | console.error('Error fetching package.json:', error) 16 | return null 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | 8 | export function truncateString(str: string, maxLength: number): string { 9 | if (str?.length <= maxLength) { 10 | return str 11 | } else { 12 | const leftHalf = str?.slice(0, Math.ceil((maxLength - 3) / 2)) 13 | const rightHalf = str?.slice(-Math.floor((maxLength - 3) / 2)) 14 | return leftHalf + '...' + rightHalf 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /example/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/types/btc.ts: -------------------------------------------------------------------------------- 1 | export interface IMempoolUtxo { 2 | txid: string 3 | vout: number 4 | status: { 5 | confirmed: boolean 6 | block_height: number 7 | block_hash: string 8 | block_time: number 9 | } 10 | value: number 11 | } 12 | 13 | export interface BlockchainInfoResponse { 14 | notice: string 15 | unspent_outputs: BlockchainInfoUTXO[] 16 | } 17 | 18 | export interface BlockchainInfoUTXO { 19 | tx_hash_big_endian: string 20 | tx_hash: string 21 | tx_output_n: number 22 | script: string 23 | value: number 24 | value_hex: string 25 | confirmations: number 26 | tx_index: number 27 | } 28 | -------------------------------------------------------------------------------- /example/lib/utils.js: -------------------------------------------------------------------------------- 1 | import { clsx } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | export function cn(...inputs) { 4 | return twMerge(clsx(inputs)); 5 | } 6 | export function truncateString(str, maxLength) { 7 | if ((str === null || str === void 0 ? void 0 : str.length) <= maxLength) { 8 | return str; 9 | } 10 | else { 11 | const leftHalf = str === null || str === void 0 ? void 0 : str.slice(0, Math.ceil((maxLength - 3) / 2)); 12 | const rightHalf = str === null || str === void 0 ? void 0 : str.slice(-Math.floor((maxLength - 3) / 2)); 13 | return leftHalf + '...' + rightHalf; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "NodeNext", 10 | "moduleResolution": "NodeNext", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /example/app/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { 3 | FRACTAL_MAINNET, 4 | FRACTAL_TESTNET, 5 | LaserEyesProvider, 6 | MAINNET, 7 | SIGNET, 8 | TESTNET, 9 | TESTNET4, 10 | } from '@omnisat/lasereyes' 11 | 12 | import App from '@/components/App' 13 | import { useState } from 'react' 14 | 15 | export default function Home() { 16 | const [network, setNetwork] = useState< 17 | | typeof MAINNET 18 | | typeof TESTNET 19 | | typeof TESTNET4 20 | | typeof SIGNET 21 | | typeof FRACTAL_MAINNET 22 | | typeof FRACTAL_TESTNET 23 | >(MAINNET) 24 | return ( 25 | 26 | 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React Library", 4 | "compilerOptions": { 5 | "jsx": "react-jsx", 6 | "lib": ["ES2015", "DOM"], 7 | "module": "ESNext", 8 | "target": "ES6", 9 | "composite": false, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "esModuleInterop": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "inlineSources": false, 15 | "isolatedModules": true, 16 | "moduleResolution": "node", 17 | "noUnusedLocals": false, 18 | "noUnusedParameters": false, 19 | "preserveWatchOutput": true, 20 | "skipLibCheck": true, 21 | "strict": true 22 | }, 23 | "include": ["src/**/*"], 24 | "exclude": ["dist", "build", "node_modules","example"] 25 | } 26 | -------------------------------------------------------------------------------- /example/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import './globals.css' 2 | 3 | import { cn } from '@/lib/utils' 4 | import { ReactNode } from 'react' 5 | import { Toaster } from 'sonner' 6 | import localFont from 'next/font/local' 7 | 8 | const windows = localFont({ 9 | src: './Windows_Regular.ttf', 10 | variable: '--font-windows', 11 | }) 12 | 13 | const pxplus = localFont({ 14 | src: './PxPlus_IBM_VGA8.ttf', 15 | variable: '--font-pxplus', 16 | }) 17 | 18 | export default function RootLayout({ children }: { children: ReactNode }) { 19 | return ( 20 | 21 | 22 | 28 | {children} 29 | 30 | 31 | 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /example/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ) 21 | } 22 | ) 23 | Input.displayName = "Input" 24 | 25 | export { Input } 26 | -------------------------------------------------------------------------------- /postprocess.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | // Define the directory where the transpiled files are located 5 | const distDir = path.join(__dirname, "dist"); 6 | 7 | // Utility function to wrap require with toESM 8 | function wrapRequireWithToESM(content) { 9 | return content.replace( 10 | /(var\s+\w+\s*=\s*)require\((['"])(.*?)\2\)/g, 11 | "$1_toESM(require($2$3$2))" 12 | ); 13 | } 14 | 15 | // Read all files in the dist directory 16 | fs.readdirSync(distDir).forEach((file) => { 17 | const filePath = path.join(distDir, file); 18 | 19 | if (path.extname(file) === ".js") { 20 | // Read the file content 21 | let content = fs.readFileSync(filePath, "utf-8"); 22 | 23 | // Replace require statements with toESM-wrapped requires 24 | content = wrapRequireWithToESM(content); 25 | 26 | // Write the modified content back to the file 27 | fs.writeFileSync(filePath, content, "utf-8"); 28 | 29 | console.log(`Processed file: ${file}`); 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /example/components/ui/sonner.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useTheme } from "next-themes" 4 | import { Toaster as Sonner } from "sonner" 5 | 6 | type ToasterProps = React.ComponentProps 7 | 8 | const Toaster = ({ ...props }: ToasterProps) => { 9 | const { theme = "system" } = useTheme() 10 | 11 | return ( 12 | 28 | ) 29 | } 30 | 31 | export { Toaster } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2024 Laser Eyes 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | The above copyright notice and this permission notice shall be included in all 10 | copies or substantial portions of the Software. 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@omnisat/lasereyes", 3 | "version": "0.0.80", 4 | "sideEffects": false, 5 | "private": false, 6 | "license": "MIT", 7 | "main": "./dist/index.js", 8 | "module": "./dist/index.mjs", 9 | "types": "./dist/index.d.ts", 10 | "files": [ 11 | "dist/**" 12 | ], 13 | "scripts": { 14 | "build": "tsup src/index.tsx --format esm,cjs --dts --external react && node postprocess.js", 15 | "clean": "rimraf dist && rimraf src/**/*.d.ts src/**/*.d.ts.map src/**/*.js", 16 | "dev": "tsup src/index.tsx --format esm,cjs --watch --dts --external react" 17 | }, 18 | "dependencies": { 19 | "@bitcoinerlab/secp256k1": "^1.1.1", 20 | "@changesets/cli": "^2.27.5", 21 | "@orangecrypto/orange-connect": "^1.2.2", 22 | "axios": "^1.7.2", 23 | "bitcoinjs-lib": "^6.1.3", 24 | "react": "^18", 25 | "react-dom": "^18", 26 | "sats-connect": "^2.6.0", 27 | "usehooks-ts": "^2.9.1", 28 | "valibot": "^0.36.0" 29 | }, 30 | "devDependencies": { 31 | "@types/react": "^18", 32 | "@types/react-dom": "^18", 33 | "rimraf": "^6.0.1", 34 | "tsup": "^6.7.0", 35 | "typescript": "^4.9.5" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox" 5 | import { Check } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Checkbox = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, ...props }, ref) => ( 13 | 21 | 24 | 25 | 26 | 27 | )) 28 | Checkbox.displayName = CheckboxPrimitive.Root.displayName 29 | 30 | export { Checkbox } 31 | -------------------------------------------------------------------------------- /example/app/api/authorize/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from 'next/server' 2 | import { Verifier } from 'bip322-js' 3 | import * as jwt from 'jsonwebtoken' 4 | import { BTC_MESSAGE_TO_SIGN } from '@/lib/const' 5 | 6 | const SECRET_KEY = process.env.JWT_SECRET_KEY || 'your-secret-key' // Ensure to set a secret key in your environment variables 7 | 8 | export const POST = async (req: NextRequest) => { 9 | try { 10 | const { address, signature, message } = await req.json() 11 | // 12 | if (!address) { 13 | // @ts-ignore 14 | return NextResponse.json( 15 | { error: 'address and signature are required' }, 16 | { status: 400 } 17 | ) 18 | } 19 | 20 | const verified = Verifier.verifySignature(address, message, signature) 21 | 22 | if (verified) { 23 | const token = jwt.sign({ address }, SECRET_KEY, { expiresIn: '1h' }) 24 | return NextResponse.json({ token }) 25 | } else { 26 | // @ts-ignore 27 | return NextResponse.json({ error: 'not authenticated' }, { status: 401 }) 28 | } 29 | } catch (e) { 30 | console.error(e) 31 | // @ts-ignore 32 | return NextResponse.json({ error: e.message }, { status: 400 }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/urls.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FRACTAL_MAINNET, 3 | FRACTAL_TESTNET, 4 | MAINNET, 5 | SIGNET, 6 | TESTNET, 7 | TESTNET4, 8 | } from "../consts/networks"; 9 | 10 | export const MEMPOOL_SPACE_URL = "https://mempool.space"; 11 | export const MEMPOOL_SPACE_TESTNET_URL = "https://mempool.space/testnet"; 12 | export const MEMPOOL_SPACE_TESTNET4_URL = "https://mempool.space/testnet4"; 13 | export const MEMPOOL_SPACE_SIGNET_URL = "https://mempool.space/signet"; 14 | export const MEMPOOL_SPACE_FRACTAL_MAINNET_URL = 15 | "https://mempool.fractalbitcoin.io"; 16 | export const MEMPOOL_SPACE_FRACTAL_TESTNET_URL = 17 | "https://mempool-testnet.fractalbitcoin.io"; 18 | 19 | export const getMempoolSpaceUrl = ( 20 | network: 21 | | typeof MAINNET 22 | | typeof TESTNET 23 | | typeof TESTNET4 24 | | typeof SIGNET 25 | | typeof FRACTAL_MAINNET 26 | | typeof FRACTAL_TESTNET 27 | ) => 28 | network === TESTNET 29 | ? MEMPOOL_SPACE_TESTNET_URL 30 | : network === TESTNET4 31 | ? MEMPOOL_SPACE_TESTNET4_URL 32 | : network === SIGNET 33 | ? MEMPOOL_SPACE_SIGNET_URL 34 | : network === FRACTAL_MAINNET 35 | ? MEMPOOL_SPACE_FRACTAL_MAINNET_URL 36 | : network === FRACTAL_TESTNET 37 | ? MEMPOOL_SPACE_FRACTAL_TESTNET_URL 38 | : MEMPOOL_SPACE_URL; 39 | -------------------------------------------------------------------------------- /example/components/ui/tooltip.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as TooltipPrimitive from "@radix-ui/react-tooltip" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const TooltipProvider = TooltipPrimitive.Provider 9 | 10 | const Tooltip = TooltipPrimitive.Root 11 | 12 | const TooltipTrigger = TooltipPrimitive.Trigger 13 | 14 | const TooltipContent = React.forwardRef< 15 | React.ElementRef, 16 | React.ComponentPropsWithoutRef 17 | >(({ className, sideOffset = 4, ...props }, ref) => ( 18 | 27 | )) 28 | TooltipContent.displayName = TooltipPrimitive.Content.displayName 29 | 30 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } 31 | -------------------------------------------------------------------------------- /example/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { cva, type VariantProps } from 'class-variance-authority' 3 | 4 | import { cn } from '@/lib/utils' 5 | 6 | const badgeVariants = cva( 7 | 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', 8 | { 9 | variants: { 10 | variant: { 11 | default: 12 | 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80', 13 | secondary: 14 | 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80', 15 | destructive: 16 | 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80', 17 | outline: 'text-foreground', 18 | success: 19 | 'border-transparent bg-green-500 text-primary-foreground hover:bg-success/80', 20 | }, 21 | }, 22 | defaultVariants: { 23 | variant: 'default', 24 | }, 25 | } 26 | ) 27 | 28 | export interface BadgeProps 29 | extends React.HTMLAttributes, 30 | VariantProps {} 31 | 32 | function Badge({ className, variant, ...props }: BadgeProps) { 33 | return ( 34 |
35 | ) 36 | } 37 | 38 | export { Badge, badgeVariants } 39 | -------------------------------------------------------------------------------- /example/components/ui/sonner.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | var __rest = (this && this.__rest) || function (s, e) { 3 | var t = {}; 4 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) 5 | t[p] = s[p]; 6 | if (s != null && typeof Object.getOwnPropertySymbols === "function") 7 | for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { 8 | if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) 9 | t[p[i]] = s[p[i]]; 10 | } 11 | return t; 12 | }; 13 | import { jsx as _jsx } from "react/jsx-runtime"; 14 | import { useTheme } from "next-themes"; 15 | import { Toaster as Sonner } from "sonner"; 16 | const Toaster = (_a) => { 17 | var props = __rest(_a, []); 18 | const { theme = "system" } = useTheme(); 19 | return (_jsx(Sonner, Object.assign({ theme: theme, className: "toaster group", toastOptions: { 20 | classNames: { 21 | toast: "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg", 22 | description: "group-[.toast]:text-muted-foreground", 23 | actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground", 24 | cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground", 25 | }, 26 | } }, props))); 27 | }; 28 | export { Toaster }; 29 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@bitcoinerlab/secp256k1": "^1.1.1", 13 | "@omnisat/lasereyes": "^0.0.80", 14 | "@radix-ui/react-avatar": "^1.1.0", 15 | "@radix-ui/react-checkbox": "^1.1.1", 16 | "@radix-ui/react-select": "^2.1.1", 17 | "@radix-ui/react-slot": "^1.1.0", 18 | "@radix-ui/react-tooltip": "^1.1.2", 19 | "@supabase/supabase-js": "^2.44.4", 20 | "axios": "^1.7.4", 21 | "bip322-js": "^2.0.0", 22 | "bitcoinjs-lib": "^6.1.6", 23 | "class-variance-authority": "^0.7.0", 24 | "clsx": "^2.1.1", 25 | "ecpair": "^2.1.0", 26 | "jsonwebtoken": "^9.0.2", 27 | "lucide-react": "^0.408.0", 28 | "next": "14.2.10", 29 | "next-themes": "^0.3.0", 30 | "prettier": "^3.3.2", 31 | "react": "^18", 32 | "react-dom": "^18", 33 | "react-icons": "^5.3.0", 34 | "recharts": "^2.12.7", 35 | "sonner": "^1.5.0", 36 | "swr": "^2.2.5", 37 | "tailwind-merge": "^2.3.0", 38 | "tailwindcss-animate": "^1.0.7" 39 | }, 40 | "devDependencies": { 41 | "@types/jsonwebtoken": "^9.0.6", 42 | "@types/node": "^20", 43 | "@types/react": "^18", 44 | "@types/react-dom": "^18", 45 | "eslint": "^8", 46 | "eslint-config-next": "14.2.4", 47 | "postcss": "^8", 48 | "tailwindcss": "^3.4.1", 49 | "typescript": "^5" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /example/lib/urls.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MAINNET, 3 | SIGNET, 4 | TESTNET, 5 | FRACTAL_TESTNET, 6 | FRACTAL_MAINNET, 7 | TESTNET4, 8 | } from '@omnisat/lasereyes' 9 | export const MEMPOOL_SPACE_URL = 'https://mempool.space' 10 | export const MEMPOOL_SPACE_TESTNET_URL = 'https://mempool.space/testnet' 11 | export const MEMPOOL_SPACE_TESTNET4_URL = 'https://mempool.space/testnet4' 12 | export const MEMPOOL_SPACE_SIGNET_URL = 'https://mempool.space/signet' 13 | export const MEMPOOL_SPACE_FRACTAL_MAINNET_URL = 14 | 'https://mempool.fractalbitcoin.io' 15 | export const MEMPOOL_SPACE_FRACTAL_TESTNET_URL = 16 | 'https://mempool-testnet.fractalbitcoin.io' 17 | 18 | export const getMempoolSpaceUrl = ( 19 | network: 20 | | typeof MAINNET 21 | | typeof TESTNET 22 | | typeof TESTNET4 23 | | typeof SIGNET 24 | | typeof FRACTAL_MAINNET 25 | | typeof FRACTAL_TESTNET 26 | ) => 27 | network === TESTNET 28 | ? MEMPOOL_SPACE_TESTNET_URL 29 | : network === TESTNET4 30 | ? MEMPOOL_SPACE_TESTNET4_URL 31 | : network === SIGNET 32 | ? MEMPOOL_SPACE_SIGNET_URL 33 | : network === FRACTAL_MAINNET 34 | ? MEMPOOL_SPACE_FRACTAL_MAINNET_URL 35 | : network === FRACTAL_TESTNET 36 | ? MEMPOOL_SPACE_FRACTAL_TESTNET_URL 37 | : MEMPOOL_SPACE_URL 38 | 39 | export const ORDPOOL_SPACE_URL = 'https://ordpool.space' 40 | export const ORDPOOL_SPACE_TESTNET_URL = 'https://ordpool.space/testnet' 41 | 42 | export const getOrdpoolSpaceUrl = (network: typeof MAINNET | typeof TESTNET) => 43 | network === TESTNET ? ORDPOOL_SPACE_TESTNET_URL : ORDPOOL_SPACE_URL 44 | -------------------------------------------------------------------------------- /example/components/ui/avatar.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as AvatarPrimitive from "@radix-ui/react-avatar" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Avatar = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | )) 21 | Avatar.displayName = AvatarPrimitive.Root.displayName 22 | 23 | const AvatarImage = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, ...props }, ref) => ( 27 | 32 | )) 33 | AvatarImage.displayName = AvatarPrimitive.Image.displayName 34 | 35 | const AvatarFallback = React.forwardRef< 36 | React.ElementRef, 37 | React.ComponentPropsWithoutRef 38 | >(({ className, ...props }, ref) => ( 39 | 47 | )) 48 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName 49 | 50 | export { Avatar, AvatarImage, AvatarFallback } 51 | -------------------------------------------------------------------------------- /example/hooks/useUtxos.ts: -------------------------------------------------------------------------------- 1 | import useSWR from 'swr' 2 | import { useCallback, useEffect, useState } from 'react' 3 | import { IMempoolUtxo } from '@/types/btc' 4 | import { getMempoolSpaceUrl } from '@/lib/urls' 5 | import { 6 | MAINNET, 7 | TESTNET, 8 | FRACTAL_MAINNET, 9 | FRACTAL_TESTNET, 10 | SIGNET, 11 | TESTNET4, 12 | } from '@omnisat/lasereyes' 13 | 14 | const useUtxos = ( 15 | address: string, 16 | network: 17 | | typeof MAINNET 18 | | typeof TESTNET 19 | | typeof TESTNET4 20 | | typeof SIGNET 21 | | typeof FRACTAL_MAINNET 22 | | typeof FRACTAL_TESTNET = MAINNET 23 | ) => { 24 | const mempoolUrl = `${getMempoolSpaceUrl(network)}/api/address/${address}/utxo` 25 | const [utxos, setUtxos] = useState([]) 26 | 27 | const fetcher = useCallback( 28 | async (url: string) => { 29 | const response = await fetch(url) 30 | if (!response.ok) { 31 | throw new Error('Failed to fetch UTXOs') 32 | } 33 | return response.json() 34 | }, 35 | [address, network, mempoolUrl, utxos, setUtxos] 36 | ) 37 | 38 | const { data: utxosData, error } = useSWR( 39 | address && network && mempoolUrl, 40 | fetcher 41 | ) 42 | 43 | useEffect(() => { 44 | setUtxos([]) 45 | }, [network, address]) 46 | 47 | useEffect(() => { 48 | if (utxosData) { 49 | setUtxos(utxosData) 50 | } 51 | }, [utxosData, address]) 52 | 53 | useEffect(() => { 54 | if (error) { 55 | console.error('Error fetching UTXOs:', error) 56 | } 57 | }, [error]) 58 | 59 | return { 60 | utxos: utxos || [], 61 | loading: !utxos && !error, 62 | fetch: () => { 63 | fetcher(mempoolUrl) 64 | }, 65 | } 66 | } 67 | 68 | export default useUtxos 69 | -------------------------------------------------------------------------------- /github/main/content/sponsors/utxo-dark-mode.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/icons/okx.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | interface OkxLogoProps extends React.SVGProps { 4 | size?: number; 5 | variant?: "first" | "second"; 6 | } 7 | 8 | const OkxLogo: React.FC = ({ 9 | size = 42, 10 | variant = "first", 11 | className, 12 | ...props 13 | }) => { 14 | return ( 15 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | ); 43 | }; 44 | 45 | export { OkxLogo }; 46 | -------------------------------------------------------------------------------- /example/app/api/submit-poll/route.ts: -------------------------------------------------------------------------------- 1 | import { supabase } from '@/lib/db' 2 | import { NextResponse } from 'next/server' 3 | 4 | export async function POST(request: Request) { 5 | try { 6 | const { 7 | wallet, 8 | address, 9 | }: { 10 | wallet: string 11 | address: string 12 | } = await request.json() 13 | 14 | // Check if the address already exists 15 | const { data: existingData, error: fetchError } = await supabase 16 | .from('wallet_poll') 17 | .select('*') 18 | .eq('address', address) 19 | .single() 20 | 21 | if (fetchError && fetchError.code !== 'PGRST116') { 22 | return NextResponse.json('error fetching data', { 23 | status: 400, 24 | }) 25 | } 26 | 27 | if (existingData) { 28 | const { data, error } = await supabase 29 | .from('wallet_poll') 30 | .update({ wallet }) 31 | .eq('address', address) 32 | 33 | if (error) { 34 | return NextResponse.json(error.message, { 35 | status: 400, 36 | }) 37 | } 38 | } else { 39 | const { data, error } = await supabase 40 | .from('wallet_poll') 41 | .insert([{ wallet, address }]) 42 | 43 | if (error) { 44 | return NextResponse.json(error.message, { 45 | status: 400, 46 | }) 47 | } 48 | } 49 | 50 | // Fetch updated poll results 51 | const { data: pollResults, error: fetchResultsError } = 52 | await supabase.rpc('get_poll_results') 53 | 54 | if (fetchResultsError) { 55 | return NextResponse.json( 56 | { error: fetchResultsError.message }, 57 | { 58 | status: 400, 59 | } 60 | ) 61 | } 62 | 63 | return NextResponse.json(pollResults) 64 | } catch (e) { 65 | return NextResponse.json('there was an error fetching your utxos', { 66 | status: 400, 67 | }) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /example/components/ClickToCopy.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { JSX, SVGProps, useState } from 'react' 4 | import { 5 | TooltipProvider, 6 | Tooltip, 7 | TooltipTrigger, 8 | TooltipContent, 9 | } from '@/components/ui/tooltip' 10 | import { Button } from '@/components/ui/button' 11 | 12 | export default function ClickToCopy({ value }: { value: string | undefined }) { 13 | const [copied, setCopied] = useState(false) 14 | const handleCopy = () => { 15 | if (typeof value === 'string') { 16 | navigator.clipboard.writeText(value) 17 | } 18 | setCopied(true) 19 | setTimeout(() => setCopied(false), 2000) 20 | } 21 | return ( 22 | 23 |
24 | 25 | 26 | 35 | 36 | Click to copy value 37 | 38 | {copied &&
Copied!
} 39 |
40 |
41 | ) 42 | } 43 | 44 | function CopyIcon(props: JSX.IntrinsicAttributes & SVGProps) { 45 | return ( 46 | 58 | 59 | 60 | 61 | ) 62 | } 63 | -------------------------------------------------------------------------------- /example/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Slot } from '@radix-ui/react-slot' 3 | import { cva, type VariantProps } from 'class-variance-authority' 4 | 5 | import { cn } from '@/lib/utils' 6 | 7 | const buttonVariants = cva( 8 | 'inline-flex items-center rounded-none justify-center whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', 9 | { 10 | variants: { 11 | variant: { 12 | default: 'bg-primary text-primary-foreground hover:bg-primary/90', 13 | destructive: 14 | 'bg-destructive text-destructive-foreground hover:bg-destructive/90', 15 | outline: 16 | 'border border-input bg-background hover:bg-accent hover:text-accent-foreground', 17 | secondary: 18 | 'bg-secondary text-secondary-foreground hover:bg-secondary/80', 19 | ghost: 'hover:bg-accent hover:text-accent-foreground', 20 | link: 'text-primary underline-offset-4 hover:underline', 21 | }, 22 | size: { 23 | default: 'h-10 px-4 py-2', 24 | sm: 'h-9 px-3', 25 | lg: 'h-11 px-8', 26 | icon: 'h-10 w-10', 27 | }, 28 | }, 29 | defaultVariants: { 30 | variant: 'default', 31 | size: 'default', 32 | }, 33 | } 34 | ) 35 | 36 | export interface ButtonProps 37 | extends React.ButtonHTMLAttributes, 38 | VariantProps { 39 | asChild?: boolean 40 | } 41 | 42 | const Button = React.forwardRef( 43 | ({ className, variant, size, asChild = false, ...props }, ref) => { 44 | const Comp = asChild ? Slot : 'button' 45 | return ( 46 | 51 | ) 52 | } 53 | ) 54 | Button.displayName = 'Button' 55 | 56 | export { Button, buttonVariants } 57 | -------------------------------------------------------------------------------- /src/consts/settings.ts: -------------------------------------------------------------------------------- 1 | import { LEATHER, MAGIC_EDEN, OYL, UNISAT, XVERSE } from "./wallets"; 2 | import { 3 | FRACTAL_MAINNET, 4 | FRACTAL_TESTNET, 5 | MAINNET, 6 | REGTEST, 7 | SIGNET, 8 | TESTNET, 9 | TESTNET4, 10 | } from "./networks"; 11 | 12 | export const LOCAL_STORAGE_DEFAULT_WALLET = "defaultWallet"; 13 | 14 | export const initialWalletContext = { 15 | hasUnisat: false, 16 | hasXverse: false, 17 | hasOyl: false, 18 | hasMagicEden: false, 19 | hasOkx: false, 20 | hasLeather: false, 21 | hasPhantom: false, 22 | hasWizz: false, 23 | hasOrange: false, 24 | isInitializing: true, 25 | connected: false, 26 | isConnecting: false, 27 | publicKey: "", 28 | address: "", 29 | paymentAddress: "", 30 | paymentPublicKey: "", 31 | balance: undefined, 32 | network: MAINNET as 33 | | typeof MAINNET 34 | | typeof TESTNET 35 | | typeof TESTNET4 36 | | typeof SIGNET 37 | | typeof FRACTAL_MAINNET 38 | | typeof FRACTAL_TESTNET, 39 | library: null, 40 | provider: null, 41 | accounts: [], 42 | connect: async ( 43 | walletName: 44 | | typeof OYL 45 | | typeof UNISAT 46 | | typeof XVERSE 47 | | typeof LEATHER 48 | | typeof MAGIC_EDEN 49 | ) => {}, 50 | disconnect: () => {}, 51 | requestAccounts: async () => [], 52 | getNetwork: async () => MAINNET, 53 | switchNetwork: async ( 54 | network: 55 | | typeof MAINNET 56 | | typeof TESTNET 57 | | typeof TESTNET4 58 | | typeof SIGNET 59 | | typeof FRACTAL_MAINNET 60 | | typeof FRACTAL_TESTNET 61 | ) => {}, 62 | getPublicKey: async () => "", 63 | getBalance: async () => "", 64 | getInscriptions: async () => [], 65 | sendBTC: async (to: string, amount: number) => "", 66 | signMessage: async (message: string) => "", 67 | signPsbt: async (tx: string) => { 68 | return { 69 | signedPsbtHex: "", 70 | signedPsbtBase64: "", 71 | txId: "", 72 | }; 73 | }, 74 | pushPsbt: async (tx: string) => { 75 | return ""; 76 | }, 77 | inscribe: async (content: any) => "", 78 | isCreatingCommit: false, 79 | isInscribing: false, 80 | }; 81 | -------------------------------------------------------------------------------- /src/icons/xverse.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | interface XverseLogoProps extends React.SVGProps { 4 | size?: number; 5 | variant?: "first" | "second"; 6 | } 7 | 8 | const XverseLogo: React.FC = ({ 9 | size = 42, 10 | variant = "first", 11 | className, 12 | ...props 13 | }) => { 14 | return ( 15 | 24 | 25 | 29 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | }; 46 | 47 | export { XverseLogo }; 48 | -------------------------------------------------------------------------------- /example/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import { cn } from '@/lib/utils' 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )) 18 | Card.displayName = 'Card' 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )) 30 | CardHeader.displayName = 'CardHeader' 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLParagraphElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |

44 | )) 45 | CardTitle.displayName = 'CardTitle' 46 | 47 | const CardDescription = React.forwardRef< 48 | HTMLParagraphElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |

56 | )) 57 | CardDescription.displayName = 'CardDescription' 58 | 59 | const CardContent = React.forwardRef< 60 | HTMLDivElement, 61 | React.HTMLAttributes 62 | >(({ className, ...props }, ref) => ( 63 |

64 | )) 65 | CardContent.displayName = 'CardContent' 66 | 67 | const CardFooter = React.forwardRef< 68 | HTMLDivElement, 69 | React.HTMLAttributes 70 | >(({ className, ...props }, ref) => ( 71 |
76 | )) 77 | CardFooter.displayName = 'CardFooter' 78 | 79 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } 80 | -------------------------------------------------------------------------------- /src/icons/oyl.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | interface OylLogoProps extends React.SVGProps { 4 | size?: number; 5 | variant?: "first" | "second"; 6 | } 7 | 8 | const OylLogo: React.FC = ({ 9 | size = 42, 10 | variant = "first", 11 | className, 12 | ...props 13 | }) => { 14 | if (variant === "first") { 15 | return ( 16 | 25 | 32 | 37 | 38 | ); 39 | } 40 | 41 | return ( 42 | 51 | 58 | 63 | 64 | ); 65 | }; 66 | 67 | export { OylLogo }; 68 | -------------------------------------------------------------------------------- /src/icons/walletIcon.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | XVERSE, 3 | WIZZ, 4 | LEATHER, 5 | MAGIC_EDEN, 6 | OKX, 7 | PHANTOM, 8 | UNISAT, 9 | OYL, 10 | ORANGE, 11 | } from "../consts/wallets"; 12 | import { WizzLogo } from "./wizz"; 13 | import { XverseLogo } from "./xverse"; 14 | import { LeatherLogo } from "./leather"; 15 | import { MagicEdenLogo } from "./magiceden"; 16 | import { OkxLogo } from "./okx"; 17 | import { PhantomLogo } from "./phantom"; 18 | import { UnisatLogo } from "./unisat"; 19 | import { OylLogo } from "./oyl"; 20 | import OrangeLogo from "./orange"; 21 | 22 | const WalletIcon = ({ 23 | size, 24 | className, 25 | variant, 26 | walletName, 27 | }: { 28 | size: number; 29 | className?: string; 30 | variant?: "first" | "second"; 31 | walletName: 32 | | typeof XVERSE 33 | | typeof WIZZ 34 | | typeof LEATHER 35 | | typeof MAGIC_EDEN 36 | | typeof OKX 37 | | typeof PHANTOM 38 | | typeof UNISAT 39 | | typeof OYL 40 | | typeof ORANGE; 41 | }) => { 42 | if (walletName === XVERSE) { 43 | return ; 44 | } else if (walletName === WIZZ) { 45 | return ; 46 | } else if (walletName === LEATHER) { 47 | return ; 48 | } else if (walletName === MAGIC_EDEN) { 49 | return ( 50 | 51 | ); 52 | } else if (walletName === OKX) { 53 | return ; 54 | } else if (walletName === PHANTOM) { 55 | return ; 56 | } else if (walletName === UNISAT) { 57 | return ; 58 | } else if (walletName === OYL) { 59 | return ; 60 | } else if (walletName === ORANGE) { 61 | return ; 62 | } else { 63 | return ; 64 | } 65 | }; 66 | 67 | export { WalletIcon }; 68 | -------------------------------------------------------------------------------- /example/components/PollResults.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { Bar, BarChart, XAxis, YAxis } from 'recharts' 4 | import { 5 | ChartConfig, 6 | ChartContainer, 7 | ChartTooltip, 8 | ChartTooltipContent, 9 | } from '@/components/ui/chart' 10 | 11 | const chartConfig = { 12 | oyl: { 13 | label: 'OYL', 14 | color: 'hsl(var(--chart-5))', 15 | }, 16 | 'orange-wallet': { 17 | label: 'ORANGE WALLET', 18 | color: 'hsl(var(--chart-1))', 19 | }, 20 | leather: { 21 | label: 'LEATHER', 22 | color: 'hsl(var(--chart-2))', 23 | }, 24 | wiz: { 25 | label: 'WIZ', 26 | color: 'hsl(var(--chart-3))', 27 | }, 28 | 'magic-eden': { 29 | label: 'MAGIC EDEN', 30 | color: 'hsl(var(--chart-4))', 31 | }, 32 | phantom: { 33 | label: 'PHANTOM', 34 | color: 'hsl(var(--chart-5))', 35 | }, 36 | } satisfies ChartConfig 37 | 38 | export function PollResults({ pollResults }: { pollResults: any[] }) { 39 | const chartData = pollResults 40 | // @ts-ignore 41 | .filter((res) => chartConfig[res.wallet.toLowerCase().replace(' ', '-')]) 42 | .map((result) => ({ 43 | wallet: result.wallet.toLowerCase().replace(' ', '-'), 44 | votes: result.votes, 45 | //@ts-ignore 46 | fill: chartConfig[result.wallet.toLowerCase().replace(' ', '-')] 47 | ?.color as string, 48 | })) 49 | 50 | return ( 51 | 55 | 62 | 72 | chartConfig[value as keyof typeof chartConfig]?.label 73 | } 74 | /> 75 | 76 | } 79 | /> 80 | 81 | 82 | 83 | ) 84 | } 85 | -------------------------------------------------------------------------------- /example/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --chart-1: 12 76% 61%; 8 | --chart-2: 173 58% 39%; 9 | --chart-3: 197 37% 24%; 10 | --chart-4: 43 74% 66%; 11 | --chart-5: 27 87% 67%; 12 | 13 | --background: 0 0% 100%; 14 | --foreground: 222.2 84% 4.9%; 15 | 16 | --card: 0 0% 100%; 17 | --card-foreground: 222.2 84% 4.9%; 18 | 19 | --popover: 0 0% 100%; 20 | --popover-foreground: 222.2 84% 4.9%; 21 | 22 | --primary: 222.2 47.4% 11.2%; 23 | --primary-foreground: 210 40% 98%; 24 | 25 | --secondary: 210 40% 96.1%; 26 | --secondary-foreground: 222.2 47.4% 11.2%; 27 | 28 | --muted: 210 40% 96.1%; 29 | --muted-foreground: 215.4 16.3% 46.9%; 30 | 31 | --accent: 210 40% 96.1%; 32 | --accent-foreground: 222.2 47.4% 11.2%; 33 | 34 | --destructive: 0 84.2% 60.2%; 35 | --destructive-foreground: 210 40% 98%; 36 | 37 | --border: 214.3 31.8% 91.4%; 38 | --input: 214.3 31.8% 91.4%; 39 | --ring: 222.2 84% 4.9%; 40 | 41 | --radius: 0.5rem; 42 | } 43 | 44 | .dark { 45 | --chart-1: 220 70% 50%; 46 | --chart-2: 160 60% 45%; 47 | --chart-3: 30 80% 55%; 48 | --chart-4: 280 65% 60%; 49 | --chart-5: 340 75% 55%; 50 | 51 | --background: 222.2 84% 4.9%; 52 | --foreground: 210 40% 98%; 53 | 54 | --card: 222.2 84% 4.9%; 55 | --card-foreground: 210 40% 98%; 56 | 57 | --popover: 222.2 84% 4.9%; 58 | --popover-foreground: 210 40% 98%; 59 | 60 | --primary: 210 40% 98%; 61 | --primary-foreground: 222.2 47.4% 11.2%; 62 | 63 | --secondary: 217.2 32.6% 17.5%; 64 | --secondary-foreground: 210 40% 98%; 65 | 66 | --muted: 217.2 32.6% 17.5%; 67 | --muted-foreground: 215 20.2% 65.1%; 68 | 69 | --accent: 217.2 32.6% 17.5%; 70 | --accent-foreground: 210 40% 98%; 71 | 72 | --destructive: 0 62.8% 30.6%; 73 | --destructive-foreground: 210 40% 98%; 74 | 75 | --border: 217.2 32.6% 17.5%; 76 | --input: 217.2 32.6% 17.5%; 77 | --ring: 212.7 26.8% 83.9%; 78 | } 79 | } 80 | 81 | @layer base { 82 | * { 83 | @apply border-border; 84 | } 85 | body { 86 | @apply bg-background text-foreground; 87 | } 88 | } 89 | 90 | body, 91 | html { 92 | font-family: 'windows', sans-serif; 93 | background-color: #1e1d1f; 94 | } 95 | -------------------------------------------------------------------------------- /example/tailwind.config.d.ts: -------------------------------------------------------------------------------- 1 | declare const config: { 2 | darkMode: ["class"]; 3 | content: string[]; 4 | prefix: string; 5 | theme: { 6 | container: { 7 | center: true; 8 | padding: string; 9 | screens: { 10 | '2xl': string; 11 | }; 12 | }; 13 | extend: { 14 | colors: { 15 | border: string; 16 | input: string; 17 | ring: string; 18 | background: string; 19 | foreground: string; 20 | primary: { 21 | DEFAULT: string; 22 | foreground: string; 23 | }; 24 | secondary: { 25 | DEFAULT: string; 26 | foreground: string; 27 | }; 28 | destructive: { 29 | DEFAULT: string; 30 | foreground: string; 31 | }; 32 | muted: { 33 | DEFAULT: string; 34 | foreground: string; 35 | }; 36 | accent: { 37 | DEFAULT: string; 38 | foreground: string; 39 | }; 40 | popover: { 41 | DEFAULT: string; 42 | foreground: string; 43 | }; 44 | card: { 45 | DEFAULT: string; 46 | foreground: string; 47 | }; 48 | }; 49 | borderRadius: { 50 | lg: string; 51 | md: string; 52 | sm: string; 53 | }; 54 | keyframes: { 55 | 'accordion-down': { 56 | from: { 57 | height: string; 58 | }; 59 | to: { 60 | height: string; 61 | }; 62 | }; 63 | 'accordion-up': { 64 | from: { 65 | height: string; 66 | }; 67 | to: { 68 | height: string; 69 | }; 70 | }; 71 | }; 72 | animation: { 73 | 'accordion-down': string; 74 | 'accordion-up': string; 75 | }; 76 | }; 77 | }; 78 | plugins: any[]; 79 | }; 80 | export default config; 81 | //# sourceMappingURL=tailwind.config.d.ts.map -------------------------------------------------------------------------------- /example/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | 3 | const config = { 4 | darkMode: ['class'], 5 | content: [ 6 | './pages/**/*.{ts,tsx}', 7 | './components/**/*.{ts,tsx}', 8 | './app/**/*.{ts,tsx}', 9 | './src/**/*.{ts,tsx}', 10 | ], 11 | prefix: '', 12 | theme: { 13 | container: { 14 | center: true, 15 | padding: '2rem', 16 | screens: { 17 | '2xl': '1400px', 18 | }, 19 | }, 20 | extend: { 21 | fontFamily: { 22 | windows: ['var(--font-windows)'], 23 | }, 24 | colors: { 25 | border: 'hsl(var(--border))', 26 | input: 'hsl(var(--input))', 27 | ring: 'hsl(var(--ring))', 28 | background: 'hsl(var(--background))', 29 | foreground: 'hsl(var(--foreground))', 30 | primary: { 31 | DEFAULT: 'hsl(var(--primary))', 32 | foreground: 'hsl(var(--primary-foreground))', 33 | }, 34 | secondary: { 35 | DEFAULT: 'hsl(var(--secondary))', 36 | foreground: 'hsl(var(--secondary-foreground))', 37 | }, 38 | destructive: { 39 | DEFAULT: 'hsl(var(--destructive))', 40 | foreground: 'hsl(var(--destructive-foreground))', 41 | }, 42 | muted: { 43 | DEFAULT: 'hsl(var(--muted))', 44 | foreground: 'hsl(var(--muted-foreground))', 45 | }, 46 | accent: { 47 | DEFAULT: 'hsl(var(--accent))', 48 | foreground: 'hsl(var(--accent-foreground))', 49 | }, 50 | popover: { 51 | DEFAULT: 'hsl(var(--popover))', 52 | foreground: 'hsl(var(--popover-foreground))', 53 | }, 54 | card: { 55 | DEFAULT: 'hsl(var(--card))', 56 | foreground: 'hsl(var(--card-foreground))', 57 | }, 58 | }, 59 | borderRadius: { 60 | lg: 'var(--radius)', 61 | md: 'calc(var(--radius) - 2px)', 62 | sm: 'calc(var(--radius) - 4px)', 63 | }, 64 | keyframes: { 65 | 'accordion-down': { 66 | from: { height: '0' }, 67 | to: { height: 'var(--radix-accordion-content-height)' }, 68 | }, 69 | 'accordion-up': { 70 | from: { height: 'var(--radix-accordion-content-height)' }, 71 | to: { height: '0' }, 72 | }, 73 | }, 74 | animation: { 75 | 'accordion-down': 'accordion-down 0.2s ease-out', 76 | 'accordion-up': 'accordion-up 0.2s ease-out', 77 | }, 78 | }, 79 | }, 80 | plugins: [require('tailwindcss-animate')], 81 | } satisfies Config 82 | 83 | export default config 84 | -------------------------------------------------------------------------------- /src/icons/wizz.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | interface WizzLogoProps extends React.SVGProps { 4 | size?: number; 5 | variant?: "first" | "second"; 6 | } 7 | 8 | const WizzLogo: React.FC = ({ 9 | size = 42, 10 | variant = "first", 11 | className, 12 | ...props 13 | }) => { 14 | return ( 15 | 24 | 25 | 29 | 30 | 34 | 38 | 42 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ); 58 | }; 59 | 60 | export { WizzLogo }; 61 | -------------------------------------------------------------------------------- /src/consts/wallets.ts: -------------------------------------------------------------------------------- 1 | export const OYL = "oyl"; 2 | export const UNISAT = "unisat"; 3 | export const XVERSE = "xverse"; 4 | export const PHANTOM = "phantom"; 5 | export const LEATHER = "leather"; 6 | export const MAGIC_EDEN = "magic-eden"; 7 | export const OKX = "okx"; 8 | export const WIZZ = "wizz"; 9 | export const ORANGE = "orange"; 10 | 11 | export const P2TR = "p2tr"; 12 | export const P2PKH = "p2pkh"; 13 | export const P2WPKH = "p2wpkh"; 14 | export const P2PSH = "p2psh"; 15 | export const P2WSH = "p2wsh"; 16 | export const P2SH = "p2sh"; 17 | 18 | export const WALLETS = { 19 | oyl: { 20 | name: OYL, 21 | icon: "data:image/svg+xml,%3Csvg%20width%3D%2296%22%20height%3D%2216%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M17.27.318c2.947%200%205.72.26%208.32.78%202.6.507%204.714%201.307%206.34%202.4%201.64%201.094%202.46%202.467%202.46%204.12%200%201.654-.82%203.047-2.46%204.18-1.64%201.12-3.766%201.954-6.38%202.5-2.6.547-5.36.82-8.28.82-2.946%200-5.72-.253-8.32-.76-2.6-.52-4.72-1.326-6.36-2.42C.964%2010.845.15%209.472.15%207.818c0-1.653.82-3.04%202.46-4.16%201.64-1.133%203.76-1.973%206.36-2.52%202.614-.546%205.38-.82%208.3-.82Zm.04%2011.96c1.4%200%202.8-.146%204.2-.44%201.414-.293%202.6-.78%203.56-1.46.96-.68%201.44-1.566%201.44-2.66%200-1.093-.48-1.98-1.44-2.66-.96-.68-2.146-1.166-3.56-1.46-1.4-.293-2.8-.44-4.2-.44h-.08c-1.4%200-2.806.147-4.22.44-1.4.294-2.58.78-3.54%201.46-.96.68-1.44%201.567-1.44%202.66%200%201.094.48%201.98%201.44%202.66.96.68%202.14%201.167%203.54%201.46%201.414.294%202.82.44%204.22.44h.08ZM61.293.758c.08-.026.14-.04.18-.04h8.24c.12%200%20.214.04.28.12.08.08.12.174.12.28%200%20.147-.066.26-.2.34l-14%207.7c-.133.067-.2.18-.2.34v4.82c0%20.107-.04.2-.12.28a.384.384%200%200%201-.28.12h-6.94c-.106%200-.2-.04-.28-.12a.384.384%200%200%201-.12-.28v-4.82c0-.16-.066-.273-.2-.34l-13.98-7.7a.376.376%200%200%201-.2-.34c0-.106.034-.2.1-.28.067-.08.16-.12.28-.12h8.24c.054%200%20.12.014.2.04l9.24%205.1c.08.027.147.04.2.04.04%200%20.1-.013.18-.04l9.26-5.1Zm11.232.36c0-.106.033-.2.1-.28.08-.08.173-.12.28-.12h6.84c.106%200%20.193.04.26.12.08.08.12.174.12.28v10.32c0%20.107.04.2.12.28.08.08.173.12.28.12h14.46c.106%200%20.193.04.26.12.08.08.12.174.12.28v2.08c0%20.107-.04.2-.12.28a.322.322%200%200%201-.26.12h-22.08c-.107%200-.2-.04-.28-.12a.424.424%200%200%201-.1-.28v-13.2Z%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E", 22 | site: "https://oyl.io/", 23 | disabled: false, 24 | }, 25 | unisat: { 26 | name: UNISAT, 27 | icon: "/wallet-logos/unisat_logo.png", 28 | site: "https://unisat.io/", 29 | disabled: false, 30 | }, 31 | xverse: { 32 | name: XVERSE, 33 | icon: "/wallet-logos/xverse_logo.png", 34 | site: "https://www.xverse.app/", 35 | config: () => {}, 36 | disabled: false, 37 | }, 38 | leather: { 39 | name: LEATHER, 40 | icon: "/wallet-logos/leather_logo.png", 41 | site: "https://leather.io/", 42 | config: () => {}, 43 | disabled: true, 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /example/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | darkMode: ['class'], 3 | content: [ 4 | './pages/**/*.{ts,tsx}', 5 | './components/**/*.{ts,tsx}', 6 | './app/**/*.{ts,tsx}', 7 | './src/**/*.{ts,tsx}', 8 | ], 9 | prefix: '', 10 | theme: { 11 | container: { 12 | center: true, 13 | padding: '2rem', 14 | screens: { 15 | '2xl': '1400px', 16 | }, 17 | }, 18 | extend: { 19 | colors: { 20 | border: 'hsl(var(--border))', 21 | input: 'hsl(var(--input))', 22 | ring: 'hsl(var(--ring))', 23 | background: 'hsl(var(--background))', 24 | foreground: 'hsl(var(--foreground))', 25 | primary: { 26 | DEFAULT: 'hsl(var(--primary))', 27 | foreground: 'hsl(var(--primary-foreground))', 28 | }, 29 | secondary: { 30 | DEFAULT: 'hsl(var(--secondary))', 31 | foreground: 'hsl(var(--secondary-foreground))', 32 | }, 33 | destructive: { 34 | DEFAULT: 'hsl(var(--destructive))', 35 | foreground: 'hsl(var(--destructive-foreground))', 36 | }, 37 | muted: { 38 | DEFAULT: 'hsl(var(--muted))', 39 | foreground: 'hsl(var(--muted-foreground))', 40 | }, 41 | accent: { 42 | DEFAULT: 'hsl(var(--accent))', 43 | foreground: 'hsl(var(--accent-foreground))', 44 | }, 45 | popover: { 46 | DEFAULT: 'hsl(var(--popover))', 47 | foreground: 'hsl(var(--popover-foreground))', 48 | }, 49 | card: { 50 | DEFAULT: 'hsl(var(--card))', 51 | foreground: 'hsl(var(--card-foreground))', 52 | }, 53 | }, 54 | borderRadius: { 55 | lg: 'var(--radius)', 56 | md: 'calc(var(--radius) - 2px)', 57 | sm: 'calc(var(--radius) - 4px)', 58 | }, 59 | keyframes: { 60 | 'accordion-down': { 61 | from: { height: '0' }, 62 | to: { height: 'var(--radix-accordion-content-height)' }, 63 | }, 64 | 'accordion-up': { 65 | from: { height: 'var(--radix-accordion-content-height)' }, 66 | to: { height: '0' }, 67 | }, 68 | }, 69 | animation: { 70 | 'accordion-down': 'accordion-down 0.2s ease-out', 71 | 'accordion-up': 'accordion-up 0.2s ease-out', 72 | }, 73 | }, 74 | }, 75 | plugins: [require('tailwindcss-animate')], 76 | }; 77 | export default config; 78 | -------------------------------------------------------------------------------- /src/icons/leather.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | interface LeatherLogoProps extends React.SVGProps { 4 | size?: number; 5 | variant?: "first" | "second"; 6 | } 7 | 8 | const LeatherLogo: React.FC = ({ 9 | size = 42, 10 | variant = "first", 11 | className, 12 | ...props 13 | }) => { 14 | if (variant === "first") { 15 | return ( 16 | 25 | 26 | 30 | 31 | ); 32 | } 33 | 34 | return ( 35 | 44 | 45 | 49 | 50 | ); 51 | }; 52 | 53 | export { LeatherLogo }; 54 | -------------------------------------------------------------------------------- /src/icons/phantom.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | interface PhantomLogoProps extends React.SVGProps { 4 | size?: number; 5 | variant?: "first" | "second"; 6 | } 7 | 8 | const PhantomLogo: React.FC = ({ 9 | size = 42, 10 | variant = "first", 11 | className, 12 | ...props 13 | }) => { 14 | if (variant === "first") { 15 | return ( 16 | 25 | 26 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ); 44 | } 45 | 46 | return ( 47 | 56 | 57 | 61 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | ); 75 | }; 76 | 77 | export { PhantomLogo }; 78 | -------------------------------------------------------------------------------- /example/components/ClickToCopyNpmInstallPill.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { cn, truncateString } from '@/lib/utils' 3 | import { badgeVariants } from '@/components/ui/badge' 4 | import { FaCopy } from 'react-icons/fa6' 5 | import { useEffect, useState } from 'react' 6 | import { useLocalStorage } from 'usehooks-ts' 7 | 8 | export const ClickToCopyNpmInstallPill = ({ 9 | className, 10 | }: { 11 | className?: string 12 | }) => { 13 | const DEPENDENCY_MANAGERS = ['npm', 'yarn', 'pnpm', 'bun'] 14 | const [dependencyManager, setDependencyManager] = useLocalStorage( 15 | 'preferredDependencyManager', 16 | DEPENDENCY_MANAGERS[0] 17 | ) 18 | const [signature, setSignature] = useState(' --') 19 | 20 | const [copied, setCopied] = useState(false) 21 | 22 | useEffect(() => { 23 | setSignature( 24 | `${dependencyManager} ${dependencyManager === 'yarn' ? 'add' : 'install'} @omnisat/lasereyes` 25 | ) 26 | }, [dependencyManager]) 27 | 28 | const setDepManagerAndSignature = (manager: string) => { 29 | setDependencyManager(manager) 30 | } 31 | 32 | const copyToClipboard = async (text: string) => { 33 | await navigator.clipboard.writeText(text) 34 | setCopied(true) 35 | setTimeout(() => setCopied(false), 2000) // Reset after 2 seconds 36 | } 37 | 38 | return ( 39 |
46 | 0 ? '' : '', 50 | copied ? 'animate-pulse' : '' 51 | )} 52 | onClick={() => copyToClipboard(signature)} 53 | > 54 | 57 | {signature?.length > 0 && !copied ? ( 58 | 59 | 60 | {dependencyManager} 61 | {' '} 62 | 63 | {signature.replaceAll(dependencyManager, ' ')} 64 | 65 | 66 | ) : ( 67 | '' 68 | )}{' '} 69 | {copied && ( 70 | Copied to clipboard 71 | )} 72 | 73 | 79 | 80 |
85 | setDepManagerAndSignature( 86 | DEPENDENCY_MANAGERS[ 87 | (DEPENDENCY_MANAGERS.indexOf(String(dependencyManager)) + 1) % 88 | DEPENDENCY_MANAGERS.length 89 | ] 90 | ) 91 | } 92 | > 93 | {dependencyManager} 94 |
95 |
96 | ) 97 | } 98 | -------------------------------------------------------------------------------- /src/icons/magiceden.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | interface MagicedenLogoProps extends React.SVGProps { 4 | size?: number; 5 | variant?: "first" | "second"; 6 | } 7 | 8 | const MagicEdenLogo: React.FC = ({ 9 | size = 42, 10 | variant = "first", 11 | className, 12 | ...props 13 | }) => { 14 | return ( 15 | 24 | 25 | 29 | 33 | 34 | 35 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | ); 63 | }; 64 | 65 | export { MagicEdenLogo }; 66 | -------------------------------------------------------------------------------- /example/components/PollCard.tsx: -------------------------------------------------------------------------------- 1 | import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' 2 | import { useEffect, useState } from 'react' 3 | import { useLaserEyes } from '@omnisat/lasereyes' 4 | import { Button } from '@/components/ui/button' 5 | import { PollResults } from '@/components/PollResults' 6 | 7 | const PollCard = () => { 8 | const { address } = useLaserEyes() 9 | const [selectedWallet, setSelectedWallet] = useState(null) 10 | const [submitted, setSubmitted] = useState(false) 11 | const [error, setError] = useState(null) 12 | const [pollResults, setPollResults] = useState([]) 13 | 14 | const wallets = ['WIZ', 'ORANGE WALLET'] 15 | 16 | useEffect(() => { 17 | setSelectedWallet(null) 18 | setPollResults([]) 19 | setSubmitted(false) 20 | }, [address]) 21 | 22 | const handleSubmit = async () => { 23 | if (selectedWallet) { 24 | try { 25 | const response = await fetch('/api/submit-poll', { 26 | method: 'POST', 27 | headers: { 28 | 'Content-Type': 'application/json', 29 | }, 30 | body: JSON.stringify({ wallet: selectedWallet, address }), 31 | }) 32 | 33 | if (response.ok) { 34 | const data = await response.json() 35 | setPollResults(data) 36 | setSubmitted(true) 37 | } else { 38 | const errorData = await response.json() 39 | setError(errorData.error) 40 | } 41 | } catch (error) { 42 | setError('An unexpected error occurred') 43 | } 44 | } else { 45 | setError('Please select a wallet') 46 | } 47 | } 48 | 49 | return ( 50 | 51 | 52 | 55 | Which wallet should we support next? 56 | 57 | 58 | 59 | {!submitted && pollResults.length === 0 ? ( 60 | <> 61 | {wallets.map((wallet) => ( 62 |
63 | 72 |
73 | ))} 74 | 86 | {error &&

{error}

} 87 | 88 | ) : ( 89 |
90 | 91 |
92 | )} 93 |
94 |
95 | ) 96 | } 97 | 98 | export default PollCard 99 | -------------------------------------------------------------------------------- /src/icons/unisat.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | interface UnisatLogoProps extends React.SVGProps { 4 | size?: number; 5 | variant?: "first" | "second"; 6 | } 7 | 8 | const UnisatLogo: React.FC = ({ 9 | size = 42, 10 | variant = "first", 11 | className, 12 | ...props 13 | }) => { 14 | return ( 15 | 24 | 25 | 29 | 33 | 37 | 41 | 42 | 43 | 51 | 52 | 53 | 54 | 55 | 56 | 64 | 65 | 66 | 67 | 68 | 69 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | ); 88 | }; 89 | 90 | export { UnisatLogo }; 91 | -------------------------------------------------------------------------------- /src/consts/networks.ts: -------------------------------------------------------------------------------- 1 | import { BitcoinNetworkType } from "@orangecrypto/orange-connect"; 2 | 3 | export const XVERSE_NETWORK: "Mainnet" | "Testnet" = "Mainnet"; 4 | 5 | export const UNISAT_MAINNET = "BITCOIN_MAINNET"; 6 | export const UNISAT_TESTNET = "BITCOIN_TESTNET"; 7 | export const UNISAT_TESTNET4 = "BITCOIN_TESTNET4"; 8 | export const UNISAT_SIGNET = "BITCOIN_SIGNET"; 9 | export const UNISAT_FRACTAL_MAINNET = "FRACTAL_BITCOIN_MAINNET"; 10 | export const UNISAT_FRACTAL_TESTNET = "FRACTAL_BITCOIN_TESTNET"; 11 | 12 | export const OP_WALLET_MAINNET = "livenet"; 13 | export const OP_WALLET_TESTNET = "testnet"; 14 | 15 | export const XVERSE_MAINNET = "Mainnet"; 16 | export const XVERSE_TESTNET = "Testnet"; 17 | export const XVERSE_SIGNET = "Signet"; 18 | 19 | export const OKX_MAINNET = "livenet"; 20 | export const OKX_TESTNET = "testnet"; 21 | 22 | export const WIZZ_MAINNET = "livenet"; 23 | export const WIZZ_TESTNET = "testnet"; 24 | export const WIZZ_TESTNET4 = "testnet4"; 25 | export const WIZZ_SIGNET = "signet"; 26 | 27 | export const ORANGE_MAINNET = "Mainnet"; 28 | export const ORANGE_TESTNET = "Testnet"; 29 | 30 | export const LEATHER_MAINNET = "mainnet"; 31 | export const LEATHER_TESTNET = "testnet"; 32 | export const MAINNET = "mainnet"; 33 | export const SIGNET = "signet"; 34 | export const TESTNET = "testnet"; 35 | export const TESTNET4 = "testnet4"; 36 | export const FRACTAL_MAINNET = "fractal mainnet"; 37 | export const FRACTAL_TESTNET = "fractal testnet"; 38 | export const REGTEST = "regtest"; 39 | 40 | export const getXverseNetwork = (network: string) => { 41 | if (network === MAINNET) return XVERSE_MAINNET; 42 | if (network === TESTNET) return XVERSE_TESTNET; 43 | if (network === SIGNET) return XVERSE_SIGNET; 44 | return XVERSE_MAINNET; 45 | }; 46 | 47 | export const getOrangeNetwork = (network: string): BitcoinNetworkType => { 48 | if (network === MAINNET) return ORANGE_MAINNET as BitcoinNetworkType; 49 | if (network === TESTNET) return ORANGE_TESTNET as BitcoinNetworkType; 50 | return ORANGE_MAINNET as BitcoinNetworkType; 51 | }; 52 | 53 | export const getLeatherNetwork = (network: string) => { 54 | if (network === MAINNET) return LEATHER_MAINNET; 55 | if (network === TESTNET) return LEATHER_TESTNET; 56 | return LEATHER_MAINNET; 57 | }; 58 | 59 | export const getUnisatNetwork = (network: string) => { 60 | if (network === MAINNET) return UNISAT_MAINNET; 61 | if (network === TESTNET) return UNISAT_TESTNET; 62 | if (network === TESTNET4) return UNISAT_TESTNET4; 63 | if (network === SIGNET) return UNISAT_SIGNET; 64 | if (network === FRACTAL_MAINNET) return UNISAT_FRACTAL_MAINNET; 65 | if (network === FRACTAL_TESTNET) return UNISAT_FRACTAL_TESTNET; 66 | return UNISAT_MAINNET; 67 | }; 68 | 69 | export const getWizzNetwork = (network: string) => { 70 | if (network === MAINNET) return WIZZ_MAINNET; 71 | if (network === TESTNET) return WIZZ_TESTNET; 72 | if (network === TESTNET4) return WIZZ_TESTNET4; 73 | if (network === SIGNET) return WIZZ_SIGNET; 74 | if (network === FRACTAL_TESTNET) return WIZZ_TESTNET; 75 | if (network === FRACTAL_MAINNET) return WIZZ_MAINNET; 76 | return WIZZ_MAINNET; 77 | }; 78 | 79 | export const getNetworkForUnisat = (network: string) => { 80 | if (network === UNISAT_MAINNET) return MAINNET; 81 | if (network === UNISAT_TESTNET) return TESTNET; 82 | if (network === UNISAT_TESTNET4) return TESTNET4; 83 | if (network === UNISAT_SIGNET) return SIGNET; 84 | if (network === UNISAT_FRACTAL_MAINNET) return FRACTAL_MAINNET; 85 | if (network === UNISAT_FRACTAL_TESTNET) return FRACTAL_TESTNET; 86 | return MAINNET; 87 | }; 88 | 89 | export const getNetworkForXverse = (network: string) => { 90 | if (network === XVERSE_MAINNET) return MAINNET; 91 | if (network === XVERSE_TESTNET) return TESTNET; 92 | return MAINNET; 93 | }; 94 | 95 | export const getNetworkForLeather = (network: string) => { 96 | if (network === LEATHER_MAINNET) return MAINNET; 97 | if (network === LEATHER_TESTNET) return TESTNET; 98 | return MAINNET; 99 | }; 100 | 101 | export const getNetworkForOkx = (network: string) => { 102 | if (network === OKX_MAINNET) return MAINNET; 103 | if (network === OKX_TESTNET) return TESTNET; 104 | return MAINNET; 105 | }; 106 | 107 | export const getNetworkForWizz = (network: string) => { 108 | if (network === WIZZ_MAINNET) return MAINNET; 109 | if (network === WIZZ_TESTNET) return TESTNET; 110 | if (network === WIZZ_TESTNET4) return TESTNET4; 111 | if (network === WIZZ_SIGNET) return SIGNET; 112 | if (network === FRACTAL_TESTNET) return TESTNET; 113 | if (network === FRACTAL_MAINNET) return MAINNET; 114 | 115 | return MAINNET; 116 | }; 117 | 118 | export const MEMPOOL_SPACE_URL = "https://mempool.space"; 119 | export const MEMPOOL_SPACE_TESTNET_URL = "https://mempool.space/testnet"; 120 | export const MEMPOOL_SPACE_SIGNET_URL = "https://mempool.space/signet"; 121 | 122 | export const getMempoolSpaceUrl = ( 123 | network: typeof MAINNET | typeof TESTNET | typeof SIGNET | typeof REGTEST 124 | ) => 125 | network === TESTNET 126 | ? MEMPOOL_SPACE_TESTNET_URL 127 | : network === SIGNET 128 | ? MEMPOOL_SPACE_SIGNET_URL 129 | : MEMPOOL_SPACE_URL; 130 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | import { LEATHER, MAGIC_EDEN, OYL, UNISAT, XVERSE } from "../consts/wallets"; 2 | import { 3 | FRACTAL_MAINNET, 4 | FRACTAL_TESTNET, 5 | MAINNET, 6 | REGTEST, 7 | SIGNET, 8 | TESTNET, 9 | TESTNET4, 10 | } from "../consts/networks"; 11 | 12 | export type LaserEyesContextType = { 13 | isInitializing: boolean; 14 | connected: boolean; 15 | isConnecting: boolean; 16 | publicKey: string; 17 | address: string; 18 | paymentAddress: string; 19 | paymentPublicKey: string; 20 | balance: number | undefined; 21 | network: 22 | | typeof MAINNET 23 | | typeof TESTNET 24 | | typeof TESTNET4 25 | | typeof SIGNET 26 | | typeof FRACTAL_MAINNET 27 | | typeof FRACTAL_TESTNET; 28 | library: any; 29 | provider: any; 30 | accounts: string[]; 31 | hasUnisat: boolean; 32 | hasXverse: boolean; 33 | hasOyl: boolean; 34 | hasMagicEden: boolean; 35 | hasOkx: boolean; 36 | hasLeather: boolean; 37 | hasPhantom: boolean; 38 | hasWizz: boolean; 39 | hasOrange: boolean; 40 | 41 | connect: ( 42 | walletName: 43 | | typeof UNISAT 44 | | typeof XVERSE 45 | | typeof MAGIC_EDEN 46 | | typeof LEATHER 47 | | typeof MAGIC_EDEN 48 | | typeof OYL 49 | ) => Promise; 50 | disconnect: () => void; 51 | requestAccounts: () => Promise; 52 | getNetwork: () => Promise; 53 | switchNetwork: ( 54 | network: 55 | | typeof MAINNET 56 | | typeof TESTNET 57 | | typeof TESTNET4 58 | | typeof SIGNET 59 | | typeof FRACTAL_MAINNET 60 | | typeof FRACTAL_TESTNET 61 | ) => Promise; 62 | getPublicKey: () => Promise; 63 | getBalance: () => Promise; 64 | getInscriptions: () => Promise; 65 | sendBTC: (to: string, amount: number) => Promise; 66 | signMessage: (message: string, toSignAddress?: string) => Promise; 67 | signPsbt: ( 68 | tx: string, 69 | finalize?: boolean, 70 | broadcast?: boolean 71 | ) => Promise< 72 | | { 73 | signedPsbtHex: string | undefined; 74 | signedPsbtBase64: string | undefined; 75 | txId?: string; 76 | } 77 | | undefined 78 | >; 79 | pushPsbt: (tx: string) => Promise; 80 | }; 81 | 82 | export type Config = { 83 | network: 84 | | typeof MAINNET 85 | | typeof TESTNET 86 | | typeof TESTNET4 87 | | typeof SIGNET 88 | | typeof FRACTAL_MAINNET 89 | | typeof FRACTAL_TESTNET; 90 | }; 91 | 92 | export interface OYLBalanceResponse { 93 | brc20s: { 94 | total: number; 95 | }; 96 | btc: { 97 | pending: number; 98 | confirmed: number; 99 | total: number; 100 | }; 101 | overall: { 102 | pending: number; 103 | confirmed: number; 104 | total: number; 105 | }; 106 | } 107 | 108 | export interface LeatherRPCResponse { 109 | jsonrpc: string; 110 | id: string; 111 | result: LeatherRequestAddressResponse | LeatherRequestSignResponse; 112 | } 113 | 114 | export interface LeatherRequestAddressResponse { 115 | addresses: LeatherAddress[]; 116 | } 117 | 118 | export interface LeatherRequestSignResponse { 119 | hex: string; 120 | } 121 | 122 | export interface LeatherAddress { 123 | symbol: string; 124 | type?: string; 125 | address: string; 126 | publicKey?: string; 127 | derivationPath?: string; 128 | tweakedPublicKey?: string; 129 | } 130 | 131 | export interface BlockchainInfoResponse { 132 | notice: string; 133 | unspent_outputs: BlockchainInfoUTXO[]; 134 | } 135 | 136 | export interface BlockchainInfoUTXO { 137 | tx_hash_big_endian: string; 138 | tx_hash: string; 139 | tx_output_n: number; 140 | script: string; 141 | value: number; 142 | value_hex: string; 143 | confirmations: number; 144 | tx_index: number; 145 | } 146 | 147 | export interface MempoolUtxo { 148 | txid: string; 149 | vout: number; 150 | status: { 151 | confirmed: boolean; 152 | block_height: number; 153 | block_hash: string; 154 | block_time: number; 155 | }; 156 | value: number; 157 | } 158 | 159 | export type PhantomBtcAccount = { 160 | address: string; 161 | addressType: "p2tr" | "p2wpkh" | "p2sh" | "p2pkh"; 162 | publicKey: string; 163 | purpose: "payment" | "ordinals"; 164 | }; 165 | 166 | export type WizzBalanceResponse = { 167 | unconfirmed: number; 168 | confirmed: number; 169 | total: number; 170 | }; 171 | 172 | export interface UTXO { 173 | tx_hash_big_endian: string; 174 | tx_hash: string; 175 | tx_output_n: number; 176 | script: string; 177 | value: number; 178 | value_hex: string; 179 | confirmations: number; 180 | tx_index: number; 181 | } 182 | 183 | export interface CommitPsbtResponse { 184 | inscriberAddress: string; 185 | psbtHex: string; 186 | psbtBase64: string; 187 | feeRate: number; 188 | totalFees: number; 189 | } 190 | 191 | export interface MempoolTransaction { 192 | txid: string; 193 | version: number; 194 | locktime: number; 195 | vin: Vin[]; 196 | vout: Vout[]; 197 | size: number; 198 | weight: number; 199 | sigops: number; 200 | fee: number; 201 | status: Status; 202 | } 203 | 204 | export interface Vin { 205 | txid: string; 206 | vout: number; 207 | prevout: Prevout; 208 | scriptsig: string; 209 | scriptsig_asm: string; 210 | is_coinbase: boolean; 211 | sequence: number; 212 | } 213 | 214 | export interface Prevout { 215 | scriptpubkey: string; 216 | scriptpubkey_asm: string; 217 | scriptpubkey_type: string; 218 | scriptpubkey_address: string; 219 | value: number; 220 | } 221 | 222 | export interface Vout { 223 | scriptpubkey: string; 224 | scriptpubkey_asm: string; 225 | scriptpubkey_type: string; 226 | scriptpubkey_address: string; 227 | value: number; 228 | } 229 | 230 | export interface Status { 231 | confirmed: boolean; 232 | block_height: number; 233 | block_hash: string; 234 | block_time: number; 235 | } 236 | -------------------------------------------------------------------------------- /example/components/ui/select.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import * as React from 'react' 4 | import * as SelectPrimitive from '@radix-ui/react-select' 5 | import { Check, ChevronDown, ChevronUp } from 'lucide-react' 6 | 7 | import { cn } from '@/lib/utils' 8 | 9 | const Select = SelectPrimitive.Root 10 | 11 | const SelectGroup = SelectPrimitive.Group 12 | 13 | const SelectValue = SelectPrimitive.Value 14 | 15 | const SelectTrigger = React.forwardRef< 16 | React.ElementRef, 17 | React.ComponentPropsWithoutRef 18 | >(({ className, children, ...props }, ref) => ( 19 | span]:line-clamp-1', 23 | className 24 | )} 25 | {...props} 26 | > 27 | {children} 28 | 29 | )) 30 | SelectTrigger.displayName = SelectPrimitive.Trigger.displayName 31 | 32 | const SelectScrollUpButton = React.forwardRef< 33 | React.ElementRef, 34 | React.ComponentPropsWithoutRef 35 | >(({ className, ...props }, ref) => ( 36 | 44 | 45 | 46 | )) 47 | SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName 48 | 49 | const SelectScrollDownButton = React.forwardRef< 50 | React.ElementRef, 51 | React.ComponentPropsWithoutRef 52 | >(({ className, ...props }, ref) => ( 53 | 61 | 62 | 63 | )) 64 | SelectScrollDownButton.displayName = 65 | SelectPrimitive.ScrollDownButton.displayName 66 | 67 | const SelectContent = React.forwardRef< 68 | React.ElementRef, 69 | React.ComponentPropsWithoutRef 70 | >(({ className, children, position = 'popper', ...props }, ref) => ( 71 | 72 | 83 | 84 | 91 | {children} 92 | 93 | 94 | 95 | 96 | )) 97 | SelectContent.displayName = SelectPrimitive.Content.displayName 98 | 99 | const SelectLabel = React.forwardRef< 100 | React.ElementRef, 101 | React.ComponentPropsWithoutRef 102 | >(({ className, ...props }, ref) => ( 103 | 108 | )) 109 | SelectLabel.displayName = SelectPrimitive.Label.displayName 110 | 111 | const SelectItem = React.forwardRef< 112 | React.ElementRef, 113 | React.ComponentPropsWithoutRef 114 | >(({ className, children, ...props }, ref) => ( 115 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | {children} 130 | 131 | )) 132 | SelectItem.displayName = SelectPrimitive.Item.displayName 133 | 134 | const SelectSeparator = React.forwardRef< 135 | React.ElementRef, 136 | React.ComponentPropsWithoutRef 137 | >(({ className, ...props }, ref) => ( 138 | 143 | )) 144 | SelectSeparator.displayName = SelectPrimitive.Separator.displayName 145 | 146 | export { 147 | Select, 148 | SelectGroup, 149 | SelectValue, 150 | SelectTrigger, 151 | SelectContent, 152 | SelectLabel, 153 | SelectItem, 154 | SelectSeparator, 155 | SelectScrollUpButton, 156 | SelectScrollDownButton, 157 | } 158 | -------------------------------------------------------------------------------- /example/public/lasereyes_connected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/lib/helpers.ts: -------------------------------------------------------------------------------- 1 | import * as bitcoin from "bitcoinjs-lib"; 2 | 3 | import { 4 | FRACTAL_MAINNET, 5 | FRACTAL_TESTNET, 6 | MAINNET, 7 | SIGNET, 8 | TESTNET, 9 | TESTNET4, 10 | } from "../consts/networks"; 11 | import axios from "axios"; 12 | import { MempoolUtxo } from "../types"; 13 | import { getMempoolSpaceUrl } from "./urls"; 14 | import * as ecc from "@bitcoinerlab/secp256k1"; 15 | 16 | bitcoin.initEccLib(ecc); 17 | 18 | export const getBitcoinNetwork = ( 19 | network: 20 | | typeof MAINNET 21 | | typeof TESTNET 22 | | typeof TESTNET4 23 | | typeof SIGNET 24 | | typeof FRACTAL_MAINNET 25 | | typeof FRACTAL_TESTNET 26 | ) => { 27 | if (network === TESTNET || network === TESTNET4 || network === SIGNET) { 28 | return bitcoin.networks.testnet; 29 | } else { 30 | return bitcoin.networks.bitcoin; 31 | } 32 | }; 33 | 34 | export const findOrdinalsAddress = (addresses: any) => { 35 | return addresses.find( 36 | ({ purpose }: { purpose: string }) => purpose === "ordinals" 37 | ); 38 | }; 39 | 40 | export const findPaymentAddress = (addresses: any) => { 41 | return addresses.find( 42 | ({ purpose }: { purpose: string }) => purpose === "payment" 43 | ); 44 | }; 45 | 46 | export const getBTCBalance = async ( 47 | address: string, 48 | network: 49 | | typeof MAINNET 50 | | typeof TESTNET 51 | | typeof TESTNET4 52 | | typeof SIGNET 53 | | typeof FRACTAL_MAINNET 54 | | typeof FRACTAL_TESTNET 55 | ): Promise => { 56 | try { 57 | const utxos: MempoolUtxo[] | undefined = await getAddressUtxos( 58 | address, 59 | network 60 | ); 61 | if (!utxos) return 0; 62 | return utxos.reduce((acc, utxo) => acc + utxo.value, 0); 63 | } catch (error) { 64 | console.error("Error fetching BTC balance:", error); 65 | throw new Error("Failed to fetch BTC balance"); 66 | } 67 | }; 68 | 69 | export const satoshisToBTC = (satoshis: number): string => { 70 | if (Number.isNaN(satoshis) || satoshis === undefined) return "--"; 71 | const btcValue = satoshis / 100000000; 72 | return btcValue.toFixed(8); 73 | }; 74 | 75 | export const isBase64 = (str: string): boolean => { 76 | const base64Regex = 77 | /^(?:[A-Za-z0-9+\/]{4})*?(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/; 78 | return base64Regex.test(str); 79 | }; 80 | 81 | export const isHex = (str: string): boolean => { 82 | const hexRegex = /^[a-fA-F0-9]+$/; 83 | return hexRegex.test(str); 84 | }; 85 | 86 | export function estimateTxSize( 87 | taprootInputCount: number, 88 | nonTaprootInputCount: number, 89 | outputCount: number 90 | ): number { 91 | const baseTxSize = 10; 92 | const taprootInputSize = 57; 93 | const nonTaprootInputSize = 41; 94 | const outputSize = 34; 95 | const totalInputSize = 96 | taprootInputCount * taprootInputSize + 97 | nonTaprootInputCount * nonTaprootInputSize; 98 | const totalOutputSize = outputCount * outputSize; 99 | return baseTxSize + totalInputSize + totalOutputSize; 100 | } 101 | 102 | export async function getAddressUtxos( 103 | address: string, 104 | network: 105 | | typeof MAINNET 106 | | typeof TESTNET 107 | | typeof TESTNET4 108 | | typeof SIGNET 109 | | typeof FRACTAL_MAINNET 110 | | typeof FRACTAL_TESTNET 111 | ) { 112 | if (address.startsWith("t")) { 113 | if (network === MAINNET) { 114 | return []; 115 | } 116 | if (network === FRACTAL_MAINNET) { 117 | return []; 118 | } 119 | if (network === FRACTAL_TESTNET) { 120 | return []; 121 | } 122 | } 123 | return (await axios 124 | .get(`${getMempoolSpaceUrl(network)}/api/address/${address}/utxo`) 125 | .then((response) => response.data)) as Array; 126 | } 127 | 128 | export async function createSendBtcPsbt( 129 | address: string, 130 | paymentAddress: string, 131 | recipientAddress: string, 132 | amount: number, 133 | paymentPublicKey: string, 134 | network: 135 | | typeof MAINNET 136 | | typeof TESTNET 137 | | typeof TESTNET4 138 | | typeof SIGNET 139 | | typeof FRACTAL_MAINNET 140 | | typeof FRACTAL_TESTNET, 141 | feeRate: number = 7 142 | ) { 143 | const isTaprootOnly = address === paymentAddress; 144 | const utxos: MempoolUtxo[] | undefined = await getAddressUtxos( 145 | paymentAddress, 146 | network 147 | ); 148 | 149 | if (!utxos) { 150 | throw new Error("No UTXOs found"); 151 | } 152 | 153 | const sortedUtxos = utxos.sort( 154 | (a: { value: number }, b: { value: number }) => b.value - a.value 155 | ); 156 | 157 | const psbt = new bitcoin.Psbt({ network: getBitcoinNetwork(network) }); 158 | 159 | const estTxSize = estimateTxSize(1, 0, 2); 160 | const satsNeeded = Math.floor(estTxSize * feeRate) + amount; 161 | let amountGathered = 0; 162 | let counter = 0; 163 | for await (let utxo of sortedUtxos) { 164 | const { txid, vout, value } = utxo; 165 | const script = bitcoin.address.toOutputScript( 166 | paymentAddress, 167 | getBitcoinNetwork(network) 168 | ); 169 | psbt.addInput({ 170 | hash: txid, 171 | index: vout, 172 | witnessUtxo: { 173 | script, 174 | value, 175 | }, 176 | }); 177 | 178 | if (!isTaprootOnly) { 179 | const redeemScript = getRedeemScript(paymentPublicKey, network); 180 | psbt.updateInput(counter, { redeemScript }); 181 | } 182 | 183 | amountGathered += value; 184 | if (amountGathered >= satsNeeded) { 185 | break; 186 | } 187 | } 188 | 189 | if (amountGathered < satsNeeded) { 190 | throw new Error("Insufficient funds"); 191 | } 192 | 193 | psbt.addOutput({ 194 | address: recipientAddress, 195 | value: amount, 196 | }); 197 | 198 | if (amountGathered > satsNeeded) { 199 | psbt.addOutput({ 200 | address: paymentAddress, 201 | value: amountGathered - satsNeeded, 202 | }); 203 | } 204 | 205 | return { 206 | psbtBase64: psbt.toBase64(), 207 | psbtHex: psbt.toHex(), 208 | }; 209 | } 210 | 211 | export function getRedeemScript( 212 | paymentPublicKey: string, 213 | network: 214 | | typeof MAINNET 215 | | typeof TESTNET 216 | | typeof TESTNET4 217 | | typeof SIGNET 218 | | typeof FRACTAL_MAINNET 219 | | typeof FRACTAL_TESTNET 220 | ) { 221 | const p2wpkh = bitcoin.payments.p2wpkh({ 222 | pubkey: Buffer.from(paymentPublicKey, "hex"), 223 | network: getBitcoinNetwork(network), 224 | }); 225 | 226 | const p2sh = bitcoin.payments.p2sh({ 227 | redeem: p2wpkh, 228 | network: getBitcoinNetwork(network), 229 | }); 230 | return p2sh?.redeem?.output; 231 | } 232 | 233 | export function delay(ms: number) { 234 | return new Promise((resolve) => setTimeout(resolve, ms)); 235 | } 236 | -------------------------------------------------------------------------------- /example/lib/btc.ts: -------------------------------------------------------------------------------- 1 | import { IMempoolUtxo } from '@/types/btc' 2 | import { 3 | FRACTAL_MAINNET, 4 | FRACTAL_TESTNET, 5 | MAINNET, 6 | P2PKH, 7 | P2SH, 8 | P2TR, 9 | P2WPKH, 10 | P2WSH, 11 | SIGNET, 12 | TESTNET, 13 | TESTNET4, 14 | } from '@omnisat/lasereyes' 15 | import * as bitcoin from 'bitcoinjs-lib' 16 | import { Psbt } from 'bitcoinjs-lib' 17 | import * as ecc2 from '@bitcoinerlab/secp256k1' 18 | import axios from 'axios' 19 | import { getMempoolSpaceUrl } from '@/lib/urls' 20 | 21 | bitcoin.initEccLib(ecc2) 22 | 23 | export const satoshisToBTC = (satoshis: number): string => { 24 | if (Number.isNaN(satoshis) || satoshis === undefined) return '--' 25 | const btcValue = satoshis / 100000000 26 | if (Number.isNaN(btcValue)) return '--' 27 | return btcValue.toFixed(8) 28 | } 29 | 30 | export const getBtcJsNetwork = (network: string): bitcoin.networks.Network => { 31 | return network === 'mainnet' || 32 | network === 'fractal_mainnet' || 33 | network === 'fractal_testnet' 34 | ? bitcoin.networks.bitcoin 35 | : bitcoin.networks.testnet 36 | } 37 | 38 | export const P2SH_P2WPKH = 'p2sh-p2wpkh' 39 | 40 | export const getAddressType = ( 41 | address: string, 42 | network: bitcoin.Network 43 | ): string => { 44 | try { 45 | const decoded = bitcoin.address.fromBase58Check(address) 46 | 47 | // Check the address version for P2PKH or P2SH 48 | if (decoded.version === network.pubKeyHash) return P2PKH 49 | if (decoded.version === network.scriptHash) { 50 | // It's a P2SH, but let's check if it wraps a SegWit script 51 | const script = bitcoin.script.decompile(decoded.hash) 52 | if (script && script.length === 2 && script[0] === bitcoin.opcodes.OP_0) { 53 | return P2SH_P2WPKH // Nested SegWit (P2SH-P2WPKH) 54 | } 55 | return P2SH 56 | } 57 | } catch (e) { 58 | // If fromBase58Check fails, try Bech32 (for SegWit addresses) 59 | try { 60 | const decoded = bitcoin.address.fromBech32(address) 61 | 62 | // Handle Bech32-based addresses (SegWit P2WPKH, P2WSH, P2TR) 63 | if (decoded.version === 0 && decoded.data.length === 20) return P2WPKH 64 | if (decoded.version === 0 && decoded.data.length === 32) return P2WSH 65 | if (decoded.version === 1 && decoded.data.length === 32) return P2TR 66 | } catch (e2) { 67 | return 'unknown' 68 | } 69 | } 70 | 71 | return 'unknown' 72 | } 73 | 74 | export async function createPsbt( 75 | inputs: IMempoolUtxo[], 76 | outputAddress: string, 77 | paymentPublicKey: string, 78 | network: 79 | | typeof MAINNET 80 | | typeof TESTNET 81 | | typeof TESTNET4 82 | | typeof SIGNET 83 | | typeof FRACTAL_MAINNET 84 | | typeof FRACTAL_TESTNET 85 | ) { 86 | if (!outputAddress) return 87 | const utxoWithMostValue = inputs.reduce((acc, utxo) => { 88 | if (utxo.value > acc.value) { 89 | return utxo 90 | } 91 | return acc 92 | }) 93 | const btcNetwork = getBtcJsNetwork(network) 94 | const psbt = new Psbt({ 95 | network: btcNetwork, 96 | }) 97 | const script = bitcoin.address.toOutputScript( 98 | outputAddress, 99 | getBitcoinNetwork(network) 100 | ) 101 | if (!script) { 102 | throw new Error('Invalid output address') 103 | } 104 | if (getAddressType(outputAddress, getBtcJsNetwork(network)) === P2SH) { 105 | console.log('fetching tx hex') 106 | const txHexResponse = await axios.get( 107 | `${getMempoolSpaceUrl(network)}/api/tx/${utxoWithMostValue.txid}/hex` 108 | ) 109 | const txHex = txHexResponse.data 110 | 111 | psbt.addInput({ 112 | hash: utxoWithMostValue.txid, 113 | index: utxoWithMostValue.vout, 114 | nonWitnessUtxo: Buffer.from(txHex, 'hex'), 115 | }) 116 | 117 | // If the address type is Nested SegWit (P2SH-P2WPKH), add redeemScript 118 | // if ( 119 | // getAddressType(outputAddress, getBtcJsNetwork(network)) === P2SH_P2WPKH 120 | // ) { 121 | console.log('adding redeem script') 122 | const redeemScript = getRedeemScript(paymentPublicKey, network) 123 | psbt.updateInput(0, { redeemScript }) 124 | // } 125 | } else { 126 | // Handle P2PKH (legacy) with witnessUtxo 127 | psbt.addInput({ 128 | hash: utxoWithMostValue.txid, 129 | index: utxoWithMostValue.vout, 130 | witnessUtxo: { 131 | script, 132 | value: utxoWithMostValue.value, 133 | }, 134 | }) 135 | } 136 | 137 | if (utxoWithMostValue.value - 546 < 1000) { 138 | throw new Error("Couldn't create test psbt: Insufficient funds") 139 | } 140 | 141 | psbt.addOutput({ 142 | address: outputAddress, 143 | value: utxoWithMostValue.value - 1000, 144 | }) 145 | 146 | return psbt 147 | } 148 | 149 | export function getRedeemScript( 150 | paymentPublicKey: string, 151 | network: 152 | | typeof MAINNET 153 | | typeof TESTNET 154 | | typeof SIGNET 155 | | typeof TESTNET4 156 | | typeof FRACTAL_MAINNET 157 | | typeof FRACTAL_TESTNET 158 | ) { 159 | const p2wpkh = bitcoin.payments.p2wpkh({ 160 | pubkey: Buffer.from(paymentPublicKey, 'hex'), 161 | network: getBitcoinNetwork(network), 162 | }) 163 | 164 | const p2sh = bitcoin.payments.p2sh({ 165 | redeem: p2wpkh, 166 | network: getBitcoinNetwork(network), 167 | }) 168 | return p2sh?.redeem?.output 169 | } 170 | 171 | export function estimateTxSize( 172 | taprootInputCount: number, 173 | nonTaprootInputCount: number, 174 | outputCount: number 175 | ): number { 176 | const baseTxSize = 10 177 | const taprootInputSize = 57 178 | const nonTaprootInputSize = 41 179 | const outputSize = 34 180 | const totalInputSize = 181 | taprootInputCount * taprootInputSize + 182 | nonTaprootInputCount * nonTaprootInputSize 183 | const totalOutputSize = outputCount * outputSize 184 | return baseTxSize + totalInputSize + totalOutputSize 185 | } 186 | 187 | // export function getAddressType(address: string) { 188 | // try { 189 | // bitcoin.address.fromBase58Check(address) 190 | // return P2PKH 191 | // } catch (e) {} 192 | // 193 | // try { 194 | // bitcoin.address.fromBase58Check(address) 195 | // return 'p2psh' 196 | // } catch (e) {} 197 | // 198 | // try { 199 | // const { version, data } = bitcoin.address.fromBech32(address) 200 | // if (version === 0) { 201 | // if (data.length === 20) { 202 | // return P2WPKH 203 | // } else if (data.length === 32) { 204 | // return P2WSH 205 | // } 206 | // } else if (version === 1 && data.length === 32) { 207 | // return P2TR 208 | // } 209 | // } catch (e) {} 210 | // 211 | // throw new Error('Invalid address') 212 | // } 213 | 214 | export const getBitcoinNetwork = ( 215 | network: 216 | | typeof MAINNET 217 | | typeof TESTNET 218 | | typeof TESTNET4 219 | | typeof SIGNET 220 | | typeof FRACTAL_MAINNET 221 | | typeof FRACTAL_TESTNET 222 | ) => { 223 | if (network === TESTNET || network === TESTNET4 || network === SIGNET) { 224 | return bitcoin.networks.testnet 225 | } else { 226 | return bitcoin.networks.bitcoin 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /github/main/content/sponsors/sat-ventures-dark-mode.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import { BitcoinNetworkType } from '@orangecrypto/orange-connect'; 2 | import * as react_jsx_runtime from 'react/jsx-runtime'; 3 | import * as React from 'react'; 4 | import { ReactNode } from 'react'; 5 | import * as bitcoin from 'bitcoinjs-lib'; 6 | 7 | declare const XVERSE_NETWORK: "Mainnet" | "Testnet"; 8 | declare const UNISAT_MAINNET = "BITCOIN_MAINNET"; 9 | declare const UNISAT_TESTNET = "BITCOIN_TESTNET"; 10 | declare const UNISAT_TESTNET4 = "BITCOIN_TESTNET4"; 11 | declare const UNISAT_SIGNET = "BITCOIN_SIGNET"; 12 | declare const UNISAT_FRACTAL_MAINNET = "FRACTAL_BITCOIN_MAINNET"; 13 | declare const UNISAT_FRACTAL_TESTNET = "FRACTAL_BITCOIN_TESTNET"; 14 | declare const OP_WALLET_MAINNET = "livenet"; 15 | declare const OP_WALLET_TESTNET = "testnet"; 16 | declare const XVERSE_MAINNET = "Mainnet"; 17 | declare const XVERSE_TESTNET = "Testnet"; 18 | declare const XVERSE_SIGNET = "Signet"; 19 | declare const OKX_MAINNET = "livenet"; 20 | declare const OKX_TESTNET = "testnet"; 21 | declare const WIZZ_MAINNET = "livenet"; 22 | declare const WIZZ_TESTNET = "testnet"; 23 | declare const WIZZ_TESTNET4 = "testnet4"; 24 | declare const WIZZ_SIGNET = "signet"; 25 | declare const ORANGE_MAINNET = "Mainnet"; 26 | declare const ORANGE_TESTNET = "Testnet"; 27 | declare const LEATHER_MAINNET = "mainnet"; 28 | declare const LEATHER_TESTNET = "testnet"; 29 | declare const MAINNET = "mainnet"; 30 | declare const SIGNET = "signet"; 31 | declare const TESTNET = "testnet"; 32 | declare const TESTNET4 = "testnet4"; 33 | declare const FRACTAL_MAINNET = "fractal mainnet"; 34 | declare const FRACTAL_TESTNET = "fractal testnet"; 35 | declare const REGTEST = "regtest"; 36 | declare const getXverseNetwork: (network: string) => "Mainnet" | "Testnet" | "Signet"; 37 | declare const getOrangeNetwork: (network: string) => BitcoinNetworkType; 38 | declare const getLeatherNetwork: (network: string) => "testnet" | "mainnet"; 39 | declare const getUnisatNetwork: (network: string) => "BITCOIN_MAINNET" | "BITCOIN_TESTNET" | "BITCOIN_TESTNET4" | "BITCOIN_SIGNET" | "FRACTAL_BITCOIN_MAINNET" | "FRACTAL_BITCOIN_TESTNET"; 40 | declare const getWizzNetwork: (network: string) => "livenet" | "testnet" | "testnet4" | "signet"; 41 | declare const getNetworkForUnisat: (network: string) => "testnet" | "testnet4" | "signet" | "mainnet" | "fractal mainnet" | "fractal testnet"; 42 | declare const getNetworkForXverse: (network: string) => "testnet" | "mainnet"; 43 | declare const getNetworkForLeather: (network: string) => "testnet" | "mainnet"; 44 | declare const getNetworkForOkx: (network: string) => "testnet" | "mainnet"; 45 | declare const getNetworkForWizz: (network: string) => "testnet" | "testnet4" | "signet" | "mainnet"; 46 | declare const MEMPOOL_SPACE_URL = "https://mempool.space"; 47 | declare const MEMPOOL_SPACE_TESTNET_URL = "https://mempool.space/testnet"; 48 | declare const MEMPOOL_SPACE_SIGNET_URL = "https://mempool.space/signet"; 49 | declare const getMempoolSpaceUrl: (network: typeof MAINNET | typeof TESTNET | typeof SIGNET | typeof REGTEST) => "https://mempool.space" | "https://mempool.space/testnet" | "https://mempool.space/signet"; 50 | 51 | declare const OYL = "oyl"; 52 | declare const UNISAT = "unisat"; 53 | declare const XVERSE = "xverse"; 54 | declare const PHANTOM = "phantom"; 55 | declare const LEATHER = "leather"; 56 | declare const MAGIC_EDEN = "magic-eden"; 57 | declare const OKX = "okx"; 58 | declare const WIZZ = "wizz"; 59 | declare const ORANGE = "orange"; 60 | declare const P2TR = "p2tr"; 61 | declare const P2PKH = "p2pkh"; 62 | declare const P2WPKH = "p2wpkh"; 63 | declare const P2PSH = "p2psh"; 64 | declare const P2WSH = "p2wsh"; 65 | declare const P2SH = "p2sh"; 66 | declare const WALLETS: { 67 | oyl: { 68 | name: string; 69 | icon: string; 70 | site: string; 71 | disabled: boolean; 72 | }; 73 | unisat: { 74 | name: string; 75 | icon: string; 76 | site: string; 77 | disabled: boolean; 78 | }; 79 | xverse: { 80 | name: string; 81 | icon: string; 82 | site: string; 83 | config: () => void; 84 | disabled: boolean; 85 | }; 86 | leather: { 87 | name: string; 88 | icon: string; 89 | site: string; 90 | config: () => void; 91 | disabled: boolean; 92 | }; 93 | }; 94 | 95 | type LaserEyesContextType = { 96 | isInitializing: boolean; 97 | connected: boolean; 98 | isConnecting: boolean; 99 | publicKey: string; 100 | address: string; 101 | paymentAddress: string; 102 | paymentPublicKey: string; 103 | balance: number | undefined; 104 | network: typeof MAINNET | typeof TESTNET | typeof TESTNET4 | typeof SIGNET | typeof FRACTAL_MAINNET | typeof FRACTAL_TESTNET; 105 | library: any; 106 | provider: any; 107 | accounts: string[]; 108 | hasUnisat: boolean; 109 | hasXverse: boolean; 110 | hasOyl: boolean; 111 | hasMagicEden: boolean; 112 | hasOkx: boolean; 113 | hasLeather: boolean; 114 | hasPhantom: boolean; 115 | hasWizz: boolean; 116 | hasOrange: boolean; 117 | connect: (walletName: typeof UNISAT | typeof XVERSE | typeof MAGIC_EDEN | typeof LEATHER | typeof MAGIC_EDEN | typeof OYL) => Promise; 118 | disconnect: () => void; 119 | requestAccounts: () => Promise; 120 | getNetwork: () => Promise; 121 | switchNetwork: (network: typeof MAINNET | typeof TESTNET | typeof TESTNET4 | typeof SIGNET | typeof FRACTAL_MAINNET | typeof FRACTAL_TESTNET) => Promise; 122 | getPublicKey: () => Promise; 123 | getBalance: () => Promise; 124 | getInscriptions: () => Promise; 125 | sendBTC: (to: string, amount: number) => Promise; 126 | signMessage: (message: string, toSignAddress?: string) => Promise; 127 | signPsbt: (tx: string, finalize?: boolean, broadcast?: boolean) => Promise<{ 128 | signedPsbtHex: string | undefined; 129 | signedPsbtBase64: string | undefined; 130 | txId?: string; 131 | } | undefined>; 132 | pushPsbt: (tx: string) => Promise; 133 | }; 134 | type Config = { 135 | network: typeof MAINNET | typeof TESTNET | typeof TESTNET4 | typeof SIGNET | typeof FRACTAL_MAINNET | typeof FRACTAL_TESTNET; 136 | }; 137 | interface MempoolUtxo { 138 | txid: string; 139 | vout: number; 140 | status: { 141 | confirmed: boolean; 142 | block_height: number; 143 | block_hash: string; 144 | block_time: number; 145 | }; 146 | value: number; 147 | } 148 | 149 | declare const createConfig: (config: Config) => Config; 150 | 151 | declare const useLaserEyes: () => LaserEyesContextType; 152 | declare const LaserEyesProvider: ({ children, config, }: { 153 | children: ReactNode; 154 | config?: Config | undefined; 155 | }) => react_jsx_runtime.JSX.Element; 156 | 157 | declare const getBitcoinNetwork: (network: typeof MAINNET | typeof TESTNET | typeof TESTNET4 | typeof SIGNET | typeof FRACTAL_MAINNET | typeof FRACTAL_TESTNET) => bitcoin.networks.Network; 158 | declare const findOrdinalsAddress: (addresses: any) => any; 159 | declare const findPaymentAddress: (addresses: any) => any; 160 | declare const getBTCBalance: (address: string, network: typeof MAINNET | typeof TESTNET | typeof TESTNET4 | typeof SIGNET | typeof FRACTAL_MAINNET | typeof FRACTAL_TESTNET) => Promise; 161 | declare const satoshisToBTC: (satoshis: number) => string; 162 | declare const isBase64: (str: string) => boolean; 163 | declare const isHex: (str: string) => boolean; 164 | declare function estimateTxSize(taprootInputCount: number, nonTaprootInputCount: number, outputCount: number): number; 165 | declare function getAddressUtxos(address: string, network: typeof MAINNET | typeof TESTNET | typeof TESTNET4 | typeof SIGNET | typeof FRACTAL_MAINNET | typeof FRACTAL_TESTNET): Promise; 166 | declare function createSendBtcPsbt(address: string, paymentAddress: string, recipientAddress: string, amount: number, paymentPublicKey: string, network: typeof MAINNET | typeof TESTNET | typeof TESTNET4 | typeof SIGNET | typeof FRACTAL_MAINNET | typeof FRACTAL_TESTNET, feeRate?: number): Promise<{ 167 | psbtBase64: string; 168 | psbtHex: string; 169 | }>; 170 | declare function getRedeemScript(paymentPublicKey: string, network: typeof MAINNET | typeof TESTNET | typeof TESTNET4 | typeof SIGNET | typeof FRACTAL_MAINNET | typeof FRACTAL_TESTNET): Buffer | undefined; 171 | declare function delay(ms: number): Promise; 172 | 173 | interface OylLogoProps extends React.SVGProps { 174 | size?: number; 175 | variant?: "first" | "second"; 176 | } 177 | declare const OylLogo: React.FC; 178 | 179 | interface LeatherLogoProps extends React.SVGProps { 180 | size?: number; 181 | variant?: "first" | "second"; 182 | } 183 | declare const LeatherLogo: React.FC; 184 | 185 | interface PhantomLogoProps extends React.SVGProps { 186 | size?: number; 187 | variant?: "first" | "second"; 188 | } 189 | declare const PhantomLogo: React.FC; 190 | 191 | interface XverseLogoProps extends React.SVGProps { 192 | size?: number; 193 | variant?: "first" | "second"; 194 | } 195 | declare const XverseLogo: React.FC; 196 | 197 | interface UnisatLogoProps extends React.SVGProps { 198 | size?: number; 199 | variant?: "first" | "second"; 200 | } 201 | declare const UnisatLogo: React.FC; 202 | 203 | interface WizzLogoProps extends React.SVGProps { 204 | size?: number; 205 | variant?: "first" | "second"; 206 | } 207 | declare const WizzLogo: React.FC; 208 | 209 | interface OkxLogoProps extends React.SVGProps { 210 | size?: number; 211 | variant?: "first" | "second"; 212 | } 213 | declare const OkxLogo: React.FC; 214 | 215 | interface MagicedenLogoProps extends React.SVGProps { 216 | size?: number; 217 | variant?: "first" | "second"; 218 | } 219 | declare const MagicEdenLogo: React.FC; 220 | 221 | declare const WalletIcon: ({ size, className, variant, walletName, }: { 222 | size: number; 223 | className?: string | undefined; 224 | variant?: "first" | "second" | undefined; 225 | walletName: typeof XVERSE | typeof WIZZ | typeof LEATHER | typeof MAGIC_EDEN | typeof OKX | typeof PHANTOM | typeof UNISAT | typeof OYL | typeof ORANGE; 226 | }) => react_jsx_runtime.JSX.Element; 227 | 228 | export { FRACTAL_MAINNET, FRACTAL_TESTNET, LEATHER, LEATHER_MAINNET, LEATHER_TESTNET, LaserEyesProvider, LeatherLogo, MAGIC_EDEN, MAINNET, MEMPOOL_SPACE_SIGNET_URL, MEMPOOL_SPACE_TESTNET_URL, MEMPOOL_SPACE_URL, MagicEdenLogo, OKX, OKX_MAINNET, OKX_TESTNET, OP_WALLET_MAINNET, OP_WALLET_TESTNET, ORANGE, ORANGE_MAINNET, ORANGE_TESTNET, OYL, OkxLogo, OylLogo, P2PKH, P2PSH, P2SH, P2TR, P2WPKH, P2WSH, PHANTOM, PhantomLogo, REGTEST, SIGNET, TESTNET, TESTNET4, UNISAT, UNISAT_FRACTAL_MAINNET, UNISAT_FRACTAL_TESTNET, UNISAT_MAINNET, UNISAT_SIGNET, UNISAT_TESTNET, UNISAT_TESTNET4, UnisatLogo, WALLETS, WIZZ, WIZZ_MAINNET, WIZZ_SIGNET, WIZZ_TESTNET, WIZZ_TESTNET4, WalletIcon, WizzLogo, XVERSE, XVERSE_MAINNET, XVERSE_NETWORK, XVERSE_SIGNET, XVERSE_TESTNET, XverseLogo, createConfig, createSendBtcPsbt, delay, estimateTxSize, findOrdinalsAddress, findPaymentAddress, getAddressUtxos, getBTCBalance, getBitcoinNetwork, getLeatherNetwork, getMempoolSpaceUrl, getNetworkForLeather, getNetworkForOkx, getNetworkForUnisat, getNetworkForWizz, getNetworkForXverse, getOrangeNetwork, getRedeemScript, getUnisatNetwork, getWizzNetwork, getXverseNetwork, isBase64, isHex, satoshisToBTC, useLaserEyes }; 229 | -------------------------------------------------------------------------------- /example/components/ui/chart.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import * as React from 'react' 4 | import * as RechartsPrimitive from 'recharts' 5 | 6 | import { cn } from '@/lib/utils' 7 | 8 | // Format: { THEME_NAME: CSS_SELECTOR } 9 | const THEMES = { light: '', dark: '.dark' } as const 10 | 11 | export type ChartConfig = { 12 | [k in string]: { 13 | label?: React.ReactNode 14 | icon?: React.ComponentType 15 | } & ( 16 | | { color?: string; theme?: never } 17 | | { color?: never; theme: Record } 18 | ) 19 | } 20 | 21 | type ChartContextProps = { 22 | config: ChartConfig 23 | } 24 | 25 | const ChartContext = React.createContext(null) 26 | 27 | function useChart() { 28 | const context = React.useContext(ChartContext) 29 | 30 | if (!context) { 31 | throw new Error('useChart must be used within a ') 32 | } 33 | 34 | return context 35 | } 36 | 37 | const ChartContainer = React.forwardRef< 38 | HTMLDivElement, 39 | React.ComponentProps<'div'> & { 40 | config: ChartConfig 41 | children: React.ComponentProps< 42 | typeof RechartsPrimitive.ResponsiveContainer 43 | >['children'] 44 | } 45 | >(({ id, className, children, config, ...props }, ref) => { 46 | const uniqueId = React.useId() 47 | const chartId = `chart-${id || uniqueId.replace(/:/g, '')}` 48 | 49 | return ( 50 | 51 |
60 | 61 | 62 | {children} 63 | 64 |
65 |
66 | ) 67 | }) 68 | ChartContainer.displayName = 'Chart' 69 | 70 | const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { 71 | const colorConfig = Object.entries(config).filter( 72 | ([_, config]) => config.theme || config.color 73 | ) 74 | 75 | if (!colorConfig.length) { 76 | return null 77 | } 78 | 79 | return ( 80 |