From 7c738144e5fee5be01dcece65658d7c05a72a671 Mon Sep 17 00:00:00 2001 From: samobasquiat Date: Sat, 2 Dec 2023 11:56:25 +0000 Subject: [PATCH] chore: export types & logics from useWidget --- queue-manager/core/src/index.ts | 1 + queue-manager/rango-preset/package.json | 1 - queue-manager/rango-preset/src/helpers.ts | 54 ++------- queue-manager/rango-preset/src/index.ts | 2 - queue-manager/rango-preset/src/types.ts | 10 +- wallets/shared/src/helpers.ts | 104 ++++++++++++------ wallets/shared/src/providers.ts | 62 ++++++++--- wallets/shared/src/rango.ts | 2 + .../WidgetInfo/WidgetInfo.helpers.ts | 45 ++++++++ .../src/containers/WidgetInfo/WidgetInfo.tsx | 7 +- .../containers/WidgetInfo/WidgetInfo.types.ts | 4 +- widget/embedded/src/index.ts | 39 ++++++- 12 files changed, 225 insertions(+), 106 deletions(-) create mode 100644 widget/embedded/src/containers/WidgetInfo/WidgetInfo.helpers.ts diff --git a/queue-manager/core/src/index.ts b/queue-manager/core/src/index.ts index 61f2a521ad..3afe0c8526 100644 --- a/queue-manager/core/src/index.ts +++ b/queue-manager/core/src/index.ts @@ -1,3 +1,4 @@ export * from './manager'; export { default as Persistor, DB_NAME } from './persistor'; export * from './types'; +export type { QueueContext } from './queue'; diff --git a/queue-manager/rango-preset/package.json b/queue-manager/rango-preset/package.json index 18992bfc02..a5a0a7d9ea 100644 --- a/queue-manager/rango-preset/package.json +++ b/queue-manager/rango-preset/package.json @@ -23,7 +23,6 @@ "peerDependencies": { "@rango-dev/queue-manager-core": "*", "@rango-dev/queue-manager-react": "*", - "@rango-dev/wallets-react": "*", "@rango-dev/wallets-shared": "*", "@sentry/browser": "*", "bignumber.js": "*", diff --git a/queue-manager/rango-preset/src/helpers.ts b/queue-manager/rango-preset/src/helpers.ts index ea3d0470e2..0ec816af60 100644 --- a/queue-manager/rango-preset/src/helpers.ts +++ b/queue-manager/rango-preset/src/helpers.ts @@ -22,10 +22,10 @@ import type { QueueName, QueueType, } from '@rango-dev/queue-manager-core'; -import type { Providers } from '@rango-dev/wallets-react'; import type { Meta, Network, + Providers, WalletState, WalletType, } from '@rango-dev/wallets-shared'; @@ -34,11 +34,15 @@ import type { EvmBlockchainMeta, Transaction, } from 'rango-sdk'; -import type { APIErrorCode, SignerErrorCode } from 'rango-types/lib'; +import type { APIErrorCode, SignerErrorCode } from 'rango-types'; import { Status } from '@rango-dev/queue-manager-core'; -import { readAccountAddress } from '@rango-dev/wallets-react'; -import { getBlockChainNameFromId, Networks } from '@rango-dev/wallets-shared'; +import { readAccountAddress } from '@rango-dev/wallets-core'; +import { + getBlockChainNameFromId, + getEvmProvider, + splitWalletNetwork, +} from '@rango-dev/wallets-shared'; import { TransactionType } from 'rango-sdk'; import { @@ -128,29 +132,6 @@ export function inMemoryTransactionsData() { }; } -/** - * Sample inputs are: - * - "metamask-ETH" - * - "metamask-BSC-BSC:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - * - "token-pocket-BSC-BSC:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - * Returns "wallet and network" separately, even if the wallet is dashed inside. - * - */ - -export function splitWalletNetwork(input: string): string[] { - const removedAddressInput = input?.split(':')[0] || ''; - const splittedInput = removedAddressInput.split('-'); - const network = splittedInput[splittedInput.length - 1]; - const walletNetwork = splittedInput.slice(0, -1); - - if (walletNetwork[walletNetwork.length - 1] === network) { - walletNetwork.pop(); - } - const wallet = walletNetwork.join('-'); - - return [wallet, network]; -} - /** * * Returns `steps`, if it's a `running` swap. @@ -624,25 +605,6 @@ export function isWalletNull(wallet: Wallet | null): boolean { ); } -/** - * On our implementation for `wallets` package, We keep the instance in 2 ways - * If it's a single chain wallet, it returns the instance directly, - * If it's a multichain wallet, it returns a `Map` of instances. - * This function will get the `ETHEREUM` instance in both types. - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function getEvmProvider(providers: Providers, type: WalletType): any { - if (type && providers[type]) { - // we need this because provider can return an instance or a map of instances, so what you are doing here is try to detect that. - if (providers[type].size) { - return providers[type].get(Networks.ETHEREUM); - } - - return providers[type]; - } - return null; -} - /** * In a `PendingSwap`, each step needs a wallet to proceed, * By using this function we can access what wallet exactly we need to run current step. diff --git a/queue-manager/rango-preset/src/index.ts b/queue-manager/rango-preset/src/index.ts index 606bfa641c..87d2e559db 100644 --- a/queue-manager/rango-preset/src/index.ts +++ b/queue-manager/rango-preset/src/index.ts @@ -51,11 +51,9 @@ export { updateSwapStatus, checkWaitingForNetworkChange, getCurrentStep, - getEvmProvider, cancelSwap, getRequiredWallet, getRunningSwaps, - splitWalletNetwork, resetRunningSwapNotifsOnPageLoad, isApprovalTX, getLastSuccessfulStep, diff --git a/queue-manager/rango-preset/src/types.ts b/queue-manager/rango-preset/src/types.ts index accc919ab9..49d6e6cecf 100644 --- a/queue-manager/rango-preset/src/types.ts +++ b/queue-manager/rango-preset/src/types.ts @@ -1,10 +1,14 @@ import type { PendingSwap, PendingSwapStep, Wallet } from './shared'; -import type { QueueDef, QueueStorage } from '@rango-dev/queue-manager-core'; -import type { QueueContext } from '@rango-dev/queue-manager-core/dist/queue'; -import type { ConnectResult, Providers } from '@rango-dev/wallets-react'; +import type { + QueueContext, + QueueDef, + QueueStorage, +} from '@rango-dev/queue-manager-core'; +import type { ConnectResult } from '@rango-dev/wallets-core'; import type { Meta, Network, + Providers, WalletState, WalletType, } from '@rango-dev/wallets-shared'; diff --git a/wallets/shared/src/helpers.ts b/wallets/shared/src/helpers.ts index 64caa324cb..a7dbc29770 100644 --- a/wallets/shared/src/helpers.ts +++ b/wallets/shared/src/helpers.ts @@ -1,13 +1,14 @@ -import type { EvmBlockchainMeta } from 'rango-types'; -import { - EvmNetworksChainInfo, +import type { AddEthereumChainParameter, - Network, - Networks, Connect, - Wallet, + EvmNetworksChainInfo, InstallObjects, + Network, + Wallet, } from './rango'; +import type { EvmBlockchainMeta } from 'rango-types'; + +import { Networks } from './rango'; export { isAddress as isEvmAddress } from 'ethers/lib/utils.js'; @@ -16,7 +17,9 @@ export function deepCopy(obj: any): any { let copy; // Handle the 3 simple types, and null or undefined - if (null == obj || 'object' != typeof obj) return obj; + if (null == obj || 'object' != typeof obj) { + return obj; + } // Handle Date if (obj instanceof Date) { @@ -38,8 +41,9 @@ export function deepCopy(obj: any): any { if (obj instanceof Object) { copy = {} as any; for (const attr in obj) { - if (Object.prototype.hasOwnProperty.call(obj, attr)) + if (Object.prototype.hasOwnProperty.call(obj, attr)) { copy[attr] = deepCopy(obj[attr]); + } } return copy; } @@ -61,17 +65,22 @@ export async function switchOrAddNetworkForMetamaskCompatibleWallets( }); } catch (switchError) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - // To resolve this error: Catch clause variable type annotation must be any or unknown if specified + /* + * @ts-ignore + * To resolve this error: Catch clause variable type annotation must be any or unknown if specified + */ const error = switchError as { code: number }; if (!targetChain) { throw new Error( `It seems you don't have ${network} network on your wallet. Please add it manually.` ); + /* eslint-disable @typescript-eslint/no-magic-numbers */ } else if (error.code === 4902 || !error.code) { - // Note: on WalletConnect `code` is undefined so we have to use !switchError.code as fallback. - // This error code indicates that the chain has not been added to wallet. + /* + * Note: on WalletConnect `code` is undefined so we have to use !switchError.code as fallback. + * This error code indicates that the chain has not been added to wallet. + */ await instance.request({ method: 'wallet_addEthereumChain', params: [targetChain], @@ -81,7 +90,7 @@ export async function switchOrAddNetworkForMetamaskCompatibleWallets( } } -export function timeout( +export async function timeout( forPromise: Promise, time: number ): Promise { @@ -122,9 +131,11 @@ export const evmChainsToRpcMap = ( Object.keys(evmNetworkChainInfo).map((chainName) => { const info = evmNetworkChainInfo[chainName]; - // This `if` is only used for satisfying typescript, - // Because we iterating over Object.keys(EVM_NETWORKS_CHAIN_INFO) - // And obviously it cannot be `undefined` and always has a value. + /* + * This `if` is only used for satisfying typescript, + * Because we iterating over Object.keys(EVM_NETWORKS_CHAIN_INFO) + * And obviously it cannot be `undefined` and always has a value. + */ if (info) { return [parseInt(info.chainId), info.rpcUrls[0]]; } @@ -166,12 +177,17 @@ export function getCoinbaseInstance( instances.set(Networks.ETHEREUM, ethInstance); } } - if (!!coinbaseSolana && lookingFor === 'coinbase') + if (!!coinbaseSolana && lookingFor === 'coinbase') { instances.set(Networks.SOLANA, coinbaseSolana); + } - if (instances.size === 0) return null; + if (instances.size === 0) { + return null; + } - if (lookingFor === 'metamask') return instances.get(Networks.ETHEREUM); + if (lookingFor === 'metamask') { + return instances.get(Networks.ETHEREUM); + } return instances; } @@ -189,7 +205,9 @@ function isBrave() { const nav: any = navigator; if (nav.brave && nav.brave.isBrave) { nav.brave.isBrave().then((res: boolean) => { - if (res) isBrave = true; + if (res) { + isBrave = true; + } }); } @@ -199,19 +217,18 @@ function isBrave() { export function detectInstallLink(install: InstallObjects | string): string { if (typeof install !== 'object') { return install; - } else { - let link; - if (isBrave()) { - link = install.BRAVE; - } else if (navigator.userAgent?.toLowerCase().indexOf('chrome') !== -1) { - link = install.CHROME; - } else if (navigator.userAgent?.toLowerCase().indexOf('firefox') !== -1) { - link = install.FIREFOX; - } else if (navigator.userAgent?.toLowerCase().indexOf('edge') !== -1) { - link = install.EDGE; - } - return link || install.DEFAULT; } + let link; + if (isBrave()) { + link = install.BRAVE; + } else if (navigator.userAgent?.toLowerCase().indexOf('chrome') !== -1) { + link = install.CHROME; + } else if (navigator.userAgent?.toLowerCase().indexOf('firefox') !== -1) { + link = install.FIREFOX; + } else if (navigator.userAgent?.toLowerCase().indexOf('edge') !== -1) { + link = install.EDGE; + } + return link || install.DEFAULT; } export function detectMobileScreens(): boolean { @@ -219,3 +236,26 @@ export function detectMobileScreens(): boolean { navigator.userAgent ); } + +/** + * Sample inputs are: + * - "metamask-ETH" + * - "metamask-BSC-BSC:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + * - "token-pocket-BSC-BSC:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + * Returns "wallet and network" separately, even if the wallet is dashed inside. + * + */ + +export function splitWalletNetwork(input: string): string[] { + const removedAddressInput = input?.split(':')[0] || ''; + const splittedInput = removedAddressInput.split('-'); + const network = splittedInput[splittedInput.length - 1]; + const walletNetwork = splittedInput.slice(0, -1); + + if (walletNetwork[walletNetwork.length - 1] === network) { + walletNetwork.pop(); + } + const wallet = walletNetwork.join('-'); + + return [wallet, network]; +} diff --git a/wallets/shared/src/providers.ts b/wallets/shared/src/providers.ts index f22981ded3..e9552cc713 100644 --- a/wallets/shared/src/providers.ts +++ b/wallets/shared/src/providers.ts @@ -1,16 +1,22 @@ -import { - Network, +import type { + CanEagerConnect, CanSwitchNetwork, + Network, + Providers, Subscribe, SwitchNetwork, - Networks, - CanEagerConnect, + WalletType, } from './rango'; -import { convertEvmBlockchainMetaToEvmChainInfo } from './helpers'; -import { switchOrAddNetworkForMetamaskCompatibleWallets } from './helpers'; import type { BlockchainMeta } from 'rango-types'; + import { isEvmBlockchain } from 'rango-types'; +import { + convertEvmBlockchainMetaToEvmChainInfo, + switchOrAddNetworkForMetamaskCompatibleWallets, +} from './helpers'; +import { Networks } from './rango'; + export async function getEvmAccounts(instance: any) { const [accounts, chainId] = await Promise.all([ instance.request({ method: 'eth_requestAccounts' }) as Promise, @@ -30,11 +36,13 @@ export const subscribeToEvm: Subscribe = ({ updateAccounts, }) => { instance?.on('accountsChanged', (addresses: string[]) => { - // TODO: after enabling autoconnect, we can consider this condition - // to be removed. - // The problem was if a user already connected its wallet, - // Metamask is triggering this event on first load, so when autoconnect is disabled, - // it's automaticlally change the state of wallet to `connected`. + /* + * TODO: after enabling autoconnect, we can consider this condition + * to be removed. + * The problem was if a user already connected its wallet, + * Metamask is triggering this event on first load, so when autoconnect is disabled, + * it's automaticlally change the state of wallet to `connected`. + */ if (state.connected) { updateAccounts(addresses); } @@ -50,8 +58,10 @@ export const canEagerlyConnectToEvm: CanEagerConnect = async ({ instance }) => { const accounts: string[] = await instance.request({ method: 'eth_accounts', }); - if (accounts.length) return true; - else return false; + if (accounts.length) { + return true; + } + return false; } catch (error) { return false; } @@ -82,13 +92,17 @@ export function getEthChainsInstance( network: Network | null, meta: BlockchainMeta[] ): Network | null { - if (!network) return null; + if (!network) { + return null; + } const evmBlockchains = evmNetworkNames(meta); return evmBlockchains.includes(network) ? Networks.ETHEREUM : null; } function isEvmNetwork(network: Network | null, meta: BlockchainMeta[]) { - if (!network) return false; + if (!network) { + return false; + } return evmNetworkNames(meta).includes(network); } @@ -114,3 +128,21 @@ export function chooseInstance( export function getNetworkInstance(provider: any, network: Network) { return provider.size ? provider.get(network) : provider; } + +/** + * On our implementation for `wallets` package, We keep the instance in 2 ways + * If it's a single chain wallet, it returns the instance directly, + * If it's a multichain wallet, it returns a `Map` of instances. + * This function will get the `ETHEREUM` instance in both types. + */ +export function getEvmProvider(providers: Providers, type: WalletType): any { + if (type && providers[type]) { + // we need this because provider can return an instance or a map of instances, so what you are doing here is try to detect that. + if (providers[type].size) { + return providers[type].get(Networks.ETHEREUM); + } + + return providers[type]; + } + return null; +} diff --git a/wallets/shared/src/rango.ts b/wallets/shared/src/rango.ts index 46cb0c1baa..d6771ec7d9 100644 --- a/wallets/shared/src/rango.ts +++ b/wallets/shared/src/rango.ts @@ -296,3 +296,5 @@ export interface Wallet { connected: boolean; info: Omit; } + +export type Providers = { [type in WalletType]?: any }; diff --git a/widget/embedded/src/containers/WidgetInfo/WidgetInfo.helpers.ts b/widget/embedded/src/containers/WidgetInfo/WidgetInfo.helpers.ts new file mode 100644 index 0000000000..89aedd0282 --- /dev/null +++ b/widget/embedded/src/containers/WidgetInfo/WidgetInfo.helpers.ts @@ -0,0 +1,45 @@ +import type { Manager } from '@rango-dev/queue-manager-core'; +import type { PendingSwap } from '@rango-dev/queue-manager-rango-preset'; + +import { + getCurrentBlockchainOfOrNull, + getCurrentStep, + getRelatedWalletOrNull, +} from '@rango-dev/queue-manager-rango-preset'; + +import { getPendingSwaps } from '../../utils/queue'; + +export class WidgetHistory { + private manager: Manager | undefined; + + constructor(manager: Manager | undefined) { + this.manager = manager; + } + + public getAllSwaps() { + return getPendingSwaps(this.manager); + } + + public getCurrentStep(swap: PendingSwap) { + return this.getCurrentStepInfo(swap).step; + } + + public getCurrentStepWallet(swap: PendingSwap) { + return this.getCurrentStepInfo(swap).wallet; + } + + public getCurrentStepNetwork(swap: PendingSwap) { + return this.getCurrentStepInfo(swap).network; + } + + private getCurrentStepInfo(swap: PendingSwap) { + const currentStep = getCurrentStep(swap); + return { + step: currentStep, + wallet: currentStep ? getRelatedWalletOrNull(swap, currentStep) : null, + network: currentStep + ? getCurrentBlockchainOfOrNull(swap, currentStep) + : null, + }; + } +} diff --git a/widget/embedded/src/containers/WidgetInfo/WidgetInfo.tsx b/widget/embedded/src/containers/WidgetInfo/WidgetInfo.tsx index f3eb57f4d1..58f241172b 100644 --- a/widget/embedded/src/containers/WidgetInfo/WidgetInfo.tsx +++ b/widget/embedded/src/containers/WidgetInfo/WidgetInfo.tsx @@ -4,23 +4,24 @@ import { useManager } from '@rango-dev/queue-manager-react'; import React, { createContext, useContext } from 'react'; import { useWalletsStore } from '../../store/wallets'; -import { getPendingSwaps } from '../../utils/queue'; import { calculateWalletUsdValue } from '../../utils/wallets'; +import { WidgetHistory } from './WidgetInfo.helpers'; + export const WidgetInfoContext = createContext< WidgetInfoContextInterface | undefined >(undefined); export function WidgetInfo(props: React.PropsWithChildren) { const { manager } = useManager(); - const swaps = getPendingSwaps(manager); + const history = new WidgetHistory(manager); const details = useWalletsStore.use.connectedWallets(); const isLoading = useWalletsStore.use.loading(); const totalBalance = calculateWalletUsdValue(details); const refetch = useWalletsStore.use.getWalletsDetails(); // eslint-disable-next-line react/jsx-no-constructed-context-values const value = { - swaps, + history, wallets: { isLoading, details, diff --git a/widget/embedded/src/containers/WidgetInfo/WidgetInfo.types.ts b/widget/embedded/src/containers/WidgetInfo/WidgetInfo.types.ts index 96a855371c..81bddb2e03 100644 --- a/widget/embedded/src/containers/WidgetInfo/WidgetInfo.types.ts +++ b/widget/embedded/src/containers/WidgetInfo/WidgetInfo.types.ts @@ -1,10 +1,10 @@ +import type { WidgetHistory } from './WidgetInfo.helpers'; import type { ConnectedWallet } from '../../store/wallets'; import type { Wallet } from '../../types'; -import type { PendingSwapWithQueueID } from '@rango-dev/queue-manager-rango-preset'; import type { Token } from 'rango-sdk'; export interface WidgetInfoContextInterface { - swaps: PendingSwapWithQueueID[]; + history: WidgetHistory; wallets: { details: ConnectedWallet[]; totalBalance: string; diff --git a/widget/embedded/src/index.ts b/widget/embedded/src/index.ts index 83f60b8451..6694b0f347 100644 --- a/widget/embedded/src/index.ts +++ b/widget/embedded/src/index.ts @@ -8,8 +8,13 @@ import type { WidgetTheme, } from './types'; import type { + EventSeverity, + PendingSwap, + PendingSwapStep, + PendingSwapWithQueueID, Route, RouteEvent, + RouteExecutionEvents, RouteFailedEvent, RouteStartedEvent, RouteSucceededEvent, @@ -28,17 +33,27 @@ import type { EventHandler as HandleWalletsUpdate, ProviderInterface, } from '@rango-dev/wallets-react'; -import type { WalletType } from '@rango-dev/wallets-shared'; +import type { + WalletInfo, + WalletState, + WalletType, +} from '@rango-dev/wallets-shared'; import { MainEvents, + PendingSwapNetworkStatus, RouteEventType, StepEventType, StepExecutionBlockedEventStatus, StepExecutionEventStatus, useEvents as useWidgetEvents, } from '@rango-dev/queue-manager-rango-preset'; -import { useWallets } from '@rango-dev/wallets-react'; +import { + readAccountAddress, + useWallets, + Events as WalletEvents, +} from '@rango-dev/wallets-react'; +import { Networks, WalletTypes } from '@rango-dev/wallets-shared'; import { WidgetWallets } from './containers/Wallets'; import { Widget } from './containers/Widget'; @@ -90,3 +105,23 @@ export { StepExecutionEventStatus, StepExecutionBlockedEventStatus, }; + +// Internal type exports for Rango +export type { + WalletState, + WalletInfo, + PendingSwap, + PendingSwapWithQueueID, + PendingSwapStep, + RouteExecutionEvents, + EventSeverity, +}; + +// Internal function and enum exports for Rango +export { + readAccountAddress, + Networks, + WalletEvents, + WalletTypes, + PendingSwapNetworkStatus, +};