Skip to content

Commit

Permalink
Merge pull request #1221 from layerswap/dev-paradex-fix
Browse files Browse the repository at this point in the history
Paradex wallet connect improvements
  • Loading branch information
babkenmes authored Jan 16, 2025
2 parents bc35e93 + 99d56f9 commit b35a653
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 52 deletions.
6 changes: 4 additions & 2 deletions components/Input/SourceWalletPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const Component: FC = () => {
const selectedWallet = selectedSourceAccount?.wallet
//TODO: sort by active wallet
const defaultWallet = walletNetwork && availableWallets?.find(w => !w.isNotAvailable)

const source_addsress = selectedSourceAccount?.address

useEffect(() => {
Expand Down Expand Up @@ -176,6 +177,7 @@ export const FormSourceWalletButton: FC = () => {
setMounWalletPortal(false)
}
const availableWallets = provider?.connectedWallets?.filter(w => !w.isNotAvailable) || []

if (!availableWallets.length && walletNetwork) {
return <>
<Connect connectFn={connect} />
Expand All @@ -190,9 +192,9 @@ export const FormSourceWalletButton: FC = () => {
}
else if (availableWallets.length > 0 && walletNetwork && values.fromCurrency) {
return <>
<button type="button" className="w-full" onClick={handleWalletChange}>
<div className="w-full" onClick={handleWalletChange}>
<Connect />
</button>
</div>
<VaulDrawer
show={openModal}
setShow={setOpenModal}
Expand Down
1 change: 0 additions & 1 deletion components/Swap/Form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ const SwapForm: FC<Props> = ({ partner }) => {

const { validationMessage } = useValidationContext();

const layerswapApiClient = new LayerSwapApiClient()
const query = useQueryState();
let valuesSwapperDisabled = false;

Expand Down
11 changes: 7 additions & 4 deletions components/Wallet/ConnectedWallets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ const WalletsHeaderWalletsList = ({ wallets }: { wallets: Wallet[] }) => {
}

const WalletsIcons = ({ wallets }: { wallets: Wallet[] }) => {
const firstWallet = wallets[0]
const secondWallet = wallets[1]

const uniqueWallets = wallets.filter((wallet, index, self) => index === self.findIndex((t) => t.id === wallet.id))

const firstWallet = uniqueWallets[0]
const secondWallet = uniqueWallets[1]

return (
<div className="-space-x-2 flex">
Expand All @@ -59,9 +62,9 @@ const WalletsIcons = ({ wallets }: { wallets: Wallet[] }) => {
<secondWallet.icon className="rounded-full border-2 border-secondary-600 bg-secondary-700 flex-shrink-0 h-6 w-6" />
}
{
wallets.length > 2 &&
uniqueWallets.length > 2 &&
<div className="h-6 w-6 flex-shrink-0 rounded-full justify-center p-1 bg-secondary-600 text-primary-text overlfow-hidden text-xs">
<span><span>+</span>{wallets.length - 2}</span>
<span><span>+</span>{uniqueWallets.length - 2}</span>
</div>
}
</div>
Expand Down
4 changes: 2 additions & 2 deletions components/WalletProviders/Wagmi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { NetworkType } from "../../Models/Network";
import resolveChain from "../../lib/resolveChain";
import React from "react";
import NetworkSettings from "../../lib/NetworkSettings";
import { WagmiProvider, injected } from 'wagmi'
import { WagmiProvider } from 'wagmi'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { createConfig } from 'wagmi';
import { Chain, http } from 'viem';
import { WalletModalProvider } from '../WalletModal';
import { argent } from '../../lib/wallets/connectors/argent';
import { rainbow } from '../../lib/wallets/connectors/rainbow';
import { coinbaseWallet, metaMask, walletConnect } from 'wagmi/connectors'
import { coinbaseWallet, metaMask, walletConnect } from '@wagmi/connectors'
import { hasInjectedProvider } from '../../lib/wallets/connectors/getInjectedConnector';
import { bitget } from '../../lib/wallets/connectors/bitget';
import { isMobile } from '../../lib/isMobile';
Expand Down
10 changes: 2 additions & 8 deletions lib/balances/providers/paradexBalanceProvider.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { Balance } from "../../../Models/Balance";
import { NetworkWithTokens } from "../../../Models/Network";
import { checkStorageIsAvailable } from "../../../helpers/storageAvailable";
import KnownInternalNames from "../../knownIds";
import * as Paradex from "../../wallets/paradex/lib";
import { LOCAL_STORAGE_KEY } from "../../wallets/paradex/lib/constants";

export class ParadexBalanceProvider {
supportsNetwork(network: NetworkWithTokens): boolean {
Expand All @@ -13,10 +11,6 @@ export class ParadexBalanceProvider {
fetchBalance = async (address: string, network: NetworkWithTokens) => {

try {
const paradexAccounts = checkStorageIsAvailable("localStorage") && window.localStorage?.getItem(LOCAL_STORAGE_KEY);
const paradexAddress = paradexAccounts && JSON.parse(paradexAccounts)[address.toLowerCase()]
if (!network?.tokens || !paradexAddress) return

const environment = process.env.NEXT_PUBLIC_API_VERSION === 'sandbox' ? 'testnet' : 'prod'
const config = await Paradex.Config.fetchConfig(environment);

Expand All @@ -29,7 +23,7 @@ export class ParadexBalanceProvider {
const getBalanceResult = await Paradex.Paraclear.getTokenBalance({
provider: paraclearProvider, //account can be passed as the provider
config,
account: { address: paradexAddress },
account: { address },
token: token.symbol,
});
const balance = {
Expand All @@ -41,8 +35,8 @@ export class ParadexBalanceProvider {
isNativeCurrency: false
}
result.push(balance)
}

}
return result
}
catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion lib/wallets/connectors/EthereumProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ export type QrModalOptions = Pick<
>;

import type { Connector, CreateConnectorFn } from 'wagmi';
import type { WalletConnectParameters } from 'wagmi/connectors';
import type { WalletConnectParameters } from '@wagmi/connectors';
export type InstructionStepName =
| 'install'
| 'create'
Expand Down
2 changes: 1 addition & 1 deletion lib/wallets/connectors/browserInjected/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createConnector } from 'wagmi'
import { injected } from 'wagmi/connectors'
import { injected } from '@wagmi/connectors'

export function browserInjected() {
return createConnector((config) => {
Expand Down
2 changes: 1 addition & 1 deletion lib/wallets/connectors/getInjectedConnector.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createConnector } from 'wagmi';
import { injected } from 'wagmi/connectors';
import { injected } from '@wagmi/connectors';
import { WalletProviderFlags, WindowProvider, CreateConnector, WalletDetailsParams } from './EthereumProvider';
// import type { CreateConnector, WalletDetailsParams } from './Wallet';

Expand Down
79 changes: 49 additions & 30 deletions lib/wallets/paradex/useParadex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import { walletClientToSigner } from "../../ethersToViem/ethers"
import AuhorizeEthereum from "./Authorize/Ethereum"
import { getWalletClient } from '@wagmi/core'
import { useConfig } from "wagmi"
import { usePersistedState } from "../../../hooks/usePersistedState"
import { LOCAL_STORAGE_KEY } from "./lib/constants"
import { switchChain } from '@wagmi/core'
import { useSettingsState } from "../../../context/settings"
import shortenAddress from "../../../components/utils/ShortenAddress"

type Props = {
network: Network | undefined,
Expand All @@ -28,8 +28,10 @@ export default function useParadex({ network }: Props): WalletProvider {
const { networks } = useSettingsState()
const selectedProvider = useWalletStore((state) => state.selectedProveder)
const selectProvider = useWalletStore((state) => state.selectProvider)
const [paradexAddresses, updateParadexAddresses] = usePersistedState<{ [key: string]: string }>({}, LOCAL_STORAGE_KEY);

const paradexAccounts = useWalletStore((state) => state.paradexAccounts)
const addParadexAccount = useWalletStore((state) => state.addParadexAccount)
const removeParadexAccount = useWalletStore((state) => state.removeParadexAccount)
const paradexNetwork = networks.find(n => n.name === KnownInternalNames.Networks.ParadexMainnet || n.name === KnownInternalNames.Networks.ParadexTestnet)
const withdrawalSupportedNetworks = [
KnownInternalNames.Networks.ParadexMainnet,
KnownInternalNames.Networks.ParadexTestnet,
Expand All @@ -49,6 +51,7 @@ export default function useParadex({ network }: Props): WalletProvider {
}
const config = useConfig()


const connectConnector = async ({ connector }: { connector: InternalConnector & LSConnector }) => {

try {
Expand All @@ -58,40 +61,37 @@ export default function useParadex({ network }: Props): WalletProvider {
if (isEvm) {
const connectionResult = evmProvider.connectConnector && await evmProvider.connectConnector({ connector })
if (!connectionResult) return
selectProvider(evmProvider.name)
if (!paradexAddresses[connectionResult.address.toLowerCase()]) {
if (!paradexAccounts?.[connectionResult.address.toLowerCase()]) {
const l1Network = networks.find(n => n.name === KnownInternalNames.Networks.EthereumMainnet || n.name === KnownInternalNames.Networks.EthereumSepolia);
const l1ChainId = Number(l1Network?.chain_id)
if (!Number(l1ChainId)) {
throw Error("Could not find ethereum network")
}
const client = await getWalletClient(config, {
chainId: l1ChainId,
})
await switchChain(config, { chainId: l1ChainId })
const client = await getWalletClient(config)
const ethersSigner = walletClientToSigner(client)
if (!ethersSigner) {
throw Error("Could not initialize ethers signer")
}
const paradexAccount = await AuhorizeEthereum(ethersSigner)
updateParadexAddresses({ ...paradexAddresses, [connectionResult.address.toLowerCase()]: paradexAccount.address })
addParadexAccount({ l1Address: connectionResult.address, paradexAddress: paradexAccount.address })
}
const wallet: Wallet = { ...connectionResult, providerName: name }
return wallet
selectProvider(evmProvider.name)
return resolveSingleWallet(connectionResult, name, paradexAccounts, removeParadexAccount, paradexNetwork?.logo)
}
else if (isStarknet) {
const connectionResult = starknetProvider.connectConnector && await starknetProvider.connectConnector({ connector })
if (!connectionResult) return
selectProvider(starknetProvider.name)
if (!paradexAddresses[connectionResult.address.toLowerCase()]) {
if (!paradexAccounts?.[connectionResult.address.toLowerCase()]) {
const snAccount = connectionResult.metadata?.starknetAccount
if (!snAccount) {
throw Error("Starknet account not found")
}
const paradexAccount = await AuthorizeStarknet(snAccount)
updateParadexAddresses({ ...paradexAddresses, [connectionResult.address.toLowerCase()]: paradexAccount.address })
addParadexAccount({ l1Address: connectionResult.address, paradexAddress: paradexAccount.address })
}
const wallet: Wallet = { ...connectionResult, providerName: name }
return wallet
selectProvider(starknetProvider.name)
return resolveSingleWallet(connectionResult, name, paradexAccounts, removeParadexAccount, paradexNetwork?.logo)
}
} catch (e) {
//TODO: handle error like in transfer
Expand All @@ -106,20 +106,19 @@ export default function useParadex({ network }: Props): WalletProvider {
}
}

const paradexL1Addresses = useMemo(() => Object.keys(paradexAddresses), [paradexAddresses])
const connectedWallets = useMemo(() => {
return [
...(evmProvider.connectedWallets ?
evmProvider.connectedWallets.filter(w => w.addresses.some(wa => paradexL1Addresses.some(pa => pa.toLowerCase() === wa.toLowerCase()))).map(w => ({ ...w, providerName: name })) : []),
...(starknetProvider?.connectedWallets ?
starknetProvider.connectedWallets.filter(w => w.addresses.some(wa => paradexL1Addresses.some(pa => pa.toLowerCase() === wa.toLowerCase()))).map(w => ({ ...w, providerName: name })) : [])]
}, [evmProvider, starknetProvider, paradexL1Addresses])
...resolveWalletsList(evmProvider.connectedWallets, paradexAccounts, name, removeParadexAccount, paradexNetwork?.logo),
...resolveWalletsList(starknetProvider.connectedWallets, paradexAccounts, name, removeParadexAccount, paradexNetwork?.logo)
]
}, [evmProvider, starknetProvider, paradexAccounts])

const availableWalletsForConnect = useMemo(() => {
return [...(evmProvider.availableWalletsForConnect ? evmProvider.availableWalletsForConnect : []), ...(starknetProvider?.availableWalletsForConnect ? starknetProvider.availableWalletsForConnect : [])]
}, [evmProvider, starknetProvider])

const switchAccount = async (wallet: Wallet, address: string) => {

if (evmProvider.connectedWallets?.some(w => w.address.toLowerCase() === address.toLowerCase()) && evmProvider.switchAccount) {
evmProvider.switchAccount(wallet, address)
selectProvider(evmProvider.name)
Expand All @@ -131,11 +130,11 @@ export default function useParadex({ network }: Props): WalletProvider {
}

const activeWallet = useMemo(() => {
if (selectedProvider === starknetProvider.name) {
return starknetProvider?.activeWallet
if (selectedProvider === starknetProvider.name && starknetProvider?.activeWallet) {
return resolveSingleWallet(starknetProvider.activeWallet, name, paradexAccounts, removeParadexAccount, paradexNetwork?.logo)
}
else if (selectedProvider === evmProvider.name) {
return evmProvider?.activeWallet
else if (selectedProvider === evmProvider.name && evmProvider?.activeWallet) {
return resolveSingleWallet(evmProvider.activeWallet, name, paradexAccounts, removeParadexAccount, paradexNetwork?.logo)
}
}, [evmProvider.activeWallet, starknetProvider.activeWallet, selectedProvider])

Expand All @@ -144,14 +143,34 @@ export default function useParadex({ network }: Props): WalletProvider {
connectConnector,
switchAccount,
connectedWallets,
activeWallet: activeWallet,
activeWallet,
withdrawalSupportedNetworks,
availableWalletsForConnect: availableWalletsForConnect as any,
availableWalletsForConnect,
name,
id,
isWrapper: true
}

return provider
}

const resolveWalletsList = (wallets: Wallet[] | undefined, accounts: { [key: string]: string } | undefined, name: string, disconnect: (address: string) => void, networkIcon?: string) => {
const l1Addresses = Object.keys(accounts || {})
if (!l1Addresses.length || !wallets?.length) return []
return wallets.filter(w => w.addresses.some(wa => l1Addresses.some(pa => pa.toLowerCase() === wa.toLowerCase())))
.map(w => (resolveSingleWallet(w, name, accounts, disconnect, networkIcon))).filter(w => w) as Wallet[]
}

const resolveSingleWallet = (wallet: Wallet, name: string, accounts: { [key: string]: string } | undefined, disconnect: (address: string) => void, networkIcon?: string): Wallet | undefined => {
const paradexAddress = accounts?.[wallet.address.toLowerCase()]
if (!paradexAddress) return
const displayName = `${wallet.id} (${shortenAddress(wallet.address)})`
return {
...wallet,
providerName: name,
displayName,
address: paradexAddress,
addresses: [paradexAddress],
disconnect: () => disconnect(wallet.address),
networkIcon
}
}
21 changes: 19 additions & 2 deletions stores/walletStore.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
import { create } from 'zustand'
import { Wallet } from '../Models/WalletProvider';
import { createJSONStorage, persist } from 'zustand/middleware';

type ParadexAccount = {
l1Address: string,
paradexAddress: string
}
interface WalletState {
connectedWallets: Wallet[];
connectWallet: (wallet: Wallet) => void;
disconnectWallet: (providerName: string, connectorName?: string) => void;
selectedProveder?: string;
selectProvider: (providerName: string) => void;
paradexAccounts?: { [key: string]: string };
addParadexAccount: (v: ParadexAccount) => void;
removeParadexAccount: (address: string) => void;
}

export const useWalletStore = create<WalletState>()((set) => ({
export const useWalletStore = create<WalletState>()(persist((set) => ({
connectedWallets: [],
selectProvider: (providerName) => set({ selectedProveder: providerName }),
addParadexAccount: (value) => set((state) => ({ paradexAccounts: { ...state.paradexAccounts, ...{ [value.l1Address.toLowerCase()]: value.paradexAddress } } })),
removeParadexAccount: (value) => set((state) => {
const { [value.toLowerCase()]: _, ...updatedAccounts } = state.paradexAccounts || {};
return { paradexAccounts: updatedAccounts }
}),
// As we are calling this method for adding wallets to the store from provider hooks,
// in some providers they are called from useEffect hooks so are triggered multiple times,
// we check if the wallet is already connected do not modify the state
Expand All @@ -30,4 +43,8 @@ export const useWalletStore = create<WalletState>()((set) => ({
disconnectWallet: (providerName, connectorName) => set((state) => ({
connectedWallets: state.connectedWallets.filter(w => connectorName ? !(w.providerName == providerName && w.id == connectorName) : w.providerName != providerName)
}))
}))
}), {
name: 'ls-paradex-accounts',
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({ paradexAccounts: state.paradexAccounts }),
},))

0 comments on commit b35a653

Please sign in to comment.