@omnisat/lasereyes 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -76,6 +76,7 @@ type LaserEyesContextType = {
76
76
  accounts: string[];
77
77
  hasUnisat: boolean;
78
78
  hasXverse: boolean;
79
+ hasOyl: boolean;
79
80
  connect: (walletName: typeof UNISAT | typeof XVERSE) => Promise<void>;
80
81
  disconnect: () => void;
81
82
  requestAccounts: () => Promise<string[]>;
@@ -96,6 +97,16 @@ type LaserEyesContextType = {
96
97
  type Config = {
97
98
  network: typeof MAINNET | typeof TESTNET | typeof REGTEST;
98
99
  };
100
+ interface BlockchainInfoUTXO {
101
+ tx_hash_big_endian: string;
102
+ tx_hash: string;
103
+ tx_output_n: number;
104
+ script: string;
105
+ value: number;
106
+ value_hex: string;
107
+ confirmations: number;
108
+ tx_index: number;
109
+ }
99
110
 
100
111
  declare const createConfig: (config: Config) => Config;
101
112
 
@@ -109,9 +120,16 @@ declare const getBitcoinNetwork: (network: typeof MAINNET | typeof TESTNET | typ
109
120
  declare const findOrdinalsAddress: (addresses: any) => any;
110
121
  declare const findPaymentAddress: (addresses: any) => any;
111
122
  declare const getBTCBalance: (address: string) => Promise<number>;
123
+ declare const getAddressUtxos: (address: string) => Promise<BlockchainInfoUTXO[]>;
112
124
  declare const satoshisToBTC: (satoshis: number) => string;
113
125
  declare const isBase64: (str: string) => boolean;
114
126
  declare const isHex: (str: string) => boolean;
115
127
  declare function getAddressType(address: string): "p2tr" | "p2pkh" | "p2wpkh" | "p2psh" | "p2wsh";
128
+ declare function estimateTxSize(taprootInputCount: number, nonTaprootInputCount: number, outputCount: number): number;
129
+ declare function createSendBtcPsbt(address: string, paymentAddress: string, recipientAddress: string, amount: number, paymentPublicKey: string, network: typeof MAINNET | typeof TESTNET, feeRate?: number): Promise<{
130
+ psbtBase64: string;
131
+ psbtHex: string;
132
+ }>;
133
+ declare function getRedeemScript(paymentPublicKey: string, network: typeof MAINNET | typeof TESTNET): Buffer | undefined;
116
134
 
117
- export { LEATHER, LEATHER_MAINNET, LEATHER_TESTNET, LaserEyesProvider, MAINNET, MEMPOOL_SPACE_TESTNET_URL, MEMPOOL_SPACE_URL, OYL, P2PKH, P2PSH, P2SH, P2TR, P2WPKH, P2WSH, PHANTOM, REGTEST, TESTNET, UNISAT, UNISAT_MAINNET, UNISAT_TESTNET, WALLETS, XVERSE, XVERSE_MAINNET, XVERSE_NETWORK, XVERSE_TESTNET, createConfig, findOrdinalsAddress, findPaymentAddress, getAddressType, getBTCBalance, getBitcoinNetwork, getLeatherNetwork, getMempoolSpaceUrl, getNetworkForLeather, getNetworkForUnisat, getNetworkForXverse, getUnisatNetwork, getXverseNetwork, isBase64, isHex, satoshisToBTC, useLaserEyes };
135
+ export { LEATHER, LEATHER_MAINNET, LEATHER_TESTNET, LaserEyesProvider, MAINNET, MEMPOOL_SPACE_TESTNET_URL, MEMPOOL_SPACE_URL, OYL, P2PKH, P2PSH, P2SH, P2TR, P2WPKH, P2WSH, PHANTOM, REGTEST, TESTNET, UNISAT, UNISAT_MAINNET, UNISAT_TESTNET, WALLETS, XVERSE, XVERSE_MAINNET, XVERSE_NETWORK, XVERSE_TESTNET, createConfig, createSendBtcPsbt, estimateTxSize, findOrdinalsAddress, findPaymentAddress, getAddressType, getAddressUtxos, getBTCBalance, getBitcoinNetwork, getLeatherNetwork, getMempoolSpaceUrl, getNetworkForLeather, getNetworkForUnisat, getNetworkForXverse, getRedeemScript, getUnisatNetwork, getXverseNetwork, isBase64, isHex, satoshisToBTC, useLaserEyes };
package/dist/index.js CHANGED
@@ -99,9 +99,12 @@ __export(src_exports, {
99
99
  XVERSE_NETWORK: () => XVERSE_NETWORK,
100
100
  XVERSE_TESTNET: () => XVERSE_TESTNET,
101
101
  createConfig: () => createConfig,
102
+ createSendBtcPsbt: () => createSendBtcPsbt,
103
+ estimateTxSize: () => estimateTxSize,
102
104
  findOrdinalsAddress: () => findOrdinalsAddress,
103
105
  findPaymentAddress: () => findPaymentAddress,
104
106
  getAddressType: () => getAddressType,
107
+ getAddressUtxos: () => getAddressUtxos,
105
108
  getBTCBalance: () => getBTCBalance,
106
109
  getBitcoinNetwork: () => getBitcoinNetwork,
107
110
  getLeatherNetwork: () => getLeatherNetwork,
@@ -109,6 +112,7 @@ __export(src_exports, {
109
112
  getNetworkForLeather: () => getNetworkForLeather,
110
113
  getNetworkForUnisat: () => getNetworkForUnisat,
111
114
  getNetworkForXverse: () => getNetworkForXverse,
115
+ getRedeemScript: () => getRedeemScript,
112
116
  getUnisatNetwork: () => getUnisatNetwork,
113
117
  getXverseNetwork: () => getXverseNetwork,
114
118
  isBase64: () => isBase64,
@@ -232,6 +236,7 @@ var LOCAL_STORAGE_DEFAULT_WALLET = "defaultWallet";
232
236
  var initialWalletContext = {
233
237
  hasUnisat: false,
234
238
  hasXverse: false,
239
+ hasOyl: false,
235
240
  connected: false,
236
241
  isConnecting: false,
237
242
  publicKey: "",
@@ -288,6 +293,15 @@ var import_usehooks_ts = require("usehooks-ts");
288
293
  // src/lib/helpers.ts
289
294
  var bitcoin = __toESM(require("bitcoinjs-lib"));
290
295
  var import_axios = __toESM(require("axios"));
296
+
297
+ // src/lib/urls.ts
298
+ var MEMPOOL_SPACE_URL2 = "https://mempool.space";
299
+ var MEMPOOL_SPACE_TESTNET_URL2 = "https://mempool.space/testnet";
300
+ var getMempoolSpaceUrl2 = (network) => network === TESTNET ? MEMPOOL_SPACE_TESTNET_URL2 : MEMPOOL_SPACE_URL2;
301
+
302
+ // src/lib/helpers.ts
303
+ var ecc = __toESM(require("@bitcoinerlab/secp256k1"));
304
+ bitcoin.initEccLib(ecc);
291
305
  var getBitcoinNetwork = (network) => {
292
306
  if (network === TESTNET) {
293
307
  return bitcoin.networks.testnet;
@@ -315,6 +329,13 @@ var getBTCBalance = (address2) => __async(void 0, null, function* () {
315
329
  throw new Error("Failed to fetch BTC balance");
316
330
  }
317
331
  });
332
+ var getAddressUtxos = (address2) => __async(void 0, null, function* () {
333
+ try {
334
+ return yield import_axios.default.get(`https://blockchain.info/unspent?active=${address2}`).then((response) => response.data.unspent_outputs);
335
+ } catch (error) {
336
+ throw new Error("Failed to fetch UTXOs");
337
+ }
338
+ });
318
339
  var satoshisToBTC = (satoshis) => {
319
340
  if (Number.isNaN(satoshis) || satoshis === void 0)
320
341
  return "--";
@@ -355,6 +376,96 @@ function getAddressType(address2) {
355
376
  }
356
377
  throw new Error("Invalid address");
357
378
  }
379
+ function estimateTxSize(taprootInputCount, nonTaprootInputCount, outputCount) {
380
+ const baseTxSize = 10;
381
+ const taprootInputSize = 57;
382
+ const nonTaprootInputSize = 41;
383
+ const outputSize = 34;
384
+ const totalInputSize = taprootInputCount * taprootInputSize + nonTaprootInputCount * nonTaprootInputSize;
385
+ const totalOutputSize = outputCount * outputSize;
386
+ return baseTxSize + totalInputSize + totalOutputSize;
387
+ }
388
+ function createSendBtcPsbt(address2, paymentAddress, recipientAddress, amount, paymentPublicKey, network, feeRate = 7) {
389
+ return __async(this, null, function* () {
390
+ const isTaprootOnly = address2 === paymentAddress;
391
+ const mempoolUrl = `${getMempoolSpaceUrl2(
392
+ network
393
+ )}/api/address/${paymentAddress}/utxo`;
394
+ const utxos = yield import_axios.default.get(mempoolUrl).then((response) => response.data);
395
+ const sortedUtxos = utxos.sort(
396
+ (a, b) => b.value - a.value
397
+ );
398
+ const psbt = new bitcoin.Psbt({ network: getBitcoinNetwork(network) });
399
+ const estTxSize = estimateTxSize(1, 0, 2);
400
+ const satsNeeded = Math.floor(estTxSize * feeRate) + amount;
401
+ let amountGathered = 0;
402
+ let counter = 0;
403
+ try {
404
+ for (var iter = __forAwait(sortedUtxos), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
405
+ let utxo = temp.value;
406
+ const { txid, vout, value } = utxo;
407
+ const script = bitcoin.address.toOutputScript(
408
+ paymentAddress,
409
+ getBitcoinNetwork(network)
410
+ );
411
+ psbt.addInput({
412
+ hash: txid,
413
+ index: vout,
414
+ witnessUtxo: {
415
+ script,
416
+ value
417
+ }
418
+ });
419
+ if (isTaprootOnly) {
420
+ const redeemScript = getRedeemScript(paymentPublicKey, network);
421
+ psbt.updateInput(counter, { redeemScript });
422
+ }
423
+ amountGathered += value;
424
+ if (amountGathered >= satsNeeded) {
425
+ break;
426
+ }
427
+ }
428
+ } catch (temp) {
429
+ error = [temp];
430
+ } finally {
431
+ try {
432
+ more && (temp = iter.return) && (yield temp.call(iter));
433
+ } finally {
434
+ if (error)
435
+ throw error[0];
436
+ }
437
+ }
438
+ if (amountGathered < satsNeeded) {
439
+ throw new Error("Insufficient funds");
440
+ }
441
+ psbt.addOutput({
442
+ address: recipientAddress,
443
+ value: amount
444
+ });
445
+ if (amountGathered > satsNeeded) {
446
+ psbt.addOutput({
447
+ address: paymentAddress,
448
+ value: amountGathered - satsNeeded - amount
449
+ });
450
+ }
451
+ return {
452
+ psbtBase64: psbt.toBase64(),
453
+ psbtHex: psbt.toHex()
454
+ };
455
+ });
456
+ }
457
+ function getRedeemScript(paymentPublicKey, network) {
458
+ var _a;
459
+ const p2wpkh = bitcoin.payments.p2wpkh({
460
+ pubkey: Buffer.from(paymentPublicKey, "hex"),
461
+ network: getBitcoinNetwork(network)
462
+ });
463
+ const p2sh = bitcoin.payments.p2sh({
464
+ redeem: p2wpkh,
465
+ network: getBitcoinNetwork(network)
466
+ });
467
+ return (_a = p2sh == null ? void 0 : p2sh.redeem) == null ? void 0 : _a.output;
468
+ }
358
469
 
359
470
  // src/providers/LaserEyesProvider.tsx
360
471
  var import_sats_connect = require("sats-connect");
@@ -384,6 +495,7 @@ var LaserEyesProvider = ({
384
495
  const [balance, setBalance] = (0, import_react.useState)();
385
496
  const [hasUnisat, setHasUnisat] = (0, import_react.useState)(false);
386
497
  const [hasXverse, setHasXverse] = (0, import_react.useState)(false);
498
+ const [hasOyl, setHasOyl] = (0, import_react.useState)(false);
387
499
  const [network, setNetwork] = (0, import_usehooks_ts.useLocalStorage)("network", MAINNET, {
388
500
  initializeWithValue: false
389
501
  });
@@ -408,6 +520,10 @@ var LaserEyesProvider = ({
408
520
  const xverseLib = (_a = window == null ? void 0 : window.XverseProviders) == null ? void 0 : _a.BitcoinProvider;
409
521
  setHasXverse(!!xverseLib);
410
522
  }, []);
523
+ (0, import_react.useEffect)(() => {
524
+ const oylLib = window == null ? void 0 : window.oyl;
525
+ setHasOyl(!!oylLib);
526
+ }, []);
411
527
  (0, import_react.useEffect)(() => {
412
528
  setBalance(void 0);
413
529
  }, [network]);
@@ -462,7 +578,6 @@ var LaserEyesProvider = ({
462
578
  try {
463
579
  localStorage == null ? void 0 : localStorage.setItem(LOCAL_STORAGE_DEFAULT_WALLET, XVERSE);
464
580
  let xverseNetwork = getXverseNetwork((config == null ? void 0 : config.network) || MAINNET);
465
- console.log({ xverseNetwork });
466
581
  const getAddressOptions = {
467
582
  payload: {
468
583
  purposes: ["ordinals", "payment"],
@@ -483,7 +598,6 @@ var LaserEyesProvider = ({
483
598
  setLibrary(window.BitcoinProvider);
484
599
  }
485
600
  getBTCBalance(foundPaymentAddress.address).then((totalBalance) => {
486
- console.log({ totalBalance });
487
601
  setBalance(totalBalance);
488
602
  });
489
603
  },
@@ -500,49 +614,39 @@ var LaserEyesProvider = ({
500
614
  throw error;
501
615
  }
502
616
  });
503
- const handleAccountsChanged = (_accounts) => {
504
- if (!_accounts.length) {
505
- disconnect();
506
- return;
507
- }
508
- if (self.accounts[0] === _accounts[0]) {
509
- return;
510
- }
511
- console.log("acct changed", _accounts[0]);
512
- self.accounts = _accounts;
513
- if (_accounts.length > 0) {
514
- connect(UNISAT);
515
- } else {
516
- setConnected(false);
517
- }
518
- };
519
- const handleNetworkChanged = (network2) => {
617
+ const connectOyl = () => __async(void 0, null, function* () {
520
618
  try {
521
- let foundNetwork = MAINNET;
522
- if (provider === UNISAT) {
523
- foundNetwork = getNetworkForUnisat(network2);
524
- setNetwork(foundNetwork);
525
- connect(provider);
526
- } else if (provider === XVERSE) {
527
- throw new Error("Not implemented");
528
- } else {
529
- throw new Error("The connected wallet doesn't support this method..");
530
- }
619
+ localStorage == null ? void 0 : localStorage.setItem(LOCAL_STORAGE_DEFAULT_WALLET, OYL);
620
+ const lib = window.oyl;
621
+ const result = yield lib.requestAccounts();
622
+ const oylPubKey = yield lib.getPublicKey();
623
+ setPaymentPublicKey(oylPubKey);
624
+ setAccounts(result);
625
+ setAddress(result[0]);
626
+ setPaymentAddress(result[1]);
627
+ setLibrary(lib);
628
+ setProvider(OYL);
629
+ handleAccountsChanged(result);
630
+ setConnected(true);
531
631
  } catch (error) {
532
- throw error;
632
+ throw new Error(`Can't lasereyes to ${OYL} wallet`);
533
633
  }
534
- };
634
+ });
535
635
  const connect = (walletName) => __async(void 0, null, function* () {
536
636
  setIsConnecting(true);
537
637
  try {
538
638
  if (!walletName)
539
639
  throw new Error("No wallet provided");
540
640
  if (walletName === UNISAT) {
641
+ console.log("connectin unisat");
541
642
  yield connectUnisat();
542
643
  } else if (walletName === XVERSE) {
543
644
  yield connectXverse();
645
+ } else if (walletName === OYL) {
646
+ console.log("connectin oyl");
647
+ yield connectOyl();
544
648
  } else {
545
- throw new Error("The connected wallet doesn't support this method..!");
649
+ throw new Error("Unsupported wallet..");
546
650
  }
547
651
  setConnected(true);
548
652
  } catch (error) {
@@ -565,6 +669,40 @@ var LaserEyesProvider = ({
565
669
  setBalance(void 0);
566
670
  localStorage == null ? void 0 : localStorage.removeItem(LOCAL_STORAGE_DEFAULT_WALLET);
567
671
  };
672
+ const handleAccountsChanged = (_accounts) => {
673
+ if (provider !== UNISAT)
674
+ return;
675
+ if (!_accounts.length) {
676
+ disconnect();
677
+ return;
678
+ }
679
+ if (self.accounts[0] === _accounts[0]) {
680
+ return;
681
+ }
682
+ console.log("acct changed", _accounts[0]);
683
+ self.accounts = _accounts;
684
+ if (_accounts.length > 0) {
685
+ connect(UNISAT);
686
+ } else {
687
+ setConnected(false);
688
+ }
689
+ };
690
+ const handleNetworkChanged = (network2) => {
691
+ try {
692
+ let foundNetwork = MAINNET;
693
+ if (provider === UNISAT) {
694
+ foundNetwork = getNetworkForUnisat(network2);
695
+ setNetwork(foundNetwork);
696
+ connect(provider);
697
+ } else if (provider === XVERSE) {
698
+ throw new Error("Not implemented");
699
+ } else {
700
+ throw new Error("The connected wallet doesn't support this method..");
701
+ }
702
+ } catch (error) {
703
+ throw error;
704
+ }
705
+ };
568
706
  const requestAccounts = () => __async(void 0, null, function* () {
569
707
  try {
570
708
  if (!library)
@@ -594,6 +732,8 @@ var LaserEyesProvider = ({
594
732
  }
595
733
  };
596
734
  return [address2];
735
+ } else if (provider === OYL) {
736
+ return yield library.requestAccounts();
597
737
  } else {
598
738
  throw new Error("The connected wallet doesn't support this method..");
599
739
  }
@@ -616,6 +756,11 @@ var LaserEyesProvider = ({
616
756
  return TESTNET;
617
757
  }
618
758
  return MAINNET;
759
+ } else if (provider === OYL) {
760
+ if (address2.slice(0, 1) === "t") {
761
+ return TESTNET;
762
+ }
763
+ return MAINNET;
619
764
  }
620
765
  return (_a = config == null ? void 0 : config.network) != null ? _a : MAINNET;
621
766
  } catch (error) {
@@ -642,7 +787,8 @@ var LaserEyesProvider = ({
642
787
  if (!library)
643
788
  return;
644
789
  if (provider === UNISAT) {
645
- const pub = yield library == null ? void 0 : library.getPublicKey();
790
+ return yield library == null ? void 0 : library.getPublicKey();
791
+ } else if (provider === OYL) {
646
792
  return yield library == null ? void 0 : library.getPublicKey();
647
793
  } else {
648
794
  throw new Error("The connected wallet doesn't support this method..");
@@ -659,6 +805,9 @@ var LaserEyesProvider = ({
659
805
  return yield library.getBalance();
660
806
  } else if (provider === XVERSE) {
661
807
  return yield getBTCBalance(paymentAddress);
808
+ } else if (provider === OYL) {
809
+ const balanceResponse = yield library.getBalance();
810
+ return balanceResponse.btc.total * 1e8;
662
811
  }
663
812
  } catch (error) {
664
813
  throw error;
@@ -670,6 +819,8 @@ var LaserEyesProvider = ({
670
819
  return;
671
820
  if (provider === UNISAT) {
672
821
  return yield library.getInscriptions(0, 10);
822
+ } else if (provider === OYL) {
823
+ return yield library.getInscriptions(0, 10);
673
824
  } else {
674
825
  throw new Error("The connected wallet doesn't support this method..");
675
826
  }
@@ -690,8 +841,39 @@ var LaserEyesProvider = ({
690
841
  if (!txId)
691
842
  throw new Error("Transaction failed");
692
843
  return txId;
693
- } else {
694
- throw new Error("The connected wallet doesn't support this method..");
844
+ } else if (provider === XVERSE) {
845
+ const response = yield (0, import_sats_connect.request)("sendTransfer", {
846
+ recipients: [
847
+ {
848
+ address: to,
849
+ amount
850
+ }
851
+ ]
852
+ });
853
+ if (response.status === "success") {
854
+ return response.result.txid;
855
+ } else {
856
+ if (response.error.code === import_sats_connect.RpcErrorCode.USER_REJECTION) {
857
+ throw new Error("User rejected the request");
858
+ } else {
859
+ throw new Error("Error sending BTC: " + response.error.message);
860
+ }
861
+ }
862
+ } else if (provider === OYL) {
863
+ const { psbtHex, psbtBase64 } = yield createSendBtcPsbt(
864
+ address2,
865
+ paymentAddress,
866
+ to,
867
+ amount,
868
+ paymentPublicKey,
869
+ //@ts-ignore
870
+ network,
871
+ 7
872
+ );
873
+ const psbt = yield signPsbt(psbtBase64, true, true);
874
+ if (!psbt)
875
+ throw new Error("Error sending BTC");
876
+ return psbt.txId;
695
877
  }
696
878
  } catch (error) {
697
879
  throw error;
@@ -834,6 +1016,26 @@ var LaserEyesProvider = ({
834
1016
  signedPsbtBase64,
835
1017
  txId
836
1018
  };
1019
+ } else if (provider === OYL) {
1020
+ const signedPsbt = yield library == null ? void 0 : library.signPsbt(psbtHex, true, true);
1021
+ console.log({ signedPsbt });
1022
+ const psbtSignedPsbt = bitcoin2.Psbt.fromHex(signedPsbt);
1023
+ console.log("psbtSignedPsbt", psbtSignedPsbt.toBase64());
1024
+ if (broadcast) {
1025
+ console.log("broadcastin");
1026
+ const txId = yield pushPsbt(psbtSignedPsbt.toHex());
1027
+ return {
1028
+ signedPsbtHex: psbtSignedPsbt.toHex(),
1029
+ signedPsbtBase64: psbtSignedPsbt.toBase64(),
1030
+ txId
1031
+ };
1032
+ } else {
1033
+ return {
1034
+ signedPsbtHex: psbtSignedPsbt.toHex(),
1035
+ signedPsbtBase64: psbtSignedPsbt.toBase64(),
1036
+ txId: void 0
1037
+ };
1038
+ }
837
1039
  } else {
838
1040
  throw new Error("The connected wallet doesn't support this method..");
839
1041
  }
@@ -847,6 +1049,8 @@ var LaserEyesProvider = ({
847
1049
  return;
848
1050
  if (provider === UNISAT) {
849
1051
  return yield library == null ? void 0 : library.pushPsbt(psbt);
1052
+ } else if (provider === OYL) {
1053
+ return yield library == null ? void 0 : library.pushPsbt(psbt);
850
1054
  } else {
851
1055
  throw new Error("The connected wallet doesn't support this method..");
852
1056
  }
@@ -871,6 +1075,7 @@ var LaserEyesProvider = ({
871
1075
  isConnecting,
872
1076
  hasUnisat,
873
1077
  hasXverse,
1078
+ hasOyl,
874
1079
  // functions
875
1080
  connect,
876
1081
  disconnect,
@@ -917,9 +1122,12 @@ var LaserEyesProvider = ({
917
1122
  XVERSE_NETWORK,
918
1123
  XVERSE_TESTNET,
919
1124
  createConfig,
1125
+ createSendBtcPsbt,
1126
+ estimateTxSize,
920
1127
  findOrdinalsAddress,
921
1128
  findPaymentAddress,
922
1129
  getAddressType,
1130
+ getAddressUtxos,
923
1131
  getBTCBalance,
924
1132
  getBitcoinNetwork,
925
1133
  getLeatherNetwork,
@@ -927,6 +1135,7 @@ var LaserEyesProvider = ({
927
1135
  getNetworkForLeather,
928
1136
  getNetworkForUnisat,
929
1137
  getNetworkForXverse,
1138
+ getRedeemScript,
930
1139
  getUnisatNetwork,
931
1140
  getXverseNetwork,
932
1141
  isBase64,
package/dist/index.mjs CHANGED
@@ -164,6 +164,7 @@ var LOCAL_STORAGE_DEFAULT_WALLET = "defaultWallet";
164
164
  var initialWalletContext = {
165
165
  hasUnisat: false,
166
166
  hasXverse: false,
167
+ hasOyl: false,
167
168
  connected: false,
168
169
  isConnecting: false,
169
170
  publicKey: "",
@@ -220,6 +221,15 @@ import { useLocalStorage } from "usehooks-ts";
220
221
  // src/lib/helpers.ts
221
222
  import * as bitcoin from "bitcoinjs-lib";
222
223
  import axios from "axios";
224
+
225
+ // src/lib/urls.ts
226
+ var MEMPOOL_SPACE_URL2 = "https://mempool.space";
227
+ var MEMPOOL_SPACE_TESTNET_URL2 = "https://mempool.space/testnet";
228
+ var getMempoolSpaceUrl2 = (network) => network === TESTNET ? MEMPOOL_SPACE_TESTNET_URL2 : MEMPOOL_SPACE_URL2;
229
+
230
+ // src/lib/helpers.ts
231
+ import * as ecc from "@bitcoinerlab/secp256k1";
232
+ bitcoin.initEccLib(ecc);
223
233
  var getBitcoinNetwork = (network) => {
224
234
  if (network === TESTNET) {
225
235
  return bitcoin.networks.testnet;
@@ -247,6 +257,13 @@ var getBTCBalance = (address2) => __async(void 0, null, function* () {
247
257
  throw new Error("Failed to fetch BTC balance");
248
258
  }
249
259
  });
260
+ var getAddressUtxos = (address2) => __async(void 0, null, function* () {
261
+ try {
262
+ return yield axios.get(`https://blockchain.info/unspent?active=${address2}`).then((response) => response.data.unspent_outputs);
263
+ } catch (error) {
264
+ throw new Error("Failed to fetch UTXOs");
265
+ }
266
+ });
250
267
  var satoshisToBTC = (satoshis) => {
251
268
  if (Number.isNaN(satoshis) || satoshis === void 0)
252
269
  return "--";
@@ -287,6 +304,96 @@ function getAddressType(address2) {
287
304
  }
288
305
  throw new Error("Invalid address");
289
306
  }
307
+ function estimateTxSize(taprootInputCount, nonTaprootInputCount, outputCount) {
308
+ const baseTxSize = 10;
309
+ const taprootInputSize = 57;
310
+ const nonTaprootInputSize = 41;
311
+ const outputSize = 34;
312
+ const totalInputSize = taprootInputCount * taprootInputSize + nonTaprootInputCount * nonTaprootInputSize;
313
+ const totalOutputSize = outputCount * outputSize;
314
+ return baseTxSize + totalInputSize + totalOutputSize;
315
+ }
316
+ function createSendBtcPsbt(address2, paymentAddress, recipientAddress, amount, paymentPublicKey, network, feeRate = 7) {
317
+ return __async(this, null, function* () {
318
+ const isTaprootOnly = address2 === paymentAddress;
319
+ const mempoolUrl = `${getMempoolSpaceUrl2(
320
+ network
321
+ )}/api/address/${paymentAddress}/utxo`;
322
+ const utxos = yield axios.get(mempoolUrl).then((response) => response.data);
323
+ const sortedUtxos = utxos.sort(
324
+ (a, b) => b.value - a.value
325
+ );
326
+ const psbt = new bitcoin.Psbt({ network: getBitcoinNetwork(network) });
327
+ const estTxSize = estimateTxSize(1, 0, 2);
328
+ const satsNeeded = Math.floor(estTxSize * feeRate) + amount;
329
+ let amountGathered = 0;
330
+ let counter = 0;
331
+ try {
332
+ for (var iter = __forAwait(sortedUtxos), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
333
+ let utxo = temp.value;
334
+ const { txid, vout, value } = utxo;
335
+ const script = bitcoin.address.toOutputScript(
336
+ paymentAddress,
337
+ getBitcoinNetwork(network)
338
+ );
339
+ psbt.addInput({
340
+ hash: txid,
341
+ index: vout,
342
+ witnessUtxo: {
343
+ script,
344
+ value
345
+ }
346
+ });
347
+ if (isTaprootOnly) {
348
+ const redeemScript = getRedeemScript(paymentPublicKey, network);
349
+ psbt.updateInput(counter, { redeemScript });
350
+ }
351
+ amountGathered += value;
352
+ if (amountGathered >= satsNeeded) {
353
+ break;
354
+ }
355
+ }
356
+ } catch (temp) {
357
+ error = [temp];
358
+ } finally {
359
+ try {
360
+ more && (temp = iter.return) && (yield temp.call(iter));
361
+ } finally {
362
+ if (error)
363
+ throw error[0];
364
+ }
365
+ }
366
+ if (amountGathered < satsNeeded) {
367
+ throw new Error("Insufficient funds");
368
+ }
369
+ psbt.addOutput({
370
+ address: recipientAddress,
371
+ value: amount
372
+ });
373
+ if (amountGathered > satsNeeded) {
374
+ psbt.addOutput({
375
+ address: paymentAddress,
376
+ value: amountGathered - satsNeeded - amount
377
+ });
378
+ }
379
+ return {
380
+ psbtBase64: psbt.toBase64(),
381
+ psbtHex: psbt.toHex()
382
+ };
383
+ });
384
+ }
385
+ function getRedeemScript(paymentPublicKey, network) {
386
+ var _a;
387
+ const p2wpkh = bitcoin.payments.p2wpkh({
388
+ pubkey: Buffer.from(paymentPublicKey, "hex"),
389
+ network: getBitcoinNetwork(network)
390
+ });
391
+ const p2sh = bitcoin.payments.p2sh({
392
+ redeem: p2wpkh,
393
+ network: getBitcoinNetwork(network)
394
+ });
395
+ return (_a = p2sh == null ? void 0 : p2sh.redeem) == null ? void 0 : _a.output;
396
+ }
290
397
 
291
398
  // src/providers/LaserEyesProvider.tsx
292
399
  import {
@@ -321,6 +428,7 @@ var LaserEyesProvider = ({
321
428
  const [balance, setBalance] = useState();
322
429
  const [hasUnisat, setHasUnisat] = useState(false);
323
430
  const [hasXverse, setHasXverse] = useState(false);
431
+ const [hasOyl, setHasOyl] = useState(false);
324
432
  const [network, setNetwork] = useLocalStorage("network", MAINNET, {
325
433
  initializeWithValue: false
326
434
  });
@@ -345,6 +453,10 @@ var LaserEyesProvider = ({
345
453
  const xverseLib = (_a = window == null ? void 0 : window.XverseProviders) == null ? void 0 : _a.BitcoinProvider;
346
454
  setHasXverse(!!xverseLib);
347
455
  }, []);
456
+ useEffect(() => {
457
+ const oylLib = window == null ? void 0 : window.oyl;
458
+ setHasOyl(!!oylLib);
459
+ }, []);
348
460
  useEffect(() => {
349
461
  setBalance(void 0);
350
462
  }, [network]);
@@ -399,7 +511,6 @@ var LaserEyesProvider = ({
399
511
  try {
400
512
  localStorage == null ? void 0 : localStorage.setItem(LOCAL_STORAGE_DEFAULT_WALLET, XVERSE);
401
513
  let xverseNetwork = getXverseNetwork((config == null ? void 0 : config.network) || MAINNET);
402
- console.log({ xverseNetwork });
403
514
  const getAddressOptions = {
404
515
  payload: {
405
516
  purposes: ["ordinals", "payment"],
@@ -420,7 +531,6 @@ var LaserEyesProvider = ({
420
531
  setLibrary(window.BitcoinProvider);
421
532
  }
422
533
  getBTCBalance(foundPaymentAddress.address).then((totalBalance) => {
423
- console.log({ totalBalance });
424
534
  setBalance(totalBalance);
425
535
  });
426
536
  },
@@ -437,49 +547,39 @@ var LaserEyesProvider = ({
437
547
  throw error;
438
548
  }
439
549
  });
440
- const handleAccountsChanged = (_accounts) => {
441
- if (!_accounts.length) {
442
- disconnect();
443
- return;
444
- }
445
- if (self.accounts[0] === _accounts[0]) {
446
- return;
447
- }
448
- console.log("acct changed", _accounts[0]);
449
- self.accounts = _accounts;
450
- if (_accounts.length > 0) {
451
- connect(UNISAT);
452
- } else {
453
- setConnected(false);
454
- }
455
- };
456
- const handleNetworkChanged = (network2) => {
550
+ const connectOyl = () => __async(void 0, null, function* () {
457
551
  try {
458
- let foundNetwork = MAINNET;
459
- if (provider === UNISAT) {
460
- foundNetwork = getNetworkForUnisat(network2);
461
- setNetwork(foundNetwork);
462
- connect(provider);
463
- } else if (provider === XVERSE) {
464
- throw new Error("Not implemented");
465
- } else {
466
- throw new Error("The connected wallet doesn't support this method..");
467
- }
552
+ localStorage == null ? void 0 : localStorage.setItem(LOCAL_STORAGE_DEFAULT_WALLET, OYL);
553
+ const lib = window.oyl;
554
+ const result = yield lib.requestAccounts();
555
+ const oylPubKey = yield lib.getPublicKey();
556
+ setPaymentPublicKey(oylPubKey);
557
+ setAccounts(result);
558
+ setAddress(result[0]);
559
+ setPaymentAddress(result[1]);
560
+ setLibrary(lib);
561
+ setProvider(OYL);
562
+ handleAccountsChanged(result);
563
+ setConnected(true);
468
564
  } catch (error) {
469
- throw error;
565
+ throw new Error(`Can't lasereyes to ${OYL} wallet`);
470
566
  }
471
- };
567
+ });
472
568
  const connect = (walletName) => __async(void 0, null, function* () {
473
569
  setIsConnecting(true);
474
570
  try {
475
571
  if (!walletName)
476
572
  throw new Error("No wallet provided");
477
573
  if (walletName === UNISAT) {
574
+ console.log("connectin unisat");
478
575
  yield connectUnisat();
479
576
  } else if (walletName === XVERSE) {
480
577
  yield connectXverse();
578
+ } else if (walletName === OYL) {
579
+ console.log("connectin oyl");
580
+ yield connectOyl();
481
581
  } else {
482
- throw new Error("The connected wallet doesn't support this method..!");
582
+ throw new Error("Unsupported wallet..");
483
583
  }
484
584
  setConnected(true);
485
585
  } catch (error) {
@@ -502,6 +602,40 @@ var LaserEyesProvider = ({
502
602
  setBalance(void 0);
503
603
  localStorage == null ? void 0 : localStorage.removeItem(LOCAL_STORAGE_DEFAULT_WALLET);
504
604
  };
605
+ const handleAccountsChanged = (_accounts) => {
606
+ if (provider !== UNISAT)
607
+ return;
608
+ if (!_accounts.length) {
609
+ disconnect();
610
+ return;
611
+ }
612
+ if (self.accounts[0] === _accounts[0]) {
613
+ return;
614
+ }
615
+ console.log("acct changed", _accounts[0]);
616
+ self.accounts = _accounts;
617
+ if (_accounts.length > 0) {
618
+ connect(UNISAT);
619
+ } else {
620
+ setConnected(false);
621
+ }
622
+ };
623
+ const handleNetworkChanged = (network2) => {
624
+ try {
625
+ let foundNetwork = MAINNET;
626
+ if (provider === UNISAT) {
627
+ foundNetwork = getNetworkForUnisat(network2);
628
+ setNetwork(foundNetwork);
629
+ connect(provider);
630
+ } else if (provider === XVERSE) {
631
+ throw new Error("Not implemented");
632
+ } else {
633
+ throw new Error("The connected wallet doesn't support this method..");
634
+ }
635
+ } catch (error) {
636
+ throw error;
637
+ }
638
+ };
505
639
  const requestAccounts = () => __async(void 0, null, function* () {
506
640
  try {
507
641
  if (!library)
@@ -531,6 +665,8 @@ var LaserEyesProvider = ({
531
665
  }
532
666
  };
533
667
  return [address2];
668
+ } else if (provider === OYL) {
669
+ return yield library.requestAccounts();
534
670
  } else {
535
671
  throw new Error("The connected wallet doesn't support this method..");
536
672
  }
@@ -553,6 +689,11 @@ var LaserEyesProvider = ({
553
689
  return TESTNET;
554
690
  }
555
691
  return MAINNET;
692
+ } else if (provider === OYL) {
693
+ if (address2.slice(0, 1) === "t") {
694
+ return TESTNET;
695
+ }
696
+ return MAINNET;
556
697
  }
557
698
  return (_a = config == null ? void 0 : config.network) != null ? _a : MAINNET;
558
699
  } catch (error) {
@@ -579,7 +720,8 @@ var LaserEyesProvider = ({
579
720
  if (!library)
580
721
  return;
581
722
  if (provider === UNISAT) {
582
- const pub = yield library == null ? void 0 : library.getPublicKey();
723
+ return yield library == null ? void 0 : library.getPublicKey();
724
+ } else if (provider === OYL) {
583
725
  return yield library == null ? void 0 : library.getPublicKey();
584
726
  } else {
585
727
  throw new Error("The connected wallet doesn't support this method..");
@@ -596,6 +738,9 @@ var LaserEyesProvider = ({
596
738
  return yield library.getBalance();
597
739
  } else if (provider === XVERSE) {
598
740
  return yield getBTCBalance(paymentAddress);
741
+ } else if (provider === OYL) {
742
+ const balanceResponse = yield library.getBalance();
743
+ return balanceResponse.btc.total * 1e8;
599
744
  }
600
745
  } catch (error) {
601
746
  throw error;
@@ -607,6 +752,8 @@ var LaserEyesProvider = ({
607
752
  return;
608
753
  if (provider === UNISAT) {
609
754
  return yield library.getInscriptions(0, 10);
755
+ } else if (provider === OYL) {
756
+ return yield library.getInscriptions(0, 10);
610
757
  } else {
611
758
  throw new Error("The connected wallet doesn't support this method..");
612
759
  }
@@ -627,8 +774,39 @@ var LaserEyesProvider = ({
627
774
  if (!txId)
628
775
  throw new Error("Transaction failed");
629
776
  return txId;
630
- } else {
631
- throw new Error("The connected wallet doesn't support this method..");
777
+ } else if (provider === XVERSE) {
778
+ const response = yield request("sendTransfer", {
779
+ recipients: [
780
+ {
781
+ address: to,
782
+ amount
783
+ }
784
+ ]
785
+ });
786
+ if (response.status === "success") {
787
+ return response.result.txid;
788
+ } else {
789
+ if (response.error.code === RpcErrorCode.USER_REJECTION) {
790
+ throw new Error("User rejected the request");
791
+ } else {
792
+ throw new Error("Error sending BTC: " + response.error.message);
793
+ }
794
+ }
795
+ } else if (provider === OYL) {
796
+ const { psbtHex, psbtBase64 } = yield createSendBtcPsbt(
797
+ address2,
798
+ paymentAddress,
799
+ to,
800
+ amount,
801
+ paymentPublicKey,
802
+ //@ts-ignore
803
+ network,
804
+ 7
805
+ );
806
+ const psbt = yield signPsbt(psbtBase64, true, true);
807
+ if (!psbt)
808
+ throw new Error("Error sending BTC");
809
+ return psbt.txId;
632
810
  }
633
811
  } catch (error) {
634
812
  throw error;
@@ -771,6 +949,26 @@ var LaserEyesProvider = ({
771
949
  signedPsbtBase64,
772
950
  txId
773
951
  };
952
+ } else if (provider === OYL) {
953
+ const signedPsbt = yield library == null ? void 0 : library.signPsbt(psbtHex, true, true);
954
+ console.log({ signedPsbt });
955
+ const psbtSignedPsbt = bitcoin2.Psbt.fromHex(signedPsbt);
956
+ console.log("psbtSignedPsbt", psbtSignedPsbt.toBase64());
957
+ if (broadcast) {
958
+ console.log("broadcastin");
959
+ const txId = yield pushPsbt(psbtSignedPsbt.toHex());
960
+ return {
961
+ signedPsbtHex: psbtSignedPsbt.toHex(),
962
+ signedPsbtBase64: psbtSignedPsbt.toBase64(),
963
+ txId
964
+ };
965
+ } else {
966
+ return {
967
+ signedPsbtHex: psbtSignedPsbt.toHex(),
968
+ signedPsbtBase64: psbtSignedPsbt.toBase64(),
969
+ txId: void 0
970
+ };
971
+ }
774
972
  } else {
775
973
  throw new Error("The connected wallet doesn't support this method..");
776
974
  }
@@ -784,6 +982,8 @@ var LaserEyesProvider = ({
784
982
  return;
785
983
  if (provider === UNISAT) {
786
984
  return yield library == null ? void 0 : library.pushPsbt(psbt);
985
+ } else if (provider === OYL) {
986
+ return yield library == null ? void 0 : library.pushPsbt(psbt);
787
987
  } else {
788
988
  throw new Error("The connected wallet doesn't support this method..");
789
989
  }
@@ -808,6 +1008,7 @@ var LaserEyesProvider = ({
808
1008
  isConnecting,
809
1009
  hasUnisat,
810
1010
  hasXverse,
1011
+ hasOyl,
811
1012
  // functions
812
1013
  connect,
813
1014
  disconnect,
@@ -853,9 +1054,12 @@ export {
853
1054
  XVERSE_NETWORK,
854
1055
  XVERSE_TESTNET,
855
1056
  createConfig,
1057
+ createSendBtcPsbt,
1058
+ estimateTxSize,
856
1059
  findOrdinalsAddress,
857
1060
  findPaymentAddress,
858
1061
  getAddressType,
1062
+ getAddressUtxos,
859
1063
  getBTCBalance,
860
1064
  getBitcoinNetwork,
861
1065
  getLeatherNetwork,
@@ -863,6 +1067,7 @@ export {
863
1067
  getNetworkForLeather,
864
1068
  getNetworkForUnisat,
865
1069
  getNetworkForXverse,
1070
+ getRedeemScript,
866
1071
  getUnisatNetwork,
867
1072
  getXverseNetwork,
868
1073
  isBase64,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnisat/lasereyes",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "sideEffects": false,
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -16,12 +16,13 @@
16
16
  "dev": "tsup src/index.tsx --format esm,cjs --watch --dts --external react"
17
17
  },
18
18
  "dependencies": {
19
+ "@bitcoinerlab/secp256k1": "^1.1.1",
19
20
  "@changesets/cli": "^2.27.5",
20
21
  "axios": "^1.7.2",
21
22
  "bitcoinjs-lib": "^6.1.3",
22
23
  "react": "^18",
23
24
  "react-dom": "^18",
24
- "sats-connect": "^2.5.0",
25
+ "sats-connect": "^2.6.0",
25
26
  "usehooks-ts": "^2.9.1",
26
27
  "valibot": "^0.36.0"
27
28
  },