diff --git a/spaceward/src/components/ConnectWallet.tsx b/spaceward/src/components/ConnectWallet.tsx
index 2e597376d..d2bc85618 100644
--- a/spaceward/src/components/ConnectWallet.tsx
+++ b/spaceward/src/components/ConnectWallet.tsx
@@ -34,6 +34,7 @@ export function ConnectWallet() {
const { balance } = useAsset("award");
const wardAmount = BigInt(balance?.amount || "0");
const ward = bigintToFixed(wardAmount, {
+ ceil: true,
decimals: 18,
display: 2,
});
diff --git a/spaceward/src/components/ui/icons-crypto.tsx b/spaceward/src/components/ui/icons-crypto.tsx
index fd8dde019..6c7c222d1 100644
--- a/spaceward/src/components/ui/icons-crypto.tsx
+++ b/spaceward/src/components/ui/icons-crypto.tsx
@@ -292,9 +292,9 @@ export const NetworkIcons: Record<
gradientUnits="userSpaceOnUse"
gradientTransform="translate(22.1185 3.26671) scale(7.79125 7.93393)"
>
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
diff --git a/spaceward/src/features/actions/util.ts b/spaceward/src/features/actions/util.ts
index 6842b72e9..9122ea29c 100644
--- a/spaceward/src/features/actions/util.ts
+++ b/spaceward/src/features/actions/util.ts
@@ -269,17 +269,16 @@ export const handleEth = async ({
throw new Error("walletconnect not initialized");
}
- return await w
- .respondSessionRequest({
- topic: walletConnectTopic,
- response: {
- jsonrpc: "2.0",
- id: walletConnectRequestId,
- result: res.hash,
- },
- })
- // fixme
- .then(() => true);
+ await w.respondSessionRequest({
+ topic: walletConnectTopic,
+ response: {
+ jsonrpc: "2.0",
+ id: walletConnectRequestId,
+ result: res.hash,
+ },
+ });
+
+ return true;
}
return provider.waitForTransaction(res.hash).then(() => {
@@ -401,6 +400,7 @@ export const handleCosmos = async ({
}
const client = await StargateClient.connect(rpc);
+
const res = await client.broadcastTx(
cosmos.tx.v1beta1.TxRaw.encode(txRaw).finish(),
);
@@ -409,5 +409,9 @@ export const handleCosmos = async ({
throw new Error("broadcast failed");
}
+ queryClient.invalidateQueries({
+ queryKey: getBalanceQueryKey("cosmos", chainName, "").slice(0, -1),
+ });
+
return true;
};
diff --git a/spaceward/src/features/assets/hooks.ts b/spaceward/src/features/assets/hooks.ts
index 93fc8cd38..cff9dcba0 100644
--- a/spaceward/src/features/assets/hooks.ts
+++ b/spaceward/src/features/assets/hooks.ts
@@ -1,81 +1,21 @@
import { walletContext } from "@cosmos-kit/react-lite";
-import type { ExtendedHttpEndpoint, WalletManager } from "@cosmos-kit/core";
import { AddressType } from "@wardenprotocol/wardenjs/codegen/warden/warden/v1beta3/key";
-import { cosmos } from "@wardenprotocol/wardenjs";
import { useQueries, useQuery } from "@tanstack/react-query";
-import { useContext, useEffect, useMemo, useState } from "react";
-import { COSMOS_CHAINS } from "@/config/tokens";
+import { useContext, useMemo } from "react";
import { useQueryHooks } from "@/hooks/useClient";
import {
balancesQueryCosmos,
balancesQueryEth,
fiatPricesQuery,
+ queryCosmosClients,
} from "./queries";
-import type { CosmosQueryClient, PriceMapSlinky } from "./types";
+import type { PriceMapSlinky } from "./types";
const DERIVE_ADDRESSES = [
AddressType.ADDRESS_TYPE_ETHEREUM,
AddressType.ADDRESS_TYPE_OSMOSIS,
];
-const queryCosmosClients = (walletManager: WalletManager) => {
- const rpcClients: Record = {};
- const rpcRetry: Record = {};
-
- return {
- queryKey: ["cosmos", "rpcClients"],
- queryFn: async () => {
- const clients: [CosmosQueryClient, string][] = [];
-
- for (let i = 0; i < COSMOS_CHAINS.length; i++) {
- const { chainName, rpc } = COSMOS_CHAINS[i];
- let client = rpcClients[chainName];
-
- if (client) {
- // todo implement client health check
- clients.push([client, chainName]);
- continue;
- }
-
- let endpoint: ExtendedHttpEndpoint | string;
-
- if (rpc) {
- const retry = rpcRetry[chainName] ?? 0;
- endpoint = rpc[retry % rpc.length];
- rpcRetry[chainName] = retry + 1;
- } else {
- const repo = walletManager.getWalletRepo(chainName);
- repo.activate();
-
- try {
- endpoint = await repo.getRpcEndpoint();
- } catch (e) {
- console.error(e);
- endpoint = `https://rpc.cosmos.directory/${chainName}`;
- }
- }
-
- try {
- const client =
- await cosmos.ClientFactory.createRPCQueryClient({
- rpcEndpoint:
- typeof endpoint === "string"
- ? endpoint
- : endpoint.url,
- });
-
- clients.push([client, chainName]);
- } catch (e) {
- console.error(e);
- continue;
- }
- }
-
- return clients;
- },
- } as const;
-};
-
export const useAssetQueries = (spaceId?: string | null) => {
const { walletManager } = useContext(walletContext);
const { isReady, useKeysBySpaceId, slinky } = useQueryHooks();
diff --git a/spaceward/src/features/assets/queries.ts b/spaceward/src/features/assets/queries.ts
index c2457b550..958afb33a 100644
--- a/spaceward/src/features/assets/queries.ts
+++ b/spaceward/src/features/assets/queries.ts
@@ -1,12 +1,15 @@
-import { assets } from "chain-registry";
+import { assets, chains } from "chain-registry";
import { ethers } from "ethers";
import { fromBech32, toBech32 } from "@cosmjs/encoding";
+import type { ExtendedHttpEndpoint, WalletManager } from "@cosmos-kit/core";
+import { cosmos } from "@wardenprotocol/wardenjs";
import { AddressType } from "@wardenprotocol/wardenjs/codegen/warden/warden/v1beta3/key";
import { QueryKeyResponse } from "@wardenprotocol/wardenjs/codegen/warden/warden/v1beta3/query";
import erc20Abi from "@/contracts/eip155/erc20Abi";
import multicallAbi from "@/contracts/eip155/multicall3Abi";
import aggregatorV3InterfaceABI from "@/contracts/eip155/priceFeedAbi";
import {
+ COSMOS_CHAINS,
COSMOS_PRICES,
EIP_155_NATIVE_PRICE_FEEDS,
ENABLED_ETH_CHAINS,
@@ -20,6 +23,7 @@ import { getAbiItem, getCosmosChain, getInterface } from "./util";
type ChainName = Parameters[0];
const assetsByChain: Record = {};
+const chainByName: Record = {};
const getChainAssets = (chainName: string) => {
if (chainName in assetsByChain) {
@@ -624,3 +628,99 @@ export const fiatPricesQuery = (enabled: boolean) => {
}),
};
};
+
+const rpcClients: Record = {};
+const rpcRetry: Record = {};
+
+const checkHealth = async (
+ client: CosmosQueryClient | undefined,
+ chainName: string,
+) => {
+ if (!client) {
+ return false;
+ }
+
+ let chain = chainByName[chainName];
+
+ if (!chain) {
+ chain = chains.find((x) => x.chain_name === chainName);
+
+ if (!chain) {
+ console.warn("chain not found", { chainName });
+ return false;
+ }
+
+ chainByName[chainName] = chain;
+ }
+
+ const header = (
+ await client.cosmos.base.tendermint.v1beta1.getLatestBlock({})
+ ).block?.header;
+
+ const isChainIdValid = header?.chainId === chain.chain_id;
+ // todo check against block height and block time
+ return isChainIdValid;
+};
+
+export const queryCosmosClients = (walletManager: WalletManager) => {
+ return {
+ queryKey: ["cosmos", "rpcClients"],
+ queryFn: async () => {
+ const clients: [CosmosQueryClient, string][] = [];
+
+ for (let i = 0; i < COSMOS_CHAINS.length; i++) {
+ const { chainName, rpc } = COSMOS_CHAINS[i];
+ const retries = (rpc?.length ?? 0) + 1;
+
+ for (let i = 0; i < retries; i++) {
+ let client = rpcClients[chainName];
+ const retry = (rpcRetry[chainName] ?? 0) % (retries + 1);
+ rpcRetry[chainName] = retry + 1;
+
+ if (!client) {
+ let endpoint: ExtendedHttpEndpoint | string;
+
+ if (!rpc?.[retry]) {
+ const repo = walletManager.getWalletRepo(chainName);
+ repo.activate();
+
+ try {
+ endpoint = await repo.getRpcEndpoint();
+ } catch (e) {
+ console.error(e);
+ endpoint = `https://rpc.cosmos.directory/${chainName}`;
+ }
+ } else {
+ endpoint = rpc[retry];
+ }
+
+ try {
+ client =
+ await cosmos.ClientFactory.createRPCQueryClient(
+ {
+ rpcEndpoint:
+ typeof endpoint === "string"
+ ? endpoint
+ : endpoint.url,
+ },
+ );
+ } catch (e) {
+ console.error(e);
+ continue;
+ }
+ }
+
+ if (await checkHealth(client, chainName)) {
+ rpcClients[chainName] = client;
+ clients.push([client!, chainName]);
+ break;
+ } else if (rpcClients[chainName]) {
+ delete rpcClients[chainName];
+ }
+ }
+ }
+
+ return clients;
+ },
+ } as const;
+};
diff --git a/spaceward/src/features/staking/StakingHeading.tsx b/spaceward/src/features/staking/StakingHeading.tsx
index dbdf73798..1443cb12a 100644
--- a/spaceward/src/features/staking/StakingHeading.tsx
+++ b/spaceward/src/features/staking/StakingHeading.tsx
@@ -27,6 +27,7 @@ export default function StakingHeading(props: HeadingProps) {
{bigintToFixed(props.availableWard ?? BigInt(0), {
+ ceil: true,
decimals: 18,
format: true,
display: 2