Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement coinmarketcap plugin with @Tool decorator #175

Merged
merged 20 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
65badd7
refactor: migrate coinmarketcap plugin to use @Tool decorator
devin-ai-integration[bot] Jan 3, 2025
53081a4
refactor: remove tools.ts after migration to @Tool decorator
devin-ai-integration[bot] Jan 3, 2025
9ee2149
feat: implement coinmarketcap plugin with @Tool decorator and integra…
devin-ai-integration[bot] Jan 3, 2025
b39afad
fix: address lint formatting issues
devin-ai-integration[bot] Jan 3, 2025
f25ab8b
fix: add explicit type annotations to parameter classes
devin-ai-integration[bot] Jan 3, 2025
b77c48f
fix: use createToolParameters correctly for parameter classes
devin-ai-integration[bot] Jan 3, 2025
2e908fd
fix: format parameter class declarations
devin-ai-integration[bot] Jan 3, 2025
b8e94ac
fix: export parameter schemas directly without createToolParameters
devin-ai-integration[bot] Jan 3, 2025
5b68561
fix: add explicit type declarations to parameter classes
devin-ai-integration[bot] Jan 3, 2025
39e5c11
fix: simplify parameter classes to match ERC20 plugin implementation
devin-ai-integration[bot] Jan 3, 2025
feeb656
fix: add static schema properties and separate schema definitions
devin-ai-integration[bot] Jan 4, 2025
af91815
fix: cast zod objects to ZodSchema type
devin-ai-integration[bot] Jan 4, 2025
fe4c2d5
fix: update package.json and fix parameter formatting
devin-ai-integration[bot] Jan 4, 2025
dfd17f4
chore: remove tests as requested
devin-ai-integration[bot] Jan 4, 2025
8c71f2e
style: fix type assertions, import sorting and tool descriptions
devin-ai-integration[bot] Jan 4, 2025
864b1e8
style: apply biome formatting to coinmarketcap plugin
devin-ai-integration[bot] Jan 4, 2025
68c62d6
Fix PR
0xaguspunk Jan 4, 2025
f084dc8
Fix pnpm lock file
0xaguspunk Jan 4, 2025
57b3572
Fix pnpm lock file
0xaguspunk Jan 4, 2025
828c4c8
Add changeset
0xaguspunk Jan 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions goat.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
"name": "[Plugin] 💰 coingecko",
"path": "./typescript/packages/plugins/coingecko"
},
{
"name": "[Plugin] 🧢 coinmarketcap",
"path": "./typescript/packages/plugins/coinmarketcap"
},
{
"name": "[Plugin] 📡 farcaster",
"path": "./typescript/packages/plugins/farcaster"
Expand Down
5 changes: 5 additions & 0 deletions typescript/.changeset/swift-mugs-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@goat-sdk/plugin-coinmarketcap": patch
---

Create plugin
31 changes: 31 additions & 0 deletions typescript/packages/plugins/coinmarketcap/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@goat-sdk/plugin-coinmarketcap",
"version": "0.1.0",
"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": {
"@goat-sdk/core": "workspace:*",
"zod": "catalog:"
},
"peerDependencies": {
"@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"]
}
47 changes: 47 additions & 0 deletions typescript/packages/plugins/coinmarketcap/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
export class CoinmarketcapApi {
private readonly BASE_URL = "https://pro-api.coinmarketcap.com/";

constructor(private readonly apiKey: string) {}

async makeRequest<T>(endpoint: string, params: T) {
const queryString = new URLSearchParams(
Object.entries(params || {}).reduce(
(acc, [key, value]) => {
if (value !== undefined) {
acc[key] = String(value);
}
return acc;
},
{} as Record<string, string>,
),
).toString();

const url = `${this.BASE_URL}${endpoint}${queryString ? `?${queryString}` : ""}`;

try {
const response = await fetch(url, {
method: "GET",
headers: {
"X-CMC_PRO_API_KEY": this.apiKey,
Accept: "application/json",
},
});

if (!response.ok) {
const errorData = await response.json();
throw new Error(
`Coinmarketcap API Error: ${response.status} - ${
errorData?.status?.error_message || response.statusText
}`,
);
}

return (await response.json()).data;
} catch (error) {
if (error instanceof Error) {
throw error;
}
throw new Error("An unknown error occurred while fetching data");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { type Chain, PluginBase } from "@goat-sdk/core";
import { CoinmarketcapService } from "./coinmarketcap.service";

export interface CoinmarketcapOptions {
apiKey: string;
}

export class CoinmarketcapPlugin extends PluginBase {
constructor(private readonly options: CoinmarketcapOptions) {
super("coinmarketcap", [new CoinmarketcapService(options.apiKey)]);
}

supportsChain(_chain: Chain): boolean {
// This plugin doesn't require specific chain support as it's an API wrapper
return true;
}
}

export function coinmarketcap(options: CoinmarketcapOptions) {
return new CoinmarketcapPlugin(options);
}
209 changes: 209 additions & 0 deletions typescript/packages/plugins/coinmarketcap/src/coinmarketcap.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import { Tool } from "@goat-sdk/core";

import {
ContentLatestParameters,
CryptocurrencyListingsParameters,
CryptocurrencyMapParameters,
CryptocurrencyOHLCVLatestParameters,
CryptocurrencyQuotesLatestParameters,
CryptocurrencyTrendingGainersLosersParameters,
CryptocurrencyTrendingLatestParameters,
CryptocurrencyTrendingMostVisitedParameters,
ExchangeListingsParameters,
ExchangeQuotesLatestParameters,
} from "./parameters";

import { CoinmarketcapApi } from "./api";

export class CoinmarketcapService {
private readonly api: CoinmarketcapApi;

constructor(apiKey: string) {
this.api = new CoinmarketcapApi(apiKey);
}

@Tool({
description:
"Fetch the latest cryptocurrency listings with market data including price, market cap, volume, and other key metrics",
})
async getCryptocurrencyListings(parameters: CryptocurrencyListingsParameters) {
try {
return await this.api.makeRequest("/v1/cryptocurrency/listings/latest", {
start: parameters.start,
limit: parameters.limit,
sort: parameters.sort,
sort_dir: parameters.sort_dir,
cryptocurrency_type: parameters.cryptocurrency_type,
tag: parameters.tag,
aux: parameters.aux?.join(","),
convert: parameters.convert,
});
} catch (error) {
throw new Error(`Failed to fetch cryptocurrency listings: ${error}`);
}
}

@Tool({
description:
"Get the latest market quotes for one or more cryptocurrencies, including price, market cap, and volume in any supported currency",
})
async getCryptocurrencyQuotes(parameters: CryptocurrencyQuotesLatestParameters) {
try {
return await this.api.makeRequest("/v2/cryptocurrency/quotes/latest", {
id: parameters.id?.join(","),
symbol: parameters.symbol?.join(","),
convert: parameters.convert,
aux: parameters.aux?.join(","),
});
} catch (error) {
throw new Error(`Failed to fetch cryptocurrency quotes: ${error}`);
}
}

@Tool({
description:
"Fetch the latest cryptocurrency exchange listings with market data including trading volume, number of markets, and liquidity metrics",
})
async getExchangeListings(parameters: ExchangeListingsParameters) {
try {
return await this.api.makeRequest("/v1/exchange/listings/latest", {
start: parameters.start,
limit: parameters.limit,
sort: parameters.sort,
sort_dir: parameters.sort_dir,
market_type: parameters.market_type,
aux: parameters.aux?.join(","),
});
} catch (error) {
throw new Error(`Failed to fetch exchange listings: ${error}`);
}
}

@Tool({
description:
"Get the latest market data for one or more exchanges including trading volume, number of markets, and other exchange-specific metrics",
})
async getExchangeQuotes(parameters: ExchangeQuotesLatestParameters) {
try {
return await this.api.makeRequest("/v1/exchange/quotes/latest", {
id: parameters.id?.join(","),
slug: parameters.slug?.join(","),
convert: parameters.convert,
aux: parameters.aux?.join(","),
});
} catch (error) {
throw new Error(`Failed to fetch exchange quotes: ${error}`);
}
}

@Tool({
description: "Fetch the latest cryptocurrency news, articles, and market analysis content from trusted sources",
})
async getContent(parameters: ContentLatestParameters) {
try {
return await this.api.makeRequest("/v1/content/latest", {
start: parameters.start,
limit: parameters.limit,
id: parameters.id?.join(","),
slug: parameters.slug?.join(","),
symbol: parameters.symbol?.join(","),
news_type: parameters.news_type,
content_type: parameters.content_type,
category: parameters.category,
language: parameters.language,
});
} catch (error) {
throw new Error(`Failed to fetch content: ${error}`);
}
}

@Tool({
description:
"Get a mapping of all cryptocurrencies with unique CoinMarketCap IDs, including active and inactive assets",
})
async getCryptocurrencyMap(parameters: CryptocurrencyMapParameters) {
try {
return await this.api.makeRequest("/v1/cryptocurrency/map", {
listing_status: parameters.listing_status,
start: parameters.start,
limit: parameters.limit,
sort: parameters.sort,
symbol: parameters.symbol?.join(","),
aux: parameters.aux?.join(","),
});
} catch (error) {
throw new Error(`Failed to fetch cryptocurrency map: ${error}`);
}
}

@Tool({
description: "Get the latest OHLCV (Open, High, Low, Close, Volume) values for cryptocurrencies",
})
async getCryptocurrencyOHLCV(parameters: CryptocurrencyOHLCVLatestParameters) {
try {
return await this.api.makeRequest("/v2/cryptocurrency/ohlcv/latest", {
id: parameters.id?.join(","),
symbol: parameters.symbol?.join(","),
convert: parameters.convert,
convert_id: parameters.convert_id,
skip_invalid: parameters.skip_invalid,
});
} catch (error) {
throw new Error(`Failed to fetch cryptocurrency OHLCV data: ${error}`);
}
}

@Tool({
description: "Get the latest trending cryptocurrencies based on CoinMarketCap user activity",
})
async getCryptocurrencyTrending(parameters: CryptocurrencyTrendingLatestParameters) {
try {
return await this.api.makeRequest("/cryptocurrency/trending/latest", {
start: parameters.start,
limit: parameters.limit,
time_period: parameters.time_period,
convert: parameters.convert,
convert_id: parameters.convert_id,
});
} catch (error) {
throw new Error(`Failed to fetch trending cryptocurrencies: ${error}`);
}
}

@Tool({
description: "Get the most visited cryptocurrencies on CoinMarketCap over a specified time period",
})
async getCryptocurrencyMostVisited(parameters: CryptocurrencyTrendingMostVisitedParameters) {
try {
return await this.api.makeRequest("/cryptocurrency/trending/most-visited", {
start: parameters.start,
limit: parameters.limit,
time_period: parameters.time_period,
convert: parameters.convert,
convert_id: parameters.convert_id,
});
} catch (error) {
throw new Error(`Failed to fetch most visited cryptocurrencies: ${error}`);
}
}

@Tool({
description:
"Get the top gaining and losing cryptocurrencies based on price changes over different time periods",
})
async getCryptocurrencyGainersLosers(parameters: CryptocurrencyTrendingGainersLosersParameters) {
try {
return await this.api.makeRequest("/cryptocurrency/trending/gainers-losers", {
start: parameters.start,
limit: parameters.limit,
time_period: parameters.time_period,
convert: parameters.convert,
convert_id: parameters.convert_id,
sort: parameters.sort,
sort_dir: parameters.sort_dir,
});
} catch (error) {
throw new Error(`Failed to fetch cryptocurrency gainers and losers: ${error}`);
}
}
}
1 change: 1 addition & 0 deletions typescript/packages/plugins/coinmarketcap/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./coinmarketcap.plugin";
Loading
Loading