@keplr-wallet/provider 0.12.212 → 0.12.213

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/src/core.ts CHANGED
@@ -22,6 +22,12 @@ import {
22
22
  IEthereumProvider,
23
23
  IStarknetProvider,
24
24
  WalletEvents,
25
+ SupportedPaymentType,
26
+ IBitcoinProvider,
27
+ Network as BitcoinNetwork,
28
+ BitcoinSignMessageType,
29
+ ChainType as BitcoinChainType,
30
+ SignPsbtOptions,
25
31
  } from "@keplr-wallet/types";
26
32
  import {
27
33
  BACKGROUND_PORT,
@@ -1136,6 +1142,104 @@ export class Keplr implements IKeplr, KeplrCoreTypes {
1136
1142
  });
1137
1143
  }
1138
1144
 
1145
+ async getBitcoinKey(chainId: string): Promise<{
1146
+ name: string;
1147
+ pubKey: Uint8Array;
1148
+ address: string;
1149
+ paymentType: SupportedPaymentType;
1150
+ isNanoLedger: boolean;
1151
+ }> {
1152
+ return new Promise((resolve, reject) => {
1153
+ let f = false;
1154
+ sendSimpleMessage(
1155
+ this.requester,
1156
+ BACKGROUND_PORT,
1157
+ "keyring-bitcoin",
1158
+ "get-bitcoin-key",
1159
+ {
1160
+ chainId,
1161
+ }
1162
+ )
1163
+ .then(resolve)
1164
+ .catch(reject)
1165
+ .finally(() => (f = true));
1166
+
1167
+ setTimeout(() => {
1168
+ if (!f) {
1169
+ this.protectedTryOpenSidePanelIfEnabled();
1170
+ }
1171
+ }, 100);
1172
+ });
1173
+ }
1174
+
1175
+ async getBitcoinKeysSettled(chainIds: string[]): Promise<
1176
+ SettledResponses<{
1177
+ name: string;
1178
+ pubKey: Uint8Array;
1179
+ address: string;
1180
+ paymentType: SupportedPaymentType;
1181
+ isNanoLedger: boolean;
1182
+ }>
1183
+ > {
1184
+ return new Promise((resolve, reject) => {
1185
+ let f = false;
1186
+ sendSimpleMessage(
1187
+ this.requester,
1188
+ BACKGROUND_PORT,
1189
+ "keyring-bitcoin",
1190
+ "get-bitcoin-keys-settled",
1191
+ {
1192
+ chainIds,
1193
+ }
1194
+ )
1195
+ .then(resolve)
1196
+ .catch(reject)
1197
+ .finally(() => (f = true));
1198
+
1199
+ setTimeout(() => {
1200
+ if (!f) {
1201
+ this.protectedTryOpenSidePanelIfEnabled();
1202
+ }
1203
+ }, 100);
1204
+ });
1205
+ }
1206
+
1207
+ async signPsbt(
1208
+ chainId: string,
1209
+ psbtHex: string,
1210
+ options?: SignPsbtOptions
1211
+ ): Promise<string> {
1212
+ return await sendSimpleMessage(
1213
+ this.requester,
1214
+ BACKGROUND_PORT,
1215
+ "keyring-bitcoin",
1216
+ "request-sign-bitcoin-psbt",
1217
+ {
1218
+ chainId,
1219
+ psbtHex,
1220
+ options,
1221
+ }
1222
+ );
1223
+ }
1224
+
1225
+ async signPsbts(
1226
+ chainId: string,
1227
+ psbtsHexes: string[],
1228
+ options?: SignPsbtOptions
1229
+ ): Promise<string[]> {
1230
+ return await sendSimpleMessage(
1231
+ this.requester,
1232
+ BACKGROUND_PORT,
1233
+ "keyring-bitcoin",
1234
+ "request-sign-bitcoin-psbts",
1235
+ {
1236
+ chainId,
1237
+ psbtsHexes,
1238
+ options,
1239
+ }
1240
+ );
1241
+ }
1242
+
1139
1243
  // IMPORTANT: protected로 시작하는 method는 InjectedKeplr.startProxy()에서 injected 쪽에서 event system으로도 호출할 수 없도록 막혀있다.
