@exodus/web3-ethereum 2.7.0 → 2.8.1

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.
@@ -9,7 +9,7 @@ interface EthereumProviderEvents {
9
9
  }
10
10
  export declare class EthereumProvider extends BaseProvider<EthereumProviderEvents> {
11
11
  #private;
12
- constructor({ accountsObservable, storage, supportedChainIds, transport, getDefaultChainId, }: Deps);
12
+ constructor({ accountsObservable, storage, supportedChainIds, transport, getDefaultChainId, walletProviderManager, }: Deps);
13
13
  protected _callRpcMethod<Params, Response>(method: string, params: Params): Promise<Response>;
14
14
  isConnected(): boolean;
15
15
  request: (args: RequestArguments) => Promise<unknown>;
@@ -5,18 +5,20 @@ export class EthereumProvider extends BaseProvider {
5
5
  #storage;
6
6
  #supportedChainIds = [];
7
7
  #getDefaultChainId;
8
+ #walletProviderManager;
8
9
  #initializePromise = null;
9
10
  #initialized = false;
10
11
  #isConnected = false;
11
12
  #chainId = null;
12
13
  #accounts = [];
13
14
  #selectedAddress = null;
14
- constructor({ accountsObservable, storage, supportedChainIds, transport, getDefaultChainId, }) {
15
+ constructor({ accountsObservable, storage, supportedChainIds, transport, getDefaultChainId, walletProviderManager, }) {
15
16
  if (!supportedChainIds?.length) {
16
17
  throw new Error('At least one supported chain must be provided');
17
18
  }
18
19
  super({ transport });
19
20
  this.#storage = createStorageWrapper(storage);
21
+ this.#walletProviderManager = walletProviderManager;
20
22
  this.#supportedChainIds = supportedChainIds
21
23
  .map((chainId) => chainId.toLowerCase())
22
24
  .sort();
@@ -105,7 +107,12 @@ export class EthereumProvider extends BaseProvider {
105
107
  };
106
108
  #requestAccounts = async () => {
107
109
  const permissions = { eth_accounts: {} };
108
- await this._callRpcMethod('wallet_requestPermissions', [permissions]);
110
+ const availableWallets = [];
111
+ this.#walletProviderManager.getWalletProviders().forEach(({ name }) => {
112
+ availableWallets.push(name);
113
+ });
114
+ const { selectedWallet } = await this._callRpcMethod('wallet_requestPermissions', [permissions, availableWallets]);
115
+ this.#walletProviderManager.setDefaultWalletProvider(selectedWallet);
109
116
  const accounts = await this._callRpcMethod('eth_accounts', []);
110
117
  this.#handleAccountsChanged(accounts);
111
118
  if (!(await this.#storage.getChainId())) {
@@ -1,5 +1,5 @@
1
1
  import type { Deps as BaseProviderDeps } from '@exodus/web3-provider';
2
- import type { Observable, Storage } from '@exodus/web3-types';
2
+ import type { Observable, Storage, WalletProviderManager } from '@exodus/web3-types';
3
3
  export declare type ChainId = string;
4
4
  export interface ProviderConnectInfo {
5
5
  chainId: ChainId;
@@ -35,10 +35,11 @@ export declare type Deps = BaseProviderDeps & {
35
35
  storage: Storage;
36
36
  supportedChainIds: string[];
37
37
  getDefaultChainId?: () => Promise<string | null>;
38
+ walletProviderManager: WalletProviderManager;
38
39
  };
39
40
  export interface ProviderStorage {
40
41
  getChainId: () => Promise<ChainId | null>;
41
42
  setChainId: (chainId: ChainId) => Promise<void>;
42
43
  clearChainId: () => Promise<void>;
43
44
  }
44
- export {};
45
+ export { WalletProviderManager };
@@ -1,3 +1,3 @@
1
1
  import type { createEvmDepsFactoryParams, EVMFactory } from '../types.js';
2
- declare const createEvmDepsFactory: ({ analytics, activeWalletAccountAtom, assetsModule, blockchainMetadata, feeMonitors, wallet, walletAccounts, }: createEvmDepsFactoryParams) => EVMFactory;
2
+ declare const createEvmDepsFactory: ({ addressProvider, analytics, activeWalletAccountAtom, assetsModule, blockchainMetadata, feeMonitors, wallet, walletAccounts, }: createEvmDepsFactoryParams) => EVMFactory;
3
3
  export default createEvmDepsFactory;
@@ -1,6 +1,6 @@
1
1
  import { fetchGasLimit, getNonce as apiGetNonce, getServer, } from '@exodus/ethereum-api';
2
2
  import BN from 'bn.js';
3
- const createEvmDepsFactory = ({ analytics, activeWalletAccountAtom, assetsModule, blockchainMetadata, feeMonitors, wallet, walletAccounts, }) => {
3
+ const createEvmDepsFactory = ({ addressProvider, analytics, activeWalletAccountAtom, assetsModule, blockchainMetadata, feeMonitors, wallet, walletAccounts, }) => {
4
4
  return (assetName, origin) => {
5
5
  const asset = assetsModule.getAsset(assetName);
6
6
  const server = getServer(asset);
@@ -26,12 +26,13 @@ const createEvmDepsFactory = ({ analytics, activeWalletAccountAtom, assetsModule
26
26
  return accountIndex;
27
27
  };
28
28
  const getAddress = async () => {
29
- const accountIndex = await _getAccountIndexForActiveWalletAccount();
30
- const { publicKey } = await wallet.getPublicKey({
31
- baseAssetName: assetName,
32
- accountIndex,
29
+ const walletAccount = await activeWalletAccountAtom.get();
30
+ const walletAccountData = walletAccounts.get(walletAccount);
31
+ const { address } = await addressProvider.getReceiveAddress({
32
+ assetName,
33
+ walletAccount: walletAccountData,
33
34
  });
34
- return '0x' + publicKey.toString('hex');
35
+ return address;
35
36
  };
36
37
  const getPrivateKey = async () => {
37
38
  const accountIndex = await _getAccountIndexForActiveWalletAccount();
@@ -3,6 +3,7 @@ import { getTransactionId, signMessage, signTransaction, signTypedData, } from '
3
3
  import { SignTypedDataVersion } from '@metamask/eth-sig-util';
4
4
  import { parseAndValidateTypedData } from './parseAndValidateTypedData.js';
5
5
  import { simulateTransaction } from '../simulation/index.js';
6
+ import { hexToBN } from '../simulation/utils.js';
6
7
  // See: https://eips.ethereum.org/EIPS/eip-2255.
7
8
  const ETH_ACCOUNTS_PERMISSION = 'eth_accounts';
8
9
  const PROXIED_METHODS = [
@@ -47,6 +48,19 @@ version) {
47
48
  // TODO: Return human-readable message from typed data.
48
49
  return typedData.toString();
49
50
  }
51
+ /*
52
+ Checks if a transaction gas limit value exceeds a block gas limit.
53
+ If so, it means that a dapp mistakenly attached an invalid value to a transaction object.
54
+ */
55
+ const isValidGasLimitValue = (value) => {
56
+ if (!value) {
57
+ return false;
58
+ }
59
+ // Note: we consider 100 million gas units as a threshold value (BSC has that highest limit at the moment).
60
+ const maximumBlockGasLimit = hexToBN('0x3b9aca00');
61
+ const proposedGasLimit = hexToBN(value);
62
+ return proposedGasLimit.lte(maximumBlockGasLimit);
63
+ };
50
64
  export function exposeHandlers(rpc, deps) {
51
65
  const { chainId, isTrusted, isAutoApproved, ensureTrusted, approveConnection, approveMessage, approveTransactions, forwardRequest, getAddress, getPrivateKey, getEstimatedGas, getFeeData, getNonce, sendRawTransaction, onTransactionsSigned = () => { }, onEthWalletWatchAssetRequest = () => { }, isUnlocked = () => true, ensureUnlocked = () => true, getAsset, } = deps;
52
66
  const network = `evm:${chainId}`;
@@ -76,7 +90,7 @@ export function exposeHandlers(rpc, deps) {
76
90
  preparedTransaction.data = '0x';
77
91
  }
78
92
  // NOTE: Keep this at the end as other parameters can affect gas estimation.
79
- if (!transaction.gas) {
93
+ if (!transaction.gas || !isValidGasLimitValue(transaction.gas)) {
80
94
  preparedTransaction.gas = await getEstimatedGas(preparedTransaction);
81
95
  }
82
96
  return preparedTransaction;
@@ -186,8 +200,9 @@ export function exposeHandlers(rpc, deps) {
186
200
  }
187
201
  return permissions;
188
202
  });
189
- exposeEvmFunction('wallet_requestPermissions', async (requestedPermissions) => {
203
+ exposeEvmFunction('wallet_requestPermissions', async (requestedPermissions, availableWallets) => {
190
204
  const parentCapability = Object.keys(requestedPermissions)[0];
205
+ let selectedWallet = null;
191
206
  if (parentCapability !== ETH_ACCOUNTS_PERMISSION) {
192
207
  // `eth_accounts` is currently the only permission.
193
208
  // See: https://docs.metamask.io/guide/rpc-api.html#restricted-methods.
@@ -199,13 +214,19 @@ export function exposeHandlers(rpc, deps) {
199
214
  }
200
215
  const trusted = await isTrusted();
201
216
  if (!trusted) {
202
- const approved = await approveConnection(network);
203
- if (!approved) {
217
+ const response = (await approveConnection(network, availableWallets));
218
+ if (!response.approved) {
204
219
  throw new UserRejectedRequestError();
205
220
  }
206
- await ensureTrusted();
221
+ selectedWallet = response.selectedWallet;
222
+ if (response.selectedWallet === 'exodus') {
223
+ await ensureTrusted();
224
+ }
207
225
  }
208
- return [{ parentCapability: ETH_ACCOUNTS_PERMISSION }];
226
+ return {
227
+ walletPermissions: [{ parentCapability: ETH_ACCOUNTS_PERMISSION }],
228
+ selectedWallet,
229
+ };
209
230
  });
210
231
  exposeEvmFunction('wallet_watchAsset', async (assetDetails) => {
211
232
  await assertTrusted();
@@ -913,6 +913,12 @@ export function validateRequest(value) {
913
913
  validate.errors = [{ keywordLocation: '#/type', instanceLocation: '#' }]
914
914
  return false
915
915
  }
916
+ if (data.length > 2) {
917
+ validate.errors = [
918
+ { keywordLocation: '#/maxItems', instanceLocation: '#' },
919
+ ]
920
+ return false
921
+ }
916
922
  if (data.length < 1) {
917
923
  validate.errors = [
918
924
  { keywordLocation: '#/minItems', instanceLocation: '#' },
@@ -929,7 +935,46 @@ export function validateRequest(value) {
929
935
  return false
930
936
  }
931
937
  }
932
- if (data.length > 1) {
938
+ if (1 in data && hasOwn(data, 1)) {
939
+ if (!Array.isArray(data[1])) {
940
+ validate.errors = [
941
+ { keywordLocation: '#/1/type', instanceLocation: '#/1' },
942
+ ]
943
+ return false
944
+ }
945
+ for (let l = 0; l < data[1].length; l++) {
946
+ if (l in data[1] && hasOwn(data[1], l)) {
947
+ if (!(typeof data[1][l] === 'string')) {
948
+ validate.errors = [
949
+ {
950
+ keywordLocation: '#/1/items/type',
951
+ instanceLocation: '#/1/' + l,
952
+ },
953
+ ]
954
+ return false
955
+ }
956
+ if (data[1][l].length > 20 && stringLength(data[1][l]) > 20) {
957
+ validate.errors = [
958
+ {
959
+ keywordLocation: '#/1/items/maxLength',
960
+ instanceLocation: '#/1/' + l,
961
+ },
962
+ ]
963
+ return false
964
+ }
965
+ if (!format3.test(data[1][l])) {
966
+ validate.errors = [
967
+ {
968
+ keywordLocation: '#/1/items/format',
969
+ instanceLocation: '#/1/' + l,
970
+ },
971
+ ]
972
+ return false
973
+ }
974
+ }
975
+ }
976
+ }
977
+ if (data.length > 2) {
933
978
  validate.errors = [
934
979
  { keywordLocation: '#/items', instanceLocation: '#' },
935
980
  ]
@@ -2,7 +2,7 @@ import type { EthBalanceChanges } from './types.js';
2
2
  import type { EthTransaction } from '@exodus/web3-ethereum-utils';
3
3
  import type { Asset, NumberUnit } from '@exodus/web3-types';
4
4
  export declare const simulateTransaction: (asset: Asset, transaction: EthTransaction) => Promise<{
5
- assetName: string;
5
+ baseAssetName: string;
6
6
  advancedDetails: never[];
7
7
  balanceChanges: EthBalanceChanges;
8
8
  displayInfo: {
@@ -46,13 +46,13 @@ const getTxFeeDetails = (asset, transaction) => {
46
46
  };
47
47
  };
48
48
  export const simulateTransaction = async (asset, transaction) => {
49
- const assetName = asset.name;
50
- const shouldSimulate = SIMULATION_SUPPORTED_ASSET_NAMES.includes(assetName);
49
+ const baseAssetName = asset.name;
50
+ const shouldSimulate = SIMULATION_SUPPORTED_ASSET_NAMES.includes(baseAssetName);
51
51
  const balanceChanges = await computeBalanceChanges(asset, shouldSimulate, transaction);
52
52
  const { txFee, txFeeDetails } = getTxFeeDetails(asset, transaction);
53
53
  const displayInfo = getEthereumDisplayInfo(balanceChanges);
54
54
  return {
55
- assetName,
55
+ baseAssetName,
56
56
  advancedDetails: [],
57
57
  balanceChanges,
58
58
  displayInfo,
@@ -1,12 +1,11 @@
1
1
  import BN from 'bn.js';
2
- import type { CurrencyParams, EthBalanceChange, EthBalanceChanges } from './types.js';
2
+ import type { EthBalanceChange, EthBalanceChanges } from './types.js';
3
3
  import type { EthTransaction } from '@exodus/web3-ethereum-utils';
4
4
  export declare const hexToBN: (hexValue?: string) => BN;
5
5
  export declare const isEthTransferTx: (transaction: EthTransaction) => boolean;
6
6
  export declare function getEthereumDisplayInfo(balanceChanges: EthBalanceChanges): {
7
7
  title: string;
8
8
  };
9
- export declare const createCurrency: ({ amount, symbol, denominator, }: CurrencyParams) => any;
10
9
  export declare const formatAccounts: ({ willSend, willReceive, willApprove, }: {
11
10
  willSend?: never[] | undefined;
12
11
  willReceive?: never[] | undefined;
@@ -1,4 +1,4 @@
1
- import { UnitType } from '@exodus/currency';
1
+ import { createCurrency } from '@exodus/web3-utils';
2
2
  import BN from 'bn.js';
3
3
  export const hexToBN = (hexValue) => new BN((hexValue || '').slice(2), 'hex');
4
4
  export const isEthTransferTx = (transaction) => {
@@ -15,10 +15,6 @@ export function getEthereumDisplayInfo(balanceChanges) {
15
15
  }
16
16
  return { title };
17
17
  }
18
- export const createCurrency = ({ amount, symbol, denominator, }) => UnitType.create({
19
- base: 0,
20
- [symbol]: denominator,
21
- }).base(amount);
22
18
  const convertToNumberUnit = (account) => ({
23
19
  ...account,
24
20
  balance: createCurrency({
package/lib/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  import type { EthTransaction, EthTransactionHash, EthWalletWatchAsset } from '@exodus/web3-ethereum-utils';
3
3
  import type { CommonDeps } from '@exodus/web3-rpc-handlers';
4
- import type { IAnalyticsModule, IActiveWalletAccountAtom, IAssetsModule, IBlockchainMetadataModule, IFeeDataModule, IWalletModule, IWalletAccountsModule } from '@exodus/web3-types';
4
+ import type { IAddressProvider, IAnalyticsModule, IActiveWalletAccountAtom, IAssetsModule, IBlockchainMetadataModule, IFeeDataModule, IWalletModule, IWalletAccountsModule, ApproveConnectionResponse, WalletAccountData } from '@exodus/web3-types';
5
5
  /**
6
6
  * EVM-compatible chains.
7
7
  */
@@ -15,6 +15,7 @@ export interface FeeData {
15
15
  maxPriorityFeePerGas?: string;
16
16
  }
17
17
  export interface createEvmDepsFactoryParams {
18
+ addressProvider: IAddressProvider;
18
19
  activeWalletAccountAtom: IActiveWalletAccountAtom;
19
20
  analytics: IAnalyticsModule;
20
21
  assetsModule: IAssetsModule;
@@ -38,3 +39,4 @@ export declare type EVMDeps = {
38
39
  export declare type Dependencies = {
39
40
  evms: EVMDeps[];
40
41
  } & CommonDeps;
42
+ export { ApproveConnectionResponse, WalletAccountData };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exodus/web3-ethereum",
3
3
  "description": "Apps connectivity library for EVM-compatible chains.",
4
- "version": "2.7.0",
4
+ "version": "2.8.1",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "module": "lib/index.js",
@@ -33,10 +33,11 @@
33
33
  "dependencies": {
34
34
  "@exodus/currency": "^2.1.2",
35
35
  "@exodus/ethereum-api": "^6.0.0",
36
- "@exodus/web3-constants": "^1.20.0",
37
- "@exodus/web3-ethereum-utils": "^1.10.0",
38
- "@exodus/web3-provider": "^2.18.0",
39
- "@exodus/web3-rpc-handlers": "^0.15.0",
36
+ "@exodus/web3-constants": "^1.21.0",
37
+ "@exodus/web3-ethereum-utils": "^1.11.0",
38
+ "@exodus/web3-provider": "^2.19.0",
39
+ "@exodus/web3-rpc-handlers": "^0.16.0",
40
+ "@exodus/web3-utils": "^1.6.0",
40
41
  "bn.js": "^5.2.0",
41
42
  "eth-rpc-errors": "^4.0.3"
42
43
  },
@@ -44,12 +45,11 @@
44
45
  "@exodus/assets": "^8.0.72",
45
46
  "@exodus/json-rpc": "^1.11.0",
46
47
  "@exodus/schemasafe": "^1.0.0",
47
- "@exodus/web3-types": "^1.16.0",
48
+ "@exodus/web3-types": "^1.17.0",
48
49
  "@exodus/web3-validation": "^1.34.0",
49
50
  "@wallet-standard/base": "^1.0.1"
50
51
  },
51
52
  "peerDependencies": {
52
53
  "@exodus/assets": "^8.0.72"
53
- },
54
- "gitHead": "88c28467495b5524e3d0d0080e58ea9d5fbe7b11"
54
+ }
55
55
  }