@thru/wallet 0.2.22
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/README.md +67 -0
- package/android/build.gradle +37 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/org/thru/walletnative/ThruWebViewBridgeModule.kt +77 -0
- package/app.plugin.cjs +101 -0
- package/dist/BrowserSDK-CpRFiJsW.d.ts +409 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +941 -0
- package/dist/index.js.map +1 -0
- package/dist/native/react.d.ts +109 -0
- package/dist/native/react.js +2381 -0
- package/dist/native/react.js.map +1 -0
- package/dist/native.d.ts +329 -0
- package/dist/native.js +1126 -0
- package/dist/native.js.map +1 -0
- package/dist/react-ui.d.ts +5 -0
- package/dist/react-ui.js +266 -0
- package/dist/react-ui.js.map +1 -0
- package/dist/react.d.ts +66 -0
- package/dist/react.js +1151 -0
- package/dist/react.js.map +1 -0
- package/expo-module.config.json +6 -0
- package/package.json +114 -0
- package/src/BrowserSDK.ts +315 -0
- package/src/index.ts +27 -0
- package/src/interfaces/IThruChain.ts +37 -0
- package/src/interfaces/accounts.ts +61 -0
- package/src/interfaces/index.ts +9 -0
- package/src/interfaces/types.ts +95 -0
- package/src/native/NativeSDK.test.ts +819 -0
- package/src/native/NativeSDK.ts +773 -0
- package/src/native/index.ts +39 -0
- package/src/native/provider/NativeProvider.ts +363 -0
- package/src/native/provider/WebViewBridge.test.ts +339 -0
- package/src/native/provider/WebViewBridge.ts +339 -0
- package/src/native/provider/chains/ThruChain.ts +85 -0
- package/src/native/provider/shell.html +88 -0
- package/src/native/provider/shell.test.ts +56 -0
- package/src/native/provider/shell.ts +111 -0
- package/src/native/provider/shims-html.d.ts +4 -0
- package/src/native/react/ThruContext.ts +37 -0
- package/src/native/react/ThruProvider.tsx +168 -0
- package/src/native/react/ThruWalletSheet.tsx +1162 -0
- package/src/native/react/android-webauthn.ts +37 -0
- package/src/native/react/hooks/useAccounts.ts +35 -0
- package/src/native/react/hooks/useThru.ts +11 -0
- package/src/native/react/hooks/useWallet.ts +71 -0
- package/src/native/react/hooks/useWalletAvailability.ts +31 -0
- package/src/native/react/hooks/waitForWallet.ts +21 -0
- package/src/native/react/index.ts +29 -0
- package/src/protocol/index.ts +2 -0
- package/src/protocol/postMessage.ts +283 -0
- package/src/protocol/walletState.ts +12 -0
- package/src/provider/EmbeddedProvider.ts +330 -0
- package/src/provider/IframeManager.ts +438 -0
- package/src/provider/chains/ThruChain.ts +86 -0
- package/src/provider/index.ts +17 -0
- package/src/provider/types/messages.ts +37 -0
- package/src/react/ThruContext.ts +31 -0
- package/src/react/ThruProvider.tsx +169 -0
- package/src/react/hooks/useAccounts.ts +38 -0
- package/src/react/hooks/useThru.ts +11 -0
- package/src/react/hooks/useWallet.ts +81 -0
- package/src/react/index.ts +30 -0
- package/src/react-ui/ThruAccountSwitcher.tsx +187 -0
- package/src/react-ui/custom.d.ts +8 -0
- package/src/react-ui/index.ts +1 -0
- package/src/static/logo.png +0 -0
- package/src/static/logomark_red.svg +11 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AddressType,
|
|
3
|
+
normalizeWalletAccountResult,
|
|
4
|
+
} from '../interfaces';
|
|
5
|
+
import type {
|
|
6
|
+
AddressType as AddressTypeValue,
|
|
7
|
+
ConnectResult,
|
|
8
|
+
IThruChain,
|
|
9
|
+
WalletAccount,
|
|
10
|
+
} from '../interfaces';
|
|
11
|
+
import {
|
|
12
|
+
DEFAULT_IFRAME_URL,
|
|
13
|
+
EMBEDDED_PROVIDER_EVENTS,
|
|
14
|
+
POST_MESSAGE_REQUEST_TYPES,
|
|
15
|
+
createRequestId,
|
|
16
|
+
type ConnectMetadataInput,
|
|
17
|
+
type ConnectRequestPayload,
|
|
18
|
+
type ManageAccountsResult,
|
|
19
|
+
type SelectAccountPayload,
|
|
20
|
+
} from '../protocol';
|
|
21
|
+
import { IframeManager } from './IframeManager';
|
|
22
|
+
import { EmbeddedThruChain } from './chains/ThruChain';
|
|
23
|
+
|
|
24
|
+
export interface EmbeddedProviderConfig {
|
|
25
|
+
iframeUrl?: string;
|
|
26
|
+
addressTypes?: AddressTypeValue[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ConnectOptions {
|
|
30
|
+
metadata?: ConnectMetadataInput;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Main embedded provider class
|
|
35
|
+
* Manages iframe lifecycle, connection state, and chain-specific interfaces
|
|
36
|
+
*/
|
|
37
|
+
export class EmbeddedProvider {
|
|
38
|
+
private iframeManager: IframeManager;
|
|
39
|
+
private _thruChain?: IThruChain;
|
|
40
|
+
private connected = false;
|
|
41
|
+
private accounts: WalletAccount[] = [];
|
|
42
|
+
private selectedAccount: WalletAccount | null = null;
|
|
43
|
+
private eventListeners = new Map<string, Set<Function>>();
|
|
44
|
+
private inlineMode = false;
|
|
45
|
+
constructor(config: EmbeddedProviderConfig) {
|
|
46
|
+
const iframeUrl = config.iframeUrl || DEFAULT_IFRAME_URL;
|
|
47
|
+
this.iframeManager = new IframeManager(iframeUrl);
|
|
48
|
+
|
|
49
|
+
// Set up event forwarding from iframe
|
|
50
|
+
this.iframeManager.onEvent = (eventType: string, payload: any) => {
|
|
51
|
+
this.emit(eventType, payload);
|
|
52
|
+
|
|
53
|
+
if (eventType === EMBEDDED_PROVIDER_EVENTS.UI_SHOW) {
|
|
54
|
+
if (this.inlineMode) {
|
|
55
|
+
this.iframeManager.showInline();
|
|
56
|
+
} else {
|
|
57
|
+
this.iframeManager.showModal();
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (
|
|
63
|
+
eventType === EMBEDDED_PROVIDER_EVENTS.DISCONNECT ||
|
|
64
|
+
eventType === EMBEDDED_PROVIDER_EVENTS.LOCK
|
|
65
|
+
) {
|
|
66
|
+
this.clearConnection();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (eventType === EMBEDDED_PROVIDER_EVENTS.ACCOUNT_CHANGED) {
|
|
71
|
+
const account =
|
|
72
|
+
(payload && (payload.account as WalletAccount | undefined)) || null;
|
|
73
|
+
this.refreshAccountCache(account ?? null);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Create chain instances
|
|
78
|
+
const addressTypes = config.addressTypes || [AddressType.THRU];
|
|
79
|
+
if (addressTypes.includes(AddressType.THRU)) {
|
|
80
|
+
this._thruChain = new EmbeddedThruChain(this.iframeManager, this);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Initialize the provider (must be called before use)
|
|
86
|
+
* Creates iframe and waits for it to be ready
|
|
87
|
+
*/
|
|
88
|
+
async initialize(): Promise<void> {
|
|
89
|
+
await this.iframeManager.createIframe();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Mount the wallet iframe inline in a container (for inline connect button).
|
|
94
|
+
*/
|
|
95
|
+
async mountInline(container: HTMLElement): Promise<void> {
|
|
96
|
+
this.inlineMode = true;
|
|
97
|
+
await this.iframeManager.mountInline(container);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Connect to wallet
|
|
102
|
+
* Shows iframe modal and requests connection
|
|
103
|
+
*/
|
|
104
|
+
async connect(options?: ConnectOptions): Promise<ConnectResult> {
|
|
105
|
+
// Emit connecting event
|
|
106
|
+
this.emit(EMBEDDED_PROVIDER_EVENTS.CONNECT_START, {});
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
if (this.inlineMode) {
|
|
110
|
+
this.iframeManager.showInline();
|
|
111
|
+
} else {
|
|
112
|
+
this.iframeManager.showModal();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const payload: ConnectRequestPayload = {};
|
|
116
|
+
|
|
117
|
+
if (options?.metadata) {
|
|
118
|
+
payload.metadata = options.metadata;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const response = await this.iframeManager.sendMessage({
|
|
122
|
+
id: createRequestId(),
|
|
123
|
+
type: POST_MESSAGE_REQUEST_TYPES.CONNECT,
|
|
124
|
+
payload,
|
|
125
|
+
origin: window.location.origin,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const result = normalizeWalletAccountResult(response.result);
|
|
129
|
+
this.connected = true;
|
|
130
|
+
this.accounts = result.accounts;
|
|
131
|
+
this.selectedAccount = result.selectedAccount;
|
|
132
|
+
|
|
133
|
+
// Emit success event
|
|
134
|
+
this.emit(EMBEDDED_PROVIDER_EVENTS.CONNECT, result);
|
|
135
|
+
|
|
136
|
+
// Hide iframe after successful connection
|
|
137
|
+
if (!this.inlineMode) {
|
|
138
|
+
this.iframeManager.hide();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return result;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
if (!this.inlineMode) {
|
|
144
|
+
this.iframeManager.hide();
|
|
145
|
+
}
|
|
146
|
+
this.emit(EMBEDDED_PROVIDER_EVENTS.CONNECT_ERROR, { error });
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Disconnect from wallet
|
|
153
|
+
*/
|
|
154
|
+
async disconnect(): Promise<void> {
|
|
155
|
+
try {
|
|
156
|
+
await this.iframeManager.sendMessage({
|
|
157
|
+
id: createRequestId(),
|
|
158
|
+
type: POST_MESSAGE_REQUEST_TYPES.DISCONNECT,
|
|
159
|
+
origin: window.location.origin,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
this.clearConnection();
|
|
163
|
+
this.emit(EMBEDDED_PROVIDER_EVENTS.DISCONNECT, {});
|
|
164
|
+
} catch (error) {
|
|
165
|
+
this.clearConnection();
|
|
166
|
+
this.emit(EMBEDDED_PROVIDER_EVENTS.ERROR, { error });
|
|
167
|
+
throw error;
|
|
168
|
+
} finally {
|
|
169
|
+
if (!this.inlineMode) {
|
|
170
|
+
this.iframeManager.hide();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Check if connected
|
|
177
|
+
*/
|
|
178
|
+
isConnected(): boolean {
|
|
179
|
+
return this.connected;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Get accounts
|
|
184
|
+
*/
|
|
185
|
+
getAccounts(): WalletAccount[] {
|
|
186
|
+
return this.accounts;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
getSelectedAccount(): WalletAccount | null {
|
|
190
|
+
return this.selectedAccount;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async selectAccount(publicKey: string): Promise<WalletAccount> {
|
|
194
|
+
if (!this.connected) {
|
|
195
|
+
throw new Error("Wallet not connected");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const knownAccount =
|
|
199
|
+
this.accounts.find((acc) => acc.address === publicKey) ?? null;
|
|
200
|
+
if (!knownAccount) {
|
|
201
|
+
console.warn(
|
|
202
|
+
"[EmbeddedProvider] Selecting account not present in local cache",
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
const payload: SelectAccountPayload = { publicKey };
|
|
206
|
+
|
|
207
|
+
const response = await this.iframeManager.sendMessage({
|
|
208
|
+
id: createRequestId(),
|
|
209
|
+
type: POST_MESSAGE_REQUEST_TYPES.SELECT_ACCOUNT,
|
|
210
|
+
payload,
|
|
211
|
+
origin: window.location.origin,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const account = response.result.account;
|
|
215
|
+
|
|
216
|
+
this.refreshAccountCache(account);
|
|
217
|
+
return account;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async manageAccounts(): Promise<ManageAccountsResult> {
|
|
221
|
+
if (!this.connected) {
|
|
222
|
+
throw new Error("Wallet not connected");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (this.inlineMode) {
|
|
226
|
+
this.iframeManager.showInline();
|
|
227
|
+
} else {
|
|
228
|
+
this.iframeManager.showModal();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
const response = await this.iframeManager.sendMessage({
|
|
233
|
+
id: createRequestId(),
|
|
234
|
+
type: POST_MESSAGE_REQUEST_TYPES.MANAGE_ACCOUNTS,
|
|
235
|
+
origin: window.location.origin,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const result = normalizeWalletAccountResult({
|
|
239
|
+
accounts: response.result.accounts,
|
|
240
|
+
selectedAccount: response.result.selectedAccount,
|
|
241
|
+
});
|
|
242
|
+
this.accounts = result.accounts;
|
|
243
|
+
this.selectedAccount = result.selectedAccount;
|
|
244
|
+
if (this.selectedAccount) {
|
|
245
|
+
this.emit(EMBEDDED_PROVIDER_EVENTS.ACCOUNT_CHANGED, {
|
|
246
|
+
account: this.selectedAccount,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return result;
|
|
250
|
+
} finally {
|
|
251
|
+
if (!this.inlineMode) {
|
|
252
|
+
this.iframeManager.hide();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Get Thru chain API
|
|
259
|
+
*/
|
|
260
|
+
get thru(): IThruChain {
|
|
261
|
+
if (!this._thruChain) {
|
|
262
|
+
throw new Error("Thru chain not enabled in provider config");
|
|
263
|
+
}
|
|
264
|
+
return this._thruChain;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Event emitter: on
|
|
269
|
+
*/
|
|
270
|
+
on(event: string, callback: Function): void {
|
|
271
|
+
if (!this.eventListeners.has(event)) {
|
|
272
|
+
this.eventListeners.set(event, new Set());
|
|
273
|
+
}
|
|
274
|
+
this.eventListeners.get(event)!.add(callback);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Event emitter: off
|
|
279
|
+
*/
|
|
280
|
+
off(event: string, callback: Function): void {
|
|
281
|
+
this.eventListeners.get(event)?.delete(callback);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Emit event to all listeners
|
|
286
|
+
*/
|
|
287
|
+
private emit(event: string, data?: any): void {
|
|
288
|
+
this.eventListeners.get(event)?.forEach((callback) => {
|
|
289
|
+
try {
|
|
290
|
+
callback(data);
|
|
291
|
+
} catch (error) {
|
|
292
|
+
console.error(`Error in event listener for ${event}:`, error);
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get iframe manager (for chain implementations)
|
|
299
|
+
* @internal
|
|
300
|
+
*/
|
|
301
|
+
getIframeManager(): IframeManager {
|
|
302
|
+
return this.iframeManager;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Destroy provider and cleanup
|
|
307
|
+
*/
|
|
308
|
+
destroy(): void {
|
|
309
|
+
this.iframeManager.destroy();
|
|
310
|
+
this.eventListeners.clear();
|
|
311
|
+
this.clearConnection();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private refreshAccountCache(account: WalletAccount | null): void {
|
|
315
|
+
if (!account) {
|
|
316
|
+
this.accounts = [];
|
|
317
|
+
this.selectedAccount = null;
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
this.accounts = [account];
|
|
322
|
+
this.selectedAccount = account;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private clearConnection(): void {
|
|
326
|
+
this.connected = false;
|
|
327
|
+
this.accounts = [];
|
|
328
|
+
this.selectedAccount = null;
|
|
329
|
+
}
|
|
330
|
+
}
|