2022-09-08 13:39:12 -07:00
|
|
|
const express = require("express");
|
|
|
|
const app = express();
|
2022-09-11 13:57:01 -07:00
|
|
|
const port = 4000;
|
2022-09-08 13:39:12 -07:00
|
|
|
|
|
|
|
const { MongoClient } = require("mongodb");
|
|
|
|
const mongoURI = process.env.MONGODB_URI;
|
|
|
|
if (!mongoURI) {
|
|
|
|
console.error("You must set your 'MONGODB_URI' environmental variable.");
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
const mongoClient = new MongoClient(mongoURI);
|
|
|
|
|
2022-09-28 16:44:07 -07:00
|
|
|
/*
|
|
|
|
* Helpers
|
|
|
|
*/
|
|
|
|
|
2022-09-14 22:25:02 -07:00
|
|
|
async function paginatedFind(collection, req, filter) {
|
2022-09-11 13:57:01 -07:00
|
|
|
const limit =
|
|
|
|
req.query?.limit && req.query.limit <= 100 ? req.query.limit : 20;
|
|
|
|
const skip = req.query?.page ? req.query?.page * limit : undefined;
|
|
|
|
const query = req.query?.before
|
2022-11-10 05:46:02 -08:00
|
|
|
? { ...filter, indexedAt: { $lt: new Date(req.query.before) } }
|
2022-09-14 22:25:02 -07:00
|
|
|
: filter;
|
2022-09-11 13:57:01 -07:00
|
|
|
const cursor = await collection.find(query, {
|
2022-11-10 05:46:02 -08:00
|
|
|
sort: { indexedAt: -1 },
|
2022-09-11 13:57:01 -07:00
|
|
|
skip,
|
|
|
|
limit,
|
|
|
|
});
|
|
|
|
return cursor;
|
|
|
|
}
|
|
|
|
|
2022-10-28 08:24:25 -07:00
|
|
|
async function findAndSendMany(
|
|
|
|
db,
|
|
|
|
res,
|
|
|
|
collectionName,
|
|
|
|
reqForPagination,
|
|
|
|
filter,
|
|
|
|
project
|
|
|
|
) {
|
|
|
|
const database = mongoClient.db(db);
|
2022-09-14 22:25:02 -07:00
|
|
|
const collection = database.collection(collectionName);
|
|
|
|
const cursor = await (reqForPagination
|
|
|
|
? paginatedFind(collection, reqForPagination, filter)
|
2022-10-28 08:24:25 -07:00
|
|
|
: collection.find(filter).project(project));
|
2022-09-08 13:39:12 -07:00
|
|
|
const result = await cursor.toArray();
|
2022-09-14 22:25:02 -07:00
|
|
|
if (result.length === 0) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
2022-09-08 13:39:12 -07:00
|
|
|
res.send(result);
|
2022-09-14 22:25:02 -07:00
|
|
|
}
|
2022-09-08 13:39:12 -07:00
|
|
|
|
2022-10-28 08:24:25 -07:00
|
|
|
async function findAndSendOne(db, res, collectionName, filter, project) {
|
|
|
|
const database = mongoClient.db(db);
|
2022-09-14 22:25:02 -07:00
|
|
|
const collection = database.collection(collectionName);
|
2022-10-28 08:24:25 -07:00
|
|
|
const result = await collection.findOne(filter, project);
|
2022-09-08 13:39:12 -07:00
|
|
|
if (!result) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
res.send(result);
|
2022-09-14 22:25:02 -07:00
|
|
|
}
|
|
|
|
|
2022-09-28 16:44:07 -07:00
|
|
|
/*
|
|
|
|
* Heartbeats
|
|
|
|
*/
|
|
|
|
|
2022-09-14 22:25:02 -07:00
|
|
|
app.get("/api/heartbeats", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
await findAndSendMany("wormhole", res, "heartbeats");
|
2022-09-08 13:39:12 -07:00
|
|
|
});
|
|
|
|
|
2022-09-28 16:44:07 -07:00
|
|
|
/*
|
|
|
|
* VAAs
|
|
|
|
*/
|
|
|
|
|
2022-09-08 13:39:12 -07:00
|
|
|
app.get("/api/vaas", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
await findAndSendMany("wormhole", res, "vaas", req);
|
2022-09-08 13:39:12 -07:00
|
|
|
});
|
|
|
|
|
2022-09-14 22:25:02 -07:00
|
|
|
app.get("/api/vaas/:chain", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
await findAndSendMany("wormhole", res, "vaas", req, {
|
2022-09-14 22:25:02 -07:00
|
|
|
_id: { $regex: `^${req.params.chain}/.*` },
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get("/api/vaas/:chain/:emitter", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
await findAndSendMany("wormhole", res, "vaas", req, {
|
2022-09-14 22:25:02 -07:00
|
|
|
_id: { $regex: `^${req.params.chain}/${req.params.emitter}/.*` },
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get("/api/vaas/:chain/:emitter/:sequence", async (req, res) => {
|
2022-09-08 13:39:12 -07:00
|
|
|
const id = `${req.params.chain}/${req.params.emitter}/${req.params.sequence}`;
|
2022-10-28 08:24:25 -07:00
|
|
|
await findAndSendOne("wormhole", res, "vaas", { _id: id });
|
2022-09-08 13:39:12 -07:00
|
|
|
});
|
|
|
|
|
2022-09-28 16:44:07 -07:00
|
|
|
app.get("/api/vaas-sans-pythnet", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
await findAndSendMany("wormhole", res, "vaas", req, {
|
2022-09-28 16:44:07 -07:00
|
|
|
_id: { $not: { $regex: `^26/.*` } },
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-09-29 06:40:07 -07:00
|
|
|
app.get("/api/vaa-counts", async (req, res) => {
|
|
|
|
const database = mongoClient.db("wormhole");
|
|
|
|
const collection = database.collection("vaas");
|
|
|
|
const cursor = await collection.aggregate([
|
|
|
|
{
|
|
|
|
$bucket: {
|
|
|
|
groupBy: "$_id",
|
|
|
|
boundaries: [
|
|
|
|
"1/",
|
|
|
|
"10/",
|
|
|
|
"11/",
|
|
|
|
"12/",
|
|
|
|
"13/",
|
|
|
|
"14/",
|
|
|
|
"15/",
|
|
|
|
"16/",
|
|
|
|
"18/",
|
|
|
|
"2/",
|
|
|
|
"26/",
|
|
|
|
"3/",
|
|
|
|
"4/",
|
|
|
|
"5/",
|
|
|
|
"6/",
|
|
|
|
"7/",
|
|
|
|
"8/",
|
|
|
|
"9/",
|
|
|
|
],
|
|
|
|
default: "unknown",
|
|
|
|
output: {
|
|
|
|
count: { $sum: 1 },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
const result = await cursor.toArray();
|
|
|
|
if (result.length === 0) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
res.send(result);
|
|
|
|
});
|
|
|
|
|
2022-09-28 16:44:07 -07:00
|
|
|
/*
|
|
|
|
* Observations
|
|
|
|
*/
|
|
|
|
|
2022-09-08 13:39:12 -07:00
|
|
|
app.get("/api/observations", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
await findAndSendMany("wormhole", res, "observations", req);
|
2022-09-14 22:25:02 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
app.get("/api/observations/:chain", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
await findAndSendMany("wormhole", res, "observations", req, {
|
2022-09-14 22:25:02 -07:00
|
|
|
_id: { $regex: `^${req.params.chain}/.*` },
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get("/api/observations/:chain/:emitter", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
await findAndSendMany("wormhole", res, "observations", req, {
|
2022-09-14 22:25:02 -07:00
|
|
|
_id: { $regex: `^${req.params.chain}/${req.params.emitter}/.*` },
|
|
|
|
});
|
2022-09-08 13:39:12 -07:00
|
|
|
});
|
|
|
|
|
2022-09-14 22:25:02 -07:00
|
|
|
app.get("/api/observations/:chain/:emitter/:sequence", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
await findAndSendMany("wormhole", res, "observations", req, {
|
2022-09-14 22:25:02 -07:00
|
|
|
_id: {
|
|
|
|
$regex: `^${req.params.chain}/${req.params.emitter}/${req.params.sequence}/.*`,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get(
|
2022-09-20 15:02:45 -07:00
|
|
|
"/api/observations/:chain/:emitter/:sequence/:signer/:hash",
|
2022-09-14 22:25:02 -07:00
|
|
|
async (req, res) => {
|
2022-09-20 15:02:45 -07:00
|
|
|
const id = `${req.params.chain}/${req.params.emitter}/${req.params.sequence}/${req.params.signer}/${req.params.hash}`;
|
2022-10-28 08:24:25 -07:00
|
|
|
await findAndSendOne("wormhole", res, "observations", { _id: id });
|
2022-09-14 22:25:02 -07:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2022-10-28 08:24:25 -07:00
|
|
|
/*
|
2022-11-08 06:53:12 -08:00
|
|
|
* GovernorConfig
|
2022-10-28 08:24:25 -07:00
|
|
|
*/
|
2022-11-08 06:53:12 -08:00
|
|
|
app.get("/api/governorConfig", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
const database = mongoClient.db("wormhole");
|
2022-11-08 06:53:12 -08:00
|
|
|
const collection = database.collection("governorConfig");
|
2022-10-28 08:24:25 -07:00
|
|
|
const cursor = await collection.find({}).project({
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
2022-11-08 06:53:12 -08:00
|
|
|
nodename: "$parsedConfig.nodename", //<-- rename fields to flatten
|
|
|
|
counter: "$parsedConfig.counter",
|
|
|
|
chains: "$parsedConfig.chains",
|
|
|
|
tokens: "$parsedConfig.tokens",
|
2022-10-28 08:24:25 -07:00
|
|
|
});
|
|
|
|
const result = await cursor.toArray();
|
|
|
|
if (result.length === 0) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
res.send(result);
|
|
|
|
});
|
|
|
|
|
2022-11-08 06:53:12 -08:00
|
|
|
app.get("/api/governorConfig/:guardianaddr", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
const id = `${req.params.guardianaddr}`;
|
|
|
|
await findAndSendOne(
|
|
|
|
"wormhole",
|
|
|
|
res,
|
2022-11-08 06:53:12 -08:00
|
|
|
"governorConfig",
|
2022-10-28 08:24:25 -07:00
|
|
|
{
|
|
|
|
_id: id,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
projection: {
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
2022-11-08 06:53:12 -08:00
|
|
|
nodename: "$parsedConfig.nodename", //<-- rename fields to flatten
|
|
|
|
counter: "$parsedConfig.counter",
|
|
|
|
chains: "$parsedConfig.chains",
|
|
|
|
tokens: "$parsedConfig.tokens",
|
2022-10-28 08:24:25 -07:00
|
|
|
},
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-11-08 06:53:12 -08:00
|
|
|
app.get("/api/governorLimits", async (req, res) => {
|
|
|
|
const database = mongoClient.db("wormhole");
|
|
|
|
const collection = database.collection("governorConfig");
|
|
|
|
const cursor = await collection.aggregate([
|
|
|
|
{
|
|
|
|
$lookup: {
|
|
|
|
from: "governorStatus",
|
|
|
|
localField: "_id",
|
|
|
|
foreignField: "_id",
|
|
|
|
as: "status",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$unwind: "$status",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
configChains: "$parsedConfig.chains",
|
|
|
|
statusChains: "$status.parsedStatus.chains",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$unwind: "$configChains",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$unwind: "$statusChains",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$match: {
|
|
|
|
$expr: { $eq: ["$configChains.chainid", "$statusChains.chainid"] },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$sort: {
|
|
|
|
"configChains.chainid": 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$group: {
|
|
|
|
_id: "$configChains.chainid",
|
|
|
|
notionalLimits: {
|
|
|
|
$push: {
|
|
|
|
notionalLimit: "$configChains.notionallimit",
|
|
|
|
maxTransactionSize: "$configChains.bigtransactionsize",
|
|
|
|
availableNotional: "$statusChains.remainingavailablenotional",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
chainId: "$_id",
|
|
|
|
notionalLimits: 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
const result = await cursor.toArray();
|
|
|
|
if (result.length === 0) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const minGuardianNum = 13;
|
|
|
|
var agg = [];
|
|
|
|
result.forEach((chain) => {
|
|
|
|
const sortedAvailableNotionals = chain.notionalLimits.sort(function (a, b) {
|
|
|
|
return parseInt(b.availableNotional) - parseInt(a.availableNotional);
|
|
|
|
});
|
|
|
|
const sortedNotionalLimits = chain.notionalLimits.sort(function (a, b) {
|
|
|
|
return parseInt(b.notionalLimit) - parseInt(a.notionalLimit);
|
|
|
|
});
|
|
|
|
|
|
|
|
const sortedMaxTransactionSize = chain.notionalLimits.sort(function (a, b) {
|
|
|
|
return parseInt(b.maxTransactionSize) - parseInt(a.maxTransactionSize);
|
|
|
|
});
|
|
|
|
agg.push({
|
|
|
|
chainId: chain.chainId,
|
|
|
|
availableNotional:
|
|
|
|
sortedAvailableNotionals[minGuardianNum - 1]?.availableNotional || null,
|
|
|
|
notionalLimit:
|
|
|
|
sortedNotionalLimits[minGuardianNum - 1]?.notionalLimit || null,
|
|
|
|
maxTransactionSize:
|
|
|
|
sortedMaxTransactionSize[minGuardianNum - 1]?.maxTransactionSize ||
|
|
|
|
null,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
res.send(
|
|
|
|
agg.sort(function (a, b) {
|
|
|
|
return parseInt(a.chainId) - parseInt(b.chainId);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get("/api/notionalLimits", async (req, res) => {
|
|
|
|
const database = mongoClient.db("wormhole");
|
|
|
|
const collection = database.collection("governorConfig");
|
|
|
|
const cursor = await collection.aggregate([
|
|
|
|
{
|
|
|
|
$match: {},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
chains: "$parsedConfig.chains",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$unwind: "$chains",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$sort: {
|
|
|
|
"chains.chainid": 1,
|
|
|
|
"chains.notionallimit": -1,
|
|
|
|
"chains.bigtransactionsize": -1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$group: {
|
|
|
|
_id: "$chains.chainid",
|
|
|
|
notionalLimits: {
|
|
|
|
$push: {
|
|
|
|
notionalLimit: "$chains.notionallimit",
|
|
|
|
maxTransactionSize: "$chains.bigtransactionsize",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
chainId: "$_id",
|
|
|
|
notionalLimits: 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
const result = await cursor.toArray();
|
|
|
|
console.log(result);
|
|
|
|
if (result.length === 0) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const minGuardianNum = 13;
|
|
|
|
var agg = [];
|
|
|
|
result.forEach((chain) => {
|
|
|
|
agg.push({
|
|
|
|
chainId: chain.chainId,
|
|
|
|
notionalLimit:
|
|
|
|
chain.notionalLimits[minGuardianNum - 1]?.notionalLimit || null,
|
|
|
|
maxTransactionSize:
|
|
|
|
chain.notionalLimits[minGuardianNum - 1]?.maxTransactionSize || null,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
res.send(
|
|
|
|
agg.sort(function (a, b) {
|
|
|
|
return parseInt(a.chainId) - parseInt(b.chainId);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get("/api/notionalLimits/:chainNum", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
const id = `${req.params.chainNum}`;
|
|
|
|
const database = mongoClient.db("wormhole");
|
2022-11-08 06:53:12 -08:00
|
|
|
const collection = database.collection("governorConfig");
|
2022-10-28 08:24:25 -07:00
|
|
|
const cursor = await collection.aggregate([
|
|
|
|
{
|
|
|
|
$match: {},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
_id: 1,
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
2022-11-08 06:53:12 -08:00
|
|
|
nodeName: "$parsedConfig.nodename",
|
|
|
|
"parsedConfig.chains": {
|
2022-10-28 08:24:25 -07:00
|
|
|
$filter: {
|
2022-11-08 06:53:12 -08:00
|
|
|
input: "$parsedConfig.chains",
|
2022-10-28 08:24:25 -07:00
|
|
|
as: "chain",
|
|
|
|
cond: { $eq: [`$$chain.chainid`, parseInt(id)] },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
_id: 1,
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
|
|
|
nodeName: 1,
|
2022-11-08 06:53:12 -08:00
|
|
|
notionalLimits: { $arrayElemAt: ["$parsedConfig.chains", 0] },
|
2022-10-28 08:24:25 -07:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
_id: 1,
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
|
|
|
nodeName: 1,
|
2022-11-08 06:53:12 -08:00
|
|
|
chainId: "$notionalLimits.chainid",
|
|
|
|
notionalLimit: "$notionalLimits.notionallimit",
|
|
|
|
maxTransactionSize: "$notionalLimits.bigtransactionsize",
|
2022-10-28 08:24:25 -07:00
|
|
|
},
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
const result = await cursor.toArray();
|
|
|
|
if (result.length === 0) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
res.send(result);
|
|
|
|
});
|
|
|
|
|
2022-11-08 06:53:12 -08:00
|
|
|
/*
|
|
|
|
* GovernorStatus
|
|
|
|
*/
|
|
|
|
app.get("/api/governorStatus", async (req, res) => {
|
|
|
|
const database = mongoClient.db("wormhole");
|
|
|
|
const collection = database.collection("governorStatus");
|
|
|
|
const cursor = await collection.find({}).project({
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
|
|
|
nodename: "$parsedStatus.nodename", //<-- rename fields to flatten
|
|
|
|
chains: "$parsedStatus.chains",
|
|
|
|
});
|
|
|
|
const result = await cursor.toArray();
|
|
|
|
if (result.length === 0) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
res.send(result);
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get("/api/governorStatus/:guardianaddr", async (req, res) => {
|
|
|
|
const id = `${req.params.guardianaddr}`;
|
|
|
|
await findAndSendOne(
|
|
|
|
"wormhole",
|
|
|
|
res,
|
|
|
|
"governorStatus",
|
|
|
|
{
|
|
|
|
_id: id,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
projection: {
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
|
|
|
nodename: "$parsedStatus.nodename",
|
|
|
|
chains: "$parsedStatus.chains",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get("/api/availableNotional", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
const database = mongoClient.db("wormhole");
|
|
|
|
const collection = database.collection("governorStatus");
|
|
|
|
const cursor = await collection.aggregate([
|
|
|
|
{
|
|
|
|
$match: {},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
chains: "$parsedStatus.chains",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$unwind: "$chains",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$sort: {
|
|
|
|
"chains.chainid": 1,
|
|
|
|
"chains.remainingavailablenotional": -1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$group: {
|
|
|
|
_id: "$chains.chainid",
|
|
|
|
availableNotionals: {
|
|
|
|
$push: {
|
|
|
|
availableNotional: "$chains.remainingavailablenotional",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
chainId: "$_id",
|
|
|
|
availableNotionals: 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
const result = await cursor.toArray();
|
|
|
|
if (result.length === 0) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const minGuardianNum = 13;
|
|
|
|
var agg = [];
|
|
|
|
result.forEach((chain) => {
|
|
|
|
agg.push({
|
|
|
|
chainId: chain.chainId,
|
|
|
|
availableNotional:
|
|
|
|
chain.availableNotionals[minGuardianNum - 1]?.availableNotional || null,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
res.send(
|
|
|
|
agg.sort(function (a, b) {
|
|
|
|
return parseInt(a.chainId) - parseInt(b.chainId);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-11-08 06:53:12 -08:00
|
|
|
app.get("/api/availableNotional/:chainNum", async (req, res) => {
|
|
|
|
const id = `${req.params.chainNum}`;
|
|
|
|
const database = mongoClient.db("wormhole");
|
|
|
|
const collection = database.collection("governorStatus");
|
|
|
|
const cursor = await collection.aggregate([
|
|
|
|
{
|
|
|
|
$match: {},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
_id: 1,
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
|
|
|
nodeName: "$parsedStatus.nodename",
|
|
|
|
"parsedStatus.chains": {
|
|
|
|
$filter: {
|
|
|
|
input: "$parsedStatus.chains",
|
|
|
|
as: "chain",
|
|
|
|
cond: { $eq: [`$$chain.chainid`, parseInt(id)] },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
_id: 1,
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
|
|
|
nodeName: 1,
|
|
|
|
availableNotional: { $arrayElemAt: ["$parsedStatus.chains", 0] },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
_id: 1,
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
|
|
|
nodeName: 1,
|
|
|
|
chainId: "$availableNotional.chainid",
|
|
|
|
availableNotional: "$availableNotional.remainingavailablenotional",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
const result = await cursor.toArray();
|
|
|
|
if (result.length === 0) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
res.send(result);
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get("/api/maxAvailableNotional/:chainNum", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
const id = `${req.params.chainNum}`;
|
|
|
|
const database = mongoClient.db("wormhole");
|
|
|
|
const collection = database.collection("governorStatus");
|
|
|
|
const cursor = await collection.aggregate([
|
|
|
|
{
|
|
|
|
$match: {},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
_id: 1,
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
|
|
|
nodeName: "$parsedStatus.nodename",
|
|
|
|
"parsedStatus.chains": {
|
|
|
|
$filter: {
|
|
|
|
input: "$parsedStatus.chains",
|
|
|
|
as: "chain",
|
|
|
|
cond: { $eq: [`$$chain.chainid`, parseInt(id)] },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
_id: 1,
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
|
|
|
nodeName: 1,
|
|
|
|
availableNotional: { $arrayElemAt: ["$parsedStatus.chains", 0] },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
_id: 1,
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
|
|
|
nodeName: 1,
|
|
|
|
chainId: "$availableNotional.chainid",
|
|
|
|
availableNotional: "$availableNotional.remainingavailablenotional",
|
|
|
|
emitters: "$availableNotional.emitters",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
const result = await cursor.toArray();
|
|
|
|
const sortedResult = result.sort(function (b, a) {
|
|
|
|
return parseInt(a.availableNotional) - parseInt(b.availableNotional);
|
|
|
|
});
|
|
|
|
if (sortedResult.length === 0) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const minGuardianNum = 13;
|
|
|
|
res.send(sortedResult[minGuardianNum - 1]);
|
|
|
|
});
|
|
|
|
|
2022-11-08 06:53:12 -08:00
|
|
|
app.get("/api/enqueuedVaas", async (req, res) => {
|
2022-10-28 08:24:25 -07:00
|
|
|
const id = `${req.params.chainNum}`;
|
|
|
|
const database = mongoClient.db("wormhole");
|
|
|
|
const collection = database.collection("governorStatus");
|
|
|
|
const cursor = await collection.aggregate([
|
|
|
|
{
|
|
|
|
$match: {},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
chains: "$parsedStatus.chains",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$unwind: "$chains",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
_id: 1,
|
|
|
|
chainId: "$chains.chainid",
|
|
|
|
emitters: "$chains.emitters",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$group: {
|
|
|
|
_id: "$chainId",
|
|
|
|
emitters: {
|
|
|
|
$push: {
|
|
|
|
emitterAddress: { $arrayElemAt: ["$emitters.emitteraddress", 0] },
|
|
|
|
enqueuedVaas: { $arrayElemAt: ["$emitters.enqueuedvaas", 0] },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
const result = await cursor.toArray();
|
|
|
|
var filteredResult = [];
|
|
|
|
var keys = [];
|
|
|
|
result.forEach((res) => {
|
|
|
|
const chainId = res._id;
|
|
|
|
const emitters = res.emitters;
|
|
|
|
emitters.forEach((emitter) => {
|
|
|
|
const emitterAddress = emitter.emitterAddress;
|
|
|
|
const enqueuedVaas = emitter.enqueuedVaas;
|
|
|
|
if (enqueuedVaas != null) {
|
|
|
|
enqueuedVaas.forEach((vaa) => {
|
|
|
|
//add to dictionary
|
|
|
|
const key = `${emitterAddress}/${vaa.sequence}/${vaa.txhash}`;
|
|
|
|
if (!keys.includes(key)) {
|
|
|
|
filteredResult.push({
|
|
|
|
chainId: chainId,
|
|
|
|
emitterAddress: emitterAddress,
|
|
|
|
sequence: vaa.sequence,
|
|
|
|
notionalValue: vaa.notionalvalue,
|
|
|
|
txHash: vaa.txhash,
|
|
|
|
});
|
|
|
|
keys.push(key);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
if (filteredResult.length === 0) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const groups = filteredResult.reduce((groups, item) => {
|
|
|
|
const group = groups[item.chainId] || [];
|
|
|
|
group.push(item);
|
|
|
|
groups[item.chainId] = group;
|
|
|
|
return groups;
|
|
|
|
}, {});
|
|
|
|
const modifiedResult = [];
|
|
|
|
for (const [key, value] of Object.entries(groups)) {
|
|
|
|
modifiedResult.push({ chainId: key, enqueuedVaas: value });
|
|
|
|
}
|
|
|
|
res.send(modifiedResult);
|
|
|
|
});
|
|
|
|
|
2022-11-08 06:53:12 -08:00
|
|
|
app.get("/api/enqueuedVaas/:chainNum", async (req, res) => {
|
|
|
|
// returns unique enqueued vaas for chainNum
|
2022-10-28 08:24:25 -07:00
|
|
|
const id = `${req.params.chainNum}`;
|
|
|
|
const database = mongoClient.db("wormhole");
|
|
|
|
const collection = database.collection("governorStatus");
|
|
|
|
const cursor = await collection.aggregate([
|
|
|
|
{
|
|
|
|
$match: {},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
_id: 1,
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
|
|
|
nodeName: "$parsedStatus.nodename",
|
|
|
|
"parsedStatus.chains": {
|
|
|
|
$filter: {
|
|
|
|
input: "$parsedStatus.chains",
|
|
|
|
as: "chain",
|
|
|
|
cond: { $eq: [`$$chain.chainid`, parseInt(id)] },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$project: {
|
|
|
|
_id: 1,
|
|
|
|
createdAt: 1,
|
|
|
|
updatedAt: 1,
|
|
|
|
nodeName: 1,
|
|
|
|
emitters: "$parsedStatus.chains.emitters",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$unwind: "$emitters",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
$group: {
|
|
|
|
_id: { $arrayElemAt: ["$emitters.emitteraddress", 0] },
|
|
|
|
enqueuedVaas: {
|
|
|
|
$push: {
|
|
|
|
enqueuedVaa: "$emitters.enqueuedvaas",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
const result = await cursor.toArray();
|
|
|
|
var filteredResult = [];
|
|
|
|
var keys = [];
|
|
|
|
result.forEach((res) => {
|
|
|
|
const emitterAddress = res._id;
|
|
|
|
const enqueuedVaas = res.enqueuedVaas;
|
|
|
|
enqueuedVaas.forEach((vaa) => {
|
|
|
|
const enqueuedVaa = vaa.enqueuedVaa;
|
|
|
|
enqueuedVaa.forEach((eV) => {
|
|
|
|
if (eV != null) {
|
|
|
|
eV.forEach((ev) => {
|
|
|
|
if (ev != null) {
|
|
|
|
//add to dictionary
|
|
|
|
const key = `${emitterAddress}/${ev.sequence}/${ev.txhash}`;
|
|
|
|
if (!keys.includes(key)) {
|
|
|
|
filteredResult.push({
|
|
|
|
chainId: id,
|
|
|
|
emitterAddress: emitterAddress,
|
|
|
|
sequence: ev.sequence,
|
|
|
|
notionalValue: ev.notionalvalue,
|
|
|
|
txHash: ev.txhash,
|
|
|
|
releaseTime: ev.releasetime,
|
|
|
|
});
|
|
|
|
|
|
|
|
keys.push(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
if (filteredResult.length === 0) {
|
|
|
|
res.sendStatus(404);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const sortedResult = filteredResult.sort(function (a, b) {
|
|
|
|
return parseInt(a.sequence) - parseInt(b.sequence);
|
|
|
|
});
|
|
|
|
res.send(sortedResult);
|
|
|
|
});
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Custody
|
|
|
|
*/
|
|
|
|
|
|
|
|
app.get("/api/custody", async (req, res) => {
|
|
|
|
await findAndSendMany("onchain_data", res, "custody", req);
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get("/api/custody/:chain/:emitter", async (req, res) => {
|
|
|
|
const id = `${req.params.chain}/${req.params.emitter}`;
|
|
|
|
await findAndSendOne(
|
|
|
|
"onchain_data",
|
|
|
|
res,
|
|
|
|
"custody",
|
|
|
|
{
|
|
|
|
_id: id,
|
|
|
|
},
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get("/api/custody/tokens", async (req, res) => {
|
|
|
|
await findAndSendMany("onchain_data", res, "custody", req, {}, { tokens: 1 });
|
|
|
|
});
|
|
|
|
|
|
|
|
app.get("/api/custody/tokens/:chain/:emitter", async (req, res) => {
|
|
|
|
const id = `${req.params.chain}/${req.params.emitter}`;
|
|
|
|
await findAndSendOne(
|
|
|
|
"onchain_data",
|
|
|
|
res,
|
|
|
|
"custody",
|
|
|
|
{
|
|
|
|
_id: id,
|
|
|
|
},
|
|
|
|
{ projection: { tokens: 1 } }
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-09-08 13:39:12 -07:00
|
|
|
app.listen(port, () => {
|
|
|
|
console.log(`Example app listening on port ${port}`);
|
|
|
|
});
|