1140
1244
  // protected로 시작하지 않는 method는 injected keplr에 없어도 event system을 통하면 호출 할 수 있다.
1141
1245
  // 이를 막기 위해서 method 이름을 protected로 시작하게 한다.
@@ -1451,6 +1555,8 @@ export class Keplr implements IKeplr, KeplrCoreTypes {
1451
1555
  public readonly ethereum = new EthereumProvider(this, this.requester);
1452
1556
 
1453
1557
  public readonly starknet = new StarknetProvider(this, this.requester);
1558
+
1559
+ public readonly bitcoin = new BitcoinProvider(this, this.requester);
1454
1560
  }
1455
1561
 
1456
1562
  // IMPORTANT: 사이드 패널을 열어야하는 JSON-RPC 메소드들이 생길 때마다 여기에 추가해야한다.
@@ -1690,3 +1796,208 @@ class StarknetProvider implements IStarknetProvider {
1690
1796
  throw new Error("Method not implemented.");
1691
1797
  }
1692
1798
  }
1799
+
1800
+ const sidePanelOpenNeededBitcoinMethods = [
1801
+ "switchNetwork",
1802
+ "switchChain",
1803
+ "signMessage",
1804
+ "sendBitcoin",
1805
+ "pushTx",
1806
+ "signPsbt",
1807
+ "signPsbts",
1808
+ ];
1809
+
1810
+ class BitcoinProvider extends EventEmitter implements IBitcoinProvider {
1811
+ constructor(
1812
+ protected readonly keplr: Keplr,
1813
+ protected readonly requester: MessageRequester
1814
+ ) {
1815
+ super();
1816
+ }
1817
+
1818
+ protected async protectedEnableAccess(): Promise<void> {
1819
+ return new Promise((resolve, reject) => {
1820
+ let f = false;
1821
+
1822
+ sendSimpleMessage(
1823
+ this.requester,
1824
+ BACKGROUND_PORT,
1825
+ "permission-interactive",
1826
+ "enable-access-for-bitcoin",
1827
+ {}
1828
+ )
1829
+ .then(resolve)
1830
+ .catch(reject)
1831
+ .finally(() => (f = true));
1832
+
1833
+ setTimeout(() => {
1834
+ if (!f) {
1835
+ this.keplr.protectedTryOpenSidePanelIfEnabled();
1836
+ }
1837
+ }, 100);
1838
+ });
1839
+ }
1840
+
1841
+ protected async protectedRequestMethod<T>({
1842
+ method,
1843
+ params,
1844
+ }: {
1845
+ method: string;
1846
+ params: unknown[];
1847
+ }): Promise<T> {
1848
+ if (method !== "getAccounts") {
1849
+ await this.protectedEnableAccess();
1850
+ }
1851
+
1852
+ return new Promise((resolve, reject) => {
1853
+ let f = false;
1854
+ sendSimpleMessage(
1855
+ this.requester,
1856
+ BACKGROUND_PORT,
1857
+ "keyring-bitcoin",
1858
+ "request-method-to-bitcoin",
1859
+ {
1860
+ method,
1861
+ params,
1862
+ }
1863
+ )
1864
+ .then(resolve)
1865
+ .catch(reject)
1866
+ .finally(() => (f = true));
1867
+
1868
+ setTimeout(() => {
1869
+ if (!f && sidePanelOpenNeededBitcoinMethods.includes(method)) {
1870
+ this.keplr.protectedTryOpenSidePanelIfEnabled();
1871
+ }
1872
+ }, 100);
1873
+ });
1874
+ }
1875
+
1876
+ async getAccounts(): Promise<string[]> {
1877
+ return this.protectedRequestMethod({
1878
+ method: "getAccounts",
1879
+ params: [],
1880
+ });
1881
+ }
1882
+
1883
+ async requestAccounts(): Promise<string[]> {
1884
+ return this.protectedRequestMethod({
1885
+ method: "requestAccounts",
1886
+ params: [],
1887
+ });
1888
+ }
1889
+
1890
+ async disconnect(): Promise<void> {
1891
+ return this.protectedRequestMethod({
1892
+ method: "disconnect",
1893
+ params: [],
1894
+ });
1895
+ }
1896
+
1897
+ async getNetwork(): Promise<BitcoinNetwork> {
1898
+ return this.protectedRequestMethod({
1899
+ method: "getNetwork",
1900
+ params: [],
1901
+ });
1902
+ }
1903
+
1904
+ async switchNetwork(network: BitcoinNetwork): Promise<BitcoinNetwork> {
1905
+ return this.protectedRequestMethod({
1906
+ method: "switchNetwork",
1907
+ params: [network],
1908
+ });
1909
+ }
1910
+
1911
+ async getChain(): Promise<{
1912
+ enum: BitcoinChainType;
1913
+ name: string;
1914
+ network: BitcoinNetwork;
1915
+ }> {
1916
+ return this.protectedRequestMethod({
1917
+ method: "getChain",
1918
+ params: [],
1919
+ });
1920
+ }
1921
+
1922
+ async switchChain(chain: BitcoinChainType): Promise<BitcoinChainType> {
1923
+ return this.protectedRequestMethod({
1924
+ method: "switchChain",
1925
+ params: [chain],
1926
+ });
1927
+ }
1928
+
1929
+ async getPublicKey(): Promise<string> {
1930
+ return this.protectedRequestMethod({
1931
+ method: "getPublicKey",
1932
+ params: [],
1933
+ });
1934
+ }
1935
+
1936
+ async getBalance(): Promise<{
1937
+ confirmed: number;
1938
+ unconfirmed: number;
1939
+ total: number;
1940
+ }> {
1941
+ return this.protectedRequestMethod({
1942
+ method: "getBalance",
1943
+ params: [],
1944
+ });
1945
+ }
1946
+
1947
+ async getInscriptions(): Promise<string[]> {
1948
+ return this.protectedRequestMethod({
1949
+ method: "getInscriptions",
1950
+ params: [],
1951
+ });
1952
+ }
1953
+
1954
+ async signMessage(
1955
+ message: string,
1956
+ type?: BitcoinSignMessageType
1957
+ ): Promise<string> {
1958
+ // Default to ECDSA if type is not provided
1959
+ return this.protectedRequestMethod({
1960
+ method: "signMessage",
1961
+ params: [message, type ?? BitcoinSignMessageType.ECDSA],
1962
+ });
1963
+ }
1964
+
1965
+ async sendBitcoin(to: string, amount: number): Promise<string> {
1966
+ return this.protectedRequestMethod({
1967
+ method: "sendBitcoin",
1968
+ params: [to, amount],
1969
+ });
1970
+ }
1971
+
1972
+ async pushTx(rawTxHex: string): Promise<string> {
1973
+ return this.protectedRequestMethod({
1974
+ method: "pushTx",
1975
+ params: [rawTxHex],
1976
+ });
1977
+ }
1978
+
1979
+ async signPsbt(psbtHex: string, options?: SignPsbtOptions): Promise<string> {
1980
+ return this.protectedRequestMethod({
1981
+ method: "signPsbt",
1982
+ params: [psbtHex, options],
1983
+ });
1984
+ }
1985
+
1986
+ async signPsbts(
1987
+ psbtsHexes: string[],
1988
+ options?: SignPsbtOptions
1989
+ ): Promise<string[]> {
1990
+ return this.protectedRequestMethod({
1991
+ method: "signPsbts",
1992
+ params: [psbtsHexes, options],
1993
+ });
1994
+ }
1995
+
1996
+ async getAddress(): Promise<string> {
1997
+ throw new Error("This method should not be called");
1998
+ }
1999
+
2000
+ async connectWallet(): Promise<string[]> {
2001
+ throw new Error("This method should not be called");
2002
+ }
2003
+ }
package/src/inject.ts CHANGED
@@ -28,6 +28,12 @@ import {
28
28
  WalletEvents,
29
29
  AccountChangeEventHandler,
30
30
  NetworkChangeEventHandler,
31
+ SupportedPaymentType,
32
+ IBitcoinProvider,
33
+ Network as BitcoinNetwork,
34
+ BitcoinSignMessageType,
35
+ ChainType as BitcoinChainType,
36
+ SignPsbtOptions,
31
37
  } from "@keplr-wallet/types";
