@0xio/sdk 2.4.0 → 2.4.2
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/CHANGELOG.md +39 -0
- package/README.md +19 -7
- package/dist/index.d.ts +19 -8
- package/dist/index.esm.js +114 -47
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +114 -47
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +114 -47
- package/dist/index.umd.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the 0xio Wallet SDK will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [2.4.2] - 2026-04-15
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **Cross-origin iframe DApp bridge**: SDK now accepts localhost origins as trusted parents for dev/testing. Fixes "Unable to post message" errors when DApps run on different localhost ports inside the desktop browser.
|
|
9
|
+
- **Parent origin capture**: `walletReady` signal now carries `parentOrigin` field. SDK stores it and uses it for cross-origin replies instead of failing silently.
|
|
10
|
+
- **postMessage targeting**: When inside an iframe, SDK replies to the captured parent origin (or wildcard fallback) instead of `window.location.origin` which fails for cross-port scenarios.
|
|
11
|
+
|
|
12
|
+
## [2.4.1] - 2026-04-14
|
|
13
|
+
|
|
14
|
+
### Security
|
|
15
|
+
- **[CRITICAL] postMessage origin validation**: Parent-frame messages now validated against a strict trusted origins set instead of accepting all origins. Prevents malicious pages from intercepting wallet requests via iframe embedding.
|
|
16
|
+
- **[CRITICAL] Removed auto-trust for iframes**: SDK no longer assumes any iframe parent is a wallet bridge. Must receive a `walletReady` signal from a trusted origin first.
|
|
17
|
+
- **[CRITICAL] Response binding**: Only responses with matching pending request IDs are processed. Forged responses from injected scripts are rejected.
|
|
18
|
+
- **[HIGH] Removed `simulateExtensionEvent`**: Dev utility that could be exploited on staging builds to inject fake wallet events has been removed.
|
|
19
|
+
- **[HIGH] No wildcard postMessage**: `postMessageToExtension` now uses specific trusted origins (`tauri://localhost`, etc.) instead of `'*'` for parent-frame communication.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- **No retry on user rejection**: `retry()` detects rejection/denied/cancelled errors and throws immediately. Prevents double confirmation popups.
|
|
23
|
+
- **`withTimeout` timer leak**: Timer is now cleared via `.finally()` when the promise resolves, preventing 30s memory retention per request.
|
|
24
|
+
- **`retry` off-by-one**: `maxRetries=1` now correctly means 1 initial + 1 retry = 2 total (was 3).
|
|
25
|
+
- **Message listener cleanup**: `cleanup()` now removes the `window.addEventListener('message')` listener, preventing accumulation on re-instantiation.
|
|
26
|
+
- **Type compatibility**: Replaced `NodeJS.Timeout` with `ReturnType<typeof setTimeout>` for browser-only environments.
|
|
27
|
+
- **`process.env` guard**: `createLogger` now uses optional chaining for `process.env.NODE_ENV`, preventing ReferenceError when imported in browser without bundler.
|
|
28
|
+
- **Duplicate `isValidNetworkId`**: Removed duplicate export from `utils.ts`, canonical version in `config/networks.ts`.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- **`setTrustedOrigins(origins)`**: New method to configure allowed parent-frame origins for iframe/bridge communication.
|
|
32
|
+
|
|
33
|
+
### Documentation
|
|
34
|
+
- Fixed all event listener examples to use `event.data.xxx` (WalletEvent wrapper)
|
|
35
|
+
- Fixed `TransactionHistory` type (`totalCount`/`hasMore` instead of `total`/`limit`/`totalPages`)
|
|
36
|
+
- Fixed `retry()` docs (positional args, not options object)
|
|
37
|
+
- Fixed `formatZeroXIO` return value (no " OCT" suffix)
|
|
38
|
+
- Fixed `toMicroZeroXIO` return type (string, not number)
|
|
39
|
+
- Removed nonexistent `ConnectOptions.timeout`, `.requestPrivateAccess`
|
|
40
|
+
- Removed nonexistent `ConnectionInfo.permissions`
|
|
41
|
+
- Changed `ErrorCode.TIMEOUT` to `ErrorCode.NETWORK_ERROR`
|
|
42
|
+
- Updated mainnet privacy support to "Yes"
|
|
43
|
+
|
|
5
44
|
## [2.4.0] - 2026-03-24
|
|
6
45
|
|
|
7
46
|
### Added
|
package/README.md
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
# 0xio Wallet SDK
|
|
2
2
|
|
|
3
|
-
**Version:** 2.4.
|
|
3
|
+
**Version:** 2.4.2
|
|
4
4
|
|
|
5
5
|
Official TypeScript SDK for integrating DApps with 0xio Wallet on Octra Network.
|
|
6
6
|
|
|
7
|
-
## What's New in v2.4.
|
|
7
|
+
## What's New in v2.4.2
|
|
8
|
+
|
|
9
|
+
- **Cross-origin iframe bridge**: Localhost origins now accepted as trusted parents for dev/testing DApp browser scenarios.
|
|
10
|
+
- **Parent origin capture**: `walletReady` signal carries `parentOrigin` for reliable cross-origin reply targeting.
|
|
11
|
+
- **postMessage fix**: Replies to parent frame use captured origin instead of `window.location.origin` (which fails cross-port).
|
|
12
|
+
|
|
13
|
+
## v2.4.1
|
|
14
|
+
|
|
15
|
+
- **No retry on user rejection**: Transactions, contract calls, and sign requests rejected by the user no longer trigger automatic retry. Prevents double confirmation popups.
|
|
16
|
+
- **Security hardening**: postMessage origin validation, removed iframe auto-trust, response ID binding.
|
|
17
|
+
- **Type fixes**: Replaced `NodeJS.Timeout` with `ReturnType<typeof setTimeout>` for browser compatibility.
|
|
18
|
+
|
|
19
|
+
## v2.4.0
|
|
8
20
|
|
|
9
21
|
- **Desktop/Mobile DApp Bridge**: SDK now supports running inside iframes (0xio Desktop) and WebViews (0xio App). Requests are relayed to the parent frame automatically.
|
|
10
22
|
- **Auto Frame Detection**: When `window.parent !== window`, the SDK assumes a wallet bridge is available and marks the wallet as detected.
|
|
@@ -163,11 +175,11 @@ console.log('Signature:', signature);
|
|
|
163
175
|
### Events
|
|
164
176
|
|
|
165
177
|
```typescript
|
|
166
|
-
wallet.on('connect', (event) => console.log('Connected:', event.address));
|
|
178
|
+
wallet.on('connect', (event) => console.log('Connected:', event.data.address));
|
|
167
179
|
wallet.on('disconnect', (event) => console.log('Disconnected'));
|
|
168
|
-
wallet.on('balanceChanged', (event) => console.log('New balance:', event.newBalance.total));
|
|
169
|
-
wallet.on('accountChanged', (event) => console.log('Account changed:', event.newAddress));
|
|
170
|
-
wallet.on('networkChanged', (event) => console.log('Network:', event.newNetwork.name));
|
|
180
|
+
wallet.on('balanceChanged', (event) => console.log('New balance:', event.data.newBalance.total));
|
|
181
|
+
wallet.on('accountChanged', (event) => console.log('Account changed:', event.data.newAddress));
|
|
182
|
+
wallet.on('networkChanged', (event) => console.log('Network:', event.data.newNetwork.name));
|
|
171
183
|
```
|
|
172
184
|
|
|
173
185
|
## Error Handling
|
|
@@ -227,7 +239,7 @@ console.log(mainnet.supportsPrivacy); // true
|
|
|
227
239
|
|
|
228
240
|
| Network | Privacy (FHE) | Explorer |
|
|
229
241
|
|---------|:---:|---|
|
|
230
|
-
| Mainnet Alpha |
|
|
242
|
+
| Mainnet Alpha | Yes | [octrascan.io](https://octrascan.io) |
|
|
231
243
|
| Devnet | Yes | [devnet.octrascan.io](https://devnet.octrascan.io) |
|
|
232
244
|
|
|
233
245
|
### NetworkInfo Type
|
package/dist/index.d.ts
CHANGED
|
@@ -412,7 +412,7 @@ declare class ZeroXIOWallet extends EventEmitter {
|
|
|
412
412
|
* to ensure secure wallet interactions.
|
|
413
413
|
*
|
|
414
414
|
* @module communication
|
|
415
|
-
* @version 2.2
|
|
415
|
+
* @version 2.4.2
|
|
416
416
|
* @license MIT
|
|
417
417
|
*/
|
|
418
418
|
|
|
@@ -452,6 +452,12 @@ declare class ExtensionCommunicator extends EventEmitter {
|
|
|
452
452
|
private extensionDetectionInterval;
|
|
453
453
|
/** Current extension availability state */
|
|
454
454
|
private isExtensionAvailableState;
|
|
455
|
+
/** Message listener reference for cleanup */
|
|
456
|
+
private messageListener;
|
|
457
|
+
/** Trusted parent origins for iframe communication */
|
|
458
|
+
private trustedOrigins;
|
|
459
|
+
/** Parent origin learned from walletReady signal */
|
|
460
|
+
private _parentOrigin;
|
|
455
461
|
/** Maximum number of concurrent pending requests */
|
|
456
462
|
private readonly MAX_CONCURRENT_REQUESTS;
|
|
457
463
|
/** Time window for rate limiting (milliseconds) */
|
|
@@ -465,7 +471,12 @@ declare class ExtensionCommunicator extends EventEmitter {
|
|
|
465
471
|
*
|
|
466
472
|
* @param {boolean} debug - Enable debug logging
|
|
467
473
|
*/
|
|
468
|
-
constructor(debug?: boolean);
|
|
474
|
+
constructor(debug?: boolean, trustedOrigins?: string[]);
|
|
475
|
+
/**
|
|
476
|
+
* Add trusted origins for iframe/bridge communication
|
|
477
|
+
* Call this before connecting if your dApp runs inside a trusted frame
|
|
478
|
+
*/
|
|
479
|
+
setTrustedOrigins(origins: string[]): void;
|
|
469
480
|
/**
|
|
470
481
|
* Initialize communication with the wallet extension
|
|
471
482
|
*
|
|
@@ -614,6 +625,10 @@ declare function getNetworkConfig(networkId?: string): NetworkInfo;
|
|
|
614
625
|
* Get all available networks
|
|
615
626
|
*/
|
|
616
627
|
declare function getAllNetworks(): NetworkInfo[];
|
|
628
|
+
/**
|
|
629
|
+
* Check if network ID is valid
|
|
630
|
+
*/
|
|
631
|
+
declare function isValidNetworkId(networkId: string): boolean;
|
|
617
632
|
|
|
618
633
|
/**
|
|
619
634
|
* Default balance structure
|
|
@@ -623,7 +638,7 @@ declare function createDefaultBalance(total?: number): Balance;
|
|
|
623
638
|
* SDK Configuration constants
|
|
624
639
|
*/
|
|
625
640
|
declare const SDK_CONFIG: {
|
|
626
|
-
readonly version: "2.
|
|
641
|
+
readonly version: "2.4.2";
|
|
627
642
|
readonly defaultNetworkId: "mainnet";
|
|
628
643
|
readonly communicationTimeout: 30000;
|
|
629
644
|
readonly retryAttempts: 3;
|
|
@@ -647,10 +662,6 @@ declare function isValidAddress(address: string): boolean;
|
|
|
647
662
|
* Validate transaction amount
|
|
648
663
|
*/
|
|
649
664
|
declare function isValidAmount(amount: number): boolean;
|
|
650
|
-
/**
|
|
651
|
-
* Validate network ID
|
|
652
|
-
*/
|
|
653
|
-
declare function isValidNetworkId(networkId: string): boolean;
|
|
654
665
|
/**
|
|
655
666
|
* Validate transaction message
|
|
656
667
|
*/
|
|
@@ -750,7 +761,7 @@ declare function createLogger(prefix: string, debug: boolean): {
|
|
|
750
761
|
groupEnd: () => void;
|
|
751
762
|
};
|
|
752
763
|
|
|
753
|
-
declare const SDK_VERSION = "2.
|
|
764
|
+
declare const SDK_VERSION = "2.4.2";
|
|
754
765
|
declare const MIN_EXTENSION_VERSION = "2.0.1";
|
|
755
766
|
declare const MIN_EXTENSION_VERSION_DEVNET = "2.2.1";
|
|
756
767
|
declare const SUPPORTED_EXTENSION_VERSIONS = "^2.0.1";
|
package/dist/index.esm.js
CHANGED
|
@@ -164,16 +164,6 @@ function isValidAmount(amount) {
|
|
|
164
164
|
Number.isFinite(amount) &&
|
|
165
165
|
amount <= Number.MAX_SAFE_INTEGER;
|
|
166
166
|
}
|
|
167
|
-
/**
|
|
168
|
-
* Validate network ID
|
|
169
|
-
*/
|
|
170
|
-
function isValidNetworkId(networkId) {
|
|
171
|
-
if (!networkId || typeof networkId !== 'string') {
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
const validNetworks = ['mainnet', 'devnet', 'custom'];
|
|
175
|
-
return validNetworks.includes(networkId.toLowerCase());
|
|
176
|
-
}
|
|
177
167
|
/**
|
|
178
168
|
* Validate transaction message
|
|
179
169
|
*/
|
|
@@ -317,14 +307,21 @@ function delay(ms) {
|
|
|
317
307
|
*/
|
|
318
308
|
async function retry(operation, maxRetries = 3, baseDelay = 1000) {
|
|
319
309
|
let lastError;
|
|
320
|
-
|
|
310
|
+
// maxRetries = number of retries AFTER the first attempt
|
|
311
|
+
// Total attempts = 1 (initial) + maxRetries
|
|
312
|
+
for (let attempt = 0; attempt < 1 + maxRetries; attempt++) {
|
|
321
313
|
try {
|
|
322
314
|
return await operation();
|
|
323
315
|
}
|
|
324
316
|
catch (error) {
|
|
325
317
|
lastError = error;
|
|
326
|
-
|
|
327
|
-
|
|
318
|
+
// Never retry user rejections — these are intentional
|
|
319
|
+
const msg = lastError.message?.toLowerCase() || '';
|
|
320
|
+
if (msg.includes('rejected') || msg.includes('denied') || msg.includes('cancelled') || msg.includes('user refused')) {
|
|
321
|
+
throw lastError;
|
|
322
|
+
}
|
|
323
|
+
if (attempt >= maxRetries) {
|
|
324
|
+
break; // All retries exhausted
|
|
328
325
|
}
|
|
329
326
|
// Exponential backoff: 1s, 2s, 4s, etc.
|
|
330
327
|
const delayMs = baseDelay * Math.pow(2, attempt);
|
|
@@ -337,12 +334,15 @@ async function retry(operation, maxRetries = 3, baseDelay = 1000) {
|
|
|
337
334
|
* Timeout wrapper for promises
|
|
338
335
|
*/
|
|
339
336
|
function withTimeout(promise, timeoutMs, timeoutMessage = 'Operation timed out') {
|
|
337
|
+
let timer;
|
|
340
338
|
const timeoutPromise = new Promise((_, reject) => {
|
|
341
|
-
setTimeout(() => {
|
|
339
|
+
timer = setTimeout(() => {
|
|
342
340
|
reject(new ZeroXIOWalletError(ErrorCode.NETWORK_ERROR, timeoutMessage));
|
|
343
341
|
}, timeoutMs);
|
|
344
342
|
});
|
|
345
|
-
return Promise.race([promise, timeoutPromise])
|
|
343
|
+
return Promise.race([promise, timeoutPromise]).finally(() => {
|
|
344
|
+
clearTimeout(timer);
|
|
345
|
+
});
|
|
346
346
|
}
|
|
347
347
|
// ===================
|
|
348
348
|
// BROWSER UTILITIES
|
|
@@ -409,7 +409,8 @@ function generateMockData() {
|
|
|
409
409
|
* Create development logger
|
|
410
410
|
*/
|
|
411
411
|
function createLogger(prefix, debug) {
|
|
412
|
-
const isDevelopment = typeof window !== 'undefined' && (
|
|
412
|
+
const isDevelopment = typeof window !== 'undefined' && ((typeof globalThis !== 'undefined' && globalThis.process?.env?.NODE_ENV === 'development') ||
|
|
413
|
+
window.location.hostname === 'localhost' ||
|
|
413
414
|
window.location.hostname === '127.0.0.1' ||
|
|
414
415
|
window.__OCTRA_SDK_DEBUG__);
|
|
415
416
|
// Only enable logging in development mode AND when debug is explicitly enabled
|
|
@@ -467,7 +468,7 @@ function createLogger(prefix, debug) {
|
|
|
467
468
|
* to ensure secure wallet interactions.
|
|
468
469
|
*
|
|
469
470
|
* @module communication
|
|
470
|
-
* @version 2.2
|
|
471
|
+
* @version 2.4.2
|
|
471
472
|
* @license MIT
|
|
472
473
|
*/
|
|
473
474
|
/**
|
|
@@ -499,7 +500,7 @@ class ExtensionCommunicator extends EventEmitter {
|
|
|
499
500
|
*
|
|
500
501
|
* @param {boolean} debug - Enable debug logging
|
|
501
502
|
*/
|
|
502
|
-
constructor(debug = false) {
|
|
503
|
+
constructor(debug = false, trustedOrigins = []) {
|
|
503
504
|
super(debug);
|
|
504
505
|
/** Legacy request counter (deprecated, kept for fallback) */
|
|
505
506
|
this.requestId = 0;
|
|
@@ -511,6 +512,12 @@ class ExtensionCommunicator extends EventEmitter {
|
|
|
511
512
|
this.extensionDetectionInterval = null;
|
|
512
513
|
/** Current extension availability state */
|
|
513
514
|
this.isExtensionAvailableState = false;
|
|
515
|
+
/** Message listener reference for cleanup */
|
|
516
|
+
this.messageListener = null;
|
|
517
|
+
/** Trusted parent origins for iframe communication */
|
|
518
|
+
this.trustedOrigins = [];
|
|
519
|
+
/** Parent origin learned from walletReady signal */
|
|
520
|
+
this._parentOrigin = null;
|
|
514
521
|
// Rate limiting configuration
|
|
515
522
|
/** Maximum number of concurrent pending requests */
|
|
516
523
|
this.MAX_CONCURRENT_REQUESTS = 50;
|
|
@@ -521,9 +528,17 @@ class ExtensionCommunicator extends EventEmitter {
|
|
|
521
528
|
/** Timestamps of recent requests for rate limiting */
|
|
522
529
|
this.requestTimestamps = [];
|
|
523
530
|
this.logger = createLogger('ExtensionCommunicator', debug);
|
|
531
|
+
this.trustedOrigins = trustedOrigins;
|
|
524
532
|
this.setupMessageListener();
|
|
525
533
|
this.startExtensionDetection();
|
|
526
534
|
}
|
|
535
|
+
/**
|
|
536
|
+
* Add trusted origins for iframe/bridge communication
|
|
537
|
+
* Call this before connecting if your dApp runs inside a trusted frame
|
|
538
|
+
*/
|
|
539
|
+
setTrustedOrigins(origins) {
|
|
540
|
+
this.trustedOrigins = origins;
|
|
541
|
+
}
|
|
527
542
|
/**
|
|
528
543
|
* Initialize communication with the wallet extension
|
|
529
544
|
*
|
|
@@ -649,34 +664,52 @@ class ExtensionCommunicator extends EventEmitter {
|
|
|
649
664
|
return; // Not in browser environment
|
|
650
665
|
}
|
|
651
666
|
const allowedOrigin = window.location.origin;
|
|
652
|
-
|
|
653
|
-
|
|
667
|
+
// Store allowed origins for parent frame communication
|
|
668
|
+
// Desktop (Tauri): tauri://localhost or https://tauri.localhost
|
|
669
|
+
// Mobile (Expo): about:blank or custom scheme
|
|
670
|
+
const trustedParentOrigins = new Set([
|
|
671
|
+
allowedOrigin,
|
|
672
|
+
'tauri://localhost',
|
|
673
|
+
'https://tauri.localhost',
|
|
674
|
+
'http://localhost',
|
|
675
|
+
'https://localhost',
|
|
676
|
+
]);
|
|
677
|
+
// Allow dApps to register additional trusted origins
|
|
678
|
+
if (this.trustedOrigins) {
|
|
679
|
+
for (const origin of this.trustedOrigins) {
|
|
680
|
+
trustedParentOrigins.add(origin);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
this.messageListener = (event) => {
|
|
684
|
+
// Strict origin validation
|
|
654
685
|
const isFromSameOrigin = event.origin === allowedOrigin;
|
|
655
|
-
const
|
|
656
|
-
|
|
686
|
+
const isLocalhost = event.origin.startsWith('http://localhost:') || event.origin.startsWith('http://127.0.0.1:');
|
|
687
|
+
const isFromTrustedParent = event.source === window.parent
|
|
688
|
+
&& window.parent !== window
|
|
689
|
+
&& (trustedParentOrigins.has(event.origin) || isLocalhost);
|
|
690
|
+
if (!isFromSameOrigin && !isFromTrustedParent) {
|
|
657
691
|
return;
|
|
658
692
|
}
|
|
659
|
-
//
|
|
693
|
+
// Only accept from same window (extension content script) or trusted parent frame
|
|
660
694
|
if (event.source !== window && event.source !== window.parent) {
|
|
661
695
|
return;
|
|
662
696
|
}
|
|
663
|
-
//
|
|
697
|
+
// Verify message structure
|
|
664
698
|
if (!event.data || event.data.source !== '0xio-sdk-bridge') {
|
|
665
699
|
return;
|
|
666
700
|
}
|
|
667
|
-
//
|
|
701
|
+
// Validate response has a pending request (prevents forged responses)
|
|
668
702
|
if (event.data.response) {
|
|
669
|
-
// Regular request/response
|
|
670
703
|
const response = event.data.response;
|
|
671
|
-
if (response && response.id) {
|
|
704
|
+
if (response && response.id && this.pendingRequests.has(response.id)) {
|
|
672
705
|
this.handleExtensionResponse(response);
|
|
673
706
|
}
|
|
674
707
|
}
|
|
675
708
|
else if (event.data.event) {
|
|
676
|
-
// Event notification from extension
|
|
677
709
|
this.handleExtensionEvent(event.data.event);
|
|
678
710
|
}
|
|
679
|
-
}
|
|
711
|
+
};
|
|
712
|
+
window.addEventListener('message', this.messageListener);
|
|
680
713
|
this.logger.log('Message listener setup complete');
|
|
681
714
|
}
|
|
682
715
|
/**
|
|
@@ -732,9 +765,21 @@ class ExtensionCommunicator extends EventEmitter {
|
|
|
732
765
|
const msg = { source: '0xio-sdk-request', request };
|
|
733
766
|
// Post to same window (extension content script picks it up)
|
|
734
767
|
window.postMessage(msg, window.location.origin);
|
|
735
|
-
// Also post to parent frame if in an iframe (desktop/mobile bridge
|
|
768
|
+
// Also post to parent frame if in an iframe (desktop/mobile bridge)
|
|
736
769
|
if (window.parent !== window) {
|
|
737
|
-
|
|
770
|
+
// Use the parent origin we learned from the walletReady signal, or wildcard
|
|
771
|
+
// Response validation is done by request ID matching, not origin
|
|
772
|
+
const parentOrigin = this._parentOrigin || '*';
|
|
773
|
+
try {
|
|
774
|
+
window.parent.postMessage(msg, parentOrigin);
|
|
775
|
+
}
|
|
776
|
+
catch {
|
|
777
|
+
// Fallback to wildcard if specific origin fails
|
|
778
|
+
try {
|
|
779
|
+
window.parent.postMessage(msg, '*');
|
|
780
|
+
}
|
|
781
|
+
catch { }
|
|
782
|
+
}
|
|
738
783
|
}
|
|
739
784
|
}
|
|
740
785
|
/**
|
|
@@ -830,17 +875,33 @@ class ExtensionCommunicator extends EventEmitter {
|
|
|
830
875
|
this.logger.log('Received octraWalletReady event');
|
|
831
876
|
this.isExtensionAvailableState = true;
|
|
832
877
|
});
|
|
833
|
-
// Listen for desktop/mobile bridge walletReady via postMessage
|
|
878
|
+
// Listen for desktop/mobile bridge walletReady via postMessage (with origin validation)
|
|
834
879
|
window.addEventListener('message', (event) => {
|
|
835
880
|
if (event.data?.source === '0xio-sdk-bridge' && event.data?.event?.type === 'walletReady') {
|
|
836
|
-
|
|
837
|
-
|
|
881
|
+
const isSameOrigin = event.origin === window.location.origin;
|
|
882
|
+
const isLocalhost = event.origin.startsWith('http://localhost:') || event.origin.startsWith('http://127.0.0.1:');
|
|
883
|
+
const isTauri = event.origin === 'tauri://localhost' || event.origin === 'https://tauri.localhost';
|
|
884
|
+
const isTrusted = this.trustedOrigins.includes(event.origin) || isTauri || isLocalhost;
|
|
885
|
+
if (isSameOrigin || isTrusted) {
|
|
886
|
+
this.logger.log('Received walletReady via postMessage from trusted origin');
|
|
887
|
+
this.isExtensionAvailableState = true;
|
|
888
|
+
// Capture parent origin for cross-origin reply targeting
|
|
889
|
+
if (event.data.parentOrigin) {
|
|
890
|
+
this._parentOrigin = event.data.parentOrigin;
|
|
891
|
+
}
|
|
892
|
+
else if (event.origin && event.origin !== 'null') {
|
|
893
|
+
this._parentOrigin = event.origin;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
else {
|
|
897
|
+
this.logger.warn(`Ignored walletReady from untrusted origin: ${event.origin}`);
|
|
898
|
+
}
|
|
838
899
|
}
|
|
839
900
|
});
|
|
840
|
-
//
|
|
901
|
+
// If running inside a frame, do NOT auto-trust the parent.
|
|
902
|
+
// Wait for a walletReady message from a trusted origin instead.
|
|
841
903
|
if (window.parent !== window) {
|
|
842
|
-
this.logger.log('Running inside a frame —
|
|
843
|
-
this.isExtensionAvailableState = true;
|
|
904
|
+
this.logger.log('Running inside a frame — waiting for trusted walletReady signal');
|
|
844
905
|
}
|
|
845
906
|
// Initial check (in case extension was already injected)
|
|
846
907
|
this.checkExtensionAvailability();
|
|
@@ -977,6 +1038,11 @@ class ExtensionCommunicator extends EventEmitter {
|
|
|
977
1038
|
this.pendingRequests.clear();
|
|
978
1039
|
this.isInitialized = false;
|
|
979
1040
|
this.isExtensionAvailableState = false;
|
|
1041
|
+
// Remove message listener to prevent accumulation
|
|
1042
|
+
if (this.messageListener) {
|
|
1043
|
+
window.removeEventListener('message', this.messageListener);
|
|
1044
|
+
this.messageListener = null;
|
|
1045
|
+
}
|
|
980
1046
|
// Call parent cleanup
|
|
981
1047
|
this.removeAllListeners();
|
|
982
1048
|
this.logger.log('Communication cleanup complete');
|
|
@@ -1050,6 +1116,12 @@ function getNetworkConfig(networkId = DEFAULT_NETWORK_ID) {
|
|
|
1050
1116
|
function getAllNetworks() {
|
|
1051
1117
|
return Object.values(NETWORKS);
|
|
1052
1118
|
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Check if network ID is valid
|
|
1121
|
+
*/
|
|
1122
|
+
function isValidNetworkId(networkId) {
|
|
1123
|
+
return networkId in NETWORKS;
|
|
1124
|
+
}
|
|
1053
1125
|
|
|
1054
1126
|
/**
|
|
1055
1127
|
* SDK Configuration
|
|
@@ -1069,7 +1141,7 @@ function createDefaultBalance(total = 0) {
|
|
|
1069
1141
|
* SDK Configuration constants
|
|
1070
1142
|
*/
|
|
1071
1143
|
const SDK_CONFIG = {
|
|
1072
|
-
version: '2.
|
|
1144
|
+
version: '2.4.2',
|
|
1073
1145
|
defaultNetworkId: DEFAULT_NETWORK_ID,
|
|
1074
1146
|
communicationTimeout: 30000, // 30 seconds
|
|
1075
1147
|
retryAttempts: 3,
|
|
@@ -1770,7 +1842,7 @@ var wallet = /*#__PURE__*/Object.freeze({
|
|
|
1770
1842
|
*/
|
|
1771
1843
|
// Main exports
|
|
1772
1844
|
// Version information
|
|
1773
|
-
const SDK_VERSION = '2.
|
|
1845
|
+
const SDK_VERSION = '2.4.2';
|
|
1774
1846
|
const MIN_EXTENSION_VERSION = '2.0.1'; // Mainnet Alpha
|
|
1775
1847
|
const MIN_EXTENSION_VERSION_DEVNET = '2.2.1'; // Devnet (contract calls, privacy)
|
|
1776
1848
|
const SUPPORTED_EXTENSION_VERSIONS = '^2.0.1'; // Supports all versions >= 2.0.1
|
|
@@ -1825,7 +1897,8 @@ if (typeof window !== 'undefined') {
|
|
|
1825
1897
|
window.__OCTRA_SDK_VERSION__ = SDK_VERSION;
|
|
1826
1898
|
window.__ZEROXIO_SDK_VERSION__ = SDK_VERSION;
|
|
1827
1899
|
// Development mode detection
|
|
1828
|
-
const isDevelopment =
|
|
1900
|
+
const isDevelopment = (typeof globalThis !== 'undefined' && globalThis.process?.env?.NODE_ENV === 'development') ||
|
|
1901
|
+
window.location.hostname === 'localhost' ||
|
|
1829
1902
|
window.location.hostname === '127.0.0.1';
|
|
1830
1903
|
if (isDevelopment) {
|
|
1831
1904
|
// Set debug flag but don't automatically log
|
|
@@ -1850,13 +1923,7 @@ if (typeof window !== 'undefined') {
|
|
|
1850
1923
|
debugMode: !!window.__ZEROXIO_SDK_DEBUG__,
|
|
1851
1924
|
environment: isDevelopment ? 'development' : 'production'
|
|
1852
1925
|
}),
|
|
1853
|
-
simulateExtensionEvent
|
|
1854
|
-
window.postMessage({
|
|
1855
|
-
source: '0xio-sdk-bridge',
|
|
1856
|
-
event: { type: eventType, data }
|
|
1857
|
-
}, window.location.origin);
|
|
1858
|
-
console.log('[0xio SDK] Simulated extension event:', eventType, data);
|
|
1859
|
-
},
|
|
1926
|
+
// simulateExtensionEvent removed for security — could be exploited on staging builds
|
|
1860
1927
|
showWelcome: () => {
|
|
1861
1928
|
console.log(`[0xio SDK] Development mode - SDK v${SDK_VERSION}`);
|
|
1862
1929
|
console.log('[0xio SDK] Debug utilities available at window.__ZEROXIO_SDK_UTILS__');
|