Skip to content

Commit

Permalink
api: icr api changes
Browse files Browse the repository at this point in the history
  • Loading branch information
psparacino committed Dec 20, 2023
1 parent 5db6016 commit d0c8167
Show file tree
Hide file tree
Showing 37 changed files with 4,712 additions and 181 deletions.
1,070 changes: 1,070 additions & 0 deletions carbonmark-api/src/.generated/mocks/icr.mocks.ts

Large diffs are not rendered by default.

1,742 changes: 1,742 additions & 0 deletions carbonmark-api/src/.generated/types/icr.types.ts

Large diffs are not rendered by default.

43 changes: 39 additions & 4 deletions carbonmark-api/src/app.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,31 @@ const GRAPH_API_ROOT_ID = "https://api.thegraph.com/subgraphs/id";
* This is also the case for SANITY_URLS
*/
const POLYGON_URLS = {
marketplace: `${GRAPH_API_ROOT_ID}/QmTfM5TvokuyKXCGWL3wTLZ1z4TqRtQ7PdVfQRVU39ovUC`,
// production below
// marketplace: `${GRAPH_API_ROOT_ID}/QmTfM5TvokuyKXCGWL3wTLZ1z4TqRtQ7PdVfQRVU39ovUC`,
// testing with test marketplace
marketplace:
"https://api.thegraph.com/subgraphs/name/psparacino/carbonmark--testing-only",
assets: `${GRAPH_API_ROOT}/cujowolf/klima-refi-current-holdings`,
offsets: `${GRAPH_API_ROOT}/klimadao/polygon-bridged-carbon`,
tokens: `${GRAPH_API_ROOT}/klimadao/klimadao-pairs`,
digitalCarbon: `${GRAPH_API_ROOT}/klimadao/polygon-digital-carbon`,
// production below
// digitalCarbon: `${GRAPH_API_ROOT}/klimadao/polygon-digital-carbon`,
// testing
digitalCarbon:
"https://api.thegraph.com/subgraphs/name/psparacino/digital-carbon",
// producion below
// icr: `${GRAPH_API_ROOT}/skjaldbaka17/carbon-registry-polygon`,
// Mainnet testing only
icr: `${GRAPH_API_ROOT}/skjaldbaka17/carbon-registry-main-test`,
};

const MUMBAI_URLS = {
...POLYGON_URLS,
marketplace: `${GRAPH_API_ROOT_ID}/QmdrYranfueu9Ann3kYCkEKTmTRReusybzFZ2nYz8YM6WF`,
// testing
digitalCarbon: `${GRAPH_API_ROOT}/psparacino/digital-carbon`,
marketplace: `${GRAPH_API_ROOT_ID}/QmUUnZTeRnfsJsQaTwLeiHmAQ5xvtk2jBW7VeP3AEW5bnv`,
icr: `${GRAPH_API_ROOT}/skjaldbaka17/carbon-registry-test`,
};

/** Sanity URLS */
Expand Down Expand Up @@ -57,6 +72,26 @@ export const TOKEN_ADDRESSES = {
},
};

export const RPC_URLS = {
export const URLS = {
polygonTestnetRpc: "https://rpc-mumbai.maticvigil.com",
};

/** Definitions of available registries */
export const REGISTRIES = {
Verra: {
id: "VCS",
title: "Verra",
url: "https://registry.verra.org",
api: "https://registry.verra.org/uiapi",
},
GoldStandard: {
id: "GS",
title: "Gold Standard",
url: "https://registry.goldstandard.org",
},
ICR: {
id: "ICR",
title: "International Carbon Registry",
url: "https://www.carbonregistry.com",
},
};
22 changes: 22 additions & 0 deletions carbonmark-api/src/graphql/icr.fragments.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
fragment ICRProjectFragment on Project {
id
projectAddress
projectName
transactionHash
}

fragment exPostAmountsFragment on ExPostHolder {
id
amount
updatedAt
retiredAmount
exPost {
tokenId
vintage
serialization
project {
id
projectName
}
}
}
27 changes: 27 additions & 0 deletions carbonmark-api/src/graphql/icr.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
query getExPostInfoViaSerialization($serialization: String!) {
exPosts(where: { serialization: $serialization }) {
serialization
supply
retiredAmount
estimatedAmount
cancelledAmount
id
lastVerificationTimestamp
project {
...ICRProjectFragment
}
tokenId
verificationPeriodEnd
verificationPeriodStart
vintage
}
}

query getHoldingsByAddress($id: ID!) {
holder(id: $id) {
id
exPostAmounts {
...exPostAmountsFragment
}
}
}
1 change: 1 addition & 0 deletions carbonmark-api/src/models/Asset.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const AssetModel = Type.Object({
name: Type.String(),
symbol: Type.String(),
decimals: Type.Number(),
tokenId: Type.Optional(Type.String()),
}),
amount: Type.String(),
});
Expand Down
2 changes: 2 additions & 0 deletions carbonmark-api/src/models/DetailedProject.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export const DetailedProjectModel = Type.Object({
listings: Type.Array(ListingModel),
price: Type.String(),
vintage: Type.String(),
serialization: Nullable(Type.String()),
tokenId: Nullable(Type.String()),
});

