@rango-dev/provider-walletconnect-2 0.16.0 → 0.16.1-next.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/dist/session.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ConnectParams, CreateSessionParams, WCInstance } from './types';
2
2
  import type { SignClient } from '@walletconnect/sign-client/dist/types/client';
3
3
  import type { PairingTypes, SessionTypes, SignClientTypes } from '@walletconnect/types';
4
+ import type { BlockchainMeta } from 'rango-types/lib';
4
5
  export declare function getLastSession(client: SignClient): SessionTypes.Struct;
5
6
  /**
6
7
  *
@@ -31,7 +32,10 @@ export declare function tryGetPairing(client: SignClient): PairingTypes.Struct |
31
32
  * Try to restore the session first, if couldn't, create a new session by showing a modal.
32
33
  *
33
34
  */
34
- export declare function tryConnect(client: SignClient, params: ConnectParams): Promise<SessionTypes.Struct>;
35
+ export declare function tryConnect(client: SignClient, params: ConnectParams): Promise<{
36
+ session: SessionTypes.Struct;
37
+ isNew: boolean;
38
+ }>;
35
39
  /**
36
40
  * Wallet connect is a multichain protocol and we can not determine the connected wallet
37
41
  * supports which wallet, `extend`ing session doesn't work during a bug in their utils packages.
@@ -50,7 +54,7 @@ export declare function cleanupSingleSession(client: SignClient, topic: string):
50
54
  *
51
55
  */
52
56
  export declare function disconnectSessions(client: SignClient): Promise<void[]>;
