Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 6 additions & 3 deletions apps/processing/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ INDEXER_ADMIN_SECRET=testing

IPFS_GATEWAYS_URL=["https://ipfs.io","https://gateway.pinata.cloud","https://dweb.link", "https://ipfs.eth.aragon.network"]

PRICING_SOURCE= # 'coingecko' or 'dummy'
PRICING_SOURCE= # 'coingecko' or 'dummy' or 'coinpaprika'

COINGECKO_API_KEY={{YOUR_KEY}}
COINGECKO_API_TYPE=demo
COINGECKO_API_KEY={{YOUR_KEY}} # empty string for demo tier
COINGECKO_API_TYPE=demo # 'demo' or 'pro'

COINPAPRIKA_API_KEY={{YOUR_KEY}} # empty string for free tier
COINPAPRIKA_API_TYPE=free # 'free' or 'starter' or 'pro' or 'business' or 'enterprise'

RETRY_MAX_ATTEMPTS=3
RETRY_BASE_DELAY_MS=3000
Expand Down
25 changes: 23 additions & 2 deletions apps/processing/src/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const baseSchema = z.object({
DATABASE_SCHEMA: z.string().default("public"),
INDEXER_GRAPHQL_URL: z.string().url(),
INDEXER_ADMIN_SECRET: z.string().optional(),
PRICING_SOURCE: z.enum(["dummy", "coingecko"]).default("coingecko"),
PRICING_SOURCE: z.enum(["dummy", "coingecko", "coinpaprika"]).default("coingecko"),
METADATA_SOURCE: z.enum(["dummy", "public-gateway"]).default("dummy"),
RETRY_MAX_ATTEMPTS: z.coerce.number().int().min(1).default(3),
RETRY_BASE_DELAY_MS: z.coerce.number().int().min(1).default(3000), // 3 seconds
Expand All @@ -55,6 +55,14 @@ const coingeckoPricingSchema = baseSchema.extend({
COINGECKO_API_TYPE: z.enum(["demo", "pro"]).default("pro"),
});

const coinpaprikaPricingSchema = baseSchema.extend({
PRICING_SOURCE: z.literal("coinpaprika"),
COINPAPRIKA_API_KEY: z.string().default(""),
COINPAPRIKA_API_TYPE: z
.enum(["free", "starter", "pro", "business", "enterprise"])
.default("free"),
});

const dummyMetadataSchema = baseSchema.extend({
METADATA_SOURCE: z.literal("dummy"),
});
Expand All @@ -65,12 +73,25 @@ const publicGatewayMetadataSchema = baseSchema.extend({
});

const validationSchema = z
.discriminatedUnion("PRICING_SOURCE", [dummyPricingSchema, coingeckoPricingSchema])
.discriminatedUnion("PRICING_SOURCE", [
dummyPricingSchema,
coingeckoPricingSchema,
coinpaprikaPricingSchema,
])
.transform((val) => {
if (val.PRICING_SOURCE === "dummy") {
return { pricingSource: val.PRICING_SOURCE, dummyPrice: val.DUMMY_PRICE, ...val };
}

if (val.PRICING_SOURCE === "coinpaprika") {
return {
pricingSource: val.PRICING_SOURCE,
apiKey: val.COINPAPRIKA_API_KEY,
apiType: val.COINPAPRIKA_API_TYPE,
...val,
};
}

return {
pricingSource: val.PRICING_SOURCE,
apiKey: val.COINGECKO_API_KEY,
Expand Down
8 changes: 7 additions & 1 deletion packages/pricing/src/external.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
export type { TokenPrice, IPricingProvider } from "./internal.js";

export { CoingeckoProvider, DummyPricingProvider, CachingPricingProvider } from "./internal.js";
export {
CoingeckoProvider,
DummyPricingProvider,
CachingPricingProvider,
CoinPaprikaProvider,
} from "./internal.js";

export { PricingProviderFactory } from "./internal.js";
export type {
PricingConfig,
PricingProvider,
DummyPricingConfig,
CoingeckoPricingConfig,
CoinPaprikaPricingConfig,
} from "./internal.js";

export {
Expand Down
14 changes: 14 additions & 0 deletions packages/pricing/src/factory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ILogger } from "@grants-stack-indexer/shared";

import {
CoingeckoProvider,
CoinPaprikaProvider,
DummyPricingProvider,
InvalidPricingSource,
IPricingProvider,
Expand Down Expand Up @@ -47,6 +48,19 @@ export class PricingProviderFactory {
deps.logger,
);
break;
case "coinpaprika":
if (!deps?.logger) {
throw new MissingDependencies();
}

pricingProvider = new CoinPaprikaProvider(
{
apiKey: options.apiKey,
apiType: options.apiType,
},
deps.logger,
);
break;
default:
throw new InvalidPricingSource();
}
Expand Down
2 changes: 1 addition & 1 deletion packages/pricing/src/interfaces/pricing.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { TimestampMs, TokenCode } from "@grants-stack-indexer/shared";
import { TokenPrice } from "../internal.js";

// available pricing providers
export type PricingProvider = "coingecko" | "dummy";
export type PricingProvider = "coingecko" | "dummy" | "coinpaprika";

/**
* Represents a pricing service that retrieves token prices.
Expand Down
10 changes: 9 additions & 1 deletion packages/pricing/src/interfaces/pricingConfig.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,16 @@ export type CoingeckoPricingConfig = {
apiType: "demo" | "pro";
};

export type CoinPaprikaPricingConfig = {
pricingSource: "coinpaprika";
apiKey: string;
apiType: "free" | "starter" | "pro" | "business" | "enterprise";
};

export type PricingConfig<Source extends PricingProvider> = Source extends "dummy"
? DummyPricingConfig
: Source extends "coingecko"
? CoingeckoPricingConfig
: never;
: Source extends "coinpaprika"
? CoinPaprikaPricingConfig
: never;
Loading
Loading