├── 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 |
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 |
--------------------------------------------------------------------------------
/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 |
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 |
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 |
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 |
38 | );
39 | }
40 |
41 | return (
42 |
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 |
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 |
31 | );
32 | }
33 |
34 | return (
35 |
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 |
43 | );
44 | }
45 |
46 | return (
47 |
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 |
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 |
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 |
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 |
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 |
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 |