diff --git a/.env.example b/.env.example index f16f77609fe..c3e3e820967 100644 --- a/.env.example +++ b/.env.example @@ -91,5 +91,10 @@ STARKNET_ADDRESS= STARKNET_PRIVATE_KEY= STARKNET_RPC_URL= -# Coinbase Commerce -COINBASE_COMMERCE_KEY= +# Coinbase +COINBASE_COMMERCE_KEY= # from coinbase developer portal +COINBASE_API_KEY= # from coinbase developer portal +COINBASE_PRIVATE_KEY= # from coinbase developer portal +# if not configured it will be generated and written to runtime.character.settings.secrets.COINBASE_GENERATED_WALLET_ID and runtime.character.settings.secrets.COINBASE_GENERATED_WALLET_HEX_SEED +COINBASE_GENERATED_WALLET_ID= # not your address but the wallet id from generating a wallet through the plugin +COINBASE_GENERATED_WALLET_HEX_SEED= # not your address but the wallet hex seed from generating a wallet through the plugin and calling export diff --git a/.gitignore b/.gitignore index fdecd8b95f2..c1bf4fc6dc1 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ characters/ packages/core/src/providers/cache packages/core/src/providers/cache/* cache/* +packages/plugin-coinbase/src/plugins/transactions.csv +packages/plugin-coinbase/package-lock.json diff --git a/agent/src/index.ts b/agent/src/index.ts index f172f87bcc4..f76ae1d8889 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -25,7 +25,10 @@ import { import { bootstrapPlugin } from "@ai16z/plugin-bootstrap"; import { solanaPlugin } from "@ai16z/plugin-solana"; import { nodePlugin } from "@ai16z/plugin-node"; -import { coinbaseCommercePlugin } from "@ai16z/plugin-coinbase"; +import { + coinbaseCommercePlugin, + coinbaseMassPaymentsPlugin, +} from "@ai16z/plugin-coinbase"; import Database from "better-sqlite3"; import fs from "fs"; import readline from "readline"; @@ -255,6 +258,12 @@ export function createAgent( process.env.COINBASE_COMMERCE_KEY ? coinbaseCommercePlugin : null, + (character.settings.secrets?.COINBASE_API_KEY || + process.env.COINBASE_API_KEY) && + (character.settings.secrets?.COINBASE_PRIVATE_KEY || + process.env.COINBASE_PRIVATE_KEY) + ? coinbaseMassPaymentsPlugin + : null, ].filter(Boolean), providers: [], actions: [], diff --git a/docs/docs/packages/plugins.md b/docs/docs/packages/plugins.md index 56f393c8ae3..5dd1d1ec94a 100644 --- a/docs/docs/packages/plugins.md +++ b/docs/docs/packages/plugins.md @@ -196,7 +196,7 @@ Integrates Solana blockchain functionality: - `walletProvider` - Wallet management - `trustScoreProvider` - Transaction trust metrics -#### 5. Coinbase Commerce Plugin (`@eliza/plugin-coinbase-commerce`) +#### 5. Coinbase Commerce Plugin (`@eliza/plugin-coinbase`) Integrates Coinbase Commerce for payment and transaction management: @@ -211,6 +211,219 @@ This plugin enables Eliza to interact with the Coinbase Commerce API to create a --- +#### 6. Coinbase MassPayments Plugin (`@eliza/plugin-coinbase`) + +This plugin facilitates the processing of cryptocurrency mass payouts using the Coinbase SDK. It enables the creation and management of mass payouts to multiple wallet addresses, logging all transaction details to a CSV file for further analysis. + +**Actions:** + +- `SEND_MASS_PAYOUT` + Sends cryptocurrency mass payouts to multiple wallet addresses. + - **Inputs**: + - `receivingAddresses` (array of strings): Wallet addresses to receive funds. + - `transferAmount` (number): Amount to send to each address (in smallest currency unit, e.g., Wei for ETH). + - `assetId` (string): Cryptocurrency asset ID (e.g., `ETH`, `BTC`). + - `network` (string): Blockchain network (e.g., `base`, `sol`, `eth`, `arb`, `pol`). + - **Outputs**: Logs transaction results (success/failure) in a CSV file. + - **Example**: + ```json + { + "receivingAddresses": [ + "0xA0ba2ACB5846A54834173fB0DD9444F756810f06", + "0xF14F2c49aa90BaFA223EE074C1C33b59891826bF" + ], + "transferAmount": 5000000000000000, + "assetId": "ETH", + "network": "eth" + } + ``` + +**Providers:** + +- `massPayoutProvider` + Retrieves details of past transactions from the generated CSV file. + - **Outputs**: A list of transaction records including the following fields: + - `address`: Recipient wallet address. + - `amount`: Amount sent. + - `status`: Transaction status (`Success` or `Failed`). + - `errorCode`: Error code (if any). + - `transactionUrl`: URL for transaction details (if available). + +**Description:** + +The Coinbase MassPayments plugin streamlines cryptocurrency distribution, ensuring efficient and scalable payouts to multiple recipients on supported blockchain networks. + +Supported networks: + +- `base` (Base blockchain) +- `sol` (Solana) +- `eth` (Ethereum) +- `arb` (Arbitrum) +- `pol` (Polygon) + +**Usage Instructions:** + +1. **Configure the Plugin** + Add the plugin to your character’s configuration: + + ```typescript + import { coinbaseMassPaymentsPlugin } from "@eliza/plugin-coinbase-masspayments"; + + const character = { + plugins: [coinbaseMassPaymentsPlugin], + }; + ``` + +2. **Ensure Secure Configuration** + Set the following environment variables or runtime settings to ensure the plugin functions securely: + + - `COINBASE_API_KEY`: API key for Coinbase SDK. + - `COINBASE_PRIVATE_KEY`: Private key for secure transactions. + +--- + +### Wallet Management + +The plugin automatically handles wallet creation or uses an existing wallet if the required details are provided during the first run. + +1. **Wallet Generation on First Run** + If no wallet information is provided (`COINBASE_GENERATED_WALLET_HEX_SEED` and `COINBASE_GENERATED_WALLET_ID`), the plugin will: + + - **Generate a new wallet** using the Coinbase SDK. + - Automatically **export the wallet details** (`seed` and `walletId`) and securely store them in `runtime.character.settings.secrets` or other configured storage. + - Log the wallet’s default address for reference. + - If the character file does not exist, the wallet details are saved to a characters/charactername-seed.txt file in the characters directory with a note indicating that the user must manually add these details to settings.secrets or the .env file. + +2. **Using an Existing Wallet** + If wallet information is available during the first run: + - Provide `COINBASE_GENERATED_WALLET_HEX_SEED` and `COINBASE_GENERATED_WALLET_ID` via `runtime.character.settings.secrets` or environment variables. + - The plugin will **import the wallet** and use it for processing mass payouts. + +--- + +### Required Configurations + +The following configurations must be provided for wallet management: + +- **Environment Variables or Secrets**: + - `COINBASE_GENERATED_WALLET_HEX_SEED`: Hexadecimal seed of the wallet. + - `COINBASE_GENERATED_WALLET_ID`: Unique wallet ID. + - These variables must be securely stored in `runtime.character.settings.secrets` or as environment variables. + +--- + +### Wallet Creation Process + +1. **Automatic Wallet Creation** + When no wallet details are available: + + - A new wallet is created using the Coinbase SDK. + - The wallet’s `seed` and `walletId` are retrieved using the following logic: + ```typescript + const walletData: WalletData = wallet.export(); + runtime.character.settings.secrets.COINBASE_GENERATED_WALLET_HEX_SEED = + walletData.seed; + runtime.character.settings.secrets.COINBASE_GENERATED_WALLET_ID = + walletData.walletId; + ``` + - The default wallet address is logged: + ```typescript + const walletAddress = wallet.getDefaultAddress(); + elizaLogger.log("Created and stored new wallet:", walletAddress); + ``` + +2. **Using Existing Wallet Details** + When the wallet details are provided: + - The wallet is imported using the following logic: + ```typescript + wallet = await Wallet.import({ + seed: storedSeed, + walletId: storedWalletId, + }); + elizaLogger.log("Imported existing wallet:", wallet.getDefaultAddress()); + ``` + +--- + +### Example Configuration + +#### Automatic Wallet Generation: + +No existing wallet information is passed. The plugin creates and stores a new wallet: + +```typescript +runtime.character.settings.secrets = { + // Empty settings for first run +}; +``` + +Output Log: + +```plaintext +[INFO] Created and stored new wallet: 0x1234567890abcdef1234567890abcdef12345678 +``` + +#### Using Existing Wallet Information: + +Existing wallet details are passed into the runtime: + +```typescript +runtime.character.settings.secrets = { + COINBASE_GENERATED_WALLET_HEX_SEED: + "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890", + COINBASE_GENERATED_WALLET_ID: "wallet-id-123", +}; +``` + +Output Log: + +```plaintext +[INFO] Imported existing wallet: 0x1234567890abcdef1234567890abcdef12345678 +``` + +--- + +3. **Example Call** + An example of using the `SEND_MASS_PAYOUT` action: + + ```typescript + const response = await runtime.triggerAction("SEND_MASS_PAYOUT", { + receivingAddresses: [ + "0xA0ba2ACB5846A54834173fB0DD9444F756810f06", + "0xF14F2c49aa90BaFA223EE074C1C33b59891826bF", + ], + transferAmount: 5000000000000000, // 0.005 ETH + assetId: "ETH", + network: "eth", + }); + console.log("Mass payout response:", response); + ``` + +4. **Transaction Logging** + All transactions (successful and failed) are logged to a `transactions.csv` file in the plugin’s working directory: + ```plaintext + Address,Amount,Status,Error Code,Transaction URL + 0xA0ba2ACB5846A54834173fB0DD9444F756810f06,5000000000000000,Success,,https://etherscan.io/tx/0x... + ``` + +**Example Output:** + +When successful, a response similar to the following will be returned: + +```json +{ + "text": "Mass payouts completed successfully.\n- Successful Transactions: 2\n- Failed Transactions: 0\nCheck the CSV file for more details." +} +``` + +**Best Practices:** + +- **Secure Secrets Storage**: Ensure `COINBASE_API_KEY` and `COINBASE_PRIVATE_KEY` are stored securely in `runtime.character.settings.secrets` or environment variables. Either add `COINBASE_GENERATED_WALLET_HEX_SEED`, and `COINBASE_GENERATED_WALLET_ID` from a previous run, or it will be dynamically created +- **Validation**: Always validate input parameters, especially `receivingAddresses` and `network`, to ensure compliance with expected formats and supported networks. +- **Error Handling**: Monitor logs for failed transactions or errors in the payout process and adjust retry logic as needed. + +--- + ### Writing Custom Plugins Create a new plugin by implementing the Plugin interface: diff --git a/package.json b/package.json index 707aa9325cd..679b1a8f0b2 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,8 @@ "test": "bash ./scripts/test.sh" }, "devDependencies": { - "@commitlint/cli": "18.6.1", - "@commitlint/config-conventional": "18.6.3", + "@commitlint/cli": "^18.4.4", + "@commitlint/config-conventional": "^18.4.4", "lerna": "8.1.5", "only-allow": "1.2.1", "concurrently": "9.1.0", @@ -34,7 +34,6 @@ "typescript": "5.6.3", "vite": "5.4.11", "vitest": "2.1.5" - }, "pnpm": { "overrides": { @@ -46,9 +45,11 @@ }, "dependencies": { "ollama-ai-provider": "0.16.1", - "optional": "0.1.4", - "sharp": "0.33.5", - "tslog": "4.9.3" + "@coinbase/coinbase-sdk": "^0.10.0", + "csv-parse": "^5.6.0", + "optional": "^0.1.4", + "sharp": "^0.33.5", + "tslog": "^4.9.3" }, "packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee" } diff --git a/packages/plugin-coinbase/src/index.ts b/packages/plugin-coinbase/src/index.ts index d5bf379585f..5df9ee08b5b 100644 --- a/packages/plugin-coinbase/src/index.ts +++ b/packages/plugin-coinbase/src/index.ts @@ -1,438 +1,10 @@ -import { - composeContext, - elizaLogger, - generateObjectV2, - ModelClass, - Provider, -} from "@ai16z/eliza"; -import { - Action, - HandlerCallback, - IAgentRuntime, - Memory, - Plugin, - State, -} from "@ai16z/eliza"; -import { ChargeContent, ChargeSchema, isChargeContent } from "./types"; -import { chargeTemplate, getChargeTemplate } from "./templates"; +import { coinbaseMassPaymentsPlugin } from "./plugins/massPayments"; +import { coinbaseCommercePlugin } from "./plugins/commerce"; -const url = "https://api.commerce.coinbase.com/charges"; -interface ChargeRequest { - name: string; - description: string; - pricing_type: string; - local_price: { - amount: string; - currency: string; - }; -} - -export async function createCharge(apiKey: string, params: ChargeRequest) { - try { - const response = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-CC-Api-Key": apiKey, - }, - body: JSON.stringify(params), - }); - - if (!response.ok) { - throw new Error(`Failed to create charge: ${response.statusText}`); - } - - const data = await response.json(); - return data.data; - } catch (error) { - console.error("Error creating charge:", error); - throw error; - } -} - -// Function to fetch all charges -export async function getAllCharges(apiKey: string) { - try { - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - "X-CC-Api-Key": apiKey, - }, - }); - - if (!response.ok) { - throw new Error( - `Failed to fetch all charges: ${response.statusText}` - ); - } - - const data = await response.json(); - return data.data; - } catch (error) { - console.error("Error fetching charges:", error); - throw error; - } -} - -// Function to fetch details of a specific charge -export async function getChargeDetails(apiKey: string, chargeId: string) { - const getUrl = `${url}${chargeId}`; - - try { - const response = await fetch(getUrl, { - method: "GET", - headers: { - "Content-Type": "application/json", - "X-CC-Api-Key": apiKey, - }, - }); - - if (!response.ok) { - throw new Error( - `Failed to fetch charge details: ${response.statusText}` - ); - } - - const data = await response.json(); - return data; - } catch (error) { - console.error( - `Error fetching charge details for ID ${chargeId}:`, - error - ); - throw error; - } -} - -export const createCoinbaseChargeAction: Action = { - name: "CREATE_CHARGE", - similes: [ - "MAKE_CHARGE", - "INITIATE_CHARGE", - "GENERATE_CHARGE", - "CREATE_TRANSACTION", - "COINBASE_CHARGE", - ], - description: "Create a charge using Coinbase Commerce.", - validate: async (runtime: IAgentRuntime, message: Memory) => { - const coinbaseCommerceKeyOk = !!runtime.getSetting( - "COINBASE_COMMERCE_KEY" - ); - - // Ensure Coinbase Commerce API key is available - return coinbaseCommerceKeyOk; - }, - handler: async ( - runtime: IAgentRuntime, - message: Memory, - state: State, - options: any, - callback: HandlerCallback - ) => { - elizaLogger.log("Composing state for message:", message); - if (!state) { - state = (await runtime.composeState(message)) as State; - } else { - state = await runtime.updateRecentMessageState(state); - } - - const context = composeContext({ - state, - template: chargeTemplate, - }); - - const chargeDetails = await generateObjectV2({ - runtime, - context, - modelClass: ModelClass.SMALL, - schema: ChargeSchema, - }); - if (!isChargeContent(chargeDetails.object)) { - throw new Error("Invalid content"); - } - const charge = chargeDetails.object as ChargeContent; - if (!charge || !charge.price || !charge.type) { - callback( - { - text: "Invalid charge details provided.", - }, - [] - ); - return; - } - - elizaLogger.log("Charge details received:", chargeDetails); - - // Initialize Coinbase Commerce client - - try { - // Create a charge - const chargeResponse = await createCharge( - runtime.getSetting("COINBASE_COMMERCE_KEY"), - { - local_price: { - amount: charge.price.toString(), - currency: charge.currency, - }, - pricing_type: charge.type, - name: charge.name, - description: charge.description, - } - ); - - elizaLogger.log( - "Coinbase Commerce charge created:", - chargeResponse - ); - - callback( - { - text: `Charge created successfully: ${chargeResponse.hosted_url}`, - attachments: [ - { - id: crypto.randomUUID(), - url: chargeResponse.id, - title: "Coinbase Commerce Charge", - description: `Charge ID: ${chargeResponse.id}`, - text: `Pay here: ${chargeResponse.hosted_url}`, - source: "coinbase", - }, - ], - }, - [] - ); - } catch (error) { - elizaLogger.error( - "Error creating Coinbase Commerce charge:", - error - ); - callback( - { - text: "Failed to create a charge. Please try again.", - }, - [] - ); - } - }, - examples: [ - [ - { - user: "{{user1}}", - content: { - text: "Create a charge for $10.00 USD to Chris for dinner", - data: { - local_price: { - amount: "10.00", - currency: "USD", - }, - pricing_type: "fixed_price", - buyer_locale: "en-US", - cancel_url: "https://example.com/cancel", - redirect_url: "https://example.com/success", - }, - }, - }, - { - user: "{{agentName}}", - content: { - text: "Charge created successfully: {{charge.id}} for {{charge.amount}} {{charge.currency}}", - action: "CREATE_CHARGE", - }, - }, - ], - ], -} as Action; - -export const getAllChargesAction: Action = { - name: "GET_ALL_CHARGES", - similes: ["FETCH_ALL_CHARGES", "RETRIEVE_ALL_CHARGES", "LIST_ALL_CHARGES"], - description: "Fetch all charges using Coinbase Commerce.", - validate: async (runtime: IAgentRuntime) => { - const coinbaseCommerceKeyOk = !!runtime.getSetting( - "COINBASE_COMMERCE_KEY" - ); - - // Ensure Coinbase Commerce API key is available - return coinbaseCommerceKeyOk; - }, - handler: async ( - runtime: IAgentRuntime, - message: Memory, - state: State, - options: any, - callback: HandlerCallback - ) => { - try { - elizaLogger.log("Composing state for message:", message); - if (!state) { - state = (await runtime.composeState(message)) as State; - } else { - state = await runtime.updateRecentMessageState(state); - } - const charges = await getAllCharges( - runtime.getSetting("COINBASE_COMMERCE_KEY") - ); - - elizaLogger.log("Fetched all charges:", charges); - - callback( - { - text: `Successfully fetched all charges. Total charges: ${charges.length}`, - }, - [] - ); - } catch (error) { - elizaLogger.error("Error fetching all charges:", error); - callback( - { - text: "Failed to fetch all charges. Please try again.", - }, - [] - ); - } - }, - examples: [ - [ - { - user: "{{user1}}", - content: { text: "Fetch all charges" }, - }, - { - user: "{{agentName}}", - content: { - text: "Successfully fetched all charges.", - action: "GET_ALL_CHARGES", - }, - }, - ], - ], -} as Action; - -export const getChargeDetailsAction: Action = { - name: "GET_CHARGE_DETAILS", - similes: ["FETCH_CHARGE_DETAILS", "RETRIEVE_CHARGE_DETAILS", "GET_CHARGE"], - description: "Fetch details of a specific charge using Coinbase Commerce.", - validate: async (runtime: IAgentRuntime) => { - const coinbaseCommerceKeyOk = !!runtime.getSetting( - "COINBASE_COMMERCE_KEY" - ); - - // Ensure Coinbase Commerce API key is available - return coinbaseCommerceKeyOk; - }, - handler: async ( - runtime: IAgentRuntime, - message: Memory, - state: State, - options: any, - callback: HandlerCallback - ) => { - elizaLogger.log("Composing state for message:", message); - if (!state) { - state = (await runtime.composeState(message)) as State; - } else { - state = await runtime.updateRecentMessageState(state); - } - - const context = composeContext({ - state, - template: getChargeTemplate, - }); - const chargeDetails = await generateObjectV2({ - runtime, - context, - modelClass: ModelClass.SMALL, - schema: ChargeSchema, - }); - if (!isChargeContent(chargeDetails.object)) { - throw new Error("Invalid content"); - } - const charge = chargeDetails.object as ChargeContent; - if (!charge.id) { - callback( - { - text: "Missing charge ID. Please provide a valid charge ID.", - }, - [] - ); - return; - } - - try { - const chargeDetails = await getChargeDetails( - runtime.getSetting("COINBASE_COMMERCE_KEY"), - charge.id - ); - - elizaLogger.log("Fetched charge details:", chargeDetails); - - callback( - { - text: `Successfully fetched charge details for ID: ${charge.id}`, - attachments: [ - { - id: crypto.randomUUID(), - url: chargeDetails.hosted_url, - title: `Charge Details for ${charge.id}`, - description: `Details: ${JSON.stringify(chargeDetails, null, 2)}`, - source: "coinbase", - text: "", - }, - ], - }, - [] - ); - } catch (error) { - elizaLogger.error( - `Error fetching details for charge ID ${charge.id}:`, - error - ); - callback( - { - text: `Failed to fetch details for charge ID: ${charge.id}. Please try again.`, - }, - [] - ); - } - }, - examples: [ - [ - { - user: "{{user1}}", - content: { - text: "Fetch details of charge ID: 123456", - }, - }, - { - user: "{{agentName}}", - content: { - text: "Successfully fetched charge details. {{charge.id}} for {{charge.amount}} {{charge.currency}} to {{charge.name}} for {{charge.description}}", - action: "GET_CHARGE_DETAILS", - }, - }, - ], - ], -}; - -export const chargeProvider: Provider = { - get: async (runtime: IAgentRuntime, message: Memory) => { - const charges = await getAllCharges( - runtime.getSetting("COINBASE_COMMERCE_KEY") - ); - return charges.data; - }, +export const plugins = { + coinbaseMassPaymentsPlugin, + coinbaseCommercePlugin, }; -export const coinbaseCommercePlugin: Plugin = { - name: "coinbaseCommerce", - description: - "Integration with Coinbase Commerce for creating and managing charges.", - actions: [ - createCoinbaseChargeAction, - getAllChargesAction, - getChargeDetailsAction, - ], - evaluators: [], - providers: [chargeProvider], -}; +export * from "./plugins/massPayments"; +export * from "./plugins/commerce"; diff --git a/packages/plugin-coinbase/src/plugins/commerce.ts b/packages/plugin-coinbase/src/plugins/commerce.ts new file mode 100644 index 00000000000..698d94dc15a --- /dev/null +++ b/packages/plugin-coinbase/src/plugins/commerce.ts @@ -0,0 +1,438 @@ +import { + composeContext, + elizaLogger, + generateObjectV2, + ModelClass, + Provider, +} from "@ai16z/eliza"; +import { + Action, + HandlerCallback, + IAgentRuntime, + Memory, + Plugin, + State, +} from "@ai16z/eliza"; +import { ChargeContent, ChargeSchema, isChargeContent } from "../types"; +import { chargeTemplate, getChargeTemplate } from "../templates"; + +const url = "https://api.commerce.coinbase.com/charges"; +interface ChargeRequest { + name: string; + description: string; + pricing_type: string; + local_price: { + amount: string; + currency: string; + }; +} + +export async function createCharge(apiKey: string, params: ChargeRequest) { + try { + const response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CC-Api-Key": apiKey, + }, + body: JSON.stringify(params), + }); + + if (!response.ok) { + throw new Error(`Failed to create charge: ${response.statusText}`); + } + + const data = await response.json(); + return data.data; + } catch (error) { + console.error("Error creating charge:", error); + throw error; + } +} + +// Function to fetch all charges +export async function getAllCharges(apiKey: string) { + try { + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + "X-CC-Api-Key": apiKey, + }, + }); + + if (!response.ok) { + throw new Error( + `Failed to fetch all charges: ${response.statusText}` + ); + } + + const data = await response.json(); + return data.data; + } catch (error) { + console.error("Error fetching charges:", error); + throw error; + } +} + +// Function to fetch details of a specific charge +export async function getChargeDetails(apiKey: string, chargeId: string) { + const getUrl = `${url}${chargeId}`; + + try { + const response = await fetch(getUrl, { + method: "GET", + headers: { + "Content-Type": "application/json", + "X-CC-Api-Key": apiKey, + }, + }); + + if (!response.ok) { + throw new Error( + `Failed to fetch charge details: ${response.statusText}` + ); + } + + const data = await response.json(); + return data; + } catch (error) { + console.error( + `Error fetching charge details for ID ${chargeId}:`, + error + ); + throw error; + } +} + +export const createCoinbaseChargeAction: Action = { + name: "CREATE_CHARGE", + similes: [ + "MAKE_CHARGE", + "INITIATE_CHARGE", + "GENERATE_CHARGE", + "CREATE_TRANSACTION", + "COINBASE_CHARGE", + ], + description: "Create a charge using Coinbase Commerce.", + validate: async (runtime: IAgentRuntime, message: Memory) => { + const coinbaseCommerceKeyOk = !!runtime.getSetting( + "COINBASE_COMMERCE_KEY" + ); + + // Ensure Coinbase Commerce API key is available + return coinbaseCommerceKeyOk; + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + options: any, + callback: HandlerCallback + ) => { + elizaLogger.log("Composing state for message:", message); + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + const context = composeContext({ + state, + template: chargeTemplate, + }); + + const chargeDetails = await generateObjectV2({ + runtime, + context, + modelClass: ModelClass.SMALL, + schema: ChargeSchema, + }); + if (!isChargeContent(chargeDetails.object)) { + throw new Error("Invalid content"); + } + const charge = chargeDetails.object as ChargeContent; + if (!charge || !charge.price || !charge.type) { + callback( + { + text: "Invalid charge details provided.", + }, + [] + ); + return; + } + + elizaLogger.log("Charge details received:", chargeDetails); + + // Initialize Coinbase Commerce client + + try { + // Create a charge + const chargeResponse = await createCharge( + runtime.getSetting("COINBASE_COMMERCE_KEY"), + { + local_price: { + amount: charge.price.toString(), + currency: charge.currency, + }, + pricing_type: charge.type, + name: charge.name, + description: charge.description, + } + ); + + elizaLogger.log( + "Coinbase Commerce charge created:", + chargeResponse + ); + + callback( + { + text: `Charge created successfully: ${chargeResponse.hosted_url}`, + attachments: [ + { + id: crypto.randomUUID(), + url: chargeResponse.id, + title: "Coinbase Commerce Charge", + description: `Charge ID: ${chargeResponse.id}`, + text: `Pay here: ${chargeResponse.hosted_url}`, + source: "coinbase", + }, + ], + }, + [] + ); + } catch (error) { + elizaLogger.error( + "Error creating Coinbase Commerce charge:", + error + ); + callback( + { + text: "Failed to create a charge. Please try again.", + }, + [] + ); + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Create a charge for $10.00 USD to Chris for dinner", + data: { + local_price: { + amount: "10.00", + currency: "USD", + }, + pricing_type: "fixed_price", + buyer_locale: "en-US", + cancel_url: "https://example.com/cancel", + redirect_url: "https://example.com/success", + }, + }, + }, + { + user: "{{agentName}}", + content: { + text: "Charge created successfully: {{charge.id}} for {{charge.amount}} {{charge.currency}}", + action: "CREATE_CHARGE", + }, + }, + ], + ], +} as Action; + +export const getAllChargesAction: Action = { + name: "GET_ALL_CHARGES", + similes: ["FETCH_ALL_CHARGES", "RETRIEVE_ALL_CHARGES", "LIST_ALL_CHARGES"], + description: "Fetch all charges using Coinbase Commerce.", + validate: async (runtime: IAgentRuntime) => { + const coinbaseCommerceKeyOk = !!runtime.getSetting( + "COINBASE_COMMERCE_KEY" + ); + + // Ensure Coinbase Commerce API key is available + return coinbaseCommerceKeyOk; + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + options: any, + callback: HandlerCallback + ) => { + try { + elizaLogger.log("Composing state for message:", message); + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + const charges = await getAllCharges( + runtime.getSetting("COINBASE_COMMERCE_KEY") + ); + + elizaLogger.log("Fetched all charges:", charges); + + callback( + { + text: `Successfully fetched all charges. Total charges: ${charges.length}`, + }, + [] + ); + } catch (error) { + elizaLogger.error("Error fetching all charges:", error); + callback( + { + text: "Failed to fetch all charges. Please try again.", + }, + [] + ); + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { text: "Fetch all charges" }, + }, + { + user: "{{agentName}}", + content: { + text: "Successfully fetched all charges.", + action: "GET_ALL_CHARGES", + }, + }, + ], + ], +} as Action; + +export const getChargeDetailsAction: Action = { + name: "GET_CHARGE_DETAILS", + similes: ["FETCH_CHARGE_DETAILS", "RETRIEVE_CHARGE_DETAILS", "GET_CHARGE"], + description: "Fetch details of a specific charge using Coinbase Commerce.", + validate: async (runtime: IAgentRuntime) => { + const coinbaseCommerceKeyOk = !!runtime.getSetting( + "COINBASE_COMMERCE_KEY" + ); + + // Ensure Coinbase Commerce API key is available + return coinbaseCommerceKeyOk; + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + options: any, + callback: HandlerCallback + ) => { + elizaLogger.log("Composing state for message:", message); + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + const context = composeContext({ + state, + template: getChargeTemplate, + }); + const chargeDetails = await generateObjectV2({ + runtime, + context, + modelClass: ModelClass.SMALL, + schema: ChargeSchema, + }); + if (!isChargeContent(chargeDetails.object)) { + throw new Error("Invalid content"); + } + const charge = chargeDetails.object as ChargeContent; + if (!charge.id) { + callback( + { + text: "Missing charge ID. Please provide a valid charge ID.", + }, + [] + ); + return; + } + + try { + const chargeDetails = await getChargeDetails( + runtime.getSetting("COINBASE_COMMERCE_KEY"), + charge.id + ); + + elizaLogger.log("Fetched charge details:", chargeDetails); + + callback( + { + text: `Successfully fetched charge details for ID: ${charge.id}`, + attachments: [ + { + id: crypto.randomUUID(), + url: chargeDetails.hosted_url, + title: `Charge Details for ${charge.id}`, + description: `Details: ${JSON.stringify(chargeDetails, null, 2)}`, + source: "coinbase", + text: "", + }, + ], + }, + [] + ); + } catch (error) { + elizaLogger.error( + `Error fetching details for charge ID ${charge.id}:`, + error + ); + callback( + { + text: `Failed to fetch details for charge ID: ${charge.id}. Please try again.`, + }, + [] + ); + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Fetch details of charge ID: 123456", + }, + }, + { + user: "{{agentName}}", + content: { + text: "Successfully fetched charge details. {{charge.id}} for {{charge.amount}} {{charge.currency}} to {{charge.name}} for {{charge.description}}", + action: "GET_CHARGE_DETAILS", + }, + }, + ], + ], +}; + +export const chargeProvider: Provider = { + get: async (runtime: IAgentRuntime, message: Memory) => { + const charges = await getAllCharges( + runtime.getSetting("COINBASE_COMMERCE_KEY") + ); + return charges.data; + }, +}; + +export const coinbaseCommercePlugin: Plugin = { + name: "coinbaseCommerce", + description: + "Integration with Coinbase Commerce for creating and managing charges.", + actions: [ + createCoinbaseChargeAction, + getAllChargesAction, + getChargeDetailsAction, + ], + evaluators: [], + providers: [chargeProvider], +}; diff --git a/packages/plugin-coinbase/src/plugins/massPayments.ts b/packages/plugin-coinbase/src/plugins/massPayments.ts new file mode 100644 index 00000000000..3f36cf9230c --- /dev/null +++ b/packages/plugin-coinbase/src/plugins/massPayments.ts @@ -0,0 +1,519 @@ +import { Coinbase, Wallet, WalletData } from "@coinbase/coinbase-sdk"; +import { + composeContext, + elizaLogger, + generateObjectV2, + ModelClass, + Action, + IAgentRuntime, + Memory, + Provider, + State, + HandlerCallback, + Plugin, +} from "@ai16z/eliza"; +import { + TransferSchema, + isTransferContent, + TransferContent, + Transaction, +} from "../types"; +import { transferTemplate } from "../templates"; +import { readFile } from "fs/promises"; +import { parse } from "csv-parse/sync"; +import path from "path"; +import { fileURLToPath } from "url"; +import fs from "fs"; +import { createArrayCsvWriter } from "csv-writer"; + +// Dynamically resolve the file path to the src/plugins directory +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const baseDir = path.resolve(__dirname, "../../plugin-coinbase/src/plugins"); +const csvFilePath = path.join(baseDir, "transactions.csv"); + +export const massPayoutProvider: Provider = { + get: async (_runtime: IAgentRuntime, _message: Memory) => { + try { + elizaLogger.log("Reading CSV file from:", csvFilePath); + + // Check if the file exists; if not, create it with headers + if (!fs.existsSync(csvFilePath)) { + elizaLogger.warn("CSV file not found. Creating a new one."); + const csvWriter = createArrayCsvWriter({ + path: csvFilePath, + header: [ + "Address", + "Amount", + "Status", + "Error Code", + "Transaction URL", + ], + }); + await csvWriter.writeRecords([]); // Create an empty file with headers + elizaLogger.log("New CSV file created with headers."); + } + + // Read and parse the CSV file + const csvData = await readFile(csvFilePath, "utf-8"); + const records = parse(csvData, { + columns: true, + skip_empty_lines: true, + }); + + elizaLogger.log("Parsed CSV records:", records); + return records.map((record: any) => ({ + address: record["Address"] || undefined, + amount: parseFloat(record["Amount"]) || undefined, + status: record["Status"] || undefined, + errorCode: record["Error Code"] || "", + transactionUrl: record["Transaction URL"] || "", + })); + } catch (error) { + elizaLogger.error("Error in massPayoutProvider:", error); + return []; + } + }, +}; + +export async function appendTransactionsToCsv(transactions: Transaction[]) { + try { + const csvWriter = createArrayCsvWriter({ + path: csvFilePath, + header: [ + "Address", + "Amount", + "Status", + "Error Code", + "Transaction URL", + ], + append: true, + }); + + const formattedTransactions = transactions.map((transaction) => [ + transaction.address, + transaction.amount.toString(), + transaction.status, + transaction.errorCode || "", + transaction.transactionUrl || "", + ]); + + elizaLogger.log("Writing transactions to CSV:", formattedTransactions); + await csvWriter.writeRecords(formattedTransactions); + elizaLogger.log("All transactions written to CSV successfully."); + } catch (error) { + elizaLogger.error("Error writing transactions to CSV:", error); + } +} + +async function initializeWallet(runtime: IAgentRuntime, networkId: string) { + let wallet: Wallet; + const storedSeed = + runtime.getSetting("COINBASE_GENERATED_WALLET_HEX_SEED") ?? + process.env.COINBASE_GENERATED_WALLET_HEX_SEED; + + const storedWalletId = + runtime.getSetting("COINBASE_GENERATED_WALLET_ID") ?? + process.env.COINBASE_GENERATED_WALLET_ID; + if (!storedSeed || !storedWalletId) { + // No stored seed or wallet ID, creating a new wallet + wallet = await Wallet.create({ networkId }); + + // Export wallet data directly + const walletData: WalletData = wallet.export(); + const walletAddress = await wallet.getDefaultAddress(); + try { + const characterFilePath = `characters/${runtime.character.name.toLowerCase()}.character.json`; + const walletIDSave = await updateCharacterSecrets( + characterFilePath, + "COINBASE_GENERATED_WALLET_ID", + walletData.walletId + ); + const seedSave = await updateCharacterSecrets( + characterFilePath, + "COINBASE_GENERATED_WALLET_HEX_SEED", + walletData.seed + ); + if (walletIDSave && seedSave) { + elizaLogger.log("Successfully updated character secrets."); + } else { + const seedFilePath = `characters/${runtime.character.name.toLowerCase()}-seed.txt`; + elizaLogger.error( + `Failed to update character secrets so adding gitignored ${seedFilePath} file please add it your env or character file and delete:` + ); + // save it to gitignored file + wallet.saveSeed(seedFilePath); + } + } catch (error) { + elizaLogger.error("Error updating character secrets:", error); + throw error; + } + + // Logging wallet creation + elizaLogger.log("Created and stored new wallet:", walletAddress); + } else { + // Importing existing wallet using stored seed and wallet ID + wallet = await Wallet.import({ + seed: storedSeed, + walletId: storedWalletId, + }); + + // Logging wallet import + elizaLogger.log( + "Imported existing wallet:", + await wallet.getDefaultAddress() + ); + } + + return wallet; +} + +async function executeMassPayout( + runtime: IAgentRuntime, + networkId: string, + receivingAddresses: string[], + transferAmount: number, + assetId: string +): Promise { + const transactions: Transaction[] = []; + try { + const sendingWallet = await initializeWallet(runtime, networkId); + for (const address of receivingAddresses) { + elizaLogger.log("Processing payout for address:", address); + if (address) { + try { + // Check balance before initiating transfer + const walletBalance = await sendingWallet.getBalance( + assetId.toLowerCase() + ); + + elizaLogger.log("Wallet balance for asset:", { + assetId, + walletBalance, + }); + + if (walletBalance.lessThan(transferAmount)) { + const insufficientFunds = `Insufficient funds for address ${address}. Required: ${transferAmount}, Available: ${walletBalance}`; + elizaLogger.error(insufficientFunds); + + transactions.push({ + address, + amount: transferAmount, + status: "Failed", + errorCode: insufficientFunds, + transactionUrl: null, + }); + continue; + } + + const transferDetails = { + amount: transferAmount, + assetId: assetId.toLowerCase(), + destination: address, + }; + elizaLogger.log("Initiating transfer:", transferDetails); + + const transfer = + await sendingWallet.createTransfer(transferDetails); + await transfer.wait(); + + const transactionUrl = transfer.getTransactionLink(); + elizaLogger.log("Transfer successful:", { + address, + transactionUrl, + }); + + transactions.push({ + address, + amount: transferAmount, + status: "Success", + errorCode: null, + transactionUrl, + }); + } catch (error) { + elizaLogger.error( + "Error during transfer for address:", + address, + error + ); + transactions.push({ + address, + amount: transferAmount, + status: "Failed", + errorCode: error?.code || "Unknown Error", + transactionUrl: null, + }); + } + } else { + elizaLogger.log("Skipping invalid or empty address."); + transactions.push({ + address: "Invalid or Empty", + amount: transferAmount, + status: "Failed", + errorCode: "Invalid Address", + transactionUrl: null, + }); + } + } + + await appendTransactionsToCsv(transactions); + elizaLogger.log("Finished processing mass payouts."); + return transactions; + } catch (error) { + elizaLogger.error( + "Error initializing sending wallet or processing payouts:", + error + ); + throw error; // Re-throw the error to be caught in the handler + } +} + +// Action for sending mass payouts +export const sendMassPayoutAction: Action = { + name: "SEND_MASS_PAYOUT", + similes: ["BULK_TRANSFER", "DISTRIBUTE_FUNDS", "SEND_PAYMENTS"], + description: + "Sends mass payouts to a list of receiving addresses using a predefined sending wallet and logs all transactions to a CSV file.", + validate: async (runtime: IAgentRuntime, message: Memory) => { + elizaLogger.log("Validating runtime and message..."); + return ( + !!( + runtime.character.settings.secrets?.COINBASE_API_KEY || + process.env.COINBASE_API_KEY + ) && + !!( + runtime.character.settings.secrets?.COINBASE_PRIVATE_KEY || + process.env.COINBASE_PRIVATE_KEY + ) + ); + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + options: any, + callback: HandlerCallback + ) => { + elizaLogger.log("Starting SEND_MASS_PAYOUT handler..."); + try { + Coinbase.configure({ + apiKeyName: + runtime.getSetting("COINBASE_API_KEY") ?? + process.env.COINBASE_API_KEY, + privateKey: + runtime.getSetting("COINBASE_PRIVATE_KEY") ?? + process.env.COINBASE_PRIVATE_KEY, + }); + if (!state) { + state = (await runtime.composeState(message, { + providers: [massPayoutProvider], + })) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + const context = composeContext({ + state, + template: transferTemplate, + }); + + const transferDetails = await generateObjectV2({ + runtime, + context, + modelClass: ModelClass.SMALL, + schema: TransferSchema, + }); + + elizaLogger.log( + "Transfer details generated:", + transferDetails.object + ); + + if (!isTransferContent(transferDetails.object)) { + callback( + { + text: "Invalid transfer details. Please check the inputs.", + }, + [] + ); + return; + } + + const { receivingAddresses, transferAmount, assetId, network } = + transferDetails.object as TransferContent; + + const allowedNetworks = Object.values(Coinbase.networks); + + if ( + !network || + !allowedNetworks.includes(network.toLowerCase() as any) || + !receivingAddresses?.length || + transferAmount <= 0 || + !assetId + ) { + elizaLogger.error("Missing or invalid input parameters:", { + network, + receivingAddresses, + transferAmount, + assetId, + }); + callback( + { + text: `Invalid input parameters. Please ensure: +- Network is one of: ${allowedNetworks.join(", ")}. +- Receiving addresses are provided. +- Transfer amount is greater than zero. +- Asset ID is valid.`, + }, + [] + ); + return; + } + + elizaLogger.log("◎ Starting mass payout..."); + const transactions = await executeMassPayout( + runtime, + network, + receivingAddresses, + transferAmount, + assetId + ); + + const successTransactions = transactions.filter( + (tx) => tx.status === "Success" + ); + const failedTransactions = transactions.filter( + (tx) => tx.status === "Failed" + ); + const successDetails = successTransactions + .map( + (tx) => + `Address: ${tx.address}, Amount: ${tx.amount}, Transaction URL: ${ + tx.transactionUrl || "N/A" + }` + ) + .join("\n"); + const failedDetails = failedTransactions + .map( + (tx) => + `Address: ${tx.address}, Amount: ${tx.amount}, Error Code: ${ + tx.errorCode || "Unknown Error" + }` + ) + .join("\n"); + callback( + { + text: `Mass payouts completed successfully. +- Successful Transactions: ${successTransactions.length} +- Failed Transactions: ${failedTransactions.length} + +Details: +${successTransactions.length > 0 ? `✅ Successful Transactions:\n${successDetails}` : "No successful transactions."} +${failedTransactions.length > 0 ? `❌ Failed Transactions:\n${failedDetails}` : "No failed transactions."} + +Check the CSV file for full details.`, + }, + [] + ); + } catch (error) { + elizaLogger.error("Error during mass payouts:", error); + callback( + { text: "Failed to complete payouts. Please try again." }, + [] + ); + } + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Distribute 0.0001 ETH on base network to 0xA0ba2ACB5846A54834173fB0DD9444F756810f06 and 0xF14F2c49aa90BaFA223EE074C1C33b59891826bF", + }, + }, + { + user: "{{agentName}}", + content: { + text: `Mass payouts completed successfully. +- Successful Transactions: {{2}} +- Failed Transactions: {{1}} + +Details: +✅ Successful Transactions: +Address: 0xABC123..., Amount: 0.005, Transaction URL: https://etherscan.io/tx/... +Address: 0xDEF456..., Amount: 0.005, Transaction URL: https://etherscan.io/tx/... + +❌ Failed Transactions: +Address: 0xGHI789..., Amount: 0.005, Error Code: Insufficient Funds + +Check the CSV file for full details.`, + action: "SEND_MASS_PAYOUT", + }, + }, + ], + ], +}; + +export const coinbaseMassPaymentsPlugin: Plugin = { + name: "automatedPayments", + description: + "Processes mass payouts using Coinbase SDK and logs all transactions (success and failure) to a CSV file. Provides dynamic transaction data through a provider.", + actions: [sendMassPayoutAction], + providers: [massPayoutProvider], +}; + +/** + * Updates a key-value pair in character.settings.secrets. + * @param {string} characterfilePath - The file path to the character. + * @param {string} key - The secret key to update or add. + * @param {string} value - The new value for the secret key. + */ +export async function updateCharacterSecrets( + characterfilePath: string, + key: string, + value: string +): Promise { + try { + const characterFilePath = path.resolve( + process.cwd(), + characterfilePath + ); + + // Check if the character file exists + if (!fs.existsSync(characterFilePath)) { + elizaLogger.error("Character file not found:", characterFilePath); + return false; + } + + // Read the existing character file + const characterData = JSON.parse( + fs.readFileSync(characterFilePath, "utf-8") + ); + + // Ensure settings and secrets exist in the character file + if (!characterData.settings) { + characterData.settings = {}; + } + if (!characterData.settings.secrets) { + characterData.settings.secrets = {}; + } + + // Update or add the key-value pair + characterData.settings.secrets[key] = value; + + // Write the updated data back to the file + fs.writeFileSync( + characterFilePath, + JSON.stringify(characterData, null, 2), + "utf-8" + ); + + console.log( + `Updated ${key} in character.settings.secrets for ${characterFilePath}.` + ); + } catch (error) { + elizaLogger.error("Error updating character secrets:", error); + return false; + } + return true; +} diff --git a/packages/plugin-coinbase/src/templates.ts b/packages/plugin-coinbase/src/templates.ts index 9e574ce37ad..c8226996520 100644 --- a/packages/plugin-coinbase/src/templates.ts +++ b/packages/plugin-coinbase/src/templates.ts @@ -45,3 +45,35 @@ Provide the charge details in the following JSON format after retrieving the cha Here are the recent user messages for context: {{recentMessages}} `; + +export const transferTemplate = ` +Extract the following details for processing a mass payout using the Coinbase SDK: +- **receivingAddresses** (array): A list of wallet addresses receiving the funds. +- **transferAmount** (number): The amount to transfer to each address (in the smallest unit, e.g., Wei for ETH). +- **assetId** (string): The asset ID to transfer (e.g., ETH, BTC). +- **network** (string): The blockchain network to use. Allowed values are: + static networks: { + readonly BaseSepolia: "base-sepolia"; + readonly BaseMainnet: "base-mainnet"; + readonly EthereumHolesky: "ethereum-holesky"; + readonly EthereumMainnet: "ethereum-mainnet"; + readonly PolygonMainnet: "polygon-mainnet"; + readonly SolanaDevnet: "solana-devnet"; + readonly SolanaMainnet: "solana-mainnet"; + readonly ArbitrumMainnet: "arbitrum-mainnet"; + }; + +Provide the details in the following JSON format: + +\`\`\`json +{ + "receivingAddresses": ["", ""], + "transferAmount": , + "assetId": "", + "network": "" +} +\`\`\` + +Here are the recent user messages for context: +{{recentMessages}} +`; diff --git a/packages/plugin-coinbase/src/types.ts b/packages/plugin-coinbase/src/types.ts index 78b0a06a98b..b6d24db7446 100644 --- a/packages/plugin-coinbase/src/types.ts +++ b/packages/plugin-coinbase/src/types.ts @@ -25,3 +25,29 @@ export const isChargeContent = (object: any): object is ChargeContent => { console.error("Invalid content: ", object); return false; }; + +export const TransferSchema = z.object({ + network: z.string().toLowerCase(), + receivingAddresses: z.array(z.string()), + transferAmount: z.number(), + assetId: z.string().toLowerCase(), +}); + +export interface TransferContent { + network: string; + receivingAddresses: string[]; + transferAmount: number; + assetId: string; +} + +export const isTransferContent = (object: any): object is TransferContent => { + return TransferSchema.safeParse(object).success; +}; + +export type Transaction = { + address: string; + amount: number; + status: string; + errorCode: string | null; + transactionUrl: string | null; +}; diff --git a/packages/plugin-coinbase/tsup.config.ts b/packages/plugin-coinbase/tsup.config.ts index e42bf4efeae..91b5946d0a4 100644 --- a/packages/plugin-coinbase/tsup.config.ts +++ b/packages/plugin-coinbase/tsup.config.ts @@ -15,6 +15,12 @@ export default defineConfig({ "https", "http", "agentkeepalive", + "@coinbase/coinbase-sdk", + "fs/promises", + "csv-writer", + "csv-parse/sync", + "path", + "url", // Add other modules you want to externalize ], }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f92740702ea..8537ec11cee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,24 +11,30 @@ importers: .: dependencies: + '@coinbase/coinbase-sdk': + specifier: ^0.10.0 + version: 0.10.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) + csv-parse: + specifier: ^5.6.0 + version: 5.6.0 ollama-ai-provider: specifier: 0.16.1 version: 0.16.1(zod@3.23.8) optional: - specifier: 0.1.4 + specifier: ^0.1.4 version: 0.1.4 sharp: - specifier: 0.33.5 + specifier: ^0.33.5 version: 0.33.5 tslog: - specifier: 4.9.3 + specifier: ^4.9.3 version: 4.9.3 devDependencies: '@commitlint/cli': - specifier: 18.6.1 + specifier: ^18.4.4 version: 18.6.1(@types/node@22.8.4)(typescript@5.6.3) '@commitlint/config-conventional': - specifier: 18.6.3 + specifier: ^18.4.4 version: 18.6.3 concurrently: specifier: 9.1.0 @@ -1256,6 +1262,9 @@ packages: '@adraffy/ens-normalize@1.10.1': resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} + '@adraffy/ens-normalize@1.11.0': + resolution: {integrity: sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==} + '@ai-sdk/anthropic@0.0.53': resolution: {integrity: sha512-33w5pmQINRRYwppgMhXY/y5ZIW6cbIhbuKbZQmy8SKZvtLBI2gM7H0QN/cH3nv0OmR4YsUw8L3DYUNlQs5hfEA==} engines: {node: '>=18'} @@ -2419,6 +2428,9 @@ packages: resolution: {integrity: sha512-d7TeUl5t+TOMJe7/CRYtf+x6hbd8N25DtH7guQTIjjr3AFVortxiAIgNejGvVqy0by4eNByw+oVil15oqxz2Eg==} deprecated: This project has been renamed to @ghostery/adblocker. Install using @ghostery/adblocker instead + '@coinbase/coinbase-sdk@0.10.0': + resolution: {integrity: sha512-sqLH7dE/0XSn5jHddjVrC1PR77sQUEytYcQAlH2d8STqRARcvddxVAByECUIL32MpbdJY7Wca3KfSa6qo811Mg==} + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -3887,6 +3899,10 @@ packages: '@noble/curves@1.3.0': resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==} + '@noble/curves@1.6.0': + resolution: {integrity: sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==} + engines: {node: ^14.21.3 || >=16} + '@noble/curves@1.7.0': resolution: {integrity: sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==} engines: {node: ^14.21.3 || >=16} @@ -4785,6 +4801,18 @@ packages: '@scure/base@1.1.9': resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} + '@scure/base@1.2.1': + resolution: {integrity: sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ==} + + '@scure/bip32@1.5.0': + resolution: {integrity: sha512-8EnFYkqEQdnkuGBVpCzKxyIwDCBLDVj3oiX0EKUFre/tOjL/Hqba1D6n/8RcmaQy4f95qQFrO2A8Sr6ybh4NRw==} + + '@scure/bip32@1.6.0': + resolution: {integrity: sha512-82q1QfklrUUdXJzjuRU7iG7D7XiFx5PHYVS0+oeNKhyDLT7WPqs6pBcM2W5ZdwOwKCwoE1Vy1se+DHjcXwCYnA==} + + '@scure/bip39@1.4.0': + resolution: {integrity: sha512-BEEm6p8IueV/ZTfQLp/0vhw4NPnT9oWf5+28nvmeUICjP99f4vr2d+qc7AVGDDtwRep6ifR43Yed9ERVmiITzw==} + '@scure/starknet@1.0.0': resolution: {integrity: sha512-o5J57zY0f+2IL/mq8+AYJJ4Xpc1fOtDhr+mFQKbHnYFmm3WQrC+8zj2HEgxak1a+x86mhmBC1Kq305KUpVf0wg==} @@ -6084,6 +6112,17 @@ packages: resolution: {integrity: sha512-JlqiAl9CPvTm5kKG0QXmVCWNWoC/XyRMOeT77cQlbxXWllgjf6SqUmaNqFon72C2o5OSZids+5FvLdsw6dvWaw==} hasBin: true + abitype@1.0.6: + resolution: {integrity: sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -6393,6 +6432,16 @@ packages: aws4@1.13.2: resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} + axios-mock-adapter@1.22.0: + resolution: {integrity: sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw==} + peerDependencies: + axios: '>= 0.17.0' + + axios-retry@4.5.0: + resolution: {integrity: sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==} + peerDependencies: + axios: 0.x || 1.x + axios@0.27.2: resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} @@ -6489,6 +6538,10 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + base64url@3.0.1: + resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} + engines: {node: '>=6.0.0'} + basic-ftp@5.0.5: resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} @@ -6548,6 +6601,13 @@ packages: bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + bip32@4.0.0: + resolution: {integrity: sha512-aOGy88DDlVUhspIXJN+dVEtclhIsfAUppD43V0j40cPTld3pv/0X/MlrZSZ6jowIaQQzFwP8M6rFU2z2mVYjDQ==} + engines: {node: '>=6.0.0'} + + bip39@3.1.0: + resolution: {integrity: sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -6556,6 +6616,9 @@ packages: engines: {node: '>= 0.8.0'} hasBin: true + bn.js@4.12.1: + resolution: {integrity: sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==} + bn.js@5.2.1: resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} @@ -6599,6 +6662,9 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + browserslist@4.24.2: resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -6614,6 +6680,9 @@ packages: bs58@6.0.0: resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + bs58check@2.1.2: + resolution: {integrity: sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==} + bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} @@ -6856,6 +6925,10 @@ packages: resolution: {integrity: sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==} engines: {node: '>=8'} + cipher-base@1.0.5: + resolution: {integrity: sha512-xq7ICKB4TMHUx7Tz1L9O2SGKOhYMOTR32oir45Bq28/AQTpHogKgHcoYFSdRbMtddl+ozNXfXY9jWcgYKmde0w==} + engines: {node: '>= 0.10'} + citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} @@ -7241,6 +7314,9 @@ packages: typescript: optional: true + create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + create-jest@29.7.0: resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -7418,6 +7494,9 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csv-parse@5.6.0: + resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==} + csv-writer@1.6.0: resolution: {integrity: sha512-NOx7YDFWEsM/fTRAJjRpPp8t+MKRVvniAg9wQlUKx20MFrPs73WLJhFf5iteqrxNYnsy924K3Iroh3yNHeYd2g==} @@ -7972,6 +8051,9 @@ packages: electron-to-chromium@1.5.64: resolution: {integrity: sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ==} + elliptic@6.6.1: + resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -8983,6 +9065,13 @@ packages: resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hash-base@3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -9051,6 +9140,9 @@ packages: history@4.10.1: resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + hogan.js@3.0.2: resolution: {integrity: sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==} hasBin: true @@ -9500,6 +9592,10 @@ packages: resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} engines: {node: '>=0.10.0'} + is-retry-allowed@2.2.0: + resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==} + engines: {node: '>=10'} + is-root@2.1.0: resolution: {integrity: sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==} engines: {node: '>=6'} @@ -9585,6 +9681,11 @@ packages: peerDependencies: ws: '*' + isows@1.0.6: + resolution: {integrity: sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==} + peerDependencies: + ws: '*' + isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} @@ -10351,6 +10452,9 @@ packages: md4w@0.2.6: resolution: {integrity: sha512-CBLQ2PxVe9WA+/nndZCx/Y+1C3DtmtSeubmXTPhMIgsXtq9gVGleikREko5FYnV6Dz4cHDWm0Ea+YMLpIjP4Kw==} + md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + mdast-util-directive@3.0.0: resolution: {integrity: sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==} @@ -10657,6 +10761,9 @@ packages: minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + minimatch@10.0.1: resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} engines: {node: 20 || >=22} @@ -10903,6 +11010,9 @@ packages: resolution: {integrity: sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==} engines: {node: '>=10'} + node-addon-api@5.1.0: + resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + node-addon-api@6.1.0: resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} @@ -10969,6 +11079,9 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + node-jose@2.2.0: + resolution: {integrity: sha512-XPCvJRr94SjLrSIm4pbYHKLEaOsDvJCpyFw/6V/KK/IXmyZ6SFBzAUDO9HQf4DB/nTEFcRGH87mNciOP23kFjw==} + node-llama-cpp@3.1.1: resolution: {integrity: sha512-CyXwxlJiAAELhy265wndAwV+nrUvVJk7+BjiYtz8BAUXCPpzZTeZTNnmcDO21FTutQyRuWhiNA/yzOLeDvmuAQ==} engines: {node: '>=18.0.0'} @@ -11261,6 +11374,14 @@ packages: otpauth@9.3.5: resolution: {integrity: sha512-jQyqOuQExeIl4YIiOUz4TdEcamgAgPX6UYeeS9Iit4lkvs7bwHb0JNDqchGRccbRfvWHV6oRwH36tOsVmc+7hQ==} + ox@0.1.2: + resolution: {integrity: sha512-ak/8K0Rtphg9vnRJlbOdaX9R7cmxD2MiSthjWGaQdMk3D7hrAlDoM+6Lxn7hN52Za3vrXfZ7enfke/5WjolDww==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + p-cancelable@3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} engines: {node: '>=12.20'} @@ -12409,6 +12530,10 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + proggy@2.0.0: resolution: {integrity: sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -12982,6 +13107,9 @@ packages: engines: {node: 20 || >=22} hasBin: true + ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} @@ -13091,6 +13219,10 @@ packages: search-insights@2.17.3: resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} + secp256k1@5.0.1: + resolution: {integrity: sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==} + engines: {node: '>=18.0.0'} + section-matter@1.0.0: resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} engines: {node: '>=4'} @@ -13177,6 +13309,10 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + shallow-clone@0.1.2: resolution: {integrity: sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw==} engines: {node: '>=0.10.0'} @@ -14094,6 +14230,9 @@ packages: peerDependencies: typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x + typeforce@1.18.0: + resolution: {integrity: sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==} + typescript-eslint@8.15.0: resolution: {integrity: sha512-wY4FRGl0ZI+ZU4Jo/yjdBu0lVTSML58pu6PgGtJmCufvzfV565pUF6iACQt092uFOd49iLOTX/sEVmHtbSrS+w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -14378,6 +14517,14 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + viem@2.21.50: + resolution: {integrity: sha512-WHB8NmkaForODuSALb0Ai3E296aEigzYSE+pzB9Y0cTNJeiZT8rpkdxxUFYfjwFMnPkz2tivqrSpuw3hO5TH6w==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + vite-node@2.1.5: resolution: {integrity: sha512-rd0QIgx74q4S1Rd56XIiL2cYEdyWn13cunYBIuqh9mpmQr7gGS0IxXoP8R6OaZtNQQLyXSWbd4rXKYUbhFpK5w==} engines: {node: ^18.0.0 || >=20.0.0} @@ -14525,6 +14672,9 @@ packages: resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} engines: {node: '>= 14'} + webauthn-p256@0.0.10: + resolution: {integrity: sha512-EeYD+gmIT80YkSIDb2iWq0lq2zbHo1CxHlQTeJ+KkCILWpVy3zASH3ByD4bopzfk0uCwXxLqKGLqp2W4O28VFA==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -14646,6 +14796,9 @@ packages: resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} engines: {node: '>=12'} + wif@2.0.6: + resolution: {integrity: sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==} + wildcard@2.0.1: resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} @@ -14864,6 +15017,8 @@ snapshots: '@adraffy/ens-normalize@1.10.1': {} + '@adraffy/ens-normalize@1.11.0': {} + '@ai-sdk/anthropic@0.0.53(zod@3.23.8)': dependencies: '@ai-sdk/provider': 0.0.26 @@ -16666,6 +16821,28 @@ snapshots: '@types/firefox-webext-browser': 120.0.4 tldts-experimental: 6.1.64 + '@coinbase/coinbase-sdk@0.10.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)': + dependencies: + '@scure/bip32': 1.6.0 + abitype: 1.0.6(typescript@5.6.3)(zod@3.23.8) + axios: 1.7.7(debug@4.3.7) + axios-mock-adapter: 1.22.0(axios@1.7.7) + axios-retry: 4.5.0(axios@1.7.7) + bip32: 4.0.0 + bip39: 3.1.0 + decimal.js: 10.4.3 + dotenv: 16.4.5 + ethers: 6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + node-jose: 2.2.0 + secp256k1: 5.0.1 + viem: 2.21.50(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) + transitivePeerDependencies: + - bufferutil + - debug + - typescript + - utf-8-validate + - zod + '@colors/colors@1.5.0': optional: true @@ -18880,6 +19057,10 @@ snapshots: dependencies: '@noble/hashes': 1.3.3 + '@noble/curves@1.6.0': + dependencies: + '@noble/hashes': 1.5.0 + '@noble/curves@1.7.0': dependencies: '@noble/hashes': 1.6.0 @@ -19850,6 +20031,25 @@ snapshots: '@scure/base@1.1.9': {} + '@scure/base@1.2.1': {} + + '@scure/bip32@1.5.0': + dependencies: + '@noble/curves': 1.6.0 + '@noble/hashes': 1.5.0 + '@scure/base': 1.1.9 + + '@scure/bip32@1.6.0': + dependencies: + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + '@scure/base': 1.2.1 + + '@scure/bip39@1.4.0': + dependencies: + '@noble/hashes': 1.5.0 + '@scure/base': 1.1.9 + '@scure/starknet@1.0.0': dependencies: '@noble/curves': 1.3.0 @@ -21582,6 +21782,11 @@ snapshots: fs-extra: 10.1.0 yargs: 17.7.2 + abitype@1.0.6(typescript@5.6.3)(zod@3.23.8): + optionalDependencies: + typescript: 5.6.3 + zod: 3.23.8 + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -21942,6 +22147,17 @@ snapshots: aws4@1.13.2: {} + axios-mock-adapter@1.22.0(axios@1.7.7): + dependencies: + axios: 1.7.7(debug@4.3.7) + fast-deep-equal: 3.1.3 + is-buffer: 2.0.5 + + axios-retry@4.5.0(axios@1.7.7): + dependencies: + axios: 1.7.7(debug@4.3.7) + is-retry-allowed: 2.2.0 + axios@0.27.2: dependencies: follow-redirects: 1.15.9(debug@4.3.7) @@ -22088,6 +22304,8 @@ snapshots: base64-js@1.5.1: {} + base64url@3.0.1: {} + basic-ftp@5.0.5: {} batch@0.6.1: {} @@ -22147,6 +22365,17 @@ snapshots: dependencies: file-uri-to-path: 1.0.0 + bip32@4.0.0: + dependencies: + '@noble/hashes': 1.6.1 + '@scure/base': 1.2.1 + typeforce: 1.18.0 + wif: 2.0.6 + + bip39@3.1.0: + dependencies: + '@noble/hashes': 1.6.1 + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -22155,6 +22384,8 @@ snapshots: blessed@0.1.81: {} + bn.js@4.12.1: {} + bn.js@5.2.1: {} bodec@0.1.0: {} @@ -22228,6 +22459,8 @@ snapshots: dependencies: fill-range: 7.1.1 + brorand@1.1.0: {} + browserslist@4.24.2: dependencies: caniuse-lite: 1.0.30001684 @@ -22247,6 +22480,12 @@ snapshots: dependencies: base-x: 5.0.0 + bs58check@2.1.2: + dependencies: + bs58: 4.0.1 + create-hash: 1.2.0 + safe-buffer: 5.2.1 + bser@2.1.1: dependencies: node-int64: 0.4.0 @@ -22524,6 +22763,11 @@ snapshots: ci-info@4.1.0: {} + cipher-base@1.0.5: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + citty@0.1.6: dependencies: consola: 3.2.3 @@ -22939,6 +23183,14 @@ snapshots: optionalDependencies: typescript: 5.6.3 + create-hash@1.2.0: + dependencies: + cipher-base: 1.0.5 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.2 + sha.js: 2.4.11 + create-jest@29.7.0(@types/node@22.8.4)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3)): dependencies: '@jest/types': 29.6.3 @@ -23174,6 +23426,8 @@ snapshots: csstype@3.1.3: {} + csv-parse@5.6.0: {} + csv-writer@1.6.0: {} culvert@0.1.2: {} @@ -23773,6 +24027,16 @@ snapshots: electron-to-chromium@1.5.64: {} + elliptic@6.6.1: + dependencies: + bn.js: 4.12.1 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + emittery@0.13.1: {} emoji-regex-xs@1.0.0: {} @@ -24988,6 +25252,17 @@ snapshots: has-yarn@3.0.0: {} + hash-base@3.1.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + safe-buffer: 5.2.1 + + hash.js@1.1.7: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -25163,6 +25438,12 @@ snapshots: tiny-warning: 1.0.3 value-equal: 1.0.1 + hmac-drbg@1.0.1: + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + hogan.js@3.0.2: dependencies: mkdirp: 0.3.0 @@ -25608,6 +25889,8 @@ snapshots: is-regexp@1.0.0: {} + is-retry-allowed@2.2.0: {} + is-root@2.1.0: {} is-ssh@1.4.0: @@ -25674,6 +25957,10 @@ snapshots: dependencies: ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10) + isows@1.0.6(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + isstream@0.1.2: {} istanbul-lib-coverage@3.2.2: {} @@ -26742,6 +27029,12 @@ snapshots: md4w@0.2.6: {} + md5.js@1.3.5: + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + mdast-util-directive@3.0.0: dependencies: '@types/mdast': 4.0.4 @@ -27385,6 +27678,8 @@ snapshots: minimalistic-assert@1.0.1: {} + minimalistic-crypto-utils@1.0.1: {} + minimatch@10.0.1: dependencies: brace-expansion: 2.0.1 @@ -27623,6 +27918,8 @@ snapshots: dependencies: semver: 7.6.3 + node-addon-api@5.1.0: {} + node-addon-api@6.1.0: {} node-addon-api@7.1.1: {} @@ -27662,8 +27959,7 @@ snapshots: node-forge@1.3.1: {} - node-gyp-build@4.8.4: - optional: true + node-gyp-build@4.8.4: {} node-gyp@10.2.0: dependencies: @@ -27682,6 +27978,18 @@ snapshots: node-int64@0.4.0: {} + node-jose@2.2.0: + dependencies: + base64url: 3.0.1 + buffer: 6.0.3 + es6-promise: 4.2.8 + lodash: 4.17.21 + long: 5.2.3 + node-forge: 1.3.1 + pako: 2.1.0 + process: 0.11.10 + uuid: 9.0.1 + node-llama-cpp@3.1.1(typescript@5.6.3): dependencies: '@huggingface/jinja': 0.3.2 @@ -28124,6 +28432,20 @@ snapshots: dependencies: '@noble/hashes': 1.5.0 + ox@0.1.2(typescript@5.6.3)(zod@3.23.8): + dependencies: + '@adraffy/ens-normalize': 1.11.0 + '@noble/curves': 1.6.0 + '@noble/hashes': 1.5.0 + '@scure/bip32': 1.6.0 + '@scure/bip39': 1.4.0 + abitype: 1.0.6(typescript@5.6.3)(zod@3.23.8) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - zod + p-cancelable@3.0.0: {} p-finally@1.0.0: {} @@ -29301,6 +29623,8 @@ snapshots: process-nextick-args@2.0.1: {} + process@0.11.10: {} + proggy@2.0.0: {} progress@2.0.3: {} @@ -30069,6 +30393,11 @@ snapshots: glob: 11.0.0 package-json-from-dist: 1.0.1 + ripemd160@2.0.2: + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + robust-predicates@3.0.2: {} rollup-plugin-dts@6.1.1(rollup@3.29.5)(typescript@5.6.3): @@ -30213,6 +30542,12 @@ snapshots: search-insights@2.17.3: {} + secp256k1@5.0.1: + dependencies: + elliptic: 6.6.1 + node-addon-api: 5.1.0 + node-gyp-build: 4.8.4 + section-matter@1.0.0: dependencies: extend-shallow: 2.0.1 @@ -30325,6 +30660,11 @@ snapshots: setprototypeof@1.2.0: {} + sha.js@2.4.11: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + shallow-clone@0.1.2: dependencies: is-extendable: 0.1.1 @@ -31334,6 +31674,8 @@ snapshots: typescript: 5.6.3 yaml: 2.6.1 + typeforce@1.18.0: {} + typescript-eslint@8.15.0(eslint@9.13.0(jiti@2.4.0))(typescript@5.6.3): dependencies: '@typescript-eslint/eslint-plugin': 8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.13.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.13.0(jiti@2.4.0))(typescript@5.6.3) @@ -31653,6 +31995,24 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 + viem@2.21.50(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8): + dependencies: + '@noble/curves': 1.6.0 + '@noble/hashes': 1.5.0 + '@scure/bip32': 1.5.0 + '@scure/bip39': 1.4.0 + abitype: 1.0.6(typescript@5.6.3)(zod@3.23.8) + isows: 1.0.6(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + ox: 0.1.2(typescript@5.6.3)(zod@3.23.8) + webauthn-p256: 0.0.10 + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + vite-node@2.1.5(@types/node@22.8.4)(terser@5.36.0): dependencies: cac: 6.7.14 @@ -31810,6 +32170,11 @@ snapshots: web-streams-polyfill@4.0.0-beta.3: {} + webauthn-p256@0.0.10: + dependencies: + '@noble/curves': 1.6.0 + '@noble/hashes': 1.5.0 + webidl-conversions@3.0.1: {} webidl-conversions@4.0.2: {} @@ -31998,6 +32363,10 @@ snapshots: dependencies: string-width: 5.1.2 + wif@2.0.6: + dependencies: + bs58check: 2.1.2 + wildcard@2.0.1: {} word-wrap@1.2.5: {}