-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpage.tsx
More file actions
162 lines (146 loc) · 5.41 KB
/
Copy pathpage.tsx
File metadata and controls
162 lines (146 loc) · 5.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
"use client";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { useAccountProviderContext } from "@/context/account-providers/provider-context";
import { EXPLORER_URL, ZERODEV_DECIMALS, ZERODEV_TOKEN_ADDRESS } from "@/lib/constants";
import { useMutation } from "@tanstack/react-query";
import { useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { encodeFunctionData, formatUnits, parseUnits } from "viem";
import { baseSepolia } from "viem/chains";
import { Loader } from "lucide-react";
import { useBalance } from "wagmi";
const GasSponsorshipExample = () => {
const { embeddedWallet, kernelAccountClient, provider } = useAccountProviderContext();
const [amount, setAmount] = useState("");
const { data: balance } = useBalance({
address: embeddedWallet?.address,
token: ZERODEV_TOKEN_ADDRESS,
query: {
refetchInterval: 5000,
},
chainId: baseSepolia.id,
});
const {
mutate: sendTransaction,
isPending,
data: txHash,
} = useMutation({
mutationKey: ["gasSponsorship sendTransaction", kernelAccountClient?.account?.address, amount],
mutationFn: async () => {
if (!kernelAccountClient?.account) throw new Error("No kernel client found");
return kernelAccountClient.sendTransaction({
account: kernelAccountClient.account,
to: ZERODEV_TOKEN_ADDRESS,
value: BigInt(0),
data: encodeFunctionData({
abi: [
{
name: "mint",
type: "function",
inputs: [
{ name: "to", type: "address" },
{ name: "amount", type: "uint256" },
],
},
],
functionName: "mint",
args: [kernelAccountClient.account.address, parseUnits(amount, ZERODEV_DECIMALS)],
}),
chain: baseSepolia,
});
},
onSuccess: (data) => {
console.log(data);
toast.success("Transaction sent successfully");
},
onError: (error) => {
console.error(error);
toast.error("Failed to send transaction");
},
});
const containerRef = useRef<HTMLDivElement>(null);
const signInTooltipRef = useRef<HTMLDivElement>(null);
const isDisabled = useMemo(() => !embeddedWallet || !kernelAccountClient, [embeddedWallet, kernelAccountClient]);
useEffect(() => {
const signInTooltip = signInTooltipRef.current;
const container = containerRef.current;
if (!isDisabled) return;
const handleMouseMove = (e: MouseEvent) => {
signInTooltip?.style.setProperty("left", `${e.clientX + 10}px`);
signInTooltip?.style.setProperty("top", `${e.clientY + 10}px`);
};
const handleMouseEnter = () => {
signInTooltip?.style.setProperty("opacity", "1");
};
const handleMouseLeave = () => {
signInTooltip?.style.setProperty("opacity", "0");
};
if (signInTooltip && container) {
// follow mouse within the container
container.addEventListener("mousemove", handleMouseMove);
container.addEventListener("mouseenter", handleMouseEnter);
container.addEventListener("mouseleave", handleMouseLeave);
}
return () => {
signInTooltip?.removeEventListener("mousemove", handleMouseMove);
container?.removeEventListener("mouseenter", handleMouseEnter);
container?.removeEventListener("mouseleave", handleMouseLeave);
};
}, [isDisabled]);
return (
<>
{/* sign in tool tip */}
{isDisabled && (
<div
ref={signInTooltipRef}
className="border-primary bg-background fixed top-0 left-0 z-[99] max-w-xs border-2 p-4 text-sm opacity-0"
>
Create 7702 Account with <span className="capitalize">{provider}</span> to try out the examples!
</div>
)}
<div
className="border-primary/10 relative h-full w-full space-y-4 border-2 p-4 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:select-none"
ref={containerRef}
aria-disabled={isDisabled}
>
<h4 className="text-lg font-medium">Sponsor a Transaction</h4>
<div className="flex w-full flex-col gap-4">
<div className="flex items-center gap-2">
<Badge className="h-9 text-sm font-medium">Mint ZDEV Token</Badge>
<Input
className="bg-background"
type="text"
placeholder="Amount"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
</div>
<p className="text-sm">
Balance: {formatUnits(balance?.value ?? BigInt(0), balance?.decimals ?? 18)} {balance?.symbol}
</p>
<Button
disabled={isPending || isDisabled}
onClick={() => sendTransaction()}
>
{isPending ? "Sending..." : "Send Sponsored Transaction"}
{isPending && <Loader className="text-primary ml-2 h-4 w-4 animate-spin" />}
</Button>
{/* Tx link */}
{txHash && (
<a
href={`${EXPLORER_URL}/tx/${txHash}`}
target="_blank"
rel="noopener noreferrer"
className="text-primary text-sm underline underline-offset-4"
>
View Sponsored Transaction
</a>
)}
</div>
</div>
</>
);
};
export default GasSponsorshipExample;