Skip to content

Commit

Permalink
feat(spaceward): technical debt (#868)
Browse files Browse the repository at this point in the history
* add more evm chains

* fix #864

* fixup asset balances query

* handle UpdateSpaceMessage

* add snap version check

* add missing env
  • Loading branch information
alex-nax authored Sep 20, 2024
1 parent f82a940 commit 019809a
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 95 deletions.
1 change: 1 addition & 0 deletions spaceward/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ if [ "$1" = 'nginx-fe' ]; then
replace_var WARDEN_COSMOSKIT_CHAIN_NAME "$filename"
replace_var WARDEN_MAINTENANCE "$filename"
replace_var WARDEN_SNAP_ORIGIN "$filename"
replace_var WARDEN_SNAP_VERSION "$filename"
replace_var WARDEN_ENVIRONMENT "$filename"
replace_var WARDEN_STORYBLOK_TOKEN "$filename"
replace_var WARDEN_ETHEREUM_ANALYZER_CONTRACT "$filename"
Expand Down
11 changes: 4 additions & 7 deletions spaceward/src/config/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,19 +216,16 @@ export const COSMOS_PRICES: Record<string, bigint | undefined> = {
OSMO: BigInt(0.4446 * 10 ** 8),
};

/* TODO networks
Astar native ASTR
Avalanche native AVAX
Binance Coin native BNB
Ethereum Classic native ETC
Polygon native MATIC
*/
const _ENABLED_ETH_CHAINS: { chainName: ChainName; testnet?: boolean }[] = [
{ chainName: "arbitrum" },
{ chainName: "astar" },
{ chainName: "avalanche" },
{ chainName: "base" },
{ chainName: "bsc" },
{ chainName: "ethereumClassic" },
{ chainName: "mainnet" },
{ chainName: "optimism" },
{ chainName: "polygon" },
{ chainName: "sepolia", testnet: true },
];

Expand Down
3 changes: 3 additions & 0 deletions spaceward/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const chainId = import.meta.env.VITE_WARDEN_CHAIN_ID || "warden";
const maintenance = import.meta.env.VITE_WARDEN_MAINTENANCE || false;
const snapOrigin =
import.meta.env.VITE_WARDEN_SNAP_ORIGIN || "local:http://localhost:8123";
const snapVersion =
import.meta.env.VITE_WARDEN_SNAP_VERSION || "0.1.5";
const spacewardEnv = import.meta.env.VITE_WARDEN_ENVIRONMENT || "development"; // development, production
const storyblokToken =
import.meta.env.VITE_WARDEN_STORYBLOK_TOKEN || "LTh76K2yz5nU6jUThhFG3Qtt";
Expand All @@ -34,6 +36,7 @@ export const env = {
chainId,
maintenance,
snapOrigin,
snapVersion,
spacewardEnv,
storyblokToken,
cosmoskitChainName,
Expand Down
6 changes: 4 additions & 2 deletions spaceward/src/features/actions/StatusSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { QueuedAction, QueuedActionStatus, useActionsState } from "./hooks";
import { getActionHandler, GetStatus, handleCosmos, handleEth, handleEthRaw } from "./util";
import { TEMP_KEY, useKeySettingsState } from "../keys/state";
import Assets from "../keys/assets";
import { useQueryClient } from "@tanstack/react-query";

interface ItemProps extends QueuedAction {
single?: boolean;
Expand All @@ -43,6 +44,7 @@ const waitForVisibility = () => {
};

function ActionItem({ single, ...item }: ItemProps) {
const queryClient = useQueryClient();
const { walletManager } = useContext(walletContext);
const { data: ks, setData: setKeySettings } = useKeySettingsState();
const { toast } = useToast()
Expand Down Expand Up @@ -307,9 +309,9 @@ function ActionItem({ single, ...item }: ItemProps) {
if (item.networkType === "eth-raw") {
res = await handleEthRaw({ action: item, w });
} else if (item.networkType === "eth") {
res = await handleEth({ action: item, w });
res = await handleEth({ action: item, w, queryClient });
} else if (item.networkType === "cosmos") {
res = await handleCosmos({ action: item, w, walletManager });
res = await handleCosmos({ action: item, w, walletManager, queryClient });
}
} catch (e) {
console.error("broadcast failed", e);
Expand Down
58 changes: 43 additions & 15 deletions spaceward/src/features/actions/util.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { hexlify, Transaction } from "ethers";
import { WalletManager } from "@cosmos-kit/core";
import { isDeliverTxSuccess, StargateClient } from "@cosmjs/stargate";
import { KeyringSnapRpcClient } from "@metamask/keyring-api";
import type { QueryClient } from "@tanstack/react-query";
import { IWeb3Wallet } from "@walletconnect/web3wallet";
import { cosmos, warden } from "@wardenprotocol/wardenjs";
import { base64FromBytes } from "@wardenprotocol/wardenjs/codegen/helpers";
import { env } from "@/env";
import { COSMOS_CHAINS } from "@/config/tokens";
import type { getClient } from "@/hooks/useClient";
import type { QueuedAction } from "./hooks";
import { IWeb3Wallet } from "@walletconnect/web3wallet";
import { hexlify, Transaction } from "ethers";
import { KeyringSnapRpcClient } from "@metamask/keyring-api";
import { getBalanceQueryKey } from "@/features/assets/queries";
import { getProvider, isSupportedNetwork } from "@/lib/eth";
import { COSMOS_CHAINS } from "@/config/tokens";
import { isUint8Array } from "@/lib/utils";
import { base64FromBytes } from "@wardenprotocol/wardenjs/codegen/helpers";
import type { QueuedAction } from "./hooks";
import { prepareTx } from "../modals/util";
import { WalletManager } from "@cosmos-kit/core";
import { isDeliverTxSuccess, StargateClient } from "@cosmjs/stargate";

export type GetStatus = (
client: Awaited<ReturnType<typeof getClient>>,
Expand Down Expand Up @@ -130,6 +132,15 @@ export const getActionHandler = ({
};
break;
}
case warden.warden.v1beta3.MsgUpdateSpaceResponse.typeUrl: {
getStatus = async () => ({
pending: false,
error: false,
done: true,
});

break;
}
default:
throw new Error(`action type not implemented: ${typeUrl}`);
}
Expand Down Expand Up @@ -203,9 +214,11 @@ export const handleEthRaw = async ({
export const handleEth = async ({
action,
w,
queryClient,
}: {
action: QueuedAction;
w: IWeb3Wallet | null;
queryClient: QueryClient;
}) => {
const {
chainName,
Expand Down Expand Up @@ -269,22 +282,25 @@ export const handleEth = async ({
.then(() => true);
}

return (
provider
.waitForTransaction(res.hash)
// fixme
.then(() => true)
);
return provider.waitForTransaction(res.hash).then(() => {
queryClient.invalidateQueries({
queryKey: getBalanceQueryKey("eip155", chainName, "").slice(0, -1),
});

return true;
});
};

export const handleCosmos = async ({
action,
w,
walletManager,
queryClient,
}: {
action: QueuedAction;
w: IWeb3Wallet | null;
walletManager: WalletManager;
queryClient: QueryClient;
}) => {
const {
chainName,
Expand Down Expand Up @@ -343,7 +359,19 @@ export const handleCosmos = async ({
},
})
// fixme
.then(() => true);
.then(() => {
// todo
// possibly add a timeout, as walletconnect with cosmos does not wait for tx result

queryClient.invalidateQueries({
queryKey: getBalanceQueryKey("cosmos", chainName, "").slice(
0,
-1,
),
});

return true;
});
}

const { signedTxBodyBytes, signedAuthInfoBytes } = prepareTx(
Expand Down
104 changes: 65 additions & 39 deletions spaceward/src/features/assets/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,85 @@
import { useQueryHooks } from "@/hooks/useClient";
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 } from "@tanstack/react-query";
import { useQueries, useQuery } from "@tanstack/react-query";
import { useContext, useEffect, useMemo, useState } from "react";
import { COSMOS_CHAINS } from "@/config/tokens";
import { useQueryHooks } from "@/hooks/useClient";
import {
balancesQueryCosmos,
balancesQueryEth,
fiatPricesQuery,
} from "./queries";
import type { CosmosQueryClient, PriceMapSlinky } from "./types";
import { COSMOS_CHAINS } from "@/config/tokens";
import { walletContext } from "@cosmos-kit/react-lite";
import { ExtendedHttpEndpoint } from "@cosmos-kit/core";

const DERIVE_ADDRESSES = [
AddressType.ADDRESS_TYPE_ETHEREUM,
AddressType.ADDRESS_TYPE_OSMOSIS,
];

const queryCosmosClients = (walletManager: WalletManager) => {
const rpcClients: Record<string, CosmosQueryClient | undefined> = {};
const rpcRetry: Record<string, number> = {};

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();
const [clients, setClients] = useState<[CosmosQueryClient, string][]>([]);
const clients = useQuery(queryCosmosClients(walletManager)).data;

const pairs = slinky.oracle.v1.useGetAllCurrencyPairs({
options: { enabled: isReady, refetchInterval: Infinity },
Expand Down Expand Up @@ -83,39 +142,6 @@ export const useAssetQueries = (spaceId?: string | null) => {
return priceMap;
}, [prices.data, currencyPairs]);

useEffect(() => {
Promise.all(
COSMOS_CHAINS.map(({ chainName, rpc }) => {
let promise: Promise<ExtendedHttpEndpoint | string>;

if (!rpc) {
const repo = walletManager.getWalletRepo(chainName);
repo.activate();
promise = repo.getRpcEndpoint();
} else {
promise = Promise.resolve(rpc[0]);
}

return promise
.then((endpoint) =>
cosmos.ClientFactory.createRPCQueryClient({
rpcEndpoint: endpoint
? typeof endpoint === "string"
? endpoint
: endpoint.url
: `https://rpc.cosmos.directory/${chainName}`,
}),
)
.then(
(client) =>
[client, chainName] as [CosmosQueryClient, string],
);
}),
).then((clients) => {
setClients(clients);
});
}, []);

const queryKeys = useKeysBySpaceId({
request: {
spaceId: spaceId ? BigInt(spaceId) : BigInt(0),
Expand Down
32 changes: 25 additions & 7 deletions spaceward/src/features/assets/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ const getAsset = (chainAssets: AssetList, denom: string) => {
return asset;
};

export const getBalanceQueryKey = (
chainType: "cosmos" | "eip155",
chainName: string,
address: string,
) => {
return ["balance", chainType, chainName, address];
};

const cosmosBalancesQuery = (params: {
address?: string;
enabled: boolean;
Expand All @@ -60,7 +68,11 @@ const cosmosBalancesQuery = (params: {
prices?: PriceMapSlinky;
}) => ({
enabled: params.enabled,
queryKey: ["cosmos", params.chainName, "balance", params.address],
queryKey: getBalanceQueryKey(
"cosmos",
params.chainName,
params.address ?? "",
),
queryFn: async () => {
if (!params.address) {
throw new Error("Address is required");
Expand Down Expand Up @@ -174,7 +186,10 @@ const eip155NativeBalanceQuery = ({
address?: `0x${string}`;
prices?: PriceMapSlinky;
}) => ({
queryKey: ["eip155", chainName, "native", address],
queryKey: [
...getBalanceQueryKey("eip155", chainName, address ?? ""),
"native",
],
queryFn: async (): Promise<BalanceEntry> => {
if (!address) {
throw new Error("Address is required");
Expand All @@ -199,10 +214,10 @@ const eip155NativeBalanceQuery = ({

const price: bigint = slinkyPrice
? BigInt(slinkyPrice.price?.price ?? 0)
: (priceFeedContract
: ((priceFeedContract
? await priceFeedContract.latestRoundData()
: undefined
)?.answer ?? BigInt(0);
)?.answer ?? BigInt(0));

const priceDecimals = slinkyPrice ? Number(slinkyPrice.decimals) : 8;

Expand Down Expand Up @@ -255,7 +270,10 @@ const eip155ERC20BalanceQuery = ({
prices?: PriceMapSlinky;
}) => ({
enabled: enabled && Boolean(address && token),
queryKey: ["eip155", chainName, "erc20", address, token],
queryKey: [
...getBalanceQueryKey("eip155", chainName, address ?? ""),
`erc20:${token}`,
],
queryFn: async (): Promise<BalanceEntry> => {
if (!address || !token) {
throw new Error("Address and token are required");
Expand Down Expand Up @@ -353,10 +371,10 @@ const eip155ERC20BalanceQuery = ({
? BigInt("100000000")
: slinkyPrice
? BigInt(slinkyPrice.price?.price ?? 0)
: (priceFeedContract
: ((priceFeedContract
? await priceFeedContract.latestRoundData()
: undefined
)?.answer ?? BigInt(0);
)?.answer ?? BigInt(0));

const priceDecimals = stablecoin
? 8
Expand Down
Loading

0 comments on commit 019809a

Please sign in to comment.