-
Notifications
You must be signed in to change notification settings - Fork 659
feat: add privy server wallet provider #242
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
Changes from all commits
f8eb4dc
7b194fa
189ff6a
e4e3558
47c6477
0b67c61
ba189e3
aaf356c
cfab8cf
2f4e4b3
72e88b4
356ed23
4d13dd2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| import { PrivyClient } from "@privy-io/server-auth"; | ||
| import { createViemAccount } from "@privy-io/server-auth/viem"; | ||
| import { ViemWalletProvider } from "./viemWalletProvider"; | ||
| import { createWalletClient, http, WalletClient } from "viem"; | ||
| import { getChain } from "../network/network"; | ||
| /** | ||
| * Configuration options for the Privy wallet provider. | ||
| * | ||
| * @interface | ||
| */ | ||
| interface PrivyWalletConfig { | ||
John-peterson-coinbase marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** The Privy application ID */ | ||
| appId: string; | ||
| /** The Privy application secret */ | ||
| appSecret: string; | ||
| /** The ID of the wallet to use, if not provided a new wallet will be created */ | ||
| walletId?: string; | ||
| /** Optional chain ID to connect to */ | ||
| chainId?: string; | ||
| /** Optional authorization key for the wallet API */ | ||
| authorizationPrivateKey?: string; | ||
| /** Optional authorization key ID for creating new wallets */ | ||
| authorizationKeyId?: string; | ||
| } | ||
|
|
||
| type PrivyWalletExport = { | ||
| walletId: string; | ||
| authorizationPrivateKey: string | undefined; | ||
| chainId: string | undefined; | ||
| }; | ||
|
|
||
| /** | ||
| * A wallet provider that uses Privy's server wallet API. | ||
| * This provider extends the ViemWalletProvider to provide Privy-specific wallet functionality | ||
| * while maintaining compatibility with the base wallet provider interface. | ||
| */ | ||
| export class PrivyWalletProvider extends ViemWalletProvider { | ||
| #walletId: string; | ||
| #authorizationPrivateKey: string | undefined; | ||
|
|
||
| /** | ||
| * Private constructor to enforce use of factory method. | ||
| * | ||
| * @param walletClient - The Viem wallet client instance | ||
| * @param config - The configuration options for the Privy wallet | ||
| */ | ||
| private constructor( | ||
| walletClient: WalletClient, | ||
| config: PrivyWalletConfig & { walletId: string }, // Require walletId in constructor | ||
| ) { | ||
| super(walletClient); | ||
| this.#walletId = config.walletId; // Now guaranteed to exist | ||
| this.#authorizationPrivateKey = config.authorizationPrivateKey; | ||
| } | ||
|
|
||
| /** | ||
| * Creates and configures a new PrivyWalletProvider instance. | ||
| * | ||
| * @param config - The configuration options for the Privy wallet | ||
| * @returns A configured PrivyWalletProvider instance | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const provider = await PrivyWalletProvider.configureWithWallet({ | ||
| * appId: "your-app-id", | ||
| * appSecret: "your-app-secret", | ||
| * walletId: "wallet-id", | ||
| * chainId: "84532" | ||
| * }); | ||
| * ``` | ||
| */ | ||
| public static async configureWithWallet(config: PrivyWalletConfig): Promise<PrivyWalletProvider> { | ||
| const privy = new PrivyClient(config.appId, config.appSecret, { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is an edge case here where the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's ok to initialise with just an
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, you're right - if the user has an existing wallet that has a key attached to it, you need to specify |
||
| walletApi: config.authorizationPrivateKey | ||
| ? { | ||
| authorizationPrivateKey: config.authorizationPrivateKey, | ||
| } | ||
| : undefined, | ||
| }); | ||
|
|
||
| let walletId: string; | ||
| let address: `0x${string}`; | ||
|
|
||
| if (!config.walletId) { | ||
| if (config.authorizationPrivateKey && !config.authorizationKeyId) { | ||
| throw new Error( | ||
| "authorizationKeyId is required when creating a new wallet with an authorization key, this can be found in your Privy Dashboard", | ||
| ); | ||
| } | ||
0xRAG marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if (config.authorizationKeyId && !config.authorizationPrivateKey) { | ||
| throw new Error( | ||
| "authorizationPrivateKey is required when creating a new wallet with an authorizationKeyId. " + | ||
| "If you don't have it, you can create a new one in your Privy Dashboard, or delete the authorization key.", | ||
| ); | ||
| } | ||
|
|
||
| try { | ||
| const wallet = await privy.walletApi.create({ | ||
| chainType: "ethereum", | ||
| authorizationKeyIds: config.authorizationKeyId ? [config.authorizationKeyId] : undefined, | ||
| }); | ||
| walletId = wallet.id; | ||
| address = wallet.address as `0x${string}`; | ||
| } catch (error) { | ||
| console.error(error); | ||
| if ( | ||
| error instanceof Error && | ||
| error.message.includes("Missing `privy-authorization-signature` header") | ||
| ) { | ||
| // Providing a more informative error message, see context: https://github.com/coinbase/agentkit/pull/242#discussion_r1956428617 | ||
| throw new Error( | ||
| "Privy error: you have an authorization key on your account which can create and modify wallets, please delete this key or pass it to the PrivyWalletProvider to create a new wallet", | ||
| ); | ||
| } | ||
| throw new Error("Failed to create wallet"); | ||
| } | ||
| } else { | ||
| walletId = config.walletId; | ||
| const wallet = await privy.walletApi.getWallet({ id: walletId }); | ||
| if (!wallet) { | ||
| throw new Error(`Wallet with ID ${walletId} not found`); | ||
| } | ||
| address = wallet.address as `0x${string}`; | ||
| } | ||
|
|
||
| const account = await createViemAccount({ | ||
| walletId, | ||
| address, | ||
| privy, | ||
| }); | ||
|
|
||
| const chainId = config.chainId || "84532"; | ||
|
|
||
| const chain = getChain(chainId); | ||
| if (!chain) { | ||
| throw new Error(`Chain with ID ${chainId} not found`); | ||
| } | ||
|
|
||
| const walletClient = createWalletClient({ | ||
| account, | ||
| chain, | ||
| transport: http(), | ||
| }); | ||
| return new PrivyWalletProvider(walletClient, { ...config, walletId }); | ||
| } | ||
|
|
||
| /** | ||
| * Gets the name of the wallet provider. | ||
| * | ||
| * @returns The string identifier for this wallet provider | ||
| */ | ||
| getName(): string { | ||
| return "privy_wallet_provider"; | ||
| } | ||
|
|
||
| /** | ||
| * Exports the wallet data. | ||
| * | ||
| * @returns The wallet data | ||
| */ | ||
| exportWallet(): PrivyWalletExport { | ||
| return { | ||
| walletId: this.#walletId, | ||
| authorizationPrivateKey: this.#authorizationPrivateKey, | ||
| chainId: this.getNetwork().chainId, | ||
| }; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| OPENAI_API_KEY= | ||
|
|
||
| # Privy Configuration - get these from your Privy dashboard | ||
| PRIVY_APP_ID= | ||
| PRIVY_APP_SECRET= | ||
|
|
||
| # Optional Wallet ID, otherwise a new wallet will be created | ||
| PRIVY_WALLET_ID= | ||
|
|
||
| # Optional Authorization Private Key, if you are using them for your server wallets | ||
| PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY= | ||
|
|
||
| # Optional Authorization Key ID from your Privy Dashboard, if you want to create a new wallet | ||
| PRIVY_WALLET_AUTHORIZATION_KEY_ID= |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "parser": "@typescript-eslint/parser", | ||
| "extends": ["../../../.eslintrc.base.json"] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| # Privy AgentKit LangChain Extension Examples - Chatbot Typescript | ||
|
|
||
| This example demonstrates an agent setup as a self-aware terminal style chatbot with a [Privy server wallet](https://docs.privy.io/guide/server-wallets/). | ||
|
|
||
| Privy's server wallets enable you to securely provision and manage cross-chain wallets via a flexible API - learn more at https://docs.privy.io/guide/server-wallets/. The Agentkit integration assumes you have a Privy server wallet ID which you want to use for your agent - creation and management of Privy wallets can be done via the Privy dashboard or API. | ||
|
|
||
| ## Ask the chatbot to engage in the Web3 ecosystem! | ||
|
|
||
| - "Transfer a portion of your ETH to a random address" | ||
| - "What is the price of BTC?" | ||
| - "What kind of wallet do you have?" | ||
|
|
||
| ## Requirements | ||
|
|
||
| - Node.js 18+ | ||
| - [Privy](https://dashboard.privy.io/apps) (see ENV Vars below for details) | ||
|
|
||
| ### Checking Node Version | ||
|
|
||
| Before using the example, ensure that you have the correct version of Node.js installed. The example requires Node.js 18 or higher. You can check your Node version by running: | ||
|
|
||
| ```bash | ||
| node --version | ||
| npm --version | ||
| ``` | ||
|
|
||
| ## Installation | ||
|
|
||
| ```bash | ||
| npm install | ||
| ``` | ||
|
|
||
| ## Run the Chatbot | ||
|
|
||
| ### Set ENV Vars | ||
|
|
||
| - Ensure the following ENV Vars from your Privy dashboard are set in `.env`: | ||
| - PRIVY_APP_ID= | ||
| - PRIVY_APP_SECRET= | ||
| - PRIVY_WALLET_ID=[optional, otherwise a new wallet will be created] | ||
| - PRIVY_WALLET_AUTHORIZATION_PRIVATE_KEY=[optional, only if you are using authorization keys for your server wallets] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updating README |
||
| - PRIVY_WALLET_AUTHORIZATION_KEY_ID=[optional, only if walletId is not provided in order to create a new wallet, this can be found in your Privy Dashboard] | ||
|
|
||
| You can manage authorization keys from your [Privy dashboard](https://dashboard.privy.io/account) or programmatically [using the API](https://docs.privy.io/guide/server-wallets/authorization/signatures). | ||
|
|
||
| ```bash | ||
| npm start | ||
| ``` | ||
|
|
||
| ## License | ||
|
|
||
| Apache-2.0 | ||
Uh oh!
There was an error while loading. Please reload this page.