diff --git a/typescript/packages/plugins/jupiter/package.json b/typescript/packages/plugins/jupiter/package.json new file mode 100644 index 000000000..28a5f9b3c --- /dev/null +++ b/typescript/packages/plugins/jupiter/package.json @@ -0,0 +1,34 @@ +{ + "name": "@goat-sdk/plugin-jupiter", + "version": "0.0.1", + "files": ["dist/**/*", "README.md", "package.json"], + "scripts": { + "build": "tsup", + "clean": "rm -rf dist", + "test": "vitest run --passWithNoTests" + }, + "sideEffects": false, + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "dependencies": { + "@jup-ag/api": "6.0.31", + "@goat-sdk/core": "workspace:*", + "zod": "catalog:", + "@solana/web3.js": "catalog:" + }, + "peerDependencies": { + "@jup-ag/api": "6.0.31", + "@goat-sdk/core": "workspace:*" + }, + "homepage": "https://ohmygoat.dev", + "repository": { + "type": "git", + "url": "git+https://github.com/goat-sdk/goat.git" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/goat-sdk/goat/issues" + }, + "keywords": ["ai", "agents", "web3"] +} diff --git a/typescript/packages/plugins/jupiter/src/index.ts b/typescript/packages/plugins/jupiter/src/index.ts new file mode 100644 index 000000000..39b9a61b2 --- /dev/null +++ b/typescript/packages/plugins/jupiter/src/index.ts @@ -0,0 +1 @@ +export * from "./plugin"; diff --git a/typescript/packages/plugins/jupiter/src/parameters.ts b/typescript/packages/plugins/jupiter/src/parameters.ts new file mode 100644 index 000000000..94bdb1853 --- /dev/null +++ b/typescript/packages/plugins/jupiter/src/parameters.ts @@ -0,0 +1,80 @@ +import { + type QuoteGetRequest, + QuoteGetSwapModeEnum, + type QuoteResponse, + type SwapInfo, + type SwapPostRequest, +} from "@jup-ag/api"; +import { z } from "zod"; + +export const getQuoteParametersSchema: z.ZodType = z.object({ + inputMint: z.string().describe("The token to swap from"), + outputMint: z.string().describe("The token to swap to"), + amount: z.number().describe("The amount of tokens to swap"), + slippageBps: z.number().optional().describe("The slippage in bps"), + autoSlippage: z.boolean().optional().describe("Whether to use auto slippage"), + autoSlippageCollisionUsdValue: z.number().optional().describe("The collision USD value for auto slippage"), + computeAutoSlippage: z.boolean().optional().describe("Whether to compute auto slippage"), + maxAutoSlippageBps: z.number().optional().describe("The maximum auto slippage in bps"), + swapMode: z.nativeEnum(QuoteGetSwapModeEnum).optional().describe("The swap mode"), + dexes: z.array(z.string()).optional().describe("The dexes to use"), + excludeDexes: z.array(z.string()).optional().describe("The dexes to exclude"), + restrictIntermediateTokens: z.boolean().optional().describe("Whether to restrict intermediate tokens"), + onlyDirectRoutes: z.boolean().optional().describe("Whether to only use direct routes"), + asLegacyTransaction: z.boolean().optional().describe("Whether to return the transaction as a legacy transaction"), + platformFeeBps: z.number().optional().describe("The platform fee in bps"), + maxAccounts: z.number().optional().describe("The maximum number of accounts"), + minimizeSlippage: z.boolean().optional().describe("Whether to minimize slippage"), + preferLiquidDexes: z.boolean().optional().describe("Whether to prefer liquid dexes"), + tokenCategoryBasedIntermediateTokens: z + .boolean() + .optional() + .describe("Whether to use token category based intermediate tokens"), +}); + +export const swapInfoSchema: z.ZodType = z.object({ + ammKey: z.string().describe("The AMM key"), + label: z.string().optional().describe("The label"), + inputMint: z.string().describe("The token to swap from"), + outputMint: z.string().describe("The token to swap to"), + inAmount: z.string().describe("The amount of tokens to swap"), + outAmount: z.string().describe("The amount of tokens to swap"), + feeAmount: z.string().describe("The fee amount"), + feeMint: z.string().describe("The fee mint"), +}); + +export const quoteResponseSchema: z.ZodType = z.object({ + inputMint: z.string().describe("The token to swap from"), + inAmount: z.string().describe("The amount of tokens to swap"), + outputMint: z.string().describe("The token to swap to"), + outAmount: z.string().describe("The amount of tokens to swap"), + otherAmountThreshold: z.string().describe("The amount of tokens to swap"), + swapMode: z.enum(["ExactIn", "ExactOut"]).describe("The swap mode"), + slippageBps: z.number().describe("The slippage in bps"), + computedAutoSlippage: z.number().optional().describe("The computed auto slippage"), + platformFee: z + .object({ + amount: z.string().describe("The amount of tokens to swap"), + feeBps: z.number().describe("The platform fee in bps"), + }) + .optional() + .describe("The platform fee"), + priceImpactPct: z.string().describe("The price impact in percentage"), + routePlan: z + .array( + z.object({ + swapInfo: swapInfoSchema.describe("The swap info"), + percent: z.number().describe("The percent of the route plan step"), + }), + ) + .describe("The route plan"), + contextSlot: z.number().optional().describe("The context slot"), + timeTaken: z.number().optional().describe("The time taken"), +}); + +export const swapParametersSchema: z.ZodType = z.object({ + swapRequest: z.object({ + userPublicKey: z.string().describe("The user public key"), + quoteResponse: quoteResponseSchema.describe("The quote response"), + }), +}); diff --git a/typescript/packages/plugins/jupiter/src/plugin.ts b/typescript/packages/plugins/jupiter/src/plugin.ts new file mode 100644 index 000000000..bdc624ec6 --- /dev/null +++ b/typescript/packages/plugins/jupiter/src/plugin.ts @@ -0,0 +1,51 @@ +import type { Plugin, SolanaWalletClient } from "@goat-sdk/core"; +import { createJupiterApiClient } from "@jup-ag/api"; +import { TransactionMessage, VersionedTransaction } from "@solana/web3.js"; +import type { z } from "zod"; +import { getQuoteParametersSchema, quoteResponseSchema } from "./parameters"; + +export function jupiter(): Plugin { + return { + name: "jupiter", + supportsSmartWallets: () => false, + supportsChain: (chain) => chain.type === "solana", + getTools: async () => { + return [ + { + name: "get_quote", + description: "This {{tool}} gets a quote for a swap on the Jupiter DEX.", + parameters: getQuoteParametersSchema, + method: (walletClient, parameters: z.infer) => + createJupiterApiClient().quoteGet(parameters), + }, + { + name: "get_swap_transaction", + description: "This {{tool}} returns a transaction to swap tokens on the Jupiter DEX.", + parameters: quoteResponseSchema, + method: async (walletClient, parameters: z.infer) => { + const response = await createJupiterApiClient().swapPost({ + swapRequest: { + userPublicKey: walletClient.getAddress(), + quoteResponse: parameters, + }, + }); + + const serializedTransaction = response.swapTransaction; + const deserializedTransaction = VersionedTransaction.deserialize( + Buffer.from(serializedTransaction, "base64"), + ); + const instructions = TransactionMessage.decompile(deserializedTransaction.message).instructions; + + return { + serializedTransaction, + instructions, + lastValidBlockHeight: response.lastValidBlockHeight, + prioritizationFeeLamports: response.prioritizationFeeLamports, + dynamicSlippageReport: response.dynamicSlippageReport, + }; + }, + }, + ]; + }, + }; +} diff --git a/typescript/packages/plugins/jupiter/tsconfig.json b/typescript/packages/plugins/jupiter/tsconfig.json new file mode 100644 index 000000000..b4ae67c1f --- /dev/null +++ b/typescript/packages/plugins/jupiter/tsconfig.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../../tsconfig.base.json", + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/typescript/packages/plugins/jupiter/tsup.config.ts b/typescript/packages/plugins/jupiter/tsup.config.ts new file mode 100644 index 000000000..2d38789ad --- /dev/null +++ b/typescript/packages/plugins/jupiter/tsup.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from "tsup"; +import { treeShakableConfig } from "../../../tsup.config.base"; + +export default defineConfig({ + ...treeShakableConfig, +}); diff --git a/typescript/pnpm-lock.yaml b/typescript/pnpm-lock.yaml index 3ea3a1602..ca02f3678 100644 --- a/typescript/pnpm-lock.yaml +++ b/typescript/pnpm-lock.yaml @@ -631,6 +631,21 @@ importers: specifier: 'catalog:' version: 3.23.8 + packages/plugins/jupiter: + dependencies: + '@goat-sdk/core': + specifier: workspace:* + version: link:../../core + '@jup-ag/api': + specifier: 6.0.31 + version: 6.0.31 + '@solana/web3.js': + specifier: 'catalog:' + version: 1.95.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + zod: + specifier: 'catalog:' + version: 3.23.8 + packages/plugins/polymarket: dependencies: '@goat-sdk/core': @@ -3587,8 +3602,16 @@ packages: '@jridgewell/sourcemap-codec': 1.5.0 dev: true +<<<<<<< HEAD /@langchain/core@0.3.22: resolution: {integrity: sha512-9rwEbxJi3Fgs8XuealNYxB6s0FCOnvXLnpiV5/oKgmEJtCRS91IqgJCWA8d59s4YkaEply/EsZVc2azNPK6Wjw==} +======= + '@jup-ag/api@6.0.31': + resolution: {integrity: sha512-MNzK/dEkQ6pHChcu7abRzIbgNrEJdzckeuCQNt46B96Hvzqb9T/m05WBUKjputVY9SrRYslZ2QwFRYsHlv+g9Q==} + + '@langchain/core@0.3.18': + resolution: {integrity: sha512-IEZCrFs1Xd0J2FTH1D3Lnm3/Yk2r8LSpwDeLYwcCom3rNAK5k4mKQ2rwIpNq3YuqBdrTNMKRO+PopjkP1SB17A==} +>>>>>>> 5882c56 (plugin: jupiter (#34)) engines: {node: '>=18'} dependencies: ansi-styles: 5.2.0 @@ -14807,6 +14830,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jup-ag/api@6.0.31': {} + '@langchain/core@0.3.18(openai@4.69.0(zod@3.23.8))': dependencies: ansi-styles: 5.2.0