diff --git a/.github/.dependabot.yml b/.github/.dependabot.yml new file mode 100644 index 0000000..4c18b91 --- /dev/null +++ b/.github/.dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: "pnpm" # Adjust for your package manager + directory: "/" # Directory where `package.json` is located + schedule: + interval: "daily" + target-branch: "dev" # Change this from "main" to "dev" + commit-message: + prefix: "deps" diff --git a/apps/demo.lasereyes.build/components/WalletCard.tsx b/apps/demo.lasereyes.build/components/WalletCard.tsx index 9cef8d0..5e54a2c 100644 --- a/apps/demo.lasereyes.build/components/WalletCard.tsx +++ b/apps/demo.lasereyes.build/components/WalletCard.tsx @@ -49,7 +49,7 @@ import { SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select" +} from '@/components/ui/select' const WalletCard = ({ wallet, @@ -68,10 +68,10 @@ const WalletCard = ({ setSignedPsbt: ( psbt: | { - signedPsbtHex: string - signedPsbtBase64: string - txId?: string - } + signedPsbtHex: string + signedPsbtBase64: string + txId?: string + } | undefined ) => void }) => { @@ -105,7 +105,7 @@ const WalletCard = ({ send, pushPsbt, switchNetwork, - getMetaBalances + getMetaBalances, } = useLaserEyes() const [hasError, setHasError] = useState(false) @@ -120,16 +120,22 @@ const WalletCard = ({ 'Inscribed 100% clientside with Laser Eyes' ) - const [runes, setRunes] = useState<{ - balance: string; - symbol: string; - name: string; - }[] | undefined>() - const [selectedRune, setSelectedRune] = useState<{ - balance: string; - symbol: string; - name: string; - } | undefined>(undefined); + const [runes, setRunes] = useState< + | { + balance: string + symbol: string + name: string + }[] + | undefined + >() + const [selectedRune, setSelectedRune] = useState< + | { + balance: string + symbol: string + name: string + } + | undefined + >(undefined) const [runeToAddress, setRuneToAddress] = useState('') const [runeAmount, setRuneAmount] = useState('') @@ -167,12 +173,12 @@ const WalletCard = ({ paymentAddress, paymentPublicKey, network as - | typeof MAINNET - | typeof TESTNET - | typeof TESTNET4 - | typeof SIGNET - | typeof FRACTAL_MAINNET - | typeof FRACTAL_TESTNET + | typeof MAINNET + | typeof TESTNET + | typeof TESTNET4 + | typeof SIGNET + | typeof FRACTAL_MAINNET + | typeof FRACTAL_TESTNET ) .then((psbt) => { if (psbt && psbt.toHex() !== unsigned) { @@ -207,7 +213,7 @@ const WalletCard = ({ useEffect(() => { if (address) { - getMetaBalances("runes").then(setRunes) + getMetaBalances('runes').then(setRunes) setRuneToAddress(address) } }, [address]) @@ -435,7 +441,6 @@ const WalletCard = ({ } }, [inscribe, inscriptionText, network]) - const sendRune = async () => { try { if (!selectedRune) throw new Error('No rune selected') @@ -443,7 +448,7 @@ const WalletCard = ({ if (!runeToAddress) throw new Error('No destination address provided') if (!runeAmount) throw new Error('No amount specified') - const txid = await send("runes", { + const txid = await send('runes', { fromAddress: address, toAddress: runeToAddress, amount: Number(runeAmount), @@ -658,22 +663,15 @@ const WalletCard = ({
- - abstract signMessage(message: string, toSignAddress?: string): Promise + abstract signMessage( + message: string, + options?: { toSignAddress?: string } + ): Promise abstract signPsbt( tx: string, @@ -109,10 +120,10 @@ export abstract class WalletProvider { broadcast?: boolean ): Promise< | { - signedPsbtHex: string | undefined - signedPsbtBase64: string | undefined - txId?: string - } + signedPsbtHex: string | undefined + signedPsbtBase64: string | undefined + txId?: string + } | undefined > @@ -151,7 +162,7 @@ export abstract class WalletProvider { throw new Error('Unsupported network') } - const runeArgs = sendArgs as RuneSendArgs; + const runeArgs = sendArgs as RuneSendArgs if (!runeArgs.runeId || !runeArgs.amount || !runeArgs.toAddress) { throw new Error('Missing required parameters') } @@ -165,7 +176,7 @@ export abstract class WalletProvider { paymentPublicKey: this.$store.get().paymentPublicKey, toAddress: runeArgs.toAddress, signPsbt: this.signPsbt.bind(this), - network + network, }) default: throw new Error('Unsupported protocol') diff --git a/packages/lasereyes-core/src/client/providers/leather.ts b/packages/lasereyes-core/src/client/providers/leather.ts index 088f412..ddfa2f6 100644 --- a/packages/lasereyes-core/src/client/providers/leather.ts +++ b/packages/lasereyes-core/src/client/providers/leather.ts @@ -13,8 +13,8 @@ import { getBTCBalance, isMainnetNetwork } from '../../lib/helpers' import { LEATHER, P2TR, P2WPKH } from '../../constants/wallets' import { listenKeys, MapStore } from 'nanostores' import { persistentMap } from '@nanostores/persistent' -import { LaserEyesStoreType } from '../types' -import { SIGNET, TESTNET, TESTNET4 } from '../../constants' +import { LaserEyesStoreType, SignMessageOptions } from '../types' +import { ECDSA, SIGNET, TESTNET, TESTNET4 } from '../../constants' import { RpcErrorCode } from 'sats-connect' import { handleStateChangePersistence, @@ -127,7 +127,7 @@ export default class LeatherProvider extends WalletProvider { return } } - + if (!this.library) throw new Error("Leather isn't installed") const getAddressesResponse: LeatherRPCResponse = await this.library.request('getAddresses') @@ -190,7 +190,14 @@ export default class LeatherProvider extends WalletProvider { } } } - async signMessage(message: string, toSignAddress?: string): Promise { + async signMessage( + message: string, + options?: SignMessageOptions + ): Promise { + const toSignAddress = options?.toSignAddress + const protocol = options?.protocol + if (protocol === ECDSA) + throw new Error("Leather doesn't support ECDSA message signing") const paymentType = toSignAddress === this.$store.get().address ? P2TR : P2WPKH if ( diff --git a/packages/lasereyes-core/src/client/providers/magic-eden.ts b/packages/lasereyes-core/src/client/providers/magic-eden.ts index c050e44..5cf9b3e 100644 --- a/packages/lasereyes-core/src/client/providers/magic-eden.ts +++ b/packages/lasereyes-core/src/client/providers/magic-eden.ts @@ -11,12 +11,14 @@ import { } from 'sats-connect' import { WalletProvider } from '.' import { + ECDSA, getSatsConnectNetwork, LaserEyesStoreType, MAGIC_EDEN, MAINNET, NetworkType, ProviderType, + SignMessageOptions, } from '../..' import { findOrdinalsAddress, @@ -226,10 +228,11 @@ export default class MagicEdenProvider extends WalletProvider { async signMessage( message: string, - toSignAddress?: string | undefined + options?: SignMessageOptions ): Promise { try { - const tempAddy = toSignAddress || this.$store.get().paymentAddress + const tempAddy = + options?.toSignAddress || this.$store.get().paymentAddress let signedMessage: string = '' await signMessage({ @@ -240,7 +243,10 @@ export default class MagicEdenProvider extends WalletProvider { }, address: tempAddy, message: message, - protocol: MessageSigningProtocols.BIP322, + protocol: + options?.protocol === ECDSA + ? MessageSigningProtocols.ECDSA + : MessageSigningProtocols.BIP322, }, onFinish: (response) => { signedMessage = response @@ -265,10 +271,10 @@ export default class MagicEdenProvider extends WalletProvider { broadcast?: boolean | undefined ): Promise< | { - signedPsbtHex: string | undefined - signedPsbtBase64: string | undefined - txId?: string | undefined - } + signedPsbtHex: string | undefined + signedPsbtBase64: string | undefined + txId?: string | undefined + } | undefined > { console.log('signPsbt', psbtBase64, _finalize, broadcast) diff --git a/packages/lasereyes-core/src/client/providers/okx.ts b/packages/lasereyes-core/src/client/providers/okx.ts index b9de257..63c3e0c 100644 --- a/packages/lasereyes-core/src/client/providers/okx.ts +++ b/packages/lasereyes-core/src/client/providers/okx.ts @@ -1,6 +1,8 @@ import * as bitcoin from 'bitcoinjs-lib' import { WalletProvider } from '.' import { + BIP322_SIMPLE, + ECDSA, FRACTAL_MAINNET, FRACTAL_TESTNET, getNetworkForOkx, @@ -10,12 +12,17 @@ import { OKX, ProviderType, SIGNET, + SignMessageOptions, TESTNET, TESTNET4, } from '../..' import { listenKeys, MapStore } from 'nanostores' import { persistentMap } from '@nanostores/persistent' -import { handleStateChangePersistence, keysToPersist, PersistedKey } from '../utils' +import { + handleStateChangePersistence, + keysToPersist, + PersistedKey, +} from '../utils' import { getBTCBalance, isMainnetNetwork } from '../../lib/helpers' const OKX_WALLET_PERSISTENCE_KEY = 'OKX_CONNECTED_WALLET_STATE' @@ -130,7 +137,7 @@ export default class OkxProvider extends WalletProvider { return } } - + try { const okxAccounts = await this.library.connect() if (!okxAccounts) throw new Error('No accounts found') @@ -195,9 +202,14 @@ export default class OkxProvider extends WalletProvider { return txId } - async signMessage(message: string, _?: string | undefined): Promise { + async signMessage( + message: string, + options?: SignMessageOptions + ): Promise { const library = this.library - return await library?.signMessage(message) + const protocol = + options?.protocol === ECDSA ? BIP322_SIMPLE : options?.protocol + return await library?.signMessage(message, protocol) } async signPsbt( diff --git a/packages/lasereyes-core/src/client/providers/op-net.ts b/packages/lasereyes-core/src/client/providers/op-net.ts index cf1946d..1d66a9d 100644 --- a/packages/lasereyes-core/src/client/providers/op-net.ts +++ b/packages/lasereyes-core/src/client/providers/op-net.ts @@ -4,6 +4,8 @@ import { getNetworkForUnisat, getUnisatNetwork } from '../../constants/networks' import { NetworkType, ProviderType } from '../../types' import { OP_NET } from '../../constants/wallets' import { listenKeys } from 'nanostores' +import { SignMessageOptions } from '../types' +import { BIP322, BIP322_SIMPLE } from '../../constants' export default class OpNetProvider extends WalletProvider { public get library(): any | undefined { @@ -127,8 +129,13 @@ export default class OpNetProvider extends WalletProvider { return txId } - async signMessage(message: string, _?: string | undefined): Promise { - return await this.library?.signMessage(message) + async signMessage( + message: string, + options?: SignMessageOptions + ): Promise { + const protocol = + options?.protocol === BIP322 ? BIP322_SIMPLE : options?.protocol + return await this.library?.signMessage(message, protocol) } async signPsbt( diff --git a/packages/lasereyes-core/src/client/providers/orange.ts b/packages/lasereyes-core/src/client/providers/orange.ts index 8c3c5f7..b6fe255 100644 --- a/packages/lasereyes-core/src/client/providers/orange.ts +++ b/packages/lasereyes-core/src/client/providers/orange.ts @@ -22,6 +22,7 @@ import { LaserEyesStoreType, getOrangeNetwork, ORANGE, + SignMessageOptions, } from '../..' import { findOrdinalsAddress, @@ -32,7 +33,11 @@ import { import { MapStore, listenKeys } from 'nanostores' import { persistentMap } from '@nanostores/persistent' -import { handleStateChangePersistence, keysToPersist, PersistedKey } from '../utils' +import { + handleStateChangePersistence, + keysToPersist, + PersistedKey, +} from '../utils' const { signMessage: signMessageOrange, sendBtcTransaction: sendBtcTxOrange } = orange @@ -80,12 +85,7 @@ export default class OrangeProvider extends WalletProvider { _: LaserEyesStoreType | undefined, changedKey: keyof LaserEyesStoreType | undefined ) { - handleStateChangePersistence( - ORANGE, - newState, - changedKey, - this.$valueStore - ) + handleStateChangePersistence(ORANGE, newState, changedKey, this.$valueStore) } initialize(): void { @@ -221,10 +221,10 @@ export default class OrangeProvider extends WalletProvider { async signMessage( message: string, - toSignAddress?: string | undefined + options?: SignMessageOptions ): Promise { let signature = '' - const tempAddy = toSignAddress || this.$store.get().paymentAddress + const tempAddy = options?.toSignAddress || this.$store.get().paymentAddress const signMessageOptions = { payload: { network: { diff --git a/packages/lasereyes-core/src/client/providers/oyl.ts b/packages/lasereyes-core/src/client/providers/oyl.ts index a3f83c1..99a0145 100644 --- a/packages/lasereyes-core/src/client/providers/oyl.ts +++ b/packages/lasereyes-core/src/client/providers/oyl.ts @@ -10,7 +10,7 @@ import { import { OYL } from '../../constants/wallets' import { listenKeys, MapStore } from 'nanostores' import { persistentMap } from '@nanostores/persistent' -import { LaserEyesStoreType } from '../types' +import { LaserEyesStoreType, SignMessageOptions } from '../types' import { handleStateChangePersistence, keysToPersist, @@ -150,11 +150,15 @@ export default class OylProvider extends WalletProvider { // @ts-ignore return psbt.txId } - async signMessage(message: string, toSignAddress?: string): Promise { - const tempAddy = toSignAddress || this.$store.get().paymentAddress + async signMessage( + message: string, + options?: SignMessageOptions + ): Promise { + const tempAddy = options?.toSignAddress || this.$store.get().paymentAddress const response = await this.library.signMessage({ address: tempAddy, message, + protocol: options?.protocol, }) return response.signature } diff --git a/packages/lasereyes-core/src/client/providers/phantom.ts b/packages/lasereyes-core/src/client/providers/phantom.ts index 0a34b9e..fe902fc 100644 --- a/packages/lasereyes-core/src/client/providers/phantom.ts +++ b/packages/lasereyes-core/src/client/providers/phantom.ts @@ -1,6 +1,6 @@ import * as bitcoin from 'bitcoinjs-lib' import { WalletProvider } from '.' -import { MAINNET, PHANTOM, TESTNET } from '../../constants' +import { ECDSA, MAINNET, PHANTOM, TESTNET } from '../../constants' import { ProviderType, NetworkType } from '../../types' import { createSendBtcPsbt, @@ -10,6 +10,7 @@ import { import { listenKeys } from 'nanostores' import { fromOutputScript } from 'bitcoinjs-lib/src/address' import { fromHexString } from '../utils' +import { SignMessageOptions } from '../types' export default class PhantomProvider extends WalletProvider { public get library(): any | undefined { @@ -121,11 +122,14 @@ export default class PhantomProvider extends WalletProvider { async signMessage( message: string, - toSignAddress?: string | undefined + options?: SignMessageOptions ): Promise { + if (options?.protocol === ECDSA) { + throw new Error('ECDSA signing is not supported by Phantom') + } const utf8Bytes = new TextEncoder().encode(message) const uintArray = new Uint8Array(utf8Bytes) - const tempAddy = toSignAddress || this.$store.get().paymentAddress + const tempAddy = options?.toSignAddress || this.$store.get().paymentAddress const response = await this.library?.signMessage(tempAddy, uintArray) const binaryString = String.fromCharCode(...response.signature) return btoa(binaryString) diff --git a/packages/lasereyes-core/src/client/providers/sparrow.ts b/packages/lasereyes-core/src/client/providers/sparrow.ts index df39eca..e8e0d16 100644 --- a/packages/lasereyes-core/src/client/providers/sparrow.ts +++ b/packages/lasereyes-core/src/client/providers/sparrow.ts @@ -167,7 +167,7 @@ export default class SparrowProvider extends WalletProvider { return txId } - async signMessage(message: string, _?: string | undefined): Promise { + async signMessage(message: string): Promise { return await this.library!.signMessage(message) } diff --git a/packages/lasereyes-core/src/client/providers/unisat.ts b/packages/lasereyes-core/src/client/providers/unisat.ts index 4de0c36..deba1ae 100644 --- a/packages/lasereyes-core/src/client/providers/unisat.ts +++ b/packages/lasereyes-core/src/client/providers/unisat.ts @@ -4,6 +4,8 @@ import { getNetworkForUnisat, getUnisatNetwork } from '../../constants/networks' import { NetworkType, ProviderType } from '../../types' import { UNISAT } from '../../constants/wallets' import { listenKeys } from 'nanostores' +import { SignMessageOptions } from '../types' +import { BIP322, BIP322_SIMPLE } from '../../constants' export default class UnisatProvider extends WalletProvider { public get library(): any | undefined { @@ -125,8 +127,13 @@ export default class UnisatProvider extends WalletProvider { return txId } - async signMessage(message: string, _?: string | undefined): Promise { - return await this.library?.signMessage(message) + override async signMessage( + message: string, + options?: SignMessageOptions + ): Promise { + const protocol = + options?.protocol === BIP322 ? BIP322_SIMPLE : options?.protocol + return await this.library?.signMessage(message, protocol) } async signPsbt( diff --git a/packages/lasereyes-core/src/client/providers/wizz.ts b/packages/lasereyes-core/src/client/providers/wizz.ts index 0855185..66df163 100644 --- a/packages/lasereyes-core/src/client/providers/wizz.ts +++ b/packages/lasereyes-core/src/client/providers/wizz.ts @@ -7,6 +7,9 @@ import { WIZZ_MAINNET, getNetworkForWizz, WIZZ, + SignMessageOptions, + BIP322, + BIP322_SIMPLE, } from '../..' import * as bitcoin from 'bitcoinjs-lib' import { listenKeys } from 'nanostores' @@ -144,8 +147,13 @@ export class WizzProvider extends WalletProvider { } } - async signMessage(message: string): Promise { - return await this.library?.signMessage(message) + async signMessage( + message: string, + options?: SignMessageOptions + ): Promise { + const protocol = + options?.protocol === BIP322 ? BIP322_SIMPLE : options?.protocol + return await this.library?.signMessage(message, protocol) } async signPsbt( diff --git a/packages/lasereyes-core/src/client/providers/xverse.ts b/packages/lasereyes-core/src/client/providers/xverse.ts index 689e7f5..d95c6df 100644 --- a/packages/lasereyes-core/src/client/providers/xverse.ts +++ b/packages/lasereyes-core/src/client/providers/xverse.ts @@ -18,6 +18,8 @@ import { SIGNET, FRACTAL_TESTNET, LaserEyesStoreType, + SignMessageOptions, + ECDSA, } from '../..' import { findOrdinalsAddress, @@ -28,7 +30,11 @@ import { } from '../../lib/helpers' import { MapStore, listenKeys } from 'nanostores' import { persistentMap } from '@nanostores/persistent' -import { handleStateChangePersistence, keysToPersist, PersistedKey } from '../utils' +import { + handleStateChangePersistence, + keysToPersist, + PersistedKey, +} from '../utils' const XVERSE_WALLET_PERSISTENCE_KEY = 'XVERSE_CONNECTED_WALLET_STATE' export default class XVerseProvider extends WalletProvider { @@ -211,13 +217,16 @@ export default class XVerseProvider extends WalletProvider { async signMessage( message: string, - toSignAddress?: string | undefined + { toSignAddress, protocol }: SignMessageOptions ): Promise { const tempAddy = toSignAddress || this.$store.get().paymentAddress const response = await request('signMessage', { address: tempAddy, message, - protocol: MessageSigningProtocols.BIP322, + protocol: + protocol === ECDSA + ? MessageSigningProtocols.ECDSA + : MessageSigningProtocols.BIP322, }) if (response.status === 'success') { diff --git a/packages/lasereyes-core/src/client/types.ts b/packages/lasereyes-core/src/client/types.ts index 500bd19..456fa1e 100644 --- a/packages/lasereyes-core/src/client/types.ts +++ b/packages/lasereyes-core/src/client/types.ts @@ -1,4 +1,5 @@ import { NetworkType, ProviderType } from '../types' +import { BIP322, ECDSA } from '../constants/signing-protocol' export type LaserEyesStoreType = { provider: ProviderType | undefined @@ -22,3 +23,8 @@ export interface SparrowWalletProvider { signMessage(message: string): Promise signPsbt(psbtBase64: string): Promise } + +export type SignMessageOptions = { + toSignAddress?: string + protocol?: typeof BIP322 | typeof ECDSA +} diff --git a/packages/lasereyes-core/src/constants/index.ts b/packages/lasereyes-core/src/constants/index.ts index bcddc0f..da576a2 100644 --- a/packages/lasereyes-core/src/constants/index.ts +++ b/packages/lasereyes-core/src/constants/index.ts @@ -2,3 +2,5 @@ export * from './networks' export * from './wallets' export * from './settings' export * from './content' +export * from './protocols' +export * from './signing-protocol' \ No newline at end of file diff --git a/packages/lasereyes-core/src/constants/signing-protocol.ts b/packages/lasereyes-core/src/constants/signing-protocol.ts new file mode 100644 index 0000000..9a06693 --- /dev/null +++ b/packages/lasereyes-core/src/constants/signing-protocol.ts @@ -0,0 +1,4 @@ +export const BIP322 = 'bip322' +export const ECDSA = 'ecdsa' + +export const BIP322_SIMPLE = 'bip322-simple' diff --git a/packages/lasereyes-core/src/lib/btc.ts b/packages/lasereyes-core/src/lib/btc.ts index 02c78d9..4d6c952 100644 --- a/packages/lasereyes-core/src/lib/btc.ts +++ b/packages/lasereyes-core/src/lib/btc.ts @@ -1,12 +1,8 @@ import * as bitcoin from 'bitcoinjs-lib' import * as ecc2 from '@bitcoinerlab/secp256k1' -import { - getBitcoinNetwork, -} from './helpers' +import { getBitcoinNetwork } from './helpers' import * as bip39 from 'bip39' -import { - NetworkType, -} from '../types' +import { NetworkType } from '../types' import { BIP32Factory, BIP32Interface } from 'bip32' import { getTransactionMempoolSpace } from './mempool-space' import { P2PKH, P2SH_P2WPKH, P2SH, P2WPKH, P2WSH, P2TR } from '../constants' @@ -22,7 +18,6 @@ export async function generatePrivateKey(network: NetworkType) { return root?.derivePath("m/44'/0'/0'/0/0").privateKey } - export const getAddressType = ( address: string, network: NetworkType @@ -61,7 +56,6 @@ export function getPublicKeyHash( return decoded } - export function getRedeemScript( paymentPublicKey: string, network: NetworkType diff --git a/packages/lasereyes-core/src/lib/helpers.ts b/packages/lasereyes-core/src/lib/helpers.ts index 2a60d2a..1c8140a 100644 --- a/packages/lasereyes-core/src/lib/helpers.ts +++ b/packages/lasereyes-core/src/lib/helpers.ts @@ -173,7 +173,6 @@ export async function createSendBtcPsbt( } } - export function delay(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)) } @@ -198,7 +197,6 @@ export async function broadcastTx( return response.data } - export const isTestnetNetwork = (network: NetworkType) => { return network === TESTNET || network === TESTNET4 || network === SIGNET } diff --git a/packages/lasereyes-core/src/lib/inscribe.ts b/packages/lasereyes-core/src/lib/inscribe.ts index d114d42..fc32ffe 100644 --- a/packages/lasereyes-core/src/lib/inscribe.ts +++ b/packages/lasereyes-core/src/lib/inscribe.ts @@ -10,14 +10,16 @@ import { getBitcoinNetwork, } from './helpers' import { getCmDruidNetwork, MAINNET, P2SH, P2TR } from '../constants' -import { - ContentType, - MempoolUtxo, - NetworkType, -} from '../types' +import { ContentType, MempoolUtxo, NetworkType } from '../types' import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371' import { TEXT_PLAIN } from '../constants/content' -import { generatePrivateKey, waitForTransaction, getOutputValueByVOutIndex, getAddressType, getRedeemScript } from './btc' +import { + generatePrivateKey, + waitForTransaction, + getOutputValueByVOutIndex, + getAddressType, + getRedeemScript, +} from './btc' import { getRecommendedFeesMempoolSpace } from './mempool-space' bitcoin.initEccLib(ecc2) @@ -49,10 +51,10 @@ export const inscribeContent = async ({ network?: NetworkType ) => Promise< | { - signedPsbtHex: string | undefined - signedPsbtBase64: string | undefined - txId?: string - } + signedPsbtHex: string | undefined + signedPsbtBase64: string | undefined + txId?: string + } | undefined > network: NetworkType @@ -66,9 +68,9 @@ export const inscribeContent = async ({ const ixs = inscriptions ? inscriptions : Array(quantity).fill({ - content: contentBase64, - mimeType, - }) + content: contentBase64, + mimeType, + }) const commitTx = await getCommitPsbt({ inscriptions: ixs, @@ -124,9 +126,9 @@ export const getCommitPsbt = async ({ isDry?: boolean }): Promise< | { - psbtHex: string - psbtBase64: string - } + psbtHex: string + psbtBase64: string + } | undefined > => { try { @@ -360,4 +362,3 @@ export const createInscriptionRevealAddressAndKeys = ( tapleaf, } } - diff --git a/packages/lasereyes-core/src/lib/mempool-space.ts b/packages/lasereyes-core/src/lib/mempool-space.ts index 4c10dd4..85c57ca 100644 --- a/packages/lasereyes-core/src/lib/mempool-space.ts +++ b/packages/lasereyes-core/src/lib/mempool-space.ts @@ -1,9 +1,7 @@ -import axios from "axios" -import { MAINNET } from "../constants" -import { NetworkType, MempoolTransactionResponse } from "../types" -import { getMempoolSpaceUrl } from "./urls" - - +import axios from 'axios' +import { MAINNET } from '../constants' +import { NetworkType, MempoolTransactionResponse } from '../types' +import { getMempoolSpaceUrl } from './urls' export async function getTransactionMempoolSpace( txId: string, @@ -31,7 +29,9 @@ export async function getRawTransactionMempoolSpace( } } -export const getRecommendedFeesMempoolSpace = async (network: NetworkType): Promise<{ +export const getRecommendedFeesMempoolSpace = async ( + network: NetworkType +): Promise<{ fastestFee: number halfHourFee: number hourFee: number diff --git a/packages/lasereyes-core/src/lib/runes.ts b/packages/lasereyes-core/src/lib/runes.ts index 9f1b591..61e0b46 100644 --- a/packages/lasereyes-core/src/lib/runes.ts +++ b/packages/lasereyes-core/src/lib/runes.ts @@ -105,6 +105,3 @@ export const createRuneSendScript = ({ ]) return script } - - - diff --git a/packages/lasereyes-core/src/lib/runes/psbt.ts b/packages/lasereyes-core/src/lib/runes/psbt.ts index 39344a9..76b3cee 100644 --- a/packages/lasereyes-core/src/lib/runes/psbt.ts +++ b/packages/lasereyes-core/src/lib/runes/psbt.ts @@ -1,14 +1,19 @@ - -import * as bitcoin from "bitcoinjs-lib" -import { createRuneMintScript, createRuneSendScript } from "./scripts" -import { getRecommendedFeesMempoolSpace } from "../mempool-space" -import { MAINNET, P2SH, P2TR, } from "../../constants" -import { getRuneOutpoints } from "./utils" -import { broadcastTx, calculateValueOfUtxosGathered, estimateTxSize, getAddressUtxos, getBitcoinNetwork } from "../helpers" -import { getRuneById } from "../sandshrew" -import { NetworkType } from "../../types" -import { getAddressType, getRedeemScript } from "../btc" -import { toXOnly } from "bitcoinjs-lib/src/psbt/bip371" +import * as bitcoin from 'bitcoinjs-lib' +import { createRuneMintScript, createRuneSendScript } from './scripts' +import { getRecommendedFeesMempoolSpace } from '../mempool-space' +import { MAINNET, P2SH, P2TR } from '../../constants' +import { getRuneOutpoints } from './utils' +import { + broadcastTx, + calculateValueOfUtxosGathered, + estimateTxSize, + getAddressUtxos, + getBitcoinNetwork, +} from '../helpers' +import { getRuneById } from '../sandshrew' +import { NetworkType } from '../../types' +import { getAddressType, getRedeemScript } from '../btc' +import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371' // import { getAddressType, getRedeemScript } from "../btc" // import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371' @@ -39,10 +44,10 @@ export const sendRune = async ({ network?: NetworkType ) => Promise< | { - signedPsbtHex: string | undefined - signedPsbtBase64: string | undefined - txId?: string - } + signedPsbtHex: string | undefined + signedPsbtBase64: string | undefined + txId?: string + } | undefined > network: NetworkType @@ -56,7 +61,7 @@ export const sendRune = async ({ toAddress, runeId, amount, - network + network, }) if (!runeSendPsbt || !runeSendPsbt?.psbtHex) { @@ -82,7 +87,6 @@ export const sendRune = async ({ } } - export const createRuneSendPsbt = async ({ fromAddress, fromAddressPublicKey, @@ -91,7 +95,7 @@ export const createRuneSendPsbt = async ({ toAddress, runeId, amount, - network + network, }: { fromAddress: string fromAddressPublicKey: string @@ -106,9 +110,12 @@ export const createRuneSendPsbt = async ({ psbtHex: string }> => { try { - const { fastestFee: feeRate } = await getRecommendedFeesMempoolSpace(network) + const { fastestFee: feeRate } = + await getRecommendedFeesMempoolSpace(network) const utxos = await getAddressUtxos(fromPaymentAddress, network) - let sortedUtxos = utxos.sort((a: { value: number }, b: { value: number }) => b.value - a.value).filter((utxo: { value: number }) => utxo.value > 3000) + let sortedUtxos = utxos + .sort((a: { value: number }, b: { value: number }) => b.value - a.value) + .filter((utxo: { value: number }) => utxo.value > 3000) if (sortedUtxos.length === 0) { throw new Error('No utxos found') } @@ -121,7 +128,7 @@ export const createRuneSendPsbt = async ({ const amountGathered = calculateValueOfUtxosGathered(sortedUtxos) const minFee = estimateTxSize(outpoints.length, 2, 4) - const calculatedFee = minFee * feeRate < 250 ? 250 : minFee * (feeRate) + const calculatedFee = minFee * feeRate < 250 ? 250 : minFee * feeRate let finalFee = calculatedFee let counter = 0 @@ -146,7 +153,10 @@ export const createRuneSendPsbt = async ({ const paymentAddressType = getAddressType(fromPaymentAddress, network) for (let i = 0; i < sortedUtxos.length; i++) { - const script = bitcoin.address.toOutputScript(fromPaymentAddress, getBitcoinNetwork(MAINNET)) + const script = bitcoin.address.toOutputScript( + fromPaymentAddress, + getBitcoinNetwork(MAINNET) + ) const utxo = sortedUtxos[i] if (paymentAddressType === P2TR) { @@ -174,7 +184,7 @@ export const createRuneSendPsbt = async ({ }) } - if (paymentAddressType === "p2wpkh") { + if (paymentAddressType === 'p2wpkh') { psbt.addInput({ hash: utxo.txid, index: utxo.vout, @@ -198,8 +208,7 @@ export const createRuneSendPsbt = async ({ psbt.addOutput(output) const inscriptionSats = 546 - const changeAmount = - amountGathered - (finalFee + (inscriptionSats * 2)) + const changeAmount = amountGathered - (finalFee + inscriptionSats * 2) psbt.addOutput({ value: BigInt(inscriptionSats), @@ -230,8 +239,9 @@ export const createRuneMintPsbt = async ({ runeId: string }) => { try { - const network = "mainnet" - const { fastestFee: feeRate } = await getRecommendedFeesMempoolSpace(network) + const network = 'mainnet' + const { fastestFee: feeRate } = + await getRecommendedFeesMempoolSpace(network) const utxos = await getAddressUtxos(address, network) const minFee = 300 @@ -248,7 +258,10 @@ export const createRuneMintPsbt = async ({ let psbt = new bitcoin.Psbt({ network: getBitcoinNetwork(network) }) for (let i = 0; i < sortedUtxos.length; i++) { - const script = bitcoin.address.toOutputScript(address, getBitcoinNetwork(network)) + const script = bitcoin.address.toOutputScript( + address, + getBitcoinNetwork(network) + ) const utxo = sortedUtxos[i] psbt.addInput({ hash: utxo.txid, @@ -268,8 +281,7 @@ export const createRuneMintPsbt = async ({ const output = { script: script, value: BigInt(0) } psbt.addOutput(output) - const changeAmount = - amountRetrieved - (finalFee + inscriptionSats) + const changeAmount = amountRetrieved - (finalFee + inscriptionSats) psbt.addOutput({ value: BigInt(inscriptionSats), diff --git a/packages/lasereyes-core/src/lib/runes/scripts.ts b/packages/lasereyes-core/src/lib/runes/scripts.ts index 7643d30..7778f2c 100644 --- a/packages/lasereyes-core/src/lib/runes/scripts.ts +++ b/packages/lasereyes-core/src/lib/runes/scripts.ts @@ -105,6 +105,3 @@ export const createRuneSendScript = ({ ]) return script } - - - diff --git a/packages/lasereyes-core/src/lib/runes/utils.ts b/packages/lasereyes-core/src/lib/runes/utils.ts index 16260fd..9396656 100644 --- a/packages/lasereyes-core/src/lib/runes/utils.ts +++ b/packages/lasereyes-core/src/lib/runes/utils.ts @@ -1,5 +1,10 @@ -import { SingleRuneOutpoint } from "../../types/sandshrew"; -import { batchOrdOutput, getOrdAddress, getRuneById, mapRuneBalances } from "../sandshrew"; +import { SingleRuneOutpoint } from '../../types/sandshrew' +import { + batchOrdOutput, + getOrdAddress, + getRuneById, + mapRuneBalances, +} from '../sandshrew' export const getRuneOutpoints = async ({ address, @@ -8,18 +13,18 @@ export const getRuneOutpoints = async ({ address: string runeId: string }): Promise => { - const addressOutpoints = await getOrdAddress(address); - const { entry } = await getRuneById(runeId); - const runeName = entry.spaced_rune; + const addressOutpoints = await getOrdAddress(address) + const { entry } = await getRuneById(runeId) + const runeName = entry.spaced_rune const ordOutputs = await batchOrdOutput({ outpoints: addressOutpoints.outputs, - rune_name: runeName + rune_name: runeName, }) const runeUtxosOutpoints = await mapRuneBalances({ ordOutputs: ordOutputs, }) - return runeUtxosOutpoints; + return runeUtxosOutpoints } diff --git a/packages/lasereyes-core/src/lib/sandshrew.ts b/packages/lasereyes-core/src/lib/sandshrew.ts index da8b2ff..4305cde 100644 --- a/packages/lasereyes-core/src/lib/sandshrew.ts +++ b/packages/lasereyes-core/src/lib/sandshrew.ts @@ -1,11 +1,20 @@ -import axios from "axios" -import { OrdAddress, OrdAddressResponse, OrdOutputs, OrdRune, RuneBalance } from "../types/ord" -import { EsploraTx, SingleRuneOutpoint } from "../types/sandshrew" -import { getPublicKeyHash } from "./btc" -import { MAINNET } from "../constants" -export const SANDSHREW_URL: string = "https://mainnet.sandshrew.io/v1/lasereyes" - -export const callSandshrewRPC = async (method: string, params: string | any) => { +import axios from 'axios' +import { + OrdAddress, + OrdAddressResponse, + OrdOutputs, + OrdRune, + RuneBalance, +} from '../types/ord' +import { EsploraTx, SingleRuneOutpoint } from '../types/sandshrew' +import { getPublicKeyHash } from './btc' +import { MAINNET } from '../constants' +export const SANDSHREW_URL: string = 'https://mainnet.sandshrew.io/v1/lasereyes' + +export const callSandshrewRPC = async ( + method: string, + params: string | any +) => { const data = JSON.stringify({ jsonrpc: '2.0', id: method, @@ -31,7 +40,9 @@ export const callSandshrewRPC = async (method: string, params: string | any) => export const getOrdAddress = async (address: string) => { try { - const response = await callSandshrewRPC('ord_address', [address]) as OrdAddressResponse + const response = (await callSandshrewRPC('ord_address', [ + address, + ])) as OrdAddressResponse return response.result as OrdAddress } catch (e) { throw e @@ -67,34 +78,36 @@ export const getTxInfo = async (txId: string): Promise => { export const batchOrdOutput = async ({ outpoints, - rune_name + rune_name, }: { outpoints: string[] rune_name: string }): Promise => { - const MAX_OUTPOINTS_PER_CALL = 1000; - const ordOutputs: OrdOutputs[] = []; + const MAX_OUTPOINTS_PER_CALL = 1000 + const ordOutputs: OrdOutputs[] = [] for (let i = 0; i < outpoints.length; i += MAX_OUTPOINTS_PER_CALL) { - const batch = outpoints.slice(i, i + MAX_OUTPOINTS_PER_CALL); + const batch = outpoints.slice(i, i + MAX_OUTPOINTS_PER_CALL) const multiCall = batch.map((outpoint) => { - return ["ord_output", [outpoint]]; - }); + return ['ord_output', [outpoint]] + }) - const { result } = await callSandshrewRPC("sandshrew_multicall", multiCall); + const { result } = await callSandshrewRPC('sandshrew_multicall', multiCall) for (let i = 0; i < result.length; i++) { - result[i].result["output"] = batch[i]; + result[i].result['output'] = batch[i] } - const filteredResult = result.filter((output: OrdOutputs) => Object.keys(output.result.runes).includes(rune_name)); - ordOutputs.push(...filteredResult); + const filteredResult = result.filter((output: OrdOutputs) => + Object.keys(output.result.runes).includes(rune_name) + ) + ordOutputs.push(...filteredResult) } - return ordOutputs; + return ordOutputs } export const getAddressRunesBalances = async (address: string) => { try { const response = await getOrdAddress(address) - const runesData = response.runes_balances; + const runesData = response.runes_balances if (!runesData) { throw new Error('No runes data found') } @@ -103,11 +116,11 @@ export const getAddressRunesBalances = async (address: string) => { name: rune[0], balance: rune[1], symbol: rune[2], - })) as RuneBalance[]; + })) as RuneBalance[] } catch (error) { - console.error("Error fetching ord address:", error); + console.error('Error fetching ord address:', error) } -}; +} export const mapRuneBalances = async ({ ordOutputs, @@ -115,39 +128,41 @@ export const mapRuneBalances = async ({ ordOutputs: OrdOutputs[] }): Promise => { try { - const runeOutpoints: SingleRuneOutpoint[] = []; + const runeOutpoints: SingleRuneOutpoint[] = [] for (let i = 0; i < ordOutputs.length; i++) { - const ordOutput = ordOutputs[i]; - const { result } = ordOutput; - if (!result.output?.split(":")) { + const ordOutput = ordOutputs[i] + const { result } = ordOutput + if (!result.output?.split(':')) { throw new Error('No output found') } - const { output, address, runes } = result; + const { output, address, runes } = result const singleRuneOutpoint: SingleRuneOutpoint = { output, wallet_addr: address, - script: "", + script: '', balances: [], decimals: [], rune_ids: [], value: result.value, - }; + } - const [txId, txIndex] = output.split(":"); - console.log(txId, txIndex, output); - singleRuneOutpoint["script"] = Buffer.from(getPublicKeyHash(address, MAINNET)).toString("hex"); - if (typeof runes === "object" && !Array.isArray(runes)) { + const [txId, txIndex] = output.split(':') + console.log(txId, txIndex, output) + singleRuneOutpoint['script'] = Buffer.from( + getPublicKeyHash(address, MAINNET) + ).toString('hex') + if (typeof runes === 'object' && !Array.isArray(runes)) { for (const rune in runes) { - singleRuneOutpoint.balances.push(runes[rune].amount); - singleRuneOutpoint.decimals.push(runes[rune].divisibility); - singleRuneOutpoint.rune_ids.push((await getRuneByName(rune)).id); + singleRuneOutpoint.balances.push(runes[rune].amount) + singleRuneOutpoint.decimals.push(runes[rune].divisibility) + singleRuneOutpoint.rune_ids.push((await getRuneByName(rune)).id) } } - runeOutpoints.push(singleRuneOutpoint); + runeOutpoints.push(singleRuneOutpoint) } - return runeOutpoints; + return runeOutpoints } catch (e) { throw e } diff --git a/packages/lasereyes-core/src/lib/utils.ts b/packages/lasereyes-core/src/lib/utils.ts index b1588ec..ffa3487 100644 --- a/packages/lasereyes-core/src/lib/utils.ts +++ b/packages/lasereyes-core/src/lib/utils.ts @@ -9,7 +9,6 @@ export const isHex = (str: string): boolean => { return hexRegex.test(str) } - export const encodeVarint = (bigIntValue: any) => { const bufferArray = [] let num = bigIntValue diff --git a/packages/lasereyes-core/src/types/index.ts b/packages/lasereyes-core/src/types/index.ts index 4078c87..37f968c 100644 --- a/packages/lasereyes-core/src/types/index.ts +++ b/packages/lasereyes-core/src/types/index.ts @@ -106,12 +106,12 @@ export type ContentType = export type Config = { network: - | typeof MAINNET - | typeof TESTNET - | typeof TESTNET4 - | typeof SIGNET - | typeof FRACTAL_MAINNET - | typeof FRACTAL_TESTNET + | typeof MAINNET + | typeof TESTNET + | typeof TESTNET4 + | typeof SIGNET + | typeof FRACTAL_MAINNET + | typeof FRACTAL_TESTNET } export type SendArgs = BTCSendArgs | RuneSendArgs @@ -133,7 +133,6 @@ export interface RuneSendArgs { network: NetworkType } - export interface OYLBalanceResponse { brc20s: { total: number diff --git a/packages/lasereyes-core/src/types/ord.ts b/packages/lasereyes-core/src/types/ord.ts index 53a155e..b6d29c3 100644 --- a/packages/lasereyes-core/src/types/ord.ts +++ b/packages/lasereyes-core/src/types/ord.ts @@ -1,5 +1,5 @@ export interface OrdOutputs { - result: OrdOutput; + result: OrdOutput } export type OrdOutputRune = { @@ -21,10 +21,10 @@ export interface OrdOutput { } export type RuneBalance = { - name: string; - balance: string; - symbol: string; -}; + name: string + balance: string + symbol: string +} export type OrdAddressResponse = { jsonrpc: string diff --git a/packages/lasereyes-core/src/types/sandshrew.ts b/packages/lasereyes-core/src/types/sandshrew.ts index 8c68965..a7f4226 100644 --- a/packages/lasereyes-core/src/types/sandshrew.ts +++ b/packages/lasereyes-core/src/types/sandshrew.ts @@ -1,12 +1,12 @@ export interface SingleRuneOutpoint { - output: string; - wallet_addr: string; - balances: number[]; - decimals: number[]; - rune_ids: string[]; - script: string; - value: number; -}; + output: string + wallet_addr: string + balances: number[] + decimals: number[] + rune_ids: string[] + script: string + value: number +} export interface EsploraTx { txid: string diff --git a/packages/lasereyes-react/lib/icons/sparrow.tsx b/packages/lasereyes-react/lib/icons/sparrow.tsx index 91dac44..79831cf 100644 --- a/packages/lasereyes-react/lib/icons/sparrow.tsx +++ b/packages/lasereyes-react/lib/icons/sparrow.tsx @@ -14,6 +14,7 @@ const SparrowLogo: React.FC = ({ height={size} viewBox="0 0 200 200" fill="none" + className={className} {...props} > diff --git a/packages/lasereyes-react/lib/providers/context.ts b/packages/lasereyes-react/lib/providers/context.ts index d1be4c2..c932654 100644 --- a/packages/lasereyes-react/lib/providers/context.ts +++ b/packages/lasereyes-react/lib/providers/context.ts @@ -10,8 +10,8 @@ import { MapStore, WritableAtom } from 'nanostores' const { $store, $network } = createStores() export const defaultMethods = { - connect: async () => { }, - disconnect: () => { }, + connect: async () => {}, + disconnect: () => {}, getBalance: async () => '', getMetaBalances: async () => [], getInscriptions: async () => [], @@ -25,9 +25,9 @@ export const defaultMethods = { signedPsbtBase64: '', signedPsbtHex: '', }), - switchNetwork: async () => { }, + switchNetwork: async () => {}, inscribe: async () => '', - send: async () => '' + send: async () => '', } export const LaserEyesStoreContext = createContext<{ $store: MapStore diff --git a/packages/lasereyes-react/lib/providers/lasereyes-provider.tsx b/packages/lasereyes-react/lib/providers/lasereyes-provider.tsx index 65a5ddf..00b88d6 100644 --- a/packages/lasereyes-react/lib/providers/lasereyes-provider.tsx +++ b/packages/lasereyes-react/lib/providers/lasereyes-provider.tsx @@ -13,6 +13,7 @@ import { Protocol, ProviderType, RuneSendArgs, + SignMessageOptions, } from '@omnisat/lasereyes-core' export default function LaserEyesProvider({ @@ -50,7 +51,8 @@ export default function LaserEyesProvider({ ) const getMetaBalances = useCallback( async (protocol: Protocol) => - await client?.getMetaBalances(protocol) ?? defaultMethods.getMetaBalances(), + (await client?.getMetaBalances(protocol)) ?? + defaultMethods.getMetaBalances(), [client] ) const getInscriptions = useCallback( @@ -72,9 +74,22 @@ export default function LaserEyesProvider({ [client] ) const signMessage = useCallback( - async (message: string, toSignAddress?: string) => - (await client?.signMessage(message, toSignAddress)) ?? - defaultMethods.signMessage(), + async ( + message: string, + toSignAddressOrOptions?: string | SignMessageOptions + ) => { + let options: SignMessageOptions = {} + if (typeof toSignAddressOrOptions === 'string') { + options = { toSignAddress: toSignAddressOrOptions } + } else if (toSignAddressOrOptions) { + options = toSignAddressOrOptions + } + + return ( + (await client?.signMessage(message, options)) ?? + defaultMethods.signMessage() + ) + }, [client] ) const requestAccounts = useCallback( @@ -133,7 +148,7 @@ export default function LaserEyesProvider({ signPsbt, switchNetwork, inscribe, - send + send, } }, [ client, @@ -141,11 +156,13 @@ export default function LaserEyesProvider({ disconnect, getBalance, getInscriptions, + getMetaBalances, getNetwork, getPublicKey, inscribe, pushPsbt, requestAccounts, + send, sendBTC, signMessage, signPsbt, diff --git a/packages/lasereyes-react/lib/providers/types.ts b/packages/lasereyes-react/lib/providers/types.ts index 21c4ead..d8c0c74 100644 --- a/packages/lasereyes-react/lib/providers/types.ts +++ b/packages/lasereyes-react/lib/providers/types.ts @@ -1,4 +1,11 @@ -import { ContentType, NetworkType, Protocol, ProviderType, SendArgs } from '@omnisat/lasereyes-core' +import { + ContentType, + NetworkType, + Protocol, + ProviderType, + SendArgs, + SignMessageOptions, +} from '@omnisat/lasereyes-core' export type LaserEyesContextType = { isInitializing: boolean @@ -35,17 +42,20 @@ export type LaserEyesContextType = { getMetaBalances: (protocol: Protocol) => Promise getInscriptions: (offset?: number, limit?: number) => Promise sendBTC: (to: string, amount: number) => Promise - signMessage: (message: string, toSignAddress?: string) => Promise + signMessage: ( + message: string, + toSignAddressOrOptions?: string | SignMessageOptions + ) => Promise signPsbt: ( tx: string, finalize?: boolean, broadcast?: boolean ) => Promise< | { - signedPsbtHex: string | undefined - signedPsbtBase64: string | undefined - txId?: string - } + signedPsbtHex: string | undefined + signedPsbtBase64: string | undefined + txId?: string + } | undefined > pushPsbt: (tx: string) => Promise diff --git a/packages/lasereyes-react/package.json b/packages/lasereyes-react/package.json index cbca070..ed9e88d 100644 --- a/packages/lasereyes-react/package.json +++ b/packages/lasereyes-react/package.json @@ -1,7 +1,7 @@ { "name": "@omnisat/lasereyes-react", "private": false, - "version": "0.0.56", + "version": "0.0.57-rc.2", "type": "module", "main": "./dist/index.umd.cjs", "module": "./dist/index.js", diff --git a/packages/lasereyes/package.json b/packages/lasereyes/package.json index 72400db..62ec799 100644 --- a/packages/lasereyes/package.json +++ b/packages/lasereyes/package.json @@ -20,7 +20,7 @@ "url": "https://github.com/omnisat/lasereyes-mono.git" }, "private": false, - "version": "0.0.139", + "version": "0.0.140-rc.5", "type": "module", "main": "./dist/index.umd.cjs", "module": "./dist/index.js", @@ -56,4 +56,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +}