Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add: tx helpers #40

Merged
merged 1 commit into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"moment": "^2.30.1",
"numerable": "^0.3.15",
"qs": "^6.13.0",
"react": "^18.2.0",
"react": "^18.3.1",
"react-dom": "^18.2.0",
"react-responsive": "^10.0.0",
"recharts": "^2.12.7",
Expand All @@ -61,9 +61,9 @@
"tailwindcss": "^3.4.12",
"tailwindcss-animate": "^1.0.7",
"typedoc": "^0.26.7",
"viem": "2.x",
"vite-plugin-dts": "^4.2.1",
"wagmi": "^2.12.10",
"viem": "2.21.54",
"wagmi": "^2.14.1",
"zustand": "^5.0.0-rc.2"
},
"devDependencies": {
Expand Down
25 changes: 17 additions & 8 deletions src/components/dapp/TransactionButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type ReactNode, useCallback } from "react";
import { type ResolvedRegister, type UseSendTransactionReturnType, useSendTransaction } from "wagmi";
import { useWalletContext } from "../../context/Wallet.context";
import Button, { type ButtonProps } from "../primitives/Button";
import Icon from "../primitives/Icon";

Expand All @@ -11,18 +12,26 @@ export type TransactionButtonProps = ButtonProps & {

export default function TransactionButton({ tx, name, children, onExecute, ...props }: TransactionButtonProps) {
const sendTxHook = useSendTransaction();
const { sendTransactionAsync, status } = sendTxHook;
const { status } = sendTxHook;
const { address: user, client, sendTransaction } = useWalletContext();

const execute = useCallback(async () => {
if (!tx) return;
console.log("CLIENT", client);

const hash = await sendTransactionAsync({
to: tx.to as `0x${string}`,
data: tx.data as `0x${string}`,
});
if (!tx || !user || !client) return;

onExecute?.(hash);
}, [tx, sendTransactionAsync, onExecute]);
const hash = await sendTransaction?.([
{
chain: client.chain,
account: user,
to: tx.to,
data: tx.data,
value: tx.value,
},
]);

hash && onExecute?.(hash);
}, [tx, client, user, sendTransaction, onExecute]);

return (
<Button {...props} onClick={execute}>
Expand Down
5 changes: 4 additions & 1 deletion src/context/Dapp.context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { WalletProvider } from "./Wallet.context";

//TODO: remove merkl-related typings in favor of redeclarations for better abstraction
import type { Chain, Explorer } from "@merkl/api";
import type { WalletOptions } from "../hooks/useWalletState";
import type { SizingConfig } from "../utils/tailwind";

export type DAppContextType = { flag?: string };
Expand All @@ -26,6 +27,7 @@ export type DAppProviderProps = {
sizing: SizingConfig;
modes: Mode[];
chains: (Chain & { explorers: Explorer[] })[];
walletOptions?: WalletOptions;
};

export function DAppProvider({
Expand All @@ -35,10 +37,11 @@ export function DAppProvider({
modes,
children,
chains,
walletOptions,
}: PropsWithChildren<DAppProviderProps>) {
return (
<ThemeProvider sizing={sizing} themes={themes ?? demoThemes} modes={modes}>
<WalletProvider chains={chains.filter(({ id }) => id !== 1337)} config={config}>
<WalletProvider walletOptions={walletOptions} chains={chains.filter(({ id }) => id !== 1337)} config={config}>
{children}
</WalletProvider>
</ThemeProvider>
Expand Down
19 changes: 11 additions & 8 deletions src/context/Wallet.context.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
//TODO: remove merkl-related typings in favor of redeclarations for better abstraction
import type { Chain, Explorer } from "@merkl/api";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { type PropsWithChildren, createContext, useContext } from "react";
import { type ResolvedRegister, WagmiProvider } from "wagmi";
import useWalletState from "../hooks/useWalletState";

//TODO: remove merkl-related typings in favor of redeclarations for better abstraction
import type { Chain, Explorer } from "@merkl/api";
import useWalletState, { type WalletOptions } from "../hooks/useWalletState";

export type WalletContextType = ReturnType<typeof useWalletState>;

Expand All @@ -22,22 +21,26 @@ export function useWalletContext() {
export type WalletProviderProps = {
config: ResolvedRegister["config"];
chains: (Chain & { explorers: Explorer[] })[];
walletOptions?: WalletOptions;
};

function WalletStateProvider({
children,
chains,
}: PropsWithChildren & { chains: (Chain & { explorers: Explorer[] })[] }) {
const walletState = useWalletState(chains);
options,
}: PropsWithChildren & { chains: (Chain & { explorers: Explorer[] })[]; options?: WalletOptions }) {
const walletState = useWalletState(chains, options);

return <WalletContext.Provider value={walletState}>{children}</WalletContext.Provider>;
}

export function WalletProvider({ config, children, chains }: PropsWithChildren<WalletProviderProps>) {
export function WalletProvider({ config, children, chains, walletOptions }: PropsWithChildren<WalletProviderProps>) {
return (
<QueryClientProvider client={queryClient}>
<WagmiProvider config={config}>
<WalletStateProvider chains={chains}>{children}</WalletStateProvider>
<WalletStateProvider options={walletOptions} chains={chains}>
{children}
</WalletStateProvider>
</WagmiProvider>
</QueryClientProvider>
);
Expand Down
45 changes: 40 additions & 5 deletions src/hooks/useWalletState.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,50 @@
import { switchChain as wagmiCoreSwitchChain } from "@wagmi/core";
import { useState } from "react";
import { useAccount, useConfig, useConnect, useDisconnect } from "wagmi";
import { getWalletClient, switchChain as wagmiCoreSwitchChain } from "@wagmi/core";
import { useCallback, useEffect, useState } from "react";
import { type Config, useAccount, useConfig, useConnect, useDisconnect } from "wagmi";

//TODO: remove merkl-related typings in favor of redeclarations for better abstraction
import type { Chain, Explorer } from "@merkl/api";
import type { WalletClient } from "viem";

export default function useWalletState(chains: (Chain & { explorers: Explorer[] })[]) {
const config = useConfig();
export type WalletOptions = {
client?: (c: WalletClient) => Promise<WalletClient>;
transaction?: (
tx: Parameters<WalletClient["sendTransaction"]>,
context: { client: WalletClient; config: Config },
) => ReturnType<WalletClient["sendTransaction"]>;
};

export default function useWalletState(chains: (Chain & { explorers: Explorer[] })[], options?: WalletOptions) {
const config = useConfig<Config>();
const wagmiConnect = useConnect();
const wagmiDisconnect = useDisconnect();
const account = useAccount();

const [blockNumber] = useState<number>();
const [client, setClient] = useState<WalletClient>();

const wrapClient = useCallback(async () => {
const _client = await getWalletClient<typeof config, 1>(config);

return (await options?.client?.(_client)) ?? _client;
}, [config, options?.client]);

// biome-ignore lint/correctness/useExhaustiveDependencies: required for correctness
useEffect(() => {
async function set() {
setClient(await wrapClient());
}

set();
}, [account, wrapClient]);

const wrapTransaction = useCallback(
async (tx: Parameters<WalletClient["sendTransaction"]>) => {
if (!client) return;
return (await options?.transaction?.(tx, { client, config })) ?? client.sendTransaction(...tx);
},
[client, options?.transaction, config],
);

async function connect(connectorId: string) {
const connector = config.connectors.find(({ id }) => id === connectorId);
Expand All @@ -32,9 +65,11 @@ export default function useWalletState(chains: (Chain & { explorers: Explorer[]
return {
config,
chains,
client,
chainId: account.chainId,
switchChain,
blockNumber,
sendTransaction: wrapTransaction,
address: account.address,
connected: account.isConnected,
connector: account.connector,
Expand Down
Loading