32
38
  import {
33
39
  Result,
@@ -55,6 +61,7 @@ export interface ProxyRequest {
55
61
  args: any[];
56
62
  ethereumProviderMethod?: keyof IEthereumProvider;
57
63
  starknetProviderMethod?: keyof IStarknetProvider;
64
+ bitcoinProviderMethod?: keyof IBitcoinProvider;
58
65
  }
59
66
 
60
67
  export interface ProxyRequestResponse {
@@ -105,6 +112,8 @@ export function injectKeplrToWindow(keplr: IKeplr): void {
105
112
  );
106
113
 
107
114
  defineUnwritablePropertyIfPossible(window, "starknet_keplr", keplr.starknet);
115
+
116
+ defineUnwritablePropertyIfPossible(window, "bitcoin_keplr", keplr.bitcoin);
108
117
  }
109
118
 
110
119
  /**
@@ -169,6 +178,7 @@ export class InjectedKeplr implements IKeplr, KeplrCoreTypes {
169
178
  !keplr[message.method] ||
170
179
  (message.method !== "ethereum" &&
171
180
  message.method !== "starknet" &&
181
+ message.method !== "bitcoin" &&
172
182
  typeof keplr[message.method] !== "function")
173
183
  ) {
174
184
  throw new Error(`Invalid method: ${message.method}`);
@@ -396,6 +406,32 @@ export class InjectedKeplr implements IKeplr, KeplrCoreTypes {
396
406
  );
397
407
  }
398
408
 
409
+ if (method === "bitcoin") {
410
+ const bitcoinProviderMethod = message.bitcoinProviderMethod;
411
+
412
+ if (bitcoinProviderMethod?.startsWith("protected")) {
413
+ throw new Error("Rejected");
414
+ }
415
+
416
+ if (
417
+ bitcoinProviderMethod === undefined ||
418
+ typeof keplr.bitcoin?.[bitcoinProviderMethod] !== "function"
419
+ ) {
420
+ throw new Error(
421
+ `${message.bitcoinProviderMethod} is not function or invalid Bitcoin provider method`
422
+ );
423
+ }
424
+
425
+ const messageArgs = JSONUint8Array.unwrap(message.args);
426
+ return await keplr.bitcoin[bitcoinProviderMethod](
427
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
428
+ // @ts-ignore
429
+ ...(typeof messageArgs === "string"
430
+ ? JSON.parse(messageArgs)
431
+ : messageArgs)
432
+ );
433
+ }
434
+
399
435
  return await keplr[method](
400
436
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
401
437
  // @ts-ignore
@@ -1044,6 +1080,48 @@ export class InjectedKeplr implements IKeplr, KeplrCoreTypes {
1044
1080
  );
1045
1081
  }
1046
1082
 
1083
+ async getBitcoinKey(chainId: string): Promise<{
1084
+ name: string;
1085
+ pubKey: Uint8Array;
1086
+ address: string;
1087
+ paymentType: SupportedPaymentType;
1088
+ isNanoLedger: boolean;
1089
+ }> {
1090
+ return await this.requestMethod("getBitcoinKey", [chainId]);
1091
+ }
1092
+
1093
+ async getBitcoinKeysSettled(chainIds: string[]): Promise<
1094
+ SettledResponses<{
1095
+ name: string;
1096
+ pubKey: Uint8Array;
1097
+ address: string;
1098
+ paymentType: SupportedPaymentType;
1099
+ isNanoLedger: boolean;
1100
+ }>
1101
+ > {
1102
+ return await this.requestMethod("getBitcoinKeysSettled", [chainIds]);
1103
+ }
1104
+
1105
+ async signPsbt(
1106
+ chainId: string,
1107
+ psbtHex: string,
1108
+ options?: SignPsbtOptions
1109
+ ): Promise<string> {
1110
+ return await this.requestMethod("signPsbt", [chainId, psbtHex, options]);
1111
+ }
1112
+
1113
+ async signPsbts(
1114
+ chainId: string,
1115
+ psbtsHexes: string[],
1116
+ options?: SignPsbtOptions
1117
+ ): Promise<string[]> {
1118
+ return await this.requestMethod("signPsbts", [
1119
+ chainId,
1120
+ psbtsHexes,
1121
+ options,
1122
+ ]);
1123
+ }
1124
+
1047
1125
  public readonly ethereum = new EthereumProvider(
1048
1126
  this.metaId,
1049
1127
  () => this,
@@ -1053,6 +1131,12 @@ export class InjectedKeplr implements IKeplr, KeplrCoreTypes {
1053
1131
  );
1054
1132
 
1055
1133
  public readonly starknet = this.generateStarknetProvider();
1134
+
1135
+ public readonly bitcoin = new BitcoinProvider(
1136
+ () => this,
1137
+ this.eventListener,
1138
+ this.parseMessage
1139
+ );
1056
1140
  }
1057
1141
 
1058
1142
  class EthereumProvider extends EventEmitter implements IEthereumProvider {
@@ -1598,3 +1682,165 @@ class StarknetProvider implements IStarknetProvider {
1598
1682
  }
1599
1683
  }
1600
1684
  }
1685
+
1686
+ export class BitcoinProvider extends EventEmitter implements IBitcoinProvider {
1687
+ constructor(
1688
+ protected readonly _injectedKeplr: () => InjectedKeplr,
1689
+ protected readonly _eventListener: {
1690
+ addMessageListener: (fn: (e: any) => void) => void;
1691
+ removeMessageListener: (fn: (e: any) => void) => void;
1692
+ postMessage: (message: any) => void;
1693
+ } = {
1694
+ addMessageListener: (fn: (e: any) => void) =>
1695
+ window.addEventListener("message", fn),
1696
+ removeMessageListener: (fn: (e: any) => void) =>
1697
+ window.removeEventListener("message", fn),
1698
+ postMessage: (message) =>
1699
+ window.postMessage(message, window.location.origin),
1700
+ },
1701
+
1702
+ protected readonly _parseMessage?: (message: any) => any
1703
+ ) {
1704
+ super();
1705
+ }
1706
+
1707
+ protected async _requestMethod<T = unknown>(
1708
+ method: keyof IBitcoinProvider,
1709
+ args: Record<string, any>
1710
+ ): Promise<T> {
1711
+ const bytes = new Uint8Array(8);
1712
+ const id: string = Array.from(crypto.getRandomValues(bytes))
1713
+ .map((value) => {
1714
+ return value.toString(16);
1715
+ })
1716
+ .join("");
1717
+
1718
+ const proxyMessage: ProxyRequest = {
1719
+ type: "proxy-request",
1720
+ id,
1721
+ method: "bitcoin",
1722
+ args: JSONUint8Array.wrap(args),
1723
+ bitcoinProviderMethod: method,
1724
+ };
1725
+
1726
+ return new Promise<T>((resolve, reject) => {
1727
+ const receiveResponse = (e: any) => {
1728
+ const proxyResponse: ProxyRequestResponse = this._parseMessage
1729
+ ? this._parseMessage(e.data)
1730
+ : e.data;
1731
+
1732
+ if (!proxyResponse || proxyResponse.type !== "proxy-request-response") {
1733
+ return;
1734
+ }
1735
+
1736
+ if (proxyResponse.id !== id) {
1737
+ return;
1738
+ }
1739
+
1740
+ this._eventListener.removeMessageListener(receiveResponse);
1741
+
1742
+ const result = JSONUint8Array.unwrap(proxyResponse.result);
1743
+
1744
+ if (!result) {
1745
+ reject(new Error("Result is null"));
1746
+ return;
1747
+ }
1748
+
1749
+ // TODO: Handle error correctly
1750
+ if (result.error) {
1751
+ reject(new Error(result.error));
1752
+ return;
1753
+ }
1754
+
1755
+ resolve(result.return as T);
1756
+ };
1757
+
1758
+ this._eventListener.addMessageListener(receiveResponse);
1759
+
1760
+ this._eventListener.postMessage(proxyMessage);
1761
+ });
1762
+ }
1763
+
1764
+ getAccounts(): Promise<string[]> {
1765
+ return this._requestMethod("getAccounts", []);
1766
+ }
1767
+
1768
+ async requestAccounts(): Promise<string[]> {
1769
+ return this._requestMethod("requestAccounts", []);
1770
+ }
1771
+
1772
+ async disconnect(): Promise<void> {
1773
+ return this._requestMethod("disconnect", []);
1774
+ }
1775
+
1776
+ async getNetwork(): Promise<BitcoinNetwork> {
1777
+ return this._requestMethod("getNetwork", []);
1778
+ }
1779
+
1780
+ async switchNetwork(network: BitcoinNetwork): Promise<BitcoinNetwork> {
1781
+ return this._requestMethod("switchNetwork", [network]);
1782
+ }
1783
+
1784
+ async getChain(): Promise<{
1785
+ enum: BitcoinChainType;
1786
+ name: string;
1787
+ network: BitcoinNetwork;
1788
+ }> {
1789
+ return this._requestMethod("getChain", []);
1790
+ }
1791
+
1792
+ async switchChain(chain: BitcoinChainType): Promise<BitcoinChainType> {
1793
+ return this._requestMethod("switchChain", [chain]);
1794
+ }
1795
+
1796
+ async getPublicKey(): Promise<string> {
1797
+ return this._requestMethod("getPublicKey", []);
1798
+ }
1799
+
1800
+ async getBalance(): Promise<{
1801
+ confirmed: number;
1802
+ unconfirmed: number;
1803
+ total: number;
1804
+ }> {
1805
+ return this._requestMethod("getBalance", []);
1806
+ }
1807
+
1808
+ async getInscriptions(): Promise<string[]> {
1809
+ return this._requestMethod("getInscriptions", []);
1810
+ }
1811
+
1812
+ async signMessage(
1813
+ message: string,
1814
+ type?: BitcoinSignMessageType
1815
+ ): Promise<string> {
1816
+ return this._requestMethod("signMessage", [message, type]);
1817
+ }
1818
+
1819
+ async sendBitcoin(to: string, amount: number): Promise<string> {
1820
+ return this._requestMethod("sendBitcoin", [to, amount]);
1821
+ }
1822
+
1823
+ async pushTx(rawTxHex: string): Promise<string> {
1824
+ return this._requestMethod("pushTx", [rawTxHex]);
1825
+ }
1826
+
1827
+ async signPsbt(psbtHex: string, options?: SignPsbtOptions): Promise<string> {
1828
+ return this._requestMethod("signPsbt", [psbtHex, options]);
1829
+ }
1830
+
1831
+ async signPsbts(
1832
+ psbtsHexes: string[],
1833
+ options?: SignPsbtOptions
1834
+ ): Promise<string[]> {
1835
+ return this._requestMethod("signPsbts", [psbtsHexes, options]);
1836
+ }
1837
+
1838
+ async getAddress(): Promise<string> {
1839
+ const accounts = await this.getAccounts();
1840
+ return accounts[0];
1841
+ }
1842
+
1843
+ async connectWallet(): Promise<string[]> {
1844
+ return this.requestAccounts();
1845
+ }
1846
+ }