diff --git a/.changeset/shaky-hoops-battle.md b/.changeset/shaky-hoops-battle.md new file mode 100644 index 00000000000..008ad2f60fe --- /dev/null +++ b/.changeset/shaky-hoops-battle.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +Allow passing an activeWallet to SwapWidget diff --git a/.vscode/settings.json b/.vscode/settings.json index 367183517b3..918823c26f9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,6 @@ { + "editor.insertSpaces": true, + "editor.tabSize": 2, "[css]": { "editor.defaultFormatter": "biomejs.biome" }, @@ -18,8 +20,9 @@ "editor.defaultFormatter": "biomejs.biome" }, "editor.codeActionsOnSave": { - "quickfix.biome": "explicit", - "source.fixAll.biome": "explicit" + "quickfix.biome": "always", + "source.fixAll.biome": "always", + "source.organizeImports.biome": "always" }, "editor.defaultFormatter": "biomejs.biome", "editor.formatOnSave": true, diff --git a/apps/dashboard/src/@/constants/thirdweb.server.ts b/apps/dashboard/src/@/constants/thirdweb.server.ts index cbc4a2c73fd..3525c40897e 100644 --- a/apps/dashboard/src/@/constants/thirdweb.server.ts +++ b/apps/dashboard/src/@/constants/thirdweb.server.ts @@ -13,6 +13,7 @@ import { import { THIRDWEB_BRIDGE_URL, THIRDWEB_BUNDLER_DOMAIN, + THIRDWEB_ENGINE_CLOUD_URL, THIRDWEB_INAPP_WALLET_DOMAIN, THIRDWEB_INSIGHT_API_DOMAIN, THIRDWEB_PAY_DOMAIN, @@ -38,6 +39,7 @@ export function getConfiguredThirdwebClient(options: { rpc: THIRDWEB_RPC_DOMAIN, social: THIRDWEB_SOCIAL_API_DOMAIN, storage: THIRDWEB_STORAGE_DOMAIN, + engineCloud: new URL(THIRDWEB_ENGINE_CLOUD_URL).hostname, }); } diff --git a/apps/dashboard/src/@/constants/urls.ts b/apps/dashboard/src/@/constants/urls.ts index fc30a7f78b2..a2856c5f3b2 100644 --- a/apps/dashboard/src/@/constants/urls.ts +++ b/apps/dashboard/src/@/constants/urls.ts @@ -25,3 +25,6 @@ export const THIRDWEB_INSIGHT_API_DOMAIN = export const THIRDWEB_BRIDGE_URL = process.env.NEXT_PUBLIC_BRIDGE_URL || "bridge.thirdweb-dev.com"; + +export const THIRDWEB_ENGINE_CLOUD_URL = + process.env.NEXT_PUBLIC_ENGINE_CLOUD_URL || "https://engine.thirdweb-dev.com"; diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet-details.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet-details.tsx index b96c82cbdd5..4263bb2afab 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet-details.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/project-wallet/project-wallet-details.tsx @@ -3,6 +3,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { + ArrowLeftIcon, ArrowLeftRightIcon, EllipsisVerticalIcon, RefreshCcwIcon, @@ -10,17 +11,22 @@ import { ShuffleIcon, WalletIcon, } from "lucide-react"; +import { useTheme } from "next-themes"; import { useCallback, useMemo, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { + createThirdwebClient, + Engine, getContract, readContract, type ThirdwebClient, toUnits, } from "thirdweb"; -import { useWalletBalance } from "thirdweb/react"; +import type { Chain } from "thirdweb/chains"; +import { SwapWidget, useWalletBalance } from "thirdweb/react"; import { isAddress, shortenAddress } from "thirdweb/utils"; +import { createWalletAdapter } from "thirdweb/wallets"; import { z } from "zod"; import { sendProjectWalletTokens } from "@/actions/project-wallet/send-tokens"; import type { Project } from "@/api/project/projects"; @@ -69,6 +75,7 @@ import { useV5DashboardChain } from "@/hooks/chains/v5-adapter"; import { useDashboardRouter } from "@/lib/DashboardRouter"; import type { ProjectWalletSummary } from "@/lib/server/project-wallet"; import { cn } from "@/lib/utils"; +import { getSDKTheme } from "@/utils/sdk-component-theme"; import { updateDefaultProjectWallet } from "../../transactions/lib/vault.client"; type GetProjectServerWallets = (params: { @@ -127,6 +134,11 @@ export function ProjectWalletDetailsSection(props: ProjectWalletControlsProps) { const [isSendOpen, setIsSendOpen] = useState(false); const [isReceiveOpen, setIsReceiveOpen] = useState(false); const [isChangeWalletOpen, setIsChangeWalletOpen] = useState(false); + const [isSwapOpen, setIsSwapOpen] = useState(false); + + // Persist swap credentials in memory so users don't have to re-enter them + const [swapSecretKey, setSwapSecretKey] = useState(""); + const [swapVaultAccessToken, setSwapVaultAccessToken] = useState(""); // Initialize chain and token from localStorage or defaults const [selectedChainId, setSelectedChainId] = useState(() => { @@ -310,6 +322,15 @@ export function ProjectWalletDetailsSection(props: ProjectWalletControlsProps) { Withdraw + + + + + ); + } + + // Screen 2: Swap Widget + return ( +
+ +
+ +
+ Swap Tokens + + Swap tokens from your project wallet + +
+
+
+ +
+ {swapClient && activeWallet && ( + + )} +
+
+ ); +} + const createSendFormSchema = (secretKeyLabel: string) => z.object({ chainId: z.number({ diff --git a/packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx index ee337950359..547dd2abd18 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/swap-widget/SwapWidget.tsx @@ -8,6 +8,7 @@ import type { ThirdwebClient } from "../../../../../client/client.js"; import { NATIVE_TOKEN_ADDRESS } from "../../../../../constants/addresses.js"; import type { SupportedFiatCurrency } from "../../../../../pay/convert/type.js"; import { getAddress } from "../../../../../utils/address.js"; +import type { Wallet } from "../../../../../wallets/interfaces/wallet.js"; import { CustomThemeProvider } from "../../../../core/design-system/CustomThemeProvider.js"; import type { Theme } from "../../../../core/design-system/index.js"; import type { BridgePrepareRequest } from "../../../../core/hooks/useBridgePrepare.js"; @@ -169,6 +170,10 @@ export type SwapWidgetProps = { * Called when the user disconnects the active wallet */ onDisconnect?: () => void; + /** + * The wallet that should be pre-selected in the SwapWidget UI. + */ + activeWallet?: Wallet; }; /** @@ -320,7 +325,7 @@ function SwapWidgetContent( }, ) { const [screen, setScreen] = useState({ id: "1:swap-ui" }); - const activeWalletInfo = useActiveWalletInfo(); + const activeWalletInfo = useActiveWalletInfo(props.activeWallet); const isPersistEnabled = props.persistTokenSelections !== false; const [amountSelection, setAmountSelection] = useState<{ diff --git a/packages/thirdweb/src/react/web/ui/Bridge/swap-widget/hooks.ts b/packages/thirdweb/src/react/web/ui/Bridge/swap-widget/hooks.ts index 5555f3e3dd8..8278302e851 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/swap-widget/hooks.ts +++ b/packages/thirdweb/src/react/web/ui/Bridge/swap-widget/hooks.ts @@ -1,21 +1,27 @@ import { useMemo } from "react"; +import type { Wallet } from "../../../../../wallets/interfaces/wallet.js"; import { useActiveAccount } from "../../../../core/hooks/wallets/useActiveAccount.js"; import { useActiveWallet } from "../../../../core/hooks/wallets/useActiveWallet.js"; import { useActiveWalletChain } from "../../../../core/hooks/wallets/useActiveWalletChain.js"; import type { ActiveWalletInfo } from "./types.js"; -export function useActiveWalletInfo(): ActiveWalletInfo | undefined { +export function useActiveWalletInfo( + activeWalletOverride?: Wallet, +): ActiveWalletInfo | undefined { const activeAccount = useActiveAccount(); const activeWallet = useActiveWallet(); const activeChain = useActiveWalletChain(); return useMemo(() => { - return activeAccount && activeWallet && activeChain + const wallet = activeWalletOverride || activeWallet; + const chain = activeWalletOverride?.getChain() || activeChain; + const account = activeWalletOverride?.getAccount() || activeAccount; + return wallet && chain && account ? { - activeChain, - activeWallet, - activeAccount, + activeChain: chain, + activeWallet: wallet, + activeAccount: account, } : undefined; - }, [activeAccount, activeWallet, activeChain]); + }, [activeAccount, activeWallet, activeChain, activeWalletOverride]); }