Skip to content

Commit

Permalink
feat: add 0x support (#255)
Browse files Browse the repository at this point in the history
* Add 0x plugin

* Fix example
  • Loading branch information
0xaguspunk authored Jan 21, 2025
1 parent 8d53145 commit 5b08d6c
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 2 deletions.
6 changes: 6 additions & 0 deletions typescript/.changeset/eight-islands-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"goat-examples-vercel-ai-viem": patch
"@goat-sdk/plugin-0x": patch
---

Release 0x plugin
32 changes: 32 additions & 0 deletions typescript/packages/plugins/0x/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@goat-sdk/plugin-0x",
"version": "0.1.0",
"files": ["dist/**/*", "README.md", "package.json"],
"scripts": {
"build": "tsup",
"clean": "rm -rf dist",
"test": "vitest run --passWithNoTests"
},
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"sideEffects": false,
"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"],
"dependencies": {
"@goat-sdk/core": "workspace:*",
"zod": "catalog:",
"@goat-sdk/wallet-evm": "workspace:*"
},
"peerDependencies": {
"@goat-sdk/core": "workspace:*"
}
}
36 changes: 36 additions & 0 deletions typescript/packages/plugins/0x/src/0x.plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { PluginBase } from "@goat-sdk/core";
import { Chain } from "@goat-sdk/core";
import { EVMWalletClient } from "@goat-sdk/wallet-evm";
import { ZeroExService } from "./0x.service";

export type ZeroExCtorParams = {
apiKey: string;
};

const supportedChains = [
"1",
"42161", // arbitrum
"43114", // avalanche
"8453", // base
"81457", // blast
"56", // bsc
"59144", // linea
"5000", // mantle
"34443", // mode
"10", // optimism
"137", // polygon
"534352", // scroll
"480", // worldcoin
];

export class ZeroExPlugin extends PluginBase<EVMWalletClient> {
constructor(params: ZeroExCtorParams) {
super("0x", [new ZeroExService(params.apiKey)]);
}

supportsChain = (chain: Chain) => chain.type === "evm" && supportedChains.includes(chain.id.toString());
}

export function zeroEx(params: ZeroExCtorParams) {
return new ZeroExPlugin(params);
}
67 changes: 67 additions & 0 deletions typescript/packages/plugins/0x/src/0x.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Tool } from "@goat-sdk/core";
import { EVMWalletClient } from "@goat-sdk/wallet-evm";
import { GetQuoteParameters } from "./parameters";

export class ZeroExService {
constructor(private readonly apiKey: string) {}

private async makeRequest(queryParams: Record<string, string | undefined>) {
const filteredParams = Object.fromEntries(
Object.entries(queryParams).filter(([_, v]) => v !== undefined),
) as Record<string, string>;

const url = new URL(
`https://api.0x.org/swap/allowance-holder/quote?${new URLSearchParams(filteredParams).toString()}`,
);

const response = await fetch(url.toString(), {
method: "GET",
headers: {
"0x-api-key": this.apiKey,
"0x-version": "v2",
},
});

if (!response.ok) {
throw new Error(`Failed to fetch ${url}: ${JSON.stringify(await response.text(), null, 2)}`);
}

return response.json();
}

@Tool({
name: "0x_getQuote",
description: "Get a quote for a swap from 0x",
})
async getQuote(walletClient: EVMWalletClient, parameters: GetQuoteParameters) {
const queryParams = {
chainId: walletClient.getChain().id.toString(),
sellToken: parameters.sellToken,
buyToken: parameters.buyToken,
sellAmount: parameters.sellAmount,
taker: parameters.taker,
txOrigin: parameters.txOrigin,
slippageBps: parameters.slippageBps?.toString(),
};

return await this.makeRequest(queryParams);
}

@Tool({
name: "0x_swap",
description: "Swap tokens using 0x",
})
async swap(walletClient: EVMWalletClient, parameters: GetQuoteParameters) {
const quote = await this.getQuote(walletClient, parameters);

const transaction = quote.transaction;

const tx = await walletClient.sendTransaction({
to: transaction.to,
value: transaction.value,
data: transaction.data,
});

return tx.hash;
}
}
2 changes: 2 additions & 0 deletions typescript/packages/plugins/0x/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./0x.plugin";
export * from "./parameters";
26 changes: 26 additions & 0 deletions typescript/packages/plugins/0x/src/parameters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { createToolParameters } from "@goat-sdk/core";
import { z } from "zod";

export class GetQuoteParameters extends createToolParameters(
z.object({
chainId: z.number().describe("The chain ID to swap on"),
sellToken: z.string().describe("The token to sell"),
buyToken: z.string().describe("The token to buy"),
sellAmount: z.string().describe("The amount of tokens to sell in base units"),
taker: z
.string()
.describe("The address which holds the sellToken balance and has the allowance set for the swap"),
txOrigin: z
.string()
.optional()
.describe(
"The contract address of the external account that started the transaction. This is only needed if taker is a smart contract.",
),
slippageBps: z
.number()
.optional()
.describe(
"The maximum acceptable slippage of the buyToken in Bps. If this parameter is set to 0, no slippage will be tolerated. If not provided, the default slippage tolerance is 100Bps",
),
}),
) {}
6 changes: 6 additions & 0 deletions typescript/packages/plugins/0x/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "../../../tsconfig.base.json",
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
6 changes: 6 additions & 0 deletions typescript/packages/plugins/0x/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from "tsup";
import { treeShakableConfig } from "../../../tsup.config.base";

export default defineConfig({
...treeShakableConfig,
});
11 changes: 11 additions & 0 deletions typescript/packages/plugins/0x/turbo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "https://turbo.build/schema.json",
"extends": ["//"],
"tasks": {
"build": {
"inputs": ["src/**", "tsup.config.ts", "!./**/*.test.{ts,tsx}", "tsconfig.json"],
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
12 changes: 12 additions & 0 deletions typescript/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions typescript/scripts/createPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ function validatePluginName(name: string): boolean {
function getWalletClientImport(type: PluginOptions["type"]): string {
switch (type) {
case "evm":
return 'import type { EVMWalletClient } from "@goat-sdk/wallet-evm";';
return 'import { EVMWalletClient } from "@goat-sdk/wallet-evm";';
case "solana":
return 'import type { SolanaWalletClient } from "@goat-sdk/wallet-solana";';
return 'import { SolanaWalletClient } from "@goat-sdk/wallet-solana";';
case "any":
return 'import { WalletClientBase } from "@goat-sdk/core";';
}
Expand Down

0 comments on commit 5b08d6c

Please sign in to comment.