export type DetailedProject = Static<typeof DetailedProjectModel>;
1 change: 1 addition & 0 deletions carbonmark-api/src/models/Project.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const ProjectModel = Type.Object({
})
)
),
serialization: Type.Optional(Type.String()),
});

export type Project = Static<typeof ProjectModel>;
3 changes: 3 additions & 0 deletions carbonmark-api/src/models/Purchase.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ export const PurchaseModel = Type.Object({
vintage: Type.String({
examples: ["2008"],
}),
serialization: Type.Optional(
Type.String({ examples: ["ICR-ISL-354-78040-14-R-0-2021"] })
),
}),
}),
price: Type.String({
Expand Down
3 changes: 2 additions & 1 deletion carbonmark-api/src/routes/activities/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ const handler = (fastify: FastifyInstance) =>
}>,
reply: FastifyReply
): Promise<Activity[]> {
const network = request.query.network ?? "polygon";
return asResponse(
reply,
await getActivities(fastify, request.query, request.query.network)
await getActivities(fastify, request.query, network)
);
};

Expand Down
5 changes: 3 additions & 2 deletions carbonmark-api/src/routes/countries/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ const handler = (
): RouteHandler<{ Querystring: Static<typeof Querystring> }> =>
async function (request, reply) {
let response;
const sdk = gql_sdk(request.query.network);
const network = request.query.network ?? "polygon";
const sdk = gql_sdk(network);
try {
response = await getAllCountries(sdk, fastify);
response = await getAllCountries(sdk, fastify, network);
} catch (error) {
//Return bad gateway and pass the error
console.error(error);
Expand Down
4 changes: 3 additions & 1 deletion carbonmark-api/src/routes/projects/[id]/activity/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ const handler = (fastify: FastifyInstance) =>
reply: FastifyReply
): Promise<Activity[]> {
request.query.projectId = [request.params.id];

const network = request.query.network ?? "polygon";
return asResponse(
reply,
await getActivities(fastify, request.query, request.query.network)
await getActivities(fastify, request.query, network)
);
};

Expand Down
62 changes: 51 additions & 11 deletions carbonmark-api/src/routes/projects/[id]/get.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { compact, concat, min } from "lodash";
import { mapValues, pipe, trim, uniq } from "lodash/fp";
import { REGISTRIES } from "../../../app.constants";
import { DetailedProject } from "../../../models/DetailedProject.model";
import { CreditId } from "../../../utils/CreditId";
import { gql_sdk } from "../../../utils/gqlSdk";
import { fetchCarbonProject } from "../../../utils/helpers/cms.utils";
import {
fetchCarbonProject,
type FetchCarbonProjectArgs,
type FetchCarbonProjectMethod,
type ProjectImage,
} from "../../../utils/helpers/cms.utils";

import { fetchMarketplaceListings } from "../../../utils/helpers/fetchMarketplaceListings";
import { fetchPoolPricesAndStats } from "../../../utils/helpers/fetchPoolPricesAndStats";
import { toGeoJSON } from "../get.utils";
import { ICR_API } from "./../../../../src/utils/ICR/ICR_API_endpoints";
import { Params, Querystring, schema } from "./get.schema";

// Handler function for the "/projects/:id" route
Expand All @@ -20,37 +28,65 @@ const handler = (fastify: FastifyInstance) =>
reply: FastifyReply
) {
const { id } = request.params;
const sdk = gql_sdk(request.query.network);

const network = request.query.network ?? "polygon";

const sdk = gql_sdk(network);

const {
vintage,
standard: registry,
registryProjectId,
projectId: key,
} = new CreditId(id);

let fetchCarbonProjectMethod: FetchCarbonProjectMethod;
let fetchCarbonProjectArgs: FetchCarbonProjectArgs;
let icrSerialization: string | undefined;

switch (registry) {
case REGISTRIES["ICR"].id: {
const { ICR_API_URL } = ICR_API(network);
fetchCarbonProjectMethod = ICR_API_URL;
fetchCarbonProjectArgs = {
serialization: id,
network: network,
};
icrSerialization = id;
break;
}
default:
fetchCarbonProjectMethod = sdk;
fetchCarbonProjectArgs = {
registry,
registryProjectId,
network: network,
};
break;
}

let poolPrices, stats, listings, projectDetails;

try {
[[poolPrices, stats], [listings], projectDetails] = await Promise.all([
fetchPoolPricesAndStats(sdk, {
key,
vintage,
network: request.query.network || "polygon",
icrSerialization,
}),
fetchMarketplaceListings(sdk, {
key,
vintage,
fastify,
expiresAfter: request.query.expiresAfter,
}),
fetchCarbonProject(sdk, {
registry,
registryProjectId,
}),
fetchCarbonProject(fetchCarbonProjectMethod, fetchCarbonProjectArgs),
]);
} catch (error) {
console.error(error);
throw error;
}

if (!projectDetails) {
// only render pages if project details exist (render even if there are no listings!)
return reply.notFound();
Expand Down Expand Up @@ -83,13 +119,17 @@ const handler = (fastify: FastifyInstance) =>
price: String(bestPrice ?? 0), // remove trailing zeros
prices: poolPrices,
images:
projectDetails?.images?.map((image) => ({
caption: image?.asset?.altText,
url: image?.asset?.url,
})) ?? [],
projectDetails?.images
?.filter((image): image is ProjectImage => !!image)
.map((image: ProjectImage) => ({
caption: image?.asset?.altText || "",
url: image?.asset?.url || "",
})) ?? [],
listings,
vintage,
stats,
serialization: icrSerialization,
tokenId: projectDetails.tokenId,
};
return reply
.header("Content-Type", "application/json; charset=utf-8")
Expand Down
59 changes: 38 additions & 21 deletions carbonmark-api/src/routes/projects/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { mapValues, omit, sortBy } from "lodash";
import { split } from "lodash/fp";
import { Project } from "../../models/Project.model";
import { CreditId } from "../../utils/CreditId";
import { fetchAllICRProjects } from "../../utils/ICR/icr.utils";
import { gql_sdk } from "../../utils/gqlSdk";
import { fetchAllCarbonProjects } from "../../utils/helpers/cms.utils";
import { fetchAllPoolPrices } from "../../utils/helpers/fetchAllPoolPrices";
Expand Down Expand Up @@ -35,40 +36,51 @@ const handler = (fastify: FastifyInstance) =>
request: FastifyRequest<{ Querystring: Querystring }>,
reply: FastifyReply
): Promise<Project[]> {
const sdk = gql_sdk(request.query.network);
const network = request.query.network ?? "polygon";

const sdk = gql_sdk(network);
//Transform the list params (category, country etc) provided so as to be an array of strings
const args = mapValues(
omit(request.query, "search", "expiresAfter"),
split(",")
);

//Get the default args to return all results unless specified
const allOptions = await getDefaultQueryArgs(sdk, fastify);
const allOptions = await getDefaultQueryArgs(sdk, fastify, network);

const [
marketplaceProjectsData,
poolProjectsData,
cmsProjects,
poolPrices,
IcrListProjects,
] = await Promise.all([
sdk.marketplace.getProjects({
search: request.query.search ?? "",
category: args.category ?? allOptions.category,
country: args.country ?? allOptions.country,
vintage: args.vintage ?? allOptions.vintage,
expiresAfter: request.query.expiresAfter ?? allOptions.expiresAfter,
}),
sdk.digital_carbon.findDigitalCarbonProjects({
search: request.query.search ?? "",
category: args.category ?? allOptions.category,
country: args.country ?? allOptions.country,
vintage: (args.vintage ?? allOptions.vintage).map(Number),
}),
fetchAllCarbonProjects(sdk),
fetchAllPoolPrices(sdk),
fetchAllICRProjects(network),
]);

const [marketplaceProjectsData, poolProjectsData, cmsProjects, poolPrices] =
await Promise.all([
sdk.marketplace.getProjects({
search: request.query.search ?? "",
category: args.category ?? allOptions.category,
country: args.country ?? allOptions.country,
vintage: args.vintage ?? allOptions.vintage,
expiresAfter: request.query.expiresAfter ?? allOptions.expiresAfter,
}),
sdk.digital_carbon.findDigitalCarbonProjects({
search: request.query.search ?? "",
category: args.category ?? allOptions.category,
country: args.country ?? allOptions.country,
vintage: (args.vintage ?? allOptions.vintage).map(Number),
}),
fetchAllCarbonProjects(sdk),
fetchAllPoolPrices(sdk),
]);
const CMSDataMap: CMSDataMap = new Map();
const ProjectMap: ProjectDataMap = new Map();

cmsProjects.forEach((project) => {
if (!CreditId.isValidProjectId(project.id)) return;

const [standard, registryProjectId] = CreditId.splitProjectId(project.id); // type guard and capitalize

CMSDataMap.set(`${standard}-${registryProjectId}`, project);
});

Expand Down Expand Up @@ -123,7 +135,12 @@ const handler = (fastify: FastifyInstance) =>
});
});
/** Compose all the data together to unique entries (unsorted) */
const entries = composeProjectEntries(ProjectMap, CMSDataMap, poolPrices);
const entries = composeProjectEntries(
ProjectMap,
CMSDataMap,
poolPrices,
IcrListProjects
);

const sortedEntries = sortBy(entries, (e) => Number(e.price));
// Send the transformed projects array as a JSON string in the response
Expand Down
Loading

0 comments on commit d0c8167

Please sign in to comment.