diff --git a/packages/account-kit/src/LoginButton.tsx b/packages/account-kit/src/AccountButton.tsx
similarity index 54%
rename from packages/account-kit/src/LoginButton.tsx
rename to packages/account-kit/src/AccountButton.tsx
index 573228a962..c55a3f5374 100644
--- a/packages/account-kit/src/LoginButton.tsx
+++ b/packages/account-kit/src/AccountButton.tsx
@@ -1,14 +1,15 @@
import { Button } from "./ui/Button";
-import { useLoginDialog } from "./useLoginDialog";
-import { LoginDialog } from "./LoginDialog";
-import { useLoginRequirements } from "./useLoginRequirements";
+import { useAccountModal } from "./useAccountModal";
+import { AccountModal } from "./AccountModal";
+import { useAccountRequirements } from "./useAccountRequirements";
import { Shadow } from "./Shadow";
const buttonClassName = "w-48";
-export function LoginButton() {
- const { requirement } = useLoginRequirements();
- const { openConnectModal, connectPending, openLoginDialog, toggleLoginDialog, loginDialogOpen } = useLoginDialog();
+export function AccountButton() {
+ const { requirement } = useAccountRequirements();
+ const { openConnectModal, connectPending, openAccountModal, toggleAccountModal, accountModalOpen } =
+ useAccountModal();
if (requirement === "connectedWallet") {
return (
@@ -18,7 +19,7 @@ export function LoginButton() {
pending={connectPending}
onClick={() => {
openConnectModal?.();
- openLoginDialog();
+ openAccountModal();
}}
>
Connect wallet
@@ -31,11 +32,11 @@ export function LoginButton() {
return (
<>
-
-
+
>
);
}
diff --git a/packages/account-kit/src/AccountDelegationDialogContent.tsx b/packages/account-kit/src/AccountDelegationDialogContent.tsx
index 3aba42aed5..7f847611a5 100644
--- a/packages/account-kit/src/AccountDelegationDialogContent.tsx
+++ b/packages/account-kit/src/AccountDelegationDialogContent.tsx
@@ -1,7 +1,7 @@
import * as Dialog from "@radix-ui/react-dialog";
import { useAppAccountClient } from "./useAppAccountClient";
import { usePublicClient, useWalletClient } from "wagmi";
-import { useLoginConfig } from "./Context";
+import { useConfig } from "./MUDAccountKitProvider";
import { encodeFunctionData } from "viem";
import { waitForTransactionReceipt } from "viem/actions";
import IBaseWorldAbi from "@latticexyz/world/out/IBaseWorld.sol/IBaseWorld.abi.json";
@@ -15,7 +15,7 @@ import { ModalContent } from "./ui/ModalContent";
export function AccountDelegationDialogContent() {
const queryClient = useQueryClient();
- const { chainId, worldAddress } = useLoginConfig();
+ const { chainId, worldAddress } = useConfig();
const publicClient = usePublicClient({ chainId });
const { data: userAccountClient } = useWalletClient({ chainId });
const appAccountClient = useAppAccountClient();
diff --git a/packages/account-kit/src/LoginDialog.tsx b/packages/account-kit/src/AccountModal.tsx
similarity index 88%
rename from packages/account-kit/src/LoginDialog.tsx
rename to packages/account-kit/src/AccountModal.tsx
index 7d244a3938..144a7535f8 100644
--- a/packages/account-kit/src/LoginDialog.tsx
+++ b/packages/account-kit/src/AccountModal.tsx
@@ -3,16 +3,16 @@ import { assertExhaustive } from "@latticexyz/common/utils";
import { AppSignerDialogContent } from "./AppSignerDialogContent";
import { GasAllowanceDialogContent } from "./GasAllowanceDialogContent";
import { AccountDelegationDialogContent } from "./AccountDelegationDialogContent";
-import { LoginRequirement } from "./common";
import { GasSpenderDialogContent } from "./GasSpenderDialogContent";
import { ConnectedChainDialogContent } from "./ConnectedChainDialogContent";
import { Modal, Props as ModalProps } from "./ui/Modal";
+import { AccountRequirement } from "./useAccountRequirements";
export type Props = Pick & {
- requirement: LoginRequirement;
+ requirement: AccountRequirement;
};
-export function LoginDialog({ requirement, ...dialogProps }: Props) {
+export function AccountModal({ requirement, ...dialogProps }: Props) {
const content = useMemo(() => {
switch (requirement) {
case "connectedWallet":
diff --git a/packages/account-kit/src/ConnectedChainDialogContent.tsx b/packages/account-kit/src/ConnectedChainDialogContent.tsx
index 79deb64f45..0d5e3f25dd 100644
--- a/packages/account-kit/src/ConnectedChainDialogContent.tsx
+++ b/packages/account-kit/src/ConnectedChainDialogContent.tsx
@@ -1,11 +1,11 @@
import * as Dialog from "@radix-ui/react-dialog";
import { useSwitchChain } from "wagmi";
-import { useLoginConfig } from "./Context";
+import { useConfig } from "./MUDAccountKitProvider";
import { Button } from "./ui/Button";
import { ModalContent } from "./ui/ModalContent";
export function ConnectedChainDialogContent() {
- const { chainId } = useLoginConfig();
+ const { chainId } = useConfig();
const { switchChain, isPending, error } = useSwitchChain();
// TODO: prompt user to add chain if missing
diff --git a/packages/account-kit/src/Context.tsx b/packages/account-kit/src/Context.tsx
deleted file mode 100644
index 7a9b559a18..0000000000
--- a/packages/account-kit/src/Context.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { createContext, useContext, type ReactNode } from "react";
-import { MUDLoginConfig } from "./common";
-
-/** @internal */
-const Context = createContext(null);
-
-export type Props = {
- config: MUDLoginConfig;
- children: ReactNode;
-};
-
-export function MUDLoginProvider({ config, children }: Props) {
- const currentConfig = useContext(Context);
- if (currentConfig) throw new Error("`MUDLoginProvider` can only be used once.");
- return {children};
-}
-
-export function useLoginConfig(): MUDLoginConfig {
- const config = useContext(Context);
- if (!config) throw new Error("`useLoginConfig` be used within a `MUDLoginProvider`.");
- return config;
-}
diff --git a/packages/account-kit/src/GasAllowanceDialogContent.tsx b/packages/account-kit/src/GasAllowanceDialogContent.tsx
index aa8021fabc..c2b0be8fd9 100644
--- a/packages/account-kit/src/GasAllowanceDialogContent.tsx
+++ b/packages/account-kit/src/GasAllowanceDialogContent.tsx
@@ -1,6 +1,6 @@
import { parseEther } from "viem";
-import { useAccount, useConfig, useWriteContract } from "wagmi";
-import { useLoginConfig } from "./Context";
+import { useAccount, useConfig as useWagmiConfig, useWriteContract } from "wagmi";
+import { useConfig } from "./MUDAccountKitProvider";
import GasTankAbi from "@latticexyz/gas-tank/out/IWorld.sol/IWorld.abi.json";
import { getGasTankBalanceQueryKey } from "./useGasTankBalance";
import { waitForTransactionReceipt } from "wagmi/actions";
@@ -10,8 +10,8 @@ import { ModalContent } from "./ui/ModalContent";
export function GasAllowanceDialogContent() {
const queryClient = useQueryClient();
- const wagmiConfig = useConfig();
- const { chainId, gasTankAddress } = useLoginConfig();
+ const wagmiConfig = useWagmiConfig();
+ const { chainId, gasTankAddress } = useConfig();
const userAccount = useAccount();
const userAccountAddress = userAccount.address;
const { writeContractAsync, isPending, error } = useWriteContract({
diff --git a/packages/account-kit/src/GasSpenderDialogContent.tsx b/packages/account-kit/src/GasSpenderDialogContent.tsx
index 4ed6fa60ae..439e8fee4d 100644
--- a/packages/account-kit/src/GasSpenderDialogContent.tsx
+++ b/packages/account-kit/src/GasSpenderDialogContent.tsx
@@ -1,7 +1,7 @@
import * as Dialog from "@radix-ui/react-dialog";
import { useAppAccountClient } from "./useAppAccountClient";
import { usePublicClient, useWalletClient } from "wagmi";
-import { useLoginConfig } from "./Context";
+import { useConfig } from "./MUDAccountKitProvider";
import { encodeFunctionData } from "viem";
import { waitForTransactionReceipt } from "viem/actions";
import { resourceToHex } from "@latticexyz/common";
@@ -14,7 +14,7 @@ import { ModalContent } from "./ui/ModalContent";
export function GasSpenderDialogContent() {
const queryClient = useQueryClient();
- const { chainId, gasTankAddress } = useLoginConfig();
+ const { chainId, gasTankAddress } = useConfig();
const publicClient = usePublicClient({ chainId });
const { data: userAccountClient } = useWalletClient({ chainId });
const appAccountClient = useAppAccountClient();
diff --git a/packages/account-kit/src/MUDAccountKitProvider.tsx b/packages/account-kit/src/MUDAccountKitProvider.tsx
new file mode 100644
index 0000000000..aaaffd32d1
--- /dev/null
+++ b/packages/account-kit/src/MUDAccountKitProvider.tsx
@@ -0,0 +1,28 @@
+import { createContext, useContext, type ReactNode } from "react";
+import { Address } from "viem";
+
+export type Config = {
+ chainId: number;
+ worldAddress: Address;
+ gasTankAddress: Address;
+};
+
+/** @internal */
+const Context = createContext(null);
+
+export type Props = {
+ config: Config;
+ children: ReactNode;
+};
+
+export function MUDAccountKitProvider({ config, children }: Props) {
+ const currentConfig = useContext(Context);
+ if (currentConfig) throw new Error("`MUDAccountKitProvider` can only be used once.");
+ return {children};
+}
+
+export function useConfig(): Config {
+ const config = useContext(Context);
+ if (!config) throw new Error("`useConfig` be used within a `MUDAccountKitProvider`.");
+ return config;
+}
diff --git a/packages/account-kit/src/common.ts b/packages/account-kit/src/common.ts
index 973527b0a4..310bb69f42 100644
--- a/packages/account-kit/src/common.ts
+++ b/packages/account-kit/src/common.ts
@@ -1,7 +1,7 @@
import { resourceToHex } from "@latticexyz/common";
import { ENTRYPOINT_ADDRESS_V07, SmartAccountClient } from "permissionless";
import { SmartAccount } from "permissionless/accounts";
-import { Address, Chain, Transport } from "viem";
+import { Chain, Transport } from "viem";
export const entryPointAddress = ENTRYPOINT_ADDRESS_V07;
/**
@@ -15,21 +15,4 @@ export type AppAccountClient = SmartAccountClient ({ open: false }));
+
+export type UseAccountModalResult = {
+ readonly openConnectModal: (() => void) | undefined;
+ readonly connectPending: boolean;
+ readonly accountModalOpen: boolean;
+ readonly openAccountModal: () => void;
+ readonly closeAccountModal: () => void;
+ readonly toggleAccountModal: (open: boolean) => void;
+};
+
+export function useAccountModal(): UseAccountModalResult {
+ const { openConnectModal, connectModalOpen } = useConnectModal();
+ const connectPending = !openConnectModal || connectModalOpen;
+
+ const accountModalOpen = useStore(store, (state) => state.open);
+
+ const openAccountModal = useCallback(() => {
+ store.setState({ open: true });
+ }, []);
+
+ const closeAccountModal = useCallback(() => {
+ store.setState({ open: false });
+ }, []);
+
+ const toggleAccountModal = useCallback((open: boolean) => {
+ store.setState({ open });
+ }, []);
+
+ return useMemo(
+ () => ({
+ openConnectModal,
+ connectPending,
+ accountModalOpen,
+ openAccountModal,
+ closeAccountModal,
+ toggleAccountModal,
+ }),
+ [closeAccountModal, connectPending, accountModalOpen, openConnectModal, openAccountModal, toggleAccountModal],
+ );
+}
diff --git a/packages/account-kit/src/useLoginRequirements.ts b/packages/account-kit/src/useAccountRequirements.ts
similarity index 60%
rename from packages/account-kit/src/useLoginRequirements.ts
rename to packages/account-kit/src/useAccountRequirements.ts
index 366dea25e0..1242aaa1cc 100644
--- a/packages/account-kit/src/useLoginRequirements.ts
+++ b/packages/account-kit/src/useAccountRequirements.ts
@@ -1,19 +1,29 @@
import { useAccount } from "wagmi";
-import { LoginRequirement, loginRequirements } from "./common";
import { useAppSigner } from "./useAppSigner";
import { useHasDelegation } from "./useHasDelegation";
import { useMemo } from "react";
import { useGasTankBalance } from "./useGasTankBalance";
import { useIsGasSpender } from "./useIsGasSpender";
-import { useLoginConfig } from "./Context";
+import { useConfig } from "./MUDAccountKitProvider";
-export type UseLoginRequirementsResult = {
- readonly requirement: LoginRequirement | null;
- readonly requirements: readonly LoginRequirement[];
+export const accountRequirements = [
+ "connectedWallet",
+ "connectedChain",
+ "appSigner",
+ "gasAllowance",
+ "gasSpender",
+ "accountDelegation",
+] as const;
+
+export type AccountRequirement = (typeof accountRequirements)[number];
+
+export type UseAccountRequirementsResult = {
+ readonly requirement: AccountRequirement | null;
+ readonly requirements: readonly AccountRequirement[];
};
-export function useLoginRequirements(): UseLoginRequirementsResult {
- const { chainId } = useLoginConfig();
+export function useAccountRequirements(): UseAccountRequirementsResult {
+ const { chainId } = useConfig();
const userAccount = useAccount();
const [appSignerAccount] = useAppSigner();
@@ -29,9 +39,9 @@ export function useLoginRequirements(): UseLoginRequirementsResult {
gasAllowance: () => gasTankBalance != null && gasTankBalance > 0n,
gasSpender: () => isGasSpender === true,
accountDelegation: () => hasDelegation === true,
- } as const satisfies Record boolean>;
+ } as const satisfies Record boolean>;
- const requirements = loginRequirements.filter((requirement) => !satisfiesRequirement[requirement]());
+ const requirements = accountRequirements.filter((requirement) => !satisfiesRequirement[requirement]());
return {
requirement: requirements.at(0) ?? null,
diff --git a/packages/account-kit/src/useAppAccountClient.ts b/packages/account-kit/src/useAppAccountClient.ts
index 44cf708d72..fb23baeb89 100644
--- a/packages/account-kit/src/useAppAccountClient.ts
+++ b/packages/account-kit/src/useAppAccountClient.ts
@@ -5,16 +5,16 @@ import { callFrom } from "@latticexyz/world/internal";
import { createSmartAccountClient } from "permissionless";
import { createPimlicoBundlerClient } from "permissionless/clients/pimlico";
import { call, getTransactionCount } from "viem/actions";
-import { useLoginConfig } from "./Context";
+import { useConfig } from "./MUDAccountKitProvider";
import { useAppSigner } from "./useAppSigner";
import { useAppAccount } from "./useAppAccount";
import { AppAccountClient, entryPointAddress } from "./common";
import { getUserBalanceSlot } from "./utils/getUserBalanceSlot";
-import { getEntryPointDepositSlot } from "./getEntryPointDepositSlot";
+import { getEntryPointDepositSlot } from "./utils/getEntryPointDepositSlot";
export function useAppAccountClient(): AppAccountClient | undefined {
const [appSignerAccount] = useAppSigner();
- const { chainId, worldAddress, gasTankAddress } = useLoginConfig();
+ const { chainId, worldAddress, gasTankAddress } = useConfig();
const { address: userAddress } = useAccount();
const publicClient = usePublicClient({ chainId });
const { data: appAccount } = useAppAccount({ publicClient, appSignerAccount });
diff --git a/packages/account-kit/src/useGasTankBalance.ts b/packages/account-kit/src/useGasTankBalance.ts
index f9da33503e..2428ecf7dd 100644
--- a/packages/account-kit/src/useGasTankBalance.ts
+++ b/packages/account-kit/src/useGasTankBalance.ts
@@ -1,5 +1,5 @@
import { useAccount, usePublicClient } from "wagmi";
-import { useLoginConfig } from "./Context";
+import { useConfig } from "./MUDAccountKitProvider";
import { useQuery } from "@tanstack/react-query";
import { getRecord } from "./utils/getRecord";
import { Address } from "abitype";
@@ -37,7 +37,7 @@ export async function getGasTankBalance({
}
export function useGasTankBalance(): bigint | undefined {
- const { chainId, gasTankAddress } = useLoginConfig();
+ const { chainId, gasTankAddress } = useConfig();
const publicClient = usePublicClient({ chainId });
const userAccount = useAccount();
diff --git a/packages/account-kit/src/useHasDelegation.ts b/packages/account-kit/src/useHasDelegation.ts
index da1074cdee..7e176d4df9 100644
--- a/packages/account-kit/src/useHasDelegation.ts
+++ b/packages/account-kit/src/useHasDelegation.ts
@@ -1,5 +1,5 @@
import { useAccount, usePublicClient } from "wagmi";
-import { useLoginConfig } from "./Context";
+import { useConfig } from "./MUDAccountKitProvider";
import { useQuery } from "@tanstack/react-query";
import { useAppAccount } from "./useAppAccount";
import { useAppSigner } from "./useAppSigner";
@@ -44,7 +44,7 @@ export async function hasDelegation({
}
export function useHasDelegation(): boolean | undefined {
- const { chainId, worldAddress } = useLoginConfig();
+ const { chainId, worldAddress } = useConfig();
const publicClient = usePublicClient({ chainId });
const userAccount = useAccount();
const [appSignerAccount] = useAppSigner();
diff --git a/packages/account-kit/src/useIsGasSpender.ts b/packages/account-kit/src/useIsGasSpender.ts
index 40132489ee..74ded3425f 100644
--- a/packages/account-kit/src/useIsGasSpender.ts
+++ b/packages/account-kit/src/useIsGasSpender.ts
@@ -1,5 +1,5 @@
import { useAccount, usePublicClient } from "wagmi";
-import { useLoginConfig } from "./Context";
+import { useConfig } from "./MUDAccountKitProvider";
import { useQuery } from "@tanstack/react-query";
import { getRecord } from "./utils/getRecord";
import { Address } from "abitype";
@@ -42,7 +42,7 @@ export async function isGasSpender({
}
export function useIsGasSpender(): boolean | undefined {
- const { chainId, gasTankAddress } = useLoginConfig();
+ const { chainId, gasTankAddress } = useConfig();
const publicClient = usePublicClient({ chainId });
const userAccount = useAccount();
diff --git a/packages/account-kit/src/useLoginDialog.ts b/packages/account-kit/src/useLoginDialog.ts
deleted file mode 100644
index e3e2b6ef6d..0000000000
--- a/packages/account-kit/src/useLoginDialog.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { useConnectModal } from "@rainbow-me/rainbowkit";
-import { useCallback, useMemo } from "react";
-import { useStore } from "zustand";
-import { createStore } from "zustand/vanilla";
-
-const store = createStore(() => ({ open: false }));
-
-export type UseLoginDialogResult = {
- readonly openConnectModal: (() => void) | undefined;
- readonly connectPending: boolean;
- readonly loginDialogOpen: boolean;
- readonly openLoginDialog: () => void;
- readonly closeLoginDialog: () => void;
- readonly toggleLoginDialog: (open: boolean) => void;
-};
-
-export function useLoginDialog(): UseLoginDialogResult {
- const { openConnectModal, connectModalOpen } = useConnectModal();
- const connectPending = !openConnectModal || connectModalOpen;
-
- const loginDialogOpen = useStore(store, (state) => state.open);
-
- const openLoginDialog = useCallback(() => {
- store.setState({ open: true });
- }, []);
-
- const closeLoginDialog = useCallback(() => {
- store.setState({ open: false });
- }, []);
-
- const toggleLoginDialog = useCallback((open: boolean) => {
- store.setState({ open });
- }, []);
-
- return useMemo(
- () => ({
- openConnectModal,
- connectPending,
- loginDialogOpen,
- openLoginDialog,
- closeLoginDialog,
- toggleLoginDialog,
- }),
- [closeLoginDialog, connectPending, loginDialogOpen, openConnectModal, openLoginDialog, toggleLoginDialog],
- );
-}
diff --git a/packages/account-kit/src/getEntryPointDepositSlot.ts b/packages/account-kit/src/utils/getEntryPointDepositSlot.ts
similarity index 73%
rename from packages/account-kit/src/getEntryPointDepositSlot.ts
rename to packages/account-kit/src/utils/getEntryPointDepositSlot.ts
index 123bf27410..00738e2917 100644
--- a/packages/account-kit/src/getEntryPointDepositSlot.ts
+++ b/packages/account-kit/src/utils/getEntryPointDepositSlot.ts
@@ -1,5 +1,7 @@
import { Address, Hex, encodeAbiParameters, keccak256 } from "viem";
-import { entryPointDepositsSlot } from "./common";
+import { entryPointDepositsSlot } from "../common";
+
+// TODO: move this to gas-tank package or similar
export function getEntryPointDepositSlot(gasTankAddress: Address): Hex {
return keccak256(