diff --git a/packages/plugin-nft-collections/jest.config.ts b/packages/plugin-nft-collections/jest.config.ts deleted file mode 100644 index 13974b484f4..00000000000 --- a/packages/plugin-nft-collections/jest.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -export default { - preset: "ts-jest", - testEnvironment: "node", - extensionsToTreatAsEsm: [".ts"], - moduleNameMapper: { - "^(\\.{1,2}/.*)\\.js$": "$1", - }, - transform: { - "^.+\\.tsx?$": [ - "ts-jest", - { - useESM: true, - }, - ], - }, -}; diff --git a/packages/plugin-nft-collections/package.json b/packages/plugin-nft-collections/package.json index afac6d85260..50e8eff6838 100644 --- a/packages/plugin-nft-collections/package.json +++ b/packages/plugin-nft-collections/package.json @@ -7,7 +7,8 @@ "types": "dist/index.d.ts", "scripts": { "build": "tsup src/index.ts --format esm --dts", - "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", + "test": "vitest run", + "test:watch": "vitest", "lint": "eslint src --ext .ts", "format": "prettier --write src/**/*.ts" }, @@ -18,16 +19,14 @@ "rate-limiter-flexible": "^5.0.4" }, "devDependencies": { - "@types/jest": "^29.5.14", "@types/node": "^20.11.16", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "eslint": "^8.56.0", - "jest": "^29.7.0", "prettier": "^3.2.5", - "ts-jest": "^29.2.5", "tsup": "^8.0.1", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "vitest": "^2.1.5" }, "peerDependencies": { "@ai16z/eliza": "workspace:*" diff --git a/packages/plugin-nft-collections/src/__tests__/reservoir.test.ts b/packages/plugin-nft-collections/src/__tests__/reservoir.test.ts new file mode 100644 index 00000000000..ad2c7e1edf4 --- /dev/null +++ b/packages/plugin-nft-collections/src/__tests__/reservoir.test.ts @@ -0,0 +1,47 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { IAgentRuntime } from "@ai16z/eliza"; +import { ReservoirService } from "../services/reservoir"; +import { MemoryCacheManager } from "../services/cache-manager"; +import { RateLimiter } from "../services/rate-limiter"; + +describe("ReservoirService", () => { + const mockRuntime = { + services: { + get: vi.fn(), + }, + messageManager: { + createMemory: vi.fn(), + }, + agentId: "00000000-0000-0000-0000-000000000000", + } as unknown as IAgentRuntime; + + let service: ReservoirService; + let cacheManager: MemoryCacheManager; + let rateLimiter: RateLimiter; + + beforeEach(() => { + cacheManager = new MemoryCacheManager(); + rateLimiter = new RateLimiter(); + service = new ReservoirService("test-api-key", { + cacheManager, + rateLimiter, + }); + }); + + it("should initialize correctly", async () => { + await service.initialize(mockRuntime); + expect(service).toBeDefined(); + }); + + it("should handle API requests with caching", async () => { + const mockData = { collections: [] }; + vi.spyOn(global, "fetch").mockResolvedValueOnce({ + ok: true, + json: () => Promise.resolve(mockData), + } as Response); + + const result = await service.getTopCollections(5); + expect(result).toBeDefined(); + expect(Array.isArray(result)).toBe(true); + }); +}); diff --git a/packages/plugin-nft-collections/src/actions/list-nft.ts b/packages/plugin-nft-collections/src/actions/list-nft.ts index b2189e6834d..6c1efcae90d 100644 --- a/packages/plugin-nft-collections/src/actions/list-nft.ts +++ b/packages/plugin-nft-collections/src/actions/list-nft.ts @@ -7,12 +7,12 @@ function extractListingDetails(text: string): { collectionAddress: string | null; price?: number | null; } { - const addressMatch = text.match(/0x[a-fA-F0-9]{40}/); - const tokenIdMatch = text.match(/token(?:Id)?\s*#?\s*(\d+)/i); + const addressMatch = text.match(/(?:collection|from)\s*(0x[a-fA-F0-9]+)/i); + const tokenIdMatch = text.match(/(?:token|nft)\s*#?\s*(\d+)/i); const priceMatch = text.match(/(\d+(?:\.\d+)?)\s*(?:eth|Ξ)/i); return { - collectionAddress: addressMatch ? addressMatch[0] : null, + collectionAddress: addressMatch ? addressMatch[1] : null, tokenId: tokenIdMatch ? tokenIdMatch[1] : null, price: priceMatch ? parseFloat(priceMatch[1]) : undefined, }; @@ -69,38 +69,11 @@ export const listNFTAction: Action = { throw new Error("You don't own this NFT"); } - // Get purchase history to determine the purchase price - const activity = - await nftService.getCollectionActivity(collectionAddress); - const purchaseTransaction = activity.activities?.find( - (act: any) => - act.type === "sale" && - act.toAddress?.toLowerCase() === - message.userId.toLowerCase() && - act.token?.tokenId === tokenId - ); - - if (!purchaseTransaction) { - throw new Error( - "Could not find purchase history for this NFT. Please specify a listing price." - ); - } - - // Calculate listing price (double the purchase price) - const purchasePrice = purchaseTransaction.price?.amount?.native; - if (!purchasePrice) { - throw new Error( - "Could not determine purchase price. Please specify a listing price." - ); - } - - const listingPrice = userSpecifiedPrice || purchasePrice * 2; - // Create the listing on ikigailabs const listing = await nftService.createListing({ tokenId, collectionAddress, - price: listingPrice, + price: userSpecifiedPrice || 0, // Default to 0 if no price specified marketplace: "ikigailabs", expirationTime: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60, // 30 days @@ -110,8 +83,7 @@ export const listNFTAction: Action = { `Successfully created listing on ikigailabs.xyz:\n` + `• Collection: ${collectionAddress}\n` + `• Token ID: ${tokenId}\n` + - `• Purchase Price: ${purchasePrice} ETH\n` + - `• Listing Price: ${listingPrice} ETH (${userSpecifiedPrice ? "user specified" : "2x purchase price"})\n` + + `• Listing Price: ${userSpecifiedPrice} ETH\n` + `• Status: ${listing.status}\n` + `• Listing URL: ${listing.marketplaceUrl}\n` + (listing.transactionHash diff --git a/packages/plugin-nft-collections/src/templates/nft-listing.ts b/packages/plugin-nft-collections/src/templates/nft-listing.ts index edf2f3cb3cd..b908b7effe1 100644 --- a/packages/plugin-nft-collections/src/templates/nft-listing.ts +++ b/packages/plugin-nft-collections/src/templates/nft-listing.ts @@ -22,8 +22,8 @@ export const listingTemplates = { }) => `Successfully created listing on ikigailabs.xyz: • Collection: ${typeof collection === "string" ? collection : collection.name} (${typeof collection === "string" ? collection : collection.address}) • Token ID: ${tokenId} -• Purchase Price: ${purchasePrice} ETH -• Listing Price: ${listingPrice} ETH (${isPriceAutomatic ? "2x purchase price" : "user specified"}) +• Purchase Price: ${purchasePrice.toFixed(1)} ETH +• Listing Price: ${listingPrice.toFixed(1)} ETH (${isPriceAutomatic ? "2x purchase price" : "user specified"}) • Status: ${status} • Listing URL: ${marketplaceUrl}${transactionHash ? `\n• Transaction: ${transactionHash}` : ""}`, diff --git a/packages/plugin-nft-collections/src/tests/actions.test.ts b/packages/plugin-nft-collections/src/tests/actions.test.ts index f43005898a4..3a292a98e05 100644 --- a/packages/plugin-nft-collections/src/tests/actions.test.ts +++ b/packages/plugin-nft-collections/src/tests/actions.test.ts @@ -86,12 +86,14 @@ describe("NFT Actions", () => { const result = await listNFTAction.handler(mockRuntime, message); expect(result).toBe(true); - expect(mockNftService.createListing).toHaveBeenCalledWith({ - tokenId: "123", - collectionAddress: "0x1234", - price: 1.5, - marketplace: "ikigailabs", - }); + expect(mockNftService.createListing).toHaveBeenCalledWith( + expect.objectContaining({ + tokenId: "123", + collectionAddress: "0x1234", + price: 1.5, + marketplace: "ikigailabs", + }) + ); }); it("should handle NFT not owned error", async () => { diff --git a/packages/plugin-nft-collections/src/tests/templates.test.ts b/packages/plugin-nft-collections/src/tests/templates.test.ts index 88f29ccde7a..ec5e97a2f29 100644 --- a/packages/plugin-nft-collections/src/tests/templates.test.ts +++ b/packages/plugin-nft-collections/src/tests/templates.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from "@jest/globals"; +import { describe, expect, it } from "vitest"; import { listingTemplates, floorSweepTemplates, diff --git a/packages/plugin-nft-collections/vitest.config.ts b/packages/plugin-nft-collections/vitest.config.ts new file mode 100644 index 00000000000..47c872fae34 --- /dev/null +++ b/packages/plugin-nft-collections/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globals: true, + environment: "node", + include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], + coverage: { + reporter: ["text", "json", "html"], + include: ["src/**/*.ts"], + exclude: ["src/**/*.{test,spec}.ts"], + }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e4f1fd401ca..b5f668c7cf4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1255,9 +1255,6 @@ importers: specifier: ^5.0.4 version: 5.0.4 devDependencies: - '@types/jest': - specifier: ^29.5.14 - version: 29.5.14 '@types/node': specifier: ^20.11.16 version: 20.17.9 @@ -1270,21 +1267,18 @@ importers: eslint: specifier: ^8.56.0 version: 8.57.1 - jest: - specifier: ^29.7.0 - version: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) prettier: specifier: ^3.2.5 version: 3.4.1 - ts-jest: - specifier: ^29.2.5 - version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.0)(jest@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)))(typescript@5.6.3) tsup: specifier: ^8.0.1 version: 8.3.5(@swc/core@1.10.1(@swc/helpers@0.5.15))(jiti@2.4.1)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.6.1) typescript: specifier: ^5.3.3 version: 5.6.3 + vitest: + specifier: ^2.1.5 + version: 2.1.5(@types/node@20.17.9)(jsdom@25.0.1(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) packages/plugin-nft-generation: dependencies: @@ -23404,41 +23398,6 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3))': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.17.9 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3))': dependencies: '@jest/console': 29.7.0 @@ -30702,21 +30661,6 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - create-jest@29.7.0(@types/node@22.10.2): dependencies: '@jest/types': 29.6.3 @@ -34516,25 +34460,6 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jest-cli@29.7.0(@types/node@22.10.2): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@18.19.68)(typescript@5.6.3)) @@ -34635,37 +34560,6 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)): - dependencies: - '@babel/core': 7.26.0 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.0) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 20.17.9 - ts-node: 10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - jest-config@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3)): dependencies: '@babel/core': 7.26.0 @@ -35003,18 +34897,6 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) - '@jest/types': 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jest@29.7.0(@types/node@22.10.2): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@18.19.68)(typescript@5.6.3)) @@ -40982,26 +40864,6 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.0)(jest@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)))(typescript@5.6.3): - dependencies: - bs-logger: 0.2.6 - ejs: 3.1.10 - fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) - jest-util: 29.7.0 - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.6.3 - typescript: 5.6.3 - yargs-parser: 21.1.1 - optionalDependencies: - '@babel/core': 7.26.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.0) - esbuild: 0.24.0 - ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@18.19.68)(ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@18.19.68)(typescript@5.6.3)))(typescript@5.6.3): dependencies: bs-logger: 0.2.6 @@ -41081,27 +40943,6 @@ snapshots: optionalDependencies: '@swc/core': 1.10.1(@swc/helpers@0.5.15) - ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.17.9 - acorn: 8.14.0 - acorn-walk: 8.3.4 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.6.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optionalDependencies: - '@swc/core': 1.10.1(@swc/helpers@0.5.15) - optional: true - ts-node@10.9.2(@swc/core@1.10.1(@swc/helpers@0.5.15))(@types/node@22.10.2)(typescript@5.6.3): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -41846,6 +41687,24 @@ snapshots: - supports-color - terser + vite-node@2.1.5(@types/node@20.17.9)(terser@5.37.0): + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.5.4 + pathe: 1.1.2 + vite: 5.4.11(@types/node@20.17.9)(terser@5.37.0) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vite-node@2.1.5(@types/node@22.10.2)(terser@5.37.0): dependencies: cac: 6.7.14 @@ -41896,6 +41755,16 @@ snapshots: dependencies: vite: link:client/@tanstack/router-plugin/vite + vite@5.4.11(@types/node@20.17.9)(terser@5.37.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.4.49 + rollup: 4.28.1 + optionalDependencies: + '@types/node': 20.17.9 + fsevents: 2.3.3 + terser: 5.37.0 + vite@5.4.11(@types/node@22.10.2)(terser@5.37.0): dependencies: esbuild: 0.21.5 @@ -41952,6 +41821,42 @@ snapshots: - supports-color - terser + vitest@2.1.5(@types/node@20.17.9)(jsdom@25.0.1(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0): + dependencies: + '@vitest/expect': 2.1.5 + '@vitest/mocker': 2.1.5(vite@5.4.11(@types/node@22.10.2)(terser@5.37.0)) + '@vitest/pretty-format': 2.1.8 + '@vitest/runner': 2.1.5 + '@vitest/snapshot': 2.1.5 + '@vitest/spy': 2.1.5 + '@vitest/utils': 2.1.5 + chai: 5.1.2 + debug: 4.4.0 + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 1.1.2 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.1 + tinypool: 1.0.2 + tinyrainbow: 1.2.0 + vite: 5.4.11(@types/node@20.17.9)(terser@5.37.0) + vite-node: 2.1.5(@types/node@20.17.9)(terser@5.37.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.17.9 + jsdom: 25.0.1(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10) + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vitest@2.1.5(@types/node@22.10.2)(jsdom@25.0.1(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0): dependencies: '@vitest/expect': 2.1.5