@0xio/sdk 2.2.0 → 2.4.0

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 CHANGED
@@ -2,6 +2,57 @@
2
2
 
3
3
  All notable changes to the 0xio Wallet SDK will be documented in this file.
4
4
 
5
+ ## [2.4.0] - 2026-03-24
6
+
7
+ ### Added
8
+ - **Desktop/Mobile DApp Bridge**: SDK now supports running inside iframes (desktop browser) and WebViews (mobile browser). Requests are relayed to the parent frame automatically.
9
+ - **Parent Frame Detection**: `postMessageToExtension()` now posts to both `window` (extension content script) and `window.parent` (iframe bridge) when running inside a frame.
10
+ - **Frame-Aware Message Listener**: `setupMessageListener()` now accepts messages from `window.parent` in addition to same-window, enabling desktop/mobile wallet bridges to communicate with DApps.
11
+ - **walletReady via postMessage**: `startExtensionDetection()` now detects `walletReady` events sent via `postMessage` from parent frames, in addition to DOM events and global signals.
12
+ - **Auto Frame Detection**: When `window.parent !== window`, the SDK assumes a wallet bridge is available and marks the wallet as detected.
13
+
14
+ ### Security
15
+ - Parent frame messages are only accepted from `window.parent`, not arbitrary origins
16
+ - Extension content script messages continue to use strict origin validation
17
+
18
+ ### Compatibility
19
+ - Fully backward compatible — extension-based DApps work unchanged
20
+ - Desktop (0xio Desktop): DApps loaded in BrowserScreen iframe now auto-connect
21
+ - Mobile (0xio App): DApps loaded in WebView browser now auto-connect via existing bridge
22
+ - Mainnet Alpha: Extension v2.0.1+
23
+ - Devnet: Extension v2.2.1+
24
+
25
+ ---
26
+
27
+ ## [2.3.0] - 2026-03-10
28
+
29
+ ### Added
30
+ - **Smart Contract Interaction**: New `callContract()` method for state-changing contract calls. The extension builds, signs, and submits via `octra_submit` — works on both mainnet and devnet.
31
+ - **Contract View Calls**: New `contractCallView()` method for read-only contract queries. No wallet unlock or approval popup required.
32
+ - **Contract Storage**: New `getContractStorage()` method to read contract storage by key directly from the chain.
33
+ - **New Types**: `ContractCallData`, `ContractViewCallData`, and `ContractParams` for type-safe contract interaction.
34
+ - **Devnet Version Constant**: New `MIN_EXTENSION_VERSION_DEVNET` export (`'2.2.1'`) for programmatic devnet compatibility checks.
35
+
36
+ ### Improved
37
+ - **Type Safety**: Replaced `any` with `ContractParams` (`ReadonlyArray<string | number | boolean>`) in `ContractCallData.params` and `ContractViewCallData.params`.
38
+ - **Event Handler Typing**: Three internal event handlers (`handleAccountChanged`, `handleNetworkChanged`, `handleBalanceChanged`) now use properly typed parameters instead of `any`.
39
+ - **Error Consistency**: `getNetworkConfig()` now throws `ZeroXIOWalletError` with `ErrorCode.NETWORK_ERROR` instead of a generic `Error`.
40
+ - **UMD Global Name**: Fixed UMD build global from `OctraWalletSDK` to `ZeroXIOWalletSDK` to match SDK branding.
41
+
42
+ ### Security
43
+ - **Fixed wildcard origin**: `simulateExtensionEvent` now uses `window.location.origin` instead of `'*'` wildcard, preventing cross-origin message injection.
44
+ - **signMessage length limit**: Added 10,000 character max to prevent memory exhaustion from oversized signing requests.
45
+ - **Narrowed dev detection**: Removed overly broad `hostname.includes('dev')` check that would enable debug mode on any domain containing "dev" (e.g., `developer.mozilla.org`). Dev mode now only activates on `localhost`, `127.0.0.1`, or when `NODE_ENV=development`.
46
+
47
+ ### Fixed
48
+ - **Message validation**: Increased `isValidMessage()` limit from 280 to 100,000 characters. The 280-char limit was blocking contract call parameters which are serialized JSON and can be large.
49
+
50
+ ### Compatibility
51
+ - **Mainnet Alpha**: Requires 0xio Wallet Extension v2.0.1 or higher
52
+ - **Devnet**: Requires 0xio Wallet Extension v2.2.1 or higher (contract calls, privacy features)
53
+
54
+ ---
55
+
5
56
  ## [2.2.0] - 2026-03-08
