Skip to content

Commit

Permalink
chore: sync staging → main #103
Browse files Browse the repository at this point in the history
Griko Nibras authored Nov 22, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents f31003e + 85bced4 commit a22908f
Showing 15 changed files with 2,683 additions and 2,029 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/branches.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Branches
on:
push:
branches: [main]
workflow_dispatch:

jobs:
sync-main-dev:
name: branch main -> dev
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Fast-forward main -> dev
run: |
git checkout dev
git merge --ff-only main
git push --set-upstream origin dev
2 changes: 1 addition & 1 deletion chain-registry
Submodule chain-registry updated 124 files
4,177 changes: 2,270 additions & 1,907 deletions package-lock.json

Large diffs are not rendered by default.

89 changes: 45 additions & 44 deletions package.json
Original file line number Diff line number Diff line change
@@ -12,95 +12,96 @@
"test:e2e": "playwright test --project=chromium"
},
"dependencies": {
"@cosmjs/amino": "^0.31.0",
"@cosmjs/cosmwasm-stargate": "^0.31.0",
"@cosmjs/encoding": "^0.31.0",
"@cosmjs/ledger-amino": "^0.31.0",
"@cosmjs/math": "^0.31.0",
"@cosmjs/proto-signing": "^0.31.0",
"@cosmjs/stargate": "^0.31.0",
"@cosmjs/tendermint-rpc": "^0.31.0",
"@cosmjs/amino": "^0.31.3",
"@cosmjs/cosmwasm-stargate": "^0.31.3",
"@cosmjs/encoding": "^0.31.3",
"@cosmjs/ledger-amino": "^0.31.3",
"@cosmjs/math": "^0.31.3",
"@cosmjs/proto-signing": "^0.31.3",
"@cosmjs/stargate": "^0.31.3",
"@cosmjs/tendermint-rpc": "^0.31.3",
"@cosmos-kit/core": "^2.0.3",
"@cosmos-kit/cosmostation-extension": "^2.0.3",
"@cosmos-kit/keplr-extension": "^2.0.3",
"@cosmos-kit/leap-extension": "^2.0.3",
"@cosmos-kit/leap-metamask-cosmos-snap": "^0.2.2",
"@cosmos-kit/okxwallet": "^2.3.8",
"@cosmos-kit/react": "^2.0.3",
"@cosmos-kit/react-lite": "^2.1.4",
"@cosmos-kit/web3auth": "^2.0.3",
"@evmos/proto": "^0.2.1",
"@evmos/provider": "^0.3.1",
"@evmos/transactions": "^0.3.2",
"@graz-sh/types": "^0.0.12",
"@headlessui/react": "^1.7.14",
"@graz-sh/types": "^0.0.13",
"@headlessui/react": "^1.7.17",
"@heroicons/react": "^2.0.18",
"@injectivelabs/sdk-ts": "^1.11.0",
"@injectivelabs/utils": "^1.11.0",
"@keplr-wallet/types": "^0.12.4",
"@injectivelabs/sdk-ts": "^1.14.4",
"@injectivelabs/utils": "^1.14.4",
"@keplr-wallet/types": "^0.12.44",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-toast": "^1.1.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-toggle-group": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.6",
"@sentry/nextjs": "^7.77.0",
"@radix-ui/react-tooltip": "^1.0.7",
"@sentry/nextjs": "^7.81.1",
"@skip-router/core": "^0.1.0-rc22",
"@tanstack/react-query": "^4.29.5",
"@types/node": "20.1.2",
"@types/react": "18.2.6",
"@types/react-dom": "18.2.4",
"@vercel/analytics": "^1.0.1",
"@tanstack/react-query": "^5.8.4",
"@types/node": "20.9.4",
"@types/react": "18.2.38",
"@types/react-dom": "18.2.17",
"@vercel/analytics": "^1.1.1",
"@web3modal/core": "^2.7.0",
"@web3modal/ui": "^2.7.0",
"autoprefixer": "10.4.14",
"axios": "^1.4.0",
"autoprefixer": "10.4.16",
"axios": "^1.6.2",
"classnames": "^2.3.2",
"clsx": "^2.0.0",
"cosmjs-types": "^0.7.2",
"cosmjs-types": "^0.9.0",
"date-fns": "^2.30.0",
"download": "^8.0.0",
"eslint-config-next": "13.4.1",
"eslint-config-next": "14.0.3",
"ethers": "^6.3.0",
"framer-motion": "^10.12.18",
"framer-motion": "^10.16.5",
"http-proxy": "^1.18.1",
"next": "13.4.7",
"pino-pretty": "^10.0.0",
"postcss": "8.4.23",
"next": "14.0.3",
"pino-pretty": "^10.2.3",
"postcss": "8.4.31",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.3.2",
"tailwindcss": "3.3.5",
"usehooks-ts": "^2.9.1",
"viem": "^1.14.0",
"wagmi": "^1.4.3",
"zod": "^3.22.4",
"zustand": "^4.4.4"
"zustand": "^4.4.6"
},
"devDependencies": {
"@playwright/test": "^1.38.0",
"@tanstack/eslint-plugin-query": "^4.36.0",
"@tanstack/eslint-plugin-query": "^5.8.4",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/degit": "^2.8.5",
"@types/download": "^8.0.2",
"@types/http-proxy": "^1.17.11",
"@types/degit": "^2.8.6",
"@types/download": "^8.0.5",
"@types/http-proxy": "^1.17.14",
"@types/jest": "^29.5.3",
"@types/testing-library__jest-dom": "^5.14.5",
"@typescript-eslint/eslint-plugin": "^6.7.0",
"@typescript-eslint/parser": "^6.7.0",
"@typescript-eslint/eslint-plugin": "^6.12.0",
"@typescript-eslint/parser": "^6.12.0",
"degit": "npm:tiged@^2.12.5",
"eslint": "^8.49.0",
"eslint": "^8.54.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"globby": "^13.2.2",
"globby": "^14.0.0",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"msw": "^1.2.3",
"p-map": "^6.0.0",
"prettier": "^3.0.3",
"prettier": "^3.1.0",
"resize-observer-polyfill": "^1.5.1",
"ts-jest": "^29.1.1",
"tsx": "^3.14.0",
"typescript": "^5.0.4"
"tsx": "^4.2.1",
"typescript": "5.1.x"
}
}
158 changes: 90 additions & 68 deletions src/components/AssetInput.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { clsx } from "clsx";
import { ethers } from "ethers";
import { FC, Fragment, useEffect, useMemo, useState } from "react";
import { FC, Fragment, useEffect, useMemo, useRef, useState } from "react";

