Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paradex wallet connect improvements #1221

Merged
merged 4 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 }),
},))
Loading