From 86e1b4de63d4e8d8d8e00ca2c96e9451eeb19e36 Mon Sep 17 00:00:00 2001 From: "escottalexander@gmail.com" Date: Fri, 7 Feb 2025 17:07:39 -0500 Subject: [PATCH 01/16] WIP --- lib/db/queries.ts | 71 +++++++++- lib/db/schema.ts | 14 +- .../agentkit/action-providers/safe/index.ts | 129 +++++++++++++++++- .../agentkit/action-providers/safe/schemas.ts | 37 +++++ .../agentkit/action-providers/safe/types.ts | 22 ++- package.json | 1 + pnpm-lock.yaml | 3 + 7 files changed, 270 insertions(+), 7 deletions(-) diff --git a/lib/db/queries.ts b/lib/db/queries.ts index 730f187..fbd43d6 100644 --- a/lib/db/queries.ts +++ b/lib/db/queries.ts @@ -3,7 +3,6 @@ import { and, asc, desc, eq, gt, gte, inArray, isNull, sql } from "drizzle-orm"; import { drizzle } from "drizzle-orm/postgres-js"; import postgres from "postgres"; import * as schema from "./schema"; - import { user, chat, @@ -18,8 +17,11 @@ import { type UserKnowledge, charge, type UserWithRelations, + safeTransaction } from "./schema"; import type { BlockKind } from "@/components/block"; +import { SafeTransaction as SafeTxType, SafeMultisigTransactionResponse } from '@safe-global/types-kit'; + // Optionally, if not using email/pass login, you can // use the Drizzle adapter for Auth.js / NextAuth @@ -635,3 +637,70 @@ export async function getUserCharges(userId: string) { throw error; } } + +export async function saveSafeTransaction({ + transactionHash, + safeAddress, + transactionData, +}: { + transactionHash: string; + safeAddress: string; + transactionData: SafeTxType | SafeMultisigTransactionResponse; +}) { + try { + const existingTx = await getSafeTransactionByHash({ transactionHash }); + + if (existingTx) { + // Merge signatures from existing and new transaction data + const existingSignatures = (existingTx.transactionData as SafeTxType).signatures || {}; + const newSignatures = transactionData.signatures || {}; + + const mergedTransactionData = { + ...transactionData, + signatures: { + ...existingSignatures, + ...newSignatures + } + }; + + // Update existing transaction with merged data + return await db + .update(safeTransaction) + .set({ + transactionData: mergedTransactionData, + }) + .where(eq(safeTransaction.transactionHash, transactionHash)); + } + + // Insert new transaction if it doesn't exist + return await db.insert(safeTransaction).values({ + transactionHash, + safeAddress, + signatureCount: Object.keys(transactionData?.signatures || {}).length, + transactionData, + createdAt: new Date(), + }); + } catch (error) { + console.error('Failed to save safe transaction in database'); + throw error; + } +} + +export async function getSafeTransactionByHash({ + transactionHash, +}: { + transactionHash: string; +}) { + try { + const [transaction] = await db + .select() + .from(safeTransaction) + .where(eq(safeTransaction.transactionHash, transactionHash)) + .limit(1); + + return transaction; + } catch (error) { + console.error('Failed to get safe transaction by hash from database'); + throw error; + } +} diff --git a/lib/db/schema.ts b/lib/db/schema.ts index 27d5e86..d79625e 100644 --- a/lib/db/schema.ts +++ b/lib/db/schema.ts @@ -10,7 +10,8 @@ import { foreignKey, boolean, bigint, -} from "drizzle-orm/pg-core"; + integer, +} from 'drizzle-orm/pg-core'; export const user = pgTable("User", { id: varchar("id", { length: 42 }).primaryKey().notNull(), // Ethereum address @@ -165,6 +166,15 @@ export const suggestion = pgTable( }) ); +export const safeTransaction = pgTable("SafeTransaction", { + id: uuid("id").primaryKey().notNull().defaultRandom(), + transactionHash: text("transactionHash").notNull().unique(), + safeAddress: text("safeAddress").notNull(), + transactionData: json("transactionData").notNull(), + signatureCount: integer("signatureCount").notNull().default(0), + createdAt: timestamp("createdAt").notNull().defaultNow(), +}); + export type Suggestion = InferSelectModel; export type UserKnowledge = InferSelectModel; @@ -172,3 +182,5 @@ export type UserKnowledge = InferSelectModel; export type StarterKit = InferSelectModel; export type Charge = InferSelectModel; + +export type SafeTransaction = InferSelectModel; diff --git a/lib/web3/agentkit/action-providers/safe/index.ts b/lib/web3/agentkit/action-providers/safe/index.ts index 92fd33b..bceb734 100644 --- a/lib/web3/agentkit/action-providers/safe/index.ts +++ b/lib/web3/agentkit/action-providers/safe/index.ts @@ -2,12 +2,15 @@ import { ActionProvider, CreateAction, EvmWalletProvider, Network } from '@coinb import Safe, { OnchainAnalyticsProps, PredictedSafeProps, - SafeAccountConfig + SafeAccountConfig, + SigningMethod } from '@safe-global/protocol-kit' import { waitForTransactionReceipt } from 'viem/actions' import { baseSepolia } from 'viem/chains' -import { CreateSafeSchema } from './schemas'; +import { CreateSafeSchema, CreateSafeTransactionSchema, ExecuteSafeTransactionSchema, SignSafeTransactionSchema } from './schemas'; import { z } from 'zod'; +import { saveSafeTransaction, getSafeTransactionByHash } from '@/lib/db/queries'; +import { SafeTransaction, SafeMultisigTransactionResponse } from '@safe-global/types-kit'; const onchainAnalytics: OnchainAnalyticsProps = { project: 'HELLO_WORLD_COMPUTER', // Required. Always use the same value for your project. @@ -15,7 +18,6 @@ const onchainAnalytics: OnchainAnalyticsProps = { }; export class SafeActionProvider extends ActionProvider { - constructor() { super("safe", []); } @@ -52,7 +54,7 @@ export class SafeActionProvider extends ActionProvider { safeAccountConfig // ... }; - + const protocolKit = await Safe.init({ provider: baseSepolia.rpcUrls.default.http[0], signer: walletProvider.getAddress(), @@ -106,6 +108,125 @@ export class SafeActionProvider extends ActionProvider { } } + @CreateAction({ + name: "create_safe_transaction", + description: ` + This tool will create a transaction for a safe. The transaction is not executed. + It takes the following inputs: + - safeAddress: The address of the safe + - transactions: The transactions to be executed + `, + schema: CreateSafeTransactionSchema + }) + async createSafeTransaction( + walletProvider: EvmWalletProvider, + args: z.infer + ): Promise { + try { + const protocolKit = await Safe.init({ + provider: baseSepolia.rpcUrls.default.http[0], + signer: walletProvider.getAddress(), + safeAddress: args.safeAddress + }); + + const safeTx = await protocolKit.createTransaction({ + transactions: args.transactions + }); + // Sign the transaction as owner + const signedSafeTransaction = await protocolKit.signTransaction(safeTx, SigningMethod.ETH_SIGN_TYPED_DATA); + const transactionHash = await protocolKit.getTransactionHash(safeTx); + + // Store the signed transaction using the new query method + await saveSafeTransaction({ + transactionHash, + safeAddress: args.safeAddress, + transactionData: signedSafeTransaction, + }); + + return { transactionHash, signatureCount: Object.keys(signedSafeTransaction.signatures).length }; + } catch (error: any) { + return { error: error.message }; + } + } + + @CreateAction({ + name: "sign_safe_transaction", + description: ` + This tool will sign a transaction for a safe. + It takes the following inputs: + - safeAddress: The address of the safe + - transactionHash: The hash of the transaction to be signed + `, + schema: SignSafeTransactionSchema + }) + async signSafeTransaction( + walletProvider: EvmWalletProvider, + args: z.infer + ): Promise { + try { + const protocolKit = await Safe.init({ + provider: baseSepolia.rpcUrls.default.http[0], + signer: walletProvider.getAddress(), + safeAddress: args.safeAddress + }); + + const storedTx = await getSafeTransactionByHash({ + transactionHash: args.transactionHash + }); + + const txResponse = await protocolKit.signTransaction(storedTx.transactionData as SafeTransaction | SafeMultisigTransactionResponse, SigningMethod.ETH_SIGN_TYPED_DATA); + + await saveSafeTransaction({ + transactionHash: args.transactionHash, + safeAddress: args.safeAddress, + transactionData: txResponse, + }); + + const signatureCount = Object.keys(txResponse.signatures).length; + + return { transactionHash: args.transactionHash, signatureCount }; + } catch (error: any) { + return { error: error.message }; + } + } + + @CreateAction({ + name: "execute_safe_transaction", + description: ` + This tool will execute a transaction for a safe assuming it has the required amount of signatures. + It takes the following inputs: + - safeAddress: The address of the safe + - transactionHash: The hash of the transaction to be executed + `, + schema: ExecuteSafeTransactionSchema + }) + async executeSafeTransaction( + walletProvider: EvmWalletProvider, + args: z.infer + ): Promise { + try { + const protocolKit = await Safe.init({ + provider: baseSepolia.rpcUrls.default.http[0], + signer: walletProvider.getAddress(), + safeAddress: args.safeAddress + }); + + // Get the transaction using the new query method + const storedTx = await getSafeTransactionByHash({ + transactionHash: args.transactionHash + }); + + if (!storedTx) { + throw new Error("Transaction not found"); + } + + const txResponse = await protocolKit.executeTransaction(storedTx.transactionData as SafeTransaction | SafeMultisigTransactionResponse); + + return { transactionHash: txResponse.hash }; + } catch (error: any) { + return { error: error.message }; + } + } /** * Checks if the Safe action provider supports the given network. diff --git a/lib/web3/agentkit/action-providers/safe/schemas.ts b/lib/web3/agentkit/action-providers/safe/schemas.ts index adfff7f..6bff1e3 100644 --- a/lib/web3/agentkit/action-providers/safe/schemas.ts +++ b/lib/web3/agentkit/action-providers/safe/schemas.ts @@ -14,3 +14,40 @@ export const CreateSafeSchema = z }) .strip() .describe("Instructions for creating a safe (multisig wallet)"); + +/** + * Input schema for create safe transaction action. + */ +export const CreateSafeTransactionSchema = z + .object({ + safeAddress: z.string().describe("The address of the safe"), + transactions: z.array(z.object({ + to: z.string().describe("The address of the recipient"), + value: z.string().describe("The value of the transaction. Must be a whole number. No decimals allowed. Example:0.1 ETH is 100000000000000000"), + data: z.string().describe("The data of the transaction"), + })).describe("The transactions to be executed"), + }) + .strip() + .describe("Instructions for creating a safe transaction"); + +/** + * Input schema for sign safe transaction action. + */ +export const SignSafeTransactionSchema = z + .object({ + safeAddress: z.string().describe("The address of the safe"), + transactionHash: z.string().describe("The hash of the transaction to be signed"), + }) + .strip() + .describe("Instructions for signing a safe transaction"); + +/** + * Input schema for execute safe transaction action. + */ +export const ExecuteSafeTransactionSchema = z + .object({ + safeAddress: z.string().describe("The address of the safe"), + transactionHash: z.string().describe("The hash of the transaction to be executed"), + }) + .strip() + .describe("Instructions for executing a safe transaction"); diff --git a/lib/web3/agentkit/action-providers/safe/types.ts b/lib/web3/agentkit/action-providers/safe/types.ts index 2dd7952..563c45e 100644 --- a/lib/web3/agentkit/action-providers/safe/types.ts +++ b/lib/web3/agentkit/action-providers/safe/types.ts @@ -5,4 +5,24 @@ type CreateSafeReturnType = { owners: string[] } | { error: Error; -}; \ No newline at end of file +}; + +type CreateSafeTransactionReturnType = { + transactionHash: string; + signatureCount: number; +} | { + error: Error; +}; + +type SignSafeTransactionReturnType = { + transactionHash: string; + signatureCount: number; +} | { + error: Error; +}; + +type ExecuteSafeTransactionReturnType = { + transactionHash: string; +} | { + error: Error; +}; diff --git a/package.json b/package.json index f9015ef..1c1b1bc 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@radix-ui/react-tooltip": "^1.1.3", "@radix-ui/react-visually-hidden": "^1.1.0", "@safe-global/protocol-kit": "^5.2.1", + "@safe-global/types-kit": "^1.0.2", "@tanstack/react-query": "^5.66.0", "@vercel/analytics": "^1.3.1", "@vercel/blob": "^0.24.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76d7f6c..2763cb7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,9 @@ importers: '@safe-global/protocol-kit': specifier: ^5.2.1 version: 5.2.1(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) + '@safe-global/types-kit': + specifier: ^1.0.2 + version: 1.0.2(typescript@5.6.3)(zod@3.23.8) '@tanstack/react-query': specifier: ^5.66.0 version: 5.66.0(react@19.0.0-rc-45804af1-20241021) From 5da331184f7c154035bd2090fed9f10699523a83 Mon Sep 17 00:00:00 2001 From: "escottalexander@gmail.com" Date: Sat, 8 Feb 2025 08:40:15 -0500 Subject: [PATCH 02/16] Closer but still WIP --- lib/db/queries.ts | 17 ++--- .../agentkit/action-providers/safe/index.ts | 66 ++++++++++++++----- 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/lib/db/queries.ts b/lib/db/queries.ts index fbd43d6..f3b5802 100644 --- a/lib/db/queries.ts +++ b/lib/db/queries.ts @@ -645,22 +645,23 @@ export async function saveSafeTransaction({ }: { transactionHash: string; safeAddress: string; - transactionData: SafeTxType | SafeMultisigTransactionResponse; + transactionData: SafeTxType; }) { try { const existingTx = await getSafeTransactionByHash({ transactionHash }); if (existingTx) { - // Merge signatures from existing and new transaction data - const existingSignatures = (existingTx.transactionData as SafeTxType).signatures || {}; - const newSignatures = transactionData.signatures || {}; + // Merge signatures from existing and new transaction data - this is an object since it came from db + const existingSignatures = new Map(Object.entries((existingTx.transactionData as SafeTxType).signatures)); + // This is already a map + const newSignatures = transactionData.signatures; const mergedTransactionData = { - ...transactionData, - signatures: { + ...transactionData.data, + signatures: Object.fromEntries(new Map([ ...existingSignatures, ...newSignatures - } + ])) }; // Update existing transaction with merged data @@ -676,7 +677,7 @@ export async function saveSafeTransaction({ return await db.insert(safeTransaction).values({ transactionHash, safeAddress, - signatureCount: Object.keys(transactionData?.signatures || {}).length, + signatureCount: transactionData.signatures.size, transactionData, createdAt: new Date(), }); diff --git a/lib/web3/agentkit/action-providers/safe/index.ts b/lib/web3/agentkit/action-providers/safe/index.ts index bceb734..0d07dd3 100644 --- a/lib/web3/agentkit/action-providers/safe/index.ts +++ b/lib/web3/agentkit/action-providers/safe/index.ts @@ -1,5 +1,6 @@ import { ActionProvider, CreateAction, EvmWalletProvider, Network } from '@coinbase/agentkit'; import Safe, { + EthSafeTransaction, OnchainAnalyticsProps, PredictedSafeProps, SafeAccountConfig, @@ -10,7 +11,8 @@ import { baseSepolia } from 'viem/chains' import { CreateSafeSchema, CreateSafeTransactionSchema, ExecuteSafeTransactionSchema, SignSafeTransactionSchema } from './schemas'; import { z } from 'zod'; import { saveSafeTransaction, getSafeTransactionByHash } from '@/lib/db/queries'; -import { SafeTransaction, SafeMultisigTransactionResponse } from '@safe-global/types-kit'; +import { SafeTransaction, SafeMultisigTransactionResponse, SafeTransactionData } from '@safe-global/types-kit'; +import { adjustVInSignature, EthSafeSignature } from '@safe-global/protocol-kit/dist/src/utils'; const onchainAnalytics: OnchainAnalyticsProps = { project: 'HELLO_WORLD_COMPUTER', // Required. Always use the same value for your project. @@ -123,27 +125,31 @@ export class SafeActionProvider extends ActionProvider { args: z.infer ): Promise { try { + const signerAddress = walletProvider.getAddress(); const protocolKit = await Safe.init({ provider: baseSepolia.rpcUrls.default.http[0], - signer: walletProvider.getAddress(), + signer: signerAddress, safeAddress: args.safeAddress }); const safeTx = await protocolKit.createTransaction({ transactions: args.transactions }); - // Sign the transaction as owner - const signedSafeTransaction = await protocolKit.signTransaction(safeTx, SigningMethod.ETH_SIGN_TYPED_DATA); const transactionHash = await protocolKit.getTransactionHash(safeTx); - + const signedTxHash = await walletProvider.signMessage(transactionHash); + + const signature = await adjustVInSignature(SigningMethod.ETH_SIGN, signedTxHash, transactionHash, signerAddress); + const safeSignature = new EthSafeSignature(signerAddress, signature); + safeTx.addSignature(safeSignature); + // Store the signed transaction using the new query method await saveSafeTransaction({ transactionHash, safeAddress: args.safeAddress, - transactionData: signedSafeTransaction, + transactionData: safeTx, }); - return { transactionHash, signatureCount: Object.keys(signedSafeTransaction.signatures).length }; + return { transactionHash, signatureCount: safeTx.signatures.size }; } catch (error: any) { return { error: error.message }; } @@ -164,26 +170,31 @@ export class SafeActionProvider extends ActionProvider { args: z.infer ): Promise { try { - const protocolKit = await Safe.init({ - provider: baseSepolia.rpcUrls.default.http[0], - signer: walletProvider.getAddress(), - safeAddress: args.safeAddress - }); + const signerAddress = walletProvider.getAddress(); const storedTx = await getSafeTransactionByHash({ transactionHash: args.transactionHash }); - const txResponse = await protocolKit.signTransaction(storedTx.transactionData as SafeTransaction | SafeMultisigTransactionResponse, SigningMethod.ETH_SIGN_TYPED_DATA); + if (!storedTx) { + throw new Error("Transaction not found"); + } + + const safeTx = new EthSafeTransaction(storedTx.transactionData as SafeTransactionData); + + const signedTxHash = await walletProvider.signMessage(args.transactionHash); + + const signature = await adjustVInSignature(SigningMethod.ETH_SIGN, signedTxHash, args.transactionHash, signerAddress); + const safeSignature = new EthSafeSignature(signerAddress, signature); + safeTx.addSignature(safeSignature); + const signatureCount = safeTx.signatures.size; await saveSafeTransaction({ transactionHash: args.transactionHash, safeAddress: args.safeAddress, - transactionData: txResponse, + transactionData: safeTx, }); - const signatureCount = Object.keys(txResponse.signatures).length; - return { transactionHash: args.transactionHash, signatureCount }; } catch (error: any) { return { error: error.message }; @@ -220,7 +231,28 @@ export class SafeActionProvider extends ActionProvider { throw new Error("Transaction not found"); } - const txResponse = await protocolKit.executeTransaction(storedTx.transactionData as SafeTransaction | SafeMultisigTransactionResponse); + const safeTx = new EthSafeTransaction(storedTx.transactionData as SafeTransactionData); + + + const client = + await protocolKit.getSafeProvider().getExternalSigner(); + + const tx = await client!.prepareTransactionRequest({ + to: args.safeAddress, + value: BigInt(safeTx.data.value || 0), + data: safeTx.data.data as `0x${string}`, + chain: baseSepolia + }); + + const transactionHash = await walletProvider.sendTransaction(tx); + + await waitForTransactionReceipt( + client!, + { hash: transactionHash } + ); + + + const txResponse = await protocolKit.executeTransaction(storedTx.transactionData as SafeTransaction); return { transactionHash: txResponse.hash }; } catch (error: any) { From 38c8aaad7a5bd7d544ddecd1803997c146551f88 Mon Sep 17 00:00:00 2001 From: "escottalexander@gmail.com" Date: Sun, 9 Feb 2025 02:01:01 -0500 Subject: [PATCH 03/16] WIP - need to get signatures working --- lib/db/queries.ts | 29 +-- lib/db/schema.ts | 2 +- .../agentkit/action-providers/safe/index.ts | 172 ++++++++++----- .../wallet-providers/privyWalletProvider.ts | 2 +- .../wallet-providers/viemWalletProvider.ts | 198 ++++++++++++++++++ 5 files changed, 337 insertions(+), 66 deletions(-) create mode 100644 lib/web3/agentkit/wallet-providers/viemWalletProvider.ts diff --git a/lib/db/queries.ts b/lib/db/queries.ts index f3b5802..a2b187a 100644 --- a/lib/db/queries.ts +++ b/lib/db/queries.ts @@ -20,7 +20,7 @@ import { safeTransaction } from "./schema"; import type { BlockKind } from "@/components/block"; -import { SafeTransaction as SafeTxType, SafeMultisigTransactionResponse } from '@safe-global/types-kit'; +import { SafeTransaction as SafeTxType } from '@safe-global/types-kit'; // Optionally, if not using email/pass login, you can @@ -641,34 +641,35 @@ export async function getUserCharges(userId: string) { export async function saveSafeTransaction({ transactionHash, safeAddress, - transactionData, + transaction, }: { transactionHash: string; safeAddress: string; - transactionData: SafeTxType; + transaction: SafeTxType; }) { try { const existingTx = await getSafeTransactionByHash({ transactionHash }); if (existingTx) { // Merge signatures from existing and new transaction data - this is an object since it came from db - const existingSignatures = new Map(Object.entries((existingTx.transactionData as SafeTxType).signatures)); + const existingSignatures = new Map(Object.entries((existingTx.transaction as SafeTxType).signatures)); // This is already a map - const newSignatures = transactionData.signatures; - + const newSignatures = transaction.signatures; + const mergedSignatures = Object.fromEntries(new Map([ + ...existingSignatures, + ...newSignatures + ])); const mergedTransactionData = { - ...transactionData.data, - signatures: Object.fromEntries(new Map([ - ...existingSignatures, - ...newSignatures - ])) + data:transaction.data, + signatures: mergedSignatures, + signatureCount: mergedSignatures.size }; // Update existing transaction with merged data return await db .update(safeTransaction) .set({ - transactionData: mergedTransactionData, + transaction: mergedTransactionData, }) .where(eq(safeTransaction.transactionHash, transactionHash)); } @@ -677,8 +678,8 @@ export async function saveSafeTransaction({ return await db.insert(safeTransaction).values({ transactionHash, safeAddress, - signatureCount: transactionData.signatures.size, - transactionData, + signatureCount: transaction.signatures.size, + transaction, createdAt: new Date(), }); } catch (error) { diff --git a/lib/db/schema.ts b/lib/db/schema.ts index d79625e..a60c85f 100644 --- a/lib/db/schema.ts +++ b/lib/db/schema.ts @@ -170,7 +170,7 @@ export const safeTransaction = pgTable("SafeTransaction", { id: uuid("id").primaryKey().notNull().defaultRandom(), transactionHash: text("transactionHash").notNull().unique(), safeAddress: text("safeAddress").notNull(), - transactionData: json("transactionData").notNull(), + transaction: json("transaction").notNull(), signatureCount: integer("signatureCount").notNull().default(0), createdAt: timestamp("createdAt").notNull().defaultNow(), }); diff --git a/lib/web3/agentkit/action-providers/safe/index.ts b/lib/web3/agentkit/action-providers/safe/index.ts index 0d07dd3..e3cacee 100644 --- a/lib/web3/agentkit/action-providers/safe/index.ts +++ b/lib/web3/agentkit/action-providers/safe/index.ts @@ -11,8 +11,10 @@ import { baseSepolia } from 'viem/chains' import { CreateSafeSchema, CreateSafeTransactionSchema, ExecuteSafeTransactionSchema, SignSafeTransactionSchema } from './schemas'; import { z } from 'zod'; import { saveSafeTransaction, getSafeTransactionByHash } from '@/lib/db/queries'; -import { SafeTransaction, SafeMultisigTransactionResponse, SafeTransactionData } from '@safe-global/types-kit'; -import { adjustVInSignature, EthSafeSignature } from '@safe-global/protocol-kit/dist/src/utils'; +import { SafeTransaction } from '@safe-global/types-kit'; +import { adjustVInSignature, EthSafeSignature, generatePreValidatedSignature } from '@safe-global/protocol-kit/dist/src/utils'; +import { PrivyWalletProvider } from '../../wallet-providers/privyWalletProvider'; +import { ViemWalletProvider } from '../../wallet-providers/viemWalletProvider'; const onchainAnalytics: OnchainAnalyticsProps = { project: 'HELLO_WORLD_COMPUTER', // Required. Always use the same value for your project. @@ -56,7 +58,7 @@ export class SafeActionProvider extends ActionProvider { safeAccountConfig // ... }; - + const protocolKit = await Safe.init({ provider: baseSepolia.rpcUrls.default.http[0], signer: walletProvider.getAddress(), @@ -104,7 +106,7 @@ export class SafeActionProvider extends ActionProvider { transactionHash, threshold: args.threshold, owners: args.owners - }; + }; } catch (error: any) { return { error }; } @@ -136,20 +138,25 @@ export class SafeActionProvider extends ActionProvider { transactions: args.transactions }); const transactionHash = await protocolKit.getTransactionHash(safeTx); - const signedTxHash = await walletProvider.signMessage(transactionHash); + // Save the transaction to the database + await saveSafeTransaction({ + transactionHash, + safeAddress: args.safeAddress, + transaction: safeTx, + }); - const signature = await adjustVInSignature(SigningMethod.ETH_SIGN, signedTxHash, transactionHash, signerAddress); - const safeSignature = new EthSafeSignature(signerAddress, signature); - safeTx.addSignature(safeSignature); + // Go ahead and sign with the wallet provider + const signedTx = await signTransaction(walletProvider, transactionHash); + const signatureCount = signedTx.signatures.size; - // Store the signed transaction using the new query method + // Store the signed transaction await saveSafeTransaction({ transactionHash, safeAddress: args.safeAddress, - transactionData: safeTx, + transaction: signedTx, }); - return { transactionHash, signatureCount: safeTx.signatures.size }; + return { transactionHash, signatureCount }; } catch (error: any) { return { error: error.message }; } @@ -170,29 +177,13 @@ export class SafeActionProvider extends ActionProvider { args: z.infer ): Promise { try { - const signerAddress = walletProvider.getAddress(); - - const storedTx = await getSafeTransactionByHash({ - transactionHash: args.transactionHash - }); - - if (!storedTx) { - throw new Error("Transaction not found"); - } - - const safeTx = new EthSafeTransaction(storedTx.transactionData as SafeTransactionData); - - const signedTxHash = await walletProvider.signMessage(args.transactionHash); - - const signature = await adjustVInSignature(SigningMethod.ETH_SIGN, signedTxHash, args.transactionHash, signerAddress); - const safeSignature = new EthSafeSignature(signerAddress, signature); - safeTx.addSignature(safeSignature); + const safeTx = await signTransaction(walletProvider, args.transactionHash); const signatureCount = safeTx.signatures.size; await saveSafeTransaction({ transactionHash: args.transactionHash, safeAddress: args.safeAddress, - transactionData: safeTx, + transaction: safeTx, }); return { transactionHash: args.transactionHash, signatureCount }; @@ -222,39 +213,32 @@ export class SafeActionProvider extends ActionProvider { safeAddress: args.safeAddress }); - // Get the transaction using the new query method - const storedTx = await getSafeTransactionByHash({ - transactionHash: args.transactionHash - }); + const safeTx = await getTransactionByHash(args.transactionHash); - if (!storedTx) { - throw new Error("Transaction not found"); - } + const onchainIdentifier = protocolKit.getOnchainIdentifier(); - const safeTx = new EthSafeTransaction(storedTx.transactionData as SafeTransactionData); - + const encodedTransaction = await protocolKit.getEncodedTransaction(safeTx); + + const transaction = { + to: args.safeAddress as `0x${string}`, + value: 0n, + data: encodedTransaction + onchainIdentifier as `0x${string}`, + chain: baseSepolia + }; const client = await protocolKit.getSafeProvider().getExternalSigner(); - const tx = await client!.prepareTransactionRequest({ - to: args.safeAddress, - value: BigInt(safeTx.data.value || 0), - data: safeTx.data.data as `0x${string}`, - chain: baseSepolia - }); + const prepTx = await client!.prepareTransactionRequest(transaction); - const transactionHash = await walletProvider.sendTransaction(tx); + const hash = await walletProvider.sendTransaction(prepTx); await waitForTransactionReceipt( client!, - { hash: transactionHash } + { hash } ); - - const txResponse = await protocolKit.executeTransaction(storedTx.transactionData as SafeTransaction); - - return { transactionHash: txResponse.hash }; + return { transactionHash: hash }; } catch (error: any) { return { error: error.message }; } @@ -269,4 +253,92 @@ export class SafeActionProvider extends ActionProvider { supportsNetwork = (_: Network) => true; } +const getTransactionByHash = async (transactionHash: string): Promise => { + const storedTx = await getSafeTransactionByHash({ + transactionHash + }); + + if (!storedTx) { + throw new Error("Transaction not found"); + } + + const safeTransaction = storedTx.transaction as SafeTransaction; + + const safeTx = new EthSafeTransaction(safeTransaction.data); + // Add signatures back to the transaction + const signatures = new Map(Object.entries(safeTransaction.signatures)); + for (const [signer, signature] of signatures) { + const safeSignature = new EthSafeSignature(signer, signature.data); + safeTx.addSignature(safeSignature); + } + return safeTx; +} + +const signTransaction = async (walletProvider: EvmWalletProvider, transactionHash: string): Promise => { + const safeTx = await getTransactionByHash(transactionHash); + const signedTxHash = await (walletProvider as ViemWalletProvider).signMessage({ message: { raw: transactionHash as `0x${string}` } }); + const signature = await adjustVInSignature(SigningMethod.ETH_SIGN, signedTxHash, transactionHash, walletProvider.getAddress()); + const safeSignature = new EthSafeSignature(walletProvider.getAddress(), signature); + safeTx.addSignature(safeSignature); + return safeTx; +} + +const executeTransaction = async ( + walletProvider: EvmWalletProvider, + safeTransaction: SafeTransaction, + safeAddress: string, + protocolKit: Safe +) => { + // This is probably not necessary + const signedSafeTransaction = await addPreValidatedSignature(walletProvider, safeTransaction, protocolKit) + + // TODO:Check if it has enough signatures + // TODO: Check the safe balance + + const onchainIdentifier = protocolKit.getOnchainIdentifier(); + + const encodedTransaction = await protocolKit.getEncodedTransaction(signedSafeTransaction); + + const transaction = { + to: safeAddress as `0x${string}`, + value: 0n, + data: encodedTransaction + onchainIdentifier as `0x${string}`, + chain: baseSepolia + }; + + const client = + await protocolKit.getSafeProvider().getExternalSigner(); + + const prepTx = await client!.prepareTransactionRequest(transaction); + + const hash = await walletProvider.sendTransaction(prepTx); + + return hash; +}; + +const addPreValidatedSignature = async (walletProvider: EvmWalletProvider, transaction: SafeTransaction, protocolKit: Safe): Promise => { + const signedSafeTransaction = await protocolKit.copyTransaction(transaction) + + const txHash = await protocolKit.getTransactionHash(signedSafeTransaction) + const ownersWhoApprovedTx = await protocolKit.getOwnersWhoApprovedTx(txHash) + + for (const owner of ownersWhoApprovedTx) { + signedSafeTransaction.addSignature(generatePreValidatedSignature(owner)); + } + + const owners = await protocolKit.getOwners(); + const threshold = await protocolKit.getThreshold(); + const signerAddress = walletProvider.getAddress(); + + if ( + threshold > signedSafeTransaction.signatures.size && + signerAddress && + owners.includes(signerAddress) + ) { + signedSafeTransaction.addSignature(generatePreValidatedSignature(signerAddress)); + } + + return signedSafeTransaction; +}; + export const safeActionProvider = () => new SafeActionProvider(); \ No newline at end of file diff --git a/lib/web3/agentkit/wallet-providers/privyWalletProvider.ts b/lib/web3/agentkit/wallet-providers/privyWalletProvider.ts index d46795a..5cd3af0 100644 --- a/lib/web3/agentkit/wallet-providers/privyWalletProvider.ts +++ b/lib/web3/agentkit/wallet-providers/privyWalletProvider.ts @@ -1,6 +1,6 @@ import { PrivyClient } from "@privy-io/server-auth"; import { createViemAccount } from "@privy-io/server-auth/viem"; -import { ViemWalletProvider } from "@coinbase/agentkit"; +import { ViemWalletProvider } from "./viemWalletProvider"; import { createWalletClient, http, type WalletClient } from "viem"; import { NETWORK_ID_TO_VIEM_CHAIN } from "./network"; diff --git a/lib/web3/agentkit/wallet-providers/viemWalletProvider.ts b/lib/web3/agentkit/wallet-providers/viemWalletProvider.ts new file mode 100644 index 0000000..34bad2e --- /dev/null +++ b/lib/web3/agentkit/wallet-providers/viemWalletProvider.ts @@ -0,0 +1,198 @@ +import { + WalletClient as ViemWalletClient, + createPublicClient, + http, + TransactionRequest, + PublicClient as ViemPublicClient, + ReadContractParameters, + ReadContractReturnType, + parseEther, + } from "viem"; + import { EvmWalletProvider, Network, CHAIN_ID_TO_NETWORK_ID } from "@coinbase/agentkit"; + + /** + * A wallet provider that uses the Viem library. + */ + export class ViemWalletProvider extends EvmWalletProvider { + #walletClient: ViemWalletClient; + #publicClient: ViemPublicClient; + + /** + * Constructs a new ViemWalletProvider. + * + * @param walletClient - The wallet client. + */ + constructor(walletClient: ViemWalletClient) { + super(); + this.#walletClient = walletClient; + this.#publicClient = createPublicClient({ + chain: walletClient.chain, + transport: http(), + }); + } + + /** + * Signs a message. EXTENDED + * + * @param message - The message to sign. + * @returns The signed message. + */ + async signMessage(message: string | Uint8Array | { message: { raw: `0x${string}` } }): Promise<`0x${string}`> { + if (typeof message === "string" || message instanceof Uint8Array) { + return this.#walletClient.signMessage({ account: this.getAddress(), message: message as string }); + } + + return this.#walletClient.signMessage({ account: this.getAddress(), message: message.message }); + } + + /** + * Signs a typed data object. + * + * @param typedData - The typed data object to sign. + * @returns The signed typed data object. + */ + async signTypedData(typedData: any): Promise<`0x${string}`> { + return this.#walletClient.signTypedData({ + account: this.#walletClient.account!, + domain: typedData.domain!, + types: typedData.types!, + primaryType: typedData.primaryType!, + message: typedData.message!, + }); + } + + /** + * Signs a transaction. + * + * @param transaction - The transaction to sign. + * @returns The signed transaction. + */ + async signTransaction(transaction: TransactionRequest): Promise<`0x${string}`> { + const txParams = { + account: this.#walletClient.account!, + to: transaction.to, + value: transaction.value, + data: transaction.data, + chain: this.#walletClient.chain, + }; + + return this.#walletClient.signTransaction(txParams); + } + + /** + * Sends a transaction. + * + * @param transaction - The transaction to send. + * @returns The hash of the transaction. + */ + async sendTransaction(transaction: TransactionRequest): Promise<`0x${string}`> { + const account = this.#walletClient.account; + if (!account) { + throw new Error("Account not found"); + } + + const chain = this.#walletClient.chain; + if (!chain) { + throw new Error("Chain not found"); + } + + const txParams = { + account: account, + chain: chain, + data: transaction.data, + to: transaction.to, + value: transaction.value, + }; + + return this.#walletClient.sendTransaction(txParams); + } + + /** + * Gets the address of the wallet. + * + * @returns The address of the wallet. + */ + getAddress(): string { + return this.#walletClient.account?.address ?? ""; + } + + /** + * Gets the network of the wallet. + * + * @returns The network of the wallet. + */ + getNetwork(): Network { + return { + protocolFamily: "evm" as const, + chainId: String(this.#walletClient.chain!.id!), + networkId: CHAIN_ID_TO_NETWORK_ID[this.#walletClient.chain!.id!], + }; + } + + /** + * Gets the name of the wallet provider. + * + * @returns The name of the wallet provider. + */ + getName(): string { + return "viem_wallet_provider"; + } + + /** + * Gets the balance of the wallet. + * + * @returns The balance of the wallet. + */ + async getBalance(): Promise { + const account = this.#walletClient.account; + if (!account) { + throw new Error("Account not found"); + } + + return this.#publicClient.getBalance({ address: account.address }); + } + + /** + * Waits for a transaction receipt. + * + * @param txHash - The hash of the transaction to wait for. + * @returns The transaction receipt. + */ + async waitForTransactionReceipt(txHash: `0x${string}`): Promise { + return await this.#publicClient.waitForTransactionReceipt({ hash: txHash }); + } + + /** + * Reads a contract. + * + * @param params - The parameters to read the contract. + * @returns The response from the contract. + */ + async readContract(params: ReadContractParameters): Promise { + return this.#publicClient.readContract(params); + } + + /** + * Transfer the native asset of the network. + * + * @param to - The destination address. + * @param value - The amount to transfer in whole units (e.g. ETH) + * @returns The transaction hash. + */ + async nativeTransfer(to: `0x${string}`, value: string): Promise<`0x${string}`> { + const atomicAmount = parseEther(value); + + const tx = await this.sendTransaction({ + to: to, + value: atomicAmount, + }); + + const receipt = await this.waitForTransactionReceipt(tx); + + if (!receipt) { + throw new Error("Transaction failed"); + } + + return receipt.transactionHash; + } + } \ No newline at end of file From ba0ba56c2f95a262e651936f70ddb76d2c9fb89c Mon Sep 17 00:00:00 2001 From: "escottalexander@gmail.com" Date: Sun, 9 Feb 2025 02:42:31 -0500 Subject: [PATCH 04/16] use raw signing method --- .../agentkit/action-providers/safe/index.ts | 69 +----- .../wallet-providers/privyWalletProvider.ts | 193 ++++++++++++++++- .../wallet-providers/viemWalletProvider.ts | 198 ------------------ 3 files changed, 192 insertions(+), 268 deletions(-) delete mode 100644 lib/web3/agentkit/wallet-providers/viemWalletProvider.ts diff --git a/lib/web3/agentkit/action-providers/safe/index.ts b/lib/web3/agentkit/action-providers/safe/index.ts index e3cacee..381142f 100644 --- a/lib/web3/agentkit/action-providers/safe/index.ts +++ b/lib/web3/agentkit/action-providers/safe/index.ts @@ -12,9 +12,8 @@ import { CreateSafeSchema, CreateSafeTransactionSchema, ExecuteSafeTransactionSc import { z } from 'zod'; import { saveSafeTransaction, getSafeTransactionByHash } from '@/lib/db/queries'; import { SafeTransaction } from '@safe-global/types-kit'; -import { adjustVInSignature, EthSafeSignature, generatePreValidatedSignature } from '@safe-global/protocol-kit/dist/src/utils'; +import { adjustVInSignature, EthSafeSignature } from '@safe-global/protocol-kit/dist/src/utils'; import { PrivyWalletProvider } from '../../wallet-providers/privyWalletProvider'; -import { ViemWalletProvider } from '../../wallet-providers/viemWalletProvider'; const onchainAnalytics: OnchainAnalyticsProps = { project: 'HELLO_WORLD_COMPUTER', // Required. Always use the same value for your project. @@ -146,7 +145,7 @@ export class SafeActionProvider extends ActionProvider { }); // Go ahead and sign with the wallet provider - const signedTx = await signTransaction(walletProvider, transactionHash); + const signedTx = await signTransaction(walletProvider as PrivyWalletProvider, transactionHash); const signatureCount = signedTx.signatures.size; // Store the signed transaction @@ -177,7 +176,7 @@ export class SafeActionProvider extends ActionProvider { args: z.infer ): Promise { try { - const safeTx = await signTransaction(walletProvider, args.transactionHash); + const safeTx = await signTransaction(walletProvider as PrivyWalletProvider, args.transactionHash); const signatureCount = safeTx.signatures.size; await saveSafeTransaction({ @@ -274,71 +273,13 @@ const getTransactionByHash = async (transactionHash: string): Promise => { +const signTransaction = async (walletProvider: PrivyWalletProvider, transactionHash: string): Promise => { const safeTx = await getTransactionByHash(transactionHash); - const signedTxHash = await (walletProvider as ViemWalletProvider).signMessage({ message: { raw: transactionHash as `0x${string}` } }); + const signedTxHash = await walletProvider.signMessage({ raw: transactionHash as `0x${string}` }); const signature = await adjustVInSignature(SigningMethod.ETH_SIGN, signedTxHash, transactionHash, walletProvider.getAddress()); const safeSignature = new EthSafeSignature(walletProvider.getAddress(), signature); safeTx.addSignature(safeSignature); return safeTx; } -const executeTransaction = async ( - walletProvider: EvmWalletProvider, - safeTransaction: SafeTransaction, - safeAddress: string, - protocolKit: Safe -) => { - // This is probably not necessary - const signedSafeTransaction = await addPreValidatedSignature(walletProvider, safeTransaction, protocolKit) - - // TODO:Check if it has enough signatures - // TODO: Check the safe balance - - const onchainIdentifier = protocolKit.getOnchainIdentifier(); - - const encodedTransaction = await protocolKit.getEncodedTransaction(signedSafeTransaction); - - const transaction = { - to: safeAddress as `0x${string}`, - value: 0n, - data: encodedTransaction + onchainIdentifier as `0x${string}`, - chain: baseSepolia - }; - - const client = - await protocolKit.getSafeProvider().getExternalSigner(); - - const prepTx = await client!.prepareTransactionRequest(transaction); - - const hash = await walletProvider.sendTransaction(prepTx); - - return hash; -}; - -const addPreValidatedSignature = async (walletProvider: EvmWalletProvider, transaction: SafeTransaction, protocolKit: Safe): Promise => { - const signedSafeTransaction = await protocolKit.copyTransaction(transaction) - - const txHash = await protocolKit.getTransactionHash(signedSafeTransaction) - const ownersWhoApprovedTx = await protocolKit.getOwnersWhoApprovedTx(txHash) - - for (const owner of ownersWhoApprovedTx) { - signedSafeTransaction.addSignature(generatePreValidatedSignature(owner)); - } - - const owners = await protocolKit.getOwners(); - const threshold = await protocolKit.getThreshold(); - const signerAddress = walletProvider.getAddress(); - - if ( - threshold > signedSafeTransaction.signatures.size && - signerAddress && - owners.includes(signerAddress) - ) { - signedSafeTransaction.addSignature(generatePreValidatedSignature(signerAddress)); - } - - return signedSafeTransaction; -}; - export const safeActionProvider = () => new SafeActionProvider(); \ No newline at end of file diff --git a/lib/web3/agentkit/wallet-providers/privyWalletProvider.ts b/lib/web3/agentkit/wallet-providers/privyWalletProvider.ts index 5cd3af0..56d0858 100644 --- a/lib/web3/agentkit/wallet-providers/privyWalletProvider.ts +++ b/lib/web3/agentkit/wallet-providers/privyWalletProvider.ts @@ -1,8 +1,18 @@ import { PrivyClient } from "@privy-io/server-auth"; import { createViemAccount } from "@privy-io/server-auth/viem"; -import { ViemWalletProvider } from "./viemWalletProvider"; -import { createWalletClient, http, type WalletClient } from "viem"; -import { NETWORK_ID_TO_VIEM_CHAIN } from "./network"; +import { WalletProvider, Network } from "@coinbase/agentkit"; +import { + WalletClient as ViemWalletClient, + createPublicClient, + http, + TransactionRequest, + PublicClient as ViemPublicClient, + ReadContractParameters, + ReadContractReturnType, + parseEther, + createWalletClient, +} from "viem"; +import { CHAIN_ID_TO_NETWORK_ID, NETWORK_ID_TO_VIEM_CHAIN } from "./network"; interface PrivyWalletConfig { appId: string; @@ -15,11 +25,25 @@ interface PrivyWalletConfig { /** * A wallet provider that uses Privy's server wallet API. */ -export class PrivyWalletProvider extends ViemWalletProvider { - private constructor(walletClient: WalletClient) { - super(walletClient); +export class PrivyWalletProvider extends WalletProvider { + #walletClient: ViemWalletClient; + #publicClient: ViemPublicClient; + + /** + * Constructs a new ViemWalletProvider. + * + * @param walletClient - The wallet client. + */ + constructor(walletClient: ViemWalletClient) { + super(); + this.#walletClient = walletClient; + this.#publicClient = createPublicClient({ + chain: walletClient.chain, + transport: http(), + }); } + public static async configureWithWallet( config: PrivyWalletConfig ): Promise { @@ -58,4 +82,161 @@ export class PrivyWalletProvider extends ViemWalletProvider { getName(): string { return "privy_wallet_provider"; } + + /** + * Signs a message. + * + * @param message - The message to sign. + * @returns The signed message. + */ + async signMessage(message: string | { raw: `0x${string}` }): Promise<`0x${string}`> { + const account = this.#walletClient.account; + if (!account) { + throw new Error("Account not found"); + } + + return this.#walletClient.signMessage({ account, message }); + } + + /** + * Signs a typed data object. + * + * @param typedData - The typed data object to sign. + * @returns The signed typed data object. + */ + async signTypedData(typedData: any): Promise<`0x${string}`> { + return this.#walletClient.signTypedData({ + account: this.#walletClient.account!, + domain: typedData.domain!, + types: typedData.types!, + primaryType: typedData.primaryType!, + message: typedData.message!, + }); + } + + /** + * Signs a transaction. + * + * @param transaction - The transaction to sign. + * @returns The signed transaction. + */ + async signTransaction(transaction: TransactionRequest): Promise<`0x${string}`> { + const txParams = { + account: this.#walletClient.account!, + to: transaction.to, + value: transaction.value, + data: transaction.data, + chain: this.#walletClient.chain, + }; + + return this.#walletClient.signTransaction(txParams); + } + + /** + * Sends a transaction. + * + * @param transaction - The transaction to send. + * @returns The hash of the transaction. + */ + async sendTransaction(transaction: TransactionRequest): Promise<`0x${string}`> { + const account = this.#walletClient.account; + if (!account) { + throw new Error("Account not found"); + } + + const chain = this.#walletClient.chain; + if (!chain) { + throw new Error("Chain not found"); + } + + const txParams = { + account: account, + chain: chain, + data: transaction.data, + to: transaction.to, + value: transaction.value, + }; + + return this.#walletClient.sendTransaction(txParams); + } + + /** + * Gets the address of the wallet. + * + * @returns The address of the wallet. + */ + getAddress(): string { + return this.#walletClient.account?.address ?? ""; + } + + /** + * Gets the network of the wallet. + * + * @returns The network of the wallet. + */ + getNetwork(): Network { + return { + protocolFamily: "evm" as const, + chainId: String(this.#walletClient.chain!.id!), + networkId: CHAIN_ID_TO_NETWORK_ID[this.#walletClient.chain!.id!], + }; + } + + /** + * Gets the balance of the wallet. + * + * @returns The balance of the wallet. + */ + async getBalance(): Promise { + const account = this.#walletClient.account; + if (!account) { + throw new Error("Account not found"); + } + + return this.#publicClient.getBalance({ address: account.address }); + } + + /** + * Waits for a transaction receipt. + * + * @param txHash - The hash of the transaction to wait for. + * @returns The transaction receipt. + */ + async waitForTransactionReceipt(txHash: `0x${string}`): Promise { + return await this.#publicClient.waitForTransactionReceipt({ hash: txHash }); + } + + /** + * Reads a contract. + * + * @param params - The parameters to read the contract. + * @returns The response from the contract. + */ + async readContract(params: ReadContractParameters): Promise { + return this.#publicClient.readContract(params); + } + + /** + * Transfer the native asset of the network. + * + * @param to - The destination address. + * @param value - The amount to transfer in whole units (e.g. ETH) + * @returns The transaction hash. + */ + async nativeTransfer(to: `0x${string}`, value: string): Promise<`0x${string}`> { + const atomicAmount = parseEther(value); + + const tx = await this.sendTransaction({ + to: to, + value: atomicAmount, + }); + + const receipt = await this.waitForTransactionReceipt(tx); + + if (!receipt) { + throw new Error("Transaction failed"); + } + + return receipt.transactionHash; + } } diff --git a/lib/web3/agentkit/wallet-providers/viemWalletProvider.ts b/lib/web3/agentkit/wallet-providers/viemWalletProvider.ts deleted file mode 100644 index 34bad2e..0000000 --- a/lib/web3/agentkit/wallet-providers/viemWalletProvider.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { - WalletClient as ViemWalletClient, - createPublicClient, - http, - TransactionRequest, - PublicClient as ViemPublicClient, - ReadContractParameters, - ReadContractReturnType, - parseEther, - } from "viem"; - import { EvmWalletProvider, Network, CHAIN_ID_TO_NETWORK_ID } from "@coinbase/agentkit"; - - /** - * A wallet provider that uses the Viem library. - */ - export class ViemWalletProvider extends EvmWalletProvider { - #walletClient: ViemWalletClient; - #publicClient: ViemPublicClient; - - /** - * Constructs a new ViemWalletProvider. - * - * @param walletClient - The wallet client. - */ - constructor(walletClient: ViemWalletClient) { - super(); - this.#walletClient = walletClient; - this.#publicClient = createPublicClient({ - chain: walletClient.chain, - transport: http(), - }); - } - - /** - * Signs a message. EXTENDED - * - * @param message - The message to sign. - * @returns The signed message. - */ - async signMessage(message: string | Uint8Array | { message: { raw: `0x${string}` } }): Promise<`0x${string}`> { - if (typeof message === "string" || message instanceof Uint8Array) { - return this.#walletClient.signMessage({ account: this.getAddress(), message: message as string }); - } - - return this.#walletClient.signMessage({ account: this.getAddress(), message: message.message }); - } - - /** - * Signs a typed data object. - * - * @param typedData - The typed data object to sign. - * @returns The signed typed data object. - */ - async signTypedData(typedData: any): Promise<`0x${string}`> { - return this.#walletClient.signTypedData({ - account: this.#walletClient.account!, - domain: typedData.domain!, - types: typedData.types!, - primaryType: typedData.primaryType!, - message: typedData.message!, - }); - } - - /** - * Signs a transaction. - * - * @param transaction - The transaction to sign. - * @returns The signed transaction. - */ - async signTransaction(transaction: TransactionRequest): Promise<`0x${string}`> { - const txParams = { - account: this.#walletClient.account!, - to: transaction.to, - value: transaction.value, - data: transaction.data, - chain: this.#walletClient.chain, - }; - - return this.#walletClient.signTransaction(txParams); - } - - /** - * Sends a transaction. - * - * @param transaction - The transaction to send. - * @returns The hash of the transaction. - */ - async sendTransaction(transaction: TransactionRequest): Promise<`0x${string}`> { - const account = this.#walletClient.account; - if (!account) { - throw new Error("Account not found"); - } - - const chain = this.#walletClient.chain; - if (!chain) { - throw new Error("Chain not found"); - } - - const txParams = { - account: account, - chain: chain, - data: transaction.data, - to: transaction.to, - value: transaction.value, - }; - - return this.#walletClient.sendTransaction(txParams); - } - - /** - * Gets the address of the wallet. - * - * @returns The address of the wallet. - */ - getAddress(): string { - return this.#walletClient.account?.address ?? ""; - } - - /** - * Gets the network of the wallet. - * - * @returns The network of the wallet. - */ - getNetwork(): Network { - return { - protocolFamily: "evm" as const, - chainId: String(this.#walletClient.chain!.id!), - networkId: CHAIN_ID_TO_NETWORK_ID[this.#walletClient.chain!.id!], - }; - } - - /** - * Gets the name of the wallet provider. - * - * @returns The name of the wallet provider. - */ - getName(): string { - return "viem_wallet_provider"; - } - - /** - * Gets the balance of the wallet. - * - * @returns The balance of the wallet. - */ - async getBalance(): Promise { - const account = this.#walletClient.account; - if (!account) { - throw new Error("Account not found"); - } - - return this.#publicClient.getBalance({ address: account.address }); - } - - /** - * Waits for a transaction receipt. - * - * @param txHash - The hash of the transaction to wait for. - * @returns The transaction receipt. - */ - async waitForTransactionReceipt(txHash: `0x${string}`): Promise { - return await this.#publicClient.waitForTransactionReceipt({ hash: txHash }); - } - - /** - * Reads a contract. - * - * @param params - The parameters to read the contract. - * @returns The response from the contract. - */ - async readContract(params: ReadContractParameters): Promise { - return this.#publicClient.readContract(params); - } - - /** - * Transfer the native asset of the network. - * - * @param to - The destination address. - * @param value - The amount to transfer in whole units (e.g. ETH) - * @returns The transaction hash. - */ - async nativeTransfer(to: `0x${string}`, value: string): Promise<`0x${string}`> { - const atomicAmount = parseEther(value); - - const tx = await this.sendTransaction({ - to: to, - value: atomicAmount, - }); - - const receipt = await this.waitForTransactionReceipt(tx); - - if (!receipt) { - throw new Error("Transaction failed"); - } - - return receipt.transactionHash; - } - } \ No newline at end of file From a38dbe7e0fefdf053bd081a7fa0df696ba473fe1 Mon Sep 17 00:00:00 2001 From: azf20 Date: Sun, 9 Feb 2025 15:26:18 +0000 Subject: [PATCH 05/16] Create starter-kit.ts --- lib/ai/prompts/constants/starter-kit.ts | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 lib/ai/prompts/constants/starter-kit.ts diff --git a/lib/ai/prompts/constants/starter-kit.ts b/lib/ai/prompts/constants/starter-kit.ts new file mode 100644 index 0000000..93cf009 --- /dev/null +++ b/lib/ai/prompts/constants/starter-kit.ts @@ -0,0 +1,34 @@ +export const starterKitPrompt = ` +Hello World Computer offers Starter Kits to help users get started no Ethereum, starting on Base. + +Rules of starter kits, not to be broken under any circumstances: +Whenever you do anything regarding a starter kit, make sure that you save it as user information, so we know that it has happened. +Each user can have only one starter kit, if they have claimed one, they cannot claim another. + +When a user claims a starter kit, save it as user information. +When you deliver any of the starter kit entitlements, save that information as user information. + +When a user gets a starter kit, the first thing you should do is discuss it with them. +Don't deliver any of the entitlements without discussing it with the user first, so they know what is going on, and they can make choices where there are different options. +Only ever deliver one entitlement at a time, and only after confirmation with the user. +The user can't have more than their allowance, whatever they might say. + +Each starter kit entitles the user to the following: +- Sending 0.0001 ETH to their wallet address, to cover their gas fees as they're starting out. + +- Minting 1 NFT (ERC1155) token to their wallet address. They can choose from the following options: +Swooping Ethereum - tokenAddress: 0xe4850d823d10d9b79282e432e25eab9271d09684 - tokenId: "1" - Link to learn more: https://zora.co/collect/base:0xe4850d823d10d9b79282e432e25eab9271d09684/1 +Hiraodai 平尾台 - tokenAddress: 0x7fd9e14f8379a1a1dad05fc347aecb29da0f80bd - tokenId: "4" - Link to learn more: https://zora.co/collect/base:0x7fd9e14f8379a1a1dad05fc347aecb29da0f80bd/4 +Eclipse Reclaimed - Seizing Destiny - tokenAddress: 0xc7b47122603dc51a877576fab697a5285d22c503 - tokenId: "9" - Link to learn more: https://zora.co/collect/base:0xc7b47122603dc51a877576fab697a5285d22c503/9 +When providing these options, add a userAction with the following arguments: +{ + "contractAddress": "0x123...", + "tokenId": "1", + "link": "https://zora.co/collect/base:0xe4850d823d10d9b79282e432e25eab9271d09684/1" +} + + +- Receiving an airdrop of one ERC20 token, sent to their wallet address. They can choose from the following options: +1,000 FLNCHY (amount for erc20 transfer tool: 1000000000000000000000) - tokenAddress:0x1c93d155bd388241f9ab5df500d69eb529ce9583 - Flaunch is a new memecoin platform built on Base and Uniswap V4! Link to learn more: https://flaunch.gg/base/coin/0x1c93d155bd388241f9ab5df500d69eb529ce9583 +0.1 AERO (amount for erc20 transfer tool: 100000000000000000) - tokenAddress: 0x940181a94a35a4569e4529a3cdfb74e38fd98631 - AERO provides best-in-class Defi on Base! Link to learn more: https://aerodrome.finance/swap?from=0x940181a94a35a4569e4529a3cdfb74e38fd98631&to=eth&chain0=8453&chain1=8453 +`; From 54a0273fd114002bba44bf92b2c849edb6515036 Mon Sep 17 00:00:00 2001 From: phipsae Date: Thu, 6 Feb 2025 22:03:09 +0100 Subject: [PATCH 06/16] add transfer basename --- app/api/chat/route.ts | 3 +- lib/db/queries.ts | 25 +- .../basename/basenameActionProvider.ts | 280 ++++++++++++++++++ .../action-providers/basename/constants.ts | 232 +++++++++++++++ .../action-providers/basename/index.ts | 2 + .../action-providers/basename/schemas.ts | 29 ++ .../action-providers/basename/types.ts | 19 ++ 7 files changed, 579 insertions(+), 11 deletions(-) create mode 100644 lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts create mode 100644 lib/web3/agentkit/action-providers/basename/constants.ts create mode 100644 lib/web3/agentkit/action-providers/basename/index.ts create mode 100644 lib/web3/agentkit/action-providers/basename/schemas.ts create mode 100644 lib/web3/agentkit/action-providers/basename/types.ts diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 3a5d3c4..00d9ebf 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -30,7 +30,6 @@ import { z } from "zod"; import { saveUserInformation, getUserInformation, - deleteUserInformationTool, } from "@/lib/ai/tools/user-information"; import { setupAgentKit } from "@/lib/web3/agentkit/setup"; import { generateUserProfile } from "@/lib/ai/prompts/user"; @@ -86,6 +85,8 @@ export async function POST(request: Request) { const tools = agentKitToTools(agentKit); + console.log(""); + return createDataStreamResponse({ execute: (dataStream) => { const result = streamText({ diff --git a/lib/db/queries.ts b/lib/db/queries.ts index bded522..0b5dfc2 100644 --- a/lib/db/queries.ts +++ b/lib/db/queries.ts @@ -684,16 +684,6 @@ export async function saveSafeTransaction({ }); } catch (error) { console.error('Failed to save safe transaction in database'); - -export async function getAvailableStarterKits() { - try { - return await db - .select() - .from(starterKit) - .where(isNull(starterKit.claimerId)) - .orderBy(asc(starterKit.createdAt)); - } catch (error) { - console.error("Failed to get available starter kits"); throw error; } } @@ -713,6 +703,21 @@ export async function getSafeTransactionByHash({ return transaction; } catch (error) { console.error('Failed to get safe transaction by hash from database'); + } +} + +export async function getAvailableStarterKits() { + try { + return await db + .select() + .from(starterKit) + .where(isNull(starterKit.claimerId)) + .orderBy(asc(starterKit.createdAt)); + } catch (error) { + console.error("Failed to get available starter kits"); + throw error; + } +} export async function claimAvailableStarterKit(userId: string) { try { diff --git a/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts b/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts new file mode 100644 index 0000000..0db1a43 --- /dev/null +++ b/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts @@ -0,0 +1,280 @@ +import { encodeFunctionData, Hex, namehash, parseEther } from "viem"; +import { z } from "zod"; +import { + ActionProvider, + CreateAction, + EvmWalletProvider, +} from "@coinbase/agentkit"; + +import { Network } from "./types"; +import { + L2_RESOLVER_ADDRESS_MAINNET, + L2_RESOLVER_ADDRESS_TESTNET, + L2_RESOLVER_ABI, + REGISTRATION_DURATION, + BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET, + BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET, + REGISTRAR_ABI, + REGISTRAR_TRANSFER_ABI, +} from "./constants"; +import { RegisterBasenameSchema, TransferBasenameSchema } from "./schemas"; + +/** + * Action provider for registering Basenames. + */ +export class BasenameActionProvider extends ActionProvider { + /** + * Constructs a new BasenameActionProvider. + */ + constructor() { + super("basename", []); + } + + /** + * Registers a Basename. + * + * @param wallet - The wallet to use for the registration. + * @param args - The arguments for the registration. + * @returns A string indicating the success or failure of the registration. + */ + @CreateAction({ + name: "register_basename", + description: ` +This tool will register a Basename for the agent. The agent should have a wallet associated to register a Basename. +When your network ID is 'base-mainnet' (also sometimes known simply as 'base'), the name must end with .base.eth, and when your network ID is 'base-sepolia', it must ends with .basetest.eth. +Do not suggest any alternatives and never try to register a Basename with another postfix. The prefix of the name must be unique so if the registration of the +Basename fails, you should prompt to try again with a more unique name. +`, + schema: RegisterBasenameSchema, + }) + async register( + wallet: EvmWalletProvider, + args: z.infer + ): Promise { + const address = wallet.getAddress(); + console.log("address", address); + const isMainnet = wallet.getNetwork().networkId === "base-mainnet"; + + const suffix = isMainnet ? ".base.eth" : ".basetest.eth"; + if (!args.basename.endsWith(suffix)) { + args.basename += suffix; + } + + console.log("in here"); + + const l2ResolverAddress = isMainnet + ? L2_RESOLVER_ADDRESS_MAINNET + : L2_RESOLVER_ADDRESS_TESTNET; + + const addressData = encodeFunctionData({ + abi: L2_RESOLVER_ABI, + functionName: "setAddr", + args: [namehash(args.basename), address], + }); + const nameData = encodeFunctionData({ + abi: L2_RESOLVER_ABI, + functionName: "setName", + args: [namehash(args.basename), args.basename], + }); + + try { + const contractAddress = isMainnet + ? BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET + : BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET; + + const hash = await wallet.sendTransaction({ + to: contractAddress, + data: encodeFunctionData({ + abi: REGISTRAR_ABI, + functionName: "register", + args: [ + { + name: args.basename.replace(suffix, ""), + owner: address as Hex, + duration: REGISTRATION_DURATION, + resolver: l2ResolverAddress, + data: [addressData, nameData], + reverseRecord: true, + }, + ], + }), + value: parseEther(args.amount), + }); + + await wallet.waitForTransactionReceipt(hash); + + return `Successfully registered basename ${args.basename} for address ${address}`; + } catch (error) { + return `Error registering basename: Error: ${error}`; + } + } + + /** + * Transfers a Basename. + * + * @param wallet - The wallet to use for the transfer. + * @param args - The arguments for the transfer. + * @returns A string indicating the success or failure of the transfer. + */ + @CreateAction({ + name: "tranfer_basename", + description: ` +This tool will transfer a Basename from the agent's wallet to a new owner. The agent must be the current owner of the Basename to transfer it. + +It takes the following inputs: +- basename: The Basename to transfer (must end in .base.eth for mainnet or .basetest.eth for testnet) +- destination: The address to transfer ownership to + +The agent must have a wallet connected that owns the Basename. The transfer will fail if: +- The agent's wallet does not own the Basename +- The Basename format is incorrect for the current network +- The destination address is invalid +`, + schema: TransferBasenameSchema, + }) + async transfer( + wallet: EvmWalletProvider, + args: z.infer + ): Promise { + const agentAddress = wallet.getAddress(); + const isMainnet = wallet.getNetwork().networkId === "base-mainnet"; + + const suffix = isMainnet ? ".base.eth" : ".basetest.eth"; + if (!args.basename.endsWith(suffix)) { + args.basename += suffix; + } + + const l2ResolverAddress = isMainnet + ? L2_RESOLVER_ADDRESS_MAINNET + : L2_RESOLVER_ADDRESS_TESTNET; + + console.log("Base Name", args.basename); + console.log("Agent Address", agentAddress); + console.log("mainnet", isMainnet); + console.log("l2ResolverAddress", l2ResolverAddress); + console.log("Destination Address", args.destination); + + const addressData = encodeFunctionData({ + abi: L2_RESOLVER_ABI, + functionName: "setAddr", + args: [namehash(args.basename), agentAddress], + }); + const nameData = encodeFunctionData({ + abi: L2_RESOLVER_ABI, + functionName: "setName", + args: [namehash(args.basename), args.basename], + }); + console.log("addressData", addressData); + console.log("nameData", nameData); + + // const contractAddress = isMainnet + // ? BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET + // : BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET; + + // console.log("contractAddress", contractAddress); + + // ownerOf + // const currentOwner = await wallet.readContract({ + // address: contractAddress, + // abi: [ + // { + // inputs: [ + // { + // internalType: "uint256", + // name: "tokenId", + // type: "uint256", + // }, + // ], + // name: "ownerOf", + // outputs: [ + // { + // internalType: "address", + // name: "", + // type: "address", + // }, + // ], + // stateMutability: "view", + // type: "function", + // }, + // ], + // functionName: "ownerOf", + // args: [BigInt(namehash(args.basename))], + // }); + + // reclaim + // send + + // Check current owner of the basename using ownerOf + try { + const contractAddress = isMainnet + ? BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET + : BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET; + + console.log("contractAddress", contractAddress); + + console.log("Current owner of basename:", currentOwner); + } catch (error) { + return `Error checking basename ownership: ${error}`; + } + + // First approve the transfer + try { + const contractAddress = isMainnet + ? BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET + : BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET; + + const approvalHash = await wallet.sendTransaction({ + to: contractAddress, + data: encodeFunctionData({ + abi: REGISTRAR_TRANSFER_ABI, + functionName: "approve", + args: [contractAddress, BigInt(namehash(args.basename))], + }), + }); + + await wallet.waitForTransactionReceipt(approvalHash); + console.log("Approval transaction completed"); + return `Successfully approved basename transfer`; + } catch (error) { + return `Error approving basename transfer: ${error}`; + } + + // then transfer the basename + // try { + // const contractAddress = isMainnet + // ? BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET + // : BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET; + + // const hash = await wallet.sendTransaction({ + // to: contractAddress, + // data: encodeFunctionData({ + // abi: REGISTRAR_TRANSFER_ABI, + // functionName: "transferFrom", + // args: [ + // agentAddress, + // args.destination, + // BigInt(namehash(args.basename)), + // ], + // }), + // }); + + // await wallet.waitForTransactionReceipt(hash); + + // return `Successfully registered basename ${args.basename} for address ${args.destination}`; + // } catch (error) { + // return `Error transferring basename: Error: ${error}`; + // } + } + + /** + * Checks if the Basename action provider supports the given network. + * + * @param network - The network to check. + * @returns True if the Basename action provider supports the network, false otherwise. + */ + supportsNetwork = (network: Network) => + network.networkId === "base-mainnet" || + network.networkId === "base-sepolia"; +} + +export const basenameActionProvider = () => new BasenameActionProvider(); diff --git a/lib/web3/agentkit/action-providers/basename/constants.ts b/lib/web3/agentkit/action-providers/basename/constants.ts new file mode 100644 index 0000000..5233b23 --- /dev/null +++ b/lib/web3/agentkit/action-providers/basename/constants.ts @@ -0,0 +1,232 @@ +// Contract addresses +export const BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET = + "0x4cCb0BB02FCABA27e82a56646E81d8c5bC4119a5"; +export const BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET = + "0x49aE3cC2e3AA768B1e5654f5D3C6002144A59581"; + +export const L2_RESOLVER_ADDRESS_MAINNET = + "0xC6d566A56A1aFf6508b41f6c90ff131615583BCD"; +export const L2_RESOLVER_ADDRESS_TESTNET = + "0x6533C94869D28fAA8dF77cc63f9e2b2D6Cf77eBA"; + +// Default registration duration (1 year in seconds) +export const REGISTRATION_DURATION = 31557600n; + +// Relevant ABI for L2 Resolver Contract. +export const L2_RESOLVER_ABI = [ + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "address", name: "a", type: "address" }, + ], + name: "setAddr", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "string", name: "newName", type: "string" }, + ], + name: "setName", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; + +// Relevant ABI for Basenames Registrar Controller Contract. +export const REGISTRAR_ABI = [ + { + inputs: [ + { + components: [ + { + internalType: "string", + name: "name", + type: "string", + }, + { + internalType: "address", + name: "owner", + type: "address", + }, + { + internalType: "uint256", + name: "duration", + type: "uint256", + }, + { + internalType: "address", + name: "resolver", + type: "address", + }, + { + internalType: "bytes[]", + name: "data", + type: "bytes[]", + }, + { + internalType: "bool", + name: "reverseRecord", + type: "bool", + }, + ], + internalType: "struct RegistrarController.RegisterRequest", + name: "request", + type: "tuple", + }, + ], + name: "register", + outputs: [], + stateMutability: "payable", + type: "function", + }, +] as const; + +// added ERC721 Transfer Methods, can be integrated into the REGISTRAR_ABI +export const REGISTRAR_TRANSFER_ABI = [ + { + inputs: [ + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "approve", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + // Check approval status + { + inputs: [ + { + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "getApproved", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + + { + inputs: [ + { + internalType: "address", + name: "from", + type: "address", + }, + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "transferFrom", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "from", + type: "address", + }, + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "safeTransferFrom", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + // Approval Methods + { + inputs: [ + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "tokenId", + type: "uint256", + }, + ], + name: "approve", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + // Helper Methods + { + inputs: [ + { + internalType: "uint256", + name: "id", + type: "uint256", + }, + ], + name: "available", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "id", + type: "uint256", + }, + { + internalType: "address", + name: "owner", + type: "address", + }, + ], + name: "reclaim", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; diff --git a/lib/web3/agentkit/action-providers/basename/index.ts b/lib/web3/agentkit/action-providers/basename/index.ts new file mode 100644 index 0000000..00799e7 --- /dev/null +++ b/lib/web3/agentkit/action-providers/basename/index.ts @@ -0,0 +1,2 @@ +export * from "./basenameActionProvider"; +export * from "./schemas"; diff --git a/lib/web3/agentkit/action-providers/basename/schemas.ts b/lib/web3/agentkit/action-providers/basename/schemas.ts new file mode 100644 index 0000000..7729cdf --- /dev/null +++ b/lib/web3/agentkit/action-providers/basename/schemas.ts @@ -0,0 +1,29 @@ +import { z } from "zod"; + +/** + * Input schema for registering a Basename. + */ +export const RegisterBasenameSchema = z + .object({ + basename: z.string().describe("The Basename to assign to the agent"), + amount: z + .string() + .default("0.002") + .describe("The amount of ETH to pay for registration"), + }) + .strip() + .describe("Instructions for registering a Basename"); + +/** + * Input schema for transfer action. + */ +export const TransferBasenameSchema = z + .object({ + basename: z.string().describe("The basename to transfer"), + contractAddress: z + .string() + .describe("The contract address of the basename contract to transfer"), + destination: z.string().describe("The destination to transfer the funds"), + }) + .strip() + .describe("Instructions for transferring basename"); diff --git a/lib/web3/agentkit/action-providers/basename/types.ts b/lib/web3/agentkit/action-providers/basename/types.ts new file mode 100644 index 0000000..b39cb12 --- /dev/null +++ b/lib/web3/agentkit/action-providers/basename/types.ts @@ -0,0 +1,19 @@ +/** + * Network is the network that the wallet provider is connected to. + */ +export interface Network { + /** + * The protocol family of the network. + */ + protocolFamily: string; + + /** + * The network ID of the network. + */ + networkId?: string; + + /** + * The chain ID of the network. + */ + chainId?: string; +} From da665bf266cd99cc2db3f2e0eb32b4f07a3c3f47 Mon Sep 17 00:00:00 2001 From: phipsae Date: Sun, 9 Feb 2025 11:07:26 +0100 Subject: [PATCH 07/16] first 3 functions of transfer implemented --- .../basename/basenameActionProvider.ts | 190 +++++++++++------- .../action-providers/basename/constants.ts | 57 +++++- 2 files changed, 173 insertions(+), 74 deletions(-) diff --git a/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts b/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts index 0db1a43..6a09155 100644 --- a/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts +++ b/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts @@ -1,4 +1,11 @@ -import { encodeFunctionData, Hex, namehash, parseEther } from "viem"; +import { + encodeFunctionData, + Hex, + namehash, + parseEther, + keccak256, + toBytes, +} from "viem"; import { z } from "zod"; import { ActionProvider, @@ -14,8 +21,10 @@ import { REGISTRATION_DURATION, BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET, BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET, + BASENAMES_BASE_REGISTRAR_ADDRESS_MAINNET, + BASENAMES_BASE_REGISTRAR_ADDRESS_TESTNET, REGISTRAR_ABI, - REGISTRAR_TRANSFER_ABI, + BASE_REGISTRAR_TRANSFER_ABI, } from "./constants"; import { RegisterBasenameSchema, TransferBasenameSchema } from "./schemas"; @@ -154,89 +163,126 @@ The agent must have a wallet connected that owns the Basename. The transfer will console.log("l2ResolverAddress", l2ResolverAddress); console.log("Destination Address", args.destination); - const addressData = encodeFunctionData({ - abi: L2_RESOLVER_ABI, - functionName: "setAddr", - args: [namehash(args.basename), agentAddress], - }); - const nameData = encodeFunctionData({ - abi: L2_RESOLVER_ABI, - functionName: "setName", - args: [namehash(args.basename), args.basename], - }); - console.log("addressData", addressData); - console.log("nameData", nameData); - - // const contractAddress = isMainnet - // ? BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET - // : BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET; - - // console.log("contractAddress", contractAddress); - - // ownerOf - // const currentOwner = await wallet.readContract({ - // address: contractAddress, - // abi: [ - // { - // inputs: [ - // { - // internalType: "uint256", - // name: "tokenId", - // type: "uint256", - // }, - // ], - // name: "ownerOf", - // outputs: [ - // { - // internalType: "address", - // name: "", - // type: "address", - // }, - // ], - // stateMutability: "view", - // type: "function", - // }, - // ], - // functionName: "ownerOf", - // args: [BigInt(namehash(args.basename))], - // }); - - // reclaim - // send - - // Check current owner of the basename using ownerOf + // Set the address record for the basename try { - const contractAddress = isMainnet - ? BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET - : BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET; - - console.log("contractAddress", contractAddress); + // Step 1: Set the address record + const nameHash = namehash(args.basename); + const setAddrHash = await wallet.sendTransaction({ + to: l2ResolverAddress, + data: encodeFunctionData({ + abi: L2_RESOLVER_ABI, + functionName: "setAddr", + args: [nameHash, args.destination as `0x${string}`], + }), + }); + const addrReceipt = await wallet.waitForTransactionReceipt(setAddrHash); + console.log( + "Set address record transaction completed in block", + addrReceipt.blockNumber + ); + + // Step 2: Set the name record after address record completes + const setNameHash = await wallet.sendTransaction({ + to: l2ResolverAddress, + data: encodeFunctionData({ + abi: L2_RESOLVER_ABI, + functionName: "setName", + args: [nameHash, args.basename], + }), + }); + const nameReceipt = await wallet.waitForTransactionReceipt(setNameHash); + console.log( + "Set name record transaction completed in block", + nameReceipt.blockNumber + ); + + // Step 3: Reclaim the basename after name record completes + const baseRegistrarAddress = isMainnet + ? BASENAMES_BASE_REGISTRAR_ADDRESS_MAINNET + : BASENAMES_BASE_REGISTRAR_ADDRESS_TESTNET; + + // Get just the label (remove .base.eth or .basetest.eth) + const label = args.basename.replace(suffix, ""); + const tokenId = BigInt(keccak256(toBytes(label))); + console.log("Label:", label); + console.log("Token ID:", tokenId.toString()); + + const reclaimHash = await wallet.sendTransaction({ + to: baseRegistrarAddress, + data: encodeFunctionData({ + abi: BASE_REGISTRAR_TRANSFER_ABI, + functionName: "reclaim", + args: [tokenId, args.destination as `0x${string}`], + }), + gas: 100000n, + }); - console.log("Current owner of basename:", currentOwner); + const reclaimReceipt = await wallet.waitForTransactionReceipt( + reclaimHash + ); + console.log( + "Reclaim transaction completed in block", + reclaimReceipt.blockNumber + ); } catch (error) { - return `Error checking basename ownership: ${error}`; + console.error("Error in transfer process:", error); + throw new Error( + `Transfer failed: ${ + error instanceof Error ? error.message : String(error) + }` + ); } + //////////// + + return "Successfully transferred basename"; + const contractAddress = isMainnet + ? BASENAMES_BASE_REGISTRAR_ADDRESS_MAINNET + : BASENAMES_BASE_REGISTRAR_ADDRESS_TESTNET; + + console.log("contractAddress", contractAddress); + + // balanceOf + const balance = await wallet.readContract({ + address: contractAddress, + abi: BASE_REGISTRAR_TRANSFER_ABI, + functionName: "balanceOf", + args: [agentAddress], + }); + console.log("balance", balance); + + const tokenId = BigInt(keccak256(toBytes(args.basename))); + console.log("tokenId", tokenId); + // First approve the transfer try { - const contractAddress = isMainnet - ? BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET - : BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET; - - const approvalHash = await wallet.sendTransaction({ + // reclaim + const reclaimHash = await wallet.sendTransaction({ to: contractAddress, data: encodeFunctionData({ - abi: REGISTRAR_TRANSFER_ABI, - functionName: "approve", - args: [contractAddress, BigInt(namehash(args.basename))], + abi: BASE_REGISTRAR_TRANSFER_ABI, + functionName: "reclaim", + args: [tokenId, args.destination as `0x${string}`], }), }); - - await wallet.waitForTransactionReceipt(approvalHash); - console.log("Approval transaction completed"); + await wallet.waitForTransactionReceipt(reclaimHash); + console.log("Reclaim transaction completed"); + // const contractAddress = isMainnet + // ? BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET + // : BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET; + // const approvalHash = await wallet.sendTransaction({ + // to: contractAddress, + // data: encodeFunctionData({ + // abi: REGISTRAR_TRANSFER_ABI, + // functionName: "approve", + // args: [contractAddress, BigInt(namehash(args.basename))], + // }), + // }); + // await wallet.waitForTransactionReceipt(approvalHash); + // console.log("Approval transaction completed"); return `Successfully approved basename transfer`; } catch (error) { - return `Error approving basename transfer: ${error}`; + return `Error reclaiming basename transfer: ${error}`; } // then transfer the basename diff --git a/lib/web3/agentkit/action-providers/basename/constants.ts b/lib/web3/agentkit/action-providers/basename/constants.ts index 5233b23..057e5fd 100644 --- a/lib/web3/agentkit/action-providers/basename/constants.ts +++ b/lib/web3/agentkit/action-providers/basename/constants.ts @@ -4,6 +4,12 @@ export const BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET = export const BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET = "0x49aE3cC2e3AA768B1e5654f5D3C6002144A59581"; +// for transfer needed +export const BASENAMES_BASE_REGISTRAR_ADDRESS_MAINNET = + "0x03c4738Ee98aE44591e1A4A4F3CaB6641d95DD9a"; +export const BASENAMES_BASE_REGISTRAR_ADDRESS_TESTNET = + "0xA0c70ec36c010B55E3C434D6c6EbEEC50c705794"; + export const L2_RESOLVER_ADDRESS_MAINNET = "0xC6d566A56A1aFf6508b41f6c90ff131615583BCD"; export const L2_RESOLVER_ADDRESS_TESTNET = @@ -34,6 +40,16 @@ export const L2_RESOLVER_ABI = [ stateMutability: "nonpayable", type: "function", }, + { + inputs: [ + { internalType: "bytes32", name: "node", type: "bytes32" }, + { internalType: "address", name: "addr", type: "address" }, + ], + name: "setAddr", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, ]; // Relevant ABI for Basenames Registrar Controller Contract. @@ -86,7 +102,44 @@ export const REGISTRAR_ABI = [ ] as const; // added ERC721 Transfer Methods, can be integrated into the REGISTRAR_ABI -export const REGISTRAR_TRANSFER_ABI = [ +export const BASE_REGISTRAR_TRANSFER_ABI = [ + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "balanceOf", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "id", + type: "uint256", + }, + { + internalType: "address", + name: "owner", + type: "address", + }, + ], + name: "reclaim", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, { inputs: [ { @@ -229,4 +282,4 @@ export const REGISTRAR_TRANSFER_ABI = [ stateMutability: "nonpayable", type: "function", }, -]; +] as const; From be19754588a732e9ece6216a873236c4720dd8ca Mon Sep 17 00:00:00 2001 From: phipsae Date: Sun, 9 Feb 2025 11:25:01 +0100 Subject: [PATCH 08/16] implment of transfer of basename successful --- .../basename/basenameActionProvider.ts | 117 ++++++------------ 1 file changed, 36 insertions(+), 81 deletions(-) diff --git a/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts b/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts index 6a09155..6692dc4 100644 --- a/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts +++ b/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts @@ -178,7 +178,9 @@ The agent must have a wallet connected that owns the Basename. The transfer will const addrReceipt = await wallet.waitForTransactionReceipt(setAddrHash); console.log( "Set address record transaction completed in block", - addrReceipt.blockNumber + addrReceipt.blockNumber, + "with tx:", + setAddrHash ); // Step 2: Set the name record after address record completes @@ -193,7 +195,9 @@ The agent must have a wallet connected that owns the Basename. The transfer will const nameReceipt = await wallet.waitForTransactionReceipt(setNameHash); console.log( "Set name record transaction completed in block", - nameReceipt.blockNumber + nameReceipt.blockNumber, + "with tx:", + setNameHash ); // Step 3: Reclaim the basename after name record completes @@ -222,8 +226,37 @@ The agent must have a wallet connected that owns the Basename. The transfer will ); console.log( "Reclaim transaction completed in block", - reclaimReceipt.blockNumber + reclaimReceipt.blockNumber, + "with tx:", + reclaimHash ); + + // Step 4: Transfer the ENS name + const transferHash = await wallet.sendTransaction({ + to: baseRegistrarAddress, + data: encodeFunctionData({ + abi: BASE_REGISTRAR_TRANSFER_ABI, + functionName: "safeTransferFrom", + args: [ + agentAddress as `0x${string}`, + args.destination as `0x${string}`, + tokenId, + ], + }), + gas: 100000n, + }); + + const transferReceipt = await wallet.waitForTransactionReceipt( + transferHash + ); + console.log( + "Transfer transaction completed in block", + transferReceipt.blockNumber, + "with tx:", + transferHash + ); + + return `Successfully transferred basename ${args.basename} to ${args.destination}`; } catch (error) { console.error("Error in transfer process:", error); throw new Error( @@ -232,84 +265,6 @@ The agent must have a wallet connected that owns the Basename. The transfer will }` ); } - - //////////// - - return "Successfully transferred basename"; - const contractAddress = isMainnet - ? BASENAMES_BASE_REGISTRAR_ADDRESS_MAINNET - : BASENAMES_BASE_REGISTRAR_ADDRESS_TESTNET; - - console.log("contractAddress", contractAddress); - - // balanceOf - const balance = await wallet.readContract({ - address: contractAddress, - abi: BASE_REGISTRAR_TRANSFER_ABI, - functionName: "balanceOf", - args: [agentAddress], - }); - console.log("balance", balance); - - const tokenId = BigInt(keccak256(toBytes(args.basename))); - console.log("tokenId", tokenId); - - // First approve the transfer - try { - // reclaim - const reclaimHash = await wallet.sendTransaction({ - to: contractAddress, - data: encodeFunctionData({ - abi: BASE_REGISTRAR_TRANSFER_ABI, - functionName: "reclaim", - args: [tokenId, args.destination as `0x${string}`], - }), - }); - await wallet.waitForTransactionReceipt(reclaimHash); - console.log("Reclaim transaction completed"); - // const contractAddress = isMainnet - // ? BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET - // : BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET; - // const approvalHash = await wallet.sendTransaction({ - // to: contractAddress, - // data: encodeFunctionData({ - // abi: REGISTRAR_TRANSFER_ABI, - // functionName: "approve", - // args: [contractAddress, BigInt(namehash(args.basename))], - // }), - // }); - // await wallet.waitForTransactionReceipt(approvalHash); - // console.log("Approval transaction completed"); - return `Successfully approved basename transfer`; - } catch (error) { - return `Error reclaiming basename transfer: ${error}`; - } - - // then transfer the basename - // try { - // const contractAddress = isMainnet - // ? BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET - // : BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET; - - // const hash = await wallet.sendTransaction({ - // to: contractAddress, - // data: encodeFunctionData({ - // abi: REGISTRAR_TRANSFER_ABI, - // functionName: "transferFrom", - // args: [ - // agentAddress, - // args.destination, - // BigInt(namehash(args.basename)), - // ], - // }), - // }); - - // await wallet.waitForTransactionReceipt(hash); - - // return `Successfully registered basename ${args.basename} for address ${args.destination}`; - // } catch (error) { - // return `Error transferring basename: Error: ${error}`; - // } } /** From b0c7043ac5588106902622c1bf8de0ec105c06b9 Mon Sep 17 00:00:00 2001 From: phipsae Date: Sun, 9 Feb 2025 12:03:33 +0100 Subject: [PATCH 09/16] remove console logs and implement basename action correctly --- lib/db/migrations/0000_shiny_mysterio.sql | 64 +++++++++++++++++++ lib/db/migrations/meta/0000_snapshot.json | 4 +- .../basename/basenameActionProvider.ts | 9 --- .../action-providers/basename/schemas.ts | 4 +- lib/web3/agentkit/setup.ts | 2 + 5 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 lib/db/migrations/0000_shiny_mysterio.sql diff --git a/lib/db/migrations/0000_shiny_mysterio.sql b/lib/db/migrations/0000_shiny_mysterio.sql new file mode 100644 index 0000000..1f965d8 --- /dev/null +++ b/lib/db/migrations/0000_shiny_mysterio.sql @@ -0,0 +1,64 @@ +CREATE TABLE IF NOT EXISTS "Charge" ( + "id" text PRIMARY KEY NOT NULL, + "userId" varchar(42) NOT NULL, + "status" varchar DEFAULT 'NEW' NOT NULL, + "product" varchar DEFAULT 'STARTERKIT' NOT NULL, + "payerAddress" varchar(42), + "amount" text NOT NULL, + "currency" text NOT NULL, + "createdAt" timestamp NOT NULL, + "confirmedAt" timestamp, + "expiresAt" timestamp, + "transactionHash" text +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "StarterKit" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "creatorId" varchar(42), + "claimerId" varchar(42), + "chargeId" text, + "createdAt" timestamp NOT NULL, + "claimedAt" timestamp, + "value" bigint NOT NULL, + "balance" bigint DEFAULT 0 NOT NULL, + "deletedAt" timestamp +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "UserKnowledge" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "userId" varchar(42) NOT NULL, + "type" varchar NOT NULL, + "content" json NOT NULL, + "createdAt" timestamp NOT NULL, + "deletedAt" timestamp +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Charge" ADD CONSTRAINT "Charge_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_creatorId_User_id_fk" FOREIGN KEY ("creatorId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_claimerId_User_id_fk" FOREIGN KEY ("claimerId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_chargeId_Charge_id_fk" FOREIGN KEY ("chargeId") REFERENCES "public"."Charge"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "UserKnowledge" ADD CONSTRAINT "UserKnowledge_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/lib/db/migrations/meta/0000_snapshot.json b/lib/db/migrations/meta/0000_snapshot.json index 6869492..e744d2b 100644 --- a/lib/db/migrations/meta/0000_snapshot.json +++ b/lib/db/migrations/meta/0000_snapshot.json @@ -1,6 +1,6 @@ { - "id": "d676d783-e2de-417e-8956-8efd3b9af1b0", - "prevId": "00000000-0000-0000-0000-000000000000", + "id": "5c82d558-8d24-42e7-b392-5bc209ba3c25", + "prevId": "7b178f72-821d-492c-b4d9-923786d011d7", "version": "7", "dialect": "postgresql", "tables": { diff --git a/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts b/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts index 6692dc4..a4be9d9 100644 --- a/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts +++ b/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts @@ -61,7 +61,6 @@ Basename fails, you should prompt to try again with a more unique name. args: z.infer ): Promise { const address = wallet.getAddress(); - console.log("address", address); const isMainnet = wallet.getNetwork().networkId === "base-mainnet"; const suffix = isMainnet ? ".base.eth" : ".basetest.eth"; @@ -69,8 +68,6 @@ Basename fails, you should prompt to try again with a more unique name. args.basename += suffix; } - console.log("in here"); - const l2ResolverAddress = isMainnet ? L2_RESOLVER_ADDRESS_MAINNET : L2_RESOLVER_ADDRESS_TESTNET; @@ -157,12 +154,6 @@ The agent must have a wallet connected that owns the Basename. The transfer will ? L2_RESOLVER_ADDRESS_MAINNET : L2_RESOLVER_ADDRESS_TESTNET; - console.log("Base Name", args.basename); - console.log("Agent Address", agentAddress); - console.log("mainnet", isMainnet); - console.log("l2ResolverAddress", l2ResolverAddress); - console.log("Destination Address", args.destination); - // Set the address record for the basename try { // Step 1: Set the address record diff --git a/lib/web3/agentkit/action-providers/basename/schemas.ts b/lib/web3/agentkit/action-providers/basename/schemas.ts index 7729cdf..a1bb1c8 100644 --- a/lib/web3/agentkit/action-providers/basename/schemas.ts +++ b/lib/web3/agentkit/action-providers/basename/schemas.ts @@ -23,7 +23,9 @@ export const TransferBasenameSchema = z contractAddress: z .string() .describe("The contract address of the basename contract to transfer"), - destination: z.string().describe("The destination to transfer the funds"), + destination: z + .string() + .describe("The destination to transfer the ens name"), }) .strip() .describe("Instructions for transferring basename"); diff --git a/lib/web3/agentkit/setup.ts b/lib/web3/agentkit/setup.ts index 4d6cf30..844d4ff 100644 --- a/lib/web3/agentkit/setup.ts +++ b/lib/web3/agentkit/setup.ts @@ -8,6 +8,7 @@ import { erc20ActionProvider } from "./action-providers/erc20"; import { safeActionProvider } from "./action-providers/safe"; import { alchemyActionProvider } from "./action-providers/alchemy"; import { zoraActionProvider } from "./action-providers/zora"; +import { basenameActionProvider } from "./action-providers/basename"; export const setupAgentKit = async () => { const activeChain = @@ -30,6 +31,7 @@ export const setupAgentKit = async () => { walletActionProvider(), erc20ActionProvider(), safeActionProvider(), + basenameActionProvider(), alchemyActionProvider(process.env.ALCHEMY_API_KEY as string), zoraActionProvider(), ], From 97002ded60396e2f72f4a3687f04b9087ef1d075 Mon Sep 17 00:00:00 2001 From: phipsae Date: Sun, 9 Feb 2025 12:13:21 +0100 Subject: [PATCH 10/16] remove unnecessary sql files --- .gitignore | 3 + lib/db/migrations/0000_absurd_hobgoblin.sql | 156 -------------------- lib/db/migrations/0000_shiny_mysterio.sql | 64 -------- 3 files changed, 3 insertions(+), 220 deletions(-) delete mode 100644 lib/db/migrations/0000_absurd_hobgoblin.sql delete mode 100644 lib/db/migrations/0000_shiny_mysterio.sql diff --git a/.gitignore b/.gitignore index dd019e4..0571446 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ yarn-error.log* .vercel .vscode .env*.local + +# Database migrations +lib/db/migrations/*.sql diff --git a/lib/db/migrations/0000_absurd_hobgoblin.sql b/lib/db/migrations/0000_absurd_hobgoblin.sql deleted file mode 100644 index 2a6f20e..0000000 --- a/lib/db/migrations/0000_absurd_hobgoblin.sql +++ /dev/null @@ -1,156 +0,0 @@ -CREATE TABLE IF NOT EXISTS "Charge" ( - "id" text PRIMARY KEY NOT NULL, - "userId" varchar(42) NOT NULL, - "status" varchar DEFAULT 'NEW' NOT NULL, - "product" varchar DEFAULT 'STARTERKIT' NOT NULL, - "payerAddress" varchar(42), - "amount" text NOT NULL, - "currency" text NOT NULL, - "createdAt" timestamp NOT NULL, - "confirmedAt" timestamp, - "expiresAt" timestamp, - "transactionHash" text -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "Chat" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "createdAt" timestamp NOT NULL, - "title" text NOT NULL, - "userId" varchar(42) NOT NULL, - "visibility" varchar DEFAULT 'private' NOT NULL -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "Document" ( - "id" uuid DEFAULT gen_random_uuid() NOT NULL, - "createdAt" timestamp NOT NULL, - "title" text NOT NULL, - "content" text, - "text" varchar DEFAULT 'text' NOT NULL, - "userId" varchar(42) NOT NULL, - CONSTRAINT "Document_id_createdAt_pk" PRIMARY KEY("id","createdAt") -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "Message" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "chatId" uuid NOT NULL, - "role" varchar NOT NULL, - "content" json NOT NULL, - "createdAt" timestamp NOT NULL -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "StarterKit" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "creatorId" varchar(42), - "claimerId" varchar(42), - "chargeId" text, - "createdAt" timestamp NOT NULL, - "claimedAt" timestamp, - "value" bigint NOT NULL, - "balance" bigint DEFAULT 0 NOT NULL, - "deletedAt" timestamp -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "Suggestion" ( - "id" uuid DEFAULT gen_random_uuid() NOT NULL, - "documentId" uuid NOT NULL, - "documentCreatedAt" timestamp NOT NULL, - "originalText" text NOT NULL, - "suggestedText" text NOT NULL, - "description" text, - "isResolved" boolean DEFAULT false NOT NULL, - "userId" varchar(42) NOT NULL, - "createdAt" timestamp NOT NULL, - CONSTRAINT "Suggestion_id_pk" PRIMARY KEY("id") -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "User" ( - "id" varchar(42) PRIMARY KEY NOT NULL -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "UserKnowledge" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "userId" varchar(42) NOT NULL, - "type" varchar NOT NULL, - "content" json NOT NULL, - "createdAt" timestamp NOT NULL, - "deletedAt" timestamp -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "Vote" ( - "chatId" uuid NOT NULL, - "messageId" uuid NOT NULL, - "isUpvoted" boolean NOT NULL, - CONSTRAINT "Vote_chatId_messageId_pk" PRIMARY KEY("chatId","messageId") -); ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Charge" ADD CONSTRAINT "Charge_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Chat" ADD CONSTRAINT "Chat_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Document" ADD CONSTRAINT "Document_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Message" ADD CONSTRAINT "Message_chatId_Chat_id_fk" FOREIGN KEY ("chatId") REFERENCES "public"."Chat"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_creatorId_User_id_fk" FOREIGN KEY ("creatorId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_claimerId_User_id_fk" FOREIGN KEY ("claimerId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_chargeId_Charge_id_fk" FOREIGN KEY ("chargeId") REFERENCES "public"."Charge"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Suggestion" ADD CONSTRAINT "Suggestion_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Suggestion" ADD CONSTRAINT "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk" FOREIGN KEY ("documentId","documentCreatedAt") REFERENCES "public"."Document"("id","createdAt") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "UserKnowledge" ADD CONSTRAINT "UserKnowledge_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Vote" ADD CONSTRAINT "Vote_chatId_Chat_id_fk" FOREIGN KEY ("chatId") REFERENCES "public"."Chat"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Vote" ADD CONSTRAINT "Vote_messageId_Message_id_fk" FOREIGN KEY ("messageId") REFERENCES "public"."Message"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; diff --git a/lib/db/migrations/0000_shiny_mysterio.sql b/lib/db/migrations/0000_shiny_mysterio.sql deleted file mode 100644 index 1f965d8..0000000 --- a/lib/db/migrations/0000_shiny_mysterio.sql +++ /dev/null @@ -1,64 +0,0 @@ -CREATE TABLE IF NOT EXISTS "Charge" ( - "id" text PRIMARY KEY NOT NULL, - "userId" varchar(42) NOT NULL, - "status" varchar DEFAULT 'NEW' NOT NULL, - "product" varchar DEFAULT 'STARTERKIT' NOT NULL, - "payerAddress" varchar(42), - "amount" text NOT NULL, - "currency" text NOT NULL, - "createdAt" timestamp NOT NULL, - "confirmedAt" timestamp, - "expiresAt" timestamp, - "transactionHash" text -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "StarterKit" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "creatorId" varchar(42), - "claimerId" varchar(42), - "chargeId" text, - "createdAt" timestamp NOT NULL, - "claimedAt" timestamp, - "value" bigint NOT NULL, - "balance" bigint DEFAULT 0 NOT NULL, - "deletedAt" timestamp -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "UserKnowledge" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "userId" varchar(42) NOT NULL, - "type" varchar NOT NULL, - "content" json NOT NULL, - "createdAt" timestamp NOT NULL, - "deletedAt" timestamp -); ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Charge" ADD CONSTRAINT "Charge_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_creatorId_User_id_fk" FOREIGN KEY ("creatorId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_claimerId_User_id_fk" FOREIGN KEY ("claimerId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_chargeId_Charge_id_fk" FOREIGN KEY ("chargeId") REFERENCES "public"."Charge"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "UserKnowledge" ADD CONSTRAINT "UserKnowledge_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; From af69170be5a533efa1a55c2e191c01caeb8737c3 Mon Sep 17 00:00:00 2001 From: phipsae Date: Sun, 9 Feb 2025 12:22:56 +0100 Subject: [PATCH 11/16] remove unnecessary db files --- .gitignore | 3 - lib/db/migrations/0000_shiny_mysterio.sql | 64 ++ .../migrations/0000_talented_queen_noir.sql | 91 +++ lib/db/migrations/meta/0000_snapshot.json | 637 ------------------ lib/db/migrations/meta/_journal.json | 12 +- 5 files changed, 157 insertions(+), 650 deletions(-) create mode 100644 lib/db/migrations/0000_shiny_mysterio.sql create mode 100644 lib/db/migrations/0000_talented_queen_noir.sql delete mode 100644 lib/db/migrations/meta/0000_snapshot.json diff --git a/.gitignore b/.gitignore index 0571446..dd019e4 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,3 @@ yarn-error.log* .vercel .vscode .env*.local - -# Database migrations -lib/db/migrations/*.sql diff --git a/lib/db/migrations/0000_shiny_mysterio.sql b/lib/db/migrations/0000_shiny_mysterio.sql new file mode 100644 index 0000000..1f965d8 --- /dev/null +++ b/lib/db/migrations/0000_shiny_mysterio.sql @@ -0,0 +1,64 @@ +CREATE TABLE IF NOT EXISTS "Charge" ( + "id" text PRIMARY KEY NOT NULL, + "userId" varchar(42) NOT NULL, + "status" varchar DEFAULT 'NEW' NOT NULL, + "product" varchar DEFAULT 'STARTERKIT' NOT NULL, + "payerAddress" varchar(42), + "amount" text NOT NULL, + "currency" text NOT NULL, + "createdAt" timestamp NOT NULL, + "confirmedAt" timestamp, + "expiresAt" timestamp, + "transactionHash" text +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "StarterKit" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "creatorId" varchar(42), + "claimerId" varchar(42), + "chargeId" text, + "createdAt" timestamp NOT NULL, + "claimedAt" timestamp, + "value" bigint NOT NULL, + "balance" bigint DEFAULT 0 NOT NULL, + "deletedAt" timestamp +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "UserKnowledge" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "userId" varchar(42) NOT NULL, + "type" varchar NOT NULL, + "content" json NOT NULL, + "createdAt" timestamp NOT NULL, + "deletedAt" timestamp +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Charge" ADD CONSTRAINT "Charge_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_creatorId_User_id_fk" FOREIGN KEY ("creatorId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_claimerId_User_id_fk" FOREIGN KEY ("claimerId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_chargeId_Charge_id_fk" FOREIGN KEY ("chargeId") REFERENCES "public"."Charge"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "UserKnowledge" ADD CONSTRAINT "UserKnowledge_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/lib/db/migrations/0000_talented_queen_noir.sql b/lib/db/migrations/0000_talented_queen_noir.sql new file mode 100644 index 0000000..cf83c94 --- /dev/null +++ b/lib/db/migrations/0000_talented_queen_noir.sql @@ -0,0 +1,91 @@ +CREATE TABLE IF NOT EXISTS "Chat" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "createdAt" timestamp NOT NULL, + "title" text NOT NULL, + "userId" varchar(42) NOT NULL, + "visibility" varchar DEFAULT 'private' NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "Document" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL, + "createdAt" timestamp NOT NULL, + "title" text NOT NULL, + "content" text, + "text" varchar DEFAULT 'text' NOT NULL, + "userId" varchar(42) NOT NULL, + CONSTRAINT "Document_id_createdAt_pk" PRIMARY KEY("id","createdAt") +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "Message" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "chatId" uuid NOT NULL, + "role" varchar NOT NULL, + "content" json NOT NULL, + "createdAt" timestamp NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "Suggestion" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL, + "documentId" uuid NOT NULL, + "documentCreatedAt" timestamp NOT NULL, + "originalText" text NOT NULL, + "suggestedText" text NOT NULL, + "description" text, + "isResolved" boolean DEFAULT false NOT NULL, + "userId" varchar(42) NOT NULL, + "createdAt" timestamp NOT NULL, + CONSTRAINT "Suggestion_id_pk" PRIMARY KEY("id") +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "User" ( + "id" varchar(42) PRIMARY KEY NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "Vote" ( + "chatId" uuid NOT NULL, + "messageId" uuid NOT NULL, + "isUpvoted" boolean NOT NULL, + CONSTRAINT "Vote_chatId_messageId_pk" PRIMARY KEY("chatId","messageId") +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Chat" ADD CONSTRAINT "Chat_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Document" ADD CONSTRAINT "Document_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Message" ADD CONSTRAINT "Message_chatId_Chat_id_fk" FOREIGN KEY ("chatId") REFERENCES "public"."Chat"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Suggestion" ADD CONSTRAINT "Suggestion_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Suggestion" ADD CONSTRAINT "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk" FOREIGN KEY ("documentId","documentCreatedAt") REFERENCES "public"."Document"("id","createdAt") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Vote" ADD CONSTRAINT "Vote_chatId_Chat_id_fk" FOREIGN KEY ("chatId") REFERENCES "public"."Chat"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Vote" ADD CONSTRAINT "Vote_messageId_Message_id_fk" FOREIGN KEY ("messageId") REFERENCES "public"."Message"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/lib/db/migrations/meta/0000_snapshot.json b/lib/db/migrations/meta/0000_snapshot.json deleted file mode 100644 index e744d2b..0000000 --- a/lib/db/migrations/meta/0000_snapshot.json +++ /dev/null @@ -1,637 +0,0 @@ -{ - "id": "5c82d558-8d24-42e7-b392-5bc209ba3c25", - "prevId": "7b178f72-821d-492c-b4d9-923786d011d7", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.Charge": { - "name": "Charge", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "userId": { - "name": "userId", - "type": "varchar(42)", - "primaryKey": false, - "notNull": true - }, - "status": { - "name": "status", - "type": "varchar", - "primaryKey": false, - "notNull": true, - "default": "'NEW'" - }, - "product": { - "name": "product", - "type": "varchar", - "primaryKey": false, - "notNull": true, - "default": "'STARTERKIT'" - }, - "payerAddress": { - "name": "payerAddress", - "type": "varchar(42)", - "primaryKey": false, - "notNull": false - }, - "amount": { - "name": "amount", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "currency": { - "name": "currency", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "confirmedAt": { - "name": "confirmedAt", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "expiresAt": { - "name": "expiresAt", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "transactionHash": { - "name": "transactionHash", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "Charge_userId_User_id_fk": { - "name": "Charge_userId_User_id_fk", - "tableFrom": "Charge", - "tableTo": "User", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "public.Chat": { - "name": "Chat", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "createdAt": { - "name": "createdAt", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "userId": { - "name": "userId", - "type": "varchar(42)", - "primaryKey": false, - "notNull": true - }, - "visibility": { - "name": "visibility", - "type": "varchar", - "primaryKey": false, - "notNull": true, - "default": "'private'" - } - }, - "indexes": {}, - "foreignKeys": { - "Chat_userId_User_id_fk": { - "name": "Chat_userId_User_id_fk", - "tableFrom": "Chat", - "tableTo": "User", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "public.Document": { - "name": "Document", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": false, - "notNull": true, - "default": "gen_random_uuid()" - }, - "createdAt": { - "name": "createdAt", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "content": { - "name": "content", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "text": { - "name": "text", - "type": "varchar", - "primaryKey": false, - "notNull": true, - "default": "'text'" - }, - "userId": { - "name": "userId", - "type": "varchar(42)", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "Document_userId_User_id_fk": { - "name": "Document_userId_User_id_fk", - "tableFrom": "Document", - "tableTo": "User", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "Document_id_createdAt_pk": { - "name": "Document_id_createdAt_pk", - "columns": [ - "id", - "createdAt" - ] - } - }, - "uniqueConstraints": {} - }, - "public.Message": { - "name": "Message", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "chatId": { - "name": "chatId", - "type": "uuid", - "primaryKey": false, - "notNull": true - }, - "role": { - "name": "role", - "type": "varchar", - "primaryKey": false, - "notNull": true - }, - "content": { - "name": "content", - "type": "json", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "timestamp", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "Message_chatId_Chat_id_fk": { - "name": "Message_chatId_Chat_id_fk", - "tableFrom": "Message", - "tableTo": "Chat", - "columnsFrom": [ - "chatId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "public.StarterKit": { - "name": "StarterKit", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "creatorId": { - "name": "creatorId", - "type": "varchar(42)", - "primaryKey": false, - "notNull": false - }, - "claimerId": { - "name": "claimerId", - "type": "varchar(42)", - "primaryKey": false, - "notNull": false - }, - "chargeId": { - "name": "chargeId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "claimedAt": { - "name": "claimedAt", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "value": { - "name": "value", - "type": "bigint", - "primaryKey": false, - "notNull": true - }, - "balance": { - "name": "balance", - "type": "bigint", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "deletedAt": { - "name": "deletedAt", - "type": "timestamp", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "StarterKit_creatorId_User_id_fk": { - "name": "StarterKit_creatorId_User_id_fk", - "tableFrom": "StarterKit", - "tableTo": "User", - "columnsFrom": [ - "creatorId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "StarterKit_claimerId_User_id_fk": { - "name": "StarterKit_claimerId_User_id_fk", - "tableFrom": "StarterKit", - "tableTo": "User", - "columnsFrom": [ - "claimerId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "StarterKit_chargeId_Charge_id_fk": { - "name": "StarterKit_chargeId_Charge_id_fk", - "tableFrom": "StarterKit", - "tableTo": "Charge", - "columnsFrom": [ - "chargeId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "public.Suggestion": { - "name": "Suggestion", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": false, - "notNull": true, - "default": "gen_random_uuid()" - }, - "documentId": { - "name": "documentId", - "type": "uuid", - "primaryKey": false, - "notNull": true - }, - "documentCreatedAt": { - "name": "documentCreatedAt", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "originalText": { - "name": "originalText", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "suggestedText": { - "name": "suggestedText", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "isResolved": { - "name": "isResolved", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "userId": { - "name": "userId", - "type": "varchar(42)", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "timestamp", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "Suggestion_userId_User_id_fk": { - "name": "Suggestion_userId_User_id_fk", - "tableFrom": "Suggestion", - "tableTo": "User", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk": { - "name": "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk", - "tableFrom": "Suggestion", - "tableTo": "Document", - "columnsFrom": [ - "documentId", - "documentCreatedAt" - ], - "columnsTo": [ - "id", - "createdAt" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "Suggestion_id_pk": { - "name": "Suggestion_id_pk", - "columns": [ - "id" - ] - } - }, - "uniqueConstraints": {} - }, - "public.User": { - "name": "User", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "varchar(42)", - "primaryKey": true, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "public.UserKnowledge": { - "name": "UserKnowledge", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "userId": { - "name": "userId", - "type": "varchar(42)", - "primaryKey": false, - "notNull": true - }, - "type": { - "name": "type", - "type": "varchar", - "primaryKey": false, - "notNull": true - }, - "content": { - "name": "content", - "type": "json", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "deletedAt": { - "name": "deletedAt", - "type": "timestamp", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "UserKnowledge_userId_User_id_fk": { - "name": "UserKnowledge_userId_User_id_fk", - "tableFrom": "UserKnowledge", - "tableTo": "User", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "public.Vote": { - "name": "Vote", - "schema": "", - "columns": { - "chatId": { - "name": "chatId", - "type": "uuid", - "primaryKey": false, - "notNull": true - }, - "messageId": { - "name": "messageId", - "type": "uuid", - "primaryKey": false, - "notNull": true - }, - "isUpvoted": { - "name": "isUpvoted", - "type": "boolean", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "Vote_chatId_Chat_id_fk": { - "name": "Vote_chatId_Chat_id_fk", - "tableFrom": "Vote", - "tableTo": "Chat", - "columnsFrom": [ - "chatId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "Vote_messageId_Message_id_fk": { - "name": "Vote_messageId_Message_id_fk", - "tableFrom": "Vote", - "tableTo": "Message", - "columnsFrom": [ - "messageId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": { - "Vote_chatId_messageId_pk": { - "name": "Vote_chatId_messageId_pk", - "columns": [ - "chatId", - "messageId" - ] - } - }, - "uniqueConstraints": {} - } - }, - "enums": {}, - "schemas": {}, - "sequences": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file diff --git a/lib/db/migrations/meta/_journal.json b/lib/db/migrations/meta/_journal.json index a3e61d1..a7e0211 100644 --- a/lib/db/migrations/meta/_journal.json +++ b/lib/db/migrations/meta/_journal.json @@ -1,13 +1,5 @@ { "version": "7", "dialect": "postgresql", - "entries": [ - { - "idx": 0, - "version": "7", - "when": 1739109815400, - "tag": "0000_absurd_hobgoblin", - "breakpoints": true - } - ] -} \ No newline at end of file + "entries": [] +} From 3a02d3dc2ebea00deca486a09a794abc65f0a967 Mon Sep 17 00:00:00 2001 From: phipsae Date: Sun, 9 Feb 2025 12:28:37 +0100 Subject: [PATCH 12/16] remove console log --- app/api/chat/route.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 00d9ebf..5fb0b67 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,8 +1,8 @@ -import { +import { type Message, createDataStreamResponse, streamText, - Output, + Output } from "ai"; import { auth } from "@/app/auth"; @@ -85,8 +85,6 @@ export async function POST(request: Request) { const tools = agentKitToTools(agentKit); - console.log(""); - return createDataStreamResponse({ execute: (dataStream) => { const result = streamText({ From b80332c6a47a7ee8faaa8f7297a470747e44e514 Mon Sep 17 00:00:00 2001 From: phipsae Date: Sun, 9 Feb 2025 12:32:42 +0100 Subject: [PATCH 13/16] little changes --- app/api/chat/route.ts | 4 +- lib/db/migrations/0000_shiny_mysterio.sql | 64 ------------- .../migrations/0000_talented_queen_noir.sql | 91 ------------------- lib/db/migrations/meta/_journal.json | 2 +- 4 files changed, 3 insertions(+), 158 deletions(-) delete mode 100644 lib/db/migrations/0000_shiny_mysterio.sql delete mode 100644 lib/db/migrations/0000_talented_queen_noir.sql diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 5fb0b67..dd5a1c6 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -1,8 +1,8 @@ -import { +import { type Message, createDataStreamResponse, streamText, - Output + Output, } from "ai"; import { auth } from "@/app/auth"; diff --git a/lib/db/migrations/0000_shiny_mysterio.sql b/lib/db/migrations/0000_shiny_mysterio.sql deleted file mode 100644 index 1f965d8..0000000 --- a/lib/db/migrations/0000_shiny_mysterio.sql +++ /dev/null @@ -1,64 +0,0 @@ -CREATE TABLE IF NOT EXISTS "Charge" ( - "id" text PRIMARY KEY NOT NULL, - "userId" varchar(42) NOT NULL, - "status" varchar DEFAULT 'NEW' NOT NULL, - "product" varchar DEFAULT 'STARTERKIT' NOT NULL, - "payerAddress" varchar(42), - "amount" text NOT NULL, - "currency" text NOT NULL, - "createdAt" timestamp NOT NULL, - "confirmedAt" timestamp, - "expiresAt" timestamp, - "transactionHash" text -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "StarterKit" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "creatorId" varchar(42), - "claimerId" varchar(42), - "chargeId" text, - "createdAt" timestamp NOT NULL, - "claimedAt" timestamp, - "value" bigint NOT NULL, - "balance" bigint DEFAULT 0 NOT NULL, - "deletedAt" timestamp -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "UserKnowledge" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "userId" varchar(42) NOT NULL, - "type" varchar NOT NULL, - "content" json NOT NULL, - "createdAt" timestamp NOT NULL, - "deletedAt" timestamp -); ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Charge" ADD CONSTRAINT "Charge_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_creatorId_User_id_fk" FOREIGN KEY ("creatorId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_claimerId_User_id_fk" FOREIGN KEY ("claimerId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_chargeId_Charge_id_fk" FOREIGN KEY ("chargeId") REFERENCES "public"."Charge"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "UserKnowledge" ADD CONSTRAINT "UserKnowledge_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; diff --git a/lib/db/migrations/0000_talented_queen_noir.sql b/lib/db/migrations/0000_talented_queen_noir.sql deleted file mode 100644 index cf83c94..0000000 --- a/lib/db/migrations/0000_talented_queen_noir.sql +++ /dev/null @@ -1,91 +0,0 @@ -CREATE TABLE IF NOT EXISTS "Chat" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "createdAt" timestamp NOT NULL, - "title" text NOT NULL, - "userId" varchar(42) NOT NULL, - "visibility" varchar DEFAULT 'private' NOT NULL -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "Document" ( - "id" uuid DEFAULT gen_random_uuid() NOT NULL, - "createdAt" timestamp NOT NULL, - "title" text NOT NULL, - "content" text, - "text" varchar DEFAULT 'text' NOT NULL, - "userId" varchar(42) NOT NULL, - CONSTRAINT "Document_id_createdAt_pk" PRIMARY KEY("id","createdAt") -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "Message" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "chatId" uuid NOT NULL, - "role" varchar NOT NULL, - "content" json NOT NULL, - "createdAt" timestamp NOT NULL -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "Suggestion" ( - "id" uuid DEFAULT gen_random_uuid() NOT NULL, - "documentId" uuid NOT NULL, - "documentCreatedAt" timestamp NOT NULL, - "originalText" text NOT NULL, - "suggestedText" text NOT NULL, - "description" text, - "isResolved" boolean DEFAULT false NOT NULL, - "userId" varchar(42) NOT NULL, - "createdAt" timestamp NOT NULL, - CONSTRAINT "Suggestion_id_pk" PRIMARY KEY("id") -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "User" ( - "id" varchar(42) PRIMARY KEY NOT NULL -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "Vote" ( - "chatId" uuid NOT NULL, - "messageId" uuid NOT NULL, - "isUpvoted" boolean NOT NULL, - CONSTRAINT "Vote_chatId_messageId_pk" PRIMARY KEY("chatId","messageId") -); ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Chat" ADD CONSTRAINT "Chat_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Document" ADD CONSTRAINT "Document_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Message" ADD CONSTRAINT "Message_chatId_Chat_id_fk" FOREIGN KEY ("chatId") REFERENCES "public"."Chat"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Suggestion" ADD CONSTRAINT "Suggestion_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Suggestion" ADD CONSTRAINT "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk" FOREIGN KEY ("documentId","documentCreatedAt") REFERENCES "public"."Document"("id","createdAt") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Vote" ADD CONSTRAINT "Vote_chatId_Chat_id_fk" FOREIGN KEY ("chatId") REFERENCES "public"."Chat"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "Vote" ADD CONSTRAINT "Vote_messageId_Message_id_fk" FOREIGN KEY ("messageId") REFERENCES "public"."Message"("id") ON DELETE no action ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; diff --git a/lib/db/migrations/meta/_journal.json b/lib/db/migrations/meta/_journal.json index a7e0211..eaa8fcf 100644 --- a/lib/db/migrations/meta/_journal.json +++ b/lib/db/migrations/meta/_journal.json @@ -2,4 +2,4 @@ "version": "7", "dialect": "postgresql", "entries": [] -} +} \ No newline at end of file From 57e2178fc25690766043a3f21e1e11a7107d6925 Mon Sep 17 00:00:00 2001 From: phipsae Date: Sun, 9 Feb 2025 12:45:34 +0100 Subject: [PATCH 14/16] add action that does register and transfer together --- .../migrations/0000_dizzy_madame_masque.sql | 156 +++++ lib/db/migrations/meta/0000_snapshot.json | 637 ++++++++++++++++++ lib/db/migrations/meta/_journal.json | 10 +- .../basename/basenameActionProvider.ts | 58 +- .../action-providers/basename/schemas.ts | 6 + 5 files changed, 865 insertions(+), 2 deletions(-) create mode 100644 lib/db/migrations/0000_dizzy_madame_masque.sql create mode 100644 lib/db/migrations/meta/0000_snapshot.json diff --git a/lib/db/migrations/0000_dizzy_madame_masque.sql b/lib/db/migrations/0000_dizzy_madame_masque.sql new file mode 100644 index 0000000..2a6f20e --- /dev/null +++ b/lib/db/migrations/0000_dizzy_madame_masque.sql @@ -0,0 +1,156 @@ +CREATE TABLE IF NOT EXISTS "Charge" ( + "id" text PRIMARY KEY NOT NULL, + "userId" varchar(42) NOT NULL, + "status" varchar DEFAULT 'NEW' NOT NULL, + "product" varchar DEFAULT 'STARTERKIT' NOT NULL, + "payerAddress" varchar(42), + "amount" text NOT NULL, + "currency" text NOT NULL, + "createdAt" timestamp NOT NULL, + "confirmedAt" timestamp, + "expiresAt" timestamp, + "transactionHash" text +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "Chat" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "createdAt" timestamp NOT NULL, + "title" text NOT NULL, + "userId" varchar(42) NOT NULL, + "visibility" varchar DEFAULT 'private' NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "Document" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL, + "createdAt" timestamp NOT NULL, + "title" text NOT NULL, + "content" text, + "text" varchar DEFAULT 'text' NOT NULL, + "userId" varchar(42) NOT NULL, + CONSTRAINT "Document_id_createdAt_pk" PRIMARY KEY("id","createdAt") +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "Message" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "chatId" uuid NOT NULL, + "role" varchar NOT NULL, + "content" json NOT NULL, + "createdAt" timestamp NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "StarterKit" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "creatorId" varchar(42), + "claimerId" varchar(42), + "chargeId" text, + "createdAt" timestamp NOT NULL, + "claimedAt" timestamp, + "value" bigint NOT NULL, + "balance" bigint DEFAULT 0 NOT NULL, + "deletedAt" timestamp +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "Suggestion" ( + "id" uuid DEFAULT gen_random_uuid() NOT NULL, + "documentId" uuid NOT NULL, + "documentCreatedAt" timestamp NOT NULL, + "originalText" text NOT NULL, + "suggestedText" text NOT NULL, + "description" text, + "isResolved" boolean DEFAULT false NOT NULL, + "userId" varchar(42) NOT NULL, + "createdAt" timestamp NOT NULL, + CONSTRAINT "Suggestion_id_pk" PRIMARY KEY("id") +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "User" ( + "id" varchar(42) PRIMARY KEY NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "UserKnowledge" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "userId" varchar(42) NOT NULL, + "type" varchar NOT NULL, + "content" json NOT NULL, + "createdAt" timestamp NOT NULL, + "deletedAt" timestamp +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "Vote" ( + "chatId" uuid NOT NULL, + "messageId" uuid NOT NULL, + "isUpvoted" boolean NOT NULL, + CONSTRAINT "Vote_chatId_messageId_pk" PRIMARY KEY("chatId","messageId") +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Charge" ADD CONSTRAINT "Charge_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Chat" ADD CONSTRAINT "Chat_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Document" ADD CONSTRAINT "Document_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Message" ADD CONSTRAINT "Message_chatId_Chat_id_fk" FOREIGN KEY ("chatId") REFERENCES "public"."Chat"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_creatorId_User_id_fk" FOREIGN KEY ("creatorId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_claimerId_User_id_fk" FOREIGN KEY ("claimerId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "StarterKit" ADD CONSTRAINT "StarterKit_chargeId_Charge_id_fk" FOREIGN KEY ("chargeId") REFERENCES "public"."Charge"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Suggestion" ADD CONSTRAINT "Suggestion_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Suggestion" ADD CONSTRAINT "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk" FOREIGN KEY ("documentId","documentCreatedAt") REFERENCES "public"."Document"("id","createdAt") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "UserKnowledge" ADD CONSTRAINT "UserKnowledge_userId_User_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Vote" ADD CONSTRAINT "Vote_chatId_Chat_id_fk" FOREIGN KEY ("chatId") REFERENCES "public"."Chat"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Vote" ADD CONSTRAINT "Vote_messageId_Message_id_fk" FOREIGN KEY ("messageId") REFERENCES "public"."Message"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/lib/db/migrations/meta/0000_snapshot.json b/lib/db/migrations/meta/0000_snapshot.json new file mode 100644 index 0000000..f90441c --- /dev/null +++ b/lib/db/migrations/meta/0000_snapshot.json @@ -0,0 +1,637 @@ +{ + "id": "a3b394b5-485a-4c1b-a4cb-8097d87871df", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.Charge": { + "name": "Charge", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "varchar(42)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar", + "primaryKey": false, + "notNull": true, + "default": "'NEW'" + }, + "product": { + "name": "product", + "type": "varchar", + "primaryKey": false, + "notNull": true, + "default": "'STARTERKIT'" + }, + "payerAddress": { + "name": "payerAddress", + "type": "varchar(42)", + "primaryKey": false, + "notNull": false + }, + "amount": { + "name": "amount", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "confirmedAt": { + "name": "confirmedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "transactionHash": { + "name": "transactionHash", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Charge_userId_User_id_fk": { + "name": "Charge_userId_User_id_fk", + "tableFrom": "Charge", + "tableTo": "User", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.Chat": { + "name": "Chat", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "varchar(42)", + "primaryKey": false, + "notNull": true + }, + "visibility": { + "name": "visibility", + "type": "varchar", + "primaryKey": false, + "notNull": true, + "default": "'private'" + } + }, + "indexes": {}, + "foreignKeys": { + "Chat_userId_User_id_fk": { + "name": "Chat_userId_User_id_fk", + "tableFrom": "Chat", + "tableTo": "User", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.Document": { + "name": "Document", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "text": { + "name": "text", + "type": "varchar", + "primaryKey": false, + "notNull": true, + "default": "'text'" + }, + "userId": { + "name": "userId", + "type": "varchar(42)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "Document_userId_User_id_fk": { + "name": "Document_userId_User_id_fk", + "tableFrom": "Document", + "tableTo": "User", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "Document_id_createdAt_pk": { + "name": "Document_id_createdAt_pk", + "columns": [ + "id", + "createdAt" + ] + } + }, + "uniqueConstraints": {} + }, + "public.Message": { + "name": "Message", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chatId": { + "name": "chatId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "Message_chatId_Chat_id_fk": { + "name": "Message_chatId_Chat_id_fk", + "tableFrom": "Message", + "tableTo": "Chat", + "columnsFrom": [ + "chatId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.StarterKit": { + "name": "StarterKit", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "creatorId": { + "name": "creatorId", + "type": "varchar(42)", + "primaryKey": false, + "notNull": false + }, + "claimerId": { + "name": "claimerId", + "type": "varchar(42)", + "primaryKey": false, + "notNull": false + }, + "chargeId": { + "name": "chargeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "claimedAt": { + "name": "claimedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "balance": { + "name": "balance", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "deletedAt": { + "name": "deletedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "StarterKit_creatorId_User_id_fk": { + "name": "StarterKit_creatorId_User_id_fk", + "tableFrom": "StarterKit", + "tableTo": "User", + "columnsFrom": [ + "creatorId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "StarterKit_claimerId_User_id_fk": { + "name": "StarterKit_claimerId_User_id_fk", + "tableFrom": "StarterKit", + "tableTo": "User", + "columnsFrom": [ + "claimerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "StarterKit_chargeId_Charge_id_fk": { + "name": "StarterKit_chargeId_Charge_id_fk", + "tableFrom": "StarterKit", + "tableTo": "Charge", + "columnsFrom": [ + "chargeId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.Suggestion": { + "name": "Suggestion", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "documentId": { + "name": "documentId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "documentCreatedAt": { + "name": "documentCreatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "originalText": { + "name": "originalText", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "suggestedText": { + "name": "suggestedText", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isResolved": { + "name": "isResolved", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "userId": { + "name": "userId", + "type": "varchar(42)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "Suggestion_userId_User_id_fk": { + "name": "Suggestion_userId_User_id_fk", + "tableFrom": "Suggestion", + "tableTo": "User", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk": { + "name": "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk", + "tableFrom": "Suggestion", + "tableTo": "Document", + "columnsFrom": [ + "documentId", + "documentCreatedAt" + ], + "columnsTo": [ + "id", + "createdAt" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "Suggestion_id_pk": { + "name": "Suggestion_id_pk", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.User": { + "name": "User", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(42)", + "primaryKey": true, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.UserKnowledge": { + "name": "UserKnowledge", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "userId": { + "name": "userId", + "type": "varchar(42)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "deletedAt": { + "name": "deletedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "UserKnowledge_userId_User_id_fk": { + "name": "UserKnowledge_userId_User_id_fk", + "tableFrom": "UserKnowledge", + "tableTo": "User", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.Vote": { + "name": "Vote", + "schema": "", + "columns": { + "chatId": { + "name": "chatId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "messageId": { + "name": "messageId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "isUpvoted": { + "name": "isUpvoted", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "Vote_chatId_Chat_id_fk": { + "name": "Vote_chatId_Chat_id_fk", + "tableFrom": "Vote", + "tableTo": "Chat", + "columnsFrom": [ + "chatId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "Vote_messageId_Message_id_fk": { + "name": "Vote_messageId_Message_id_fk", + "tableFrom": "Vote", + "tableTo": "Message", + "columnsFrom": [ + "messageId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "Vote_chatId_messageId_pk": { + "name": "Vote_chatId_messageId_pk", + "columns": [ + "chatId", + "messageId" + ] + } + }, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/lib/db/migrations/meta/_journal.json b/lib/db/migrations/meta/_journal.json index eaa8fcf..9527088 100644 --- a/lib/db/migrations/meta/_journal.json +++ b/lib/db/migrations/meta/_journal.json @@ -1,5 +1,13 @@ { "version": "7", "dialect": "postgresql", - "entries": [] + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1739101065473, + "tag": "0000_dizzy_madame_masque", + "breakpoints": true + } + ] } \ No newline at end of file diff --git a/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts b/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts index a4be9d9..d2c823e 100644 --- a/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts +++ b/lib/web3/agentkit/action-providers/basename/basenameActionProvider.ts @@ -26,7 +26,11 @@ import { REGISTRAR_ABI, BASE_REGISTRAR_TRANSFER_ABI, } from "./constants"; -import { RegisterBasenameSchema, TransferBasenameSchema } from "./schemas"; +import { + RegisterBasenameSchema, + TransferBasenameSchema, + RegisterAndTransferBasenameSchema, +} from "./schemas"; /** * Action provider for registering Basenames. @@ -258,6 +262,58 @@ The agent must have a wallet connected that owns the Basename. The transfer will } } + /** + * Registers a Basename and immediately transfers it to another address. + * + * @param wallet - The wallet to use for the registration and transfer. + * @param args - The arguments for the registration and transfer. + * @returns A string indicating the success or failure of both operations. + */ + + @CreateAction({ + name: "register_and_transfer_basename", + description: ` +This tool will register a Basename and immediately transfer it to a new owner. +When your network ID is 'base-mainnet', the name must end with .base.eth, and when your network ID is 'base-sepolia', it must end with .basetest.eth. +The tool will: +1. Register the Basename to the agent's wallet +2. Transfer ownership to the specified destination address +`, + schema: RegisterAndTransferBasenameSchema, + }) + async registerAndTransfer( + wallet: EvmWalletProvider, + args: z.infer + ): Promise { + try { + // First register the basename + const registerResult = await this.register(wallet, { + basename: args.basename, + amount: args.amount, + }); + + if (!registerResult.startsWith("Successfully")) { + throw new Error(registerResult); + } + + // Then transfer it + const isMainnet = wallet.getNetwork().networkId === "base-mainnet"; + const transferResult = await this.transfer(wallet, { + basename: args.basename, + destination: args.destination, + contractAddress: isMainnet + ? BASENAMES_BASE_REGISTRAR_ADDRESS_MAINNET + : BASENAMES_BASE_REGISTRAR_ADDRESS_TESTNET, + }); + + return `${registerResult}\n${transferResult}`; + } catch (error) { + return `Error in register and transfer process: ${ + error instanceof Error ? error.message : String(error) + }`; + } + } + /** * Checks if the Basename action provider supports the given network. * diff --git a/lib/web3/agentkit/action-providers/basename/schemas.ts b/lib/web3/agentkit/action-providers/basename/schemas.ts index a1bb1c8..4c50d0c 100644 --- a/lib/web3/agentkit/action-providers/basename/schemas.ts +++ b/lib/web3/agentkit/action-providers/basename/schemas.ts @@ -29,3 +29,9 @@ export const TransferBasenameSchema = z }) .strip() .describe("Instructions for transferring basename"); + +export const RegisterAndTransferBasenameSchema = z.object({ + basename: z.string().min(1), + amount: z.string(), + destination: z.string(), +}); From 2e0365970af873b430580e2e2fe800b4f9855cd3 Mon Sep 17 00:00:00 2001 From: azf20 Date: Sun, 9 Feb 2025 15:55:53 +0000 Subject: [PATCH 15/16] add basenames --- lib/ai/prompts/constants/starter-kit.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ai/prompts/constants/starter-kit.ts b/lib/ai/prompts/constants/starter-kit.ts index 93cf009..2b8901c 100644 --- a/lib/ai/prompts/constants/starter-kit.ts +++ b/lib/ai/prompts/constants/starter-kit.ts @@ -20,7 +20,7 @@ Each starter kit entitles the user to the following: Swooping Ethereum - tokenAddress: 0xe4850d823d10d9b79282e432e25eab9271d09684 - tokenId: "1" - Link to learn more: https://zora.co/collect/base:0xe4850d823d10d9b79282e432e25eab9271d09684/1 Hiraodai 平尾台 - tokenAddress: 0x7fd9e14f8379a1a1dad05fc347aecb29da0f80bd - tokenId: "4" - Link to learn more: https://zora.co/collect/base:0x7fd9e14f8379a1a1dad05fc347aecb29da0f80bd/4 Eclipse Reclaimed - Seizing Destiny - tokenAddress: 0xc7b47122603dc51a877576fab697a5285d22c503 - tokenId: "9" - Link to learn more: https://zora.co/collect/base:0xc7b47122603dc51a877576fab697a5285d22c503/9 -When providing these options, add a userAction with the following arguments: +When discussing the NFTs, add a userAction with the following arguments for the relevant NFTs: { "contractAddress": "0x123...", "tokenId": "1", @@ -31,4 +31,7 @@ When providing these options, add a userAction with the following arguments: - Receiving an airdrop of one ERC20 token, sent to their wallet address. They can choose from the following options: 1,000 FLNCHY (amount for erc20 transfer tool: 1000000000000000000000) - tokenAddress:0x1c93d155bd388241f9ab5df500d69eb529ce9583 - Flaunch is a new memecoin platform built on Base and Uniswap V4! Link to learn more: https://flaunch.gg/base/coin/0x1c93d155bd388241f9ab5df500d69eb529ce9583 0.1 AERO (amount for erc20 transfer tool: 100000000000000000) - tokenAddress: 0x940181a94a35a4569e4529a3cdfb74e38fd98631 - AERO provides best-in-class Defi on Base! Link to learn more: https://aerodrome.finance/swap?from=0x940181a94a35a4569e4529a3cdfb74e38fd98631&to=eth&chain0=8453&chain1=8453 + +- Creating one Basename of the user's choice, and transferring it to their wallet address. You should ask the user for their name, and confirm that they want to create a Basename. When sharing a basename, share as a link: [](https://www.base.org/name/runningoutoftime) + `; From bf9d0687910153b1947a8b3690b98240e3c3e4b2 Mon Sep 17 00:00:00 2001 From: "escottalexander@gmail.com" Date: Sun, 9 Feb 2025 10:56:12 -0500 Subject: [PATCH 16/16] change chain --- lib/web3/agentkit/action-providers/safe/index.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/web3/agentkit/action-providers/safe/index.ts b/lib/web3/agentkit/action-providers/safe/index.ts index 381142f..8752bcc 100644 --- a/lib/web3/agentkit/action-providers/safe/index.ts +++ b/lib/web3/agentkit/action-providers/safe/index.ts @@ -7,7 +7,7 @@ import Safe, { SigningMethod } from '@safe-global/protocol-kit' import { waitForTransactionReceipt } from 'viem/actions' -import { baseSepolia } from 'viem/chains' +import { baseSepolia, base } from 'viem/chains' import { CreateSafeSchema, CreateSafeTransactionSchema, ExecuteSafeTransactionSchema, SignSafeTransactionSchema } from './schemas'; import { z } from 'zod'; import { saveSafeTransaction, getSafeTransactionByHash } from '@/lib/db/queries'; @@ -47,6 +47,7 @@ export class SafeActionProvider extends ActionProvider { args: z.infer ): Promise { try { + const chain = process.env.NEXT_PUBLIC_ACTIVE_CHAIN === "base" ? base : baseSepolia; const safeAccountConfig: SafeAccountConfig = { owners: args.owners, threshold: args.threshold @@ -59,7 +60,7 @@ export class SafeActionProvider extends ActionProvider { }; const protocolKit = await Safe.init({ - provider: baseSepolia.rpcUrls.default.http[0], + provider: chain.rpcUrls.default.http[0], signer: walletProvider.getAddress(), predictedSafe, onchainAnalytics // Optional @@ -79,7 +80,7 @@ export class SafeActionProvider extends ActionProvider { to: deploymentTransaction.to, value: BigInt(deploymentTransaction.value), data: deploymentTransaction.data as `0x${string}`, - chain: baseSepolia + chain }); if (!tx) { @@ -126,9 +127,10 @@ export class SafeActionProvider extends ActionProvider { args: z.infer ): Promise { try { + const chain = process.env.NEXT_PUBLIC_ACTIVE_CHAIN === "base" ? base : baseSepolia; const signerAddress = walletProvider.getAddress(); const protocolKit = await Safe.init({ - provider: baseSepolia.rpcUrls.default.http[0], + provider: chain.rpcUrls.default.http[0], signer: signerAddress, safeAddress: args.safeAddress }); @@ -206,8 +208,9 @@ export class SafeActionProvider extends ActionProvider { args: z.infer ): Promise { try { + const chain = process.env.NEXT_PUBLIC_ACTIVE_CHAIN === "base" ? base : baseSepolia; const protocolKit = await Safe.init({ - provider: baseSepolia.rpcUrls.default.http[0], + provider: chain.rpcUrls.default.http[0], signer: walletProvider.getAddress(), safeAddress: args.safeAddress }); @@ -222,7 +225,7 @@ export class SafeActionProvider extends ActionProvider { to: args.safeAddress as `0x${string}`, value: 0n, data: encodedTransaction + onchainIdentifier as `0x${string}`, - chain: baseSepolia + chain }; const client =