import { Chain } from "@/api/queries";
import { AssetWithMetadata, useAssets } from "@/context/assets";
@@ -11,6 +12,7 @@ import { getFee, useBalancesByChain } from "@/utils/utils";

import AssetSelect from "./AssetSelect";
import ChainSelect from "./ChainSelect";
import { UsdDiff, UsdValue, useUsdDiffReset } from "./UsdValue";

interface Props {
amount: string;
@@ -51,11 +53,7 @@ const AssetInput: FC<Props> = ({

const { address } = useAccount(chain?.chainID ?? "cosmoshub-4");

const { data: balances, isInitialLoading } = useBalancesByChain(
address,
chain,
showBalance,
);
const { data: balances } = useBalancesByChain(address, chain, showBalance);

const selectedAssetBalance = useMemo(() => {
if (!asset || !balances) {
@@ -83,12 +81,17 @@ const AssetInput: FC<Props> = ({

const { slippage } = useSettingsStore();

// hotfix side effect to prevent negative amounts
const reset = useUsdDiffReset();
useEffect(() => {
if (parseFloat(amount) < 0) {
onAmountChange?.("0.0");
}
}, [amount, onAmountChange]);
const parsed = parseFloat(amount);

// hotfix side effect to prevent negative amounts
if (parsed < 0) onAmountChange?.("0.0");
if (parsed == 0) reset();
}, [amount, onAmountChange, reset]);

const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => inputRef.current?.focus(), []);

return (
<Fragment>
@@ -114,25 +117,18 @@ const AssetInput: FC<Props> = ({
<div>
{!onAmountChange && (
<p
className={`w-full text-3xl font-medium h-10 ${
amount === "0.0" ? "text-neutral-300" : "text-black"
}`}
className={clsx(
"w-full text-3xl font-medium h-10",
amount === "0.0" ? "text-neutral-300" : "text-black",
)}
data-testid="amount"
>
{amount}
</p>
)}
{showSlippage && !onAmountChange && amount !== "0.0" && (
<button
className="text-neutral-400 text-sm hover:underline"
onClick={() => disclosure.open("settingsDialog")}
>
Max Slippage: {slippage}%
</button>
)}
{onAmountChange && (
<input
className="w-full text-3xl font-medium focus:outline-none placeholder:text-neutral-300 h-10"
className="w-full text-3xl font-medium focus:outline-none placeholder:text-neutral-300 h-10 tabular-nums"
type="text"
placeholder="0.0"
value={amount}
@@ -159,58 +155,84 @@ const AssetInput: FC<Props> = ({
onAmountChange?.("");
}
}}
ref={inputRef}
/>
)}
</div>
{showBalance && address && (
<div className="flex items-center justify-between">
{isInitialLoading && (
<div className="w-[100px] h-[20.5px] bg-neutral-100 animate-pulse" />
<div className="flex items-center space-x-2 tabular-nums h-8">
{asset && parseFloat(amount) > 0 && (
<div className="text-neutral-400 text-sm">
<UsdValue
chainId={asset.originChainID}
denom={asset.originDenom}
value={amount}
context={onAmountChange ? "src" : "dest"}
/>
</div>
)}
{!isInitialLoading && selectedAssetBalance && (
<Fragment>
<p className="text-sm font-medium text-neutral-400">
AVAILABLE:{" "}
<span className="text-neutral-700">
{selectedAssetBalance} {asset?.symbol}
</span>
</p>
<div>
<button
className="font-extrabold text-xs bg-neutral-400 text-white px-3 py-1 rounded-md transition-transform enabled:hover:scale-110 enabled:hover:rotate-2 disabled:cursor-not-allowed"
disabled={maxButtonDisabled}
onClick={() => {
if (!selectedAssetBalance || !chain || !asset) {
return;
}

const feeDenom = getFeeDenom(chain.chainID);

let amount = selectedAssetBalance;

// if selected asset is the fee denom, subtract the fee
if (feeDenom && feeDenom.denom === asset.denom) {
const fee = getFee(chain.chainID);

const feeInt = parseFloat(
ethers.formatUnits(fee.toString(), asset.decimals),
).toFixed(asset.decimals);

amount = (
parseFloat(selectedAssetBalance) - parseFloat(feeInt)
).toFixed(asset.decimals);
}

onAmountChange?.(amount);
}}
{!onAmountChange && (
<UsdDiff.Value>
{({ isLoading, percentage }) => (
<div
className={clsx(
"text-sm",
isLoading && "hidden",
percentage > 0 ? "text-green-500" : "text-red-500",
)}
>
MAX
</button>
({percentage.toFixed(2)}%)
</div>
)}
</UsdDiff.Value>
)}
<div className="flex-grow" />
{showBalance && address && selectedAssetBalance && (
<div className="text-neutral-400 text-sm flex items-center space-x-2">
<div className="max-w-[16ch] truncate">
Balance: {selectedAssetBalance}
</div>
</Fragment>
<button
className={clsx(
"px-2 py-1 rounded-md uppercase font-semibold text-xs bg-[#FF486E] text-white",
"transition-transform enabled:hover:scale-110 enabled:hover:rotate-2 disabled:cursor-not-allowed",
)}
disabled={maxButtonDisabled}
onClick={() => {
if (!selectedAssetBalance || !chain || !asset) return;

const feeDenom = getFeeDenom(chain.chainID);

let amount = selectedAssetBalance;

// if selected asset is the fee denom, subtract the fee
if (feeDenom && feeDenom.denom === asset.denom) {
const fee = getFee(chain.chainID);

const feeInt = parseFloat(
ethers.formatUnits(fee.toString(), asset.decimals),
).toFixed(asset.decimals);

amount = (
parseFloat(selectedAssetBalance) - parseFloat(feeInt)
).toFixed(asset.decimals);
}

onAmountChange?.(amount);
}}
>
Max
</button>
</div>
)}
{showSlippage && !onAmountChange && amount !== "0.0" && (
<button
className="text-neutral-400 text-sm hover:underline"
onClick={() => disclosure.open("settingsDialog")}
>
Max Slippage: {slippage}%
</button>
)}
</div>
)}
</div>
</div>
<Toast
open={isError}
7 changes: 4 additions & 3 deletions src/components/SwapWidget/SwapWidget.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ArrowsUpDownIcon } from "@heroicons/react/20/solid";
import * as Tooltip from "@radix-ui/react-tooltip";
import { FC, Fragment } from "react";
import { FC } from "react";

import { useChains as useSkipChains } from "@/api/queries";
import { useAccount } from "@/hooks/useAccount";
@@ -16,6 +16,7 @@ import RouteTransactionCountBanner from "../RouteTransactionCountBanner";
import { SettingsButton } from "../SettingsButton";
import { SettingsDialog } from "../SettingsDialog";
import TransactionDialog from "../TransactionDialog";
import { UsdDiff } from "../UsdValue";
import { useWalletModal, WalletModal } from "../WalletModal";
import { useSwapWidget } from "./useSwapWidget";

@@ -64,7 +65,7 @@ export const SwapWidget: FC = () => {
sourceChain.chainType !== destinationChain.chainType;

return (
<Fragment>
<UsdDiff.Provider>
<Tooltip.Provider>
<div className="space-y-6">
<div className="flex items-center">
@@ -225,6 +226,6 @@ export const SwapWidget: FC = () => {
<JsonDialog />
</Tooltip.Provider>
<WalletModal />
</Fragment>
</UsdDiff.Provider>
);
};
91 changes: 91 additions & 0 deletions src/components/UsdValue.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { createContext, ReactNode, useContext, useEffect, useRef } from "react";
import { create } from "zustand";

import { Args, useUsdDiffValue, useUsdValue } from "@/hooks/useUsdValue";

type UsdValueProps = Args & {
loading?: ReactNode;
context?: "src" | "dest";
};

export const UsdValue = ({
loading = "...",
context,
...args
}: UsdValueProps) => {
const { data: usdValue = 0, isLoading } = useUsdValue(args);

const contextStore = useContext(ctx);
useEffect(() => {
if (contextStore && context) {
contextStore.setState({ [context]: args });
return () => {
contextStore.setState({ [context]: undefined });
};
}
});

return <>{isLoading ? loading : `$${usdValue.toFixed(2)}`}</>;
};

///////////////////////////////////////////////////////////////////////////////

type Store = { src?: Args; dest?: Args };

const createContextStore = () => {
return create<Store>(() => ({ src: undefined, dest: undefined }));
};

type Context = ReturnType<typeof createContextStore> | undefined;
const ctx = createContext<Context>(undefined);

///////////////////////////////////////////////////////////////////////////////

type UsdDiffValueProps = {
src: Args;
dest: Args;
onLoading?: ReactNode;
onUndefined?: ReactNode;
children?: (args: { isLoading: boolean; percentage: number }) => ReactNode;
};

export const UsdDiffValue = (props: UsdDiffValueProps) => {
const { src, dest, onLoading = "...", children } = props;
const { data: percentage = 0, isLoading } = useUsdDiffValue([src, dest]);

if (children) {
return <>{children({ isLoading, percentage })}</>;
}
if (isLoading) {
return <>{onLoading}</>;
}
return <>{percentage.toFixed(2)}%</>;
};

export const UsdDiff = {
Provider: ({ children }: { children: ReactNode }) => {
const store = useRef<Context>(createContextStore());
return <ctx.Provider value={store.current}>{children}</ctx.Provider>;
},
Value: (props: Omit<UsdDiffValueProps, "src" | "dest">) => {
const useStore = useContext(ctx);
if (!useStore) {
throw new Error("UsdDiff.Value must be used inside UsdDiff.Provider");
}

const { src, dest } = useStore();
if (!(src && dest)) {
return <>{props.onUndefined ?? null}</>;
}

return <UsdDiffValue src={src} dest={dest} {...props} />;
},
};

export const useUsdDiffReset = () => {
const store = useContext(ctx);
if (!store) {
throw new Error("useUsdDiffReset must be used inside UsdDiff.Provider");
}
return () => store.setState({ src: undefined, dest: undefined });
};
15 changes: 11 additions & 4 deletions src/components/WalletModal/WalletModal.tsx
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import { FC } from "react";
import { useAccount, useConnect, useDisconnect } from "wagmi";

import { useChainByID } from "@/api/queries";
import { EVM_WALLET_LOGOS } from "@/constants/constants";
import { EVM_WALLET_LOGOS, INJECTED_EVM_WALLET_LOGOS } from "@/constants/constants";
import { DialogContent } from "@/elements/Dialog";
import { getChainByID } from "@/utils/utils";

@@ -128,12 +128,19 @@ const WalletModalWithContext: FC = () => {
continue;
}

let logoUrl

if (connector.id === "injected" && connector.name in INJECTED_EVM_WALLET_LOGOS) {
logoUrl = INJECTED_EVM_WALLET_LOGOS[connector.name]
} else {
logoUrl = EVM_WALLET_LOGOS[connector.id]
}

const minimalWallet: MinimalWallet = {
walletName: connector.id,
walletPrettyName:
connector.id === "injected" ? "Browser Wallet" : connector.name,
walletPrettyName: connector.name,
walletInfo: {
logo: EVM_WALLET_LOGOS[connector.id],
logo: logoUrl,
},
connect: async () => {
await connect({ connector });
4 changes: 4 additions & 0 deletions src/constants/constants.ts
Original file line number Diff line number Diff line change
@@ -26,6 +26,10 @@ export const EVM_WALLET_LOGOS: Record<string, string> = {
"https://raw.githubusercontent.com/rainbow-me/rainbowkit/6b460fcba954e155828e03f46228ee88a171a83b/packages/rainbowkit/src/wallets/walletConnectors/injectedWallet/injectedWallet.svg",
};

export const INJECTED_EVM_WALLET_LOGOS: Record<string, string> = {
"OKX Wallet": "https://raw.githubusercontent.com/rainbow-me/rainbowkit/6b460fcba954e155828e03f46228ee88a171a83b/packages/rainbowkit/src/wallets/walletConnectors/okxWallet/okxWallet.svg",
}

export const kava = {
id: 2222,
name: "Kava",
77 changes: 77 additions & 0 deletions src/hooks/useUsdValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useQuery } from "@tanstack/react-query";
import { useMemo } from "react";

import { getAssets } from "@/chains";
import { raise } from "@/utils/assert";
import { getUsdPrice } from "@/utils/llama";

export interface Args {
chainId: string;
denom: string;
value: string;
}

export function useUsdValue({ chainId, denom, value }: Args) {
const queryKey = useMemo(
() => ["USE_USD_VALUE", chainId, denom, value] as const,
[chainId, denom, value],
);

const enabled = useMemo(() => {
const parsed = parseFloat(value);
return !isNaN(parsed) && parsed > 0;
}, [value]);

return useQuery({
queryKey,
queryFn: async ({ queryKey: [, chainId, denom, value] }) => {
return getUsdValue({ chainId, denom, value });
},
staleTime: 1000 * 60, // 1 minute
enabled,
});
}

export function useUsdDiffValue([args1, args2]: [Args, Args]) {
const queryKey = useMemo(() => {
const hash = [...Object.values(args1), ...Object.values(args2)].join("-");
return ["USE_USD_DIFF_VALUES", hash] as const;
}, [args1, args2]);

const enabled = useMemo(() => {
const parsed1 = parseFloat(args1.value);
const parsed2 = parseFloat(args2.value);
return !isNaN(parsed1) && parsed1 > 0 && !isNaN(parsed2) && parsed2 > 0;
}, [args1.value, args2.value]);

return useQuery({
// intentionally not including args1 and args2 since query key is using
// hashed values of args1 and args2
// eslint-disable-next-line @tanstack/query/exhaustive-deps
queryKey,
queryFn: async () => {
const [v1, v2] = await Promise.all([
getUsdValue(args1),
getUsdValue(args2),
]);
// return percentage difference
return ((v2 - v1) / v1) * 100;
},
staleTime: 1000 * 60, // 1 minute
enabled,
});
}

async function getUsdValue({ chainId, denom, value }: Args) {
const assets = getAssets(chainId);
const asset =
assets.find((asset) => asset.base === denom) ||
raise(`getUsdValue error: ${denom} not found in ${chainId}`);
const coingeckoId =
asset.coingecko_id ||
raise(
`getUsdValue error: ${denom} does not have a 'coingecko_id' in ${chainId}`,
);
const usd = await getUsdPrice({ coingeckoId });
return parseFloat(value) * usd;
}
4 changes: 4 additions & 0 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import { wallets as cosmostationWallets } from "@cosmos-kit/cosmostation-extensi
import { wallets as keplrWallets } from "@cosmos-kit/keplr-extension";
import { wallets as leapWallets } from "@cosmos-kit/leap-extension";
import { wallets as metamaskWallets } from "@cosmos-kit/leap-metamask-cosmos-snap";
import { wallets as okxWallets } from "@cosmos-kit/okxwallet";
import { ChainProvider } from "@cosmos-kit/react";
import * as RadixToast from "@radix-ui/react-toast";
import { QueryClientProvider } from "@tanstack/react-query";
@@ -25,6 +26,7 @@ import { AssetsProvider } from "@/context/assets";
import { ToastProvider } from "@/context/toast";
import { SkipProvider } from "@/solve";
import { queryClient } from "@/utils/query";
import { OKXConnector } from "@/wallets/OKXConnector";

const { publicClient, chains: evmChains } = configureChains(EVM_CHAINS, [
publicProvider(),
@@ -36,6 +38,7 @@ const wagmiConfig = createConfig({
new MetaMaskConnector({
chains: evmChains,
}),
OKXConnector,
],
publicClient,
});
@@ -51,6 +54,7 @@ export default function App({ Component, pageProps }: AppProps) {
...cosmostationWallets,
...leapWallets,
...metamaskWallets,
...okxWallets,
];

return (
34 changes: 34 additions & 0 deletions src/utils/llama.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { z } from "zod";

interface Args {
coingeckoId: string;
}

const cache = new Map<string, number>();

export async function getUsdPrice({ coingeckoId }: Args) {
const cached = cache.get(coingeckoId);
if (cached) return cached;

const endpoint = `https://coins.llama.fi/prices/current/coingecko:${coingeckoId}`;

const response = await fetch(endpoint);
const data = await response.json();

const { coins } = await priceResponseSchema.parseAsync(data);
const { price } = coins[`coingecko:${coingeckoId}`];

cache.set(coingeckoId, price);
return price;
}

const priceResponseSchema = z.object({
coins: z.record(
z.object({
price: z.number(),
symbol: z.string(),
timestamp: z.number(),
confidence: z.number(),
}),
),
});
5 changes: 4 additions & 1 deletion src/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -222,6 +222,10 @@ export async function getOfflineSigner(
return walletClient.getOfflineSignerDirect(chainId);
}

if (walletClient.getOfflineSigner) {
return walletClient.getOfflineSigner(chainId, "direct");
}

throw new Error("unsupported wallet");
}

@@ -429,7 +433,6 @@ export function useBalancesByChain(
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
keepPreviousData: true,
enabled: !!chain && !!address && enabled,
});
}
26 changes: 26 additions & 0 deletions src/wallets/OKXConnector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { InjectedConnector } from "@wagmi/core";

export const OKXConnector = new InjectedConnector({
options: {
name: "OKX Wallet",
shimDisconnect: true,
getProvider: () => {
if (typeof window === 'undefined') return

if (typeof window.okexchain === 'undefined') return

if (typeof window.ethereum !== 'undefined') return window.ethereum

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const isOkxWallet = (ethereum?: any) => !!ethereum?.isOkxWallet

if (isOkxWallet(window.okexchain.ethereum)) return window.okexchain.ethereum

if (window.okexchain.ethereum?.providers) {
return window.okexchain.ethereum.providers.find(isOkxWallet)
}

return window["okexchain"]["ethereum"] ?? null
}
}
})
5 changes: 4 additions & 1 deletion window.d.ts
Original file line number Diff line number Diff line change
@@ -2,5 +2,8 @@ import { Window as KeplrWindow } from "@keplr-wallet/types";

declare global {
// eslint-disable-next-line no-unused-vars
interface Window extends KeplrWindow {}
interface Window extends KeplrWindow {
okexchain?: any
ethereum?: any
}
}

1 comment on commit a22908f

@vercel
Copy link

@vercel vercel bot commented on a22908f Nov 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.