@0xsequence/provider 0.34.1 → 0.35.3

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.
@@ -1,7 +1,7 @@
1
1
  import { EventEmitter2 } from 'eventemitter2';
2
2
  import { logger, packMessageData, sanitizeNumberString, sanitizeHost, sanitizeAlphanumeric, base64EncodeObject, base64DecodeObject, shallowCopy, resolveProperties, encodeMessageDigest, encodeTypedDataDigest } from '@0xsequence/utils';
3
3
  import { ethers, BigNumber } from 'ethers';
4
- import { addressOf } from '@0xsequence/config';
4
+ import { addressOf, isConfigEqual } from '@0xsequence/config';
5
5
  import { isValidSignature as isValidSignature$1, recoverConfig, Signer, resolveArrayProperties } from '@0xsequence/wallet';
6
6
  import { isSignedTransactions } from '@0xsequence/transactions';
7
7
  import { signAuthorization } from '@0xsequence/auth';
@@ -490,7 +490,54 @@ const recoverWalletConfig = async (address, digest, signature, chainId, walletCo
490
490
 
491
491
  return config;
492
492
  };
493
- const isBrowserExtension = () => window.location.protocol === 'chrome-extension:' || window.location.protocol === 'moz-extension:'; // window.localstorage helper
493
+ const isBrowserExtension = () => window.location.protocol === 'chrome-extension:' || window.location.protocol === 'moz-extension:';
494
+ /**
495
+ * Returns the status of a signer's wallet on given chain by checking wallet deployment and config status
496
+ *
497
+ * @param {Signer} signer
498
+ * @param {number} chainId
499
+ * @return {Promise<boolean>} Promise that returns true if the wallet is up to date, false otherwise
500
+ */
501
+
502
+ const isWalletUpToDate = async (signer, chainId) => {
503
+ var _walletState$find;
504
+
505
+ const walletState = await signer.getWalletState();
506
+ const networks = await signer.getNetworks();
507
+ const walletStateForRequiredChain = walletState.find(state => state.chainId === chainId);
508
+
509
+ if (!walletStateForRequiredChain) {
510
+ throw new Error(`WalletRequestHandler: could not find wallet state for chainId ${chainId}`);
511
+ }
512
+
513
+ const isDeployed = walletStateForRequiredChain.deployed;
514
+
515
+ if (!networks) {
516
+ throw new Error(`isWalletUpToDate util: could not get networks from signer`);
517
+ }
518
+
519
+ const authChain = networks.find(network => network.isAuthChain);
520
+
521
+ if (!authChain) {
522
+ throw new Error(`isWalletUpToDate util: could not get auth chain network information`);
523
+ }
524
+
525
+ const authChainId = authChain.chainId;
526
+ const authChainConfig = (_walletState$find = walletState.find(state => state.chainId === authChainId)) == null ? void 0 : _walletState$find.config;
527
+
528
+ if (!authChainConfig) {
529
+ throw new Error(`isWalletUpToDate util: could not get auth chain config`);
530
+ }
531
+
532
+ const requiredChainConfig = walletStateForRequiredChain.config;
533
+
534
+ if (!requiredChainConfig) {
535
+ throw new Error(`isWalletUpToDate util: could not get config for chainId ${chainId}`);
536
+ }
537
+
538
+ const isUpToDate = isConfigEqual(authChainConfig, requiredChainConfig);
539
+ return isDeployed && isUpToDate;
540
+ }; // window.localstorage helper
494
541
 
