init
This commit is contained in:
4
packages/core/index.ts
Normal file
4
packages/core/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from "../genaiscript/genaisrc/_state";
|
||||
export * from "./news";
|
||||
export * from "./quotes";
|
||||
export * from "./types";
|
14
packages/core/market/index.ts
Normal file
14
packages/core/market/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import {ApiResponse} from "./types";
|
||||
|
||||
export async function collect_gainers_losers(x: { apiKey: string, limit: number }): Promise<ApiResponse> {
|
||||
const { apiKey, limit } = x;
|
||||
|
||||
//
|
||||
const data: ApiResponse = await fetch(`https://pro-api.coinmarketcap.com/v1/cryptocurrency/trending/gainers-losers?limit=${limit}`, {
|
||||
headers: {
|
||||
"x-cmc_pro_api_key": apiKey
|
||||
}
|
||||
}).then((symbolDataRequest) => symbolDataRequest.json());
|
||||
|
||||
return data;
|
||||
}
|
48
packages/core/market/types.ts
Normal file
48
packages/core/market/types.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
type Quote = {
|
||||
price: number;
|
||||
volume_24h: number;
|
||||
percent_change_1h: number;
|
||||
percent_change_24h: number;
|
||||
percent_change_7d: number;
|
||||
market_cap: number;
|
||||
last_updated: string;
|
||||
};
|
||||
|
||||
type Platform = null;
|
||||
|
||||
type Tag = string;
|
||||
|
||||
type Data = {
|
||||
id: number;
|
||||
name: string;
|
||||
symbol: string;
|
||||
slug: string;
|
||||
cmc_rank?: number;
|
||||
num_market_pairs: number;
|
||||
circulating_supply: number;
|
||||
total_supply: number;
|
||||
max_supply: number;
|
||||
last_updated: string;
|
||||
date_added: string;
|
||||
tags: Tag[];
|
||||
platform: Platform;
|
||||
quote: {
|
||||
USD: Quote;
|
||||
BTC?: Quote;
|
||||
ETH?: Quote;
|
||||
};
|
||||
};
|
||||
|
||||
type Status = {
|
||||
timestamp: string;
|
||||
error_code: number;
|
||||
error_message: string | null;
|
||||
elapsed: number;
|
||||
credit_count: number;
|
||||
};
|
||||
|
||||
export type ApiResponse = {
|
||||
data: Data[];
|
||||
status: Status;
|
||||
};
|
||||
|
178
packages/core/news/index.ts
Normal file
178
packages/core/news/index.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import {types, Instance} from 'mobx-state-tree';
|
||||
import {runInAction} from "mobx";
|
||||
|
||||
|
||||
const Article = types.model('Article', {
|
||||
title: types.string,
|
||||
content: types.string,
|
||||
url: types.maybe(types.string),
|
||||
source: types.maybe(types.string),
|
||||
pubDate: types.maybe(types.string),
|
||||
|
||||
summary: types.maybe(types.string),
|
||||
description: types.maybe(types.string),
|
||||
authorsByline: types.maybe(types.string),
|
||||
shortSummary: types.maybe(types.string),
|
||||
labels: types.maybe(types.frozen()),
|
||||
imageUrl: types.maybe(types.string),
|
||||
score: types.maybe(types.number),
|
||||
});
|
||||
|
||||
|
||||
export const NewsStore = types
|
||||
.model('NewsStore', {
|
||||
symbolsNews: types.map(types.array(Article)),
|
||||
isLoading: types.boolean,
|
||||
error: types.maybe(types.string),
|
||||
apiKey: types.string,
|
||||
})
|
||||
.actions((self) => ({
|
||||
|
||||
addNews(symbol: string, articles: any[]) {
|
||||
if (!self.symbolsNews.has(symbol)) {
|
||||
self.symbolsNews.set(symbol, []);
|
||||
}
|
||||
|
||||
|
||||
const mappedArticles = articles.map((article) => Article.create({
|
||||
title: article.title || 'No Title',
|
||||
content: article.content || 'No Content',
|
||||
url: article.url,
|
||||
source: article.domain,
|
||||
pubDate: article.pubDate,
|
||||
summary: article.summary,
|
||||
description: article.description,
|
||||
authorsByline: article.authorsByline,
|
||||
shortSummary: article.shortSummary,
|
||||
labels: article.labels,
|
||||
imageUrl: article.imageUrl,
|
||||
score: article.score,
|
||||
|
||||
|
||||
}));
|
||||
self.symbolsNews.get(symbol)!.push(...mappedArticles);
|
||||
self.isLoading = false;
|
||||
},
|
||||
|
||||
clearNews(symbol: string) {
|
||||
if (self.symbolsNews.has(symbol)) {
|
||||
self.symbolsNews.set(symbol, []);
|
||||
}
|
||||
},
|
||||
|
||||
setLoading(loading: boolean) {
|
||||
self.isLoading = loading;
|
||||
},
|
||||
|
||||
setError(message: string) {
|
||||
self.error = message;
|
||||
self.isLoading = false;
|
||||
},
|
||||
|
||||
async fetchNewsForSymbol(symbol: string, limit: number, sort: "date" | "relevance") {
|
||||
self.setLoading(true);
|
||||
self.setError(undefined);
|
||||
|
||||
try {
|
||||
await runInAction(async () => {
|
||||
const newsData = await collect_news({symbol, apiKey: self.apiKey, limit, sort});
|
||||
if (newsData && newsData.articles) {
|
||||
self.addNews(symbol, newsData.articles);
|
||||
} else {
|
||||
self.setError("Failed to fetch news or invalid response format.");
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
} catch (err: any) {
|
||||
console.error('Error fetching news:', err);
|
||||
self.setError(err.message || "Failed to fetch news.");
|
||||
}
|
||||
},
|
||||
}))
|
||||
.views((self) => ({
|
||||
|
||||
getNewsForSymbol(symbol: string) {
|
||||
|
||||
return self.symbolsNews.get(symbol) || [];
|
||||
},
|
||||
|
||||
getAllSymbols() {
|
||||
return Array.from(self.symbolsNews.keys());
|
||||
},
|
||||
|
||||
hasNewsForSymbol(symbol: string) {
|
||||
return self.symbolsNews.has(symbol) && self.symbolsNews.get(symbol)!.length > 0;
|
||||
},
|
||||
}));
|
||||
|
||||
export type INewsStore = Instance<typeof NewsStore>;
|
||||
|
||||
|
||||
export const createNewsStore = (apikey, perigon) => NewsStore.create({
|
||||
symbolsNews: {},
|
||||
isLoading: false,
|
||||
error: undefined,
|
||||
apiKey: apikey,
|
||||
});
|
||||
|
||||
/* @collect_news return value structure
|
||||
{
|
||||
news: {
|
||||
status: 200,
|
||||
numResults: 4080,
|
||||
articles: [
|
||||
[Object], [Object],
|
||||
[Object], [Object],
|
||||
[Object], [Object],
|
||||
[Object], [Object],
|
||||
[Object], [Object]
|
||||
]
|
||||
}
|
||||
}
|
||||
*/
|
||||
export async function collect_news(x: { symbol: string, apiKey: string, limit: number, sort: "date" | "relevance" }) {
|
||||
|
||||
|
||||
const {symbol, apiKey, limit, sort} = x;
|
||||
|
||||
const symbolNameMap = {
|
||||
"BTC": "Bitcoin",
|
||||
"ETH": "Ethereum",
|
||||
"XRP": "Ripple",
|
||||
"LTC": "Litecoin",
|
||||
"ADA": "Cardano",
|
||||
"DOGE": "Dogecoin",
|
||||
"BNB": "Binance Coin",
|
||||
"DOT": "Polkadot",
|
||||
"SOL": "Solana",
|
||||
"AVAX": "Avalanche"
|
||||
};
|
||||
|
||||
|
||||
const cryptocurrencyName = symbolNameMap[symbol] ?? symbol;
|
||||
|
||||
|
||||
const rawContentQuery = "scandal OR \"corporate misconduct*\" OR fraud OR \"financial irregularities*\" OR lawsuit OR \"legal action*\" OR bankruptcy OR \"financial distress*\" OR \"data breach\" OR \"security vulnerability*\" OR \"environmental impact\" OR \"ecological damage*\" OR \"labor dispute\" OR \"worker rights*\" OR \"product failure\" OR \"quality issue*\" OR \"ethical concern\" OR \"moral dilemma*\" OR \"health risk\" OR \"safety hazard*\" OR \"regulatory violation\" OR \"compliance issue*\" OR \"market manipulation\" OR \"trading irregularity*\" OR \"public relations crisis\" OR \"reputation damage*\" OR \"political controversy\" OR \"government intervention*\" OR \"consumer complaint\" OR \"customer dissatisfaction*\" OR \"supply chain disruption\" OR \"logistics problem*\" OR \"intellectual property dispute\" OR \"patent infringement*\"";
|
||||
const contentQuery = encodeURIComponent(rawContentQuery);
|
||||
|
||||
|
||||
const rawTitleQuery = `${cryptocurrencyName} OR ${symbol} OR "${cryptocurrencyName} price" OR "${cryptocurrencyName} market" OR "${cryptocurrencyName} news"`;
|
||||
const titleQuery = encodeURIComponent(rawTitleQuery);
|
||||
|
||||
try {
|
||||
const result = await allNews({
|
||||
q: contentQuery,
|
||||
title: titleQuery,
|
||||
size: limit,
|
||||
sortBy: sort,
|
||||
apiKey: apiKey
|
||||
});
|
||||
|
||||
|
||||
return result.data;
|
||||
} catch (err) {
|
||||
console.error('Error fetching news:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
75
packages/core/news/news.test.ts
Normal file
75
packages/core/news/news.test.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import {describe, expect, it} from 'vitest';
|
||||
import {collect_news, createNewsStore, NewsStore} from './index';
|
||||
|
||||
|
||||
const testApiKey = '';
|
||||
|
||||
describe('NewsStore', () => {
|
||||
it('should create a NewsStore instance', () => {
|
||||
const store = createNewsStore(testApiKey);
|
||||
expect(store).toBeDefined();
|
||||
expect(store.isLoading).toBe(false);
|
||||
expect(store.error).toBeUndefined();
|
||||
expect(store.getAllSymbols()).toEqual([]);
|
||||
});
|
||||
|
||||
it('should add news articles for a symbol', () => {
|
||||
const store = createNewsStore(testApiKey);
|
||||
const articles = [
|
||||
{ title: 'Article 1', content: 'Content 1', url: 'http://example.com/1', source: 'Source 1', publishedAt: '2025-01-01' },
|
||||
{ title: 'Article 2', content: 'Content 2', url: 'http://example.com/2', source: 'Source 2', publishedAt: '2025-01-02' }
|
||||
];
|
||||
|
||||
store.addNews('BTC', articles);
|
||||
|
||||
expect(store.getNewsForSymbol('BTC')).toHaveLength(2);
|
||||
expect(store.getNewsForSymbol('BTC')[0].title).toBe('Article 1');
|
||||
expect(store.getNewsForSymbol('BTC')[1].title).toBe('Article 2');
|
||||
expect(store.hasNewsForSymbol('BTC')).toBe(true);
|
||||
});
|
||||
|
||||
it('should clear news articles for a symbol', () => {
|
||||
const store = createNewsStore(testApiKey);
|
||||
const articles = [
|
||||
{ title: 'Article 1', content: 'Content 1', url: 'http://example.com/1', source: 'Source 1', publishedAt: '2025-01-01' }
|
||||
];
|
||||
|
||||
store.addNews('BTC', articles);
|
||||
store.clearNews('BTC');
|
||||
|
||||
expect(store.getNewsForSymbol('BTC')).toHaveLength(0);
|
||||
expect(store.hasNewsForSymbol('BTC')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle fetchNewsForSymbol successfully', async () => {
|
||||
const store = createNewsStore(testApiKey);
|
||||
|
||||
await store.fetchNewsForSymbol('BTC', 10, 'date');
|
||||
|
||||
const storeNews = store.getNewsForSymbol('BTC');
|
||||
|
||||
console.log(storeNews);
|
||||
|
||||
expect(storeNews).toHaveLength(10);
|
||||
expect(store.getNewsForSymbol('BTC')[0].title).toBeTypeOf("string");
|
||||
expect(store.isLoading).toBe(false);
|
||||
expect(store.error).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
it('should throw an error for invalid symbol in collect_news', async () => {
|
||||
await expect(collect_news({ symbol: 'INVALID', apiKey: testApiKey, limit: 10, sort: 'date' }))
|
||||
.rejects.toThrow('Invalid symbol: INVALID. Must be one of BTC, ETH, XRP, LTC, ADA, DOGE, BNB, DOT, SOL, AVAX.');
|
||||
});
|
||||
|
||||
it('should fetch news using collect_news', async () => {
|
||||
|
||||
const result = await collect_news({ symbol: 'BTC', apiKey: testApiKey, limit: 1, sort: 'date' });
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.status).toBe(200);
|
||||
expect(result.articles).toBeDefined();
|
||||
});
|
||||
});
|
9
packages/core/package.json
Normal file
9
packages/core/package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "@web-agent-rs/core",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"mobx-state-tree": "^7.0.2",
|
||||
"@web-agent-rs/perigon": "workspace:*"
|
||||
}
|
||||
}
|
137
packages/core/portfolio/index.ts
Normal file
137
packages/core/portfolio/index.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import {getSnapshot, types} from "mobx-state-tree";
|
||||
import {NewsStore} from "../news";
|
||||
|
||||
|
||||
const PortfolioCashModel = types.model("PortfolioCash", {
|
||||
amount: types.number,
|
||||
currency: types.enumeration("Currency", ["USD", "BTC"]),
|
||||
});
|
||||
|
||||
|
||||
const PortfolioActionModel = types.model("PortfolioAction", {
|
||||
action: types.enumeration("Action", ["buy", "sell", "hold"]),
|
||||
symbol: types.string,
|
||||
quantity: types.number,
|
||||
timestamp: types.Date,
|
||||
});
|
||||
|
||||
|
||||
export const PortfolioNewsportfolioNewsModel = types.model("PortfolioNews", {
|
||||
symbol: types.string,
|
||||
date_created: types.string,
|
||||
news: types.array(types.model("NewsItem", {
|
||||
symbol: types.maybe(types.string),
|
||||
date_created: types.maybe(types.string),
|
||||
news: types.string,
|
||||
timestamp: types.maybe(types.string),
|
||||
})),
|
||||
timestamp: types.maybe(types.string),
|
||||
});
|
||||
|
||||
export const portfolioQuoteModel = types.model("PortfolioQuote", {
|
||||
symbol: types.string,
|
||||
quote: types.string,
|
||||
date_created: types.string,
|
||||
});
|
||||
|
||||
|
||||
const PortfolioAssetContextModel = types.model("PortfolioAssetContext", {
|
||||
timestamp: types.Date,
|
||||
portfolio_snapshot: PortfolioCashModel,
|
||||
});
|
||||
|
||||
|
||||
const PortfolioAssetModel = types.model("PortfolioAsset", {
|
||||
symbol: types.string,
|
||||
quantity: types.number,
|
||||
recommended_action: types.maybe(
|
||||
types.enumeration("RecommendedAction", ["buy", "sell", "hold"])
|
||||
),
|
||||
last_taken_action: types.optional(types.enumeration("LastAction", ["buy", "sell", "hold", "none", "never"]), "never"),
|
||||
context: PortfolioAssetContextModel,
|
||||
});
|
||||
|
||||
|
||||
const PortfolioModel = types
|
||||
.model("Portfolio", {
|
||||
supportedSymbols: types.array(types.string),
|
||||
liquidity: PortfolioCashModel,
|
||||
actions: types.optional(types.array(PortfolioActionModel), []),
|
||||
assets: types.array(PortfolioAssetModel),
|
||||
news: types.optional(types.array(NewsStore), []),
|
||||
quotes: types.optional(types.array(portfolioQuoteModel), []),
|
||||
})
|
||||
.actions((self) => ({
|
||||
|
||||
addAction(actionData: {
|
||||
action: "buy" | "sell" | "hold";
|
||||
symbol: string;
|
||||
quantity: number;
|
||||
}) {
|
||||
self.actions.push({
|
||||
...actionData,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
},
|
||||
addNews(newsData: any) {
|
||||
self.news.push({
|
||||
...newsData,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
},
|
||||
addQuote(quoteData: any) {
|
||||
self.quotes
|
||||
self.quotes.push({
|
||||
...quoteData,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
},
|
||||
|
||||
updateLiquidity(amount: number) {
|
||||
self.liquidity.amount = amount;
|
||||
},
|
||||
}));
|
||||
|
||||
const tokenList = [
|
||||
"AAVE", "AVAX", "BAT", "BCH", "BTC",
|
||||
"CRV", "DOGE", "DOT", "ETH", "GRT",
|
||||
"LINK", "LTC", "MKR", "SHIB", "SUSHI",
|
||||
"UNI", "USDC", "USDT", "XTZ", "YFI",
|
||||
];
|
||||
|
||||
|
||||
const portfolioCash = PortfolioCashModel.create({
|
||||
amount: 10000,
|
||||
currency: "USD",
|
||||
});
|
||||
|
||||
|
||||
const portfolioAssets = tokenList.map((token) =>
|
||||
PortfolioAssetModel.create({
|
||||
symbol: token,
|
||||
quantity: 0,
|
||||
recommended_action: "hold",
|
||||
last_taken_action: undefined,
|
||||
context: {
|
||||
timestamp: new Date(),
|
||||
portfolio_snapshot: getSnapshot(portfolioCash),
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
const portfolioActions = [];
|
||||
const portfolioNews = [];
|
||||
const portfolioQuotes = [];
|
||||
|
||||
|
||||
const portfolioInstance = PortfolioModel.create({
|
||||
liquidity: portfolioCash,
|
||||
actions: portfolioActions,
|
||||
assets: portfolioAssets,
|
||||
supportedSymbols: tokenList,
|
||||
news: portfolioNews,
|
||||
quotes: portfolioQuotes
|
||||
});
|
||||
|
||||
export default portfolioInstance;
|
13
packages/core/quotes/index.ts
Normal file
13
packages/core/quotes/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import {ApiResponse} from "./types";
|
||||
|
||||
export async function collect_quote(x: { symbol: string, apiKey: string }) {
|
||||
const {symbol, apiKey} = x;
|
||||
|
||||
const data: ApiResponse = await fetch(`https://pro-api.coinmarketcap.com/v2/cryptocurrency/quotes/latest?symbol=${symbol}`, {
|
||||
headers: {
|
||||
"x-cmc_pro_api_key": apiKey
|
||||
}
|
||||
}).then((symbolDataRequest) => symbolDataRequest.json());
|
||||
|
||||
return data;
|
||||
}
|
77
packages/core/quotes/models.ts
Normal file
77
packages/core/quotes/models.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import {types, flow, Instance} from "mobx-state-tree";
|
||||
import {collect_quote} from './index';
|
||||
|
||||
|
||||
const QuoteData = types.optional(types.frozen(), {})
|
||||
|
||||
export const QuoteStore = types
|
||||
.model("QuoteStore", {
|
||||
apiKey: types.string,
|
||||
quotes: types.map(QuoteData),
|
||||
})
|
||||
.views(self => ({
|
||||
getQuote(symbol) {
|
||||
return self.quotes.get(symbol);
|
||||
},
|
||||
|
||||
hasQuote(symbol) {
|
||||
return self.quotes.has(symbol);
|
||||
}
|
||||
}))
|
||||
.actions(self => {
|
||||
|
||||
const extractUsefulData = (data, symbol) => {
|
||||
return data.data[symbol].map(qd => ({
|
||||
symbol: qd.symbol,
|
||||
slug: qd.slug,
|
||||
tags: qd.tags,
|
||||
id: qd.id,
|
||||
...qd.quote.USD
|
||||
})).at(0);
|
||||
};
|
||||
|
||||
const fetchQuote = flow(function* (symbol) {
|
||||
try {
|
||||
const data = yield collect_quote({symbol, apiKey: self.apiKey});
|
||||
const usefulData = extractUsefulData(data, symbol);
|
||||
|
||||
|
||||
self.quotes.set(symbol, usefulData);
|
||||
|
||||
return usefulData;
|
||||
} catch (error) {
|
||||
console.error(`An error occurred fetching the quote for symbol: ${symbol}`, error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
const fetchQuotes = flow(function* (symbols) {
|
||||
const results = {};
|
||||
|
||||
for (const symbol of symbols) {
|
||||
|
||||
if (self.quotes.has(symbol)) {
|
||||
results[symbol] = self.quotes.get(symbol);
|
||||
} else {
|
||||
|
||||
const data = yield fetchQuote(symbol);
|
||||
results[symbol] = extractUsefulData(data, symbol);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
});
|
||||
|
||||
const clearCache = () => {
|
||||
self.quotes.clear();
|
||||
};
|
||||
|
||||
return {
|
||||
fetchQuote,
|
||||
fetchQuotes,
|
||||
clearCache
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
export type QuoteManagerType = Instance<typeof QuoteStore>;
|
17
packages/core/quotes/quote.test.ts
Normal file
17
packages/core/quotes/quote.test.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import {describe, it} from 'vitest';
|
||||
import {QuoteStore} from "./models";
|
||||
|
||||
describe('QuoteStore', () => {
|
||||
it('should get data for symbols using the quoteManager', async () => {
|
||||
const testApiKey = '';
|
||||
const quoteManager = QuoteStore.create({
|
||||
apiKey: testApiKey,
|
||||
});
|
||||
|
||||
const symbol = 'BTC';
|
||||
|
||||
const data = await quoteManager.fetchQuote(symbol);
|
||||
|
||||
console.log(JSON.stringify(data));
|
||||
});
|
||||
});
|
71
packages/core/quotes/types.ts
Normal file
71
packages/core/quotes/types.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
type Status = {
|
||||
timestamp: string;
|
||||
error_code: number;
|
||||
error_message: string | null;
|
||||
elapsed: number;
|
||||
credit_count: number;
|
||||
notice: string | null;
|
||||
};
|
||||
|
||||
type Tag = {
|
||||
slug: string;
|
||||
name: string;
|
||||
category: string;
|
||||
};
|
||||
|
||||
type Quote = {
|
||||
USD: {
|
||||
price: number | null;
|
||||
volume_24h: number;
|
||||
volume_change_24h: number;
|
||||
percent_change_1h: number;
|
||||
percent_change_24h: number;
|
||||
percent_change_7d: number;
|
||||
percent_change_30d: number;
|
||||
percent_change_60d: number;
|
||||
percent_change_90d: number;
|
||||
market_cap: number | null;
|
||||
market_cap_dominance: number | null;
|
||||
fully_diluted_market_cap: number | null;
|
||||
tvl: number | null;
|
||||
last_updated: string;
|
||||
};
|
||||
};
|
||||
|
||||
type Platform = {
|
||||
id: number;
|
||||
name: string;
|
||||
symbol: string;
|
||||
slug: string;
|
||||
token_address: string;
|
||||
};
|
||||
|
||||
type Cryptocurrency = {
|
||||
id: number;
|
||||
name: string;
|
||||
symbol: string;
|
||||
slug: string;
|
||||
num_market_pairs: number;
|
||||
date_added: string;
|
||||
tags: Tag[];
|
||||
max_supply: number | null;
|
||||
circulating_supply: number | null;
|
||||
total_supply: number;
|
||||
platform: Platform | null;
|
||||
is_active: number;
|
||||
infinite_supply: boolean;
|
||||
cmc_rank: number | null;
|
||||
is_fiat: number;
|
||||
self_reported_circulating_supply: number | null;
|
||||
self_reported_market_cap: number | null;
|
||||
tvl_ratio: number | null;
|
||||
last_updated: string;
|
||||
quote: Quote;
|
||||
};
|
||||
|
||||
export type ApiResponse = {
|
||||
status: Status;
|
||||
data: {
|
||||
[SYMBOL: string]: Cryptocurrency[];
|
||||
};
|
||||
};
|
69
packages/core/types/index.ts
Normal file
69
packages/core/types/index.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
export type CryptoDataResponse = {
|
||||
status: {
|
||||
timestamp: string;
|
||||
error_code: number;
|
||||
error_message: string | null;
|
||||
elapsed: number;
|
||||
credit_count: number;
|
||||
notice: string | null;
|
||||
};
|
||||
data: {
|
||||
[key: string]: CryptoAsset[];
|
||||
};
|
||||
};
|
||||
|
||||
export type CryptoAsset = {
|
||||
id: number;
|
||||
name: string;
|
||||
symbol: string;
|
||||
slug: string;
|
||||
num_market_pairs: number;
|
||||
date_added: string;
|
||||
tags: CryptoTag[];
|
||||
max_supply: number | null;
|
||||
circulating_supply: number;
|
||||
total_supply: number;
|
||||
platform: CryptoPlatform | null;
|
||||
is_active: number;
|
||||
infinite_supply: boolean;
|
||||
cmc_rank: number;
|
||||
is_fiat: number;
|
||||
self_reported_circulating_supply: number | null;
|
||||
self_reported_market_cap: number | null;
|
||||
tvl_ratio: number;
|
||||
last_updated: string;
|
||||
quote: {
|
||||
[currency: string]: CryptoQuote;
|
||||
};
|
||||
};
|
||||
|
||||
export type CryptoTag = {
|
||||
slug: string;
|
||||
name: string;
|
||||
category: string;
|
||||
};
|
||||
|
||||
export type CryptoPlatform = {
|
||||
id: number;
|
||||
name: string;
|
||||
symbol: string;
|
||||
slug: string;
|
||||
token_address: string;
|
||||
};
|
||||
|
||||
export type CryptoQuote = {
|
||||
price: number;
|
||||
volume_24h: number;
|
||||
volume_change_24h: number;
|
||||
percent_change_1h: number;
|
||||
percent_change_24h: number;
|
||||
percent_change_7d: number;
|
||||
percent_change_30d: number;
|
||||
percent_change_60d: number;
|
||||
percent_change_90d: number;
|
||||
market_cap: number;
|
||||
market_cap_dominance: number;
|
||||
fully_diluted_market_cap: number;
|
||||
tvl: number;
|
||||
last_updated: string;
|
||||
};
|
Reference in New Issue
Block a user