From be7e2d30ad4733ef6b06bda2a8c7487d71cc3506 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 28 Aug 2024 14:25:16 +0200 Subject: [PATCH 1/3] fix key icon in sidebar cleanup --- spaceward/pnpm-lock.yaml | 2 +- spaceward/src/config/tokens.ts | 38 +++-- spaceward/src/env.ts | 3 + spaceward/src/features/actions/Sidebar.tsx | 153 +++++++----------- spaceward/src/features/actions/hooks.ts | 9 +- spaceward/src/features/actions/util.ts | 126 +++++++++++++++ spaceward/src/features/assets/hooks.ts | 8 +- spaceward/src/features/assets/queries.ts | 18 +-- .../features/metamask/MetaMaskRequests.tsx | 2 +- spaceward/src/features/modals/CreateKey.tsx | 59 +++++-- spaceward/src/features/modals/SendAssets.tsx | 65 ++++---- spaceward/src/features/walletconnect/util.ts | 15 +- spaceward/src/hooks/useEthereumTx.tsx | 4 +- spaceward/src/hooks/useKeychainSigner.tsx | 2 + 14 files changed, 327 insertions(+), 177 deletions(-) diff --git a/spaceward/pnpm-lock.yaml b/spaceward/pnpm-lock.yaml index ea83029cd..17039f5be 100644 --- a/spaceward/pnpm-lock.yaml +++ b/spaceward/pnpm-lock.yaml @@ -20282,4 +20282,4 @@ snapshots: optionalDependencies: '@types/react': 18.2.79 immer: 10.0.4 - react: 18.2.0 \ No newline at end of file + react: 18.2.0 diff --git a/spaceward/src/config/tokens.ts b/spaceward/src/config/tokens.ts index 6523f4400..147e50ea1 100644 --- a/spaceward/src/config/tokens.ts +++ b/spaceward/src/config/tokens.ts @@ -1,3 +1,4 @@ +import { env } from "@/env"; import { getProvider } from "../lib/eth"; type ChainName = Parameters[0]; @@ -222,17 +223,26 @@ Binance Coin native BNB Ethereum Classic native ETC Polygon native MATIC */ -export const ENABLED_ETH_CHAINS: { chainName: ChainName; testnet?: boolean }[] = - [ - { chainName: "arbitrum" }, - { chainName: "base" }, - { chainName: "bsc" }, - { chainName: "mainnet" }, - { chainName: "optimism" }, - { chainName: "sepolia", testnet: true }, - ]; +const _ENABLED_ETH_CHAINS: { chainName: ChainName; testnet?: boolean }[] = [ + { chainName: "arbitrum" }, + { chainName: "base" }, + { chainName: "bsc" }, + { chainName: "mainnet" }, + { chainName: "optimism" }, + { chainName: "sepolia", testnet: true }, +]; + +export const ENABLED_ETH_CHAINS = _ENABLED_ETH_CHAINS.filter(({ testnet }) => + env.networkVisibility === "all" + ? true + : env.networkVisibility === "mainnet" + ? !testnet + : Boolean(testnet), +); -export const COSMOS_CHAINS: { +console.log("ENABLED_ETH_CHAINS", ENABLED_ETH_CHAINS); + +const _COSMOS_CHAINS: { chainName: string; feeAmount?: string; rpc?: string; @@ -272,3 +282,11 @@ export const COSMOS_CHAINS: { testnet: true, }, ]; + +export const COSMOS_CHAINS = _COSMOS_CHAINS.filter(({ testnet }) => + env.networkVisibility === "all" + ? true + : env.networkVisibility === "mainnet" + ? !testnet + : Boolean(testnet), +); diff --git a/spaceward/src/env.ts b/spaceward/src/env.ts index c3b61301d..da7d8b107 100644 --- a/spaceward/src/env.ts +++ b/spaceward/src/env.ts @@ -22,6 +22,8 @@ const aminoAnalyzerContract = const p2pRelayURL = import.meta.env.VITE_P2P_RELAY_URL || "https://relay.devnet.wardenprotocol.org:443"; +const networkVisibility: "testnet" | "mainnet" | "all" = + import.meta.env.VITE_WARDEN_NETWORK_VISIBILITY || "testnet"; export const env = { apiURL, @@ -38,4 +40,5 @@ export const env = { ethereumAnalyzerContract, aminoAnalyzerContract, p2pRelayURL, + networkVisibility, }; diff --git a/spaceward/src/features/actions/Sidebar.tsx b/spaceward/src/features/actions/Sidebar.tsx index f1a893381..22e185e21 100644 --- a/spaceward/src/features/actions/Sidebar.tsx +++ b/spaceward/src/features/actions/Sidebar.tsx @@ -1,42 +1,41 @@ import clsx from "clsx"; -import { useEffect } from "react"; -import { useChain } from "@cosmos-kit/react-lite"; -import { env } from "@/env"; -import { QueuedAction, QueuedActionStatus, useActionsState } from "./hooks"; -import { getClient, getSigningClient } from "@/hooks/useClient"; -import { cosmos, warden } from "@wardenprotocol/wardenjs"; +import { hexlify, Transaction } from "ethers"; +import { useContext, useEffect } from "react"; import { isDeliverTxSuccess, StargateClient } from "@cosmjs/stargate"; +import { useChain, walletContext } from "@cosmos-kit/react-lite"; +import { KeyringSnapRpcClient } from "@metamask/keyring-api"; +import { cosmos, warden } from "@wardenprotocol/wardenjs"; +import { base64FromBytes } from "@wardenprotocol/wardenjs/codegen/helpers"; import { ActionStatus } from "@wardenprotocol/wardenjs/codegen/warden/act/v1beta1/action"; -import { hexlify, Transaction } from "ethers"; -import { getProvider, isSupportedNetwork } from "@/lib/eth"; +import { useToast } from "@/components/ui/use-toast"; + import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; -import "./animate.css"; -import { isUint8Array } from "@/lib/utils"; -import { prepareTx } from "../modals/util"; + import { COSMOS_CHAINS } from "@/config/tokens"; +import { env } from "@/env"; +import { getClient, getSigningClient } from "@/hooks/useClient"; import { useWeb3Wallet } from "@/hooks/useWeb3Wallet"; -import { base64FromBytes } from "@wardenprotocol/wardenjs/codegen/helpers"; -import { useToast } from "@/components/ui/use-toast"; -import { KeyringSnapRpcClient } from "@metamask/keyring-api"; +import { getProvider, isSupportedNetwork } from "@/lib/eth"; +import { isUint8Array } from "@/lib/utils"; +import "./animate.css"; +import { QueuedAction, QueuedActionStatus, useActionsState } from "./hooks"; +import { getActionHandler } from "./util"; +import { prepareTx } from "../modals/util"; +import { TEMP_KEY, useKeySettingsState } from "../keys/state"; +import Assets from "../keys/assets"; interface ItemProps extends QueuedAction { single?: boolean; } -type GetStatus = (client: Awaited>) => Promise<{ - pending: boolean; - error: boolean; - done: boolean; - next?: "eth" | "eth-raw" | "cosmos"; - value?: any; -}>; - function ActionItem({ single, ...item }: ItemProps) { - const { toast } = useToast(); + const { walletManager } = useContext(walletContext); + const { data: ks, setData: setKeySettings } = useKeySettingsState(); + const { toast } = useToast() const { w } = useWeb3Wallet("wss://relay.walletconnect.org"); const { setData } = useActionsState(); @@ -44,6 +43,11 @@ function ActionItem({ single, ...item }: ItemProps) { env.cosmoskitChainName, ); + const type = typeof item.keyThemeIndex !== "undefined" ? + "key" : (["walletConnectRequestId", "walletConnectTopic"] as const).every(key => typeof item[key] !== "undefined") ? + "wc" : typeof item.snapRequestId ? + "snap" : undefined + useEffect(() => { if (item.status === QueuedActionStatus.Signed) { const signer = getOfflineSigner(); @@ -174,6 +178,7 @@ function ActionItem({ single, ...item }: ItemProps) { ) as unknown as number; } else { console.error("action failed", res); + toast({ title: "Failed", description: "Unexpected action status", @@ -198,73 +203,7 @@ function ActionItem({ single, ...item }: ItemProps) { } else if (item.status === QueuedActionStatus.ActionReady) { let cancel = false; let timeout: number | undefined; - let getStatus: GetStatus | undefined; - - switch (item.action?.result?.typeUrl) { - case warden.warden.v1beta3.MsgNewSignRequestResponse.typeUrl: { - const { id } = - warden.warden.v1beta3.MsgNewSignRequestResponse.decode( - item.action.result.value, - ); - - getStatus = (client) => - client.warden.warden.v1beta3 - .signRequestById({ id }) - .then(({ signRequest }) => { - switch (signRequest?.status) { - case warden.warden.v1beta3.SignRequestStatus - .SIGN_REQUEST_STATUS_PENDING: - return { - pending: true, - error: false, - done: false, - }; - case warden.warden.v1beta3.SignRequestStatus - .SIGN_REQUEST_STATUS_FULFILLED: - const analyzers = ( - item.data as Parameters< - typeof warden.warden.v1beta3.MsgNewSignRequest.encode - >[0] - ).analyzers; - const { - walletConnectRequestId, - walletConnectTopic, - snapRequestId - } = item; - - return { - pending: false, - error: false, - done: true, - next: analyzers.includes( - env.ethereumAnalyzerContract, - ) - ? "eth" - : analyzers.includes( - env.aminoAnalyzerContract, - ) - ? "cosmos" - : // fixme - (walletConnectRequestId && - walletConnectTopic) || snapRequestId - ? "eth-raw" - : undefined, - value: signRequest?.signedData, - }; - default: - return { - pending: false, - error: true, - done: true, - }; - } - }); - - break; - } - default: - console.warn("unknown action", item.action); - } + const { getStatus } = getActionHandler(item); async function checkResult() { if (cancel || !getStatus) { @@ -273,7 +212,7 @@ function ActionItem({ single, ...item }: ItemProps) { const client = await getClient(); const status = await getStatus(client); - + // TMP_KEY to key Id if (status.error) { toast({ title: "Failed", @@ -296,6 +235,14 @@ function ActionItem({ single, ...item }: ItemProps) { duration: 10000, }); } + + if (type === "key" && typeof status.value === "bigint") { + const keyId = status.value; + const settings = { ...ks?.settings, [keyId.toString()]: ks?.settings?.[TEMP_KEY] }; + delete settings[TEMP_KEY]; + setKeySettings({ settings }) + } + setData({ [item.id]: status.next ? { @@ -335,8 +282,6 @@ function ActionItem({ single, ...item }: ItemProps) { return; } - const type = snapRequestId ? "snap" : (walletConnectRequestId && walletConnectTopic) ? "wc" : undefined - switch (type) { case "wc": { if (!w) { @@ -426,7 +371,7 @@ function ActionItem({ single, ...item }: ItemProps) { }).then(() => true); } } else { - const provider = getProvider(chainName); + const provider = getProvider(chainName).provider; promise = provider .broadcastTransaction(signedTx.serialized) @@ -541,13 +486,22 @@ function ActionItem({ single, ...item }: ItemProps) { signatures: [sig], }); - promise = StargateClient.connect(chain.rpc) + let getRpc: Promise; + + if (chain.rpc) { + getRpc = Promise.resolve(chain.rpc); + } else { + const repo = walletManager.getWalletRepo(chainName); + repo.activate(); + getRpc = repo.getRpcEndpoint().then(endpoint => endpoint ? typeof endpoint === "string" ? endpoint : endpoint.url : "https://rpc.cosmos.directory/" + chainName); + } + + promise = getRpc.then(rpc => StargateClient.connect(rpc)) .then((client) => { return client.broadcastTx( cosmos.tx.v1beta1.TxRaw.encode( txRaw, ).finish(), - // fixme ); }) .then((res) => { @@ -627,7 +581,10 @@ function ActionItem({ single, ...item }: ItemProps) { "mx-2 p-3 rounded-lg": !single, })} > -

Action

+
+ {type === "key" ? : null} +

{item.title ?? "Action"}

+

{item.status === QueuedActionStatus.Signed @@ -665,7 +622,7 @@ function ActionItem({ single, ...item }: ItemProps) { /> - + ); } diff --git a/spaceward/src/features/actions/hooks.ts b/spaceward/src/features/actions/hooks.ts index 94d84859c..4d8c139cf 100644 --- a/spaceward/src/features/actions/hooks.ts +++ b/spaceward/src/features/actions/hooks.ts @@ -46,7 +46,8 @@ export interface QueuedAction { /** @deprecated fix naming */ signDoc?: StdSignDoc; pubkey?: Uint8Array; - + title?: string; + keyThemeIndex?: number; walletConnectRequestId?: number; walletConnectTopic?: string; snapRequestId?: string; @@ -73,6 +74,8 @@ export function useEnqueueAction( tx?: TransactionLike; signDoc?: StdSignDoc; pubkey?: Uint8Array; + title?: string; + keyThemeIndex?: number; walletConnectRequestId?: number; walletConnectTopic?: string; snapRequestId?: string; @@ -84,7 +87,9 @@ export function useEnqueueAction( tx, pubkey, signDoc, + title, snapRequestId, + keyThemeIndex, walletConnectRequestId, walletConnectTopic, ...opts @@ -111,8 +116,10 @@ export function useEnqueueAction( chainName, pubkey, signDoc, + title, snapRequestId, tx, + keyThemeIndex, walletConnectRequestId, walletConnectTopic, }, diff --git a/spaceward/src/features/actions/util.ts b/spaceward/src/features/actions/util.ts index e69de29bb..eb387eb22 100644 --- a/spaceward/src/features/actions/util.ts +++ b/spaceward/src/features/actions/util.ts @@ -0,0 +1,126 @@ +import { warden } from "@wardenprotocol/wardenjs"; +import { env } from "@/env"; +import type { getClient } from "@/hooks/useClient"; +import type { QueuedAction } from "./hooks"; + +type GetStatus = (client: Awaited>) => Promise<{ + pending: boolean; + error: boolean; + done: boolean; + next?: "eth" | "eth-raw" | "cosmos"; + value?: any; +}>; + +export const getActionHandler = ({ + action, + data, + snapRequestId, + walletConnectRequestId, + walletConnectTopic, +}: QueuedAction) => { + let getStatus: GetStatus; + + if (!action?.result) { + throw new Error("invalid action result"); + } + + const { typeUrl, value } = action.result; + + switch (typeUrl) { + case warden.warden.v1beta3.MsgNewSignRequestResponse.typeUrl: { + const { id } = + warden.warden.v1beta3.MsgNewSignRequestResponse.decode(value); + + getStatus = async (client) => { + const { signRequest } = + await client.warden.warden.v1beta3.signRequestById({ + id, + }); + + switch (signRequest?.status) { + case warden.warden.v1beta3.SignRequestStatus + .SIGN_REQUEST_STATUS_PENDING: + return { + pending: true, + error: false, + done: false, + }; + case warden.warden.v1beta3.SignRequestStatus + .SIGN_REQUEST_STATUS_FULFILLED: + const analyzers = ( + data as Parameters< + typeof warden.warden.v1beta3.MsgNewSignRequest.encode + >[0] + ).analyzers; + + return { + pending: false, + error: false, + done: true, + next: analyzers.includes( + env.ethereumAnalyzerContract, + ) + ? "eth" + : analyzers.includes(env.aminoAnalyzerContract) + ? "cosmos" + : // fixme + (walletConnectRequestId && + walletConnectTopic) || + snapRequestId + ? "eth-raw" + : undefined, + value: signRequest?.signedData, + }; + + default: + return { + pending: false, + error: true, + done: true, + }; + } + }; + break; + } + case warden.warden.v1beta3.MsgNewKeyRequestResponse.typeUrl: { + const { id } = + warden.warden.v1beta3.MsgNewKeyRequestResponse.decode(value); + + getStatus = async (client) => { + const { keyRequest } = + await client.warden.warden.v1beta3.keyRequestById({ + id, + }); + + switch (keyRequest?.status) { + case warden.warden.v1beta3.KeyRequestStatus + .KEY_REQUEST_STATUS_PENDING: + return { + pending: true, + error: false, + done: false, + }; + case warden.warden.v1beta3.KeyRequestStatus + .KEY_REQUEST_STATUS_FULFILLED: + return { + pending: false, + error: false, + done: true, + value: keyRequest.id, + }; + default: + return { + pending: false, + error: true, + done: true, + }; + } + }; + break; + } + default: + throw new Error(`action type not implemented: ${typeUrl}`); + } + + return { getStatus }; +}; diff --git a/spaceward/src/features/assets/hooks.ts b/spaceward/src/features/assets/hooks.ts index c94a4ec13..9245ccfed 100644 --- a/spaceward/src/features/assets/hooks.ts +++ b/spaceward/src/features/assets/hooks.ts @@ -82,17 +82,13 @@ export const useAssetQueries = (spaceId?: string | null) => { useEffect(() => { Promise.all( - COSMOS_CHAINS.map(({ chainName, rpc, testnet }) => { + COSMOS_CHAINS.map(({ chainName, rpc }) => { let promise: Promise; - if (!testnet) { + if (!rpc) { const repo = walletManager.getWalletRepo(chainName); repo.activate(); promise = repo.getRpcEndpoint(); - } else if (!rpc) { - promise = Promise.reject( - new Error("rpc endpoint is required for testnet"), - ); } else { promise = Promise.resolve(rpc); } diff --git a/spaceward/src/features/assets/queries.ts b/spaceward/src/features/assets/queries.ts index 2b8321349..2ff61f580 100644 --- a/spaceward/src/features/assets/queries.ts +++ b/spaceward/src/features/assets/queries.ts @@ -9,7 +9,7 @@ import aggregatorV3InterfaceABI from "@/contracts/eip155/priceFeedAbi"; import { COSMOS_PRICES, EIP_155_NATIVE_PRICE_FEEDS, - ENABLED_ETH_CHAINS as _ENABLED_ETH_CHAINS, + ENABLED_ETH_CHAINS, ERC20_TOKENS, MULTICALL3_ADDRESS, } from "@/config/tokens"; @@ -17,11 +17,6 @@ import { getProvider } from "@/lib/eth"; import { BalanceEntry, CosmosQueryClient, PriceMapSlinky } from "./types"; import { getAbiItem, getCosmosChain, getInterface } from "./util"; -// fixme -const ENABLED_ETH_CHAINS = _ENABLED_ETH_CHAINS - .filter(({ testnet }) => /* !testnet */ true) - .map(({ chainName }) => chainName); - type ChainName = Parameters[0]; const assetsByChain: Record = {}; @@ -497,7 +492,7 @@ export const balancesQueryEth = ( ) => { const byAddress: Record = {}; // debug - const debugAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; // vitalik.eth + // const debugAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; // vitalik.eth const eth = keys?.flatMap((key) => @@ -513,7 +508,7 @@ export const balancesQueryEth = ( ) ?? []; // debug - if (keys?.length) { + /* if (keys?.length) { byAddress[debugAddress] = { key: keys?.[0].key, addresses: [ @@ -525,7 +520,7 @@ export const balancesQueryEth = ( }; eth.push(debugAddress); - } + } */ const select = (result: BalanceEntry) => ({ results: [result], @@ -533,7 +528,7 @@ export const balancesQueryEth = ( }); const queries = [ - ...ENABLED_ETH_CHAINS.flatMap((chainName) => { + ...ENABLED_ETH_CHAINS.flatMap(({ chainName }) => { return eth.map((address) => ({ ...eip155NativeBalanceQuery({ enabled, @@ -545,7 +540,8 @@ export const balancesQueryEth = ( })); }), ...ERC20_TOKENS.filter(({ chainName }) => - ENABLED_ETH_CHAINS.includes(chainName), + // fixme not optimal + ENABLED_ETH_CHAINS.find((x) => x.chainName === chainName), ).flatMap(({ chainName, address: token, priceFeed, stablecoin }) => eth.map((address) => ({ ...eip155ERC20BalanceQuery({ diff --git a/spaceward/src/features/metamask/MetaMaskRequests.tsx b/spaceward/src/features/metamask/MetaMaskRequests.tsx index 8a761fc6d..ab5ca5d24 100644 --- a/spaceward/src/features/metamask/MetaMaskRequests.tsx +++ b/spaceward/src/features/metamask/MetaMaskRequests.tsx @@ -133,7 +133,7 @@ export function MetaMaskRequests() { throw new Error(`chainId not supported: ${chainId}`) } const tx = await buildSignTransaction(txParam); - const storeId = await signEthereumTx(keyId, tx, chainName, undefined, { requestId: req.id }); + const storeId = await signEthereumTx(keyId, tx, chainName, "Approve snap transaction", undefined, { requestId: req.id }); if (!storeId) { throw new Error( diff --git a/spaceward/src/features/modals/CreateKey.tsx b/spaceward/src/features/modals/CreateKey.tsx index 69b5f27ad..6f9d45b44 100644 --- a/spaceward/src/features/modals/CreateKey.tsx +++ b/spaceward/src/features/modals/CreateKey.tsx @@ -1,6 +1,7 @@ import clsx from "clsx"; import { Circle, Dice5Icon, RefreshCw } from "lucide-react"; import { useState } from "react"; +import { warden } from "@wardenprotocol/wardenjs"; import { Keychain } from "@wardenprotocol/wardenjs/codegen/warden/warden/v1beta3/keychain"; import { Button } from "@/components/ui/button"; import { useQueryHooks } from "@/hooks/useClient"; @@ -8,11 +9,13 @@ import { pasteFromClipboard } from "@/utils/browser"; import type { CreateKeyParams, ModalParams } from "./types"; import Assets, { KEY_THEMES } from "../keys/assets"; import { TEMP_KEY, useKeySettingsState } from "../keys/state"; -import useRequestKey from "@/hooks/useRequestKey"; import { useSpaceId } from "@/hooks/useSpaceId"; import { useModalState } from "./state"; import { Icons } from "@/components/ui/icons-assets"; import descriptions from "../keychains/description"; +import { useNewAction } from "@/hooks/useAction"; +import { useEnqueueAction } from "../actions/hooks"; +import { set } from "react-hook-form"; const DESCRIPTION_MAP = Object.fromEntries(descriptions.map((d) => [d.key, d])); const THEME_DISPLAY_COUNT = 4; @@ -26,9 +29,31 @@ export default function CreateKeyModal({ const { useKeychains, isReady } = useQueryHooks(); const { data: ks, setData: setKeySettings } = useKeySettingsState(); const { setData: setModal } = useModalState(); - const { requestKey } = useRequestKey(); const { spaceId: _spaceId } = useSpaceId(); const spaceId = selectedSpaceId ?? _spaceId; + const { getMessage, authority } = useNewAction(warden.warden.v1beta3.MsgNewKeyRequest); + const { addAction } = useEnqueueAction(getMessage); + const [pending, setPending] = useState(false); + + function requestKey(keychainId: bigint, spaceId: bigint, themeIndex: number) { + if (!authority) { + throw new Error("no authority"); + } + + return addAction( + { + spaceId, + keychainId, + ruleId: BigInt(0), + keyType: warden.warden.v1beta3.KeyType.KEY_TYPE_ECDSA_SECP256K1, + authority, + }, + { + keyThemeIndex: themeIndex, + title: "Creating key" + }, + ); + } const keychainsQuery = useKeychains({ options: { @@ -67,27 +92,29 @@ export default function CreateKeyModal({ }; setKeySettings({ settings: { ...ks.settings, [TEMP_KEY]: settings } }); + setPending(true); try { - setModal({ - background: { "create-key": { next } }, - type: next, - params: {}, - }); - - await requestKey(keychain.id, BigInt(spaceId)); + const storeId = await requestKey(keychain.id, BigInt(spaceId), themeIndex); + + if (storeId) { + setModal({ + type: undefined, + params: undefined + }); + } } catch (e) { console.error(e); } - setModal({ background: {} }); + setPending(false); } const desc = selected >= 0 ? DESCRIPTION_MAP[ - keychainsQuery.data?.keychains[selected]?.description ?? "" - ] + keychainsQuery.data?.keychains[selected]?.description ?? "" + ] : undefined; return hidden ? null : isDetails ? ( @@ -188,9 +215,9 @@ export default function CreateKeyModal({

= 3 - // ? "before:content-[''] before:absolute no-scrollbar overflow-scroll before:left-0 before:bottom-0 before:w-full before:h-[90px] before:z-20 before:bg-gradient-to-b before:from-[transparent] before:to-[#222]" - // : "", + (keychainsQuery.data?.keychains.length ?? 0) >= 3 + ? "before:content-[''] before:fixed no-scrollbar overflow-scroll before:left-0 before:bottom-0 before:w-full before:h-[0] before:z-20 before:bg-gradient-to-b before:from-[transparent] before:to-[#222]" + : "", )} > {keychainsQuery.data?.keychains.map((item, i) => { @@ -413,7 +440,7 @@ export default function CreateKeyModal({
diff --git a/spaceward/src/features/modals/SendAssets.tsx b/spaceward/src/features/modals/SendAssets.tsx index 7ca59d705..0fd5adfd2 100644 --- a/spaceward/src/features/modals/SendAssets.tsx +++ b/spaceward/src/features/modals/SendAssets.tsx @@ -1,5 +1,5 @@ import clsx from "clsx"; -import { useMemo, useState } from "react"; +import { useContext, useMemo, useState } from "react"; import { Icons } from "@/components/ui/icons-assets"; import type { TransferParams } from "./types"; import { bigintToFixed, bigintToFloat } from "@/lib/math"; @@ -16,6 +16,8 @@ import { StargateClient } from "@cosmjs/stargate"; import { useModalState } from "./state"; import KeySelector from "./KeySelector"; import { COSMOS_CHAINS } from "@/config/tokens"; +import { walletContext } from "@cosmos-kit/react-lite"; +import { BalanceEntry } from "../assets/types"; export default function SendAssetsModal({ // address, @@ -23,13 +25,14 @@ export default function SendAssetsModal({ token, keyResponse: key, }: TransferParams) { + const { walletManager } = useContext(walletContext); const { setData: setModal } = useModalState(); const { formatter, fiatConversion } = useFiatConversion(); const { spaceId } = useSpaceId(); const { queryBalances } = useAssetQueries(spaceId); - const results = queryBalances + const results: (BalanceEntry & { refetch: () => void })[] = queryBalances .filter((result) => result.data?.key?.key?.id === key?.key?.id) .flatMap(({ data, refetch }) => { return (data?.results ?? []).map((item) => ({ ...item, refetch })); @@ -37,7 +40,7 @@ export default function SendAssetsModal({ const noAssets = results.every((result) => !result.balance); - const _selectedToken = useMemo( + const selectedToken = useMemo( () => results.find( (data) => data.chainName === chainName && data.token === token, @@ -45,8 +48,6 @@ export default function SendAssetsModal({ [token, chainName, results], ); - // fixme types - const selectedToken = _selectedToken as typeof _selectedToken | undefined; const [pending, setPending] = useState(false); const [amount, setAmount] = useState(""); const [destinationAddress, setDestinationAddress] = useState(""); @@ -56,15 +57,15 @@ export default function SendAssetsModal({ amountNum && selectedToken ? Number.isFinite(amountNum) ? bigintToFloat(selectedToken.balance, selectedToken.decimals) < - amountNum + amountNum : true : false; const addressWarning = destinationAddress && selectedToken ? !validateAddress(destinationAddress, [ - selectedToken.type.startsWith("eip155:") ? "eth" : "bech32", - ]).ok + selectedToken.type.startsWith("eip155:") ? "eth" : "bech32", + ]).ok : false; const { signEthereumTx } = useEthereumTx(); @@ -75,7 +76,7 @@ export default function SendAssetsModal({ return; } - const { address, chainName } = selectedToken; + const { address, chainName, token } = selectedToken; setPending(true); try { @@ -86,9 +87,11 @@ export default function SendAssetsModal({ amount, }); + const title = `Send ${amount} ${token}`; + if (txBuild.type === "eth") { const { tx } = txBuild; - const storeId = await signEthereumTx(key.key.id, tx, chainName); + const storeId = await signEthereumTx(key.key.id, tx, chainName, title); if (storeId) { setModal({ type: undefined }); @@ -98,10 +101,16 @@ export default function SendAssetsModal({ (item) => item.chainName === chainName, ); - const endpoint = - chain?.rpc ?? `https://rpc.cosmos.directory/${chainName}`; + let rpc = chain?.rpc; + + if (!rpc) { + const repo = walletManager.getWalletRepo(chainName); + repo.activate(); + const endpoint = await repo.getRpcEndpoint(); + rpc = endpoint ? typeof endpoint === "string" ? endpoint : endpoint.url : `https://rpc.cosmos.directory/${chainName}`; + } - const client = await StargateClient.connect(endpoint); + const client = await StargateClient.connect(rpc); const signDoc = await createAminoSignDoc({ tx: txBuild as TxBuild<"cosmos">, @@ -109,7 +118,7 @@ export default function SendAssetsModal({ address, }); - const storeId = await signAmino(key, signDoc, chainName); + const storeId = await signAmino(key, signDoc, chainName, title); if (storeId) { setModal({ type: undefined }); @@ -145,8 +154,8 @@ export default function SendAssetsModal({ const maxAmount = selectedToken ? bigintToFixed(selectedToken.balance, { - decimals: selectedToken.decimals, - }) + decimals: selectedToken.decimals, + }) : "0"; return ( @@ -234,17 +243,17 @@ export default function SendAssetsModal({ {/* todo useFiatConversion hook */} {formatter.format( (amount ? parseFloat(amount) : 0) * - (fiatConversion && selectedToken - ? bigintToFloat( - (selectedToken.price * - BigInt(10) ** - BigInt( - fiatConversion.decimals, - )) / - fiatConversion.value, - selectedToken.priceDecimals, - ) - : 0), + (fiatConversion && selectedToken + ? bigintToFloat( + (selectedToken.price * + BigInt(10) ** + BigInt( + fiatConversion.decimals, + )) / + fiatConversion.value, + selectedToken.priceDecimals, + ) + : 0), )}
{pending ? "Loading.." : "Send"} diff --git a/spaceward/src/features/walletconnect/util.ts b/spaceward/src/features/walletconnect/util.ts index e6560e81c..727763080 100644 --- a/spaceward/src/features/walletconnect/util.ts +++ b/spaceward/src/features/walletconnect/util.ts @@ -317,10 +317,16 @@ export async function approveRequest({ ...feeData, }; - storeId = await eth.signEthereumTx(key.id, tx, chainName, { - requestId: req.id, - topic, - }); + storeId = await eth.signEthereumTx( + key.id, + tx, + chainName, + "Approve walletconnect request", + { + requestId: req.id, + topic, + }, + ); if (!storeId) { // todo error @@ -486,6 +492,7 @@ export async function approveRequest({ { key }, signDoc, chain.chain_name, + "Approve walletconnect request", { requestId: req.id, topic, diff --git a/spaceward/src/hooks/useEthereumTx.tsx b/spaceward/src/hooks/useEthereumTx.tsx index 9691a618b..47352c777 100644 --- a/spaceward/src/hooks/useEthereumTx.tsx +++ b/spaceward/src/hooks/useEthereumTx.tsx @@ -48,6 +48,7 @@ export function useEthereumTx() { keyId: bigint, _tx: ethers.TransactionLike, chainName: string, + title: string, wc?: { requestId: number; topic: string; @@ -84,7 +85,8 @@ export function useEthereumTx() { chainName, walletConnectRequestId: wc?.requestId, walletConnectTopic: wc?.topic, - snapRequestId: snap?.requestId + snapRequestId: snap?.requestId, + title }, ); }; diff --git a/spaceward/src/hooks/useKeychainSigner.tsx b/spaceward/src/hooks/useKeychainSigner.tsx index 3df2dde63..ecf7c8f01 100644 --- a/spaceward/src/hooks/useKeychainSigner.tsx +++ b/spaceward/src/hooks/useKeychainSigner.tsx @@ -17,6 +17,7 @@ export function useKeychainSigner() { key: Pick, signDoc: StdSignDoc, chainName: string, + title: string, wc?: { requestId: number; topic: string; @@ -51,6 +52,7 @@ export function useKeychainSigner() { signDoc, walletConnectRequestId: wc?.requestId, walletConnectTopic: wc?.topic, + title }, ); } From bd7f9fead42a561adecee419b122bb97c735457b Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 28 Aug 2024 16:06:26 +0200 Subject: [PATCH 2/3] fix getProider call --- spaceward/src/components/SendEth.tsx | 2 +- spaceward/src/config/tokens.ts | 2 -- spaceward/src/features/actions/Sidebar.tsx | 2 +- spaceward/src/features/assets/Assets.tsx | 2 +- spaceward/src/features/assets/queries.ts | 6 +++--- spaceward/src/features/home/HomeAssets.tsx | 2 +- spaceward/src/features/home/TotalAssetValue.tsx | 2 +- spaceward/src/features/intents/util/query.ts | 2 +- spaceward/src/features/modals/util.ts | 2 +- spaceward/src/pages/Intents.tsx | 5 ++++- 10 files changed, 14 insertions(+), 13 deletions(-) diff --git a/spaceward/src/components/SendEth.tsx b/spaceward/src/components/SendEth.tsx index 29fa8c7e4..456c3ea1d 100644 --- a/spaceward/src/components/SendEth.tsx +++ b/spaceward/src/components/SendEth.tsx @@ -8,7 +8,7 @@ import { ArrowUpRight } from "lucide-react"; import { useEthereumTx } from "@/hooks/useEthereumTx"; import { getProvider } from "@/lib/eth"; -const provider = getProvider("sepolia"); +const { provider } = getProvider("sepolia"); async function buildEthTransaction( chainId: string | number, diff --git a/spaceward/src/config/tokens.ts b/spaceward/src/config/tokens.ts index 147e50ea1..99a18ce2c 100644 --- a/spaceward/src/config/tokens.ts +++ b/spaceward/src/config/tokens.ts @@ -240,8 +240,6 @@ export const ENABLED_ETH_CHAINS = _ENABLED_ETH_CHAINS.filter(({ testnet }) => : Boolean(testnet), ); -console.log("ENABLED_ETH_CHAINS", ENABLED_ETH_CHAINS); - const _COSMOS_CHAINS: { chainName: string; feeAmount?: string; diff --git a/spaceward/src/features/actions/Sidebar.tsx b/spaceward/src/features/actions/Sidebar.tsx index 22e185e21..835504eca 100644 --- a/spaceward/src/features/actions/Sidebar.tsx +++ b/spaceward/src/features/actions/Sidebar.tsx @@ -371,7 +371,7 @@ function ActionItem({ single, ...item }: ItemProps) { }).then(() => true); } } else { - const provider = getProvider(chainName).provider; + const { provider } = getProvider(chainName); promise = provider .broadcastTransaction(signedTx.serialized) diff --git a/spaceward/src/features/assets/Assets.tsx b/spaceward/src/features/assets/Assets.tsx index 2856fb5fe..f4306329f 100644 --- a/spaceward/src/features/assets/Assets.tsx +++ b/spaceward/src/features/assets/Assets.tsx @@ -15,7 +15,7 @@ import { PageRequest } from "@wardenprotocol/wardenjs/codegen/cosmos/base/query/ import { AddressType } from "@wardenprotocol/wardenjs/codegen/warden/warden/v1beta3/key"; import { getProvider } from "@/lib/eth"; -const provider = getProvider("sepolia"); +const { provider } = getProvider("sepolia"); const USDollar = new Intl.NumberFormat("en-US", { style: "currency", diff --git a/spaceward/src/features/assets/queries.ts b/spaceward/src/features/assets/queries.ts index 2ff61f580..90badf647 100644 --- a/spaceward/src/features/assets/queries.ts +++ b/spaceward/src/features/assets/queries.ts @@ -190,7 +190,7 @@ const eip155NativeBalanceQuery = ({ ? new ethers.Contract( priceFeed, aggregatorV3InterfaceABI, - getProvider(chainName), + provider, ) : undefined; @@ -345,7 +345,7 @@ const eip155ERC20BalanceQuery = ({ ? new ethers.Contract( priceFeed, aggregatorV3InterfaceABI, - getProvider(chainName), + provider, ) : undefined; @@ -574,7 +574,7 @@ const fiatPriceQuery = (enabled: boolean, name: string) => { throw new Error("Invalid fiat currency"); } - const provider = getProvider("mainnet"); + const { provider } = getProvider("mainnet"); const priceFeedContract = new ethers.Contract( FIAT_PRICE_FEEDS[name], diff --git a/spaceward/src/features/home/HomeAssets.tsx b/spaceward/src/features/home/HomeAssets.tsx index 512535db8..2c53c194a 100644 --- a/spaceward/src/features/home/HomeAssets.tsx +++ b/spaceward/src/features/home/HomeAssets.tsx @@ -14,7 +14,7 @@ import { AddressType } from "@wardenprotocol/wardenjs/codegen/warden/warden/v1be import { PageRequest } from "@wardenprotocol/wardenjs/codegen/cosmos/base/query/v1beta1/pagination"; import { getProvider } from "@/lib/eth"; -const provider = getProvider("sepolia"); +const { provider } = getProvider("sepolia"); async function getEthBalance(address: string) { const balance = await provider.getBalance(address); diff --git a/spaceward/src/features/home/TotalAssetValue.tsx b/spaceward/src/features/home/TotalAssetValue.tsx index f502c32db..c521ba5dc 100644 --- a/spaceward/src/features/home/TotalAssetValue.tsx +++ b/spaceward/src/features/home/TotalAssetValue.tsx @@ -9,7 +9,7 @@ import { AddressType } from "@wardenprotocol/wardenjs/codegen/warden/warden/v1be import { getProvider } from "@/lib/eth"; const chainId = 11155111; -const provider = getProvider("sepolia"); +const { provider } = getProvider("sepolia"); async function getEthBalance(address: string) { const balance = await provider.getBalance(address); diff --git a/spaceward/src/features/intents/util/query.ts b/spaceward/src/features/intents/util/query.ts index 24632f28b..ffbf5517a 100644 --- a/spaceward/src/features/intents/util/query.ts +++ b/spaceward/src/features/intents/util/query.ts @@ -1,6 +1,6 @@ import { KNOWN_ADDRESSES, compareAddress, getProvider } from "@/lib/eth"; -const provider = getProvider("mainnet"); +const { provider } = getProvider("mainnet"); const resolveEns = async (address: `0x${string}`) => { try { diff --git a/spaceward/src/features/modals/util.ts b/spaceward/src/features/modals/util.ts index 0d5cae2f8..2e0c186bb 100644 --- a/spaceward/src/features/modals/util.ts +++ b/spaceward/src/features/modals/util.ts @@ -71,7 +71,7 @@ export async function buildTransaction({ } const amount = parseUnits(_amount, item.decimals); - const provider = getProvider(item.chainName); + const { provider } = getProvider(item.chainName); const nonce = await provider.getTransactionCount(from); const feeData = await provider.getFeeData(); const gasLimit = BigInt(21000); diff --git a/spaceward/src/pages/Intents.tsx b/spaceward/src/pages/Intents.tsx index 4df6ce63a..7c2d9d513 100644 --- a/spaceward/src/pages/Intents.tsx +++ b/spaceward/src/pages/Intents.tsx @@ -160,9 +160,12 @@ export const useRules = () => { const rules = useRules({ request: { - pagination: PageRequest.fromPartial({ limit: BigInt(100) }), + pagination: PageRequest.fromPartial({ limit: BigInt(100000) }), }, }); + console.log(rules.data) + + /** @deprecated would be nice to query intent by creator or space */ const rulesBySpace = useMemo( From 6ee3139522c08a64a57d13f91355b765472241ab Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 29 Aug 2024 16:15:10 +0200 Subject: [PATCH 3/3] bump ethers version; fix erc20 send --- spaceward/package.json | 2 +- spaceward/pnpm-lock.yaml | 26 +++++++++++++------------- spaceward/src/features/modals/util.ts | 15 +++++++++++++-- spaceward/src/lib/eth/constants.ts | 4 +++- spaceward/src/pages/Intents.tsx | 3 --- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/spaceward/package.json b/spaceward/package.json index 1275e7b54..02145ff64 100644 --- a/spaceward/package.json +++ b/spaceward/package.json @@ -89,7 +89,7 @@ "dayjs": "^1.11.10", "emoji-hash-gen": "^1.0.3", "esbuild-plugin-polyfill-node": "^0.3.0", - "ethers": "^6.9.2", + "ethers": "6.13.2", "js-sha256": "^0.10.1", "jsqr": "^1.4.0", "libp2p": "^1.0.8", diff --git a/spaceward/pnpm-lock.yaml b/spaceward/pnpm-lock.yaml index 17039f5be..68985b13a 100644 --- a/spaceward/pnpm-lock.yaml +++ b/spaceward/pnpm-lock.yaml @@ -244,8 +244,8 @@ importers: specifier: ^0.3.0 version: 0.3.0(esbuild@0.20.2) ethers: - specifier: ^6.9.2 - version: 6.12.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + specifier: 6.13.2 + version: 6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) js-sha256: specifier: ^0.10.1 version: 0.10.1 @@ -5447,8 +5447,8 @@ packages: ethers@5.7.2: resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} - ethers@6.12.0: - resolution: {integrity: sha512-zL5NlOTjML239gIvtVJuaSk0N9GQLi1Hom3ZWUszE5lDTQE/IVB62mrPkQ2W1bGcZwVGSLaetQbWNQSvI4rGDQ==} + ethers@6.13.2: + resolution: {integrity: sha512-9VkriTTed+/27BGuY1s0hf441kqwHJ1wtN2edksEtiRvXx+soxRX3iSXTfFqq2+YwrOqbDoTHjIhQnjJRlzKmg==} engines: {node: '>=14.0.0'} event-iterator@2.0.0: @@ -8530,8 +8530,8 @@ packages: utf-8-validate: optional: true - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -8542,12 +8542,12 @@ packages: utf-8-validate: optional: true - ws@8.5.0: - resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 + utf-8-validate: '>=5.0.2' peerDependenciesMeta: bufferutil: optional: true @@ -16655,7 +16655,7 @@ snapshots: - bufferutil - utf-8-validate - ethers@6.12.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): + ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10): dependencies: '@adraffy/ens-normalize': 1.10.1 '@noble/curves': 1.2.0 @@ -16663,7 +16663,7 @@ snapshots: '@types/node': 18.15.13 aes-js: 4.0.0-beta.5 tslib: 2.4.0 - ws: 8.5.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -20204,12 +20204,12 @@ snapshots: bufferutil: 4.0.8 utf-8-validate: 5.0.10 - ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): + ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.8 utf-8-validate: 5.0.10 - ws@8.5.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): + ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.8 utf-8-validate: 5.0.10 diff --git a/spaceward/src/features/modals/util.ts b/spaceward/src/features/modals/util.ts index 2e0c186bb..5fe856ac1 100644 --- a/spaceward/src/features/modals/util.ts +++ b/spaceward/src/features/modals/util.ts @@ -2,6 +2,7 @@ import { assets } from "chain-registry"; import { AbiCoder, concat, + ethers, keccak256, parseUnits, toUtf8Bytes, @@ -93,6 +94,12 @@ export async function buildTransaction({ throw new Error("missing token contract address"); } + const contract = new ethers.Contract( + item.erc20Token, + erc20Abi, + provider, + ); + const abiItem = getAbiItem(erc20Abi, "transfer")!; const signature = `${abiItem.name}(${abiItem.inputs.map((x) => x.type).join(",")})`; const sigHash = keccak256(toUtf8Bytes(signature)); @@ -115,9 +122,13 @@ export async function buildTransaction({ ...feeData, }; - const gasLimit = await provider.estimateGas({ ...tx, from }); // fixme gas limit - tx.gasLimit = gasLimit * BigInt(2); + const gasLimit = await provider.estimateGas({ + ...(await contract.transfer.populateTransaction(to, amount)), + from, + }) * BigInt(2); + + tx.gasLimit = gasLimit; return { tx, type: "eth" } as TxBuild<"eth">; } else { throw new Error(`unsupported type: ${item.type}`); diff --git a/spaceward/src/lib/eth/constants.ts b/spaceward/src/lib/eth/constants.ts index 6482b149a..57c790328 100644 --- a/spaceward/src/lib/eth/constants.ts +++ b/spaceward/src/lib/eth/constants.ts @@ -33,8 +33,10 @@ export const ETH_CHAIN_CONFIG: Record< "7777777": { rpc: ["https://rpc.zora.energy/"] }, "11155111": { rpc: [ + "https://ethereum-sepolia.rpc.subquery.network/public", "https://rpc.sepolia.org/", - "wss://ethereum-sepolia-rpc.publicnode.com", + "https://ethereum-sepolia.blockpi.network/v1/rpc/public", + "https://sepolia.gateway.tenderly.co", ], }, }; diff --git a/spaceward/src/pages/Intents.tsx b/spaceward/src/pages/Intents.tsx index 7c2d9d513..734a4aac3 100644 --- a/spaceward/src/pages/Intents.tsx +++ b/spaceward/src/pages/Intents.tsx @@ -163,9 +163,6 @@ export const useRules = () => { pagination: PageRequest.fromPartial({ limit: BigInt(100000) }), }, }); - console.log(rules.data) - - /** @deprecated would be nice to query intent by creator or space */ const rulesBySpace = useMemo(