diff --git a/.env b/.env
index afa8ab9..ef52312 100644
--- a/.env
+++ b/.env
@@ -2,10 +2,4 @@ DB_NAME=postgres
DB_PASS=postgres
DB_PORT=5432
DB_USERNAME=postgres
-DB_HOST=db
-SHARED_CONFIG_URL="https://ipfs.io/ipfs/bafkreiasnla2ya55of6nwm3swjstip4q2ixfa3t6tvixyibclfovxnerte"
-1_METADATA='{"id": 1, "rpcUrl": "http://evm1:8545"}'
-2_METADATA='{"id": 2, "rpcUrl": "http://evm2:8545"}'
-3_METADATA='{"id": 3, "rpcUrl": "ws://substrate-pallet:9944"}'
-
-ENV_DOMAINS='[1,2,3]'
\ No newline at end of file
+DB_HOST=db
\ No newline at end of file
diff --git a/README.md b/README.md
index d8701a2..0c39cc5 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,10 @@ Fetches transfers ordered by time. Results can be filtered by various query para
- **component**: Component of the transfer.
*Possible values*: `deposit`, `execution`
*Default*: `deposit`
- **sender**: The address of the sender to filter by.
+`GET /domains`
+
+Fetches all active domains.
+
`GET /health`
Health check endpoint to ensure the system is running properly
diff --git a/db/migrations/1734434955039-Data.js b/db/migrations/1734706828708-Data.js
similarity index 97%
rename from db/migrations/1734434955039-Data.js
rename to db/migrations/1734706828708-Data.js
index 1f05bd9..526221d 100644
--- a/db/migrations/1734434955039-Data.js
+++ b/db/migrations/1734706828708-Data.js
@@ -1,5 +1,5 @@
-module.exports = class Data1734434955039 {
- name = 'Data1734434955039'
+module.exports = class Data1734706828708 {
+ name = 'Data1734706828708'
async up(db) {
await db.query(`CREATE TABLE "account" ("id" character varying NOT NULL, "address_status" text, CONSTRAINT "PK_54115ee388cdb6d86bb4bf5b2ea" PRIMARY KEY ("id"))`)
@@ -12,7 +12,7 @@ module.exports = class Data1734434955039 {
await db.query(`CREATE INDEX "IDX_863162a8edb416799e89f386f8" ON "route" ("to_domain_id") `)
await db.query(`CREATE INDEX "IDX_4536bd7b96f363dedfe95dd26f" ON "route" ("resource_id") `)
await db.query(`CREATE UNIQUE INDEX "IDX_7dc7af2a7a9c846759377d1450" ON "route" ("from_domain_id", "to_domain_id", "resource_id") `)
- await db.query(`CREATE TABLE "domain" ("id" character varying NOT NULL, "type" text NOT NULL, "name" text NOT NULL, CONSTRAINT "PK_27e3ec3ea0ae02c8c5bceab3ba9" PRIMARY KEY ("id"))`)
+ await db.query(`CREATE TABLE "domain" ("id" character varying NOT NULL, "type" text NOT NULL, "name" text NOT NULL, "icon_url" text NOT NULL, "explorer_url" text NOT NULL, CONSTRAINT "PK_27e3ec3ea0ae02c8c5bceab3ba9" PRIMARY KEY ("id"))`)
await db.query(`CREATE TABLE "token" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "decimals" integer NOT NULL, "token_address" text NOT NULL, "token_symbol" text NOT NULL, "resource_id" character varying, "domain_id" character varying, CONSTRAINT "PK_82fae97f905930df5d62a702fc9" PRIMARY KEY ("id"))`)
await db.query(`CREATE INDEX "IDX_435ef0917a04e91698042dff2b" ON "token" ("resource_id") `)
await db.query(`CREATE INDEX "IDX_3d565341b16e9c03d63e05eac8" ON "token" ("domain_id") `)
@@ -78,4 +78,4 @@ module.exports = class Data1734434955039 {
await db.query(`ALTER TABLE "transfer" DROP CONSTRAINT "FK_f6b9e9b86a1ce51c26cd08f596a"`)
await db.query(`ALTER TABLE "transfer" DROP CONSTRAINT "FK_da16210e30a21445643e6acbcd7"`)
}
-}
\ No newline at end of file
+}
diff --git a/envs/.env.api b/envs/.env.api
deleted file mode 100644
index a487c9b..0000000
--- a/envs/.env.api
+++ /dev/null
@@ -1,5 +0,0 @@
-DB_NAME=squid
-DB_PASS=squid
-DB_PORT=5432
-DB_USERNAME=postgres
-DB_HOST=db
\ No newline at end of file
diff --git a/envs/.env.evm-1 b/envs/.env.evm-1
deleted file mode 100644
index 0692246..0000000
--- a/envs/.env.evm-1
+++ /dev/null
@@ -1,13 +0,0 @@
-DB_NAME=squid
-DB_PASS=squid
-DB_PORT=5432
-DB_USERNAME=postgres
-DB_HOST=db
-START_SCRIPT_ENV=index
-
-PROCESSOR_PROMETHEUS_PORT=3000
-
-SHARED_CONFIG_URL="https://ipfs.io/ipfs/bafkreiasnla2ya55of6nwm3swjstip4q2ixfa3t6tvixyibclfovxnerte"
-
-DOMAIN_ID=1
-1_METADATA='{"rpcUrl": "http://evm1:8545"}'
diff --git a/envs/.env.evm-2 b/envs/.env.evm-2
deleted file mode 100644
index 1889c40..0000000
--- a/envs/.env.evm-2
+++ /dev/null
@@ -1,13 +0,0 @@
-DB_NAME=squid
-DB_PASS=squid
-DB_PORT=5432
-DB_USERNAME=postgres
-DB_HOST=db
-START_SCRIPT_ENV=index
-
-PROCESSOR_PROMETHEUS_PORT=3002
-
-SHARED_CONFIG_URL="https://ipfs.io/ipfs/bafkreiasnla2ya55of6nwm3swjstip4q2ixfa3t6tvixyibclfovxnerte"
-
-DOMAIN_ID=2
-2_METADATA='{"rpcUrl": "http://evm2:8545"}'
diff --git a/envs/.env.indexer.example b/envs/.env.indexer.example
index afa8ab9..3eae8a5 100644
--- a/envs/.env.indexer.example
+++ b/envs/.env.indexer.example
@@ -4,8 +4,8 @@ DB_PORT=5432
DB_USERNAME=postgres
DB_HOST=db
SHARED_CONFIG_URL="https://ipfs.io/ipfs/bafkreiasnla2ya55of6nwm3swjstip4q2ixfa3t6tvixyibclfovxnerte"
-1_METADATA='{"id": 1, "rpcUrl": "http://evm1:8545"}'
-2_METADATA='{"id": 2, "rpcUrl": "http://evm2:8545"}'
-3_METADATA='{"id": 3, "rpcUrl": "ws://substrate-pallet:9944"}'
+1_METADATA='{"id": 1, "rpcUrl": "http://evm1:8545", "iconUrl": "https://example.com/icon1.png", "explorerUrl": "https://explorer.com/1"}'
+2_METADATA='{"id": 2, "rpcUrl": "http://evm2:8545", "iconUrl": "https://example.com/icon2.png", "explorerUrl": "https://explorer.com/2"}'
+3_METADATA='{"id": 3, "rpcUrl": "ws://substrate-pallet:9944", "iconUrl": "https://example.com/icon3.png", "explorerUrl": "https://explorer.com/3"}'
ENV_DOMAINS='[1,2,3]'
\ No newline at end of file
diff --git a/envs/.env.substrate b/envs/.env.substrate
deleted file mode 100644
index 0a2a60d..0000000
--- a/envs/.env.substrate
+++ /dev/null
@@ -1,13 +0,0 @@
-DB_NAME=squid
-DB_PASS=squid
-DB_PORT=5432
-DB_USERNAME=postgres
-DB_HOST=db
-START_SCRIPT_ENV=index
-
-PROCESSOR_PROMETHEUS_PORT=3004
-
-SHARED_CONFIG_URL="https://ipfs.io/ipfs/bafkreiasnla2ya55of6nwm3swjstip4q2ixfa3t6tvixyibclfovxnerte"
-
-DOMAIN_ID=3
-3_METADATA='{"rpcUrl": "ws://substrate-pallet:9944"}'
diff --git a/schema.graphql b/schema.graphql
index 8b8b562..bf55092 100644
--- a/schema.graphql
+++ b/schema.graphql
@@ -67,6 +67,8 @@ type Domain @entity {
id: ID!
type: String!
name: String!
+ iconURL: String!
+ explorerURL: String!
routesFrom: [Route!] @derivedFrom(field: "fromDomain")
routesTo: [Route!] @derivedFrom(field: "toDomain")
token: [Token!] @derivedFrom(field: "domain")
diff --git a/src/api/controllers/DomainsController.ts b/src/api/controllers/DomainsController.ts
new file mode 100644
index 0000000..6166aae
--- /dev/null
+++ b/src/api/controllers/DomainsController.ts
@@ -0,0 +1,30 @@
+/*
+The Licensed Work is (c) 2024 Sygma
+SPDX-License-Identifier: LGPL-3.0-only
+*/
+import type { FastifyReply, FastifyRequest } from "fastify";
+import type { DataSource } from "typeorm";
+
+import { logger } from "../../utils/logger";
+import { DomainsService } from "../services/dataAccess/domains.service";
+
+export class DomainsController {
+ private domainsService: DomainsService;
+
+ constructor(dataSource: DataSource) {
+ this.domainsService = new DomainsService(dataSource);
+ }
+
+ public async getDomains(
+ request: FastifyRequest,
+ reply: FastifyReply,
+ ): Promise {
+ try {
+ const domainsResult = await this.domainsService.findDomains({});
+ await reply.status(200).send(domainsResult);
+ } catch (error) {
+ logger.error("Error occurred when fetching domains", error);
+ await reply.status(500).send({ error: "Internal server error" });
+ }
+ }
+}
diff --git a/src/api/routes/domains.routes.ts b/src/api/routes/domains.routes.ts
new file mode 100644
index 0000000..990160f
--- /dev/null
+++ b/src/api/routes/domains.routes.ts
@@ -0,0 +1,18 @@
+/*
+The Licensed Work is (c) 2024 Sygma
+SPDX-License-Identifier: LGPL-3.0-only
+*/
+import type { FastifyInstance } from "fastify";
+
+import { DomainsController } from "../controllers/DomainsController";
+import { domainsSchema } from "../schemas/domains.schema";
+
+export async function domainRoutes(server: FastifyInstance): Promise {
+ const domainsController = new DomainsController(server.db);
+ server.get(
+ "/domains",
+ { schema: domainsSchema },
+ domainsController.getDomains.bind(domainsController),
+ );
+ return Promise.resolve();
+}
diff --git a/src/api/routes/index.ts b/src/api/routes/index.ts
index f523559..e0bf4b7 100644
--- a/src/api/routes/index.ts
+++ b/src/api/routes/index.ts
@@ -4,8 +4,10 @@ SPDX-License-Identifier: LGPL-3.0-only
*/
import type { FastifyInstance } from "fastify";
+import { domainRoutes } from "./domains.routes";
import { transferRoutes } from "./transfers.routes";
export async function registerRoutes(server: FastifyInstance): Promise {
await server.register(transferRoutes, { prefix: "/api" });
+ await server.register(domainRoutes, { prefix: "/api" });
}
diff --git a/src/api/schemas/domains.schema.ts b/src/api/schemas/domains.schema.ts
new file mode 100644
index 0000000..485eb8a
--- /dev/null
+++ b/src/api/schemas/domains.schema.ts
@@ -0,0 +1,25 @@
+/*
+The Licensed Work is (c) 2024 Sygma
+SPDX-License-Identifier: LGPL-3.0-only
+*/
+import { domainMetadataSchema, domainSchema } from ".";
+
+export const domainsSchema = {
+ summary: "Get domains",
+ response: {
+ 200: {
+ description: "List of domains",
+ content: {
+ "application/json": {
+ schema: {
+ type: "array",
+ items: {
+ ...domainSchema.properties,
+ ...domainMetadataSchema.properties,
+ },
+ },
+ },
+ },
+ },
+ },
+};
diff --git a/src/api/schemas/index.ts b/src/api/schemas/index.ts
index 88d6049..32b1b2c 100644
--- a/src/api/schemas/index.ts
+++ b/src/api/schemas/index.ts
@@ -25,6 +25,14 @@ export const domainSchema = {
},
};
+export const domainMetadataSchema = {
+ type: "object",
+ properties: {
+ iconURL: { type: "string", example: "https://example.com/icon1.png" },
+ explorerURL: { type: "string", example: "https://explorer.com/1" },
+ },
+};
+
export const tokenSchema = {
tpye: "object",
properties: {
diff --git a/src/api/services/dataAccess/domains.service.ts b/src/api/services/dataAccess/domains.service.ts
new file mode 100644
index 0000000..c08f104
--- /dev/null
+++ b/src/api/services/dataAccess/domains.service.ts
@@ -0,0 +1,24 @@
+/*
+The Licensed Work is (c) 2024 Sygma
+SPDX-License-Identifier: LGPL-3.0-only
+*/
+import type { DataSource, FindOptionsWhere, Repository } from "typeorm";
+
+import { Domain } from "../../../model";
+
+export class DomainsService {
+ private domainRepository: Repository;
+ constructor(dataSource: DataSource) {
+ this.domainRepository = dataSource.getRepository(Domain);
+ }
+
+ public async findDomains(where: FindOptionsWhere): Promise {
+ const domains = await this.domainRepository.find({
+ where,
+ order: {
+ id: "ASC",
+ },
+ });
+ return domains;
+ }
+}
diff --git a/src/indexer/config/envLoader.ts b/src/indexer/config/envLoader.ts
index 6d5b5f2..65ae8b6 100644
--- a/src/indexer/config/envLoader.ts
+++ b/src/indexer/config/envLoader.ts
@@ -15,6 +15,8 @@ export type DomainMetadata = {
domainId: number;
rpcUrl: string;
domainGateway?: string;
+ iconUrl: string;
+ explorerUrl: string;
};
export type EnvVariables = {
diff --git a/src/indexer/evmIndexer/evmProcessor.ts b/src/indexer/evmIndexer/evmProcessor.ts
index 1e14a40..b5a32df 100644
--- a/src/indexer/evmIndexer/evmProcessor.ts
+++ b/src/indexer/evmIndexer/evmProcessor.ts
@@ -72,7 +72,7 @@ export class EVMProcessor implements IProcessor {
for (const block of ctx.blocks) {
this.logger.info(
- `Processing block ${block.header.height} on networ ${domain.name}(${domain.id})`,
+ `Processing block ${block.header.height} on network ${domain.name}(${domain.id})`,
);
for (const log of block.logs) {
try {
diff --git a/src/indexer/substrateIndexer/substrateParser.ts b/src/indexer/substrateIndexer/substrateParser.ts
index 4a09986..8af07b7 100644
--- a/src/indexer/substrateIndexer/substrateParser.ts
+++ b/src/indexer/substrateIndexer/substrateParser.ts
@@ -73,7 +73,7 @@ export class SubstrateParser implements ISubstrateParser {
});
if (!resource) {
throw new NotFoundError(
- `Unssupported resource with ID ${event.resourceId}`,
+ `Unsupported resource with ID ${event.resourceId}`,
);
}
@@ -218,7 +218,7 @@ export class SubstrateParser implements ISubstrateParser {
});
if (!resource) {
throw new NotFoundError(
- `Unssupported resource with ID ${event.resourceId}`,
+ `Unsupported resource with ID ${event.resourceId}`,
);
}
diff --git a/src/indexer/substrateIndexer/substrateProcessor.ts b/src/indexer/substrateIndexer/substrateProcessor.ts
index 5e42901..8281725 100644
--- a/src/indexer/substrateIndexer/substrateProcessor.ts
+++ b/src/indexer/substrateIndexer/substrateProcessor.ts
@@ -87,7 +87,7 @@ export class SubstrateProcessor implements IProcessor {
const fees: FeeCollectedData[] = [];
for (const block of ctx.blocks) {
this.logger.info(
- `processing bloc ${block.header.height} on networ ${domain.name}(${domain.id})`,
+ `Processing block ${block.header.height} on network ${domain.name}(${domain.id})`,
);
for (const event of block.events) {
try {
diff --git a/src/main_init.ts b/src/main_init.ts
index bb2a090..44d991d 100644
--- a/src/main_init.ts
+++ b/src/main_init.ts
@@ -7,7 +7,7 @@ import type { EntityManager } from "typeorm";
import type { Domain as DomainConfig } from "./indexer/config";
import { fetchSharedConfig } from "./indexer/config";
-import { getEnv } from "./indexer/config/envLoader";
+import { getDomainMetadata, getEnv } from "./indexer/config/envLoader";
import { Domain, Resource, Token } from "./model";
import { initDatabase } from "./utils";
@@ -18,21 +18,33 @@ export async function init(): Promise {
const dataSource = await initDatabase(envVars.dbConfig);
const sharedConfig = await fetchSharedConfig(envVars.sharedConfigURL);
- await insertDomains(sharedConfig.domains, dataSource.manager);
+ await insertDomains(
+ sharedConfig.domains,
+ dataSource.manager,
+ envVars.envDomains,
+ );
await dataSource.destroy();
}
async function insertDomains(
domains: Array,
manager: EntityManager,
+ supportedDomainsIDs: number[],
): Promise {
- for (const domain of domains) {
+ for (const domainID of supportedDomainsIDs) {
+ const domain = domains.find((domain) => domain.id == domainID);
+ if (!domain) {
+ throw new Error(`domain with id ${domainID} not found in shared-config`);
+ }
+ const domainMetadata = getDomainMetadata(domain.id.toString());
await manager.upsert(
Domain,
{
id: domain.id.toString(),
type: domain.type,
name: domain.name,
+ iconURL: domainMetadata.iconUrl ?? "",
+ explorerURL: domainMetadata.explorerUrl ?? "",
},
["id"],
);
diff --git a/src/model/generated/domain.model.ts b/src/model/generated/domain.model.ts
index 3e72997..c5281c8 100644
--- a/src/model/generated/domain.model.ts
+++ b/src/model/generated/domain.model.ts
@@ -21,6 +21,12 @@ export class Domain {
@StringColumn_({nullable: false})
name!: string
+ @StringColumn_({nullable: false})
+ iconURL!: string
+
+ @StringColumn_({nullable: false})
+ explorerURL!: string
+
@OneToMany_(() => Route, e => e.fromDomain)
routesFrom!: Route[]
diff --git a/tests/e2e/domains.spec.ts b/tests/e2e/domains.spec.ts
new file mode 100644
index 0000000..ddd30d7
--- /dev/null
+++ b/tests/e2e/domains.spec.ts
@@ -0,0 +1,24 @@
+/*
+The Licensed Work is (c) 2024 Sygma
+SPDX-License-Identifier: LGPL-3.0-only
+*/
+
+import { expect } from "chai";
+import { Domain } from "../../src/model";
+
+const NUMBER_OF_DOMAINS = 3;
+
+describe("Domains tests", function () {
+ it("should succesfully fetch all domains", async () => {
+ const response = await fetch("http://localhost:8000/api/domains");
+ const domains: Array = await response.json();
+ expect(domains.length).to.be.deep.equal(NUMBER_OF_DOMAINS);
+ domains.map((domain) => {
+ expect(domain.id).to.be.not.null;
+ expect(domain.name).to.be.not.null;
+ expect(domain.type).to.be.not.null;
+ expect(domain.iconURL).to.be.not.null;
+ expect(domain.explorerURL).to.be.not.null;
+ });
+ });
+});