53
- export declare function getAccountsFromSession(session: SessionTypes.Struct): {
57
+ export declare function getAccountsFromSession(session: SessionTypes.Struct, chainId?: string): {
54
58
  accounts: string[];
55
59
  chainId: string;
56
60
  }[];
@@ -60,4 +64,8 @@ export declare function getAccountsFromEvent(event: SignClientTypes.BaseEventArg
60
64
  accounts: string[];
61
65
  chainId: string;
62
66
  }[];
67
+ export declare function updateSessionAccounts(instance: any, requestedNetwork: string, currentNetwork: string, meta: BlockchainMeta[]): Promise<void>;
68
+ export declare function needSessionRecreateOnSwitchNetwork(instance: any): boolean;
69
+ export declare function persistCurrentChainId(instance: any, chainId: string): any;
70
+ export declare function getPersistedChainId(instance: any): Promise<any>;
63
71
  //# sourceMappingURL=session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAC/E,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAe9B,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,uBAEhD;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,YAAY,CAAC,MAAM,GAC3B,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,SAAS,CAAC,CAW1C;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CA+C9B;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,UAAU,GACjB,YAAY,CAAC,MAAM,GAAG,SAAS,CAMjC;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAkC9B;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,CACjD,QAAQ,EAAE,UAAU,EACpB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CA0B9B;AAoBD;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,iBAiB3E;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,UAAU,mBAwB1D;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,YAAY,CAAC,MAAM;;;IAkBlE;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,eAAe,CAAC,aAAa,CAAC;IACnC,UAAU,EAAE,YAAY,CAAC,UAAU,CAAC;CACrC,CAAC;;;IAeH"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAC/E,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAiBtD,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,uBAEhD;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,YAAY,CAAC,MAAM,GAC3B,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,SAAS,CAAC,CAW1C;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CA+C9B;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,UAAU,GACjB,YAAY,CAAC,MAAM,GAAG,SAAS,CAMjC;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,CAoC3D;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,CACjD,QAAQ,EAAE,UAAU,EACpB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CA0B9B;AAoBD;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,iBAiB3E;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,UAAU,mBAwB1D;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,YAAY,CAAC,MAAM,EAC5B,OAAO,CAAC,EAAE,MAAM;;;IAgCjB;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,eAAe,CAAC,aAAa,CAAC;IACnC,UAAU,EAAE,YAAY,CAAC,UAAU,CAAC;CACrC,CAAC;;;IAeH;AAMD,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,GAAG,EACb,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,cAAc,EAAE,iBA8CvB;AAMD,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,GAAG,GAAG,OAAO,CAIzE;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,OAInE;AAED,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,GAAG,gBAGtD"}
@@ -6,6 +6,7 @@ declare class EVMSigner implements GenericSigner<EvmTransaction> {
6
6
  private client;
7
7
  private session;
8
8
  constructor(client: SignClient, session: SessionTypes.Struct);
9
+ static buildTx(evmTx: EvmTransaction, disableV2?: boolean): {};
9
10
  signMessage(msg: string, address: string, chainId: string | null): Promise<string>;
10
11
  signAndSendTx(tx: EvmTransaction, address: string, chainId: string | null): Promise<{
11
12
  hash: string;
@@ -1 +1 @@
1
- {"version":3,"file":"evm.d.ts","sourceRoot":"","sources":["../../src/signers/evm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAK/D,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD,cAAM,SAAU,YAAW,aAAa,CAAC,cAAc,CAAC;IACtD,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAsB;gBAEzB,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM;IAK/C,WAAW,CACtB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,OAAO,CAAC,MAAM,CAAC;IAqCZ,aAAa,CACjB,EAAE,EAAE,cAAc,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAsB5B,OAAO,CAAC,iCAAiC;CAwD1C;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"evm.d.ts","sourceRoot":"","sources":["../../src/signers/evm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAK/D,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD,cAAM,SAAU,YAAW,aAAa,CAAC,cAAc,CAAC;IACtD,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAAsB;gBAEzB,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM;IAK5D,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,SAAS,UAAQ;IAoC1C,WAAW,CACtB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,OAAO,CAAC,MAAM,CAAC;IAqCZ,aAAa,CACjB,EAAE,EAAE,cAAc,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAuB5B,OAAO,CAAC,iCAAiC;CAwD1C;AAED,eAAe,SAAS,CAAC"}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@rango-dev/provider-walletconnect-2",
3
- "version": "0.16.0",
3
+ "version": "0.16.1-next.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
- "module": "./dist/index.js",
6
+ "source": "./src/index.ts",
7
7
  "main": "./dist/index.js",
8
8
  "exports": {
9
9
  ".": "./dist/index.js"
@@ -15,6 +15,7 @@
15
15
  ],
16
16
  "scripts": {
17
17
  "build": "node ../../scripts/build/command.mjs --path wallets/provider-walletconnect-2",
18
+ "ts-check": "tsc --declaration --emitDeclarationOnly -p ./tsconfig.json",
18
19
  "clean": "rimraf dist",
19
20
  "format": "prettier --write '{.,src}/**/*.{ts,tsx}'",
20
21
  "lint": "eslint \"**/*.{ts,tsx}\" --ignore-path ../../.eslintignore"
@@ -23,24 +24,24 @@
23
24
  "@cosmjs/launchpad": "^0.27.1",
24
25
  "@keplr-wallet/cosmos": "^0.9.12",
25
26
  "@keplr-wallet/simple-fetch": "^0.12.14",
26
- "@rango-dev/signer-cosmos": "^0.23.0",
27
- "@rango-dev/signer-evm": "^0.23.0",
28
- "@rango-dev/signer-solana": "^0.23.0",
29
- "@rango-dev/wallets-shared": "^0.23.0",
30
- "@solana/web3.js": "^1.67.2",
31
- "@walletconnect/encoding": "^1.0.2",
32
- "@walletconnect/modal": "^2.6.1",
33
- "@walletconnect/sign-client": "^2.9.1",
34
- "@walletconnect/utils": "^2.9.1",
27
+ "@rango-dev/signer-cosmos": "^0.23.1-next.0",
28
+ "@rango-dev/signer-evm": "^0.23.1-next.0",
29
+ "@rango-dev/signer-solana": "^0.23.1-next.0",
30
+ "@rango-dev/wallets-shared": "^0.23.1-next.0",
31
+ "@solana/web3.js": "1.67.2",
32
+ "@walletconnect/encoding": "1.0.2",
33
+ "@walletconnect/modal": "2.6.1",
34
+ "@walletconnect/sign-client": "2.9.1",
35
+ "@walletconnect/utils": "2.9.1",
35
36
  "bs58": "^5.0.0",
36
37
  "caip": "^1.1.0",
37
38
  "cosmos-wallet": "^1.2.0",
38
- "rango-types": "^0.1.47"
39
+ "rango-types": "^0.1.57"
39
40
  },
40
41
  "devDependencies": {
41
- "@walletconnect/types": "^2.9.1"
42
+ "@walletconnect/types": "2.9.1"
42
43
  },
43
44
  "publishConfig": {
44
45
  "access": "public"
45
46
  }
46
- }
47
+ }
package/src/constants.ts CHANGED
@@ -13,6 +13,8 @@ export enum NAMESPACES {
13
13
  MULTIVERSX = 'multiversx',
14
14
  }
15
15
 
16
+ export const CHAIN_ID_STORAGE = 'wc@2:client//namespaces';
17
+
16
18
  // Refrence: https://docs.walletconnect.com/2.0/advanced/rpc-reference/solana-rpc
17
19
  export enum SolanaRPCMethods {
18
20
  GET_ACCOUNTS = 'solana_getAccounts',
@@ -36,6 +38,9 @@ export enum EthereumRPCMethods {
36
38
  SIGN_TRANSACTION = 'eth_signTransaction',
37
39
  SEND_TRANSACTION = 'eth_sendTransaction',
38
40
  SEND_RAW_TRANSACTION = 'eth_sendRawTransaction',
41
+ SWITCH_CHAIN = 'wallet_switchEthereumChain',
42
+ ADD_CHAIN = 'wallet_addEthereumChain',
43
+ GET_CHAIN = 'eth_chainId',
39
44
  }
40
45
 
41
46
  export enum StarknetRPCMethods {
@@ -57,11 +62,16 @@ export const DEFAULT_ETHEREUM_METHODS = [
57
62
  EthereumRPCMethods.PERSONAL_SIGN,
58
63
  EthereumRPCMethods.SEND_TRANSACTION,
59
64
  EthereumRPCMethods.SIGN_TRANSACTION,
65
+ EthereumRPCMethods.SWITCH_CHAIN,
66
+ EthereumRPCMethods.ADD_CHAIN,
67
+ EthereumRPCMethods.GET_CHAIN,
60
68
  ];
69
+
61
70
  export const DEFAULT_SOLANA_METHODS = [
62
71
  SolanaRPCMethods.SIGN_TRANSACTION,
63
72
  SolanaRPCMethods.SIGN_MESSAGE,
64
73
  ];
74
+
65
75
  export const DEFAULT_COSMOS_METHODS = [
66
76
  CosmosRPCMethods.GET_ACCOUNTS,
67
77
  CosmosRPCMethods.SIGN_AMINO,
@@ -73,10 +83,9 @@ export const DEFAULT_SOLANA_CHAIN_ID = '4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ';
73
83
 
74
84
  export const DEFAULT_APP_METADATA = {
75
85
  name: 'Rango Exchange',
76
- description:
77
- 'Easiest DEX UX with best price to exchange all coins on all blockchains.',
86
+ description: 'The Ultimate Cross-Chain Solution',
78
87
  url: 'https://app.rango.exchange/',
79
- icons: ['https://avatars.githubusercontent.com/u/37784886'],
88
+ icons: ['https://app.rango.exchange/logo-rounded.png'],
80
89
  };
81
90
 
82
91
  export const RELAY_URL = 'wss://relay.walletconnect.com';
package/src/helpers.ts CHANGED
@@ -3,10 +3,18 @@ import type { WalletState } from '@rango-dev/wallets-shared';
3
3
  import type { ProposalTypes } from '@walletconnect/types';
4
4
  import type { BlockchainMeta } from 'rango-types';
5
5
 
6
- import { Networks } from '@rango-dev/wallets-shared';
6
+ import {
7
+ convertEvmBlockchainMetaToEvmChainInfo,
8
+ isEvmAddress,
9
+ Networks,
10
+ } from '@rango-dev/wallets-shared';
7
11
  import { WalletConnectModal } from '@walletconnect/modal';
8
- import { ChainId } from 'caip';
9
- import { cosmosBlockchains, evmBlockchains } from 'rango-types';
12
+ import { AccountId, ChainId } from 'caip';
13
+ import {
14
+ cosmosBlockchains,
15
+ evmBlockchains,
16
+ isEvmBlockchain,
17
+ } from 'rango-types';
10
18
 
11
19
  import {
12
20
  DEFAULT_COSMOS_METHODS,
@@ -14,6 +22,7 @@ import {
14
22
  DEFAULT_ETHEREUM_METHODS,
15
23
  DEFAULT_SOLANA_CHAIN_ID,
16
24
  DEFAULT_SOLANA_METHODS,
25
+ EthereumRPCMethods,
17
26
  NAMESPACES,
18
27
  } from './constants';
19
28
  import { getLastSession } from './session';
@@ -192,3 +201,84 @@ export function getChainIdByNetworkName(
192
201
 
193
202
  return chainId;
194
203
  }
204
+
205
+ export async function switchOrAddEvmChain(
206
+ meta: BlockchainMeta[],
207
+ requestedNetwork: string,
208
+ currentNetwork: string,
209
+ instance: any
210
+ ) {
211
+ const evmBlockchains = meta.filter(isEvmBlockchain);
212
+ const evmNetworksChainInfo =
213
+ convertEvmBlockchainMetaToEvmChainInfo(evmBlockchains);
214
+ const targetChain = evmNetworksChainInfo[requestedNetwork];
215
+ const targetBlockchain = meta.find(
216
+ (blockchain: BlockchainMeta) => blockchain.name === requestedNetwork
217
+ );
218
+ const chainIdInHex = targetBlockchain?.chainId;
219
+
220
+ const currentChainId = getChainIdByNetworkName(currentNetwork, meta);
221
+ const currentChainEip = ChainId.format({
222
+ namespace: NAMESPACES.ETHEREUM,
223
+ reference: String(currentChainId),
224
+ });
225
+
226
+ const session = instance.session;
227
+
228
+ try {
229
+ await instance.client.request({
230
+ topic: session.topic,
231
+ request: {
232
+ method: EthereumRPCMethods.SWITCH_CHAIN,
233
+ params: [
234
+ {
235
+ chainId: chainIdInHex,
236
+ },
237
+ ],
238
+ },
239
+ // It's required to pass current chain, otherwise it won't work
240
+ chainId: currentChainEip,
241
+ });
242
+ } catch (err: any) {
243
+ const addChainError = 4902;
244
+ if (
245
+ err?.code === addChainError ||
246
+ err?.message?.includes(String(addChainError))
247
+ ) {
248
+ await instance.client.request({
249
+ topic: session.topic,
250
+ request: {
251
+ method: EthereumRPCMethods.ADD_CHAIN,
252
+ params: [targetChain],
253
+ },
254
+ // It's required to pass current chain, otherwise it won't work
255
+ chainId: currentChainEip,
256
+ });
257
+ } else {
258
+ throw err;
259
+ }
260
+ }
261
+ }
262
+
263
+ export function getCurrentEvmAccountAddress(instance: any) {
264
+ return instance.session.namespaces.eip155.accounts
265
+ ?.map((account: string) => {
266
+ return new AccountId(account).address;
267
+ })
268
+ ?.filter((address: string) => isEvmAddress(address))?.[0];
269
+ }
270
+
271
+ export function getEvmAccount(
272
+ network: string,
273
+ address: string,
274
+ meta: BlockchainMeta[]
275
+ ) {
276
+ const currentChainId = getChainIdByNetworkName(network, meta);
277
+ return AccountId.format({
278
+ chainId: {
279
+ namespace: NAMESPACES.ETHEREUM,
280
+ reference: String(currentChainId),
281
+ },
282
+ address,
283
+ });
284
+ }
package/src/index.ts CHANGED
@@ -12,8 +12,9 @@ import type {
12
12
  import type { ISignClient } from '@walletconnect/types';
13
13
  import type { BlockchainMeta, SignerFactory } from 'rango-types';
14
14
 
15
- import { WalletTypes } from '@rango-dev/wallets-shared';
15
+ import { Networks, WalletTypes } from '@rango-dev/wallets-shared';
16
16
  import Client from '@walletconnect/sign-client';
17
+ import { AccountId, ChainId } from 'caip';
17
18
  import { evmBlockchains } from 'rango-types';
18
19
 
19
20
  import {
@@ -22,14 +23,22 @@ import {
22
23
  EthereumEvents,
23
24
  RELAY_URL,
24
25
  } from './constants';
25
- import { createModalInstance, simulateRequest } from './helpers';
26
+ import {
27
+ createModalInstance,
28
+ simulateRequest,
29
+ switchOrAddEvmChain,
30
+ } from './helpers';
26
31
  import {
27
32
  cleanupSingleSession,
28
33
  disconnectSessions,
29
34
  getAccountsFromEvent,
30
35
  getAccountsFromSession,
36
+ getPersistedChainId,
37
+ needSessionRecreateOnSwitchNetwork,
38
+ persistCurrentChainId,
31
39
  tryConnect,
32
40
  trySwitchByCreatingNewSession,
41
+ updateSessionAccounts,
33
42
  } from './session';
34
43
  import signer from './signer';
35
44
 
@@ -51,14 +60,15 @@ export const config: WalletConfig = {
51
60
  checkInstallation: false,
52
61
  isAsyncInstance: true,
53
62
  defaultNetwork: DEFAULT_NETWORK,
63
+ isAsyncSwitchNetwork: true,
54
64
  };
55
65
 
56
66
  export const getInstance: GetInstance = async (options) => {
57
67
  const { currentProvider, getState, meta } = options;
58
68
 
59
69
  /*
60
- *Create a new pair, if exists use the pair,
61
- *Or use the already created one.
70
+ * Create a new pair, if exists use the pair,
71
+ * Or use the already created one.
62
72
  */
63
73
  let provider: ISignClient;
64
74
  if (!currentProvider) {
@@ -67,7 +77,6 @@ export const getInstance: GetInstance = async (options) => {
67
77
  'You need to set `WC_PROJECT_ID` in Wallet Connect provider.'
68
78
  );
69
79
  }
70
-
71
80
  provider = await Client.init({
72
81
  relayUrl: RELAY_URL,
73
82
  projectId: envs.WC_PROJECT_ID,
@@ -91,15 +100,29 @@ export const connect: Connect = async ({ instance, network, meta }) => {
91
100
  const requestedNetwork = network || DEFAULT_NETWORK;
92
101
 
93
102
  // Try to restore the session first, if couldn't, create a new session by showing a modal.
94
- const session = await tryConnect(client, {
103
+ const { session, isNew } = await tryConnect(client, {
95
104
  network: requestedNetwork,
96
105
  meta,
97
106
  });
98
107
  // Override the value (session).
99
108
  instance.session = session;
100
-
101
- const accounts = getAccountsFromSession(session);
102
- return accounts;
109
+ const currentChainId = !isNew
110
+ ? String(await getPersistedChainId(instance))
111
+ : undefined;
112
+ const accounts = getAccountsFromSession(session, currentChainId);
113
+ /*
114
+ * TODO: we need to fix next lines to support multiple accounts
115
+ * for now, it will return the current evm account on the current chain
116
+ */
117
+ if (!isNew) {
118
+ return {
119
+ chainId: String(currentChainId),
120
+ accounts: accounts?.[0].accounts,
121
+ };
122
+ }
123
+ const newChainId = accounts?.[accounts.length - 1].chainId;
124
+ void persistCurrentChainId(instance, newChainId);
125
+ return accounts?.[accounts.length - 1];
103
126
  };
104
127
 
105
128
  export const subscribe: Subscribe = ({
@@ -126,13 +149,16 @@ export const subscribe: Subscribe = ({
126
149
  // Listen to events triggred by wallet. (e.g. accountsChanged and chainChanged)
127
150
  client.on('session_event', (args) => {
128
151
  if (args.params.event.name === EthereumEvents.ACCOUNTS_CHANGED) {
129
- const accounts = args.params.event.data;
130
- const chainId = args.params.chainId;
152
+ const accounts = args.params.event.data.map((account: string) => {
153
+ return new AccountId(account).address;
154
+ });
155
+ const chainId = ChainId.parse(args.params.chainId).reference;
131
156
  updateAccounts(accounts);
132
157
  updateChainId(chainId);
133
158
  } else if (args.params.event.name === EthereumEvents.CHAIN_CHANGED) {
134
- const chainId = args.params.chainId;
159
+ const chainId = args.params.event.data;
135
160
  updateChainId(chainId);
161
+ void persistCurrentChainId(instance, chainId);
136
162
  } else {
137
163
  console.log('[WC2] session_event not supported', args.params.event);
138
164
  }
@@ -149,17 +175,27 @@ export const switchNetwork: SwitchNetwork = async ({
149
175
  network,
150
176
  instance,
151
177
  meta,
178
+ getState,
152
179
  }) => {
153
- /**
154
- * Wallet connect is a multichain protocol and we can not determine the connected wallet
155
- * supports which wallet, `extend`ing session doesn't work during a bug in their utils packages.
156
- * So we will try to make a new session with `network` that user needs to switch.
157
- */
158
- const session = await trySwitchByCreatingNewSession(instance, {
159
- network,
160
- meta,
161
- });
162
- instance.session = session;
180
+ const needRecreateSession = needSessionRecreateOnSwitchNetwork(instance);
181
+ config.isAsyncSwitchNetwork = true;
182
+ if (needRecreateSession) {
183
+ config.isAsyncSwitchNetwork = false;
184
+ /**
185
+ * In case of trust wallet that doesn't support switch chain method,
186
+ * we need to handle it manually by deleting last session and create a new one
187
+ * with correct chain id.
188
+ */
189
+ const session = await trySwitchByCreatingNewSession(instance, {
190
+ network,
191
+ meta,
192
+ });
193
+ instance.session = session;
194
+ return;
195
+ }
196
+ const currentNetwork = getState?.().network || Networks.ETHEREUM;
197
+ await updateSessionAccounts(instance, network, currentNetwork, meta);
198
+ await switchOrAddEvmChain(meta, network, currentNetwork, instance);
163
199
  };
164
200
 
165
201
  /**
package/src/session.ts CHANGED
@@ -5,16 +5,19 @@ import type {
5
5
  SessionTypes,
6
6
  SignClientTypes,
7
7
  } from '@walletconnect/types';
8
+ import type { BlockchainMeta } from 'rango-types/lib';
8
9
 
9
10
  import { Networks, timeout } from '@rango-dev/wallets-shared';
10
11
  import { getSdkError } from '@walletconnect/utils';
11
12
  import { AccountId } from 'caip';
12
13
 
13
- import { PING_TIMEOUT } from './constants';
14
+ import { CHAIN_ID_STORAGE, PING_TIMEOUT } from './constants';
14
15
  import {
15
16
  generateOptionalNamespace,
16
17
  generateRequiredNamespace,
17
18
  getChainIdByNetworkName,
19
+ getCurrentEvmAccountAddress,
20
+ getEvmAccount,
18
21
  getModal,
19
22
  solanaChainIdToNetworkName,
20
23
  } from './helpers';
@@ -130,7 +133,7 @@ export function tryGetPairing(
130
133
  export async function tryConnect(
131
134
  client: SignClient,
132
135
  params: ConnectParams
133
- ): Promise<SessionTypes.Struct> {
136
+ ): Promise<{ session: SessionTypes.Struct; isNew: boolean }> {
134
137
  const { network, meta } = params;
135
138
 
136
139
  const requiredNamespaces = generateRequiredNamespace(meta, network);
@@ -145,6 +148,7 @@ export async function tryConnect(
145
148
  }
146
149
 
147
150
  // Check if the user has a session, if yes, restore the session and use it.
151
+ let isNew = false;
148
152
  let session: SessionTypes.Struct | undefined;
149
153
  const pairing = tryGetPairing(client);
150
154
  if (pairing) {
@@ -161,9 +165,10 @@ export async function tryConnect(
161
165
  requiredNamespaces,
162
166
  optionalNamespaces,
163
167
  });
168
+ isNew = true;
164
169
  }
165
170
 
166
- return session;
171
+ return { session, isNew };
167
172
  }
168
173
 
169
174
  /**
@@ -275,7 +280,10 @@ export async function disconnectSessions(client: SignClient) {
275
280
  return await Promise.all(allPromises);
276
281
  }
277
282
 
278
- export function getAccountsFromSession(session: SessionTypes.Struct) {
283
+ export function getAccountsFromSession(
284
+ session: SessionTypes.Struct,
285
+ chainId?: string
286
+ ) {
279
287
  const accounts = Object.values(session.namespaces)
280
288
  .map((namespace) => namespace.accounts)
281
289
  .flat()
@@ -290,8 +298,21 @@ export function getAccountsFromSession(session: SessionTypes.Struct) {
290
298
  accounts: [address],
291
299
  chainId: chain,
292
300
  };
301
+ })
302
+ // TODO: fix, ignore solana and cosmos for now
303
+ .filter((account) => account.chainId !== Networks.SOLANA);
304
+ // sort accounts, so connected chain is first item in array
305
+ if (!!chainId) {
306
+ accounts.sort((a, b) => {
307
+ if (a.chainId === chainId) {
308
+ return 1;
309
+ }
310
+ if (b.chainId === chainId) {
311
+ return -1;
312
+ }
313
+ return 0;
293
314
  });
294
-
315
+ }
295
316
  return accounts;
296
317
  }
297
318
 
@@ -314,3 +335,80 @@ export function getAccountsFromEvent(
314
335
 
315
336
  return accounts;
316
337
  }
338
+
339
+ /*
340
+ * Before switch network, we need to update session namespace accounts
341
+ * to contain both current chain and target chain accoutns.
342
+ */
343
+ export async function updateSessionAccounts(
344
+ instance: any,
345
+ requestedNetwork: string,
346
+ currentNetwork: string,
347
+ meta: BlockchainMeta[]
348
+ ) {
349
+ const session = instance.session;
350
+
351
+ const namespaces = session.namespaces;
352
+ let needUpdateNamepspace = false;
353
+ const accounts = namespaces.eip155.accounts;
354
+
355
+ const currentAccountAddress = getCurrentEvmAccountAddress(instance);
356
+ const requestedAccount = getEvmAccount(
357
+ requestedNetwork,
358
+ currentAccountAddress,
359
+ meta
360
+ );
361
+ if (!accounts.includes(requestedAccount)) {
362
+ accounts.push(requestedAccount);
363
+ needUpdateNamepspace = true;
364
+ }
365
+
366
+ const currentAccount = getEvmAccount(
367
+ currentNetwork,
368
+ currentAccountAddress,
369
+ meta
370
+ );
371
+ if (!accounts.includes(currentAccount)) {
372
+ accounts.push(currentAccount);
373
+ needUpdateNamepspace = true;
374
+ }
375
+
376
+ if (needUpdateNamepspace) {
377
+ const updatedNamespaces = {
378
+ ...namespaces,
379
+ eip155: {
380
+ ...namespaces.eip155,
381
+ accounts,
382
+ },
383
+ };
384
+ await instance.client.session
385
+ .update({
386
+ topic: session.topic,
387
+ namespaces: updatedNamespaces,
388
+ })
389
+ .catch((err: unknown) => {
390
+ console.log(err);
391
+ });
392
+ }
393
+ }
394
+
395
+ /*
396
+ * For some wallet e.g. Trust Wallet Mobile which doesn't support
397
+ * RPC method for switch network, we need to recreate session
398
+ */
399
+ export function needSessionRecreateOnSwitchNetwork(instance: any): boolean {
400
+ const TRUST_WALLET_KEYWORD = 'trust';
401
+ const peerName = instance?.session?.peer?.metadata?.name;
402
+ return peerName?.toLowerCase()?.includes(TRUST_WALLET_KEYWORD);
403
+ }
404
+
405
+ export function persistCurrentChainId(instance: any, chainId: string) {
406
+ return instance.client.core.storage.setItem(CHAIN_ID_STORAGE, {
407
+ defaultChainId: parseInt(chainId),
408
+ });
409
+ }
410
+
411
+ export async function getPersistedChainId(instance: any) {
412
+ return (await instance.client.core.storage.getItem(CHAIN_ID_STORAGE))
413
+ ?.defaultChainId;
414
+ }
@@ -20,6 +20,42 @@ class EVMSigner implements GenericSigner<EvmTransaction> {
20
20
  this.session = session;
21
21
  }
22
22
 
23
+ static buildTx(evmTx: EvmTransaction, disableV2 = false) {
24
+ let tx = {};
25
+ if (evmTx.from) {
26
+ tx = { ...tx, from: evmTx.from };
27
+ }
28
+ if (evmTx.to) {
29
+ tx = { ...tx, to: evmTx.to };
30
+ }
31
+ if (evmTx.data) {
32
+ tx = { ...tx, data: evmTx.data };
33
+ }
34
+ if (evmTx.value) {
35
+ tx = { ...tx, value: evmTx.value };
36
+ }
37
+ if (evmTx.nonce) {
38
+ tx = { ...tx, nonce: evmTx.nonce };
39
+ }
40
+ if (evmTx.gasLimit) {
41
+ tx = { ...tx, gasLimit: evmTx.gasLimit };
42
+ }
43
+ if (evmTx.gasPrice) {
44
+ const shift = 16;
45
+ tx = {
46
+ ...tx,
47
+ gasPrice: '0x' + parseInt(evmTx.gasPrice).toString(shift),
48
+ };
49
+ }
50
+ if (evmTx.maxFeePerGas && !disableV2) {
51
+ tx = { ...tx, maxFeePerGas: evmTx.maxFeePerGas };
52
+ }
53
+ if (evmTx.maxPriorityFeePerGas && !disableV2) {
54
+ tx = { ...tx, maxPriorityFeePerGas: evmTx.maxPriorityFeePerGas };
55
+ }
56
+ return tx;
57
+ }
58
+
23
59
  public async signMessage(
24
60
  msg: string,
25
61
  address: string,
@@ -71,12 +107,13 @@ class EVMSigner implements GenericSigner<EvmTransaction> {
71
107
  chainId,
72
108
  });
73
109
  try {
110
+ const transaction = EVMSigner.buildTx(tx);
74
111
  const hash: string = await this.client.request({
75
112
  topic: this.session.topic,
76
113
  chainId: requestedFor.caipChainId,
77
114
  request: {
78
115
  method: EthereumRPCMethods.SEND_TRANSACTION,
79
- params: [tx],
116
+ params: [transaction],
80
117
  },
81
118
  });
82
119
  return {