@keplr-wallet/background 0.13.12 → 0.13.14

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.
@@ -31,11 +31,31 @@ import {
31
31
  ShowSensitiveLegacyKeyRingDataMsg,
32
32
  ExportKeyRingVaultsMsg,
33
33
  SearchKeyRingsMsg,
34
+ GetAllWalletsMsg,
35
+ SwitchAccountMsg,
34
36
  } from "./messages";
35
37
  import { KeyRingService } from "./service";
38
+ import { PermissionService } from "../permission/service";
39
+ import { getBasicAccessPermissionType } from "../permission/types";
40
+ import { ChainsService } from "../chains/service";
41
+ import type { KeyRingCosmosService } from "../keyring-cosmos/service";
42
+ import type { KeyRingStarknetService } from "../keyring-starknet/service";
43
+ import type { PermissionInteractiveService } from "../permission-interactive/service";
36
44
 
37
- export const getHandler: (service: KeyRingService) => Handler = (
38
- service: KeyRingService
45
+ export const getHandler: (
46
+ service: KeyRingService,
47
+ permissionService: PermissionService,
48
+ permissionInteractiveService: PermissionInteractiveService,
49
+ chainsService: ChainsService,
50
+ keyRingCosmosService: KeyRingCosmosService,
51
+ keyRingStarknetService: KeyRingStarknetService
52
+ ) => Handler = (
53
+ service,
54
+ permissionService,
55
+ permissionInteractiveService,
56
+ chainsService,
57
+ keyRingCosmosService,
58
+ keyRingStarknetService
39
59
  ) => {
40
60
  return (env: Env, msg: Message<unknown>) => {
41
61
  switch (msg.constructor) {
@@ -134,6 +154,20 @@ export const getHandler: (service: KeyRingService) => Handler = (
134
154
  );
135
155
  case SearchKeyRingsMsg:
136
156
  return handleSearchKeyRingsMsg(service)(env, msg as SearchKeyRingsMsg);
157
+ case GetAllWalletsMsg:
158
+ return handleGetAllWalletsMsg(
159
+ service,
160
+ permissionService,
161
+ permissionInteractiveService,
162
+ chainsService,
163
+ keyRingCosmosService,
164
+ keyRingStarknetService
165
+ )(env, msg as GetAllWalletsMsg);
166
+ case SwitchAccountMsg:
167
+ return handleSwitchAccountMsg(service, permissionService)(
168
+ env,
169
+ msg as SwitchAccountMsg
170
+ );
137
171
  default:
138
172
  throw new KeplrError("keyring", 221, "Unknown msg type");
139
173
  }
@@ -435,3 +469,136 @@ const handleSearchKeyRingsMsg: (
435
469
  return service.searchKeyRings(msg.searchText);
436
470
  };
437
471
  };
472
+
473
+ const handleGetAllWalletsMsg: (
474
+ service: KeyRingService,
475
+ permissionService: PermissionService,
476
+ permissionInteractiveService: PermissionInteractiveService,
477
+ chainsService: ChainsService,
478
+ keyRingCosmosService: KeyRingCosmosService,
479
+ keyRingStarknetService: KeyRingStarknetService
480
+ ) => InternalHandler<GetAllWalletsMsg> = (
481
+ service,
482
+ permissionService,
483
+ permissionInteractiveService,
484
+ chainsService,
485
+ keyRingCosmosService,
486
+ keyRingStarknetService
487
+ ) => {
488
+ return async (env, msg) => {
489
+ const origin = msg.origin;
490
+ const permittedChains = permissionService.getOriginPermittedChains(
491
+ origin,
492
+ getBasicAccessPermissionType()
493
+ );
494
+
495
+ if (permittedChains.length === 0) {
496
+ throw new KeplrError(
497
+ "keyring",
498
+ 510,
499
+ "No permitted chains. Call enable() first."
500
+ );
501
+ }
502
+
503
+ const returnAll =
504
+ await permissionInteractiveService.checkOrGrantGetAllKeyRingInfosPermission(
505
+ env,
506
+ origin
507
+ );
508
+ const keyInfos = returnAll
509
+ ? service.getKeyInfos()
510
+ : service.getKeyInfos().filter((k) => k.isSelected);
511
+ const wallets: {
512
+ id: string;
513
+ name: string;
514
+ isSelected: boolean;
515
+ addresses: { [chainId: string]: string };
516
+ }[] = [];
517
+
518
+ for (const keyInfo of keyInfos) {
519
+ const addresses: { [chainId: string]: string } = {};
520
+
521
+ for (const chainIdentifier of permittedChains) {
522
+ try {
523
+ const modularChainInfo =
524
+ chainsService.getModularChainInfoOrThrow(chainIdentifier);
525
+
526
+ if ("cosmos" in modularChainInfo) {
527
+ const key = await keyRingCosmosService.getKey(
528
+ keyInfo.id,
529
+ modularChainInfo.chainId
530
+ );
531
+ const chainInfo = modularChainInfo.cosmos;
532
+ const isEthermintLike = KeyRingService.isEthermintLike(chainInfo);
533
+ const evmInfo = ChainsService.getEVMInfo(chainInfo);
534
+
535
+ if (isEthermintLike || evmInfo !== undefined) {
536
+ addresses[modularChainInfo.chainId] = key.ethereumHexAddress;
537
+ } else {
538
+ addresses[modularChainInfo.chainId] = key.bech32Address;
539
+ }
540
+ } else if ("starknet" in modularChainInfo) {
541
+ const starknetKey = await keyRingStarknetService.getStarknetKey(
542
+ keyInfo.id,
543
+ modularChainInfo.chainId
544
+ );
545
+ addresses[modularChainInfo.chainId] = starknetKey.hexAddress;
546
+ }
547
+ // Bitcoin: skip (out of scope)
548
+ } catch (e) {
549
+ console.log(e);
550
+ continue;
551
+ }
552
+ }
553
+
554
+ wallets.push({
555
+ id: keyInfo.id,
556
+ name: keyInfo.name,
557
+ isSelected: keyInfo.isSelected,
558
+ addresses,
559
+ });
560
+ }
561
+
562
+ return wallets;
563
+ };
564
+ };
565
+
566
+ const handleSwitchAccountMsg: (
567
+ service: KeyRingService,
568
+ permissionService: PermissionService
569
+ ) => InternalHandler<SwitchAccountMsg> = (service, permissionService) => {
570
+ return async (env, msg) => {
571
+ if (msg.id === service.selectedVaultId) {
572
+ return;
573
+ }
574
+
575
+ await service.ensureUnlockInteractive(env);
576
+
577
+ const permittedChains = permissionService.getOriginPermittedChains(
578
+ msg.origin,
579
+ getBasicAccessPermissionType()
580
+ );
581
+ if (permittedChains.length === 0) {
582
+ throw new KeplrError(
583
+ "keyring",
584
+ 510,
585
+ "No permitted chains. Call enable() first."
586
+ );
587
+ }
588
+
589
+ if (
590
+ !permissionService.hasGlobalPermission(
591
+ "get-all-keyring-infos",
592
+ msg.origin
593
+ )
594
+ ) {
595
+ throw new KeplrError(
596
+ "keyring",
597
+ 511,
598
+ "Permission denied. The requested account is not accessible from this origin."
599
+ );
600
+ }
601
+
602
+ await service.switchAccountInteractive(env, msg.id, msg.origin);
603
+ };
604
+ };
@@ -26,11 +26,26 @@ import {
26
26
  ExportKeyRingVaultsMsg,
27
27
  SearchKeyRingsMsg,
28
28
  AppendLedgerExtendedKeysMsg,
29
+ GetAllWalletsMsg,
30
+ SwitchAccountMsg,
29
31
  } from "./messages";
30
32
  import { ROUTE } from "./constants";
31
33
  import { getHandler } from "./handler";
34
+ import type { PermissionService } from "../permission/service";
35
+ import type { PermissionInteractiveService } from "../permission-interactive/service";
36
+ import type { ChainsService } from "../chains/service";
37
+ import type { KeyRingCosmosService } from "../keyring-cosmos/service";
38
+ import type { KeyRingStarknetService } from "../keyring-starknet/service";
32
39
 
33
- export function init(router: Router, service: KeyRingService): void {
40
+ export function init(
41
+ router: Router,
42
+ service: KeyRingService,
43
+ permissionService: PermissionService,
44
+ permissionInteractiveService: PermissionInteractiveService,
45
+ chainsService: ChainsService,
46
+ keyRingCosmosService: KeyRingCosmosService,
47
+ keyRingStarknetService: KeyRingStarknetService
48
+ ): void {
34
49
  router.registerMessage(GetIsLockedMsg);
35
50
  router.registerMessage(GetKeyRingStatusMsg);
36
51
  router.registerMessage(GetKeyRingStatusOnlyMsg);
@@ -56,6 +71,18 @@ export function init(router: Router, service: KeyRingService): void {
56
71
  router.registerMessage(ShowSensitiveLegacyKeyRingDataMsg);
57
72
  router.registerMessage(ExportKeyRingVaultsMsg);
58
73
  router.registerMessage(SearchKeyRingsMsg);
74
+ router.registerMessage(GetAllWalletsMsg);
75
+ router.registerMessage(SwitchAccountMsg);
59
76
 
60
- router.addHandler(ROUTE, getHandler(service));
77
+ router.addHandler(
78
+ ROUTE,
79
+ getHandler(
80
+ service,
81
+ permissionService,
82
+ permissionInteractiveService,
83
+ chainsService,
84
+ keyRingCosmosService,
85
+ keyRingStarknetService
86
+ )
87
+ );
61
88
  }
@@ -781,3 +781,64 @@ export class SearchKeyRingsMsg extends Message<KeyInfo[]> {
781
781
  return SearchKeyRingsMsg.type();
782
782
  }
783
783
  }
784
+
785
+ export class GetAllWalletsMsg extends Message<
786
+ {
787
+ id: string;
788
+ name: string;
789
+ isSelected: boolean;
790
+ addresses: { [chainId: string]: string };
791
+ }[]
792
+ > {
793
+ public static type() {
794
+ return "get-all-wallets";
795
+ }
796
+
797
+ constructor() {
798
+ super();
799
+ }
800
+
801
+ validateBasic(): void {
802
+ // noop
803
+ }
804
+
805
+ override approveExternal(): boolean {
806
+ return true;
807
+ }
808
+
809
+ route(): string {
810
+ return ROUTE;
811
+ }
812
+
813
+ type(): string {
814
+ return GetAllWalletsMsg.type();
815
+ }
816
+ }
817
+
818
+ export class SwitchAccountMsg extends Message<void> {
819
+ public static type() {
820
+ return "switch-account";
821
+ }
822
+
823
+ constructor(public readonly id: string) {
824
+ super();
825
+ }
826
+
827
+ validateBasic(): void {
828
+ if (!this.id) {
829
+ throw new Error("id not set");
830
+ }
831
+ }
832
+
833
+ override approveExternal(): boolean {
834
+ return true;
835
+ }
836
+
837
+ route(): string {
838
+ return ROUTE;
839
+ }
840
+
841
+ type(): string {
842
+ return SwitchAccountMsg.type();
843
+ }
844
+ }
@@ -1067,6 +1067,37 @@ export class KeyRingService {
1067
1067
  );
1068
1068
  }
1069
1069
 
1070
+ async switchAccountInteractive(
1071
+ env: Env,
1072
+ vaultId: string,
1073
+ origin: string
1074
+ ): Promise<void> {
1075
+ if (this.vaultService.isLocked) {
1076
+ throw new Error("KeyRing is locked");
1077
+ }
1078
+
1079
+ const vault = this.vaultService.getVault("keyRing", vaultId);
1080
+ if (!vault) {
1081
+ throw new Error("Vault is null");
1082
+ }
1083
+
1084
+ const targetName = this.getKeyRingName(vaultId);
1085
+
1086
+ await this.interactionService.waitApproveV2(
1087
+ env,
1088
+ "/switch-account",
1089
+ "switch-account",
1090
+ {
1091
+ vaultId,
1092
+ name: targetName,
1093
+ origin,
1094
+ },
1095
+ () => {
1096
+ this.selectKeyRing(vaultId);
1097
+ }
1098
+ );
1099
+ }
1100
+
1070
1101
  async deleteKeyRing(vaultId: string, password: string) {
1071
1102
  if (this.vaultService.isLocked) {
1072
1103
  throw new Error("KeyRing is locked");
@@ -14,7 +14,6 @@ import {
14
14
  GENESIS_HASH_TO_CHAIN_TYPE,
15
15
  CHAIN_TYPE_TO_GENESIS_HASH,
16
16
  SignPsbtOptions,
17
- Inscription,
18
17
  } from "@keplr-wallet/types";
19
18
  import { Env, KeplrError, WEBPAGE_PORT } from "@keplr-wallet/router";
20
19
  import { Psbt, address } from "bitcoinjs-lib";
@@ -32,6 +31,10 @@ import {
32
31
  signet,
33
32
  testnet,
34
33
  } from "./constants";
34
+ import {
35
+ adaptUniSatInscriptionDataResponse,
36
+ UniSatInscriptionDataResponse,
37
+ } from "./unisat-inscriptions";
35
38
  import { AnalyticsService } from "../analytics";
36
39
  import { KVStore } from "@keplr-wallet/common";
37
40
  import { action, autorun, makeObservable, observable, runInAction } from "mobx";
@@ -738,50 +741,28 @@ export class KeyRingBitcoinService {
738
741
  const bitcoinKey = await this.getBitcoinKeySelected(currentChainId);
739
742
 
740
743
  const network = this.getNetworkConfig(currentChainId).id;
744
+
745
+ if (network !== Network.MAINNET && network !== Network.LIVENET) {
746
+ return {
747
+ total: 0,
748
+ list: [],
749
+ };
750
+ }
751
+
741
752
  const apiUrl = getBitcoinInscriptionsApiUrl(network);
742
753
 
743
754
  const params = new URLSearchParams();
744
- params.append("sort_by", "inscr_num");
745
- params.append("order", "desc");
746
- params.append("exclude_brc20", "false");
747
- params.append("address", bitcoinKey.address);
748
- params.append("offset", offset?.toString() ?? "0");
749
- params.append("count", limit?.toString() ?? "20");
755
+ params.append("cursor", offset?.toString() ?? "0");
756
+ params.append("size", limit?.toString() ?? "20");
750
757
 
751
758
  // execute API request through rate limiter to avoid rate limit from the server
752
759
  const res = await bitcoinInscriptionsRateLimiter.add(async () => {
753
- return await simpleFetch<{
754
- data: Array<{
755
- inscription_name?: string;
756
- inscription_id: string;
757
- inscription_number: number;
758
- parent_ids: string[];
759
- output_value: number;
760
- genesis_block_hash: string;
761
- genesis_ts: string;
762
- genesis_height: number;
763
- metadata: any;
764
- mime_type?: string;
765
- owner_wallet_addr: string;
766
- last_sale_price?: number;
767
- slug?: string;
768
- collection_name?: string;
769
- satpoint: string;
770
- last_transfer_block_height?: number;
771
- content_url: string;
772
- bis_url: string;
773
- render_url?: string;
774
- bitmap_number?: number;
775
- delegate?: {
776
- delegate_id: string;
777
- render_url?: string;
778
- mime_type?: string;
779
- content_url: string;
780
- bis_url: string;
781
- };
782
- }>;
783
- block_height: number;
784
- }>(`${apiUrl}/wallet/inscriptions?${params.toString()}`);
760
+ return await simpleFetch<UniSatInscriptionDataResponse>(
761
+ `${apiUrl}/unisat`,
762
+ `/v1/indexer/address/${
763
+ bitcoinKey.address
764
+ }/inscription-data?${params.toString()}`
765
+ );
785
766
  });
786
767
 
787
768
  if (res.status === 429) {
@@ -792,44 +773,7 @@ export class KeyRingBitcoinService {
792
773
  throw new Error("Failed to get inscriptions");
793
774
  }
794
775
 
795
- const list: Inscription[] = [];
796
-
797
- for (const item of res.data.data) {
798
- const {
799
- inscription_id,
800
- inscription_number,
801
- owner_wallet_addr,
802
- mime_type,
803
- satpoint,
804
- output_value,
805
- genesis_height,
806
- content_url,
807
- render_url,
808
- } = item;
809
-
810
- const [txid, outputIndex, satIndex] = satpoint.split(":");
811
-
812
- list.push({
813
- id: inscription_id,
814
- inscriptionId: inscription_id,
815
- content: content_url,
816
- number: inscription_number,
817
- address: owner_wallet_addr,
818
- contentType: mime_type ?? "",
819
- output: `${txid}:${outputIndex}`,
820
- location: satpoint,
821
- genesisTransaction: txid,
822
- height: genesis_height,
823
- preview: render_url ?? "",
824
- outputValue: output_value,
825
- offset: parseInt(satIndex, 10),
826
- });
827
- }
828
-
829
- return {
830
- total: list.length,
831
- list,
832
- };
776
+ return adaptUniSatInscriptionDataResponse(network, res.data);
833
777
  }
834
778
 
835
779
  async sendBitcoin(
@@ -0,0 +1,78 @@
1
+ import { Inscription, Network } from "@keplr-wallet/types";
2
+
3
+ export interface UniSatInscriptionDataItem {
4
+ utxo: {
5
+ txid: string;
6
+ vout: number;
7
+ satoshi: number;
8
+ address: string;
9
+ };
10
+ address: string;
11
+ offset: number;
12
+ inscriptionNumber: number;
13
+ inscriptionId: string;
14
+ contentType?: string;
15
+ height: number;
16
+ }
17
+
18
+ export interface UniSatInscriptionDataResponse {
19
+ code: number;
20
+ msg?: string;
21
+ data: {
22
+ cursor: number;
23
+ total: number;
24
+ totalConfirmed?: number;
25
+ totalUnconfirmed?: number;
26
+ totalUnconfirmedSpend?: number;
27
+ inscription: UniSatInscriptionDataItem[];
28
+ };
29
+ }
30
+
31
+ export const getOrdinalsExplorerBaseUrl = (network: Network): string => {
32
+ switch (network) {
33
+ case Network.LIVENET:
34
+ case Network.MAINNET:
35
+ return "https://ordinals.com";
36
+ case Network.TESTNET:
37
+ case Network.SIGNET:
38
+ return "";
39
+ }
40
+ };
41
+
42
+ export const adaptUniSatInscriptionDataResponse = (
43
+ network: Network,
44
+ response: UniSatInscriptionDataResponse
45
+ ): { total: number; list: Inscription[] } => {
46
+ const explorerBaseUrl = getOrdinalsExplorerBaseUrl(network);
47
+
48
+ return {
49
+ total: response.data.total,
50
+ list: response.data.inscription.map((item) => {
51
+ const {
52
+ inscriptionId,
53
+ inscriptionNumber,
54
+ address,
55
+ contentType,
56
+ offset,
57
+ height,
58
+ utxo,
59
+ } = item;
60
+
61
+ return {
62
+ id: inscriptionId,
63
+ inscriptionId,
64
+ content: `${explorerBaseUrl}/content/${inscriptionId}`,
65
+ number: inscriptionNumber,
66
+ address,
67
+ contentType: contentType ?? "",
68
+ output: `${utxo.txid}:${utxo.vout}`,
69
+ location: `${utxo.txid}:${utxo.vout}:${offset}`,
70
+ genesisTransaction: utxo.txid,
71
+ height,
72
+ preview: `${explorerBaseUrl}/preview/${inscriptionId}`,
73
+ outputValue: utxo.satoshi,
74
+ offset,
75
+ };
76
+ }),
77
+ };
78
+ };
@@ -240,4 +240,43 @@ export class PermissionInteractiveService {
240
240
  origin
241
241
  );
242
242
  }
243
+
244
+ async checkOrGrantGetAllKeyRingInfosPermission(
245
+ env: Env,
246
+ origin: string
247
+ ): Promise<boolean> {
248
+ await this.keyRingService.ensureUnlockInteractive(env);
249
+
250
+ if (
251
+ this.permissionService.hasGlobalPermission(
252
+ "get-all-keyring-infos",
253
+ origin
254
+ )
255
+ ) {
256
+ return true;
257
+ }
258
+
259
+ if (
260
+ this.permissionService.hasGlobalPermission(
261
+ "get-current-keyring-info",
262
+ origin
263
+ )
264
+ ) {
265
+ return false;
266
+ }
267
+
268
+ try {
269
+ await this.permissionService.checkOrGrantGlobalPermission(
270
+ env,
271
+ "get-all-keyring-infos",
272
+ origin
273
+ );
274
+ return true;
275
+ } catch {
276
+ this.permissionService.addGlobalPermission("get-current-keyring-info", [
277
+ origin,
278
+ ]);
279
+ return false;
280
+ }
281
+ }
243
282
  }