Skip to content

Commit

Permalink
chore: migrate get wallet details (#192)
Browse files Browse the repository at this point in the history
  • Loading branch information
John-peterson-coinbase committed Feb 1, 2025
1 parent 017a36a commit 98159f4
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 1 deletion.
1 change: 1 addition & 0 deletions cdp-agentkit-core/typescript/src/action_providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from "./erc721";
export * from "./morpho";
export * from "./basename";
export * from "./farcaster";
export * from "./wallet";
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Wallet Action Provider

This action provider provides actions for getting basic wallet information.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./walletActionProvider";
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { WalletProvider } from "../../wallet_providers/wallet_provider";
import { walletActionProvider } from "./walletActionProvider";

describe("Wallet Action Provider", () => {
const MOCK_ADDRESS = "0xe6b2af36b3bb8d47206a129ff11d5a2de2a63c83";
const MOCK_BALANCE = 1000000000000000000n; // 1 ETH in wei
const MOCK_NETWORK = {
protocolFamily: "evm",
networkId: "base-sepolia",
chainId: "123",
};
const MOCK_PROVIDER_NAME = "TestWallet";

let mockWallet: jest.Mocked<WalletProvider>;
const actionProvider = walletActionProvider();

beforeEach(() => {
mockWallet = {
getAddress: jest.fn().mockReturnValue(MOCK_ADDRESS),
getNetwork: jest.fn().mockReturnValue(MOCK_NETWORK),
getBalance: jest.fn().mockResolvedValue(MOCK_BALANCE),
getName: jest.fn().mockReturnValue(MOCK_PROVIDER_NAME),
} as unknown as jest.Mocked<WalletProvider>;
});

describe("getWalletDetails", () => {
it("should successfully get wallet details", async () => {
const response = await actionProvider.getWalletDetails(mockWallet, {});

expect(mockWallet.getAddress).toHaveBeenCalled();
expect(mockWallet.getNetwork).toHaveBeenCalled();
expect(mockWallet.getBalance).toHaveBeenCalled();
expect(mockWallet.getName).toHaveBeenCalled();

const expectedResponse = `Wallet Details:
- Provider: ${MOCK_PROVIDER_NAME}
- Address: ${MOCK_ADDRESS}
- Network:
* Protocol Family: ${MOCK_NETWORK.protocolFamily}
* Network ID: ${MOCK_NETWORK.networkId}
* Chain ID: ${MOCK_NETWORK.chainId}
- Native Balance: ${MOCK_BALANCE.toString()}`;

expect(response).toBe(expectedResponse);
});

it("should handle missing network IDs gracefully", async () => {
mockWallet.getNetwork.mockReturnValue({
protocolFamily: "evm",
});

const response = await actionProvider.getWalletDetails(mockWallet, {});

expect(response).toContain("Network ID: N/A");
expect(response).toContain("Chain ID: N/A");
});

it("should handle errors when getting wallet details", async () => {
const error = new Error("Failed to get wallet details");
mockWallet.getBalance.mockRejectedValue(error);

const response = await actionProvider.getWalletDetails(mockWallet, {});
expect(response).toBe(`Error getting wallet details: ${error}`);
});
});

describe("supportsNetwork", () => {
it("should return true for any network", () => {
const evmNetwork = { protocolFamily: "evm", networkId: "base-sepolia" };
const solanaNetwork = { protocolFamily: "solana", networkId: "mainnet" };
const bitcoinNetwork = { protocolFamily: "bitcoin", networkId: "mainnet" };

expect(actionProvider.supportsNetwork(evmNetwork)).toBe(true);
expect(actionProvider.supportsNetwork(solanaNetwork)).toBe(true);
expect(actionProvider.supportsNetwork(bitcoinNetwork)).toBe(true);
});
});

describe("action provider setup", () => {
it("should have the correct name", () => {
expect(actionProvider.name).toBe("wallet");
});

it("should have empty actionProviders array", () => {
expect(actionProvider.actionProviders).toEqual([]);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { z } from "zod";
import { ActionProvider } from "../action_provider";
import { Network, WalletProvider } from "../../wallet_providers/wallet_provider";
import { CreateAction } from "../action_decorator";

/**
* Schema for the get_wallet_details action.
* This action doesn't require any input parameters, so we use an empty object schema.
*/
const GetWalletDetailsSchema = z.object({});

/**
* WalletActionProvider provides actions for getting basic wallet information.
*/
export class WalletActionProvider extends ActionProvider {
/**
* Constructor for the WalletActionProvider.
*/
constructor() {
super("wallet", []);
}

/**
* Gets the details of the connected wallet including address, network, and balance.
*
* @param walletProvider - The wallet provider to get the details from.
* @param _ - Empty args object (not used).
* @returns A formatted string containing the wallet details.
*/
@CreateAction({
name: "get_wallet_details",
description: `
This tool will return the details of the connected wallet including:
- Wallet address
- Network information (protocol family, network ID, chain ID)
- Native token balance
- Wallet provider name
`,
schema: GetWalletDetailsSchema,
})
async getWalletDetails(
walletProvider: WalletProvider,
_: z.infer<typeof GetWalletDetailsSchema>,
): Promise<string> {
try {
const address = walletProvider.getAddress();
const network = walletProvider.getNetwork();
const balance = await walletProvider.getBalance();
const name = walletProvider.getName();

return `Wallet Details:
- Provider: ${name}
- Address: ${address}
- Network:
* Protocol Family: ${network.protocolFamily}
* Network ID: ${network.networkId || "N/A"}
* Chain ID: ${network.chainId || "N/A"}
- Native Balance: ${balance.toString()}`;
} catch (error) {
return `Error getting wallet details: ${error}`;
}
}

/**
* Checks if the wallet action provider supports the given network.
* Since wallet actions are network-agnostic, this always returns true.
*
* @param _ - The network to check.
* @returns True, as wallet actions are supported on all networks.
*/
supportsNetwork = (_: Network): boolean => true;
}

/**
* Factory function to create a new WalletActionProvider instance.
*
* @returns A new WalletActionProvider instance.
*/
export const walletActionProvider = () => new WalletActionProvider();
4 changes: 3 additions & 1 deletion cdp-langchain/examples/chatbot-typescript/chatbot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ async function initializeAgent() {
});
const erc721 = erc721ActionProvider();
const pyth = pythActionProvider();
const wallet = walletActionProvider();

const agentKit = new AgentKit({ walletProvider, actionProviders: [pyth, cdp, erc721] });
const agentKit = new AgentKit({ walletProvider, actionProviders: [pyth, cdp, erc721, wallet] });
const actions = agentKit.getActions();
for (const action of actions) {
console.log(action.name);
Expand Down Expand Up @@ -192,6 +193,7 @@ import {
ViemWalletProvider,
cdpActionProvider,
erc721ActionProvider,
walletActionProvider,
} from "@coinbase/cdp-agentkit-core";
import { createWalletClient, http } from "viem";
import { baseSepolia } from "viem/chains";
Expand Down

0 comments on commit 98159f4

Please sign in to comment.