diff --git a/.github/workflows/pnpm-lockfile-check.yml b/.github/workflows/pnpm-lockfile-check.yml index a048b3703f2..3b303f8809e 100644 --- a/.github/workflows/pnpm-lockfile-check.yml +++ b/.github/workflows/pnpm-lockfile-check.yml @@ -2,7 +2,7 @@ name: Pnpm Lockfile Check on: pull_request: - branches: ["*"] + branches: [main] jobs: check-lockfile: @@ -38,4 +38,4 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, body: '❌ The pnpm-lockfile is out of date. Please run `pnpm install --no-frozen-lockfile` and commit the updated pnpm-lock.yaml file.' - }) \ No newline at end of file + }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 80a2ffd295c..708e4ec8e54 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1334,6 +1334,47 @@ export interface IAwsS3Service extends Service { generateSignedUrl(fileName: string, expiresIn: number): Promise; } +export interface UploadIrysResult { + success: boolean; + url?: string; + error?: string; + data?: any; +} + +export interface DataIrysFetchedFromGQL { + success: boolean; + data: any; + error?: string; +} + +export interface GraphQLTag { + name: string; + values: any[]; +} + +export const enum IrysMessageType { + REQUEST = "REQUEST", + DATA_STORAGE = "DATA_STORAGE", + REQUEST_RESPONSE = "REQUEST_RESPONSE", +} + +export const enum IrysDataType { + FILE = "FILE", + IMAGE = "IMAGE", + OTHER = "OTHER", +} + +export interface IrysTimestamp { + from: number; + to: number; +} + +export interface IIrysService extends Service { + getDataFromAnAgent(agentsWalletPublicKeys: string[], tags: GraphQLTag[], timestamp: IrysTimestamp): Promise; + workerUploadDataOnIrys(data: any, dataType: IrysDataType, messageType: IrysMessageType, serviceCategory: string[], protocol: string[], validationThreshold: number[], minimumProviders: number[], testProvider: boolean[], reputation: number[]): Promise; + providerUploadDataOnIrys(data: any, dataType: IrysDataType, serviceCategory: string[], protocol: string[]): Promise; +} + export interface ITeeLogService extends Service { getInstance(): ITeeLogService; log( @@ -1379,6 +1420,7 @@ export enum ServiceType { AWS_S3 = "aws_s3", BUTTPLUG = "buttplug", SLACK = "slack", + IRYS = "irys", TEE_LOG = "tee_log", GOPLUS_SECURITY = "goplus_security", } diff --git a/packages/plugin-irys/.npmignore b/packages/plugin-irys/.npmignore new file mode 100644 index 00000000000..078562eceab --- /dev/null +++ b/packages/plugin-irys/.npmignore @@ -0,0 +1,6 @@ +* + +!dist/** +!package.json +!readme.md +!tsup.config.ts \ No newline at end of file diff --git a/packages/plugin-irys/OrchestratorDiagram.png b/packages/plugin-irys/OrchestratorDiagram.png new file mode 100644 index 00000000000..1379266f79a Binary files /dev/null and b/packages/plugin-irys/OrchestratorDiagram.png differ diff --git a/packages/plugin-irys/README.md b/packages/plugin-irys/README.md new file mode 100644 index 00000000000..c2ef9b41cbe --- /dev/null +++ b/packages/plugin-irys/README.md @@ -0,0 +1,319 @@ +# @elizaos/plugin-irys + +A plugin for ElizaOS that enables decentralized data storage and retrieval using Irys, a programmable datachain platform. + +## Overview + +This plugin integrates Irys functionality into ElizaOS, allowing agents to store and retrieve data in a decentralized manner. It provides a service for creating a decentralized knowledge base and enabling multi-agent collaboration. + +## Installation + +To install this plugin, run the following command: + +```bash +pnpm add @elizaos/plugin-irys +``` + +## Features + +- **Decentralized Data Storage**: Store data permanently on the Irys network +- **Data Retrieval**: Fetch stored data using GraphQL queries +- **Multi-Agent Support**: Enable data sharing and collaboration between agents +- **Ethereum Integration**: Built-in support for Ethereum wallet authentication + +## Configuration + +The plugin requires the following environment variables: + +- `EVM_WALLET_PRIVATE_KEY`: Your EVM wallet private key +- `AGENTS_WALLET_PUBLIC_KEYS`: The public keys of the agents that will be used to retrieve the data (string separated by commas) + +For this plugin to work, you need to have an EVM (Base network) wallet with a private key and public address. To prevent any security issues, we recommend using a dedicated wallet for this plugin. + +> **Important**: The wallet address needs to have Base Sepolia ETH tokens to store images/files and any data larger than 100KB. + +## How it works + +![Orchestrator Diagram](./OrchestratorDiagram.png) + +The system consists of three main components that work together to enable decentralized multi-agent operations: + +### 1. Providers +Providers are the data management layer of the system. They: +- Interact with the Orchestrator to store data +- Aggregate information from multiple sources to enhance context +- Support agents with enriched data for better decision-making + +### 2. Orchestrators +Orchestrators manage the flow of communication and requests. They: +- Interact with the Irys datachain to store and retrieve data +- Implement a tagging system for request categorization +- Validate data integrity and authenticity +- Coordinate the overall system workflow + +### 3. Workers +Workers are specialized agents that execute specific tasks. They: +- Perform autonomous operations (e.g., social media interactions, DeFi operations) +- Interact with Orchestrators to get contextual data from Providers +- Interact with Orchestrators to store execution results on the Irys datachain +- Maintain transparency by documenting all actions + +This architecture ensures a robust, transparent, and efficient system where: +- Data is securely stored and verified on the blockchain +- Requests are properly routed and managed +- Operations are executed autonomously +- All actions are traceable and accountable + +You can find more information about the system in the [A Decentralized Framework for Multi-Agent Systems Using Datachain Technology](https://trophe.net/article/A_Decentralized_Framework_for_Multi-Agent_Systems_Using_Datachain_Technology.pdf) paper. + +## Usage + +### Worker + +As a worker, you can store data on the Irys network using the `workerUploadDataOnIrys` function. You can use this function to store data from any source to document your actions. You can also use this function to store a request to get data from the Orchestrator to enhance your context. + +```typescript +const { IrysService } = require('@elizaos/plugin-irys'); + +const irysService : IrysService = runtime.getService(ServiceType.IRYS) +const data = "Provide Liquidity to the ETH pool on Stargate"; +const result = await irysService.workerUploadDataOnIrys( + data, + IrysDataType.OTHER, + IrysMessageType.DATA_STORAGE, + ["DeFi"], + ["Stargate", "LayerZero"] +); +console.log(`Data uploaded successfully at the following url: ${result.url}`); +``` + +To upload files or images : + +```typescript +const { IrysService } = require('@elizaos/plugin-irys'); + +const irysService : IrysService = runtime.getService(ServiceType.IRYS) +const userAttachmentToStore = state.recentMessagesData[1].content.attachments[0].url.replace("agent\\agent", "agent"); + +const result = await irysService.workerUploadDataOnIrys( + userAttachmentToStore, + IrysDataType.IMAGE, + IrysMessageType.DATA_STORAGE, + ["Social Media"], + ["X", "Twitter"] +); +console.log(`Data uploaded successfully at the following url: ${result.url}`); +``` + +To store a request to get data from the Orchestrator to enhance your context, you can use the `workerUploadDataOnIrys` function with the `IrysMessageType.REQUEST` message type. + +```typescript +const { IrysService } = require('@elizaos/plugin-irys'); + +const irysService : IrysService = runtime.getService(ServiceType.IRYS) +const data = "Which Pool farm has the highest APY on Stargate?"; +const result = await irysService.workerUploadDataOnIrys( + data, + IrysDataType.OTHER, + IrysMessageType.REQUEST, + ["DeFi"], + ["Stargate", "LayerZero"], + [0.5], // Validation Threshold - Not implemented yet + [1], // Minimum Providers + [false], // Test Provider - Not implemented yet + [0.5] // Reputation - Not implemented yet +); +console.log(`Data uploaded successfully at the following url: ${result.url}`); +console.log(`Response from the Orchestrator: ${result.data}`); +``` + +### Provider + +As a provider, you can store data on the Irys network using the `providerUploadDataOnIrys` function. The data you provide can be retrieved by the Orchestrator to enhance the context of the Worker. + +```typescript +const { IrysService } = require('@elizaos/plugin-irys'); + +const irysService : IrysService = runtime.getService(ServiceType.IRYS) +const data = "ETH Pool Farm APY : 6,86%"; +const result = await irysService.providerUploadDataOnIrys( + data, + IrysDataType.OTHER, + ["DeFi"], + ["Stargate", "LayerZero"] +); +console.log(`Data uploaded successfully at the following url: ${result.url}`); +``` + +To upload files or images : + +```typescript +const { IrysService } = require('@elizaos/plugin-irys'); + +const irysService : IrysService = runtime.getService(ServiceType.IRYS) +const userAttachmentToStore = state.recentMessagesData[1].content.attachments[0].url.replace("agent\\agent", "agent"); + +const result = await irysService.providerUploadDataOnIrys( + userAttachmentToStore, + IrysDataType.IMAGE, + ["Social Media"], + ["X", "Twitter"] +); +console.log(`Data uploaded successfully at the following url: ${result.url}`); +``` + +### Retrieving Data + +To retrieve data from the Irys network, you can use the `getDataFromAnAgent` function. This function will retrieve all data associated with the given wallet addresses, tags and timestamp. The function automatically detects the content type and returns either JSON data or file/image URLs accordingly. + +- For files and images: Returns the URL of the stored content +- For other data types: Returns a JSON object with the following structure: + +```typescript +{ + data: string, // The stored data + address: string // The address of the agent that stored the data +} +``` + +By using only the provider address you want to retrieve data from : + +```typescript +const { IrysService } = require('@elizaos/plugin-irys'); + +const irysService = runtime.getService(ServiceType.IRYS) +const agentsWalletPublicKeys = runtime.getSetting("AGENTS_WALLET_PUBLIC_KEYS").split(","); +const data = await irysService.getDataFromAnAgent(agentsWalletPublicKeys); +console.log(`Data retrieved successfully. Data: ${data}`); +``` + +By using tags and timestamp: + +```typescript +const { IrysService } = require('@elizaos/plugin-irys'); + +const irysService = runtime.getService(ServiceType.IRYS) +const tags = [ + { name: "Message-Type", values: [IrysMessageType.DATA_STORAGE] }, + { name: "Service-Category", values: ["DeFi"] }, + { name: "Protocol", values: ["Stargate", "LayerZero"] }, +]; +const timestamp = { from: 1710000000, to: 1710000000 }; +const data = await irysService.getDataFromAnAgent(null, tags, timestamp); +console.log(`Data retrieved successfully. Data: ${data}`); +``` + +If everything is null, the function will retrieve all data from the Irys network. + +## About Irys + +Irys is the first Layer 1 (L1) programmable datachain designed to optimize both data storage and execution. By integrating storage and execution, Irys enhances the utility of blockspace, enabling a broader spectrum of web services to operate on-chain. + +### Key Features of Irys + +- **Unified Platform**: Combines data storage and execution, allowing developers to eliminate dependencies and integrate efficient on-chain data seamlessly. +- **Cost-Effective Storage**: Optimized specifically for data storage, making it significantly cheaper to store data on-chain compared to traditional blockchains. +- **Programmable Datachain**: The IrysVM can utilize on-chain data during computations, enabling dynamic and real-time applications. +- **Decentralization**: Designed to minimize centralization risks by distributing control. +- **Free Storage for Small Data**: Storing less than 100KB of data is free. +- **GraphQL Querying**: Metadata stored on Irys can be queried using GraphQL. + +### GraphQL Query Examples + +The plugin uses GraphQL to retrieve transaction metadata. Here's an example query structure: + +```typescript +const QUERY = gql` + query($owners: [String!], $tags: [TagFilter!], $timestamp: TimestampFilter) { + transactions(owners: $owners, tags: $tags, timestamp: $timestamp) { + edges { + node { + id, + address + } + } + } + } +`; + +const variables = { + owners: owners, + tags: tags, + timestamp: timestamp +} + +const data: TransactionGQL = await graphQLClient.request(QUERY, variables); +``` + +## API Reference + +### IrysService + +The main service provided by this plugin implements the following interface: + +```typescript + +interface UploadIrysResult { + success: boolean; + url?: string; + error?: string; + data?: any; +} + +interface DataIrysFetchedFromGQL { + success: boolean; + data: any; + error?: string; +} + +interface GraphQLTag { + name: string; + values: any[]; +} + +const enum IrysMessageType { + REQUEST = "REQUEST", + DATA_STORAGE = "DATA_STORAGE", + REQUEST_RESPONSE = "REQUEST_RESPONSE", +} + +const enum IrysDataType { + FILE = "FILE", + IMAGE = "IMAGE", + OTHER = "OTHER", +} + +interface IrysTimestamp { + from: number; + to: number; +} + +interface IIrysService extends Service { + getDataFromAnAgent(agentsWalletPublicKeys: string[], tags: GraphQLTag[], timestamp: IrysTimestamp): Promise; + workerUploadDataOnIrys(data: any, dataType: IrysDataType, messageType: IrysMessageType, serviceCategory: string[], protocol: string[], validationThreshold: number[], minimumProviders: number[], testProvider: boolean[], reputation: number[]): Promise; + providerUploadDataOnIrys(data: any, dataType: IrysDataType, serviceCategory: string[], protocol: string[]): Promise; +} +``` + +#### Methods + +- `getDataFromAnAgent(agentsWalletPublicKeys: string[], tags: GraphQLTag[], timestamp: IrysTimestamp)`: Retrieves all data associated with the given parameters +- `workerUploadDataOnIrys(data: any, dataType: IrysDataType, messageType: IrysMessageType, serviceCategory: string[], protocol: string[], validationThreshold: number[], minimumProviders: number[], testProvider: boolean[], reputation: number[])`: Uploads data to Irys and returns the orchestrator response (request or data storage) +- `providerUploadDataOnIrys(data: any, dataType: IrysDataType, serviceCategory: string[], protocol: string[])`: Uploads data to Irys and returns orchestrator response (data storage) + +## Testing + +To run the tests, you can use the following command: + +```bash +pnpm test +``` + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +## Ressources + +- [Irys Documentation](https://docs.irys.xyz/) +- [A Decentralized Framework for Multi-Agent Systems Using Datachain Technology](https://trophe.net/article/A_Decentralized_Framework_for_Multi-Agent_Systems_Using_Datachain_Technology.pdf) diff --git a/packages/plugin-irys/eslint.config.mjs b/packages/plugin-irys/eslint.config.mjs new file mode 100644 index 00000000000..92fe5bbebef --- /dev/null +++ b/packages/plugin-irys/eslint.config.mjs @@ -0,0 +1,3 @@ +import eslintGlobalConfig from "../../eslint.config.mjs"; + +export default [...eslintGlobalConfig]; diff --git a/packages/plugin-irys/package.json b/packages/plugin-irys/package.json new file mode 100644 index 00000000000..15cd8a39047 --- /dev/null +++ b/packages/plugin-irys/package.json @@ -0,0 +1,23 @@ +{ + "name": "@elizaos/plugin-irys", + "version": "0.1.0-alpha.1", + "main": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "dependencies": { + "@elizaos/core": "workspace:*", + "@irys/upload": "^0.0.14", + "@irys/upload-ethereum": "^0.0.14", + "graphql-request": "^4.0.0" + }, + "devDependencies": { + "tsup": "8.3.5", + "@types/node": "^20.0.0" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch", + "lint": "eslint --fix --cache .", + "test": "vitest run" + } +} diff --git a/packages/plugin-irys/src/index.ts b/packages/plugin-irys/src/index.ts new file mode 100644 index 00000000000..0cf83ac3ec0 --- /dev/null +++ b/packages/plugin-irys/src/index.ts @@ -0,0 +1,14 @@ +import { Plugin } from "@elizaos/core"; +import IrysService from "./services/irysService"; + +const irysPlugin: Plugin = { + name: "plugin-irys", + description: "Store and retrieve data on Irys to create a decentralized knowledge base and enable multi-agent collaboration", + actions: [], + providers: [], + evaluators: [], + clients: [], + services: [new IrysService()], +} + +export default irysPlugin; diff --git a/packages/plugin-irys/src/services/irysService.ts b/packages/plugin-irys/src/services/irysService.ts new file mode 100644 index 00000000000..e97bae6ee85 --- /dev/null +++ b/packages/plugin-irys/src/services/irysService.ts @@ -0,0 +1,345 @@ +import { + IAgentRuntime, + Service, + ServiceType, + IIrysService, + UploadIrysResult, + DataIrysFetchedFromGQL, + GraphQLTag, + IrysMessageType, + generateMessageResponse, + ModelClass, + IrysDataType, + IrysTimestamp, +} from "@elizaos/core"; +import { Uploader } from "@irys/upload"; +import { BaseEth } from "@irys/upload-ethereum"; +import { GraphQLClient, gql } from 'graphql-request'; +import crypto from 'crypto'; + +interface NodeGQL { + id: string; + address: string; +} + +interface TransactionsIdAddress { + success: boolean; + data: NodeGQL[]; + error?: string; +} + +interface TransactionGQL { + transactions: { + edges: { + node: { + id: string; + address: string; + } + }[] + } +} + +export class IrysService extends Service implements IIrysService { + static serviceType: ServiceType = ServiceType.IRYS; + + private runtime: IAgentRuntime | null = null; + private irysUploader: any | null = null; + private endpointForTransactionId: string = "https://uploader.irys.xyz/graphql"; + private endpointForData: string = "https://gateway.irys.xyz"; + + async initialize(runtime: IAgentRuntime): Promise { + console.log("Initializing IrysService"); + this.runtime = runtime; + } + + private async getTransactionId(owners: string[] = null, tags: GraphQLTag[] = null, timestamp: IrysTimestamp = null): Promise { + const graphQLClient = new GraphQLClient(this.endpointForTransactionId); + const QUERY = gql` + query($owners: [String!], $tags: [TagFilter!], $timestamp: TimestampFilter) { + transactions(owners: $owners, tags: $tags, timestamp: $timestamp) { + edges { + node { + id, + address + } + } + } + } + `; + try { + const variables = { + owners: owners, + tags: tags, + timestamp: timestamp + } + const data: TransactionGQL = await graphQLClient.request(QUERY, variables); + const listOfTransactions : NodeGQL[] = data.transactions.edges.map((edge: any) => edge.node); + console.log("Transaction IDs retrieved") + return { success: true, data: listOfTransactions }; + } catch (error) { + console.error("Error fetching transaction IDs", error); + return { success: false, data: [], error: "Error fetching transaction IDs" }; + } + } + + private async initializeIrysUploader(): Promise { + if (this.irysUploader) return true; + if (!this.runtime) return false; + + try { + const EVM_WALLET_PRIVATE_KEY = this.runtime.getSetting("EVM_WALLET_PRIVATE_KEY"); + if (!EVM_WALLET_PRIVATE_KEY) return false; + + const irysUploader = await Uploader(BaseEth).withWallet(EVM_WALLET_PRIVATE_KEY); + this.irysUploader = irysUploader; + return true; + } catch (error) { + console.error("Error initializing Irys uploader:", error); + return false; + } + } + + private async fetchDataFromTransactionId(transactionId: string): Promise { + console.log(`Fetching data from transaction ID: ${transactionId}`); + const response = await fetch(`${this.endpointForData}/${transactionId}`); + if (!response.ok) return { success: false, data: null, error: "Error fetching data from transaction ID" }; + return { + success: true, + data: response, + }; + } + private converToValues(value: any): any[] { + if (Array.isArray(value)) { + return value; + } + return [value]; + } + + private async orchestrateRequest(requestMessage: string, tags: GraphQLTag[], timestamp: IrysTimestamp = null): Promise { + let serviceCategory = tags.find((tag) => tag.name == "Service-Category")?.values; + let protocol = tags.find((tag) => tag.name == "Protocol")?.values; + let minimumProviders = Number(tags.find((tag) => tag.name == "Minimum-Providers")?.values); + /* + Further implementation of the orchestrator + { name: "Validation-Threshold", values: validationThreshold }, + { name: "Test-Provider", values: testProvider }, + { name: "Reputation", values: reputation }, + */ + const tagsToRetrieve : GraphQLTag[] = [ + { name: "Message-Type", values: [IrysMessageType.DATA_STORAGE] }, + { name: "Service-Category", values: this.converToValues(serviceCategory) }, + { name: "Protocol", values: this.converToValues(protocol) }, + ]; + const data = await this.getDataFromAnAgent(null, tagsToRetrieve, timestamp); + if (!data.success) return { success: false, data: null, error: data.error }; + const dataArray = data.data as Array; + try { + for (let i = 0; i < dataArray.length; i++) { + const node = dataArray[i]; + const templateRequest = ` + Determine the truthfulness of the relationship between the given context and text. + Context: ${requestMessage} + Text: ${node.data} + Return True or False + `; + const responseFromModel = await generateMessageResponse({ + runtime: this.runtime, + context: templateRequest, + modelClass: ModelClass.MEDIUM, + }); + console.log("RESPONSE FROM MODEL : ", responseFromModel) + if (!responseFromModel.success || ((responseFromModel.content?.toString().toLowerCase().includes('false')) && (!responseFromModel.content?.toString().toLowerCase().includes('true')))) { + dataArray.splice(i, 1); + i--; + } + } + } catch (error) { + if (error.message.includes("TypeError: Cannot read properties of undefined (reading 'settings')")) { + return { success: false, data: null, error: "Error in the orchestrator" }; + } + } + let responseTags: GraphQLTag[] = [ + { name: "Message-Type", values: [IrysMessageType.REQUEST_RESPONSE] }, + { name: "Service-Category", values: [serviceCategory] }, + { name: "Protocol", values: [protocol] }, + { name: "Request-Id", values: [tags.find((tag) => tag.name == "Request-Id")?.values[0]] }, + ]; + if (dataArray.length == 0) { + const response = await this.uploadDataOnIrys("No relevant data found from providers", responseTags, IrysMessageType.REQUEST_RESPONSE); + console.log("Response from Irys: ", response); + return { success: false, data: null, error: "No relevant data found from providers" }; + } + const listProviders = new Set(dataArray.map((provider: any) => provider.address)); + if (listProviders.size < minimumProviders) { + const response = await this.uploadDataOnIrys("Not enough providers", responseTags, IrysMessageType.REQUEST_RESPONSE); + console.log("Response from Irys: ", response); + return { success: false, data: null, error: "Not enough providers" }; + } + const listData = dataArray.map((provider: any) => provider.data); + const response = await this.uploadDataOnIrys(listData, responseTags, IrysMessageType.REQUEST_RESPONSE); + console.log("Response from Irys: ", response); + return { + success: true, + data: listData + } + } + + // Orchestrator + private async uploadDataOnIrys(data: any, tags: GraphQLTag[], messageType: IrysMessageType, timestamp: IrysTimestamp = null): Promise { + if (!(await this.initializeIrysUploader())) { + return { + success: false, + error: "Irys uploader not initialized", + }; + } + + // Transform tags to the correct format + const formattedTags = tags.map(tag => ({ + name: tag.name, + value: Array.isArray(tag.values) ? tag.values.join(',') : tag.values + })); + + const requestId = String(crypto.createHash('sha256').update(new Date().toISOString()).digest('hex')); + formattedTags.push({ + name: "Request-Id", + value: requestId + }); + try { + const dataToStore = { + data: data, + }; + const receipt = await this.irysUploader.upload(JSON.stringify(dataToStore), { tags: formattedTags }); + if (messageType == IrysMessageType.DATA_STORAGE || messageType == IrysMessageType.REQUEST_RESPONSE) { + return { success: true, url: `https://gateway.irys.xyz/${receipt.id}`}; + } else if (messageType == IrysMessageType.REQUEST) { + const response = await this.orchestrateRequest(data, tags, timestamp); + return { + success: response.success, + url: `https://gateway.irys.xyz/${receipt.id}`, + data: response.data, + error: response.error ? response.error : null + } + + } + return { success: true, url: `https://gateway.irys.xyz/${receipt.id}` }; + } catch (error) { + return { success: false, error: "Error uploading to Irys, " + error }; + } + } + + private async uploadFileOrImageOnIrys(data: string, tags: GraphQLTag[]): Promise { + if (!(await this.initializeIrysUploader())) { + return { + success: false, + error: "Irys uploader not initialized" + }; + } + + const formattedTags = tags.map(tag => ({ + name: tag.name, + value: Array.isArray(tag.values) ? tag.values.join(',') : tag.values + })); + + try { + const receipt = await this.irysUploader.uploadFile(data, { tags: formattedTags }); + return { success: true, url: `https://gateway.irys.xyz/${receipt.id}` }; + } catch (error) { + return { success: false, error: "Error uploading to Irys, " + error }; + } + } + + private normalizeArrayValues(arr: number[], min: number, max?: number): void { + for (let i = 0; i < arr.length; i++) { + arr[i] = Math.max(min, max !== undefined ? Math.min(arr[i], max) : arr[i]); + } + } + + private normalizeArraySize(arr: any[]): any { + if (arr.length == 1) { + return arr[0]; + } + return arr; + } + + async workerUploadDataOnIrys(data: any, dataType: IrysDataType, messageType: IrysMessageType, serviceCategory: string[], protocol: string[], validationThreshold: number[] = [], minimumProviders: number[] = [], testProvider: boolean[] = [], reputation: number[] = []): Promise { + this.normalizeArrayValues(validationThreshold, 0, 1); + this.normalizeArrayValues(minimumProviders, 0); + this.normalizeArrayValues(reputation, 0, 1); + + const tags = [ + { name: "Message-Type", values: messageType }, + { name: "Service-Category", values: this.normalizeArraySize(serviceCategory) }, + { name: "Protocol", values: this.normalizeArraySize(protocol) }, + ] as GraphQLTag[]; + + if (messageType == IrysMessageType.REQUEST) { + if (validationThreshold.length > 0) { + tags.push({ name: "Validation-Threshold", values: this.normalizeArraySize(validationThreshold) }); + } + if (minimumProviders.length > 0) { + tags.push({ name: "Minimum-Providers", values: this.normalizeArraySize(minimumProviders) }); + } + if (testProvider.length > 0) { + tags.push({ name: "Test-Provider", values: this.normalizeArraySize(testProvider) }); + } + if (reputation.length > 0) { + tags.push({ name: "Reputation", values: this.normalizeArraySize(reputation) }); + } + } + if (dataType == IrysDataType.FILE || dataType == IrysDataType.IMAGE) { + return await this.uploadFileOrImageOnIrys(data, tags); + } + + return await this.uploadDataOnIrys(data, tags, messageType); + } + + async providerUploadDataOnIrys(data: any, dataType: IrysDataType, serviceCategory: string[], protocol: string[]): Promise { + const tags = [ + { name: "Message-Type", values: [IrysMessageType.DATA_STORAGE] }, + { name: "Service-Category", values: serviceCategory }, + { name: "Protocol", values: protocol }, + ] as GraphQLTag[]; + + if (dataType == IrysDataType.FILE || dataType == IrysDataType.IMAGE) { + return await this.uploadFileOrImageOnIrys(data, tags); + } + + return await this.uploadDataOnIrys(data, tags, IrysMessageType.DATA_STORAGE); + } + + async getDataFromAnAgent(agentsWalletPublicKeys: string[] = null, tags: GraphQLTag[] = null, timestamp: IrysTimestamp = null): Promise { + try { + const transactionIdsResponse = await this.getTransactionId(agentsWalletPublicKeys, tags, timestamp); + if (!transactionIdsResponse.success) return { success: false, data: null, error: "Error fetching transaction IDs" }; + const transactionIdsAndResponse = transactionIdsResponse.data.map((node: NodeGQL) => node); + const dataPromises: Promise[] = transactionIdsAndResponse.map(async (node: NodeGQL) => { + const fetchDataFromTransactionIdResponse = await this.fetchDataFromTransactionId(node.id); + if (await fetchDataFromTransactionIdResponse.data.headers.get('content-type') == "application/octet-stream") { + let data = null; + const responseText = await fetchDataFromTransactionIdResponse.data.text(); + try { + data = JSON.parse(responseText); + } catch (error) { + data = responseText; + } + return { + data: data, + address: node.address + } + } + else { + return { + data: fetchDataFromTransactionIdResponse.data.url, + address: node.address + } + } + }); + const data = await Promise.all(dataPromises); + return { success: true, data: data }; + } catch (error) { + return { success: false, data: null, error: "Error fetching data from transaction IDs " + error }; + } + } +} + +export default IrysService; \ No newline at end of file diff --git a/packages/plugin-irys/tests/provider.test.ts b/packages/plugin-irys/tests/provider.test.ts new file mode 100644 index 00000000000..be6166ed312 --- /dev/null +++ b/packages/plugin-irys/tests/provider.test.ts @@ -0,0 +1,63 @@ +import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; +import { IrysService } from "../src/services/irysService"; +import { defaultCharacter, IrysDataType } from "@elizaos/core"; + +// Mock NodeCache +vi.mock("node-cache", () => { + return { + default: vi.fn().mockImplementation(() => ({ + set: vi.fn(), + get: vi.fn().mockReturnValue(null), + })), + }; +}); + +// Mock path module +vi.mock("path", async () => { + const actual = await vi.importActual("path"); + return { + ...actual, + join: vi.fn().mockImplementation((...args) => args.join("/")), + }; +}); + +// Mock the ICacheManager +const mockCacheManager = { + get: vi.fn().mockResolvedValue(null), + set: vi.fn(), + delete: vi.fn(), +}; + +describe("IrysService", () => { + let irysService; + let mockedRuntime; + + beforeEach(async () => { + vi.clearAllMocks(); + mockCacheManager.get.mockResolvedValue(null); + + mockedRuntime = { + character: defaultCharacter, + getSetting: vi.fn().mockImplementation((key: string) => { + if (key === "EVM_WALLET_PRIVATE_KEY") // TEST PRIVATE KEY + return "0xd6ed963c4eb8436b284f62636a621c164161ee25218b3be5ca4cad1261f8c390"; + return undefined; + }), + }; + irysService = new IrysService(); + await irysService.initialize(mockedRuntime); + }); + + afterEach(() => { + vi.clearAllTimers(); + }); + + describe("Store String on Irys", () => { + it("should store string on Irys", async () => { + const result = await irysService.providerUploadDataOnIrys("Hello World", IrysDataType.OTHER, ["test"], ["test"]); + console.log("Store String on Irys ERROR : ", result.error) + expect(result.success).toBe(true); + }); + }); +}); + diff --git a/packages/plugin-irys/tests/wallet.test.ts b/packages/plugin-irys/tests/wallet.test.ts new file mode 100644 index 00000000000..0c1ffc4a14e --- /dev/null +++ b/packages/plugin-irys/tests/wallet.test.ts @@ -0,0 +1,66 @@ +import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; +import { IrysService } from "../src/services/irysService"; +import { defaultCharacter } from "@elizaos/core"; + +// Mock NodeCache +vi.mock("node-cache", () => { + return { + default: vi.fn().mockImplementation(() => ({ + set: vi.fn(), + get: vi.fn().mockReturnValue(null), + })), + }; +}); + +// Mock path module +vi.mock("path", async () => { + const actual = await vi.importActual("path"); + return { + ...actual, + join: vi.fn().mockImplementation((...args) => args.join("/")), + }; +}); + +// Mock the ICacheManager +const mockCacheManager = { + get: vi.fn().mockResolvedValue(null), + set: vi.fn(), + delete: vi.fn(), +}; + +describe("IrysService", () => { + let irysService; + let mockedRuntime; + + beforeEach(async () => { + vi.clearAllMocks(); + mockCacheManager.get.mockResolvedValue(null); + + mockedRuntime = { + character: defaultCharacter, + getSetting: vi.fn().mockImplementation((key: string) => { + if (key === "EVM_WALLET_PRIVATE_KEY") // TEST PRIVATE KEY + return "0xd6ed963c4eb8436b284f62636a621c164161ee25218b3be5ca4cad1261f8c390"; + return undefined; + }), + }; + irysService = new IrysService(); + await irysService.initialize(mockedRuntime); + }); + + afterEach(() => { + vi.clearAllTimers(); + }); + + describe("Initialize IrysService", () => { + it("should initialize IrysService", async () => { + expect(irysService).toBeDefined(); + }); + + it("should initialize IrysUploader", async () => { + const result = await irysService.initializeIrysUploader(); + expect(result).toBe(true); + }); + }); +}); + diff --git a/packages/plugin-irys/tests/worker.test.ts b/packages/plugin-irys/tests/worker.test.ts new file mode 100644 index 00000000000..279be9cb413 --- /dev/null +++ b/packages/plugin-irys/tests/worker.test.ts @@ -0,0 +1,84 @@ +import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; +import { IrysService } from "../src/services/irysService"; +import { defaultCharacter, IrysDataType, IrysMessageType } from "@elizaos/core"; + +// Mock NodeCache +vi.mock("node-cache", () => { + return { + default: vi.fn().mockImplementation(() => ({ + set: vi.fn(), + get: vi.fn().mockReturnValue(null), + })), + }; +}); + +// Mock path module +vi.mock("path", async () => { + const actual = await vi.importActual("path"); + return { + ...actual, + join: vi.fn().mockImplementation((...args) => args.join("/")), + }; +}); + +// Mock the ICacheManager +const mockCacheManager = { + get: vi.fn().mockResolvedValue(null), + set: vi.fn(), + delete: vi.fn(), +}; + +describe("IrysService", () => { + let irysService; + let mockedRuntime; + + beforeEach(async () => { + + vi.clearAllMocks(); + mockCacheManager.get.mockResolvedValue(null); + + mockedRuntime = { + character: defaultCharacter, + getSetting: vi.fn().mockImplementation((key: string) => { + if (key === "EVM_WALLET_PRIVATE_KEY") // TEST PRIVATE KEY + return "0xd6ed963c4eb8436b284f62636a621c164161ee25218b3be5ca4cad1261f8c390"; + return undefined; + }), + }; + irysService = new IrysService(); + await irysService.initialize(mockedRuntime); + }); + + afterEach(() => { + vi.clearAllTimers(); + }); + + describe("Store String on Irys", () => { + it("should store string on Irys", async () => { + const result = await irysService.workerUploadDataOnIrys( + "Hello World", + IrysDataType.OTHER, + IrysMessageType.DATA_STORAGE, + ["test"], + ["test"] + ); + console.log("Store String on Irys ERROR : ", result.error) + expect(result.success).toBe(true); + }); + + it("should retrieve data from Irys", async () => { + const result = await irysService.getDataFromAnAgent(["0x7131780570930a0ef05ef7a66489111fc31e9538"], []); + console.log("should retrieve data from Irys ERROR : ", result.error) + expect(result.success).toBe(true); + expect(result.data.length).toBeGreaterThan(0); + }); + + it("should get a response from the orchestrator", async () => { + const result = await irysService.workerUploadDataOnIrys("Hello World", IrysDataType.OTHER, IrysMessageType.REQUEST, ["test"], ["test"]); + console.log("should get a response from the orchestrator ERROR : ", result.error) + expect(result.success).toBe(true); + expect(result.data.length).toBeGreaterThan(0); + }); + }); +}); + diff --git a/packages/plugin-irys/tsconfig.json b/packages/plugin-irys/tsconfig.json new file mode 100644 index 00000000000..2ef05a1844a --- /dev/null +++ b/packages/plugin-irys/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "types": [ + "node" + ] + }, + "include": [ + "src/**/*.ts", + "src/**/*.d.ts" + ] +} \ No newline at end of file diff --git a/packages/plugin-irys/tsup.config.ts b/packages/plugin-irys/tsup.config.ts new file mode 100644 index 00000000000..b5e4388b214 --- /dev/null +++ b/packages/plugin-irys/tsup.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + outDir: "dist", + sourcemap: true, + clean: true, + format: ["esm"], // Ensure you're targeting CommonJS + external: [ + "dotenv", // Externalize dotenv to prevent bundling + "fs", // Externalize fs to use Node.js built-in module + "path", // Externalize other built-ins if necessary + "@reflink/reflink", + "@node-llama-cpp", + "https", + "http", + "agentkeepalive", + "zod", + // Add other modules you want to externalize + ], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f615978213e..5d0ab398c2c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1718,6 +1718,28 @@ importers: specifier: 7.1.0 version: 7.1.0 + packages/plugin-irys: + dependencies: + '@elizaos/core': + specifier: workspace:* + version: link:../core + '@irys/upload': + specifier: ^0.0.14 + version: 0.0.14(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@irys/upload-ethereum': + specifier: ^0.0.14 + version: 0.0.14(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + graphql-request: + specifier: ^4.0.0 + version: 4.3.0(encoding@0.1.13)(graphql@16.10.0) + devDependencies: + '@types/node': + specifier: ^20.0.0 + version: 20.17.9 + tsup: + specifier: 8.3.5 + version: 8.3.5(@swc/core@1.10.7(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) + packages/plugin-letzai: dependencies: '@elizaos/core': @@ -5843,6 +5865,25 @@ packages: '@ioredis/commands@1.2.0': resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + '@irys/arweave@0.0.2': + resolution: {integrity: sha512-ddE5h4qXbl0xfGlxrtBIwzflaxZUDlDs43TuT0u1OMfyobHul4AA1VEX72Rpzw2bOh4vzoytSqA1jCM7x9YtHg==} + + '@irys/bundles@0.0.1': + resolution: {integrity: sha512-yeQNzElERksFbfbNxJQsMkhtkI3+tNqIMZ/Wwxh76NVBmCnCP5huefOv7ET0MOO7TEQL+TqvKSqmFklYSvTyHw==} + + '@irys/query@0.0.9': + resolution: {integrity: sha512-uBIy8qeOQupUSBzR+1KU02JJXFp5Ue9l810PIbBF/ylUB8RTreUFkyyABZ7J3FUaOIXFYrT7WVFSJSzXM7P+8w==} + engines: {node: '>=16.10.0'} + + '@irys/upload-core@0.0.9': + resolution: {integrity: sha512-Ha4pX8jgYBA3dg5KHDPk+Am0QO+SmvnmgCwKa6uiDXZKuVr0neSx4V1OAHoP+As+j7yYgfChdsdrvsNzZGGehA==} + + '@irys/upload-ethereum@0.0.14': + resolution: {integrity: sha512-hzJkmuQ7JnHNhaunbBpwZSxrbchdiWCTkeFUYI4OZyRNFK1vdPfQ+fAiFBnqSTS8yuqlnN+6xad2b8gS+1JmSA==} + + '@irys/upload@0.0.14': + resolution: {integrity: sha512-6XdkyS5cVINcPjv1MzA6jDsawfG7Bw6sq5wilNx5B4X7nNotBPC3SuRrZs06G/0BTUj15W+TRO/tZTDWRUfZzA==} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -7759,6 +7800,12 @@ packages: '@radix-ui/rect@1.1.0': resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} + '@randlabs/communication-bridge@1.0.1': + resolution: {integrity: sha512-CzS0U8IFfXNK7QaJFE4pjbxDGfPjbXBEsEaCn9FN15F+ouSAEUQkva3Gl66hrkBZOGexKFEWMwUHIDKpZ2hfVg==} + + '@randlabs/myalgo-connect@1.4.2': + resolution: {integrity: sha512-K9hEyUi7G8tqOp7kWIALJLVbGCByhilcy6123WfcorxWwiE1sbQupPyIU5f3YdQK6wMjBsyTWiLW52ZBMp7sXA==} + '@raydium-io/raydium-sdk-v2@0.1.82-alpha': resolution: {integrity: sha512-PScLnWZV5Y/igcvP4hbD/1ztzW2w5a2YStolu9A5VT6uB2q+izeo+SE7IqzZggyaReXyisjdkNGpB/kMdkdJGQ==} @@ -8762,6 +8809,10 @@ packages: '@supabase/supabase-js@2.46.2': resolution: {integrity: sha512-5FEzYMZhfIZrMWEqo5/dQincvrhM+DeMWH3/okeZrkBBW1AJxblOQhnhF4/dfNYK25oZ1O8dAnnxZ9gQqdr40w==} + '@supercharge/promise-pool@3.2.0': + resolution: {integrity: sha512-pj0cAALblTZBPtMltWOlZTQSLT07jIaFNeM8TWoJD1cQMgDB9mcMlVMoetiB35OzNJpqQ2b+QEtwiR9f20mADg==} + engines: {node: '>=8'} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} engines: {node: '>=14'} @@ -10123,6 +10174,10 @@ packages: resolution: {integrity: sha512-1aQJZX2Ax5X7Bq9j9Wkv0gczxexnkshlNNxTc0sD5DjAb+NIgfHkI3rpnjSgr6pK1s4V0Z7viBgE9/FHcIwkyw==} engines: {node: '>=8'} + algo-msgpack-with-bigint@2.1.1: + resolution: {integrity: sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ==} + engines: {node: '>= 10'} + algoliasearch-helper@3.22.6: resolution: {integrity: sha512-F2gSb43QHyvZmvH/2hxIjbk/uFdO2MguQYTFP7J+RowMW1csjIODMobEnpLI8nbLQuzZnGZdIxl5Bpy1k9+CFQ==} peerDependencies: @@ -10135,6 +10190,10 @@ packages: resolution: {integrity: sha512-zrLtGhC63z3sVLDDKGW+SlCRN9eJHFTgdEmoAOpsVh6wgGL1GgTTDou7tpCBjevzgIvi3AIyDAQO3Xjbg5eqZg==} engines: {node: '>= 14.0.0'} + algosdk@1.24.1: + resolution: {integrity: sha512-9moZxdqeJ6GdE4N6fA/GlUP4LrbLZMYcYkt141J4Ss68OfEgH9qW0wBuZ3ZOKEx/xjc5bg7mLP2Gjg7nwrkmww==} + engines: {node: '>=14.0.0'} + amp-message@0.1.2: resolution: {integrity: sha512-JqutcFwoU1+jhv7ArgW38bqrE+LQdcRv4NxNw0mp0JHQyB6tXesWRjtYKlDgHRY2o3JE5UTaBGUK8kSWUdxWUg==} @@ -10225,6 +10284,9 @@ packages: aproba@2.0.0: resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + arconnect@0.4.2: + resolution: {integrity: sha512-Jkpd4QL3TVqnd3U683gzXmZUVqBUy17DdJDuL/3D9rkysLgX6ymJ2e+sR+xyZF5Rh42CBqDXWNMmCjBXeP7Gbw==} + are-docs-informative@0.0.2: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} @@ -10317,9 +10379,21 @@ packages: resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} engines: {node: '>=8'} + arweave-stream-tx@1.2.2: + resolution: {integrity: sha512-bNt9rj0hbAEzoUZEF2s6WJbIz8nasZlZpxIw03Xm8fzb9gRiiZlZGW3lxQLjfc9Z0VRUWDzwtqoYeEoB/JDToQ==} + peerDependencies: + arweave: ^1.10.0 + + arweave@1.15.5: + resolution: {integrity: sha512-Zj3b8juz1ZtDaQDPQlzWyk2I4wZPx3RmcGq8pVJeZXl2Tjw0WRy5ueHPelxZtBLqCirGoZxZEAFRs6SZUSCBjg==} + engines: {node: '>=18'} + asn1.js@4.10.1: resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + asn1.js@5.4.1: + resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} + asn1@0.2.6: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} @@ -11798,6 +11872,9 @@ packages: csv-parse@5.6.0: resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==} + csv-stringify@6.5.2: + resolution: {integrity: sha512-RFPahj0sXcmUyjrObAK+DOWtMvMIFV328n4qZJhgX3x2RqkQgOTU2mCUmiFR0CzM6AzChlRSUErjiJeEt8BaQA==} + csv-writer@1.6.0: resolution: {integrity: sha512-NOx7YDFWEsM/fTRAJjRpPp8t+MKRVvniAg9wQlUKx20MFrPs73WLJhFf5iteqrxNYnsy924K3Iroh3yNHeYd2g==} @@ -13028,6 +13105,10 @@ packages: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} + extract-files@9.0.0: + resolution: {integrity: sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==} + engines: {node: ^10.17.0 || ^12.0.0 || >= 13.7.0} + extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -13312,6 +13393,10 @@ packages: resolution: {integrity: sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==} engines: {node: '>= 0.12'} + form-data@3.0.2: + resolution: {integrity: sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==} + engines: {node: '>= 6'} + form-data@4.0.1: resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} engines: {node: '>= 6'} @@ -13706,6 +13791,11 @@ packages: graphemesplit@2.4.4: resolution: {integrity: sha512-lKrpp1mk1NH26USxC/Asw4OHbhSQf5XfrWZ+CDv/dFVvd1j17kFgMotdJvOesmHkbFX9P9sBfpH8VogxOWLg8w==} + graphql-request@4.3.0: + resolution: {integrity: sha512-2v6hQViJvSsifK606AliqiNiijb1uwWp6Re7o0RTyH+uRTv/u7Uqm2g4Fjq/LgZIzARB38RZEvVBFOQOVdlBow==} + peerDependencies: + graphql: 14 - 16 + graphql-request@6.1.0: resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} peerDependencies: @@ -13883,6 +13973,9 @@ packages: hey-listen@1.0.8: resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + hi-base32@0.5.1: + resolution: {integrity: sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==} + history@4.10.1: resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} @@ -14857,6 +14950,9 @@ packages: js-sha3@0.8.0: resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + js-sha512@0.8.0: + resolution: {integrity: sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==} + js-tiktoken@1.0.15: resolution: {integrity: sha512-65ruOWWXDEZHHbAo7EjOcNxOGasQKbL4Fq3jEr2xsCqSsoOo6VVSqzWQb6PRIqypFSDcma4jO90YP0w5X8qVXQ==} @@ -16166,6 +16262,9 @@ packages: resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} engines: {node: '>=10'} + multistream@4.1.0: + resolution: {integrity: sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw==} + mustache@4.0.0: resolution: {integrity: sha512-FJgjyX/IVkbXBXYUwH+OYwQKqWpFPLaLVESd70yHjSDunwzV2hZOoTBvPf4KLoxesUzzyfTH6F784Uqd7Wm5yA==} engines: {npm: '>=1.4.0'} @@ -19879,6 +19978,9 @@ packages: resolution: {integrity: sha512-LQIHmHnuzfZgZWAf2HzL83TIIrD8NhhI0DVxqo9/FdOd4ilec+NTNZOlDZf7EwrTNoutccbsHjvWHYXLAtvxjw==} hasBin: true + tmp-promise@3.0.3: + resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -21027,6 +21129,9 @@ packages: resolution: {integrity: sha512-sfAcO2yeSU0CSPFI/DmZp3FsFE9T+8913nv1xWBOyzODv13fwkn6Vl7HqxGpkr9F608M+8SuFId3s+BlZqfXww==} engines: {node: '>=4.0'} + vlq@2.0.4: + resolution: {integrity: sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA==} + vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} @@ -26571,6 +26676,102 @@ snapshots: '@ioredis/commands@1.2.0': {} + '@irys/arweave@0.0.2': + dependencies: + asn1.js: 5.4.1 + async-retry: 1.3.3 + axios: 1.7.9(debug@4.4.0) + base64-js: 1.5.1 + bignumber.js: 9.1.2 + transitivePeerDependencies: + - debug + + '@irys/bundles@0.0.1(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)': + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/providers': 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@ethersproject/signing-key': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/wallet': 5.7.0 + '@irys/arweave': 0.0.2 + '@noble/ed25519': 1.7.3 + base64url: 3.0.1 + bs58: 4.0.1 + keccak: 3.0.4 + secp256k1: 5.0.1 + optionalDependencies: + '@randlabs/myalgo-connect': 1.4.2 + algosdk: 1.24.1(encoding@0.1.13) + arweave-stream-tx: 1.2.2(arweave@1.15.5) + multistream: 4.1.0 + tmp-promise: 3.0.3 + transitivePeerDependencies: + - arweave + - bufferutil + - debug + - encoding + - utf-8-validate + + '@irys/query@0.0.9': + dependencies: + async-retry: 1.3.3 + axios: 1.7.9(debug@4.4.0) + transitivePeerDependencies: + - debug + + '@irys/upload-core@0.0.9(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)': + dependencies: + '@irys/bundles': 0.0.1(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@irys/query': 0.0.9 + '@supercharge/promise-pool': 3.2.0 + async-retry: 1.3.3 + axios: 1.7.9(debug@4.4.0) + base64url: 3.0.1 + bignumber.js: 9.1.2 + transitivePeerDependencies: + - arweave + - bufferutil + - debug + - encoding + - utf-8-validate + + '@irys/upload-ethereum@0.0.14(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)': + dependencies: + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/contracts': 5.7.0 + '@ethersproject/providers': 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@ethersproject/wallet': 5.7.0 + '@irys/bundles': 0.0.1(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@irys/upload': 0.0.14(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@irys/upload-core': 0.0.9(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + bignumber.js: 9.1.2 + transitivePeerDependencies: + - arweave + - bufferutil + - debug + - encoding + - utf-8-validate + + '@irys/upload@0.0.14(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)': + dependencies: + '@irys/bundles': 0.0.1(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@irys/upload-core': 0.0.9(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + async-retry: 1.3.3 + axios: 1.7.9(debug@4.4.0) + base64url: 3.0.1 + bignumber.js: 9.1.2 + csv-parse: 5.6.0 + csv-stringify: 6.5.2 + inquirer: 8.2.6 + mime-types: 2.1.35 + transitivePeerDependencies: + - arweave + - bufferutil + - debug + - encoding + - utf-8-validate + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -29845,6 +30046,14 @@ snapshots: '@radix-ui/rect@1.1.0': {} + '@randlabs/communication-bridge@1.0.1': + optional: true + + '@randlabs/myalgo-connect@1.4.2': + dependencies: + '@randlabs/communication-bridge': 1.0.1 + optional: true + '@raydium-io/raydium-sdk-v2@0.1.82-alpha(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 @@ -31482,6 +31691,8 @@ snapshots: - bufferutil - utf-8-validate + '@supercharge/promise-pool@3.2.0': {} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 @@ -33534,6 +33745,9 @@ snapshots: alawmulaw@6.0.0: {} + algo-msgpack-with-bigint@2.1.1: + optional: true + algoliasearch-helper@3.22.6(algoliasearch@4.24.0): dependencies: '@algolia/events': 4.0.1 @@ -33573,6 +33787,22 @@ snapshots: '@algolia/requester-fetch': 5.19.0 '@algolia/requester-node-http': 5.19.0 + algosdk@1.24.1(encoding@0.1.13): + dependencies: + algo-msgpack-with-bigint: 2.1.1 + buffer: 6.0.3 + cross-fetch: 3.2.0(encoding@0.1.13) + hi-base32: 0.5.1 + js-sha256: 0.9.0 + js-sha3: 0.8.0 + js-sha512: 0.8.0 + json-bigint: 1.0.0 + tweetnacl: 1.0.3 + vlq: 2.0.4 + transitivePeerDependencies: + - encoding + optional: true + amp-message@0.1.2: dependencies: amp: 0.3.1 @@ -33648,6 +33878,11 @@ snapshots: aproba@2.0.0: {} + arconnect@0.4.2: + dependencies: + arweave: 1.15.5 + optional: true + are-docs-informative@0.0.2: {} are-we-there-yet@2.0.0: @@ -33754,12 +33989,33 @@ snapshots: arrify@2.0.1: {} + arweave-stream-tx@1.2.2(arweave@1.15.5): + dependencies: + arweave: 1.15.5 + exponential-backoff: 3.1.1 + optional: true + + arweave@1.15.5: + dependencies: + arconnect: 0.4.2 + asn1.js: 5.4.1 + base64-js: 1.5.1 + bignumber.js: 9.1.2 + optional: true + asn1.js@4.10.1: dependencies: bn.js: 4.12.1 inherits: 2.0.4 minimalistic-assert: 1.0.1 + asn1.js@5.4.1: + dependencies: + bn.js: 4.12.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + safer-buffer: 2.1.2 + asn1@0.2.6: dependencies: safer-buffer: 2.1.2 @@ -35732,6 +35988,8 @@ snapshots: csv-parse@5.6.0: {} + csv-stringify@6.5.2: {} + csv-writer@1.6.0: {} culvert@0.1.2: {} @@ -37463,6 +37721,8 @@ snapshots: iconv-lite: 0.4.24 tmp: 0.0.33 + extract-files@9.0.0: {} + extract-zip@2.0.1: dependencies: debug: 4.4.0(supports-color@5.5.0) @@ -37800,6 +38060,12 @@ snapshots: mime-types: 2.1.35 safe-buffer: 5.2.1 + form-data@3.0.2: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + form-data@4.0.1: dependencies: asynckit: 0.4.0 @@ -38333,6 +38599,15 @@ snapshots: js-base64: 3.7.7 unicode-trie: 2.0.0 + graphql-request@4.3.0(encoding@0.1.13)(graphql@16.10.0): + dependencies: + cross-fetch: 3.2.0(encoding@0.1.13) + extract-files: 9.0.0 + form-data: 3.0.2 + graphql: 16.10.0 + transitivePeerDependencies: + - encoding + graphql-request@6.1.0(encoding@0.1.13)(graphql@16.10.0): dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.10.0) @@ -38629,6 +38904,9 @@ snapshots: hey-listen@1.0.8: {} + hi-base32@0.5.1: + optional: true + history@4.10.1: dependencies: '@babel/runtime': 7.26.0 @@ -40099,6 +40377,9 @@ snapshots: js-sha3@0.8.0: {} + js-sha512@0.8.0: + optional: true + js-tiktoken@1.0.15: dependencies: base64-js: 1.5.1 @@ -40758,7 +41039,7 @@ snapshots: log-symbols@4.1.0: dependencies: - chalk: 4.1.0 + chalk: 4.1.2 is-unicode-supported: 0.1.0 log-symbols@6.0.0: @@ -41855,6 +42136,12 @@ snapshots: arrify: 2.0.1 minimatch: 3.0.5 + multistream@4.1.0: + dependencies: + once: 1.4.0 + readable-stream: 3.6.2 + optional: true + mustache@4.0.0: {} mustache@4.2.0: {} @@ -46391,6 +46678,11 @@ snapshots: dependencies: tldts-core: 6.1.71 + tmp-promise@3.0.3: + dependencies: + tmp: 0.2.3 + optional: true + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -47943,6 +48235,9 @@ snapshots: ini: 1.3.8 js-git: 0.7.8 + vlq@2.0.4: + optional: true + vm-browserify@1.1.2: {} vscode-jsonrpc@8.2.0: {}