6
57
 
7
58
  ### Added
@@ -18,7 +69,7 @@ All notable changes to the 0xio Wallet SDK will be documented in this file.
18
69
 
19
70
  | Network | RPC | Explorer | Privacy | Testnet |
20
71
  |---------|-----|----------|---------|---------|
21
- | Mainnet Alpha | `https://octra.network` | `https://octrascan.io` | No | No |
72
+ | Mainnet | `http://46.101.86.250:8080` | `https://lite.octrascan.io` | Yes | No |
22
73
  | Devnet | `http://165.227.225.79:8080` | `https://devnet.octrascan.io` | Yes | Yes |
23
74
  | Custom | User-defined | User-defined | No | No |
24
75
 
package/README.md CHANGED
@@ -1,15 +1,16 @@
1
1
  # 0xio Wallet SDK
2
2
 
3
- **Version:** 2.2.0
3
+ **Version:** 2.4.0
4
4
 
5
5
  Official TypeScript SDK for integrating DApps with 0xio Wallet on Octra Network.
6
6
 
7
- ## What's New in v2.2.0
7
+ ## What's New in v2.4.0
8
8
 
9
- - **Devnet Support**: Built-in Octra Devnet network configuration DApps can now target devnet out of the box
10
- - **Expanded `NetworkInfo`**: New fields `explorerAddressUrl`, `indexerUrl`, and `supportsPrivacy` for richer network metadata
11
- - **Privacy Detection**: `supportsPrivacy` flag lets DApps check if a network supports FHE/encrypted balances
12
- - **Explorer URL Fix**: Mainnet explorer URLs now include trailing `/` for correct link generation
9
+ - **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
+ - **Auto Frame Detection**: When `window.parent !== window`, the SDK assumes a wallet bridge is available and marks the wallet as detected.
11
+ - **Frame-Aware Messaging**: `postMessageToExtension()` posts to both `window` and `window.parent` when running inside a frame; `setupMessageListener()` accepts messages from `window.parent`.
12
+ - **walletReady via postMessage**: Extension detection now recognizes `walletReady` events from parent frames.
13
+ - **Fully backward compatible** — extension-based DApps work unchanged with no code changes needed.
13
14
 
14
15
  ## Installation
15
16
 
@@ -103,6 +104,42 @@ interface TransactionResult {
103
104
  }
104
105
  ```
105
106
 
107
+ ### Smart Contracts
108
+
109
+ #### `wallet.callContract(data: ContractCallData): Promise<TransactionResult>`
110
+ Execute a state-changing contract call. The extension signs and submits via `octra_submit`.
111
+
112
+ ```typescript
113
+ const result = await wallet.callContract({
114
+ contract: 'oct26Lia...', // Contract address
115
+ method: 'swap', // AML method name
116
+ params: [100, true, 90], // Method arguments (flat, not array-wrapped)
117
+ amount: '0', // Native OCT to send (optional, default '0')
118
+ ou: '10000', // Operational units (optional, default '10000')
119
+ });
120
+ console.log('TX Hash:', result.txHash);
121
+ ```
122
+
123
+ #### `wallet.contractCallView(data: ContractViewCallData): Promise<any>`
124
+ Read-only contract query. No signing, no approval popup, no wallet unlock required.
125
+
126
+ ```typescript
127
+ const price = await wallet.contractCallView({
128
+ contract: 'oct26Lia...',
129
+ method: 'get_active_price',
130
+ params: [],
131
+ });
132
+ console.log('Price:', price);
133
+ ```
134
+
135
+ #### `wallet.getContractStorage(contract: string, key: string): Promise<string | null>`
136
+ Read contract storage by key.
137
+
138
+ ```typescript
139
+ const value = await wallet.getContractStorage('oct26Lia...', 'total_supply');
140
+ console.log('Total supply:', value);
141
+ ```
142
+
106
143
  ### Message Signing
107
144
 
108
145
  #### `wallet.signMessage(message: string): Promise<string>`
@@ -184,8 +221,8 @@ console.log(devnet.isTestnet); // true
184
221
 
185
222
  // Get mainnet config
186
223
  const mainnet = getNetworkConfig('mainnet');
187
- console.log(mainnet.rpcUrl); // https://octra.network
188
- console.log(mainnet.supportsPrivacy); // false
224
+ console.log(mainnet.rpcUrl); // http://46.101.86.250:8080
225
+ console.log(mainnet.supportsPrivacy); // true
189
226
  ```
