Skip to content

Commit

Permalink
Upstream/feat/solana sample elevenlabs (#62)
Browse files Browse the repository at this point in the history
* Gutted out wagmi for dynamic, so we can add solana too

* feat: wip solana support

* fix: adapt dynamic signer to solana wallet client type

* fix: type for solana wallet client

* fix: new env default

* fix: add loading state

* fix: commit lockfile changes

* fix: cleanup tools initialization

* fix: remove dynamic example in favour of default one

* fix: import sendEth plugin from sdk package

* fix: regen lock file after removing dynamic specific example

* fix: lint errors

---------

Co-authored-by: alfonso-paella <[email protected]>
  • Loading branch information
matthew1809 and alfonso-paella authored Dec 16, 2024
1 parent a92d30f commit 9a80478
Show file tree
Hide file tree
Showing 27 changed files with 1,511 additions and 2,004 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,20 @@
},
"dependencies": {
"@11labs/react": "^0.0.4",
"@dynamic-labs/ethereum": "4.0.0-alpha.45",
"@dynamic-labs/sdk-react-core": "4.0.0-alpha.45",
"@dynamic-labs/solana": "4.0.0-alpha.45",
"@dynamic-labs/solana-core": "3.9.2",
"@dynamic-labs/wagmi-connector": "4.0.0-alpha.45",
"@goat-sdk/adapter-eleven-labs": "workspace:*",
"@goat-sdk/core": "workspace:*",
"@goat-sdk/wallet-viem": "workspace:*",
"@goat-sdk/plugin-coingecko": "workspace:*",
"@goat-sdk/wallet-solana": "workspace:*",
"@goat-sdk/wallet-viem": "workspace:*",
"@tanstack/react-query": "^5.62.2",
"connectkit": "1.8.2",
"next": "15.0.3",
"react": "19.0.0-rc-66855b96-20241106",
"react-dom": "19.0.0-rc-66855b96-20241106",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"viem": "catalog:",
"wagmi": "^2.13.3"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ import { useConversation } from "@11labs/react";
import { getOnChainTools } from "@goat-sdk/adapter-eleven-labs";
import { useCallback } from "react";

import { isEthereumWallet } from "@dynamic-labs/ethereum";
import { DynamicWidget, useIsLoggedIn } from "@dynamic-labs/sdk-react-core";
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
import { isSolanaWallet } from "@dynamic-labs/solana";

import { sendETH } from "@goat-sdk/core";
import { coingecko } from "@goat-sdk/plugin-coingecko";
import { viem } from "@goat-sdk/wallet-viem";
import { ConnectKitButton } from "connectkit";
import { useAccount, useWalletClient } from "wagmi";
import { createSolanaWalletFromDynamic } from "../utils";

export function Conversation() {
const { isConnected } = useAccount();
const { data: wallet } = useWalletClient(); // Get the viem wallet client from Wagmi
const isLoggedIn = useIsLoggedIn();
const { primaryWallet, sdkHasLoaded } = useDynamicContext();

const isConnected = sdkHasLoaded && isLoggedIn && primaryWallet;

const conversation = useConversation({
onConnect: () => console.log("Connected"),
Expand All @@ -26,42 +32,69 @@ export function Conversation() {
// Request microphone permission
await navigator.mediaDevices.getUserMedia({ audio: true });

if (!wallet) {
if (!primaryWallet) {
throw new Error("Wallet not connected");
}

// const wallet = viem Client
const tools = await getOnChainTools({
wallet: viem(wallet),
plugins: [
sendETH(),
coingecko({
apiKey: process.env.NEXT_PUBLIC_COINGECKO_API_KEY ?? "",
}),
],
options: {
logTools: true,
},
});
let tools = null;

if (isSolanaWallet(primaryWallet)) {
const connection = await primaryWallet.getConnection();
const signer = await primaryWallet.getSigner();

tools = await getOnChainTools({
wallet: createSolanaWalletFromDynamic(connection, signer),
plugins: [
coingecko({
apiKey: process.env.NEXT_PUBLIC_COINGECKO_API_KEY ?? "",
}),
],
});
} else if (isEthereumWallet(primaryWallet)) {
tools = await getOnChainTools({
wallet: viem(await primaryWallet.getWalletClient()),
plugins: [
sendETH(),
coingecko({
apiKey: process.env.NEXT_PUBLIC_COINGECKO_API_KEY ?? "",
}),
],
options: {
logTools: true,
},
});
} else {
throw new Error("Unsupported wallet type");
}

if (!tools) {
throw new Error("Failed to initialize tools");
}

console.log("tools", tools);

// Start the conversation with your agent
await conversation.startSession({
agentId: process.env.NEXT_PUBLIC_ELEVEN_LABS_AGENT_ID ?? "", // Replace with your agent ID
agentId: process.env.NEXT_PUBLIC_ELEVEN_LABS_AGENT_ID ?? "",
clientTools: tools,
});
} catch (error) {
console.error("Failed to start conversation:", error);
}
}, [conversation, wallet]);
}, [conversation, primaryWallet]);

const stopConversation = useCallback(async () => {
await conversation.endSession();
}, [conversation]);

if (!sdkHasLoaded) {
return <div>Loading...</div>;
}

return (
<div className="flex flex-col items-center gap-4">
<h1 className="text-2xl font-bold">1. Connect Wallet to start</h1>
<ConnectKitButton />
<DynamicWidget />

<h1 className="text-2xl font-bold">2. Start Conversation with Agent</h1>
<div className="flex flex-col items-center gap-4">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,18 @@
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ConnectKitProvider, getDefaultConfig } from "connectkit";
import { http, WagmiProvider, createConfig } from "wagmi";
import { sepolia } from "wagmi/chains";

const config = createConfig(
getDefaultConfig({
// Your dApps chains
chains: [sepolia],
transports: {
// RPC URL for each chain
[sepolia.id]: http(process.env.NEXT_PUBLIC_SEPOLIA_RPC_URL ?? ""),
},

// Required API Keys
walletConnectProjectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID ?? "",

// Required App Info
appName: "GOAT Conversational AI",
}),
);

const queryClient = new QueryClient();
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";
import { SolanaWalletConnectors } from "@dynamic-labs/solana";

export const Web3Provider = ({ children }: { children: React.ReactNode }) => {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<ConnectKitProvider>{children}</ConnectKitProvider>
</QueryClientProvider>
</WagmiProvider>
<DynamicContextProvider
settings={{
environmentId: "f268a013-0fa0-4d9d-9314-c6919e2dfde7", // TODO replace in prod
walletConnectors: [EthereumWalletConnectors, SolanaWalletConnectors],
}}
>
{children}
</DynamicContextProvider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { ISolana } from "@dynamic-labs/solana-core";
import type { SolanaReadRequest, SolanaTransaction, SolanaWalletClient } from "@goat-sdk/core";
import { type Connection, PublicKey, TransactionMessage, VersionedTransaction } from "@solana/web3.js";

export function createSolanaWalletFromDynamic(connection: Connection, signer: ISolana): SolanaWalletClient {
const publicKey = signer.publicKey;
if (!publicKey) {
throw new Error("Signer public key is undefined");
}

return {
getAddress: () => new PublicKey(publicKey.toBytes()).toBase58(),
getChain() {
return {
type: "solana",
};
},
async signMessage(message: string) {
const messageBytes = Buffer.from(message);
const signature = await signer.signMessage(messageBytes);
return {
signature: Buffer.from(signature.signature).toString("hex"),
};
},
async sendTransaction({ instructions }: SolanaTransaction) {
const latestBlockhash = await connection.getLatestBlockhash("confirmed");
const message = new TransactionMessage({
payerKey: new PublicKey(publicKey.toBytes()),
recentBlockhash: latestBlockhash.blockhash,
instructions,
}).compileToV0Message();
const transaction = new VersionedTransaction(message);
const signedTx = await signer.signTransaction(transaction);

const txid = await connection.sendTransaction(signedTx, {
maxRetries: 5,
});

return {
hash: txid,
};
},
async read(request: SolanaReadRequest) {
const { accountAddress } = request;
const pubkey = new PublicKey(accountAddress);
const accountInfo = await connection.getAccountInfo(pubkey);

if (!accountInfo) {
throw new Error(`Account ${accountAddress} not found`);
}

return {
value: accountInfo,
};
},
async balanceOf(address: string) {
const pubkey = new PublicKey(address);
const balance = await connection.getBalance(pubkey);

return {
decimals: 9,
symbol: "SOL",
name: "Solana",
value: BigInt(balance),
};
},
};
}
4 changes: 0 additions & 4 deletions typescript/examples/eleven-labs/dynamic/.example.env

This file was deleted.

40 changes: 0 additions & 40 deletions typescript/examples/eleven-labs/dynamic/.gitignore

This file was deleted.

44 changes: 0 additions & 44 deletions typescript/examples/eleven-labs/dynamic/CHANGELOG.md

This file was deleted.

30 changes: 0 additions & 30 deletions typescript/examples/eleven-labs/dynamic/README.md

This file was deleted.

7 changes: 0 additions & 7 deletions typescript/examples/eleven-labs/dynamic/next.config.ts

This file was deleted.

Loading

0 comments on commit 9a80478

Please sign in to comment.