Skip to content

Commit 5ac1076

Browse files
Handle already connected wallets in 1193 provider
1 parent bdcbe0e commit 5ac1076

File tree

30 files changed

+3293
-193
lines changed

30 files changed

+3293
-193
lines changed

.changeset/many-pants-tease.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Handle already connected wallets in 1193 provider

apps/playground-web/package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
{
22
"dependencies": {
33
"@abstract-foundation/agw-react": "^1.6.4",
4+
"@ai-sdk/react": "^2.0.25",
45
"@hookform/resolvers": "^3.9.1",
6+
"@radix-ui/react-avatar": "^1.1.10",
57
"@radix-ui/react-checkbox": "^1.3.2",
68
"@radix-ui/react-collapsible": "^1.1.11",
79
"@radix-ui/react-dialog": "1.1.14",
@@ -14,8 +16,11 @@
1416
"@radix-ui/react-slot": "^1.2.3",
1517
"@radix-ui/react-switch": "^1.2.5",
1618
"@radix-ui/react-tooltip": "1.2.7",
19+
"@radix-ui/react-use-controllable-state": "^1.2.2",
1720
"@tanstack/react-query": "5.81.5",
21+
"@thirdweb-dev/ai-sdk-provider": "workspace:*",
1822
"@workspace/ui": "workspace:*",
23+
"ai": "^5.0.25",
1924
"class-variance-authority": "^0.7.1",
2025
"clsx": "^2.1.1",
2126
"date-fns": "4.1.0",
@@ -37,9 +42,11 @@
3742
"server-only": "^0.0.1",
3843
"shiki": "1.27.0",
3944
"sonner": "2.0.6",
45+
"streamdown": "^1.1.4",
4046
"tailwind-merge": "^2.6.0",
4147
"thirdweb": "workspace:*",
4248
"use-debounce": "^10.0.5",
49+
"use-stick-to-bottom": "^1.1.1",
4350
"zod": "3.25.75"
4451
},
4552
"devDependencies": {
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
"use client";
2+
3+
import { useChat } from "@ai-sdk/react";
4+
import { DefaultChatTransport } from "ai";
5+
import { useMemo, useState } from "react";
6+
import { defineChain, prepareTransaction } from "thirdweb";
7+
import {
8+
ConnectButton,
9+
TransactionButton,
10+
useActiveAccount,
11+
} from "thirdweb/react";
12+
import { THIRDWEB_CLIENT } from "../../../../lib/client";
13+
import { ThirdwebAiMessage } from "@thirdweb-dev/ai-sdk-provider";
14+
import { Response } from "@/components/response";
15+
import {
16+
Conversation,
17+
ConversationContent,
18+
ConversationScrollButton,
19+
} from "@/components/conversation";
20+
import { Message, MessageContent } from "@/components/message";
21+
import {
22+
Reasoning,
23+
ReasoningContent,
24+
ReasoningTrigger,
25+
} from "@/components/reasoning";
26+
import { PromptInput, PromptInputSubmit, PromptInputTextarea, PromptInputToolbar } from "@/components/prompt-input";
27+
28+
export function ChatContainer() {
29+
const [sessionId, setSessionId] = useState("");
30+
31+
const { messages, sendMessage, status, setMessages,addToolResult } =
32+
useChat<ThirdwebAiMessage>({
33+
transport: new DefaultChatTransport({
34+
api: "/api/chat",
35+
}),
36+
onFinish: ({ message }) => {
37+
setSessionId(message.metadata?.session_id ?? "");
38+
},
39+
});
40+
const [input, setInput] = useState("");
41+
42+
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
43+
e.preventDefault();
44+
if (input.trim()) {
45+
sendMessage(
46+
{ text: input },
47+
{
48+
body: {
49+
sessionId,
50+
},
51+
}
52+
);
53+
setInput("");
54+
}
55+
};
56+
57+
return (
58+
<div className="max-w-4xl mx-auto p-6 relative size-full">
59+
<div className="flex flex-col h-[600px] rounded-lg border">
60+
<Conversation>
61+
<ConversationContent>
62+
{messages.map((message) => (
63+
<Message from={message.role} key={message.id}>
64+
<MessageContent>
65+
{message.parts.map((part, i) => {
66+
switch (part.type) {
67+
case "text":
68+
return (
69+
<Response key={`${message.id}-${i}`}>
70+
{part.text}
71+
</Response>
72+
);
73+
case "reasoning":
74+
return (
75+
<Reasoning
76+
key={`${message.id}-${i}`}
77+
className="w-full"
78+
isStreaming={status === "streaming"}
79+
>
80+
<ReasoningTrigger />
81+
<ReasoningContent className="text-xs text-muted-foreground italic flex flex-col gap-2">{part.text}</ReasoningContent>
82+
</Reasoning>
83+
);
84+
case "tool-sign_transaction":
85+
return (
86+
<SignTransactionButton input={part.input} addToolResult={addToolResult} toolCallId={part.toolCallId} />
87+
);
88+
default:
89+
return null;
90+
}
91+
})}
92+
</MessageContent>
93+
</Message>
94+
))}
95+
</ConversationContent>
96+
<ConversationScrollButton />
97+
</Conversation>
98+
99+
<PromptInput
100+
onSubmit={handleSubmit}
101+
className="mt-4 w-full max-w-2xl mx-auto relative"
102+
>
103+
<PromptInputTextarea
104+
value={input}
105+
placeholder="Say something..."
106+
onChange={(e) => setInput(e.currentTarget.value)}
107+
className="pr-12"
108+
/>
109+
<PromptInputSubmit
110+
status={status === 'streaming' ? 'streaming' : 'ready'}
111+
disabled={!input.trim()}
112+
className="absolute top-4 right-4"
113+
/>
114+
</PromptInput>
115+
116+
</div>
117+
</div>
118+
);
119+
}
120+
121+
type SignTransactionButtonProps = {
122+
input: {
123+
chain_id?: number;
124+
to?: string;
125+
data?: string;
126+
value?: string;
127+
} | undefined;
128+
addToolResult: ReturnType<typeof useChat<ThirdwebAiMessage>>["addToolResult"];
129+
toolCallId: string;
130+
};
131+
132+
const SignTransactionButton = (props: SignTransactionButtonProps) => {
133+
const { input, addToolResult, toolCallId } = props;
134+
const transactionData: {
135+
chain_id: number;
136+
to: string;
137+
data: `0x${string}`;
138+
value: bigint;
139+
} = useMemo(() => {
140+
return {
141+
chain_id: input?.chain_id || 8453,
142+
to: input?.to || "",
143+
data: (input?.data as `0x${string}`) || "0x",
144+
value: input?.value ? BigInt(input.value) : BigInt(0),
145+
};
146+
}, [input]);
147+
const account = useActiveAccount();
148+
149+
if (!account) {
150+
return <ConnectButton client={THIRDWEB_CLIENT} />;
151+
}
152+
153+
return (
154+
<div className="py-4">
155+
<TransactionButton
156+
style={{
157+
width: "100%",
158+
}}
159+
transaction={() =>
160+
prepareTransaction({
161+
client: THIRDWEB_CLIENT,
162+
chain: defineChain(transactionData.chain_id),
163+
to: transactionData.to,
164+
data: transactionData.data,
165+
value: transactionData.value,
166+
})
167+
}
168+
onTransactionSent={(transaction) => {
169+
addToolResult({
170+
tool: "sign_transaction",
171+
toolCallId,
172+
output: {
173+
transaction_hash: transaction.transactionHash,
174+
},
175+
});
176+
}}
177+
>
178+
Sign Transaction
179+
</TransactionButton>
180+
</div>
181+
);
182+
};

0 commit comments

Comments
 (0)