495
542
  class LocalStore {
496
543
  constructor(key, def) {
@@ -1399,8 +1446,20 @@ class WindowMessageProvider extends BaseProviderTransport {
1399
1446
  } // Open popup window on center of the app window
1400
1447
 
1401
1448
 
1402
- const windowSize = [450, 750];
1403
- const windowPos = [Math.abs(window.screenX + window.innerWidth / 2 - windowSize[0] / 2), Math.abs(window.screenY + window.innerHeight / 2 - windowSize[1] / 2)];
1449
+ let windowSize;
1450
+ let windowPos;
1451
+
1452
+ if (isBrowserExtension()) {
1453
+ windowSize = [450, 750];
1454
+ windowPos = [window.screenX, window.screenY]; // windowPos = [
1455
+ // Math.abs(window.screen.width / 2 - windowSize[0] / 2),
1456
+ // Math.abs(window.screen.height / 2 - windowSize[1] / 2)
1457
+ // ]
1458
+ } else {
1459
+ windowSize = [450, 750];
1460
+ windowPos = [Math.abs(window.screenX + window.innerWidth / 2 - windowSize[0] / 2), Math.abs(window.screenY + window.innerHeight / 2 - windowSize[1] / 2)];
1461
+ }
1462
+
1404
1463
  const windowFeatures = `toolbar=0,location=0,menubar=0,scrollbars=yes,status=yes` + `,width=${windowSize[0]},height=${windowSize[1]}` + `,left=${windowPos[0]},top=${windowPos[1]}`; // serialize params
1405
1464
 
1406
1465
  walletURL.search = windowSessionParams.toString();
@@ -1532,7 +1591,7 @@ class WindowMessageHandler extends BaseWalletTransport {
1532
1591
  this._init = InitState.NIL;
1533
1592
  }
1534
1593
 
1535
- register() {
1594
+ register(windowHref) {
1536
1595
  const isPopup = parent.window.opener !== null;
1537
1596
  this._isPopup = isPopup;
1538
1597
 
@@ -1544,7 +1603,7 @@ class WindowMessageHandler extends BaseWalletTransport {
1544
1603
  const {
1545
1604
  pathname,
1546
1605
  search: rawParams
1547
- } = new URL(window.location.href);
1606
+ } = new URL(windowHref || window.location.href);
1548
1607
  let session = this.getWindowTransportSession(rawParams); // provider should always include sid when opening a new window
1549
1608
 
1550
1609
  const isNewWindowSession = !!session.sessionId; // attempt to restore previous session in the case of a redirect or window reload
@@ -1729,37 +1788,21 @@ class WalletRequestHandler {
1729
1788
  }
1730
1789
 
1731
1790
  case 'personal_sign':
1791
+ case 'eth_sign':
1732
1792
  {
1733
1793
  // note: message from json-rpc input is in hex format
1734
- const [message, signingAddress] = request.params;
1735
- let sig = ''; // TODO:
1736
- // if (process.env.TEST_MODE === 'true' && this.prompter === null) {
1794
+ let message; // there is a difference in the order of the params:
1795
+ // personal_sign: [data, address]
1796
+ // eth_sign: [address, data]
1737
1797
 
1738
- if (_this.prompter === null) {
1739
- // prompter is null, so we'll sign from here
1740
- sig = await signer.signMessage(ethers.utils.arrayify(message), chainId);
1798
+ if (request.method === 'personal_sign') {
1799
+ const [data, address] = request.params;
1800
+ message = data;
1741
1801
  } else {
1742
- // prompt user to provide the response
1743
- sig = await _this.prompter.promptSignMessage({
1744
- chainId: chainId,
1745
- message: message
1746
- });
1802
+ const [address, data] = request.params;
1803
+ message = data;
1747
1804
  }
1748
1805
 
1749
- if (sig && sig.length > 0) {
1750
- response.result = sig;
1751
- } else {
1752
- // The user has declined the request when value is null
1753
- throw new Error('declined by user');
1754
- }
1755
-
1756
- break;
1757
- }
1758
-
1759
- case 'eth_sign':
1760
- {
1761
- // note: message from json-rpc input is in hex format
1762
- const [signingAddress, message] = request.params;
1763
1806
  let sig = ''; // TODO:
1764
1807
  // if (process.env.TEST_MODE === 'true' && this.prompter === null) {
1765
1808
 
@@ -1767,11 +1810,14 @@ class WalletRequestHandler {
1767
1810
  // prompter is null, so we'll sign from here
1768
1811
  sig = await signer.signMessage(ethers.utils.arrayify(message), chainId);
1769
1812
  } else {
1770
- // prompt user to provide the response
1771
- sig = await _this.prompter.promptSignMessage({
1772
- chainId: chainId,
1773
- message: message
1774
- });
1813
+ const promptResultForDeployment = await _this.handleConfirmWalletDeployPrompt(_this.prompter, signer, chainId);
1814
+
1815
+ if (promptResultForDeployment) {
1816
+ sig = await _this.prompter.promptSignMessage({
1817
+ chainId: chainId,
1818
+ message: message
1819
+ });
1820
+ }
1775
1821
  }
1776
1822
 
1777
1823
  if (sig && sig.length > 0) {
@@ -1810,11 +1856,14 @@ class WalletRequestHandler {
1810
1856
  // prompter is null, so we'll sign from here
1811
1857
  sig = await signer.signTypedData(typedData.domain, typedData.types, typedData.message, chainId);
1812
1858
  } else {
1813
- // prompt user to provide the response
1814
- sig = await _this.prompter.promptSignMessage({
1815
- chainId: chainId,
1816
- typedData: typedData
1817
- });
1859
+ const promptResultForDeployment = await _this.handleConfirmWalletDeployPrompt(_this.prompter, signer, chainId);
1860
+
1861
+ if (promptResultForDeployment) {
1862
+ sig = await _this.prompter.promptSignMessage({
1863
+ chainId: chainId,
1864
+ typedData: typedData
1865
+ });
1866
+ }
1818
1867
  }
1819
1868
 
1820
1869
  if (sig && sig.length > 0) {
@@ -2342,6 +2391,35 @@ class WalletRequestHandler {
2342
2391
  this.signer = signer;
2343
2392
  }
2344
2393
 
2394
+ async handleConfirmWalletDeployPrompt(prompter, signer, chainId) {
2395
+ // check if wallet is deployed and up to date, if not, prompt user to deploy
2396
+ // if no chainId is provided, we'll assume the wallet is auth chain wallet and is up to date
2397
+ if (!chainId) {
2398
+ return true;
2399
+ }
2400
+
2401
+ const isUpToDate = await isWalletUpToDate(signer, chainId);
2402
+
2403
+ if (isUpToDate) {
2404
+ return true;
2405
+ }
2406
+
2407
+ const promptResult = await prompter.promptConfirmWalletDeploy(chainId); // if client returned true, check again to make sure wallet is deployed and up to date
2408
+
2409
+ if (promptResult) {
2410
+ const isPromptResultCorrect = await isWalletUpToDate(signer, chainId);
2411
+
2412
+ if (!isPromptResultCorrect) {
2413
+ logger.error('WalletRequestHandler: result for promptConfirmWalletDeploy is not correct');
2414
+ return false;
2415
+ } else {
2416
+ return true;
2417
+ }
2418
+ }
2419
+
2420
+ return false;
2421
+ }
2422
+
2345
2423
  }
2346
2424
  const permittedJsonRpcMethods = ['net_version', 'eth_chainId', 'eth_getBalance', 'eth_getTransactionCount', 'eth_blockNumber', 'eth_getBlockByNumber', 'eth_getBlockByHash', 'eth_getTransactionByHash', 'eth_getCode', 'eth_estimateGas', 'eth_gasPrice', 'sequence_getWalletContext', 'sequence_getNetworks', 'sequence_setDefaultNetwork'];
2347
2425
 
@@ -3549,4 +3627,4 @@ const DefaultProviderConfig = {
3549
3627
  }
3550
3628
  };
3551
3629
 
3552
- export { BaseInjectedTransport, BaseProviderTransport, BaseWalletTransport, CHANNEL_ID, DefaultProviderConfig, ErrSignedInRequired, EventType, ExtensionMessageHandler, ExtensionMessageProvider, InitState, LocalStore, LocalWeb3Provider, MuxMessageProvider, OpenState, PROVIDER_OPEN_TIMEOUT, ProviderError, ProxyMessageChannel, ProxyMessageChannelPort, ProxyMessageHandler, ProxyMessageProvider, Wallet, WalletRequestHandler, Web3Provider, Web3Signer, WindowMessageHandler, WindowMessageProvider, WindowSessionParams, isBrowserExtension, isSequenceProvider, isValidSignature, nextMessageIdx, recoverWalletConfig };
3630
+ export { BaseInjectedTransport, BaseProviderTransport, BaseWalletTransport, CHANNEL_ID, DefaultProviderConfig, ErrSignedInRequired, EventType, ExtensionMessageHandler, ExtensionMessageProvider, InitState, LocalStore, LocalWeb3Provider, MuxMessageProvider, OpenState, PROVIDER_OPEN_TIMEOUT, ProviderError, ProxyMessageChannel, ProxyMessageChannelPort, ProxyMessageHandler, ProxyMessageProvider, Wallet, WalletRequestHandler, Web3Provider, Web3Signer, WindowMessageHandler, WindowMessageProvider, WindowSessionParams, isBrowserExtension, isSequenceProvider, isValidSignature, isWalletUpToDate, nextMessageIdx, recoverWalletConfig };
@@ -14,7 +14,7 @@ export declare class WalletCommands {
14
14
  isValidSignature(address: string, digest: Uint8Array, signature: string, chainId: number, walletContext?: WalletContext): Promise<boolean | undefined>;
15
15
  isValidMessageSignature(address: string, message: string | Uint8Array, signature: string, chainId: number, walletContext?: WalletContext): Promise<boolean | undefined>;
16
16
  isValidTypedDataSignature(address: string, typedData: TypedData, signature: string, chainId: number, walletContext?: WalletContext): Promise<boolean | undefined>;
17
- recoverWalletConfig: (address: string, digest: BytesLike, signature: string | DecodedSignature, chainId: BigNumberish, walletContext?: any) => Promise<WalletConfig>;
18
- recoverWalletConfigFromMessage: (address: string, message: string | Uint8Array, signature: string | DecodedSignature, chainId: BigNumberish, walletContext?: any) => Promise<WalletConfig>;
19
- recoverWalletConfigFromTypedData: (address: string, typedData: TypedData, signature: string | DecodedSignature, chainId: BigNumberish, walletContext?: any) => Promise<WalletConfig>;
17
+ recoverWalletConfig: (address: string, digest: BytesLike, signature: string | DecodedSignature, chainId: BigNumberish, walletContext?: WalletContext | undefined) => Promise<WalletConfig>;
18
+ recoverWalletConfigFromMessage: (address: string, message: string | Uint8Array, signature: string | DecodedSignature, chainId: BigNumberish, walletContext?: WalletContext | undefined) => Promise<WalletConfig>;
19
+ recoverWalletConfigFromTypedData: (address: string, typedData: TypedData, signature: string | DecodedSignature, chainId: BigNumberish, walletContext?: WalletContext | undefined) => Promise<WalletConfig>;
20
20
  }
@@ -1,5 +1,5 @@
1
1
  import { ProviderTransport, ProviderMessage, ProviderMessageRequest, ProviderEventTypes, ProviderMessageResponse, ProviderMessageResponseCallback, OpenState, OpenWalletIntent, ConnectDetails, WalletSession, ProviderRpcError, InitState, TypedEventEmitter } from '../types';
2
- import { NetworkConfig, WalletContext } from '@0xsequence/network';
2
+ import { NetworkConfig, WalletContext, JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network';
3
3
  export declare const PROVIDER_OPEN_TIMEOUT = 30000;
4
4
  export declare const nextMessageIdx: () => number;
5
5
  export declare abstract class BaseProviderTransport implements ProviderTransport {
@@ -1,6 +1,6 @@
1
1
  import { WalletTransport, ProviderMessage, ProviderMessageRequest, ProviderMessageResponse, ProviderRpcError, InitState, ConnectDetails, WalletSession, TransportSession } from '../types';
2
2
  import { WalletRequestHandler } from './wallet-request-handler';
3
- import { NetworkConfig, WalletContext } from '@0xsequence/network';
3
+ import { NetworkConfig, WalletContext, JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network';
4
4
  export declare abstract class BaseWalletTransport implements WalletTransport {
5
5
  protected walletRequestHandler: WalletRequestHandler;
6
6
  protected _sessionId: string;
@@ -1,4 +1,5 @@
1
1
  import { ProviderMessage, ProviderTransport, ProviderEventTypes, ProviderMessageRequest, ProviderMessageResponse, WalletSession, OpenWalletIntent, ConnectDetails } from '../../types';
2
+ import { JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network';
2
3
  export declare class MuxMessageProvider implements ProviderTransport {
3
4
  private messageProviders;
4
5
  private provider;
@@ -1,6 +1,6 @@
1
1
  import { ProviderMessageRequest, ProviderMessageResponse, ProviderMessageRequestHandler, MessageToSign, ProviderRpcError, ConnectOptions, ConnectDetails, PromptConnectDetails, WalletSession, ProviderEventTypes } from '../types';
2
2
  import { ExternalProvider } from '@ethersproject/providers';
3
- import { Networks, NetworkConfig, JsonRpcHandler } from '@0xsequence/network';
3
+ import { Networks, NetworkConfig, JsonRpcHandler, JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network';
4
4
  import { Signer } from '@0xsequence/wallet';
5
5
  import { TransactionRequest } from '@0xsequence/transactions';
6
6
  export interface WalletSignInOptions {
@@ -42,8 +42,10 @@ export declare class WalletRequestHandler implements ExternalProvider, JsonRpcHa
42
42
  isSignedIn(): boolean;
43
43
  getSigner(): Signer | null;
44
44
  setSigner(signer: Signer | null): void;
45
+ private handleConfirmWalletDeployPrompt;
45
46
  }
46
47
  export interface WalletUserPrompter {
48
+ promptConfirmWalletDeploy(chainId: number): Promise<boolean>;
47
49
  promptConnect(options?: ConnectOptions): Promise<PromptConnectDetails>;
48
50
  promptSignMessage(message: MessageToSign): Promise<string>;
49
51
  promptSignTransaction(txn: TransactionRequest, chaindId?: number): Promise<string>;
@@ -5,7 +5,7 @@ export declare class WindowMessageHandler extends BaseWalletTransport {
5
5
  protected parentWindow: Window;
6
6
  private _isPopup;
7
7
  constructor(walletRequestHandler: WalletRequestHandler);
8
- register(): void;
8
+ register(windowHref?: any): void;
9
9
  unregister(): void;
10
10
  private onWindowEvent;
11
11
  sendMessage(message: ProviderMessage<any>): void;
@@ -1,9 +1,19 @@
1
1
  import { BigNumberish, BytesLike } from 'ethers';
2
+ import { WalletContext } from '@0xsequence/network';
2
3
  import { WalletConfig, DecodedSignature } from '@0xsequence/config';
3
4
  import { Web3Provider } from './provider';
4
- export declare const isValidSignature: (address: string, digest: Uint8Array, sig: string, provider: Web3Provider, chainId?: number | undefined, walletContext?: any) => Promise<boolean | undefined>;
5
- export declare const recoverWalletConfig: (address: string, digest: BytesLike, signature: string | DecodedSignature, chainId: BigNumberish, walletContext?: any) => Promise<WalletConfig>;
5
+ import { Signer } from '@0xsequence/wallet';
6
+ export declare const isValidSignature: (address: string, digest: Uint8Array, sig: string, provider: Web3Provider, chainId?: number | undefined, walletContext?: WalletContext | undefined) => Promise<boolean | undefined>;
7
+ export declare const recoverWalletConfig: (address: string, digest: BytesLike, signature: string | DecodedSignature, chainId: BigNumberish, walletContext?: WalletContext | undefined) => Promise<WalletConfig>;
6
8
  export declare const isBrowserExtension: () => boolean;
9
+ /**
10
+ * Returns the status of a signer's wallet on given chain by checking wallet deployment and config status
11
+ *
12
+ * @param {Signer} signer
13
+ * @param {number} chainId
14
+ * @return {Promise<boolean>} Promise that returns true if the wallet is up to date, false otherwise
15
+ */
16
+ export declare const isWalletUpToDate: (signer: Signer, chainId: number) => Promise<boolean>;
7
17
  export declare class LocalStore<T extends Object = string> {
8
18
  def?: T | undefined;
9
19
  readonly key: string;
@@ -50,7 +50,7 @@ export declare class Wallet implements WalletProvider {
50
50
  isConnected(): boolean;
51
51
  getSession: () => WalletSession | undefined;
52
52
  getAddress: () => Promise<string>;
53
- getNetworks: (chainId?: any) => Promise<NetworkConfig[]>;
53
+ getNetworks: (chainId?: ChainIdLike | undefined) => Promise<NetworkConfig[]>;
54
54
  getChainId: () => Promise<number>;
55
55
  getAuthChainId: () => Promise<number>;
56
56
  openWallet: (path?: string | undefined, intent?: OpenWalletIntent | undefined, networkId?: string | number | undefined) => Promise<boolean>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xsequence/provider",
3
- "version": "0.34.1",
3
+ "version": "0.35.3",
4
4
  "description": "provider sub-package for Sequence",
5
5
  "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/provider",
6
6
  "source": "src/index.ts",
@@ -13,13 +13,13 @@
13
13
  "typecheck": "tsc --noEmit"
14
14
  },
15
15
  "dependencies": {
16
- "@0xsequence/abi": "^0.34.0",
17
- "@0xsequence/auth": "^0.34.1",
18
- "@0xsequence/config": "^0.34.0",
19
- "@0xsequence/network": "^0.34.0",
20
- "@0xsequence/transactions": "^0.34.0",
21
- "@0xsequence/utils": "^0.34.0",
22
- "@0xsequence/wallet": "^0.34.0",
16
+ "@0xsequence/abi": "^0.35.3",
17
+ "@0xsequence/auth": "^0.35.3",
18
+ "@0xsequence/config": "^0.35.3",
19
+ "@0xsequence/network": "^0.35.3",
20
+ "@0xsequence/transactions": "^0.35.3",
21
+ "@0xsequence/utils": "^0.35.3",
22
+ "@0xsequence/wallet": "^0.35.3",
23
23
  "@ethersproject/abstract-signer": "^5.5.0",
24
24
  "@ethersproject/hash": "^5.5.0",
25
25
  "@ethersproject/providers": "^5.5.1",
@@ -1,10 +1,8 @@
1
1
  import { EventEmitter2 as EventEmitter } from 'eventemitter2'
2
2
 
3
3
  import {
4
- ProviderMessage,
5
4
  ProviderMessageRequest,
6
5
  ProviderMessageResponse,
7
- ProviderMessageResponseCallback,
8
6
  ProviderMessageRequestHandler,
9
7
  MessageToSign,
10
8
  ProviderRpcError,
@@ -18,7 +16,7 @@ import {
18
16
  } from '../types'
19
17
 
20
18
  import { BigNumber, ethers } from 'ethers'
21
- import { JsonRpcProvider, ExternalProvider } from '@ethersproject/providers'
19
+ import { ExternalProvider } from '@ethersproject/providers'
22
20
 
23
21
  import {
24
22
  Networks,
@@ -28,14 +26,13 @@ import {
28
26
  JsonRpcResponseCallback,
29
27
  JsonRpcResponse
30
28
  } from '@0xsequence/network'
31
-
32
- import { Signer, Account } from '@0xsequence/wallet'
33
- import { isSignedTransactions, SignedTransactions, TransactionRequest } from '@0xsequence/transactions'
34
-
29
+ import { Signer } from '@0xsequence/wallet'
30
+ import { isSignedTransactions, TransactionRequest } from '@0xsequence/transactions'
35
31
  import { signAuthorization, AuthorizationOptions } from '@0xsequence/auth'
36
-
37
32
  import { logger, TypedData } from '@0xsequence/utils'
38
33
 
34
+ import { isWalletUpToDate } from '../utils'
35
+
39
36
  export interface WalletSignInOptions {
40
37
  connect?: boolean
41
38
  mainnetNetworks?: Networks
@@ -252,43 +249,34 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
252
249
  break
253
250
  }
254
251
 
255
- case 'personal_sign': {
252
+ case 'personal_sign':
253
+ case 'eth_sign': {
256
254
  // note: message from json-rpc input is in hex format
257
- const [message, signingAddress] = request.params!
258
-
259
- let sig = ''
260
- // TODO:
261
- // if (process.env.TEST_MODE === 'true' && this.prompter === null) {
262
- if (this.prompter === null) {
263
- // prompter is null, so we'll sign from here
264
- sig = await signer.signMessage(ethers.utils.arrayify(message), chainId)
255
+ let message: any
256
+
257
+ // there is a difference in the order of the params:
258
+ // personal_sign: [data, address]
259
+ // eth_sign: [address, data]
260
+ if (request.method === 'personal_sign') {
261
+ const [data, address] = request.params!
262
+ message = data
265
263
  } else {
266
- // prompt user to provide the response
267
- sig = await this.prompter.promptSignMessage({ chainId: chainId, message: message })
264
+ const [address, data] = request.params!
265
+ message = data
268
266
  }
269
267
 
270
- if (sig && sig.length > 0) {
271
- response.result = sig
272
- } else {
273
- // The user has declined the request when value is null
274
- throw new Error('declined by user')
275
- }
276
- break
277
- }
278
-
279
- case 'eth_sign': {
280
- // note: message from json-rpc input is in hex format
281
- const [signingAddress, message] = request.params!
282
-
283
268
  let sig = ''
269
+
284
270
  // TODO:
285
271
  // if (process.env.TEST_MODE === 'true' && this.prompter === null) {
286
272
  if (this.prompter === null) {
287
273
  // prompter is null, so we'll sign from here
288
274
  sig = await signer.signMessage(ethers.utils.arrayify(message), chainId)
289
275
  } else {
290
- // prompt user to provide the response
291
- sig = await this.prompter.promptSignMessage({ chainId: chainId, message: message })
276
+ const promptResultForDeployment = await this.handleConfirmWalletDeployPrompt(this.prompter, signer, chainId)
277
+ if (promptResultForDeployment) {
278
+ sig = await this.prompter.promptSignMessage({ chainId: chainId, message: message })
279
+ }
292
280
  }
293
281
 
294
282
  if (sig && sig.length > 0) {
@@ -320,12 +308,15 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
320
308
  }
321
309
 
322
310
  let sig = ''
311
+
323
312
  if (this.prompter === null) {
324
313
  // prompter is null, so we'll sign from here
325
314
  sig = await signer.signTypedData(typedData.domain, typedData.types, typedData.message, chainId)
326
315
  } else {
327
- // prompt user to provide the response
328
- sig = await this.prompter.promptSignMessage({ chainId: chainId, typedData: typedData })
316
+ const promptResultForDeployment = await this.handleConfirmWalletDeployPrompt(this.prompter, signer, chainId)
317
+ if (promptResultForDeployment) {
318
+ sig = await this.prompter.promptSignMessage({ chainId: chainId, typedData: typedData })
319
+ }
329
320
  }
330
321
 
331
322
  if (sig && sig.length > 0) {
@@ -698,9 +689,38 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
698
689
  setSigner(signer: Signer | null) {
699
690
  this.signer = signer
700
691
  }
692
+
693
+ private async handleConfirmWalletDeployPrompt(
694
+ prompter: WalletUserPrompter,
695
+ signer: Signer,
696
+ chainId?: number
697
+ ): Promise<boolean> {
698
+ // check if wallet is deployed and up to date, if not, prompt user to deploy
699
+ // if no chainId is provided, we'll assume the wallet is auth chain wallet and is up to date
700
+ if (!chainId) {
701
+ return true
702
+ }
703
+ const isUpToDate = await isWalletUpToDate(signer, chainId)
704
+ if (isUpToDate) {
705
+ return true
706
+ }
707
+ const promptResult = await prompter.promptConfirmWalletDeploy(chainId)
708
+ // if client returned true, check again to make sure wallet is deployed and up to date
709
+ if (promptResult) {
710
+ const isPromptResultCorrect = await isWalletUpToDate(signer, chainId)
711
+ if (!isPromptResultCorrect) {
712
+ logger.error('WalletRequestHandler: result for promptConfirmWalletDeploy is not correct')
713
+ return false
714
+ } else {
715
+ return true
716
+ }
717
+ }
718
+ return false
719
+ }
701
720
  }
702
721
 
703
722
  export interface WalletUserPrompter {
723
+ promptConfirmWalletDeploy(chainId: number): Promise<boolean>
704
724
  promptConnect(options?: ConnectOptions): Promise<PromptConnectDetails>
705
725
  promptSignMessage(message: MessageToSign): Promise<string>
706
726
  promptSignTransaction(txn: TransactionRequest, chaindId?: number): Promise<string>
@@ -22,7 +22,7 @@ export class WindowMessageHandler extends BaseWalletTransport {
22
22
  this._init = InitState.NIL
23
23
  }
24
24
 
25
- register() {
25
+ register(windowHref?: any) {
26
26
  const isPopup = parent.window.opener !== null
27
27
  this._isPopup = isPopup
28
28
  if (isPopup !== true) {
@@ -30,7 +30,7 @@ export class WindowMessageHandler extends BaseWalletTransport {
30
30
  }
31
31
 
32
32
  // record open details (sessionId + default network) from the window url
33
- const { pathname, search: rawParams } = new URL(window.location.href)
33
+ const { pathname, search: rawParams } = new URL(windowHref || window.location.href)
34
34
 
35
35
  let session: TransportSession | null = this.getWindowTransportSession(rawParams)
36
36
 
@@ -101,11 +101,23 @@ export class WindowMessageProvider extends BaseProviderTransport {
101
101
  }
102
102
 
103
103
  // Open popup window on center of the app window
104
- const windowSize = [450, 750]
105
- const windowPos = [
106
- Math.abs(window.screenX + window.innerWidth / 2 - windowSize[0] / 2),
107
- Math.abs(window.screenY + window.innerHeight / 2 - windowSize[1] / 2)
108
- ]
104
+ let windowSize: number[]
105
+ let windowPos: number[]
106
+
107
+ if (isBrowserExtension()) {
108
+ windowSize = [450, 750]
109
+ windowPos = [window.screenX, window.screenY]
110
+ // windowPos = [
111
+ // Math.abs(window.screen.width / 2 - windowSize[0] / 2),
112
+ // Math.abs(window.screen.height / 2 - windowSize[1] / 2)
113
+ // ]
114
+ } else {
115
+ windowSize = [450, 750]
116
+ windowPos = [
117
+ Math.abs(window.screenX + window.innerWidth / 2 - windowSize[0] / 2),
118
+ Math.abs(window.screenY + window.innerHeight / 2 - windowSize[1] / 2)
119
+ ]
120
+ }
109
121
 
110
122
  const windowFeatures =
111
123
  `toolbar=0,location=0,menubar=0,scrollbars=yes,status=yes` +
package/src/utils.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { BigNumberish, BytesLike } from 'ethers'
2
2
  import { WalletContext } from '@0xsequence/network'
3
- import { WalletConfig, addressOf, DecodedSignature } from '@0xsequence/config'
3
+ import { WalletConfig, addressOf, DecodedSignature, isConfigEqual } from '@0xsequence/config'
4
4
  import { packMessageData } from '@0xsequence/utils'
5
5
  import { Web3Provider } from './provider'
6
- import { isValidSignature as _isValidSignature, recoverConfig } from '@0xsequence/wallet'
6
+ import { isValidSignature as _isValidSignature, recoverConfig, Signer } from '@0xsequence/wallet'
7
7
 
8
8
  export const isValidSignature = async (
9
9
  address: string,
@@ -13,15 +13,15 @@ export const isValidSignature = async (
13
13
  chainId?: number,
14
14
  walletContext?: WalletContext
15
15
  ): Promise<boolean | undefined> => {
16
- chainId = chainId || await provider.getChainId()
17
- walletContext = walletContext || await provider.getSigner().getWalletContext()
16
+ chainId = chainId || (await provider.getChainId())
17
+ walletContext = walletContext || (await provider.getSigner().getWalletContext())
18
18
  return _isValidSignature(address, digest, sig, provider, walletContext, chainId)
19
19
  }
20
20
 
21
21
  export const recoverWalletConfig = async (
22
22
  address: string,
23
23
  digest: BytesLike,
24
- signature: string | DecodedSignature,
24
+ signature: string | DecodedSignature,
25
25
  chainId: BigNumberish,
26
26
  walletContext?: WalletContext
27
27
  ): Promise<WalletConfig> => {
@@ -40,7 +40,48 @@ export const recoverWalletConfig = async (
40
40
  return config
41
41
  }
42
42
 
43
- export const isBrowserExtension = (): boolean => window.location.protocol === 'chrome-extension:' || window.location.protocol === 'moz-extension:'
43
+ export const isBrowserExtension = (): boolean =>
44
+ window.location.protocol === 'chrome-extension:' || window.location.protocol === 'moz-extension:'
45
+
46
+ /**
47
+ * Returns the status of a signer's wallet on given chain by checking wallet deployment and config status
48
+ *
49
+ * @param {Signer} signer
50
+ * @param {number} chainId
51
+ * @return {Promise<boolean>} Promise that returns true if the wallet is up to date, false otherwise
52
+ */
53
+ export const isWalletUpToDate = async (signer: Signer, chainId: number): Promise<boolean> => {
54
+ const walletState = await signer.getWalletState()
55
+ const networks = await signer.getNetworks()
56
+
57
+ const walletStateForRequiredChain = walletState.find(state => state.chainId === chainId)
58
+ if (!walletStateForRequiredChain) {
59
+ throw new Error(`WalletRequestHandler: could not find wallet state for chainId ${chainId}`)
60
+ }
61
+
62
+ const isDeployed = walletStateForRequiredChain.deployed
63
+
64
+ if (!networks) {
65
+ throw new Error(`isWalletUpToDate util: could not get networks from signer`)
66
+ }
67
+ const authChain = networks.find(network => network.isAuthChain)
68
+ if (!authChain) {
69
+ throw new Error(`isWalletUpToDate util: could not get auth chain network information`)
70
+ }
71
+ const authChainId = authChain.chainId
72
+ const authChainConfig = walletState.find(state => state.chainId === authChainId)?.config
73
+ if (!authChainConfig) {
74
+ throw new Error(`isWalletUpToDate util: could not get auth chain config`)
75
+ }
76
+ const requiredChainConfig = walletStateForRequiredChain.config
77
+ if (!requiredChainConfig) {
78
+ throw new Error(`isWalletUpToDate util: could not get config for chainId ${chainId}`)
79
+ }
80
+
81
+ const isUpToDate = isConfigEqual(authChainConfig, requiredChainConfig)
82
+
83
+ return isDeployed && isUpToDate
84
+ }
44
85
 
45
86
  // window.localstorage helper
46
87
  export class LocalStore<T extends Object = string> {