@hot-labs/kit 1.0.56 → 1.0.57
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/build/core/chains.js +1 -1
- package/build/core/chains.js.map +1 -1
- package/build/evm/connector.d.ts +1 -3
- package/build/evm/connector.js +3 -11
- package/build/evm/connector.js.map +1 -1
- package/build/evm/injected.js +3 -3
- package/build/evm/injected.js.map +1 -1
- package/build/hot-wallet/iframe.d.ts +36 -3
- package/build/hot-wallet/iframe.js +134 -27
- package/build/hot-wallet/iframe.js.map +1 -1
- package/build/hot-wallet/proxy.d.ts +8 -0
- package/build/hot-wallet/proxy.js +45 -0
- package/build/hot-wallet/proxy.js.map +1 -0
- package/build/solana/connector.js +2 -2
- package/build/solana/connector.js.map +1 -1
- package/build/solana/injected/account.js +1 -2
- package/build/solana/injected/account.js.map +1 -1
- package/build/solana/injected/index.js +16 -19
- package/build/solana/injected/index.js.map +1 -1
- package/build/stellar/connector.d.ts +10 -4
- package/build/stellar/connector.js +39 -28
- package/build/stellar/connector.js.map +1 -1
- package/build/stellar/freigher.d.ts +46 -0
- package/build/stellar/freigher.js +110 -0
- package/build/stellar/freigher.js.map +1 -0
- package/build/stellar/{injected.d.ts → hotWallet.d.ts} +1 -6
- package/build/stellar/hotWallet.js +28 -0
- package/build/stellar/hotWallet.js.map +1 -0
- package/build/ton/connector.js +6 -3
- package/build/ton/connector.js.map +1 -1
- package/build/ton/injected.js +6 -6
- package/build/ton/injected.js.map +1 -1
- package/build/ui/Popup.js +1 -1
- package/build/ui/Popup.js.map +1 -1
- package/build/ui/styles.js +4 -3
- package/build/ui/styles.js.map +1 -1
- package/package.json +3 -2
- package/src/core/chains.ts +1 -1
- package/src/evm/connector.ts +4 -12
- package/src/evm/injected.ts +3 -3
- package/src/hot-wallet/iframe.ts +151 -24
- package/src/hot-wallet/proxy.ts +54 -0
- package/src/solana/connector.ts +2 -2
- package/src/solana/injected/account.ts +1 -2
- package/src/solana/injected/index.ts +15 -18
- package/src/stellar/connector.ts +42 -29
- package/src/stellar/freigher.ts +119 -0
- package/src/stellar/{injected.ts → hotWallet.ts} +8 -12
- package/src/ton/connector.ts +14 -10
- package/src/ton/injected.ts +6 -6
- package/src/ui/Popup.tsx +7 -3
- package/src/ui/styles.ts +4 -3
- package/build/stellar/injected.js +0 -32
- package/build/stellar/injected.js.map +0 -1
package/src/evm/injected.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import HOT from "../hot-wallet/iframe";
|
|
2
2
|
|
|
3
3
|
class HotEvmProvider {
|
|
4
4
|
_events = new Map<string, Set<any>>();
|
|
@@ -6,7 +6,7 @@ class HotEvmProvider {
|
|
|
6
6
|
isHotWallet = true;
|
|
7
7
|
isMetaMask = true;
|
|
8
8
|
|
|
9
|
-
request = (request: any) =>
|
|
9
|
+
request = (request: any) => HOT.request("ethereum", request);
|
|
10
10
|
removeListener() {}
|
|
11
11
|
on() {}
|
|
12
12
|
}
|
|
@@ -31,7 +31,7 @@ function announceProvider(provider: HotEvmProvider) {
|
|
|
31
31
|
);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
if (isInjected
|
|
34
|
+
if (HOT.isInjected) {
|
|
35
35
|
const hotProvider = new HotEvmProvider();
|
|
36
36
|
window.addEventListener("eip6963:requestProvider", () => announceProvider(hotProvider));
|
|
37
37
|
announceProvider(hotProvider);
|
package/src/hot-wallet/iframe.ts
CHANGED
|
@@ -1,32 +1,159 @@
|
|
|
1
|
-
|
|
1
|
+
import uuid4 from "uuid4";
|
|
2
|
+
import { createRequest, getResponse } from "./proxy";
|
|
3
|
+
import { base58 } from "@scure/base";
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
hotExtension?: {
|
|
8
|
+
autoRun: boolean;
|
|
9
|
+
request: (method: string, args: any) => any;
|
|
10
|
+
subscribe: (event: string, args: any) => () => void;
|
|
11
|
+
evm: any;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const wait = (timeout: number) => {
|
|
17
|
+
return new Promise<void>((resolve) => setTimeout(resolve, timeout));
|
|
7
18
|
};
|
|
8
19
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const temp_url = URL.createObjectURL(new Blob());
|
|
14
|
-
const uuid = temp_url.toString();
|
|
15
|
-
URL.revokeObjectURL(temp_url);
|
|
16
|
-
return uuid.split(/[:/]/g).pop()!.toLowerCase(); // remove prefixes
|
|
20
|
+
export class RequestFailed extends Error {
|
|
21
|
+
name = "RequestFailed";
|
|
22
|
+
constructor(readonly payload: any) {
|
|
23
|
+
super();
|
|
17
24
|
}
|
|
18
|
-
}
|
|
25
|
+
}
|
|
19
26
|
|
|
20
|
-
export const
|
|
21
|
-
|
|
22
|
-
return
|
|
23
|
-
|
|
24
|
-
if (e.data.id !== id) return;
|
|
25
|
-
window?.removeEventListener("message", handler);
|
|
26
|
-
return e.data.success ? resolve(e.data.payload) : reject(e.data.payload);
|
|
27
|
-
};
|
|
27
|
+
export const getExtension = () => {
|
|
28
|
+
if (typeof window === "undefined") return null;
|
|
29
|
+
return window.hotExtension;
|
|
30
|
+
};
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
let connector: HTMLDivElement | undefined;
|
|
33
|
+
if (typeof window !== "undefined") {
|
|
34
|
+
window.addEventListener("message", (e: any) => {
|
|
35
|
+
if (e.data === "hot-close") {
|
|
36
|
+
connector?.remove();
|
|
37
|
+
connector = undefined;
|
|
38
|
+
}
|
|
31
39
|
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const createIframe = (widget: string) => {
|
|
43
|
+
connector?.remove();
|
|
44
|
+
connector = document.createElement("div");
|
|
45
|
+
|
|
46
|
+
const iframe = document.createElement("iframe");
|
|
47
|
+
connector?.appendChild(iframe);
|
|
48
|
+
|
|
49
|
+
iframe.src = widget;
|
|
50
|
+
iframe.allow = "usb";
|
|
51
|
+
iframe.style.border = "none";
|
|
52
|
+
iframe.style.borderRadius = "16px";
|
|
53
|
+
iframe.style.background = "#fff";
|
|
54
|
+
iframe.style.overflow = "hidden";
|
|
55
|
+
iframe.style.background = "#1D1F20";
|
|
56
|
+
iframe.style.border = "1px solid #2C3034";
|
|
57
|
+
iframe.style.width = "375px";
|
|
58
|
+
iframe.style.height = "560px";
|
|
59
|
+
iframe.onclick = (e) => e.stopPropagation();
|
|
60
|
+
|
|
61
|
+
connector.style.padding = "16px";
|
|
62
|
+
connector.style.zIndex = "100000000000000";
|
|
63
|
+
connector.style.position = "fixed";
|
|
64
|
+
connector.style.display = "flex";
|
|
65
|
+
connector.style.justifyContent = "center";
|
|
66
|
+
connector.style.alignItems = "center";
|
|
67
|
+
connector.style.top = "0";
|
|
68
|
+
connector.style.left = "0";
|
|
69
|
+
connector.style.width = "100%";
|
|
70
|
+
connector.style.height = "100%";
|
|
71
|
+
connector.style.background = "rgba(0, 0, 0, 0.1)";
|
|
72
|
+
connector.style.backdropFilter = "blur(24px)";
|
|
73
|
+
connector.onclick = () => {
|
|
74
|
+
connector?.remove();
|
|
75
|
+
connector = undefined;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
document.body.appendChild(connector);
|
|
79
|
+
return connector;
|
|
32
80
|
};
|
|
81
|
+
|
|
82
|
+
class HOT {
|
|
83
|
+
walletId = "https://t.me/herewalletbot/app";
|
|
84
|
+
ancestorOrigins = ["http://localhost:1234", "https://my.herewallet.app", "https://tgapp-dev.herewallet.app", "https://tgapp.herewallet.app", "https://beta.herewallet.app"];
|
|
85
|
+
|
|
86
|
+
get isInjected() {
|
|
87
|
+
if (typeof window === "undefined") return false;
|
|
88
|
+
if (window.hotExtension != null) return window.hotExtension.autoRun;
|
|
89
|
+
return this.ancestorOrigins.includes(window.location.ancestorOrigins?.[0]);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
openInHotBrowserUrl: string | null = null;
|
|
93
|
+
toggleOpenInHotBrowser(url: string | null) {
|
|
94
|
+
this.openInHotBrowserUrl = url;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
customProvider?: (data: any, chain: number, address?: string | null) => Promise<any>;
|
|
98
|
+
setupEthProvider(provider?: (data: any, chain: number, address?: string | null) => Promise<any>) {
|
|
99
|
+
this.customProvider = provider;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async injectedRequest(method: string, request: any): Promise<any> {
|
|
103
|
+
const id = uuid4();
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
const handler = (e: any) => {
|
|
106
|
+
if (e.data.id !== id) return;
|
|
107
|
+
window?.removeEventListener("message", handler);
|
|
108
|
+
if (e.data.success) return resolve(e.data.payload);
|
|
109
|
+
else return reject(e.data.payload);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
window?.parent.postMessage({ $hot: true, method, request, id }, "*");
|
|
113
|
+
window?.addEventListener("message", handler);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
subscribe(event: string, cb: (e: any) => void) {
|
|
118
|
+
if (!window.hotExtension) return () => {};
|
|
119
|
+
return window.hotExtension.subscribe(event, cb);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async request(method: string, request: any): Promise<any> {
|
|
123
|
+
if (window.hotExtension != null) return window.hotExtension.request(method, request);
|
|
124
|
+
if (this.isInjected) return this.injectedRequest(method, request);
|
|
125
|
+
|
|
126
|
+
const id = uuid4();
|
|
127
|
+
const WebApp: any = (window as any)?.Telegram?.WebApp;
|
|
128
|
+
|
|
129
|
+
const requestId = await createRequest({
|
|
130
|
+
inside: !!this.openInHotBrowserUrl || (method === "ethereum" && this.customProvider == null),
|
|
131
|
+
origin: typeof this.openInHotBrowserUrl === "string" ? this.openInHotBrowserUrl : location.href,
|
|
132
|
+
$hot: true,
|
|
133
|
+
method,
|
|
134
|
+
request,
|
|
135
|
+
id,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const link = `${this.walletId}?startapp=hotconnect-${base58.encode(Buffer.from(requestId, "utf8"))}`;
|
|
139
|
+
if (WebApp) WebApp?.openTelegramLink(link);
|
|
140
|
+
else {
|
|
141
|
+
const origin = `https://hot-labs.org/hot-widget/index.html`;
|
|
142
|
+
createIframe(`${origin}?hotconnect-${base58.encode(Buffer.from(requestId, "utf8"))}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const poolResponse = async () => {
|
|
146
|
+
await wait(3000);
|
|
147
|
+
const data: any = await getResponse(requestId).catch(() => null);
|
|
148
|
+
if (data == null) return await poolResponse();
|
|
149
|
+
if (data.success) return data.payload;
|
|
150
|
+
throw new RequestFailed(data.payload);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const result = await poolResponse();
|
|
154
|
+
connector?.remove();
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export default new HOT();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import uuid4 from "uuid4";
|
|
2
|
+
import { sha1 } from "@noble/hashes/legacy.js";
|
|
3
|
+
import { base58 } from "@scure/base";
|
|
4
|
+
|
|
5
|
+
export const proxyApi = "https://h4n.app";
|
|
6
|
+
|
|
7
|
+
export const getResponse = async (id: string): Promise<object> => {
|
|
8
|
+
const res = await fetch(`${proxyApi}/${id}/response`, {
|
|
9
|
+
headers: { "content-type": "application/json" },
|
|
10
|
+
method: "GET",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (res.ok === false) {
|
|
14
|
+
throw Error(await res.text());
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { data } = await res.json();
|
|
18
|
+
return JSON.parse(data);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const deleteRequest = async (id: string) => {
|
|
22
|
+
const res = await fetch(`${proxyApi}/${id}`, {
|
|
23
|
+
headers: { "content-type": "application/json" },
|
|
24
|
+
method: "DELETE",
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (res.ok === false) {
|
|
28
|
+
throw Error(await res.text());
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const computeRequestId = async (request: object) => {
|
|
33
|
+
const query = base58.encode(Buffer.from(JSON.stringify({ ...request, _id: uuid4() }), "utf8"));
|
|
34
|
+
const hashsum = sha1(Buffer.from(query, "utf8"));
|
|
35
|
+
const id = Buffer.from(hashsum).toString("base64");
|
|
36
|
+
const requestId = id.replaceAll("/", "_").replaceAll("-", "+").slice(0, 13);
|
|
37
|
+
return { requestId, query };
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const createRequest = async (request: object, signal?: AbortSignal) => {
|
|
41
|
+
const { query, requestId } = await computeRequestId(request);
|
|
42
|
+
const res = await fetch(`${proxyApi}/${requestId}/request`, {
|
|
43
|
+
body: JSON.stringify({ data: query }),
|
|
44
|
+
headers: { "content-type": "application/json" },
|
|
45
|
+
method: "POST",
|
|
46
|
+
signal,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (res.ok === false) {
|
|
50
|
+
throw Error(await res.text());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return requestId;
|
|
54
|
+
};
|
package/src/solana/connector.ts
CHANGED
|
@@ -6,8 +6,8 @@ import { runInAction } from "mobx";
|
|
|
6
6
|
import { ConnectorType, OmniConnector, WC_ICON } from "../OmniConnector";
|
|
7
7
|
import { HotConnector } from "../HotConnector";
|
|
8
8
|
import { OmniWallet } from "../OmniWallet";
|
|
9
|
-
import { isInjected } from "../hot-wallet/iframe";
|
|
10
9
|
import { WalletType } from "../core/chains";
|
|
10
|
+
import HOT from "../hot-wallet/iframe";
|
|
11
11
|
|
|
12
12
|
import SolanaProtocolWallet from "./protocol";
|
|
13
13
|
import { getWallets } from "./wallets";
|
|
@@ -117,7 +117,7 @@ class SolanaConnector extends OmniConnector<SolanaWallet, { wallet: Wallet }> {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
async getConnectedWallet() {
|
|
120
|
-
if (isInjected
|
|
120
|
+
if (HOT.isInjected) return { type: "wallet", id: "HOT Wallet" };
|
|
121
121
|
return this.getStorage();
|
|
122
122
|
}
|
|
123
123
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { SolanaSignAndSendTransaction, SolanaSignMessage, SolanaSignTransaction } from "@solana/wallet-standard-features";
|
|
2
1
|
import type { WalletAccount } from "@wallet-standard/base";
|
|
3
2
|
import { SOLANA_CHAINS } from "./utils";
|
|
4
3
|
|
|
5
4
|
const chains = SOLANA_CHAINS;
|
|
6
|
-
const features = [
|
|
5
|
+
const features = ["solana:signAndSendTransaction", "solana:signMessage", "solana:signMessage"] as const;
|
|
7
6
|
|
|
8
7
|
export class GhostWalletAccount implements WalletAccount {
|
|
9
8
|
readonly #address: WalletAccount["address"];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Transaction, VersionedTransaction, PublicKey } from "@solana/web3.js";
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import HOT from "../../hot-wallet/iframe";
|
|
4
4
|
import { registerWallet } from "./register";
|
|
5
5
|
import { GhostWallet } from "./solana-wallet";
|
|
6
6
|
|
|
@@ -25,17 +25,17 @@ const hotSolana = {
|
|
|
25
25
|
publicKey: null as PublicKey | null,
|
|
26
26
|
|
|
27
27
|
async connect(options: any) {
|
|
28
|
-
const { publicKey } = await
|
|
28
|
+
const { publicKey } = await HOT.request("solana:connect", options);
|
|
29
29
|
hotSolana.publicKey = new PublicKey(publicKey);
|
|
30
30
|
return { publicKey: hotSolana.publicKey };
|
|
31
31
|
},
|
|
32
32
|
|
|
33
33
|
async disconnect() {
|
|
34
|
-
return await
|
|
34
|
+
return await HOT.request("solana:disconnect", {});
|
|
35
35
|
},
|
|
36
36
|
|
|
37
37
|
async signAllTransactions(transactions: any[]) {
|
|
38
|
-
const result = await
|
|
38
|
+
const result = await HOT.request("solana:signAllTransactions", {
|
|
39
39
|
transactions: transactions.map((t) => t.serialize().toString("base64")),
|
|
40
40
|
});
|
|
41
41
|
|
|
@@ -43,7 +43,7 @@ const hotSolana = {
|
|
|
43
43
|
},
|
|
44
44
|
|
|
45
45
|
async signTransaction(transaction: any) {
|
|
46
|
-
const result = await
|
|
46
|
+
const result = await HOT.request("solana:signAllTransactions", {
|
|
47
47
|
transactions: [transaction.serialize().toString("base64")],
|
|
48
48
|
});
|
|
49
49
|
|
|
@@ -51,7 +51,7 @@ const hotSolana = {
|
|
|
51
51
|
},
|
|
52
52
|
|
|
53
53
|
async signAndSendTransaction(transaction: any, options: any) {
|
|
54
|
-
const result = await
|
|
54
|
+
const result = await HOT.request("solana:signAndSendTransaction", {
|
|
55
55
|
transaction: transaction.serialize().toString("base64"),
|
|
56
56
|
options,
|
|
57
57
|
});
|
|
@@ -61,11 +61,10 @@ const hotSolana = {
|
|
|
61
61
|
|
|
62
62
|
async signIn(input: any) {
|
|
63
63
|
throw "Not supported";
|
|
64
|
-
return await requestHot("solana:signIn", input);
|
|
65
64
|
},
|
|
66
65
|
|
|
67
66
|
async signMessage(message: string) {
|
|
68
|
-
const result = await
|
|
67
|
+
const result = await HOT.request("solana:signMessage", { message: Buffer.from(message).toString("base64") });
|
|
69
68
|
return { signature: Buffer.from(result.signature, "base64") };
|
|
70
69
|
},
|
|
71
70
|
|
|
@@ -84,15 +83,13 @@ HOT.subscribe("solana:accountChanged", (publicKey: string | null) => {
|
|
|
84
83
|
});
|
|
85
84
|
*/
|
|
86
85
|
|
|
87
|
-
|
|
88
|
-
registerWallet(new GhostWallet(hotSolana));
|
|
86
|
+
registerWallet(new GhostWallet(hotSolana));
|
|
89
87
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
88
|
+
// New wallets no longer need to register wallet globals - and can
|
|
89
|
+
// ignore the code below. However if you have legacy apps relying on globals,
|
|
90
|
+
// this is the safest way to attach the reference to the window, guarding against errors.
|
|
91
|
+
try {
|
|
92
|
+
Object.defineProperty(window, "hotSolana", { value: hotSolana });
|
|
93
|
+
} catch (error) {
|
|
94
|
+
//
|
|
98
95
|
}
|
package/src/stellar/connector.ts
CHANGED
|
@@ -1,68 +1,81 @@
|
|
|
1
|
-
import { sep43Modules, HotWalletModule, StellarWalletsKit, WalletNetwork, ISupportedWallet } from "@creit.tech/stellar-wallets-kit";
|
|
2
1
|
import { Transaction } from "@stellar/stellar-base";
|
|
3
2
|
|
|
3
|
+
import HOT from "../hot-wallet/iframe";
|
|
4
4
|
import { WalletType } from "../core/chains";
|
|
5
5
|
import { HotConnector } from "../HotConnector";
|
|
6
6
|
import { ConnectorType, OmniConnector } from "../OmniConnector";
|
|
7
|
-
import { isInjected } from "../hot-wallet/iframe";
|
|
8
7
|
import { OmniWallet } from "../OmniWallet";
|
|
9
|
-
import StellarWallet from "./wallet";
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
import { FreighterModule } from "./freigher";
|
|
10
|
+
import { HotWalletModule } from "./hotWallet";
|
|
11
|
+
import StellarWallet from "./wallet";
|
|
13
12
|
|
|
13
|
+
class StellarConnector extends OmniConnector<StellarWallet> {
|
|
14
14
|
icon = "https://storage.herewallet.app/upload/1469894e53ca248ac6adceb2194e6950a13a52d972beb378a20bce7815ba01a4.png";
|
|
15
15
|
walletTypes = [WalletType.STELLAR, WalletType.OMNI];
|
|
16
16
|
type = ConnectorType.WALLET;
|
|
17
17
|
name = "Stellar Wallet";
|
|
18
18
|
id = "stellar";
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
modules = {
|
|
21
|
+
hotWallet: new HotWalletModule(),
|
|
22
|
+
freighter: new FreighterModule(),
|
|
23
|
+
};
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const hot = wallets.find((w) => w.id === "hot-wallet");
|
|
26
|
-
this.options = wallets.filter((w) => w.id !== "hot-wallet").map((w) => ({ ...w, name: w.name, icon: w.icon, download: w.url, type: "external" as const, id: w.id }));
|
|
27
|
-
if (hot) this.options.unshift({ ...hot, name: hot.name, icon: hot.icon, download: hot.url, type: "external" as const, id: hot.id });
|
|
28
|
-
});
|
|
25
|
+
constructor(wibe3: HotConnector) {
|
|
26
|
+
super(wibe3);
|
|
29
27
|
|
|
30
|
-
this.
|
|
31
|
-
|
|
28
|
+
this.options = Object.values(this.modules).map((module) => ({
|
|
29
|
+
type: "external" as const,
|
|
30
|
+
name: module.productName,
|
|
31
|
+
icon: module.productIcon,
|
|
32
|
+
download: module.productUrl,
|
|
33
|
+
id: module.productId,
|
|
34
|
+
}));
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
36
|
+
this.getConnectedWallet().then(async ({ id, address }) => {
|
|
37
|
+
if (!id || !address) return;
|
|
38
|
+
const wallet = this.getWallet(id);
|
|
39
|
+
const isAvailable = await wallet?.isAvailable();
|
|
40
|
+
if (isAvailable && wallet) this.selectWallet(address, wallet);
|
|
37
41
|
});
|
|
38
42
|
}
|
|
39
43
|
|
|
44
|
+
getWallet(id: string): FreighterModule | HotWalletModule | null {
|
|
45
|
+
return Object.values(this.modules).find((module) => module.productId === id) || null;
|
|
46
|
+
}
|
|
47
|
+
|
|
40
48
|
async createWallet(address: string): Promise<OmniWallet> {
|
|
41
49
|
return new StellarWallet(this, { address });
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
async getConnectedWallet() {
|
|
45
|
-
if (isInjected
|
|
46
|
-
this.
|
|
47
|
-
|
|
48
|
-
return { type: "wallet", id: "hot-wallet", address };
|
|
53
|
+
if (HOT.isInjected) {
|
|
54
|
+
const { address } = await this.modules.hotWallet.getAddress();
|
|
55
|
+
return { type: "wallet", id: this.modules.hotWallet.productId, address };
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
return await this.getStorage();
|
|
52
59
|
}
|
|
53
60
|
|
|
61
|
+
async selectWallet(address: string, wallet: HotWalletModule | FreighterModule) {
|
|
62
|
+
const signMessage = async (message: string) => wallet.signMessage(message);
|
|
63
|
+
const signTransaction = async (transaction: Transaction) => wallet.signTransaction(transaction.toXDR());
|
|
64
|
+
return this.setWallet(new StellarWallet(this, { address, signMessage, signTransaction }));
|
|
65
|
+
}
|
|
66
|
+
|
|
54
67
|
async connect(id: string) {
|
|
55
|
-
this.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
68
|
+
const wallet = this.getWallet(id);
|
|
69
|
+
if (!wallet) throw new Error("Wallet not found");
|
|
70
|
+
|
|
71
|
+
const { address } = await wallet.getAddress();
|
|
59
72
|
this.setStorage({ type: "wallet", id, address });
|
|
60
|
-
return this.
|
|
73
|
+
return this.selectWallet(address, wallet);
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
async disconnect() {
|
|
64
77
|
super.disconnect();
|
|
65
|
-
this.
|
|
78
|
+
this.removeStorage();
|
|
66
79
|
}
|
|
67
80
|
}
|
|
68
81
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { isConnected, requestAccess, getAddress, signTransaction, signAuthEntry, signMessage, getNetwork } from "@stellar/freighter-api";
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
interface Window {
|
|
5
|
+
stellar?: {
|
|
6
|
+
provider: string;
|
|
7
|
+
platform: string;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function parseError(e: any) {
|
|
13
|
+
return {
|
|
14
|
+
code: e?.error?.code || e?.code || -1,
|
|
15
|
+
message: e?.error?.message || e?.message || (typeof e === "string" && e) || "Unhandled error from the wallet",
|
|
16
|
+
ext: e?.error?.ext || e?.ext,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const FREIGHTER_ID = "freighter";
|
|
21
|
+
export class FreighterModule {
|
|
22
|
+
productId = FREIGHTER_ID;
|
|
23
|
+
productName = "Freighter";
|
|
24
|
+
productUrl = "https://freighter.app";
|
|
25
|
+
productIcon = "https://stellar.creit.tech/wallet-icons/freighter.png";
|
|
26
|
+
|
|
27
|
+
async runChecks() {
|
|
28
|
+
if (!(await this.isAvailable())) {
|
|
29
|
+
throw new Error("Freighter is not connected");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async isAvailable() {
|
|
34
|
+
if (window.stellar?.provider === "freighter" && window.stellar?.platform === "mobile") return false;
|
|
35
|
+
return isConnected()
|
|
36
|
+
.then(({ isConnected: isConnected2, error }) => !error && isConnected2)
|
|
37
|
+
.catch(() => false);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async getAddress(params?: { skipRequestAccess?: boolean }): Promise<{ address: string }> {
|
|
41
|
+
return this.runChecks()
|
|
42
|
+
.then(async () => {
|
|
43
|
+
if (params?.skipRequestAccess) return true;
|
|
44
|
+
return requestAccess();
|
|
45
|
+
})
|
|
46
|
+
.then(() => getAddress())
|
|
47
|
+
.then(({ address, error }) => {
|
|
48
|
+
if (error) throw error;
|
|
49
|
+
if (!address)
|
|
50
|
+
throw {
|
|
51
|
+
code: -3,
|
|
52
|
+
message: "Getting the address is not allowed, please request access first.",
|
|
53
|
+
};
|
|
54
|
+
return { address };
|
|
55
|
+
})
|
|
56
|
+
.catch((e) => {
|
|
57
|
+
throw parseError(e);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async signTransaction(xdr: string, opts?: { address?: string; networkPassphrase?: string }): Promise<{ signedTxXdr: string; signerAddress?: string }> {
|
|
61
|
+
return this.runChecks()
|
|
62
|
+
.then(async () => {
|
|
63
|
+
const { signedTxXdr, signerAddress, error } = await signTransaction(xdr, {
|
|
64
|
+
address: opts?.address,
|
|
65
|
+
networkPassphrase: opts?.networkPassphrase,
|
|
66
|
+
});
|
|
67
|
+
if (error) throw error;
|
|
68
|
+
return { signedTxXdr, signerAddress };
|
|
69
|
+
})
|
|
70
|
+
.catch((e) => {
|
|
71
|
+
throw parseError(e);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async signAuthEntry(authEntry: string, opts?: { address?: string; networkPassphrase?: string }): Promise<{ signedAuthEntry: string; signerAddress?: string }> {
|
|
76
|
+
return this.runChecks()
|
|
77
|
+
.then(async () => {
|
|
78
|
+
const { signedAuthEntry, signerAddress, error } = await signAuthEntry(authEntry, {
|
|
79
|
+
address: opts?.address,
|
|
80
|
+
networkPassphrase: opts?.networkPassphrase,
|
|
81
|
+
});
|
|
82
|
+
if (error || !signedAuthEntry) throw error;
|
|
83
|
+
return { signedAuthEntry: Buffer.from(signedAuthEntry).toString("base64"), signerAddress };
|
|
84
|
+
})
|
|
85
|
+
.catch((e) => {
|
|
86
|
+
throw parseError(e);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async signMessage(message: string, opts?: { address?: string; networkPassphrase?: string }): Promise<{ signedMessage: string; signerAddress?: string }> {
|
|
91
|
+
return this.runChecks()
|
|
92
|
+
.then(async () => {
|
|
93
|
+
const { signedMessage, signerAddress, error } = await signMessage(message, {
|
|
94
|
+
address: opts?.address,
|
|
95
|
+
networkPassphrase: opts?.networkPassphrase,
|
|
96
|
+
});
|
|
97
|
+
if (error || !signedMessage) throw error;
|
|
98
|
+
return {
|
|
99
|
+
signedMessage: typeof signedMessage === "string" ? signedMessage : Buffer.from(signedMessage).toString("base64"),
|
|
100
|
+
signerAddress,
|
|
101
|
+
};
|
|
102
|
+
})
|
|
103
|
+
.catch((e) => {
|
|
104
|
+
throw parseError(e);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async getNetwork(): Promise<{ network: string; networkPassphrase: string }> {
|
|
109
|
+
return this.runChecks()
|
|
110
|
+
.then(async () => {
|
|
111
|
+
const { network, networkPassphrase, error } = await getNetwork();
|
|
112
|
+
if (error) throw error;
|
|
113
|
+
return { network, networkPassphrase };
|
|
114
|
+
})
|
|
115
|
+
.catch((e) => {
|
|
116
|
+
throw parseError(e);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { Networks } from "@stellar/stellar-sdk";
|
|
2
|
+
import HOT from "../hot-wallet/iframe";
|
|
3
3
|
|
|
4
4
|
export const HOTWALLET_ID: string = "hot-wallet";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
* **IMPORTANT**: This module requires that you have a "global" and a "Buffer" polyfill in your app, if not provided then this module will break your app.
|
|
8
|
-
*/
|
|
9
|
-
export class HotWalletModule implements ModuleInterface {
|
|
10
|
-
moduleType: ModuleType = ModuleType.HOT_WALLET;
|
|
6
|
+
export class HotWalletModule {
|
|
11
7
|
productId: string = HOTWALLET_ID;
|
|
12
8
|
productName: string = "HOT Wallet";
|
|
13
9
|
productUrl: string = "https://hot-labs.org/wallet";
|
|
@@ -18,22 +14,22 @@ export class HotWalletModule implements ModuleInterface {
|
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
async getAddress(): Promise<{ address: string }> {
|
|
21
|
-
return await
|
|
17
|
+
return await HOT.request("stellar:getAddress", {});
|
|
22
18
|
}
|
|
23
19
|
|
|
24
20
|
async signTransaction(xdr: string, opts?: { address?: string }): Promise<{ signedTxXdr: string; signerAddress?: string }> {
|
|
25
|
-
return await
|
|
21
|
+
return await HOT.request("stellar:signTransaction", { xdr, accountToSign: opts?.address });
|
|
26
22
|
}
|
|
27
23
|
|
|
28
24
|
async signAuthEntry(authEntry: string, opts?: { address?: string }): Promise<{ signedAuthEntry: string; signerAddress?: string }> {
|
|
29
|
-
return await
|
|
25
|
+
return await HOT.request("stellar:signAuthEntry", { authEntry, accountToSign: opts?.address });
|
|
30
26
|
}
|
|
31
27
|
|
|
32
28
|
async signMessage(message: string, opts?: { address?: string }): Promise<{ signedMessage: string; signerAddress?: string }> {
|
|
33
|
-
return await
|
|
29
|
+
return await HOT.request("stellar:signMessage", { message, accountToSign: opts?.address });
|
|
34
30
|
}
|
|
35
31
|
|
|
36
32
|
async getNetwork(): Promise<{ network: string; networkPassphrase: string }> {
|
|
37
|
-
return { network: "mainnet", networkPassphrase:
|
|
33
|
+
return { network: "mainnet", networkPassphrase: Networks.PUBLIC };
|
|
38
34
|
}
|
|
39
35
|
}
|