Skip to content

Commit 227ab57

Browse files
feat: Add auto-execute transactions to Nebula context
This commit introduces the ability to specify whether transactions should be automatically executed within the Nebula context. This provides more control over the AI's interaction with the blockchain. Co-authored-by: joaquim.verges <[email protected]>
1 parent 14fe9ee commit 227ab57

File tree

4 files changed

+2330
-3
lines changed

4 files changed

+2330
-3
lines changed

apps/playground-web/src/app/ai/api/chat.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export async function promptNebula(params: {
2323
chain_ids: params.context.chainIds?.map(Number) || [],
2424
session_id: params.context.sessionId ?? undefined,
2525
wallet_address: params.context.walletAddress,
26+
auto_execute_transactions: params.context.autoExecuteTransactions || false,
2627
};
2728
}
2829

apps/playground-web/src/app/ai/api/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export type NebulaContext = {
3434
chainIds: string[] | null;
3535
walletAddress: string | null;
3636
sessionId: string | null;
37+
autoExecuteTransactions?: boolean;
3738
};
3839

3940
export type NebulaSwapData = {

apps/playground-web/src/app/ai/components/ChatPageContent.tsx

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,19 @@ export function ChatPageContent(props: {
6868
});
6969

7070
const contextFilters = useMemo(() => {
71+
// Parse user-entered chain IDs
72+
const userChainIdArray = userChainIds
73+
.split(',')
74+
.map(id => id.trim())
75+
.filter(id => id !== '' && !isNaN(Number(id)));
76+
7177
return {
72-
chainIds: _contextFilters?.chainIds || [],
78+
chainIds: userChainIdArray.length > 0 ? userChainIdArray : (_contextFilters?.chainIds || []),
7379
sessionId: _contextFilters?.sessionId || null,
74-
walletAddress: address || _contextFilters?.walletAddress || null,
80+
walletAddress: userWalletAddress.trim() || address || _contextFilters?.walletAddress || null,
81+
autoExecuteTransactions: userAutoExecute,
7582
} satisfies NebulaContext;
76-
}, [_contextFilters, address]);
83+
}, [_contextFilters, address, userWalletAddress, userChainIds, userAutoExecute]);
7784

7885
const setContextFilters = useCallback((v: NebulaContext | undefined) => {
7986
_setContextFilters(v);
@@ -115,6 +122,11 @@ export function ChatPageContent(props: {
115122
const [enableAutoScroll, setEnableAutoScroll] = useState(false);
116123
const [showConnectModal, setShowConnectModal] = useState(false);
117124

125+
// User-configurable context options
126+
const [userWalletAddress, setUserWalletAddress] = useState("");
127+
const [userChainIds, setUserChainIds] = useState("");
128+
const [userAutoExecute, setUserAutoExecute] = useState(false);
129+
118130
const handleSendMessage = useCallback(
119131
async (message: NebulaUserMessage) => {
120132
setUserHasSubmittedMessage(true);
@@ -227,6 +239,14 @@ export function ChatPageContent(props: {
227239
{/* Chat input - anchored at bottom (same as chat state) */}
228240
<div className="flex-shrink-0 border-t bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
229241
<div className="container max-w-[800px] py-4">
242+
<ContextOptionsBar
243+
walletAddress={userWalletAddress}
244+
chainIds={userChainIds}
245+
autoExecute={userAutoExecute}
246+
onWalletAddressChange={setUserWalletAddress}
247+
onChainIdsChange={setUserChainIds}
248+
onAutoExecuteChange={setUserAutoExecute}
249+
/>
230250
<SimpleChatBar
231251
abortChatStream={() => {
232252
chatAbortController?.abort();
@@ -279,6 +299,14 @@ export function ChatPageContent(props: {
279299
{/* Chat input - anchored at bottom */}
280300
<div className="flex-shrink-0 border-t bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
281301
<div className="container max-w-[800px] py-4">
302+
<ContextOptionsBar
303+
walletAddress={userWalletAddress}
304+
chainIds={userChainIds}
305+
autoExecute={userAutoExecute}
306+
onWalletAddressChange={setUserWalletAddress}
307+
onChainIdsChange={setUserChainIds}
308+
onAutoExecuteChange={setUserAutoExecute}
309+
/>
282310
<SimpleChatBar
283311
abortChatStream={() => {
284312
chatAbortController?.abort();
@@ -708,6 +736,64 @@ function RenderMessage(props: {
708736
);
709737
}
710738

739+
function ContextOptionsBar(props: {
740+
walletAddress: string;
741+
chainIds: string;
742+
autoExecute: boolean;
743+
onWalletAddressChange: (value: string) => void;
744+
onChainIdsChange: (value: string) => void;
745+
onAutoExecuteChange: (value: boolean) => void;
746+
}) {
747+
return (
748+
<div className="mb-4 rounded-lg border bg-card p-3">
749+
<div className="flex flex-wrap items-center gap-4">
750+
<div className="flex items-center gap-2">
751+
<label htmlFor="wallet-address" className="text-sm font-medium text-muted-foreground">
752+
Wallet Address:
753+
</label>
754+
<input
755+
id="wallet-address"
756+
type="text"
757+
value={props.walletAddress}
758+
onChange={(e) => props.onWalletAddressChange(e.target.value)}
759+
placeholder="0x..."
760+
className="px-2 py-1 text-sm border border-border rounded focus:outline-none focus:ring-1 focus:ring-ring focus:border-ring bg-background"
761+
style={{ width: "200px" }}
762+
/>
763+
</div>
764+
765+
<div className="flex items-center gap-2">
766+
<label htmlFor="chain-ids" className="text-sm font-medium text-muted-foreground">
767+
Chain IDs:
768+
</label>
769+
<input
770+
id="chain-ids"
771+
type="text"
772+
value={props.chainIds}
773+
onChange={(e) => props.onChainIdsChange(e.target.value)}
774+
placeholder="1, 8453"
775+
className="px-2 py-1 text-sm border border-border rounded focus:outline-none focus:ring-1 focus:ring-ring focus:border-ring bg-background"
776+
style={{ width: "100px" }}
777+
/>
778+
</div>
779+
780+
<div className="flex items-center gap-2">
781+
<input
782+
id="auto-execute"
783+
type="checkbox"
784+
checked={props.autoExecute}
785+
onChange={(e) => props.onAutoExecuteChange(e.target.checked)}
786+
className="w-4 h-4 text-primary border-border rounded focus:ring-ring"
787+
/>
788+
<label htmlFor="auto-execute" className="text-sm font-medium text-muted-foreground">
789+
Auto Execute Transactions
790+
</label>
791+
</div>
792+
</div>
793+
</div>
794+
);
795+
}
796+
711797
const NEBULA_LAST_USED_CHAIN_IDS_KEY = "nebula-last-used-chain-ids";
712798

713799
function saveLastUsedChainIds(chainIds: string[] | undefined) {

0 commit comments

Comments
 (0)