190
227
 
191
228
  | Network | Privacy (FHE) | Explorer |
@@ -209,9 +246,22 @@ interface NetworkInfo {
209
246
  }
210
247
  ```
211
248
 
249
+ ## Desktop & Mobile Support
250
+
251
+ The SDK automatically detects when your DApp is running inside:
252
+
253
+ - **0xio Desktop's built-in browser** — Your DApp is loaded in an iframe. The SDK detects `window.parent !== window` and relays all requests to the desktop wallet via the iframe bridge.
254
+ - **0xio App's built-in browser** — Your DApp is loaded in a WebView. The SDK communicates with the mobile wallet via the existing WebView bridge.
255
+ - **0xio Wallet Extension** — Standard browser extension communication (unchanged).
256
+
257
+ No code changes are needed for DApp developers. Just use the SDK as normal and it will auto-detect the environment and choose the correct transport.
258
+
212
259
  ## Requirements
213
260
 
214
- - 0xio Wallet Extension v2.0.1 or higher
261
+ - 0xio Wallet Extension v2.0.1 or higher (Mainnet Alpha)
262
+ - 0xio Wallet Extension v2.2.1 or higher (Devnet — required for contract calls and privacy features)
263
+ - 0xio Desktop v1.0+ (for iframe bridge support)
264
+ - 0xio App v1.0+ (for WebView bridge support)
215
265
  - Modern browser (Chrome, Firefox, Edge, Brave)
216
266
 
217
267
  ## Documentation
package/dist/index.d.ts CHANGED
@@ -30,6 +30,30 @@ interface TransactionData {
30
30
  readonly feeLevel?: 1 | 3;
31
31
  readonly isPrivate?: boolean;
32
32
  }
33
+ /** Flat array of AML-compatible primitive values for contract method arguments. */
34
+ type ContractParams = ReadonlyArray<string | number | boolean>;
35
+ interface ContractCallData {
36
+ /** Contract address (oct-prefixed, 47 chars) */
37
+ readonly contract: string;
38
+ /** Contract method name (e.g. 'swap', 'approve') */
39
+ readonly method: string;
40
+ /** Method arguments — flat primitives, NOT array-wrapped: `[amount, flag]` not `[[amount, flag]]` */
41
+ readonly params: ContractParams;
42
+ /** Native OCT to send with call (human-readable for sendTransaction, micro-units for callContract) */
43
+ readonly amount?: string | number;
44
+ /** Operation units / gas limit (default: 10000) */
45
+ readonly ou?: string | number;
46
+ }
47
+ interface ContractViewCallData {
48
+ /** Contract address (oct-prefixed, 47 chars) */
49
+ readonly contract: string;
50
+ /** Contract method name (e.g. 'balance_of', 'get_active_bin') */
51
+ readonly method: string;
52
+ /** Method arguments — flat primitives, NOT array-wrapped */
53
+ readonly params: ContractParams;
54
+ /** Caller address for view context (defaults to connected wallet) */
55
+ readonly caller?: string;
56
+ }
33
57
  interface SignedTransaction {
34
58
  readonly from: string;
35
59
  readonly to_: string;
@@ -292,6 +316,20 @@ declare class ZeroXIOWallet extends EventEmitter {
292
316
  * Send transaction
293
317
  */
294
318
  sendTransaction(txData: TransactionData): Promise<TransactionResult>;
319
+ /**
320
+ * Call a smart contract method (state-changing).
321
+ * The extension builds, signs, and submits the transaction via octra_submit.
322
+ */
323
+ callContract(callData: ContractCallData): Promise<TransactionResult>;
324
+ /**
325
+ * Read-only contract view call (no signing, no approval popup).
326
+ * Use this to query contract state without submitting a transaction.
327
+ */
328
+ contractCallView(viewData: ContractViewCallData): Promise<any>;
329
+ /**
330
+ * Read contract storage by key.
331
+ */
332
+ getContractStorage(contract: string, key: string): Promise<string | null>;
295
333
  /**
296
334
  * Get transaction history
297
335
  */
@@ -585,7 +623,7 @@ declare function createDefaultBalance(total?: number): Balance;
585
623
  * SDK Configuration constants
586
624
  */
587
625
  declare const SDK_CONFIG: {
588
- readonly version: "2.2.0";
626
+ readonly version: "2.3.0";
589
627
  readonly defaultNetworkId: "mainnet";
590
628
  readonly communicationTimeout: 30000;
591
629
  readonly retryAttempts: 3;
@@ -712,8 +750,9 @@ declare function createLogger(prefix: string, debug: boolean): {
712
750
  groupEnd: () => void;
713
751
  };
714
752
 
715
- declare const SDK_VERSION = "2.2.0";
753
+ declare const SDK_VERSION = "2.3.0";
716
754
  declare const MIN_EXTENSION_VERSION = "2.0.1";
755
+ declare const MIN_EXTENSION_VERSION_DEVNET = "2.2.1";
717
756
  declare const SUPPORTED_EXTENSION_VERSIONS = "^2.0.1";
718
757
  declare function createZeroXIOWallet(config: {
719
758
  appName: string;
@@ -728,5 +767,5 @@ declare function checkSDKCompatibility(): {
728
767
  recommendations: string[];
729
768
  };
730
769
 
731
- export { DEFAULT_NETWORK_ID, ErrorCode, EventEmitter, ExtensionCommunicator, MIN_EXTENSION_VERSION, NETWORKS, SDK_CONFIG, SDK_VERSION, SUPPORTED_EXTENSION_VERSIONS, ZeroXIOWallet, ZeroXIOWalletError, checkBrowserSupport, checkSDKCompatibility, createDefaultBalance, createErrorMessage, createLogger, createOctraWallet, createZeroXIOWallet, delay, formatAddress, formatOCT, formatTimestamp, formatTxHash, formatOCT as formatZeroXIO, fromMicroOCT, fromMicroOCT as fromMicroZeroXIO, generateMockData, getAllNetworks, getDefaultNetwork, getNetworkConfig, isBrowser, isErrorType, isValidAddress, isValidAmount, isValidFeeLevel, isValidMessage, isValidNetworkId, retry, toMicroOCT, toMicroOCT as toMicroZeroXIO, withTimeout };
732
- export type { AccountChangedEvent, Balance, BalanceChangedEvent, ConnectEvent, ConnectOptions, ConnectionInfo, DisconnectEvent, ErrorEvent, ExtensionRequest, ExtensionResponse, NetworkChangedEvent, NetworkInfo, PendingPrivateTransfer, Permission, PrivateBalanceInfo, PrivateTransferData, SDKConfig, SignedTransaction, Transaction, TransactionConfirmedEvent, TransactionData, TransactionFinality, TransactionHistory, TransactionResult, WalletAddress, WalletEvent, WalletEventType };
770
+ export { DEFAULT_NETWORK_ID, ErrorCode, EventEmitter, ExtensionCommunicator, MIN_EXTENSION_VERSION, MIN_EXTENSION_VERSION_DEVNET, NETWORKS, SDK_CONFIG, SDK_VERSION, SUPPORTED_EXTENSION_VERSIONS, ZeroXIOWallet, ZeroXIOWalletError, checkBrowserSupport, checkSDKCompatibility, createDefaultBalance, createErrorMessage, createLogger, createOctraWallet, createZeroXIOWallet, delay, formatAddress, formatOCT, formatTimestamp, formatTxHash, formatOCT as formatZeroXIO, fromMicroOCT, fromMicroOCT as fromMicroZeroXIO, generateMockData, getAllNetworks, getDefaultNetwork, getNetworkConfig, isBrowser, isErrorType, isValidAddress, isValidAmount, isValidFeeLevel, isValidMessage, isValidNetworkId, retry, toMicroOCT, toMicroOCT as toMicroZeroXIO, withTimeout };
771
+ export type { AccountChangedEvent, Balance, BalanceChangedEvent, ConnectEvent, ConnectOptions, ConnectionInfo, ContractCallData, ContractParams, ContractViewCallData, DisconnectEvent, ErrorEvent, ExtensionRequest, ExtensionResponse, NetworkChangedEvent, NetworkInfo, PendingPrivateTransfer, Permission, PrivateBalanceInfo, PrivateTransferData, SDKConfig, SignedTransaction, Transaction, TransactionConfirmedEvent, TransactionData, TransactionFinality, TransactionHistory, TransactionResult, WalletAddress, WalletEvent, WalletEventType };
package/dist/index.esm.js CHANGED
@@ -184,8 +184,8 @@ function isValidMessage(message) {
184
184
  if (typeof message !== 'string') {
185
185
  return false;
186
186
  }
187
- // Check length (adjust based on network limits)
188
- return message.length <= 280;
187
+ // 100KB limit contract call params can be large (serialized JSON)
188
+ return message.length <= 100000;
189
189
  }
190
190
  /**
191
191
  * Validate fee level
@@ -394,12 +394,12 @@ function generateMockData() {
394
394
  },
395
395
  networkInfo: {
396
396
  id: 'mainnet',
397
- name: 'Octra Mainnet Alpha',
398
- rpcUrl: 'https://octra.network',
399
- explorerUrl: 'https://octrascan.io/transactions/',
400
- explorerAddressUrl: 'https://octrascan.io/addresses/',
401
- indexerUrl: 'https://network.octrascan.com',
402
- supportsPrivacy: false,
397
+ name: 'Octra Mainnet',
398
+ rpcUrl: 'http://46.101.86.250:8080',
399
+ explorerUrl: 'https://lite.octrascan.io/tx.html?hash=',
400
+ explorerAddressUrl: 'https://lite.octrascan.io/address.html?addr=',
401
+ indexerUrl: 'https://lite.octrascan.io',
402
+ supportsPrivacy: true,
403
403
  color: '#f59e0b',
404
404
  isTestnet: false
405
405
  }
@@ -411,41 +411,44 @@ function generateMockData() {
411
411
  function createLogger(prefix, debug) {
412
412
  const isDevelopment = typeof window !== 'undefined' && (window.location.hostname === 'localhost' ||
413
413
  window.location.hostname === '127.0.0.1' ||
414
- window.location.hostname.includes('dev') ||
415
414
  window.__OCTRA_SDK_DEBUG__);
416
415
  // Only enable logging in development mode AND when debug is explicitly enabled
417
416
  const shouldLog = debug && isDevelopment;
417
+ const ts = () => {
418
+ const d = new Date();
419
+ return `${d.toTimeString().slice(0, 8)}.${String(d.getMilliseconds()).padStart(3, '0')}`;
420
+ };
418
421
  return {
419
422
  log: (...args) => {
420
423
  if (shouldLog) {
421
- console.log(`[${prefix}]`, ...args);
424
+ console.log(`[${ts()}][${prefix}]`, ...args);
422
425
  }
423
426
  },
424
427
  warn: (...args) => {
425
428
  if (shouldLog) {
426
- console.warn(`[${prefix}]`, ...args);
429
+ console.warn(`[${ts()}][${prefix}]`, ...args);
427
430
  }
428
431
  },
429
432
  error: (...args) => {
430
433
  // Always show errors in development, even without debug flag
431
434
  if (isDevelopment) {
432
- console.error(`[${prefix}]`, ...args);
435
+ console.error(`[${ts()}][${prefix}]`, ...args);
433
436
  }
434
437
  },
435
438
  debug: (...args) => {
436
439
  if (shouldLog) {
437
- console.debug(`[${prefix}]`, ...args);
440
+ console.debug(`[${ts()}][${prefix}]`, ...args);
438
441
  }
439
442
  },
440
443
  table: (data) => {
441
444
  if (shouldLog) {
442
- console.log(`[${prefix}] Table data:`);
445
+ console.log(`[${ts()}][${prefix}] Table data:`);
443
446
  console.table(data);
444
447
  }
445
448
  },
446
449
  group: (label) => {
447
450
  if (shouldLog) {
448
- console.group(`[${prefix}] ${label}`);
451
+ console.group(`[${ts()}][${prefix}] ${label}`);
449
452
  }
450
453
  },
451
454
  groupEnd: () => {
@@ -647,13 +650,14 @@ class ExtensionCommunicator extends EventEmitter {
647
650
  }
648
651
  const allowedOrigin = window.location.origin;
649
652
  window.addEventListener('message', (event) => {
650
- // SECURITY: Validate origin first - critical security check
651
- if (event.origin !== allowedOrigin) {
652
- this.logger.warn('Blocked message from untrusted origin:', event.origin);
653
+ // Accept messages from same origin OR from parent frame (desktop/mobile bridge)
654
+ const isFromSameOrigin = event.origin === allowedOrigin;
655
+ const isFromParent = event.source === window.parent && window.parent !== window;
656
+ if (!isFromSameOrigin && !isFromParent) {
653
657
  return;
654
658
  }
655
- // Only accept messages from same window
656
- if (event.source !== window) {
659
+ // Accept from same window or parent frame
660
+ if (event.source !== window && event.source !== window.parent) {
657
661
  return;
658
662
  }
659
663
  // Check if it's a 0xio SDK response
@@ -725,12 +729,13 @@ class ExtensionCommunicator extends EventEmitter {
725
729
  * Post message to extension via content script
726
730
  */
727
731
  postMessageToExtension(request) {
728
- // SECURITY: Use specific origin instead of wildcard
729
- const targetOrigin = window.location.origin;
730
- window.postMessage({
731
- source: '0xio-sdk-request',
732
- request
733
- }, targetOrigin);
732
+ const msg = { source: '0xio-sdk-request', request };
733
+ // Post to same window (extension content script picks it up)
734
+ window.postMessage(msg, window.location.origin);
735
+ // Also post to parent frame if in an iframe (desktop/mobile bridge picks it up)
736
+ if (window.parent !== window) {
737
+ window.parent.postMessage(msg, '*');
738
+ }
734
739
  }
735
740
  /**
736
741
  * Check if we're in a context that can communicate with extension
@@ -825,6 +830,18 @@ class ExtensionCommunicator extends EventEmitter {
825
830
  this.logger.log('Received octraWalletReady event');
826
831
  this.isExtensionAvailableState = true;
827
832
  });
833
+ // Listen for desktop/mobile bridge walletReady via postMessage
834
+ window.addEventListener('message', (event) => {
835
+ if (event.data?.source === '0xio-sdk-bridge' && event.data?.event?.type === 'walletReady') {
836
+ this.logger.log('Received walletReady via postMessage (desktop/mobile bridge)');
837
+ this.isExtensionAvailableState = true;
838
+ }
839
+ });
840
+ // Also detect if running inside a frame with a wallet bridge parent
841
+ if (window.parent !== window) {
842
+ this.logger.log('Running inside a frame — checking for parent wallet bridge');
843
+ this.isExtensionAvailableState = true;
844
+ }
828
845
  // Initial check (in case extension was already injected)
829
846
  this.checkExtensionAvailability();
830
847
  // Set up periodic checks as fallback
@@ -984,12 +1001,12 @@ class ExtensionCommunicator extends EventEmitter {
984
1001
  const NETWORKS = {
985
1002
  'mainnet': {
986
1003
  id: 'mainnet',
987
- name: 'Octra Mainnet Alpha',
988
- rpcUrl: 'https://octra.network',
989
- explorerUrl: 'https://octrascan.io/transactions/',
990
- explorerAddressUrl: 'https://octrascan.io/addresses/',
991
- indexerUrl: 'https://network.octrascan.com',
992
- supportsPrivacy: false,
1004
+ name: 'Octra Mainnet',
1005
+ rpcUrl: 'http://46.101.86.250:8080',
1006
+ explorerUrl: 'https://lite.octrascan.io/tx.html?hash=',
1007
+ explorerAddressUrl: 'https://lite.octrascan.io/address.html?addr=',
1008
+ indexerUrl: 'https://lite.octrascan.io',
1009
+ supportsPrivacy: true,
993
1010
  color: '#f59e0b',
994
1011
  isTestnet: false
995
1012
  },
@@ -1023,7 +1040,7 @@ const DEFAULT_NETWORK_ID = 'mainnet';
1023
1040
  function getNetworkConfig(networkId = DEFAULT_NETWORK_ID) {
1024
1041
  const network = NETWORKS[networkId];
1025
1042
  if (!network) {
1026
- throw new Error(`Unknown network ID: ${networkId}`);
1043
+ throw new ZeroXIOWalletError(ErrorCode.NETWORK_ERROR, `Unknown network ID: ${networkId}`);
1027
1044
  }
1028
1045
  return network;
1029
1046
  }
@@ -1052,7 +1069,7 @@ function createDefaultBalance(total = 0) {
1052
1069
  * SDK Configuration constants
1053
1070
  */
1054
1071
  const SDK_CONFIG = {
1055
- version: '2.2.0',
1072
+ version: '2.3.0',
1056
1073
  defaultNetworkId: DEFAULT_NETWORK_ID,
1057
1074
  communicationTimeout: 30000, // 30 seconds
1058
1075
  retryAttempts: 3,
@@ -1360,6 +1377,78 @@ class ZeroXIOWallet extends EventEmitter {
1360
1377
  throw new ZeroXIOWalletError(ErrorCode.TRANSACTION_FAILED, 'Failed to send transaction', error);
1361
1378
  }
1362
1379
  }
1380
+ /**
1381
+ * Call a smart contract method (state-changing).
1382
+ * The extension builds, signs, and submits the transaction via octra_submit.
1383
+ */
1384
+ async callContract(callData) {
1385
+ this.ensureConnected();
1386
+ try {
1387
+ this.logger.log('Calling contract:', callData);
1388
+ const result = await this.communicator.sendRequest('call_contract', {
1389
+ contract: callData.contract,
1390
+ method: callData.method,
1391
+ params: callData.params,
1392
+ amount: callData.amount != null ? String(callData.amount) : '0',
1393
+ ou: callData.ou != null ? String(callData.ou) : '10000',
1394
+ });
1395
+ this.logger.log('Contract call result:', result);
1396
+ return result;
1397
+ }
1398
+ catch (error) {
1399
+ this.logger.error('Contract call failed:', error);
1400
+ if (error instanceof ZeroXIOWalletError) {
1401
+ throw error;
1402
+ }
1403
+ throw new ZeroXIOWalletError(ErrorCode.TRANSACTION_FAILED, 'Failed to call contract', error);
1404
+ }
1405
+ }
1406
+ /**
1407
+ * Read-only contract view call (no signing, no approval popup).
1408
+ * Use this to query contract state without submitting a transaction.
1409
+ */
1410
+ async contractCallView(viewData) {
1411
+ this.ensureInitialized();
1412
+ try {
1413
+ this.logger.log('Contract view call:', viewData);
1414
+ const result = await this.communicator.sendRequest('contract_call_view', {
1415
+ contract: viewData.contract,
1416
+ method: viewData.method,
1417
+ params: viewData.params,
1418
+ caller: viewData.caller || this.getAddress() || '',
1419
+ });
1420
+ this.logger.log('Contract view result:', result);
1421
+ return result;
1422
+ }
1423
+ catch (error) {
1424
+ this.logger.error('Contract view call failed:', error);
1425
+ if (error instanceof ZeroXIOWalletError) {
1426
+ throw error;
1427
+ }
1428
+ throw new ZeroXIOWalletError(ErrorCode.NETWORK_ERROR, 'Failed to call contract view', error);
1429
+ }
1430
+ }
1431
+ /**
1432
+ * Read contract storage by key.
1433
+ */
1434
+ async getContractStorage(contract, key) {
1435
+ this.ensureInitialized();
1436
+ try {
1437
+ this.logger.log('Getting contract storage:', { contract, key });
1438
+ const result = await this.communicator.sendRequest('get_contract_storage', {
1439
+ contract,
1440
+ key,
1441
+ });
1442
+ return result;
1443
+ }
1444
+ catch (error) {
1445
+ this.logger.error('Get contract storage failed:', error);
1446
+ if (error instanceof ZeroXIOWalletError) {
1447
+ throw error;
1448
+ }
1449
+ throw new ZeroXIOWalletError(ErrorCode.NETWORK_ERROR, 'Failed to get contract storage', error);
1450
+ }
1451
+ }
1363
1452
  /**
1364
1453
  * Get transaction history
1365
1454
  */
@@ -1500,6 +1589,9 @@ class ZeroXIOWallet extends EventEmitter {
1500
1589
  if (!message || typeof message !== 'string') {
1501
1590
  throw new ZeroXIOWalletError(ErrorCode.SIGNATURE_FAILED, 'Message must be a non-empty string');
1502
1591
  }
1592
+ if (message.length > 10000) {
1593
+ throw new ZeroXIOWalletError(ErrorCode.SIGNATURE_FAILED, 'Message too long (max 10,000 characters)');
1594
+ }
1503
1595
  try {
1504
1596
  this.logger.log('Requesting message signature for:', message.substring(0, 100) + (message.length > 100 ? '...' : ''));
1505
1597
  const result = await this.communicator.sendRequest('signMessage', { message });
@@ -1564,7 +1656,7 @@ class ZeroXIOWallet extends EventEmitter {
1564
1656
  const accountChangedEvent = {
1565
1657
  previousAddress,
1566
1658
  newAddress: data.address,
1567
- balance: data.balance
1659
+ balance: data.balance ?? this.connectionInfo.balance
1568
1660
  };
1569
1661
  this.emit('accountChanged', accountChangedEvent);
1570
1662
  this.logger.log('Account changed:', accountChangedEvent);
@@ -1678,8 +1770,9 @@ var wallet = /*#__PURE__*/Object.freeze({
1678
1770
  */
1679
1771
  // Main exports
1680
1772
  // Version information
1681
- const SDK_VERSION = '2.2.0';
1682
- const MIN_EXTENSION_VERSION = '2.0.1';
1773
+ const SDK_VERSION = '2.3.0';
1774
+ const MIN_EXTENSION_VERSION = '2.0.1'; // Mainnet Alpha
1775
+ const MIN_EXTENSION_VERSION_DEVNET = '2.2.1'; // Devnet (contract calls, privacy)
1683
1776
  const SUPPORTED_EXTENSION_VERSIONS = '^2.0.1'; // Supports all versions >= 2.0.1
1684
1777
  // Quick setup function for simple use cases
1685
1778
  async function createZeroXIOWallet(config) {
@@ -1733,8 +1826,7 @@ if (typeof window !== 'undefined') {
1733
1826
  window.__ZEROXIO_SDK_VERSION__ = SDK_VERSION;
1734
1827
  // Development mode detection
1735
1828
  const isDevelopment = window.location.hostname === 'localhost' ||
1736
- window.location.hostname === '127.0.0.1' ||
1737
- window.location.hostname.includes('dev');
1829
+ window.location.hostname === '127.0.0.1';
1738
1830
  if (isDevelopment) {
1739
1831
  // Set debug flag but don't automatically log
1740
1832
  window.__OCTRA_SDK_DEBUG__ = true;
@@ -1762,7 +1854,7 @@ if (typeof window !== 'undefined') {
1762
1854
  window.postMessage({
1763
1855
  source: '0xio-sdk-bridge',
1764
1856
  event: { type: eventType, data }
1765
- }, '*');
1857
+ }, window.location.origin);
1766
1858
  console.log('[0xio SDK] Simulated extension event:', eventType, data);
1767
1859
  },
1768
1860
  showWelcome: () => {
@@ -1779,5 +1871,5 @@ if (typeof window !== 'undefined') {
1779
1871
  }
1780
1872
  }
1781
1873
 
1782
- export { DEFAULT_NETWORK_ID, ErrorCode, EventEmitter, ExtensionCommunicator, MIN_EXTENSION_VERSION, NETWORKS, SDK_CONFIG, SDK_VERSION, SUPPORTED_EXTENSION_VERSIONS, ZeroXIOWallet, ZeroXIOWalletError, checkBrowserSupport, checkSDKCompatibility, createDefaultBalance, createErrorMessage, createLogger, createOctraWallet, createZeroXIOWallet, delay, formatAddress, formatOCT, formatTimestamp, formatTxHash, formatOCT as formatZeroXIO, fromMicroOCT, fromMicroOCT as fromMicroZeroXIO, generateMockData, getAllNetworks, getDefaultNetwork, getNetworkConfig, isBrowser, isErrorType, isValidAddress, isValidAmount, isValidFeeLevel, isValidMessage, isValidNetworkId, retry, toMicroOCT, toMicroOCT as toMicroZeroXIO, withTimeout };
1874
+ export { DEFAULT_NETWORK_ID, ErrorCode, EventEmitter, ExtensionCommunicator, MIN_EXTENSION_VERSION, MIN_EXTENSION_VERSION_DEVNET, NETWORKS, SDK_CONFIG, SDK_VERSION, SUPPORTED_EXTENSION_VERSIONS, ZeroXIOWallet, ZeroXIOWalletError, checkBrowserSupport, checkSDKCompatibility, createDefaultBalance, createErrorMessage, createLogger, createOctraWallet, createZeroXIOWallet, delay, formatAddress, formatOCT, formatTimestamp, formatTxHash, formatOCT as formatZeroXIO, fromMicroOCT, fromMicroOCT as fromMicroZeroXIO, generateMockData, getAllNetworks, getDefaultNetwork, getNetworkConfig, isBrowser, isErrorType, isValidAddress, isValidAmount, isValidFeeLevel, isValidMessage, isValidNetworkId, retry, toMicroOCT, toMicroOCT as toMicroZeroXIO, withTimeout };
1783
1875
  //# sourceMappingURL=index.esm.js.map