Feature/remove legacy web and server project (#477)

* remove legacy web project

* remove legacy server project
This commit is contained in:
walker-16 2023-06-29 17:12:45 -03:00 committed by GitHub
parent 781d7ca8d7
commit 9fa7541238
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
104 changed files with 0 additions and 49438 deletions

1
server/.gitignore vendored
View File

@ -1 +0,0 @@
node_modules

View File

@ -1,11 +0,0 @@
FROM node:16-alpine@sha256:004dbac84fed48e20f9888a23e32fa7cf83c2995e174a78d41d9a9dd1e051a20
RUN mkdir -p /app
WORKDIR /app
COPY package.json package-lock.json /app/
RUN --mount=type=cache,uid=1000,gid=1000,target=/home/node/.npm \
npm ci
COPY . .
ENTRYPOINT node src/index.js

1375
server/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +0,0 @@
{
"dependencies": {
"express": "^4.18.1",
"mongodb": "4.9"
}
}

View File

@ -1,850 +0,0 @@
const express = require("express");
const app = express();
const port = 4000;
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);
/*
* Helpers
*/
async function paginatedFind(collection, req, filter) {
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
? { ...filter, indexedAt: { $lt: new Date(req.query.before) } }
: filter;
const cursor = await collection.find(query, {
sort: { indexedAt: -1 },
skip,
limit,
});
return cursor;
}
async function findAndSendMany(
db,
res,
collectionName,
reqForPagination,
filter,
project
) {
const database = mongoClient.db(db);
const collection = database.collection(collectionName);
const cursor = await (reqForPagination
? paginatedFind(collection, reqForPagination, filter)
: collection.find(filter).project(project));
const result = await cursor.toArray();
if (result.length === 0) {
res.sendStatus(404);
return;
}
res.send(result);
}
async function findAndSendOne(db, res, collectionName, filter, project) {
const database = mongoClient.db(db);
const collection = database.collection(collectionName);
const result = await collection.findOne(filter, project);
if (!result) {
res.sendStatus(404);
return;
}
res.send(result);
}
/*
* Heartbeats
*/
app.get("/api/heartbeats", async (req, res) => {
await findAndSendMany("wormhole", res, "heartbeats");
});
/*
* VAAs
*/
app.get("/api/vaas", async (req, res) => {
await findAndSendMany("wormhole", res, "vaas", req);
});
app.get("/api/vaas/:chain", async (req, res) => {
await findAndSendMany("wormhole", res, "vaas", req, {
_id: { $regex: `^${req.params.chain}/.*` },
});
});
app.get("/api/vaas/:chain/:emitter", async (req, res) => {
await findAndSendMany("wormhole", res, "vaas", req, {
_id: { $regex: `^${req.params.chain}/${req.params.emitter}/.*` },
});
});
app.get("/api/vaas/:chain/:emitter/:sequence", async (req, res) => {
const id = `${req.params.chain}/${req.params.emitter}/${req.params.sequence}`;
await findAndSendOne("wormhole", res, "vaas", { _id: id });
});
app.get("/api/vaas-sans-pythnet", async (req, res) => {
await findAndSendMany("wormhole", res, "vaas", req, {
_id: { $not: { $regex: `^26/.*` } },
});
});
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);
});
/*
* Observations
*/
app.get("/api/observations", async (req, res) => {
await findAndSendMany("wormhole", res, "observations", req);
});
app.get("/api/observations/:chain", async (req, res) => {
await findAndSendMany("wormhole", res, "observations", req, {
_id: { $regex: `^${req.params.chain}/.*` },
});
});
app.get("/api/observations/:chain/:emitter", async (req, res) => {
await findAndSendMany("wormhole", res, "observations", req, {
_id: { $regex: `^${req.params.chain}/${req.params.emitter}/.*` },
});
});
app.get("/api/observations/:chain/:emitter/:sequence", async (req, res) => {
await findAndSendMany("wormhole", res, "observations", req, {
_id: {
$regex: `^${req.params.chain}/${req.params.emitter}/${req.params.sequence}/.*`,
},
});
});
app.get(
"/api/observations/:chain/:emitter/:sequence/:signer/:hash",
async (req, res) => {
const id = `${req.params.chain}/${req.params.emitter}/${req.params.sequence}/${req.params.signer}/${req.params.hash}`;
await findAndSendOne("wormhole", res, "observations", { _id: id });
}
);
/*
* GovernorConfig
*/
app.get("/api/governorConfig", async (req, res) => {
const database = mongoClient.db("wormhole");
const collection = database.collection("governorConfig");
const cursor = await collection.find({}).project({
createdAt: 1,
updatedAt: 1,
nodename: "$parsedConfig.nodename", //<-- rename fields to flatten
counter: "$parsedConfig.counter",
chains: "$parsedConfig.chains",
tokens: "$parsedConfig.tokens",
});
const result = await cursor.toArray();
if (result.length === 0) {
res.sendStatus(404);
return;
}
res.send(result);
});
app.get("/api/governorConfig/:guardianaddr", async (req, res) => {
const id = `${req.params.guardianaddr}`;
await findAndSendOne(
"wormhole",
res,
"governorConfig",
{
_id: id,
},
{
projection: {
createdAt: 1,
updatedAt: 1,
nodename: "$parsedConfig.nodename", //<-- rename fields to flatten
counter: "$parsedConfig.counter",
chains: "$parsedConfig.chains",
tokens: "$parsedConfig.tokens",
},
}
);
});
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) => {
const id = `${req.params.chainNum}`;
const database = mongoClient.db("wormhole");
const collection = database.collection("governorConfig");
const cursor = await collection.aggregate([
{
$match: {},
},
{
$project: {
_id: 1,
createdAt: 1,
updatedAt: 1,
nodeName: "$parsedConfig.nodename",
"parsedConfig.chains": {
$filter: {
input: "$parsedConfig.chains",
as: "chain",
cond: { $eq: [`$$chain.chainid`, parseInt(id)] },
},
},
},
},
{
$project: {
_id: 1,
createdAt: 1,
updatedAt: 1,
nodeName: 1,
notionalLimits: { $arrayElemAt: ["$parsedConfig.chains", 0] },
},
},
{
$project: {
_id: 1,
createdAt: 1,
updatedAt: 1,
nodeName: 1,
chainId: "$notionalLimits.chainid",
notionalLimit: "$notionalLimits.notionallimit",
maxTransactionSize: "$notionalLimits.bigtransactionsize",
},
},
]);
const result = await cursor.toArray();
if (result.length === 0) {
res.sendStatus(404);
return;
}
res.send(result);
});
/*
* 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) => {
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);
})
);
});
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) => {
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]);
});
app.get("/api/enqueuedVaas", 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: {
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);
});
app.get("/api/enqueuedVaas/:chainNum", async (req, res) => {
// returns unique enqueued vaas for chainNum
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 } }
);
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});

24
web/.gitignore vendored
View File

@ -1,24 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -1,10 +0,0 @@
FROM node:16-alpine@sha256:f21f35732964a96306a84a8c4b5a829f6d3a0c5163237ff4b6b8b34f8d70064b
RUN mkdir -p /app
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,uid=1000,gid=1000,target=/home/node/.npm \
npm ci
COPY . .
RUN npm run build

View File

@ -1,77 +0,0 @@
const { ProvidePlugin } = require("webpack");
module.exports = function override(config, env) {
return {
...config,
module: {
...config.module,
rules: [
...config.module.rules,
{
test: /\.js$/,
enforce: "pre",
use: ["source-map-loader"],
},
{
test: /\.wasm$/,
type: "webassembly/async",
},
{
test: /\.m?js$/,
type: "javascript/auto",
},
{
test: /\.m?js$/,
resolve: {
fullySpecified: false,
},
},
],
},
plugins: [
...config.plugins,
new ProvidePlugin({
Buffer: ["buffer", "Buffer"],
process: "process/browser",
}),
],
resolve: {
...config.resolve,
fallback: {
assert: "assert",
buffer: "buffer",
console: "console-browserify",
constants: "constants-browserify",
crypto: "crypto-browserify",
domain: "domain-browser",
events: "events",
fs: false,
http: "stream-http",
https: "https-browserify",
os: "os-browserify/browser",
path: "path-browserify",
punycode: "punycode",
process: "process/browser",
querystring: "querystring-es3",
stream: "stream-browserify",
_stream_duplex: "readable-stream/duplex",
_stream_passthrough: "readable-stream/passthrough",
_stream_readable: "readable-stream/readable",
_stream_transform: "readable-stream/transform",
_stream_writable: "readable-stream/writable",
string_decoder: "string_decoder",
sys: "util",
timers: "timers-browserify",
tty: "tty-browserify",
url: "url",
util: "util",
vm: "vm-browserify",
zlib: "browserify-zlib",
},
},
experiments: {
asyncWebAssembly: true,
},
ignoreWarnings: [/Failed to parse source map/],
};
};

41610
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,91 +0,0 @@
{
"name": "wormhole-explorer",
"version": "0.1.0",
"private": true,
"license": "Apache-2.0",
"proxy": "http://server:4000",
"dependencies": {
"@certusone/wormhole-sdk": "^0.9.2",
"@certusone/wormhole-sdk-proto-web": "^0.0.4",
"@emotion/react": "^11.10.0",
"@emotion/styled": "^11.10.0",
"@metaplex/js": "^4.12.0",
"@mui/icons-material": "^5.8.4",
"@mui/material": "^5.9.3",
"@tanstack/react-table": "^8.5.11",
"@terra-money/terra.js": "^3.1.3",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^28.1.6",
"@types/node": "^18.6.4",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"@types/react-router-dom": "^5.3.3",
"assert": "^2.0.0",
"axios": "^1.1.3",
"binary-parser": "^2.2.1",
"coingecko-api": "^1.0.10",
"dotenv": "^16.0.2",
"elliptic": "^6.5.4",
"ethers": "^5.6.9",
"long": "^5.2.0",
"numeral": "^2.0.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^3.1.4",
"react-router-dom": "^5.3.3",
"react-scripts": "5.0.1",
"serve": "^14.0.1",
"stream-http": "^3.2.0",
"typescript": "^4.7.4",
"url": "^0.11.0",
"web-vitals": "^2.1.4",
"web3": "^1.7.5"
},
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/elliptic": "^6.4.14",
"@types/numeral": "^2.0.2",
"browserify-zlib": "^0.2.0",
"buffer": "^6.0.3",
"console-browserify": "^1.2.0",
"constants-browserify": "^1.0.0",
"crypto-browserify": "^3.12.0",
"https-browserify": "^1.0.0",
"os-browserify": "^0.3.0",
"path-browserify": "^1.0.1",
"prettier": "^2.3.2",
"process": "^0.11.10",
"react-app-rewired": "^2.2.1",
"readable-stream": "^3.6.0",
"stream-browserify": "^3.0.0",
"timers-browserify": "^2.0.12",
"tty-browserify": "^0.0.1",
"util": "^0.12.4",
"vm-browserify": "^1.1.2"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="https://certusone.github.io/wormhole-explorer/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,43 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="A dashboard for monitoring Wormhole" />
<meta name="theme-color" content="#ffffff" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="%PUBLIC_URL%/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="%PUBLIC_URL%/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="%PUBLIC_URL%/favicon-16x16.png"
/>
<link rel="manifest" href="%PUBLIC_URL%/site.webmanifest" />
<link
rel="mask-icon"
href="%PUBLIC_URL%/safari-pinned-tab.svg"
color="#5bbad5"
/>
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta
name="msapplication-config"
content="%PUBLIC_URL%/browserconfig.xml"
/>
<title>Wormscan</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -1,3 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@ -1,63 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2 5343 c0 -912 3 -1642 5 -1623 20 210 27 270 44 370 118 697 437
1318 944 1839 536 551 1184 896 1915 1020 100 17 160 24 370 44 19 2 -711 5
-1622 5 l-1658 2 2 -1657z"/>
<path d="M3720 6993 c19 -1 84 -8 145 -13 566 -55 1147 -271 1620 -603 304
-213 612 -514 842 -824 313 -421 533 -938 622 -1463 17 -100 33 -234 44 -375
2 -22 4 708 5 1623 l2 1662 -1657 -2 c-912 0 -1642 -3 -1623 -5z"/>
<path d="M3335 6734 c-367 -28 -650 -85 -942 -189 -280 -100 -658 -313 -893
-501 -121 -98 -326 -300 -435 -429 -810 -960 -1007 -2225 -523 -3360 129 -301
313 -597 530 -848 l68 -79 0 -89 0 -89 91 0 91 0 67 -58 c469 -412 1058 -679
1692 -767 179 -25 552 -30 739 -11 607 65 1148 277 1626 637 129 98 144 120
34 51 -355 -222 -731 -357 -1174 -419 -188 -27 -574 -24 -774 5 -458 67 -901
242 -1248 493 l-89 64 58 3 57 3 0 69 c0 39 2 70 5 70 3 0 30 -18 60 -41 82
-62 257 -165 390 -229 495 -239 1080 -322 1622 -230 149 26 353 79 480 126
111 41 329 143 422 197 l62 37 255 0 254 0 0 200 0 201 81 92 c86 97 173 209
228 292 l33 50 -42 -50 c-59 -69 -279 -285 -291 -285 -5 0 -9 41 -9 91 l0 91
89 94 c300 317 484 683 566 1129 26 138 32 488 11 640 -64 458 -265 878 -573
1196 l-93 96 0 91 c0 55 4 92 10 92 17 0 239 -220 307 -305 36 -44 95 -126
131 -181 37 -56 73 -110 80 -120 7 -11 -17 40 -52 111 -105 210 -259 433 -413
597 l-63 67 0 151 c0 82 3 150 7 150 4 0 55 -46 112 -102 110 -108 98 -87 -46
83 l-73 86 0 87 0 86 -86 0 -87 0 -86 73 c-507 431 -1110 693 -1766 767 -99
11 -421 20 -500 14z m999 -389 c225 -30 426 -80 641 -159 129 -48 396 -180
505 -250 l85 -55 -231 -1 -232 0 -77 36 c-183 83 -457 161 -710 201 -158 25
-593 25 -755 0 -501 -78 -902 -250 -1300 -558 -114 -88 -330 -302 -424 -419
-385 -483 -596 -1092 -596 -1722 l0 -88 -50 0 -50 0 0 -282 c0 -155 -2 -279
-5 -276 -9 8 -52 243 -67 358 -21 172 -16 564 11 737 97 633 375 1186 826
1639 469 472 1099 773 1765 843 58 6 121 13 140 15 71 8 418 -4 524 -19z m289
-652 l67 -6 0 -72 0 -71 -72 9 c-123 15 -477 2 -603 -22 -696 -133 -1250 -552
-1555 -1176 -142 -290 -220 -622 -220 -937 l0 -88 -75 0 -74 0 -7 36 c-17 91
21 410 72 612 143 562 520 1076 1016 1384 265 164 610 285 933 327 84 10 422
13 518 4z m1067 -2178 l0 -2185 -410 0 -410 0 0 2185 0 2185 410 0 410 0 0
-2185z m-1000 1491 l0 -43 -57 -7 c-326 -38 -665 -199 -915 -434 l-66 -63 -62
3 -61 3 66 72 c112 122 293 256 461 341 164 82 460 167 602 171 31 1 32 0 32
-43z m1259 -360 c284 -239 466 -547 541 -914 113 -556 -32 -1099 -408 -1530
-51 -58 -202 -192 -216 -192 -3 0 -6 26 -6 58 0 58 0 59 51 102 70 59 185 193
251 292 326 490 366 1094 106 1612 -91 182 -234 373 -357 476 -51 43 -51 44
-51 102 0 32 3 58 6 58 3 0 40 -29 83 -64z m-2029 -1836 l0 -1480 -410 0 -410
0 0 1480 0 1480 410 0 410 0 0 -1480z m-1790 -570 l0 -910 -410 0 -410 0 0
910 0 910 410 0 410 0 0 -910z m323 238 c99 -204 219 -377 375 -539 l93 -97
-3 -98 -3 -97 -109 104 c-182 174 -311 340 -437 567 l-59 107 0 215 0 215 44
-132 c25 -72 69 -182 99 -245z m1868 -641 c109 -37 216 -62 312 -73 l57 -7 0
-43 c0 -50 3 -49 -105 -34 -117 17 -270 58 -381 101 l-104 41 0 54 0 54 58
-29 c32 -16 105 -45 163 -64z m64 -572 c83 0 185 3 228 7 l77 8 0 -64 c0 -59
2 -65 23 -68 12 -2 -18 -8 -68 -14 -180 -18 -627 -5 -567 17 19 7 22 15 22 68
l0 60 68 -7 c37 -4 135 -7 217 -7z"/>
<path d="M6990 3268 c0 -193 -73 -574 -160 -843 -179 -547 -473 -1013 -906
-1435 -274 -266 -541 -458 -859 -617 -430 -216 -831 -324 -1350 -366 -22 -2
708 -4 1623 -5 l1662 -2 0 1660 c0 913 -2 1660 -5 1660 -3 0 -5 -24 -5 -52z"/>
<path d="M2 1658 l-2 -1658 1663 2 c914 1 1644 3 1622 5 -524 42 -931 153
-1365 373 -483 245 -932 634 -1272 1100 -296 408 -511 920 -597 1430 -17 100
-24 160 -44 370 -2 19 -5 -711 -5 -1622z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1,19 +0,0 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View File

@ -1,8 +0,0 @@
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@ -1,113 +0,0 @@
import { GitHub } from "@mui/icons-material";
import {
AppBar,
Box,
Button,
CssBaseline,
IconButton,
Toolbar,
Typography,
} from "@mui/material";
import { ErrorBoundary } from "react-error-boundary";
import {
HashRouter as Router,
Link,
Redirect,
Route,
Switch,
} from "react-router-dom";
import CustomThemeProvider from "./components/CustomThemeProvider";
import ErrorFallback from "./components/ErrorFallback";
import Governance from "./components/Governance";
import Guardians from "./components/Guardians";
import GovernorStatus from "./components/GovernorStatus";
import CustodyData from "./components/Custody";
import Home from "./components/Home";
import VAAs from "./components/VAAs";
import { NetworkContextProvider } from "./contexts/NetworkContext";
import { SettingsContextProvider } from "./contexts/SettingsContext";
import WormholeStatsIcon from "./icons/WormholeStatsIcon";
function App() {
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<SettingsContextProvider>
<CustomThemeProvider>
<CssBaseline />
<Router>
<NetworkContextProvider>
<AppBar position="static" color="transparent" elevation={0}>
<Toolbar variant="dense">
<Button
component={Link}
to="/"
color="inherit"
sx={{ textTransform: "none" }}
size="small"
>
<Box pr={1} display="flex" alignItems="center">
<WormholeStatsIcon />
</Box>
<Typography variant="h6">Wormscan</Typography>
</Button>
<Box px={2}>
<Button component={Link} to="/governance">
Governance
</Button>
<Button component={Link} to="/guardians">
Guardians
</Button>
<Button component={Link} to="/custody">
Custody
</Button>
<Button component={Link} to="/governorStatus">
Governor Status
</Button>
</Box>
<Box flexGrow={1} />
<Box>
<IconButton
sx={{ ml: 1 }}
href="https://github.com/certusone/wormhole-explorer"
target="_blank"
rel="noopener noreferrer"
color="inherit"
>
<GitHub />
</IconButton>
</Box>
</Toolbar>
</AppBar>
<Switch>
<Route exact path="/guardians">
<Guardians />
</Route>
<Route exact path="/custody">
<CustodyData />
</Route>
<Route exact path="/governorStatus">
<GovernorStatus />
</Route>
<Route exact path="/governance">
<Governance />
</Route>
<Route exact path="/VAAs/:chain">
<VAAs />
</Route>
<Route exact path="/">
<Home />
</Route>
<Route>
<Redirect to="/" />
</Route>
</Switch>
</NetworkContextProvider>
</Router>
</CustomThemeProvider>
</SettingsContextProvider>
</ErrorBoundary>
);
}
export default App;

View File

@ -1,265 +0,0 @@
import { GetLastHeartbeatsResponse_Entry } from "@certusone/wormhole-sdk-proto-web/lib/cjs/publicrpc/v1/publicrpc";
import {
ErrorOutline,
InfoOutlined,
WarningAmberOutlined,
} from "@mui/icons-material";
import {
Alert,
AlertColor,
Box,
Link,
List,
ListItem,
ListItemIcon,
ListItemText,
Tooltip,
Typography,
} from "@mui/material";
import { useMemo } from "react";
import { ChainIdToHeartbeats } from "../hooks/useChainHeartbeats";
import useLatestRelease from "../hooks/useLatestRelease";
import chainIdToName from "../utils/chainIdToName";
import CollapsibleSection from "./CollapsibleSection";
export const BEHIND_DIFF = 1000;
type AlertEntry = {
severity: AlertColor;
text: string;
};
const alertSeverityOrder: AlertColor[] = [
"error",
"warning",
"success",
"info",
];
function chainDownAlerts(
heartbeats: GetLastHeartbeatsResponse_Entry[],
chainIdsToHeartbeats: ChainIdToHeartbeats
): AlertEntry[] {
const downChains: { [chainId: string]: string[] } = {};
Object.entries(chainIdsToHeartbeats).forEach(([chainId, chainHeartbeats]) => {
// Search for known guardians without heartbeats
const missingGuardians = heartbeats.filter(
(guardianHeartbeat) =>
chainHeartbeats.findIndex(
(chainHeartbeat) =>
chainHeartbeat.guardian === guardianHeartbeat.p2pNodeAddr
) === -1
);
missingGuardians.forEach((guardianHeartbeat) => {
if (!downChains[chainId]) {
downChains[chainId] = [];
}
downChains[chainId].push(guardianHeartbeat.rawHeartbeat?.nodeName || "");
});
// Search for guardians with heartbeats but who are not picking up a height
// Could be disconnected or erroring post initial checks
// Track highest height to check for lagging guardians
let highest = BigInt(0);
chainHeartbeats.forEach((chainHeartbeat) => {
const height = BigInt(chainHeartbeat.network.height);
if (height > highest) {
highest = height;
}
if (chainHeartbeat.network.height === 0) {
if (!downChains[chainId]) {
downChains[chainId] = [];
}
downChains[chainId].push(chainHeartbeat.name);
}
});
// Search for guardians which are lagging significantly behind
chainHeartbeats.forEach((chainHeartbeat) => {
if (chainHeartbeat.network.height !== 0) {
const height = BigInt(chainHeartbeat.network.height);
const diff = highest - height;
if (diff > BEHIND_DIFF) {
if (!downChains[chainId]) {
downChains[chainId] = [];
}
downChains[chainId].push(chainHeartbeat.name);
}
}
});
});
return Object.entries(downChains).map(([chainId, names]) => ({
severity: names.length >= 7 ? "error" : "warning",
text: `${names.length} guardian${names.length > 1 ? "s" : ""} [${names.join(
", "
)}] ${names.length > 1 ? "are" : "is"} down on ${chainIdToName(
Number(chainId)
)} (${chainId})!`,
}));
}
const releaseChecker = (
release: string | null,
heartbeats: GetLastHeartbeatsResponse_Entry[]
): AlertEntry[] =>
release === null
? []
: heartbeats
.filter((heartbeat) => heartbeat.rawHeartbeat?.version !== release)
.map((heartbeat) => ({
severity: "info",
text: `${heartbeat.rawHeartbeat?.nodeName} is not running the latest release (${heartbeat.rawHeartbeat?.version} !== ${release})`,
}));
function Alerts({
heartbeats,
chainIdsToHeartbeats,
}: {
heartbeats: GetLastHeartbeatsResponse_Entry[];
chainIdsToHeartbeats: ChainIdToHeartbeats;
}) {
const latestRelease = useLatestRelease();
const alerts = useMemo(() => {
const alerts: AlertEntry[] = [
...chainDownAlerts(heartbeats, chainIdsToHeartbeats),
...releaseChecker(latestRelease, heartbeats),
];
return alerts.sort((a, b) =>
alertSeverityOrder.indexOf(a.severity) <
alertSeverityOrder.indexOf(b.severity)
? -1
: alertSeverityOrder.indexOf(a.severity) >
alertSeverityOrder.indexOf(b.severity)
? 1
: 0
);
}, [latestRelease, heartbeats, chainIdsToHeartbeats]);
const numErrors = useMemo(
() => alerts.filter((alert) => alert.severity === "error").length,
[alerts]
);
const numInfos = useMemo(
() => alerts.filter((alert) => alert.severity === "info").length,
[alerts]
);
const numSuccess = useMemo(
() => alerts.filter((alert) => alert.severity === "success").length,
[alerts]
);
const numWarnings = useMemo(
() => alerts.filter((alert) => alert.severity === "warning").length,
[alerts]
);
return (
<CollapsibleSection
header={
<Box
sx={{
display: "flex",
alignItems: "center",
paddingRight: 1,
}}
>
<Tooltip
title={
<>
<Typography variant="body1">
This section shows alerts for the following conditions:
</Typography>
<List dense>
<ListItem>
<ListItemIcon>
<ErrorOutline color="error" />
</ListItemIcon>
<ListItemText
primary="Chains with a quorum of guardians down"
secondary={`A guardian is considered down if it is
reporting a height of 0, more than ${BEHIND_DIFF} behind the highest height, or missing from the list of
heartbeats`}
/>
</ListItem>
<ListItem>
<ListItemIcon>
<WarningAmberOutlined color="warning" />
</ListItemIcon>
<ListItemText
primary="Chains with one or more guardians down"
secondary={`A guardian is considered down if it is
reporting a height of 0, more than ${BEHIND_DIFF} behind the highest height, or missing from the list of
heartbeats`}
/>
</ListItem>
<ListItem>
<ListItemIcon>
<InfoOutlined color="info" />
</ListItemIcon>
<ListItemText
primary="Guardians not running the latest release"
secondary={
<>
The guardian version is compared to the latest release
from{" "}
<Link
href="https://github.com/wormhole-foundation/wormhole/releases"
target="_blank"
rel="noopener noreferrer"
>
https://github.com/wormhole-foundation/wormhole/releases
</Link>
</>
}
/>
</ListItem>
</List>
</>
}
componentsProps={{ tooltip: { sx: { maxWidth: "100%" } } }}
>
<Box>
Alerts
<InfoOutlined sx={{ fontSize: ".8em", ml: 0.5 }} />
</Box>
</Tooltip>
<Box flexGrow={1} />
{numInfos > 0 ? (
<>
<InfoOutlined color="info" sx={{ ml: 2 }} />
<Typography variant="h6" component="strong" sx={{ ml: 0.5 }}>
{numInfos}
</Typography>
</>
) : null}
{numSuccess > 0 ? (
<>
<InfoOutlined color="success" sx={{ ml: 2 }} />
<Typography variant="h6" component="strong" sx={{ ml: 0.5 }}>
{numSuccess}
</Typography>
</>
) : null}
{numWarnings > 0 ? (
<>
<WarningAmberOutlined color="warning" sx={{ ml: 2 }} />
<Typography variant="h6" component="strong" sx={{ ml: 0.5 }}>
{numWarnings}
</Typography>
</>
) : null}
{numErrors > 0 ? (
<>
<ErrorOutline color="error" sx={{ ml: 2 }} />
<Typography variant="h6" component="strong" sx={{ ml: 0.5 }}>
{numErrors}
</Typography>
</>
) : null}
</Box>
}
>
{alerts.map((alert) => (
<Alert key={alert.text} severity={alert.severity}>
{alert.text}
</Alert>
))}
</CollapsibleSection>
);
}
export default Alerts;

View File

@ -1,128 +0,0 @@
import { Box, Button, CircularProgress, Grid, Typography } from "@mui/material";
import { useMemo } from "react";
import { Link } from "react-router-dom";
import {
ChainIdToHeartbeats,
HeartbeatInfo,
} from "../hooks/useChainHeartbeats";
import chainIdToIcon from "../utils/chainIdToIcon";
import chainIdToName from "../utils/chainIdToName";
import { EXPECTED_GUARDIAN_COUNT } from "../utils/consts";
import { BEHIND_DIFF } from "./Alerts";
function Chain({
chainId,
heartbeats,
}: {
chainId: string;
heartbeats: HeartbeatInfo[];
}) {
const highest = useMemo(() => {
let highest = BigInt(0);
heartbeats.forEach((heartbeat) => {
const height = BigInt(heartbeat.network.height);
if (height > highest) {
highest = height;
}
});
return highest;
}, [heartbeats]);
const upCount = useMemo(
() =>
heartbeats.reduce(
(total, heartbeat) =>
total +
(heartbeat.network.height === 0 ||
highest - BigInt(heartbeat.network.height) > BEHIND_DIFF
? 0
: 1),
0
),
[heartbeats, highest]
);
const percentUp = (upCount / EXPECTED_GUARDIAN_COUNT) * 100;
const icon = chainIdToIcon(Number(chainId));
const name = chainIdToName(Number(chainId));
return (
<Grid key={chainId} item xs={2}>
<Box px={1} textAlign="center">
<Button
component={Link}
to={`/VAAs/${chainId}`}
color="inherit"
size="small"
sx={{ textTransform: "none" }}
>
<Box py={1}>
<Box sx={{ position: "relative", display: "inline-flex" }}>
<CircularProgress
variant="determinate"
value={percentUp || 100}
color={
upCount > 15 ? "success" : upCount > 13 ? "warning" : "error"
}
/>
<Box
sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: "absolute",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
{icon ? (
<img
src={icon}
alt={name}
style={{ height: 22, maxWidth: 22 }}
/>
) : null}
</Box>
</Box>
<Box>
<Typography
variant="caption"
sx={{ position: "absolute", transform: "translate(-50%,-4px)" }}
>
{name}
</Typography>
</Box>
<Box mt={1} mb={-1}>
<Typography variant="caption">
{upCount}/{EXPECTED_GUARDIAN_COUNT}
</Typography>
</Box>
</Box>
</Button>
</Box>
</Grid>
);
}
function Chains({
chainIdsToHeartbeats,
}: {
chainIdsToHeartbeats: ChainIdToHeartbeats;
}) {
return (
<Box mt={1} sx={{ backgroundColor: "#ebeef9" }}>
<Box maxWidth={420} mx="auto" pb={1}>
<Grid container spacing={1}>
{Object.keys(chainIdsToHeartbeats).map((chainId) => (
<Chain
key={chainId}
chainId={chainId}
heartbeats={chainIdsToHeartbeats[Number(chainId)]}
/>
))}
</Grid>
</Box>
</Box>
);
}
export default Chains;

View File

@ -1,44 +0,0 @@
import { ExpandMore } from "@mui/icons-material";
import {
Accordion,
AccordionDetails,
AccordionSummary,
Typography,
} from "@mui/material";
import { ReactNode } from "react";
function CollapsibleSection({
header,
children,
}: {
header: ReactNode;
children: ReactNode;
}) {
return (
<Accordion
defaultExpanded
disableGutters
sx={{
background: "transparent",
my: 0.5,
"&.Mui-expanded:first-of-type": {
marginTop: 0.5,
},
"&:not(:last-child)": {
borderBottom: 0,
},
"&:before": {
display: "none",
},
}}
>
<AccordionSummary expandIcon={<ExpandMore />}>
<Typography variant="h5" sx={{ width: "100%" }}>
{header}
</Typography>
</AccordionSummary>
<AccordionDetails>{children}</AccordionDetails>
</Accordion>
);
}
export default CollapsibleSection;

View File

@ -1,127 +0,0 @@
import { ChevronRight } from "@mui/icons-material";
import { Box } from "@mui/system";
import { Card, IconButton } from "@mui/material";
import {
createColumnHelper,
getCoreRowModel,
getSortedRowModel,
SortingState,
useReactTable,
getExpandedRowModel,
Row,
} from "@tanstack/react-table";
import numeral from "numeral";
import { useState, ReactElement } from "react";
import useCustodyData, { CustodyDataResponse } from "../hooks/useCustodyData";
import Table from "./Table";
import TokenDetails from "./TokenDetails";
const columnHelper = createColumnHelper<CustodyDataResponse>();
const columns = [
columnHelper.display({
id: "_expand",
cell: ({ row }) =>
row.getCanExpand() ? (
<IconButton
size="small"
{...{
onClick: row.getToggleExpandedHandler(),
style: { cursor: "pointer" },
}}
>
<ChevronRight
sx={{
transition: ".2s",
transform: row.getIsExpanded() ? "rotate(90deg)" : undefined,
}}
/>
</IconButton>
) : null,
}),
columnHelper.accessor("chainId", {
header: () => "Chain Id",
sortingFn: `text`,
}),
columnHelper.accessor("chainName", {
header: () => "Chain Name",
}),
columnHelper.accessor("custodyUSD", {
header: () => "Total Value Locked (USD)",
cell: (info) => (
<Box textAlign="left">${numeral(info.getValue()).format("0,0.0000")}</Box>
),
}),
columnHelper.accessor("tokens", {
header: () => "Locked Tokens",
cell: (info) => {
const value = info.getValue();
return `${value.length} Token` + (value.length === 1 ? "" : `s`);
},
}),
columnHelper.accessor("updatedAt", {
header: () => "Last Updated",
cell: (info) =>
info.getValue() ? new Date(info.getValue()).toLocaleString() : null,
}),
];
/*
interface Token {
tokenAddress: string;
name: string;
decimals: number;
symbol: string;
balance: BigInt;
qty: number;
tokenPrice: number;
tokenBalanceUSD: number;
}
*/
function AddTokenDetails({
row,
}: {
row: Row<CustodyDataResponse>;
}): ReactElement {
const id = row.original._id;
return TokenDetails(id);
}
function CustodyData() {
const custody = useCustodyData();
const [sorting, setSorting] = useState<SortingState>([]);
const lockedValue = custody
.map((x) => x.custodyUSD)
.reduce((partialSum, a) => partialSum + a, 0);
const table = useReactTable({
columns,
data: custody,
state: {
sorting,
},
getRowId: (chain) => chain.chainName,
getRowCanExpand: () => true,
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,
});
return (
<Box m={2}>
<Card>
<Box m={2}>
Total Locked Value (USD): ${numeral(lockedValue).format("0,0.0000")}
</Box>
<Table<CustodyDataResponse>
table={table}
renderSubComponent={AddTokenDetails}
/>
</Card>
</Box>
);
}
export default CustodyData;

View File

@ -1,47 +0,0 @@
import { createTheme, responsiveFontSizes, ThemeProvider } from "@mui/material";
import { grey } from "@mui/material/colors";
import { ReactNode, useMemo } from "react";
function CustomThemeProvider({ children }: { children: ReactNode }) {
const mode = "light";
const theme = useMemo(
() =>
responsiveFontSizes(
createTheme({
palette: {
mode,
},
components: {
MuiCssBaseline: {
styleOverrides: {
body: {
overflowY: "scroll",
},
"*": {
scrollbarWidth: "thin",
scrollbarColor: `${grey[400]} rgb(255,255,255)`,
},
"*::-webkit-scrollbar": {
width: "8px",
height: "8px",
backgroundColor: "rgb(255,255,255)",
},
"*::-webkit-scrollbar-thumb": {
backgroundColor: grey[400],
borderRadius: "4px",
},
"*::-webkit-scrollbar-corner": {
// this hides an annoying white box which appears when both scrollbars are present
backgroundColor: "transparent",
},
},
},
},
})
),
[mode]
);
return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
}
export default CustomThemeProvider;

View File

@ -1,55 +0,0 @@
import { ChainId, getSignedVAA } from "@certusone/wormhole-sdk";
import { GovernorGetEnqueuedVAAsResponse_Entry } from "@certusone/wormhole-sdk-proto-web/lib/cjs/publicrpc/v1/publicrpc";
import { useEffect, useState } from "react";
import { useNetworkContext } from "../contexts/NetworkContext";
const VAA_CHECK_TIMEOUT = 60000;
function EnqueuedVAAChecker({
vaa: { emitterAddress, emitterChain, sequence },
}: {
vaa: GovernorGetEnqueuedVAAsResponse_Entry;
}) {
const {
currentNetwork: { endpoint },
} = useNetworkContext();
const [vaaHasQuorum, setVaaHasQuorum] = useState<boolean | null>(null);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
setVaaHasQuorum(null);
let result = false;
try {
const response = await getSignedVAA(
endpoint,
emitterChain as ChainId,
emitterAddress,
sequence
);
if (!!response.vaaBytes) result = true;
} catch (e) {}
if (!cancelled) {
setVaaHasQuorum(result);
if (result) {
cancelled = true;
return;
}
await new Promise((resolve) =>
setTimeout(resolve, VAA_CHECK_TIMEOUT)
);
}
}
})();
return () => {
cancelled = true;
};
}, [endpoint, emitterChain, emitterAddress, sequence]);
return (
<span role="img">
{vaaHasQuorum === null ? "⏳" : vaaHasQuorum ? "✅" : "❌"}
</span>
);
}
export default EnqueuedVAAChecker;

View File

@ -1,96 +0,0 @@
import { Box, Card } from "@mui/material";
import {
createColumnHelper,
getCoreRowModel,
getSortedRowModel,
SortingState,
useReactTable,
} from "@tanstack/react-table";
import { useState } from "react";
import useEnqueuedVaaDetails, {
EnqueuedVaaDetailsResponse,
} from "../hooks/useEnqueuedVaaDetails";
import Table from "./Table";
import numeral from "numeral";
import EnqueuedVaaExists from "./EnqueuedVaaExists";
const columnHelper = createColumnHelper<EnqueuedVaaDetailsResponse>();
const columns = [
columnHelper.accessor("chainId", {
header: () => "Chain Id",
cell: (info) => (
<Box component="pre" m={0}>
{info.getValue()}
</Box>
),
}),
columnHelper.accessor("emitterAddress", {
header: () => "Emitter Address",
cell: (info) => (
<Box component="pre" m={0}>
{info.getValue()}
</Box>
),
}),
columnHelper.accessor("sequence", {
header: () => "Sequence",
cell: (info) => (
<Box component="pre" m={0}>
{info.getValue()}
</Box>
),
}),
columnHelper.accessor("notionalValue", {
header: () => "Notional Value (USD)",
cell: (info) => (
<Box textAlign="left">${numeral(info.getValue()).format("0,0.0")}</Box>
),
}),
columnHelper.accessor("releaseTime", {
header: () => "Release Time",
cell: (info) =>
info.getValue()
? new Date(info.getValue() * 1000).toLocaleString()
: null,
}),
columnHelper.display({
id: "hasQuorum",
header: () => "Has Quorum?",
cell: (info) => EnqueuedVaaExists(info.row.original),
}),
columnHelper.accessor("txHash", {
header: () => "Transaction",
cell: (info) => (
<Box component="pre" m={0}>
{info.getValue()}
</Box>
),
}),
];
function EnqueuedVaaDetails(id: string) {
const enqueuedVaaDetails = useEnqueuedVaaDetails(id);
const [sorting, setSorting] = useState<SortingState>([]);
const table = useReactTable({
columns,
data: enqueuedVaaDetails,
state: {
sorting,
},
getRowId: (vaa) =>
`${vaa.chainId}/${vaa.emitterAddress.slice(2)}/${vaa.sequence}`,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,
});
return (
<Box m={2}>
<Card>
<Table<EnqueuedVaaDetailsResponse> table={table} />
</Card>
</Box>
);
}
export default EnqueuedVaaDetails;

View File

@ -1,46 +0,0 @@
import axios from "axios";
import { useEffect, useState } from "react";
import { EnqueuedVaaDetailsResponse } from "../hooks/useEnqueuedVaaDetails";
const VAA_CHECK_TIMEOUT = 60000;
function EnqueuedVaaExists(row: EnqueuedVaaDetailsResponse) {
const [vaaHasQuorum, setVaaHasQuorum] = useState<boolean | null>(null);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
setVaaHasQuorum(null);
let result = false;
try {
const response = await axios.get(
`/api/vaas/${row.chainId}/${row.emitterAddress.slice(2)}/${
row.sequence
}`
);
if (response.data) result = true;
} catch (e) {}
if (!cancelled) {
setVaaHasQuorum(result);
if (result) {
cancelled = true;
return;
}
await new Promise((resolve) =>
setTimeout(resolve, VAA_CHECK_TIMEOUT)
);
}
}
})();
return () => {
cancelled = true;
};
}, [row]);
return (
<span role="img">
{vaaHasQuorum === null ? "⏳" : vaaHasQuorum ? "✅" : "❌"}
</span>
);
}
export default EnqueuedVaaExists;

View File

@ -1,12 +0,0 @@
import { Typography } from "@mui/material";
function ErrorFallback({ error }: { error: { message: string } }) {
return (
<div role="alert">
<Typography>Something went wrong:</Typography>
<pre>{error.message}</pre>
</div>
);
}
export default ErrorFallback;

View File

@ -1,83 +0,0 @@
import { Box, Card } from "@mui/material";
import {
createColumnHelper,
getCoreRowModel,
getSortedRowModel,
SortingState,
useReactTable,
} from "@tanstack/react-table";
import numeral from "numeral";
import { useState } from "react";
import useGovernorStatusChainDetails, {
GovernorStatusChainDetailsResponse,
} from "../hooks/useGovernorStatusDetails";
import Table from "./Table";
const columnHelper = createColumnHelper<GovernorStatusChainDetailsResponse>();
const columns = [
columnHelper.accessor("_id", {
header: () => "Address",
cell: (info) => (
<Box component="pre" m={0}>
{info.getValue()}
</Box>
),
}),
columnHelper.accessor("createdAt", {
header: () => "Created",
cell: (info) => (info.getValue() ? info.getValue().toLocaleString() : null),
}),
columnHelper.accessor("updatedAt", {
header: () => "LastUpdated",
cell: (info) => (info.getValue() ? info.getValue().toLocaleString() : null),
}),
columnHelper.accessor("nodeName", {
header: () => "Guardian",
cell: (info) => (
<Box component="pre" m={0}>
{info.getValue()}
</Box>
),
}),
columnHelper.accessor("chainId", {
header: () => "Chain",
cell: (info) => (
<Box component="pre" m={0}>
{info.getValue()}
</Box>
),
}),
columnHelper.accessor("availableNotional", {
header: () => "Available Notional (USD)",
cell: (info) => (
<Box textAlign="left">${numeral(info.getValue()).format("0,0")}</Box>
),
}),
];
function GovStatusDetails(id: string) {
const govStatusDetails = useGovernorStatusChainDetails(id);
const [sorting, setSorting] = useState<SortingState>([]);
const table = useReactTable({
columns,
data: govStatusDetails,
state: {
sorting,
},
getRowId: (chain) => chain._id,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,
});
return (
<Box m={2}>
<Card>
<Table<GovernorStatusChainDetailsResponse> table={table} />
</Card>
</Box>
);
}
export default GovStatusDetails;

View File

@ -1,177 +0,0 @@
import { ChevronRight } from "@mui/icons-material";
import { Card, IconButton, Typography } from "@mui/material";
import { Box } from "@mui/system";
import {
createColumnHelper,
getCoreRowModel,
getExpandedRowModel,
Row,
useReactTable,
} from "@tanstack/react-table";
import { ReactElement, useMemo } from "react";
import useLatestObservations, {
ObservationsResponse,
} from "../hooks/useLatestObservations";
import useLatestVAAs, { VAAsResponse } from "../hooks/useLatestVAAs";
import * as vaa from "../utils/vaa";
import Table from "./Table";
const columnHelper = createColumnHelper<VAAsResponse>();
// TODO: parse once
const columns = [
columnHelper.display({
id: "_expand",
cell: ({ row }) =>
row.getCanExpand() ? (
<IconButton
size="small"
{...{
onClick: row.getToggleExpandedHandler(),
style: { cursor: "pointer" },
}}
>
<ChevronRight
sx={{
transition: ".2s",
transform: row.getIsExpanded() ? "rotate(90deg)" : undefined,
}}
/>
</IconButton>
) : null,
}),
columnHelper.accessor("_id", {
id: "sequence",
header: () => "Sequence",
cell: (info) => info.getValue().split("/")[2],
}),
columnHelper.accessor("vaas", {
id: "type",
header: () => "Type",
cell: (info) =>
vaa.parse(Buffer.from(info.getValue(), "base64")).payload.type,
}),
columnHelper.accessor("vaas", {
id: "chain",
header: () => "Chain",
cell: (info) =>
(vaa.parse(Buffer.from(info.getValue(), "base64")).payload as any)
.chain || "",
}),
columnHelper.accessor("vaas", {
id: "address",
header: () => "Address",
cell: (info) =>
(vaa.parse(Buffer.from(info.getValue(), "base64")).payload as any)
.address || "",
}),
columnHelper.accessor("vaas", {
id: "module",
header: () => "Module",
cell: (info) =>
(vaa.parse(Buffer.from(info.getValue(), "base64")).payload as any)
.module || "",
}),
columnHelper.accessor("updatedAt", {
header: () => "Observed At",
cell: (info) => new Date(info.getValue()).toLocaleString(),
}),
];
const obsColumnHelper = createColumnHelper<CollatedObservation>();
const obsColumns = [
obsColumnHelper.accessor("_id", {
header: () => "Sequence",
cell: (info) => info.getValue().split("/")[2],
}),
obsColumnHelper.accessor("count", {
header: () => "Count",
cell: (info) => info.getValue(),
}),
];
function VAADetails({ row }: { row: Row<VAAsResponse> }): ReactElement {
return (
<Typography variant="body2" sx={{ wordBreak: "break-all" }}>
{Buffer.from(row.original.vaas, "base64").toString("hex")}
</Typography>
);
}
type CollatedObservation = {
_id: string;
count: number;
observations: ObservationsResponse[];
};
function Governance() {
const vaas = useLatestVAAs(
"1/0000000000000000000000000000000000000000000000000000000000000004"
);
const obs = useLatestObservations(
"1/0000000000000000000000000000000000000000000000000000000000000004"
);
const collatedObservations: CollatedObservation[] = useMemo(
() =>
// NOTE: this ignores differing digests
Object.entries(
obs.reduce((obvsById, o) => {
if (!obvsById[o.messageId]) {
obvsById[o.messageId] = [];
}
obvsById[o.messageId].push(o);
return obvsById;
}, {} as any)
).map(([key, val]: [string, any]) => ({
_id: key,
count: val.length,
observations: val,
})),
[obs]
);
const table = useReactTable({
columns,
data: vaas,
getRowId: (vaa) => vaa._id,
getRowCanExpand: () => true,
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
enableSorting: false,
});
const obsTable = useReactTable({
columns: obsColumns,
data: collatedObservations,
getRowId: (o) => o._id,
getCoreRowModel: getCoreRowModel(),
enableSorting: false,
});
return (
<>
<Box m={2}>
<Card>
<Box m={2}>
<Typography variant="h5">Latest Governance VAAs</Typography>
<Table<VAAsResponse>
table={table}
renderSubComponent={VAADetails}
/>
</Box>
</Card>
</Box>
<Box m={2}>
<Card>
<Box m={2}>
<Typography variant="h5">Latest Governance Observations</Typography>
<Typography variant="caption">
Collated from the latest 100 governance observations
</Typography>
<Table<CollatedObservation> table={obsTable} />
</Box>
</Card>
</Box>
</>
);
}
export default Governance;

View File

@ -1,320 +0,0 @@
import {
GovernorGetAvailableNotionalByChainResponse_Entry,
GovernorGetEnqueuedVAAsResponse_Entry,
GovernorGetTokenListResponse_Entry,
} from "@certusone/wormhole-sdk-proto-web/lib/cjs/publicrpc/v1/publicrpc";
import { ExpandMore } from "@mui/icons-material";
import {
Accordion,
AccordionDetails,
AccordionSummary,
Box,
Card,
LinearProgress,
Link,
Tooltip,
Typography,
} from "@mui/material";
import {
createColumnHelper,
getCoreRowModel,
getSortedRowModel,
SortingState,
useReactTable,
} from "@tanstack/react-table";
import { useMemo, useState } from "react";
import useGovernorInfo from "../hooks/useGovernorInfo";
import chainIdToName from "../utils/chainIdToName";
import Table from "./Table";
import numeral from "numeral";
import EnqueuedVAAChecker from "./EnqueuedVAAChecker";
import { CHAIN_INFO_MAP } from "../utils/consts";
import {
ChainId,
CHAIN_ID_ALGORAND,
CHAIN_ID_NEAR,
CHAIN_ID_TERRA2,
isEVMChain,
tryHexToNativeAssetString,
tryHexToNativeString,
} from "@certusone/wormhole-sdk";
import useSymbolInfo from "../hooks/useSymbolInfo";
const calculatePercent = (
notional: GovernorGetAvailableNotionalByChainResponse_Entry
): number => {
try {
return (
((Number(notional.notionalLimit) -
Number(notional.remainingAvailableNotional)) /
Number(notional.notionalLimit)) *
100
);
} catch (e) {
return 0;
}
};
const notionalColumnHelper =
createColumnHelper<GovernorGetAvailableNotionalByChainResponse_Entry>();
const notionalColumns = [
notionalColumnHelper.accessor("chainId", {
header: () => "Chain",
cell: (info) => `${chainIdToName(info.getValue())} (${info.getValue()})`,
sortingFn: `text`,
}),
notionalColumnHelper.accessor("notionalLimit", {
header: () => <Box order="1">Limit</Box>,
cell: (info) => (
<Box textAlign="right">${numeral(info.getValue()).format("0,0")}</Box>
),
}),
notionalColumnHelper.accessor("bigTransactionSize", {
header: () => <Box order="1">Big Transaction</Box>,
cell: (info) => (
<Box textAlign="right">${numeral(info.getValue()).format("0,0")}</Box>
),
}),
notionalColumnHelper.accessor("remainingAvailableNotional", {
header: () => <Box order="1">Remaining</Box>,
cell: (info) => (
<Box textAlign="right">${numeral(info.getValue()).format("0,0")}</Box>
),
}),
notionalColumnHelper.accessor(calculatePercent, {
id: "progress",
header: () => "Progress",
cell: (info) => (
<Tooltip title={info.getValue()} arrow>
<LinearProgress
variant="determinate"
value={info.getValue()}
color={
info.getValue() > 80
? "error"
: info.getValue() > 50
? "warning"
: "success"
}
/>
</Tooltip>
),
}),
];
const enqueuedColumnHelper =
createColumnHelper<GovernorGetEnqueuedVAAsResponse_Entry>();
const enqueuedColumns = [
enqueuedColumnHelper.accessor("emitterChain", {
header: () => "Chain",
cell: (info) => `${chainIdToName(info.getValue())} (${info.getValue()})`,
sortingFn: `text`,
}),
enqueuedColumnHelper.accessor("emitterAddress", {
header: () => "Emitter",
}),
enqueuedColumnHelper.accessor("sequence", {
header: () => "Sequence",
cell: (info) => (
<Link
href={`https://wormhole-v2-mainnet-api.certus.one/v1/signed_vaa/${info.row.original.emitterChain}/${info.row.original.emitterAddress}/${info.row.original.sequence}`}
target="_blank"
rel="noopener noreferrer"
>
{info.getValue()}
</Link>
),
}),
enqueuedColumnHelper.display({
id: "hasQuorum",
header: () => "Has Quorum?",
cell: (info) => <EnqueuedVAAChecker vaa={info.row.original} />,
}),
enqueuedColumnHelper.accessor("txHash", {
header: () => "Transaction Hash",
cell: (info) => {
const chain = info.row.original.emitterChain;
const chainInfo = CHAIN_INFO_MAP[chain];
var txHash: string = "";
if (!isEVMChain(chainInfo.chainId)) {
txHash = tryHexToNativeString(
info.getValue().slice(2),
CHAIN_INFO_MAP[chain].chainId
);
} else {
txHash = info.getValue();
}
const explorerString = chainInfo.explorerStem;
const url = `${explorerString}/tx/${txHash}`;
return (
<Link href={url} target="_blank" rel="noopener noreferrer">
{txHash}
</Link>
);
},
}),
enqueuedColumnHelper.accessor("releaseTime", {
header: () => "Release Time",
cell: (info) => new Date(info.getValue() * 1000).toLocaleString(),
}),
enqueuedColumnHelper.accessor("notionalValue", {
header: () => <Box order="1">Notional Value</Box>,
cell: (info) => (
<Box textAlign="right">${numeral(info.getValue()).format("0,0")}</Box>
),
}),
];
interface GovernorGetTokenListResponse_Entry_ext
extends GovernorGetTokenListResponse_Entry {
symbol: string;
}
const tokenColumnHelper =
createColumnHelper<GovernorGetTokenListResponse_Entry_ext>();
const tokenColumns = [
tokenColumnHelper.accessor("originChainId", {
header: () => "Chain",
cell: (info) => `${chainIdToName(info.getValue())} (${info.getValue()})`,
sortingFn: `text`,
}),
tokenColumnHelper.accessor("originAddress", {
header: () => "Token",
cell: (info) => {
const chain = info.row.original.originChainId;
const chainInfo = CHAIN_INFO_MAP[chain];
const chainId: ChainId = chainInfo.chainId;
var tokenAddress: string = "";
if (
chainId === CHAIN_ID_ALGORAND ||
chainId === CHAIN_ID_NEAR ||
chainId === CHAIN_ID_TERRA2
) {
return info.getValue();
}
try {
tokenAddress = tryHexToNativeAssetString(
info.getValue().slice(2),
CHAIN_INFO_MAP[chain]?.chainId
);
} catch (e) {
console.log(e);
tokenAddress = info.getValue();
}
const explorerString = chainInfo?.explorerStem;
const url = `${explorerString}/address/${tokenAddress}`;
return (
<Link href={url} target="_blank" rel="noopener noreferrer">
{tokenAddress}
</Link>
);
},
}),
tokenColumnHelper.display({
id: "Symbol",
header: () => "Symbol",
cell: (info) => `${info.row.original?.symbol}`,
}),
tokenColumnHelper.accessor("price", {
header: () => <Box order="1">Price</Box>,
cell: (info) => (
<Box textAlign="right">
${numeral(info.getValue()).format("0,0.0000")}
</Box>
),
}),
];
function Governor() {
const governorInfo = useGovernorInfo();
const tokenSymbols = useSymbolInfo(governorInfo.tokens);
// TODO: governorInfo.tokens triggers updates to displayTokens, not tokenSymbols
// Should fix this
const displayTokens = useMemo(
() =>
governorInfo.tokens.map((tk) => ({
...tk,
symbol:
tokenSymbols.get([tk.originChainId, tk.originAddress].join("_"))
?.symbol || "",
})),
[governorInfo.tokens, tokenSymbols]
);
const [notionalSorting, setNotionalSorting] = useState<SortingState>([]);
const notionalTable = useReactTable({
columns: notionalColumns,
data: governorInfo.notionals,
state: {
sorting: notionalSorting,
},
getRowId: (notional) => notional.chainId.toString(),
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setNotionalSorting,
});
const [enqueuedSorting, setEnqueuedSorting] = useState<SortingState>([]);
const enqueuedTable = useReactTable({
columns: enqueuedColumns,
data: governorInfo.enqueued,
state: {
sorting: enqueuedSorting,
},
getRowId: (vaa) => JSON.stringify(vaa),
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setEnqueuedSorting,
});
const [tokenSorting, setTokenSorting] = useState<SortingState>([]);
const tokenTable = useReactTable({
columns: tokenColumns,
data: displayTokens,
state: {
sorting: tokenSorting,
},
getRowId: (token) => `${token.originChainId}_${token.originAddress}`,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setTokenSorting,
});
return (
<>
<Box mb={2}>
<Card>
<Table<GovernorGetAvailableNotionalByChainResponse_Entry>
table={notionalTable}
/>
</Card>
</Box>
<Box my={2}>
<Card>
<Table<GovernorGetEnqueuedVAAsResponse_Entry> table={enqueuedTable} />
{governorInfo.enqueued.length === 0 ? (
<Typography variant="body2" sx={{ py: 1, textAlign: "center" }}>
No enqueued VAAs
</Typography>
) : null}
</Card>
</Box>
<Box mt={2}>
<Card>
<Accordion>
<AccordionSummary expandIcon={<ExpandMore />}>
<Typography>Tokens ({governorInfo.tokens.length})</Typography>
</AccordionSummary>
<AccordionDetails>
<Table<GovernorGetTokenListResponse_Entry_ext>
table={tokenTable}
/>
</AccordionDetails>
</Accordion>
</Card>
</Box>
</>
);
}
export default Governor;

View File

@ -1,192 +0,0 @@
import { Box, IconButton, Card } from "@mui/material";
import { ChevronRight } from "@mui/icons-material";
import {
createColumnHelper,
getCoreRowModel,
getSortedRowModel,
SortingState,
useReactTable,
getExpandedRowModel,
Row,
} from "@tanstack/react-table";
import { useState, ReactElement } from "react";
import useGovernorStatus, {
GovernorStatusResponse,
} from "../hooks/useGovernorStatus";
import Table from "./Table";
import GovStatusDetails from "./GovStatusDetails";
import useEnqueuedVaas, {
EnqueuedVaasResponse,
} from "../hooks/useEnqueuedVaas";
import EnqueuedVaaDetails from "./EnqueuedVaaDetails";
import numeral from "numeral";
const columnHelper = createColumnHelper<GovernorStatusResponse>();
const columns = [
columnHelper.display({
id: "_expand",
cell: ({ row }) =>
row.getCanExpand() ? (
<IconButton
size="small"
{...{
onClick: row.getToggleExpandedHandler(),
style: { cursor: "pointer" },
}}
>
<ChevronRight
sx={{
transition: ".2s",
transform: row.getIsExpanded() ? "rotate(90deg)" : undefined,
}}
/>
</IconButton>
) : null,
}),
columnHelper.accessor("chainId", {
header: () => "ChainId",
cell: (info) => (
<Box component="pre" m={0}>
{info.getValue()}
</Box>
),
}),
columnHelper.accessor("availableNotional", {
header: () => "Available Notional (USD)",
cell: (info) => (
<Box textAlign="left">${numeral(info.getValue()).format("0,0")}</Box>
),
}),
columnHelper.accessor("notionalLimit", {
header: () => "Notional Limit (USD)",
cell: (info) => (
<Box textAlign="left">${numeral(info.getValue()).format("0,0")}</Box>
),
}),
columnHelper.accessor("maxTransactionSize", {
header: () => "Max Transaction Size (USD)",
cell: (info) => (
<Box textAlign="left">${numeral(info.getValue()).format("0,0")}</Box>
),
}),
];
const columnHelperVaa = createColumnHelper<EnqueuedVaasResponse>();
const columnsVaa = [
columnHelperVaa.display({
id: "_expand",
cell: ({ row }) =>
row.getCanExpand() ? (
<IconButton
size="small"
{...{
onClick: row.getToggleExpandedHandler(),
style: { cursor: "pointer" },
}}
>
<ChevronRight
sx={{
transition: ".2s",
transform: row.getIsExpanded() ? "rotate(90deg)" : undefined,
}}
/>
</IconButton>
) : null,
}),
columnHelperVaa.accessor("chainId", {
header: () => "ChainId",
cell: (info) => (
<Box component="pre" m={0}>
{info.getValue()}
</Box>
),
}),
columnHelperVaa.accessor("enqueuedVaas", {
header: () => "Enqueued VAAs",
cell: (info) => {
const value = info.getValue();
return `${value.length}`;
},
}),
];
function AddGovernorStatusDetails({
row,
}: {
row: Row<GovernorStatusResponse>;
}): ReactElement {
const id = row.original.chainId.toString();
return GovStatusDetails(id);
}
function AddEnqueuedVaas({
row,
}: {
row: Row<EnqueuedVaasResponse>;
}): ReactElement {
const id = row.original.chainId.toString();
return EnqueuedVaaDetails(id);
}
function GovernorStatus() {
const governorStatus = useGovernorStatus();
console.log(governorStatus);
const [sorting, setSorting] = useState<SortingState>([]);
const governorStatusTable = useReactTable({
columns,
data: governorStatus,
state: {
sorting,
},
getRowId: (governorStatus) => governorStatus.chainId.toString(),
getRowCanExpand: () => true,
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,
});
const enqueuedVaas = useEnqueuedVaas();
const enqueuedVAATable = useReactTable({
columns: columnsVaa,
data: enqueuedVaas,
state: {
sorting,
},
getRowId: (enqueuedVaa) => enqueuedVaa.chainId.toString(),
getRowCanExpand: () => true,
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,
});
return (
<Box m={2}>
<Box m={2}>
Available Notional By Chain:
<Card>
<Table<GovernorStatusResponse>
table={governorStatusTable}
renderSubComponent={AddGovernorStatusDetails}
/>
</Card>
</Box>
<Box m={2}>
Enqueued VAAs:
<Card>
<Table<EnqueuedVaasResponse>
table={enqueuedVAATable}
renderSubComponent={AddEnqueuedVaas}
/>
</Card>
</Box>
</Box>
);
}
export default GovernorStatus;

View File

@ -1,77 +0,0 @@
import { Box, Card } from "@mui/material";
import {
createColumnHelper,
getCoreRowModel,
getSortedRowModel,
SortingState,
useReactTable,
} from "@tanstack/react-table";
import { useState } from "react";
import useHeartbeats, { HeartbeatResponse } from "../hooks/useHeartbeats";
import longToDate from "../utils/longToDate";
import Table from "./Table";
const columnHelper = createColumnHelper<HeartbeatResponse>();
const columns = [
columnHelper.accessor("nodename", {
header: () => "Guardian",
sortingFn: `text`,
}),
columnHelper.accessor("version", {
header: () => "Version",
}),
columnHelper.accessor("features", {
header: () => "Features",
cell: (info) => {
const value = info.getValue();
return value && value.length > 0 ? value.join(", ") : "none";
},
}),
columnHelper.accessor("counter", {
header: () => "Counter",
}),
columnHelper.accessor("boottimestamp", {
header: () => "Boot",
cell: (info) =>
info.getValue() ? longToDate(info.getValue()).toLocaleString() : null,
}),
columnHelper.accessor("timestamp", {
header: () => "Timestamp",
cell: (info) =>
info.getValue() ? longToDate(info.getValue()).toLocaleString() : null,
}),
columnHelper.accessor("guardianaddr", {
header: () => "Address",
cell: (info) => (
<Box component="pre" m={0}>
{info.getValue()}
</Box>
),
}),
];
function Guardians() {
const heartbeats = useHeartbeats();
const [sorting, setSorting] = useState<SortingState>([]);
const table = useReactTable({
columns,
data: heartbeats,
state: {
sorting,
},
getRowId: (heartbeat) => heartbeat.guardianaddr,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,
});
return (
<Box m={2}>
<Card>
<Table<HeartbeatResponse> table={table} />
</Card>
</Box>
);
}
export default Guardians;

View File

@ -1,17 +0,0 @@
import { Box } from "@mui/material";
import useChainHeartbeats from "../hooks/useChainHeartbeats";
import useHeartbeats from "../hooks/useHeartbeats";
import Chains from "./Chains";
import LatestVAAs from "./LatestVAAs";
function Home() {
const heartbeats = useHeartbeats();
const chainIdsToHeartbeats = useChainHeartbeats(heartbeats);
return (
<Box>
<Chains chainIdsToHeartbeats={chainIdsToHeartbeats} />
<LatestVAAs />
</Box>
);
}
export default Home;

View File

@ -1,121 +0,0 @@
import { ChainId, tryHexToNativeString } from "@certusone/wormhole-sdk";
import { parseVaa } from "@certusone/wormhole-sdk/lib/cjs/vaa";
import { parseTransferPayload } from "@certusone/wormhole-sdk/lib/cjs/utils";
import { ChevronRight } from "@mui/icons-material";
import { Card, IconButton, Typography } from "@mui/material";
import { Box } from "@mui/system";
import {
createColumnHelper,
getCoreRowModel,
getExpandedRowModel,
Row,
useReactTable,
} from "@tanstack/react-table";
import { BigNumber } from "ethers";
import { ReactElement } from "react";
import useLatestNonPythNetVAAs from "../hooks/useLatestNonPythNetVAAs";
import { VAAsResponse } from "../hooks/useLatestVAAs";
import Table from "./Table";
const columnHelper = createColumnHelper<VAAsResponse>();
const columns = [
columnHelper.display({
id: "_expand",
cell: ({ row }) =>
row.getCanExpand() ? (
<IconButton
size="small"
{...{
onClick: row.getToggleExpandedHandler(),
style: { cursor: "pointer" },
}}
>
<ChevronRight
sx={{
transition: ".2s",
transform: row.getIsExpanded() ? "rotate(90deg)" : undefined,
}}
/>
</IconButton>
) : null,
}),
columnHelper.accessor("_id", {
id: "chain",
header: () => "Chain",
cell: (info) => info.getValue().split("/")[0],
}),
columnHelper.accessor("_id", {
id: "emitter",
header: () => "Emitter",
cell: (info) => info.getValue().split("/")[1],
}),
columnHelper.accessor("_id", {
id: "sequence",
header: () => "Sequence",
cell: (info) => info.getValue().split("/")[2],
}),
columnHelper.accessor("updatedAt", {
header: () => "Observed At",
cell: (info) => new Date(info.getValue()).toLocaleString(),
}),
];
function VAADetails({ row }: { row: Row<VAAsResponse> }): ReactElement {
const parsedVaa = parseVaa(
new Uint8Array(Buffer.from(row.original.vaas, "base64"))
);
const payload = parsedVaa.payload;
const parsedPayload = parseTransferPayload(payload);
let token = parsedPayload.originAddress;
// FromChain is a misnomer - actually OriginChain
if (parsedPayload.originAddress && parsedPayload.originChain)
try {
token = tryHexToNativeString(
parsedPayload.originAddress,
parsedPayload.originChain as ChainId
);
} catch (e) {}
return (
<>
Version: {parsedVaa.version}
<br />
Timestamp: {new Date(parsedVaa.timestamp * 1000).toLocaleString()}
<br />
Consistency: {parsedVaa.consistencyLevel}
<br />
Nonce: {parsedVaa.nonce}
<br />
Origin: {parsedPayload.originChain}
<br />
Token: {token}
<br />
Amount: {BigNumber.from(parsedPayload.amount).toString()}
<br />
</>
);
}
function LatestVAAs() {
const vaas = useLatestNonPythNetVAAs();
const table = useReactTable({
columns,
data: vaas,
getRowId: (vaa) => vaa._id,
getRowCanExpand: () => true,
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
enableSorting: false,
});
return (
<Box m={2}>
<Card>
<Box m={2}>
<Typography variant="h5">Latest Messages</Typography>
</Box>
<Table<VAAsResponse> table={table} renderSubComponent={VAADetails} />
</Card>
</Box>
);
}
export default LatestVAAs;

View File

@ -1,68 +0,0 @@
import { MenuItem, Select, SelectChangeEvent, useTheme } from "@mui/material";
import { useCallback } from "react";
import { networkOptions, useNetworkContext } from "../contexts/NetworkContext";
function NetworkSelector() {
const theme = useTheme();
const { currentNetwork, setCurrentNetwork } = useNetworkContext();
const handleChange = useCallback(
(e: SelectChangeEvent) => {
setCurrentNetwork(networkOptions[Number(e.target.value)]);
},
[setCurrentNetwork]
);
return (
<Select
onChange={handleChange}
value={(networkOptions.indexOf(currentNetwork) || 0).toString()}
margin="dense"
size="small"
sx={{
minWidth: 130,
// theme fixes
"& img": { filter: "invert(0)!important" },
"& .MuiOutlinedInput-notchedOutline": {
borderColor:
theme.palette.mode === "light" ? "rgba(255,255,255,.6)" : null,
},
"&:hover .MuiOutlinedInput-notchedOutline": {
borderColor:
theme.palette.mode === "light" ? "rgba(255,255,255,.8)" : null,
},
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor:
theme.palette.mode === "light" ? "rgba(255,255,255,.8)" : null,
},
"& .MuiSvgIcon-root": {
fill: theme.palette.mode === "light" ? "white" : null,
},
}}
SelectDisplayProps={{
style: { paddingTop: 4, paddingBottom: 4 },
}}
>
{networkOptions.map((network, idx) => (
<MenuItem key={network.endpoint} value={idx}>
{network.logo !== "" ? (
<img
src={network.logo}
alt={network.name}
style={{
height: 20,
maxHeight: 20,
verticalAlign: "middle",
// theme fixes
...(theme.palette.mode === "light"
? { filter: "invert(1)" }
: {}),
}}
/>
) : (
network.name
)}
</MenuItem>
))}
</Select>
);
}
export default NetworkSelector;

View File

@ -1,109 +0,0 @@
import {
ContrastOutlined,
DarkModeOutlined,
LightModeOutlined,
SettingsOutlined,
} from "@mui/icons-material";
import {
Box,
Dialog,
IconButton,
Slider,
TextField,
ToggleButton,
ToggleButtonGroup,
Typography,
} from "@mui/material";
import { useCallback, useState } from "react";
import { Theme, useSettingsContext } from "../contexts/SettingsContext";
function SettingsContent() {
const {
settings,
updateBackgroundOpacity,
updateBackgroundUrl,
updateTheme,
} = useSettingsContext();
const handleThemeChange = useCallback(
(event: any, newTheme: Theme) => {
updateTheme(newTheme);
},
[updateTheme]
);
const handleBackgroundOpacityChange = useCallback(
(event: any) => {
updateBackgroundOpacity(event.target.value);
},
[updateBackgroundOpacity]
);
const handleBackgroundUrlChange = useCallback(
(event: any) => {
updateBackgroundUrl(event.target.value);
},
[updateBackgroundUrl]
);
return (
<>
<Box mt={2} mx={2} textAlign="center">
<ToggleButtonGroup
value={settings.theme}
exclusive
onChange={handleThemeChange}
>
<ToggleButton value="light">
<LightModeOutlined />
</ToggleButton>
<ToggleButton value="dark">
<DarkModeOutlined />
</ToggleButton>
<ToggleButton value="auto">
<ContrastOutlined />
</ToggleButton>
</ToggleButtonGroup>
</Box>
<Box m={2}>
<TextField
value={settings.backgroundUrl || ""}
onChange={handleBackgroundUrlChange}
label="Background URL"
margin="dense"
fullWidth
/>
</Box>
<Box m={2}>
<Typography variant="body2">Background Opacity</Typography>
<Box pr={2} pt={2}>
<Slider
min={0.05}
max={1}
step={0.05}
value={settings.backgroundOpacity || 0.1}
onChange={handleBackgroundOpacityChange}
/>
</Box>
</Box>
</>
);
}
function Settings() {
const [open, setOpen] = useState(false);
const handleOpen = useCallback(() => {
setOpen(true);
}, []);
const handleClose = useCallback(() => {
setOpen(false);
}, []);
return (
<>
<IconButton color="inherit" onClick={handleOpen}>
<SettingsOutlined />
</IconButton>
<Dialog open={open} onClose={handleClose} maxWidth="xs" fullWidth>
<SettingsContent />
</Dialog>
</>
);
}
export default Settings;

View File

@ -1,118 +0,0 @@
import { ArrowDownward, ArrowUpward } from "@mui/icons-material";
import {
Box,
Collapse,
Divider,
SxProps,
Table as MuiTable,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Theme,
useTheme,
} from "@mui/material";
import { grey } from "@mui/material/colors";
import { flexRender, Row, Table as TanTable } from "@tanstack/react-table";
import { Fragment, ReactElement } from "react";
import { ErrorBoundary } from "react-error-boundary";
import ErrorFallback from "./ErrorFallback";
function Table<T>({
table,
conditionalRowStyle,
renderSubComponent: SubComponent,
}: {
table: TanTable<T>;
conditionalRowStyle?: (a: T) => SxProps<Theme> | undefined;
renderSubComponent?: ({ row }: { row: Row<T> }) => ReactElement;
}) {
const theme = useTheme();
return (
<TableContainer>
<MuiTable size="small">
<TableHead>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableCell
key={header.id}
sx={
header.column.getCanSort()
? {
cursor: "pointer",
userSelect: "select-none",
":hover": {
background:
theme.palette.mode === "dark"
? grey[800]
: grey[100],
},
}
: {}
}
onClick={header.column.getToggleSortingHandler()}
>
<Box display="flex" alignContent="center">
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
<Box flexGrow={1} />
<Box display="flex" alignItems="center">
{{
asc: <ArrowUpward fontSize="small" sx={{ ml: 0.5 }} />,
desc: (
<ArrowDownward fontSize="small" sx={{ ml: 0.5 }} />
),
}[header.column.getIsSorted() as string] ?? null}
</Box>
</Box>
</TableCell>
))}
</TableRow>
))}
</TableHead>
<TableBody>
{table.getRowModel().rows.map((row) => (
<Fragment key={row.id}>
<TableRow
sx={
conditionalRowStyle ? conditionalRowStyle(row.original) : {}
}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
{SubComponent && (
<TableRow>
{/* 2nd row is a custom 1 cell row */}
<TableCell
colSpan={row.getVisibleCells().length}
sx={{ p: 0, borderBottom: 0 }}
>
<Collapse in={row.getIsExpanded()}>
<Box p={1.5}>
<ErrorBoundary FallbackComponent={ErrorFallback}>
<SubComponent row={row} />
</ErrorBoundary>
</Box>
<Divider />
</Collapse>
</TableCell>
</TableRow>
)}
</Fragment>
))}
</TableBody>
</MuiTable>
</TableContainer>
);
}
export default Table;

View File

@ -1,84 +0,0 @@
import { Box, Card } from "@mui/material";
import {
createColumnHelper,
getCoreRowModel,
getSortedRowModel,
SortingState,
useReactTable,
} from "@tanstack/react-table";
import numeral from "numeral";
import { useState } from "react";
import useTokenDetails, {
TokenDetailsResponse,
} from "../hooks/useTokenDetails";
import Table from "./Table";
/*
export type TokenDetailsResponse {
tokenAddress: string;
name: string;
decimals: number;
symbol: string;
balance: BigInt;
qty: number;
tokenPrice: number;
tokenBalanceUSD: number;
}
*/
const columnHelper = createColumnHelper<TokenDetailsResponse>();
const columns = [
columnHelper.accessor("tokenAddress", {
header: () => "Token Address",
sortingFn: `text`,
}),
columnHelper.accessor("name", {
header: () => "Name",
}),
columnHelper.accessor("symbol", {
header: () => "Symbol",
}),
columnHelper.accessor("qty", {
header: () => "Token Balance",
cell: (info) => (
<Box textAlign="left">{numeral(info.getValue()).format("0,0.0000")}</Box>
),
}),
columnHelper.accessor("tokenPrice", {
header: () => "Token Price",
cell: (info) => (
<Box textAlign="left">${numeral(info.getValue()).format("0,0.0000")}</Box>
),
}),
columnHelper.accessor("tokenBalanceUSD", {
header: () => "Locked Value (USD)",
cell: (info) => (
<Box textAlign="left">${numeral(info.getValue()).format("0,0.0000")}</Box>
),
}),
];
function TokenDetails(id: string) {
const tokenDetails = useTokenDetails(id);
const [sorting, setSorting] = useState<SortingState>([]);
const table = useReactTable({
columns,
data: tokenDetails,
state: {
sorting,
},
getRowId: (token) => token.tokenAddress,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
onSortingChange: setSorting,
});
return (
<Box m={2}>
<Card>
<Table<TokenDetailsResponse> table={table} />
</Card>
</Box>
);
}
export default TokenDetails;

View File

@ -1,140 +0,0 @@
import {
ChainId,
parseTransferPayload,
parseVaa,
tryHexToNativeString,
} from "@certusone/wormhole-sdk";
import { ChevronRight } from "@mui/icons-material";
import { Card, IconButton, Typography } from "@mui/material";
import { Box } from "@mui/system";
import {
createColumnHelper,
getCoreRowModel,
getExpandedRowModel,
Row,
useReactTable,
} from "@tanstack/react-table";
import { BigNumber } from "ethers";
import { ReactElement } from "react";
import { useParams } from "react-router-dom";
import useLatestVAAs, { VAAsResponse } from "../hooks/useLatestVAAs";
import chainIdToIcon from "../utils/chainIdToIcon";
import chainIdToName from "../utils/chainIdToName";
import Table from "./Table";
const columnHelper = createColumnHelper<VAAsResponse>();
const columns = [
columnHelper.display({
id: "_expand",
cell: ({ row }) =>
row.getCanExpand() ? (
<IconButton
size="small"
{...{
onClick: row.getToggleExpandedHandler(),
style: { cursor: "pointer" },
}}
>
<ChevronRight
sx={{
transition: ".2s",
transform: row.getIsExpanded() ? "rotate(90deg)" : undefined,
}}
/>
</IconButton>
) : null,
}),
columnHelper.accessor("_id", {
id: "chain",
header: () => "Chain",
cell: (info) => info.getValue().split("/")[0],
}),
columnHelper.accessor("_id", {
id: "emitter",
header: () => "Emitter",
cell: (info) => info.getValue().split("/")[1],
}),
columnHelper.accessor("_id", {
id: "sequence",
header: () => "Sequence",
cell: (info) => info.getValue().split("/")[2],
}),
columnHelper.accessor("updatedAt", {
header: () => "Observed At",
cell: (info) => new Date(info.getValue()).toLocaleString(),
}),
];
function VAADetails({ row }: { row: Row<VAAsResponse> }): ReactElement {
const parsedVaa = parseVaa(
new Uint8Array(Buffer.from(row.original.vaas, "base64"))
);
const payload = parsedVaa.payload;
const parsedPayload = parseTransferPayload(payload);
let token = parsedPayload.originAddress;
// FromChain is a misnomer - actually OriginChain
if (parsedPayload.originAddress && parsedPayload.originChain)
try {
token = tryHexToNativeString(
parsedPayload.originAddress,
parsedPayload.originChain as ChainId
);
} catch (e) {}
return (
<>
Version: {parsedVaa.version}
<br />
Timestamp: {new Date(parsedVaa.timestamp * 1000).toLocaleString()}
<br />
Consistency: {parsedVaa.consistencyLevel}
<br />
Nonce: {parsedVaa.nonce}
<br />
Origin: {parsedPayload.originChain}
<br />
Token: {token}
<br />
Amount: {BigNumber.from(parsedPayload.amount).toString()}
<br />
</>
);
}
function VAAs() {
const { chain } = useParams<{ chain: string }>();
const vaas = useLatestVAAs(chain);
const table = useReactTable({
columns,
data: vaas,
getRowId: (vaa) => vaa._id,
getRowCanExpand: () => true,
getCoreRowModel: getCoreRowModel(),
getExpandedRowModel: getExpandedRowModel(),
enableSorting: false,
});
const name = chainIdToName(Number(chain));
return (
<Box m={2}>
<Card>
<Box m={2}>
<Typography variant="h5" display="flex" alignItems="center">
<Box pr={1} display="flex" alignItems="center">
<img
src={chainIdToIcon(Number(chain))}
alt={name}
style={{
height: 22,
maxWidth: 22,
}}
/>
</Box>
Latest Messages from {name}
</Typography>
</Box>
<Table<VAAsResponse> table={table} renderSubComponent={VAADetails} />
</Card>
</Box>
);
}
export default VAAs;

File diff suppressed because one or more lines are too long

View File

@ -1,111 +0,0 @@
import React, {
ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from "react";
const STORAGE_KEY = "settings";
export type Theme = "light" | "dark" | "auto";
type Settings = {
backgroundUrl?: string;
backgroundOpacity?: number;
defaultEndpoint?: string;
theme: Theme;
};
type SettingsContextValue = {
settings: Settings;
updateBackgroundOpacity(value: number): void;
updateBackgroundUrl(value: string): void;
updateDefaultEndpoint(value: string): void;
updateTheme(value: Theme): void;
};
const isTheme = (arg: any): arg is Theme => {
return arg && (arg === "light" || arg === "dark" || arg === "auto");
};
const isSettings = (arg: any): arg is Settings => {
return arg && arg.theme && isTheme(arg.theme);
};
let localStorageSettings: Settings | null = null;
try {
const value = localStorage.getItem(STORAGE_KEY);
if (value) {
const parsedValue = JSON.parse(value);
if (isSettings(parsedValue)) {
localStorageSettings = parsedValue;
}
}
} catch (e) {}
const initialSettings: Settings = localStorageSettings || { theme: "auto" };
const saveSettings = (settings: Settings) => {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
} catch (e) {}
};
const SettingsContext = React.createContext<SettingsContextValue>({
settings: initialSettings,
updateBackgroundOpacity: (value: number) => {},
updateBackgroundUrl: (value: string) => {},
updateDefaultEndpoint: (value: string) => {},
updateTheme: (value: Theme) => {},
});
export const SettingsContextProvider = ({
children,
}: {
children: ReactNode;
}) => {
const [settings, setSettings] = useState<Settings>(initialSettings);
const updateBackgroundOpacity = useCallback((value: number) => {
setSettings((settings) => ({ ...settings, backgroundOpacity: value }));
}, []);
const updateBackgroundUrl = useCallback((value: string) => {
setSettings((settings) => ({ ...settings, backgroundUrl: value }));
}, []);
const updateDefaultEndpoint = useCallback((value: string) => {
setSettings((settings) => ({ ...settings, defaultEndpoint: value }));
}, []);
const updateTheme = useCallback((value: Theme) => {
setSettings((settings) => ({ ...settings, theme: value }));
}, []);
// sync settings to state
useEffect(() => {
saveSettings(settings);
}, [settings]);
const value = useMemo(
() => ({
settings,
updateBackgroundOpacity,
updateBackgroundUrl,
updateDefaultEndpoint,
updateTheme,
}),
[
settings,
updateBackgroundOpacity,
updateBackgroundUrl,
updateDefaultEndpoint,
updateTheme,
]
);
return (
<SettingsContext.Provider value={value}>
{children}
</SettingsContext.Provider>
);
};
export const useSettingsContext = () => {
return useContext(SettingsContext);
};

View File

@ -1,29 +0,0 @@
import { HeartbeatNetwork, HeartbeatResponse } from "./useHeartbeats";
export type HeartbeatInfo = {
guardian: string;
name: string;
network: HeartbeatNetwork;
};
export type ChainIdToHeartbeats = {
[chainId: number]: HeartbeatInfo[];
};
function useChainHeartbeats(heartbeats: HeartbeatResponse[]) {
const chainIdsToHeartbeats: ChainIdToHeartbeats = {};
heartbeats.forEach((heartbeat) => {
heartbeat.networks.forEach((network) => {
if (!chainIdsToHeartbeats[network.id]) {
chainIdsToHeartbeats[network.id] = [];
}
chainIdsToHeartbeats[network.id].push({
guardian: heartbeat.guardianaddr,
name: heartbeat.nodename || "",
network,
});
});
});
return chainIdsToHeartbeats;
}
export default useChainHeartbeats;

View File

@ -1,52 +0,0 @@
import axios from "axios";
import { useEffect, useState } from "react";
import { useNetworkContext } from "../contexts/NetworkContext";
import { POLL_TIME } from "../utils/consts";
export type Token = {
tokenAddress: string;
name: string;
decimals: number;
symbol: string;
balance: BigInt;
qty: number;
tokenPrice: number;
tokenBalanceUSD: number;
};
export type CustodyDataResponse = {
_id: string;
updatedAt: string;
chainId: number;
chainName: string;
custodyUSD: number;
emitterAddress: string;
tokens: Token[];
};
function useCustodyData(): CustodyDataResponse[] {
const { currentNetwork } = useNetworkContext();
const [custodyData, setCustodyData] = useState<CustodyDataResponse[]>([]);
useEffect(() => {
setCustodyData([]);
}, [currentNetwork]);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await axios.get<CustodyDataResponse[]>(`/api/custody`);
if (!cancelled) {
setCustodyData(
response.data.sort((a, b) => (a.chainId > b.chainId ? 1 : -1))
);
await new Promise((resolve) => setTimeout(resolve, POLL_TIME));
}
}
})();
return () => {
cancelled = true;
};
}, [currentNetwork]);
return custodyData;
}
export default useCustodyData;

View File

@ -1,42 +0,0 @@
import axios from "axios";
import { useEffect, useState } from "react";
import { useNetworkContext } from "../contexts/NetworkContext";
import { POLL_TIME } from "../utils/consts";
export type EnqueuedVaaDetailsResponse = {
chainId: string;
emitterAddress: string;
sequence: number;
notionalValue: number;
txHash: string;
releaseTime: number;
};
function useEnqueuedVaaDetails(id?: string): EnqueuedVaaDetailsResponse[] {
const { currentNetwork } = useNetworkContext();
const [enqueuedVaaDetails, setEnqueuedVaaDetails] = useState<
EnqueuedVaaDetailsResponse[]
>([]);
useEffect(() => {
setEnqueuedVaaDetails([]);
}, [currentNetwork]);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await axios.get<EnqueuedVaaDetailsResponse[]>(
`/api/enqueuedVaas${id ? `/${id}` : ""}`
);
if (!cancelled) {
setEnqueuedVaaDetails(response.data);
await new Promise((resolve) => setTimeout(resolve, POLL_TIME));
}
}
})();
return () => {
cancelled = true;
};
}, [currentNetwork, id]);
return enqueuedVaaDetails;
}
export default useEnqueuedVaaDetails;

View File

@ -1,44 +0,0 @@
import axios from "axios";
import { useEffect, useState } from "react";
import { useNetworkContext } from "../contexts/NetworkContext";
import { POLL_TIME } from "../utils/consts";
export type EnqueuedVaa = {
chainId: number;
emitterAddress: string;
sequence: number;
notionalValue: number;
txHash: string;
};
export type EnqueuedVaasResponse = {
chainId: string;
enqueuedVaas: EnqueuedVaa[];
};
function useEnqueuedVaas(id?: string): EnqueuedVaasResponse[] {
const { currentNetwork } = useNetworkContext();
const [enqueuedVaas, setEnqueuedVaas] = useState<EnqueuedVaasResponse[]>([]);
useEffect(() => {
setEnqueuedVaas([]);
}, [currentNetwork]);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await axios.get<EnqueuedVaasResponse[]>(
`/api/enqueuedVaas`
);
if (!cancelled) {
setEnqueuedVaas(response.data);
await new Promise((resolve) => setTimeout(resolve, POLL_TIME));
}
}
})();
return () => {
cancelled = true;
};
}, [currentNetwork, id]);
return enqueuedVaas;
}
export default useEnqueuedVaas;

View File

@ -1,84 +0,0 @@
import {
GovernorGetAvailableNotionalByChainResponse_Entry,
GovernorGetEnqueuedVAAsResponse_Entry,
GovernorGetTokenListResponse_Entry,
} from "@certusone/wormhole-sdk-proto-web/lib/cjs/publicrpc/v1/publicrpc";
import { useEffect, useState } from "react";
import { useNetworkContext } from "../contexts/NetworkContext";
import { getGovernorAvailableNotionalByChain } from "../utils/getGovernorAvailableNotionalByChain";
import { getGovernorEnqueuedVAAs } from "../utils/getGovernorEnqueuedVAAs";
import { getGovernorTokenList } from "../utils/getGovernorTokenList";
type GovernorInfo = {
notionals: GovernorGetAvailableNotionalByChainResponse_Entry[];
tokens: GovernorGetTokenListResponse_Entry[];
enqueued: GovernorGetEnqueuedVAAsResponse_Entry[];
};
const createEmptyInfo = (): GovernorInfo => ({
notionals: [],
tokens: [],
enqueued: [],
});
const TIMEOUT = 10 * 1000;
function useGovernorInfo(): GovernorInfo {
const { currentNetwork } = useNetworkContext();
const [governorInfo, setGovernorInfo] = useState<GovernorInfo>(
createEmptyInfo()
);
useEffect(() => {
setGovernorInfo(createEmptyInfo());
}, [currentNetwork]);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await getGovernorAvailableNotionalByChain(
currentNetwork
);
if (!cancelled) {
setGovernorInfo((info) => ({ ...info, notionals: response.entries }));
await new Promise((resolve) => setTimeout(resolve, TIMEOUT));
}
}
})();
return () => {
cancelled = true;
};
}, [currentNetwork]);
useEffect(() => {
let cancelled = false;
(async () => {
// TODO: only update GovernorInfo with changes to token list, but that will cause displaySymbols to break
while (!cancelled) {
const response = await getGovernorTokenList(currentNetwork);
if (!cancelled) {
setGovernorInfo((info) => ({ ...info, tokens: response.entries }));
await new Promise((resolve) => setTimeout(resolve, TIMEOUT));
}
}
})();
return () => {
cancelled = true;
};
}, [currentNetwork]);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await getGovernorEnqueuedVAAs(currentNetwork);
if (!cancelled) {
setGovernorInfo((info) => ({ ...info, enqueued: response.entries }));
await new Promise((resolve) => setTimeout(resolve, TIMEOUT));
}
}
})();
return () => {
cancelled = true;
};
}, [currentNetwork]);
return governorInfo;
}
export default useGovernorInfo;

View File

@ -1,60 +0,0 @@
import axios from "axios";
import { useEffect, useState } from "react";
import { POLL_TIME } from "../utils/consts";
export type EnqueuedVaa = {
sequence: number;
releasetime: number;
notionalvalue: number;
txhash: string;
};
export type Emitter = {
emitteraddress: string;
totalequeuedvaas: number;
enqueuedvaas: EnqueuedVaa[] | null;
};
export type Chain = {
chainId: number;
remainingavailablenotional: number;
emitters: Emitter[];
};
export type GovernorStatusResponse = {
chainId: number;
availableNotional: number;
notionalLimit: number;
maxTransactionSize: number;
// emitters: Emitter[];
};
function useGovernorStatus(): GovernorStatusResponse[] {
// const { currentNetwork } = useNetworkContext();
const [governorStatus, setGovernorStatus] = useState<
GovernorStatusResponse[]
>([]);
useEffect(() => {
setGovernorStatus([]);
}, []);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await axios.get<GovernorStatusResponse[]>(
"/api/governorLimits"
);
if (!cancelled) {
console.log(response.data);
setGovernorStatus(response.data);
await new Promise((resolve) => setTimeout(resolve, POLL_TIME));
}
}
})();
return () => {
cancelled = true;
};
}, []);
return governorStatus;
}
export default useGovernorStatus;

View File

@ -1,44 +0,0 @@
import axios from "axios";
import { useEffect, useState } from "react";
import { useNetworkContext } from "../contexts/NetworkContext";
import { POLL_TIME } from "../utils/consts";
export type GovernorStatusChainDetailsResponse = {
_id: string;
createdAt: string;
updatedAt: number;
nodeName: string;
chainId: number;
availableNotional: number;
};
function useGovernorStatusChainDetails(
id?: string
): GovernorStatusChainDetailsResponse[] {
const { currentNetwork } = useNetworkContext();
const [governorStatusChainDetails, setGovernorStatusChainDetails] = useState<
GovernorStatusChainDetailsResponse[]
>([]);
useEffect(() => {
setGovernorStatusChainDetails([]);
}, [currentNetwork]);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await axios.get<GovernorStatusChainDetailsResponse[]>(
`/api/availableNotional${id ? `/${id}` : ""}`
);
if (!cancelled) {
setGovernorStatusChainDetails(response.data);
await new Promise((resolve) => setTimeout(resolve, POLL_TIME));
}
}
})();
return () => {
cancelled = true;
};
}, [currentNetwork, id]);
return governorStatusChainDetails;
}
export default useGovernorStatusChainDetails;

View File

@ -1,57 +0,0 @@
import axios from "axios";
import { useEffect, useState } from "react";
import { useNetworkContext } from "../contexts/NetworkContext";
import { POLL_TIME } from "../utils/consts";
import { NumberLong } from "../utils/longToDate";
export type HeartbeatNetwork = {
contractaddress: string;
errorcount: number;
height: number;
id: number;
};
export type HeartbeatResponse = {
boottimestamp: NumberLong;
counter: number;
indexedAt: string;
features: string[] | null;
guardianaddr: string;
networks: HeartbeatNetwork[];
nodename: string;
timestamp: NumberLong;
updatedAt: string;
version: string;
_id: string;
};
function useHeartbeats(): HeartbeatResponse[] {
const { currentNetwork } = useNetworkContext();
const [heartbeats, setHeartbeats] = useState<HeartbeatResponse[]>([]);
useEffect(() => {
setHeartbeats([]);
}, [currentNetwork]);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await axios.get<HeartbeatResponse[]>(
"/api/heartbeats"
);
if (!cancelled) {
setHeartbeats(
response.data.sort(
(a, b) => a.nodename.localeCompare(b.nodename || "") || -1
)
);
await new Promise((resolve) => setTimeout(resolve, POLL_TIME));
}
}
})();
return () => {
cancelled = true;
};
}, [currentNetwork]);
return heartbeats;
}
export default useHeartbeats;

View File

@ -1,32 +0,0 @@
import axios from "axios";
import { useEffect, useState } from "react";
import { useNetworkContext } from "../contexts/NetworkContext";
import { POLL_TIME } from "../utils/consts";
import { VAAsResponse } from "./useLatestVAAs";
function useLatestNonPythNetVAAs(id?: string): VAAsResponse[] {
const { currentNetwork } = useNetworkContext();
const [vaas, setVAAs] = useState<VAAsResponse[]>([]);
useEffect(() => {
setVAAs([]);
}, [currentNetwork]);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await axios.get<VAAsResponse[]>(
`/api/vaas-sans-pythnet`
);
if (!cancelled) {
setVAAs(response.data);
await new Promise((resolve) => setTimeout(resolve, POLL_TIME));
}
}
})();
return () => {
cancelled = true;
};
}, [currentNetwork, id]);
return vaas;
}
export default useLatestNonPythNetVAAs;

View File

@ -1,42 +0,0 @@
import axios from "axios";
import { useEffect, useState } from "react";
import { useNetworkContext } from "../contexts/NetworkContext";
import { POLL_TIME } from "../utils/consts";
export type ObservationsResponse = {
indexedAt: string;
updatedAt: string;
addr: string;
hash: string;
messageId: string;
signature: string;
txhash: string;
_id: string;
};
function useLatestObservations(id?: string): ObservationsResponse[] {
const { currentNetwork } = useNetworkContext();
const [vaas, setVAAs] = useState<ObservationsResponse[]>([]);
useEffect(() => {
setVAAs([]);
}, [currentNetwork]);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await axios.get<ObservationsResponse[]>(
`/api/observations${id ? `/${id}` : ""}?limit=100`
);
if (!cancelled) {
setVAAs(response.data);
await new Promise((resolve) => setTimeout(resolve, POLL_TIME));
}
}
})();
return () => {
cancelled = true;
};
}, [currentNetwork, id]);
return vaas;
}
export default useLatestObservations;

View File

@ -1,26 +0,0 @@
import axios from "axios";
import { useEffect, useState } from "react";
// https://docs.github.com/en/rest/releases/releases#get-the-latest-release
function useLatestRelease(): string | null {
const [latestRelease, setLatestRelease] = useState<string | null>(null);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await axios.get(
"https://api.github.com/repos/wormhole-foundation/wormhole/releases/latest"
);
if (!cancelled) {
setLatestRelease(response.data?.tag_name || null);
await new Promise((resolve) => setTimeout(resolve, 60000));
}
}
})();
return () => {
cancelled = true;
};
}, []);
return latestRelease;
}
export default useLatestRelease;

View File

@ -1,44 +0,0 @@
import axios from "axios";
import { useEffect, useState } from "react";
import { useNetworkContext } from "../contexts/NetworkContext";
import { POLL_TIME } from "../utils/consts";
export type VAAsResponse = {
indexedAt: string;
updatedAt: string;
emitterAddr: string;
emitterChain: number;
guardianSetIndex: number;
sequence: number;
timestamp: string;
version: number;
vaas: string;
_id: string;
};
function useLatestVAAs(id?: string): VAAsResponse[] {
const { currentNetwork } = useNetworkContext();
const [vaas, setVAAs] = useState<VAAsResponse[]>([]);
useEffect(() => {
setVAAs([]);
}, [currentNetwork]);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await axios.get<VAAsResponse[]>(
`/api/vaas${id ? `/${id}` : ""}`
);
if (!cancelled) {
setVAAs(response.data);
await new Promise((resolve) => setTimeout(resolve, POLL_TIME));
}
}
})();
return () => {
cancelled = true;
};
}, [currentNetwork, id]);
return vaas;
}
export default useLatestVAAs;

View File

@ -1,418 +0,0 @@
import {
ChainId,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
isEVMChain,
isHexNativeTerra,
tryHexToNativeAssetString,
tryNativeToHexString,
} from "@certusone/wormhole-sdk";
import { GovernorGetTokenListResponse_Entry } from "@certusone/wormhole-sdk-proto-web/lib/cjs/publicrpc/v1/publicrpc";
import { LCDClient } from "@terra-money/terra.js";
import axios from "axios";
import { ethers } from "ethers";
import { useEffect, useMemo } from "react";
import { CHAIN_INFO_MAP } from "../utils/consts";
require("dotenv").config();
function newProvider(
url: string,
batch: boolean = false
): ethers.providers.JsonRpcProvider | ethers.providers.JsonRpcBatchProvider {
// only support http(s), not ws(s) as the websocket constructor can blow up the entire process
// it uses a nasty setTimeout(()=>{},0) so we are unable to cleanly catch its errors
if (url.startsWith("http")) {
if (batch) {
return new ethers.providers.JsonRpcBatchProvider(url);
}
return new ethers.providers.JsonRpcProvider(url);
}
throw new Error("url does not start with http/https!");
}
const ERC20_BASIC_ABI = [
"function name() view returns (string name)",
"function symbol() view returns (string symbol)",
"function decimals() view returns (uint8 decimals)",
];
//for evm chains
async function getTokenContract(
address: string,
provider:
| ethers.providers.JsonRpcProvider
| ethers.providers.JsonRpcBatchProvider
) {
const contract = new ethers.Contract(address, ERC20_BASIC_ABI, provider);
return contract;
}
type TokenInfo = {
address: string;
name: string;
decimals: number;
symbol: string;
};
async function getEvmTokenMetaData(
chain: ChainId,
governorTokenList: GovernorGetTokenListResponse_Entry[]
) {
const chainInfo = CHAIN_INFO_MAP[chain];
var formattedAddressMap: { [key: string]: string } = {};
var formattedTokens: string[] = [];
try {
const tokenListByChain = governorTokenList
.filter(
(g) =>
g.originChainId === chainInfo.chainId &&
!TOKEN_CACHE.get([chain, g.originAddress].join("_"))
)
.map((tk) => tk.originAddress);
tokenListByChain.forEach((tk) => {
const tk_ = tryHexToNativeAssetString(tk.slice(2), chainInfo.chainId);
formattedTokens.push(tk_);
formattedAddressMap[tk_] = tk;
});
let provider = newProvider(
chainInfo.endpointUrl,
true
) as ethers.providers.JsonRpcBatchProvider;
const tokenContracts = await Promise.all(
formattedTokens.map((tokenAddress) =>
getTokenContract(tokenAddress, provider)
)
);
const tokenInfos = await Promise.all(
tokenContracts.map((tokenContract) =>
Promise.all([
tokenContract.address.toLowerCase(),
tokenContract.name(),
tokenContract.decimals(),
// tokenContract.balanceOf(bridgeAddress),
tokenContract.symbol(),
])
)
);
tokenInfos.forEach((tk) => {
TOKEN_CACHE.set([chain, formattedAddressMap[tk[0]]].join("_"), {
address: tk[0],
name: tk[1],
decimals: tk[2],
symbol: tk[3],
});
});
} catch (e) {
console.log(e);
console.log(chain, chainInfo);
}
return;
}
async function loadTokenListSolana(
url = "https://token-list.solana.com/solana.tokenlist.json"
) {
const response: any = await axios.get(url).catch(function (error) {
if (error.response) {
console.log("could not load token list", error.response.status);
}
});
if (response["status"] === 200) {
const data = response["data"];
const token_list = data.tokens;
return token_list;
} else {
console.log("bad response for token list");
return [];
}
}
async function getSolanaTokenMetaData(
chain: ChainId,
governorTokenList: GovernorGetTokenListResponse_Entry[]
) {
const chainInfo = CHAIN_INFO_MAP[chain];
var formattedAddressMap: { [key: string]: string } = {};
var formattedTokens: string[] = [];
try {
const tokenListByChain = governorTokenList
.filter(
(g) =>
g.originChainId === chainInfo.chainId &&
!TOKEN_CACHE.get([chain, g.originAddress].join("_"))
)
.map((tk) => tk.originAddress);
tokenListByChain.forEach((tk) => {
const tk_ = tryHexToNativeAssetString(tk.slice(2), chainInfo.chainId);
formattedTokens.push(tk_);
formattedAddressMap[tk_] = tk;
});
var metaDataArr: any[] = [];
try {
metaDataArr = await loadTokenListSolana();
} catch (e) {
console.log(e);
}
var tokenContracts: TokenInfo[] = [];
formattedTokens.forEach((token) => {
const metaData = metaDataArr.filter((x) => x.address === token);
if (metaData.length > 0) {
tokenContracts.push(metaData[0]);
}
});
tokenContracts.forEach((tokenContract) => {
TOKEN_CACHE.set(
[chain, formattedAddressMap[tokenContract.address]].join("_"),
{
address: tokenContract.address,
name: tokenContract.name,
decimals: tokenContract.decimals,
symbol: tokenContract.symbol,
}
);
});
} catch (e) {
console.log(e);
console.log(chain, chainInfo);
}
return;
}
type TerraMetadata = {
address: string;
symbol?: string;
logo?: string;
name?: string;
decimals?: number;
};
const fetchSingleTerraMetadata = async (address: string, lcd: LCDClient) =>
lcd.wasm
.contractQuery(address, {
token_info: {},
})
.then(
({ symbol, name, decimals }: any) =>
({ address: address, symbol, name, decimals } as TerraMetadata)
);
async function getSingleTerraMetaData(
originAddress: string,
originChain: ChainId
) {
const TERRA_HOST = {
URL:
originChain === CHAIN_ID_TERRA
? "https://columbus-fcd.terra.dev"
: "https://phoenix-fcd.terra.dev",
chainID: originChain === CHAIN_ID_TERRA ? "columbus-5" : "phoenix-1",
name: "mainnet",
};
const lcd = new LCDClient(TERRA_HOST);
if (isHexNativeTerra(tryNativeToHexString(originAddress, originChain))) {
if (originAddress === "uusd") {
return {
address: originAddress,
name: "UST Classic",
symbol: "USTC",
decimals: 6,
};
} else if (originAddress === "uluna") {
return {
address: originAddress,
name: "Luna Classic",
symbol: "LUNC",
decimals: 6,
};
} else {
return {
address: originAddress,
name: "",
symbol: "",
decimals: 8,
};
}
} else {
return await fetchSingleTerraMetadata(originAddress, lcd);
}
}
async function getTerraTokenMetaData(
chain: ChainId,
governorTokenList: GovernorGetTokenListResponse_Entry[]
) {
const chainInfo = CHAIN_INFO_MAP[chain];
var formattedAddressMap: { [key: string]: string } = {};
var formattedTokens: string[] = [];
try {
const tokenListByChain = governorTokenList
.filter(
(g) =>
g.originChainId === chainInfo.chainId &&
!TOKEN_CACHE.get([chain, g.originAddress].join("_"))
)
.map((tk) => tk.originAddress);
tokenListByChain.forEach((tk) => {
const tk_ = tryHexToNativeAssetString(tk.slice(2), chainInfo.chainId);
formattedTokens.push(tk_);
formattedAddressMap[tk_] = tk;
});
var tokenContracts: any[] = [];
for (let i = 0; i < formattedTokens.length; i++) {
const token = formattedTokens[i];
const metaData = await getSingleTerraMetaData(token, chain);
tokenContracts.push(metaData);
}
tokenContracts.forEach((tokenContract) => {
TOKEN_CACHE.set(
[chain, formattedAddressMap[tokenContract.address]].join("_"),
{
address: tokenContract.address,
name: tokenContract.name,
decimals: tokenContract.decimals,
symbol: tokenContract.symbol,
}
);
});
} catch (e) {
console.log(e);
console.log(chain, chainInfo);
}
return;
}
const MISC_TOKEN_META_DATA: {
[key: string]: {
[key: string]: { name: string; symbol: string; decimals: number };
};
} = {
"8": {
"0x0000000000000000000000000000000000000000000000000000000000000000": {
name: "ALGO",
symbol: "ALGO",
decimals: 6,
},
"0x000000000000000000000000000000000000000000000000000000000004c5c1": {
name: "USDT",
symbol: "USDT",
decimals: 6,
},
"0x0000000000000000000000000000000000000000000000000000000001e1ab70": {
name: "USDC",
symbol: "USDC",
decimals: 6,
},
},
"15": {
"0x0000000000000000000000000000000000000000000000000000000000000000": {
name: "NEAR",
symbol: "NEAR",
decimals: 24,
},
},
"18": {
"0x01fa6c6fbc36d8c245b0a852a43eb5d644e8b4c477b27bfab9537c10945939da": {
name: "LUNA",
symbol: "LUNA",
decimals: 6,
},
},
};
async function getMiscTokenMetaData(
chain: ChainId,
governorTokenList: GovernorGetTokenListResponse_Entry[]
) {
const chainInfo = CHAIN_INFO_MAP[chain];
const tokenMetaDataByChain = MISC_TOKEN_META_DATA[chain.toString()];
try {
const tokenListByChain = governorTokenList
.filter(
(g) =>
g.originChainId === chainInfo.chainId &&
!TOKEN_CACHE.get([chain, g.originAddress].join("_"))
)
.map((tk) => tk.originAddress);
tokenListByChain.forEach((tk) => {
const metaData = tokenMetaDataByChain[tk];
TOKEN_CACHE.set([chain, tk].join("_"), {
address: tk,
name: metaData?.name,
decimals: metaData?.decimals,
symbol: metaData?.symbol,
});
});
} catch (e) {
console.log(e);
console.log(chain, chainInfo);
}
return;
}
const TOKEN_CACHE = new Map<string, TokenInfo>();
async function getTokenMetaData(
governorTokenList: GovernorGetTokenListResponse_Entry[]
) {
const chains = Object.keys(CHAIN_INFO_MAP);
for (let i = 0; i < chains.length; i++) {
const chain = chains[i];
const chainInfo = CHAIN_INFO_MAP[chain];
const chainId = chainInfo.chainId;
try {
//grab token info
if (isEVMChain(chainId)) {
await getEvmTokenMetaData(chainId, governorTokenList);
} else if (chainId === CHAIN_ID_SOLANA) {
await getSolanaTokenMetaData(chainId, governorTokenList);
} else if (chainId === CHAIN_ID_TERRA) {
await getTerraTokenMetaData(chainId, governorTokenList);
} else {
// currently no support for ALGORAND, NEAR, TERRA2
console.log(`the chain=${chain} is not supported`);
await getMiscTokenMetaData(chainId, governorTokenList);
}
await new Promise((resolve) => setTimeout(resolve, 6000));
} catch (e) {
console.log(e);
console.log(chain, chainInfo);
continue;
}
}
await new Promise((resolve) => setTimeout(resolve, 3000000));
return;
}
const TIMEOUT = 60 * 1000;
function useSymbolInfo(tokens: GovernorGetTokenListResponse_Entry[]) {
// TODO: GovernorInfo gets fetched repeatedly, but we don't need to refresh the list
// So using string'd version of token list as a hack around when to update the token list
const memoizedTokens = useMemo(() => JSON.stringify(tokens), [tokens]);
useEffect(() => {
const tokens = JSON.parse(memoizedTokens);
(async () => {
// TODO: use a state setter to update instead of relying on TOKEN_CACHE.
await getTokenMetaData(tokens);
await new Promise((resolve) => setTimeout(resolve, TIMEOUT));
})();
}, [memoizedTokens]);
return TOKEN_CACHE;
}
export default useSymbolInfo;

View File

@ -1,47 +0,0 @@
import axios from "axios";
import { useEffect, useState } from "react";
import { useNetworkContext } from "../contexts/NetworkContext";
import { POLL_TIME } from "../utils/consts";
export type TokenDetailsResponse = {
tokenAddress: string;
name: string;
decimals: number;
symbol: string;
balance: BigInt;
qty: number;
tokenPrice: number;
tokenBalanceUSD: number;
};
export type TokensResponse = {
_id: string;
tokens: TokenDetailsResponse[];
};
function useTokenDetails(id?: string): TokenDetailsResponse[] {
const { currentNetwork } = useNetworkContext();
const [tokenDetails, setTokenDetails] = useState<TokenDetailsResponse[]>([]);
useEffect(() => {
setTokenDetails([]);
}, [currentNetwork]);
useEffect(() => {
let cancelled = false;
(async () => {
while (!cancelled) {
const response = await axios.get<TokensResponse>(
`/api/custody/tokens${id ? `/${id}` : ""}`
);
if (!cancelled) {
setTokenDetails(response.data.tokens);
await new Promise((resolve) => setTimeout(resolve, POLL_TIME));
}
}
})();
return () => {
cancelled = true;
};
}, [currentNetwork, id]);
return tokenDetails;
}
export default useTokenDetails;

View File

@ -1,43 +0,0 @@
import { SvgIcon } from "@mui/material";
// paths taken from wormhole-symbol.inline.svg
function WormholeStatsIcon() {
return (
<SvgIcon viewBox="0 0 40 40">
<path
fill="#000000"
d="M20,40c-5.3,0-10.4-2.1-14.1-5.9C2.1,30.4,0,25.3,0,20S2.1,9.6,5.9,5.9C9.6,2.1,14.7,0,20,0s10.4,2.1,14.1,5.9
C37.9,9.6,40,14.7,40,20s-2.1,10.4-5.9,14.1C30.4,37.9,25.3,40,20,40z M20,1.5c-4.9,0-9.6,1.9-13,5.4s-5.4,8.1-5.4,13s2,9.6,5.4,13
c3.5,3.5,8.2,5.4,13,5.4s9.6-1.9,13-5.4c3.5-3.5,5.4-8.1,5.4-13s-2-9.6-5.4-13S24.9,1.5,20,1.5L20,1.5z"
/>
<path
fill="#000000"
d="M22.5,36.8c-4.4,0-8.6-1.7-11.7-4.9C7.7,28.8,6,24.6,6,20.2s1.8-8.6,4.9-11.7s7.3-4.8,11.7-4.9
c4.4,0,8.6,1.7,11.7,4.9c3.1,3.1,4.9,7.3,4.9,11.7s-1.8,8.6-4.9,11.7C31.1,35.1,26.9,36.8,22.5,36.8z M22.5,4.9
c-4.1,0-8,1.6-10.9,4.5s-4.5,6.8-4.5,10.9s1.6,8,4.5,10.9s6.8,4.5,10.9,4.5s8-1.6,10.9-4.5s4.5-6.8,4.5-10.9s-1.6-8-4.5-10.9
S26.6,4.9,22.5,4.9L22.5,4.9z"
/>
<path
fill="#000000"
d="M25.1,33.6c-3.5,0-6.8-1.4-9.3-3.8c-2.5-2.5-3.8-5.8-3.9-9.3c0-3.5,1.4-6.8,3.9-9.3s5.8-3.8,9.3-3.8
s6.8,1.4,9.3,3.8c2.5,2.5,3.8,5.8,3.9,9.3c0,3.5-1.4,6.8-3.9,9.3S28.5,33.6,25.1,33.6L25.1,33.6z M25.1,8.2c-3.3,0-6.4,1.3-8.7,3.6
s-3.6,5.4-3.6,8.7s1.3,6.4,3.6,8.7s5.4,3.6,8.7,3.6s6.4-1.3,8.7-3.6s3.6-5.4,3.6-8.7s-1.3-6.4-3.6-8.7C31.4,9.5,28.3,8.2,25.1,8.2
L25.1,8.2z"
/>
<path
fill="#000000"
d="M27.6,30.5c-2.6,0-5-1-6.9-2.8c-1.8-1.8-2.8-4.3-2.8-6.9s1-5,2.8-6.9c1.8-1.8,4.3-2.8,6.9-2.8s5,1,6.9,2.8
s2.8,4.3,2.8,6.9s-1,5-2.8,6.9C32.6,29.5,30.2,30.5,27.6,30.5L27.6,30.5z M27.6,11.6c-2.4,0-4.8,1-6.5,2.7c-1.7,1.7-2.7,4.1-2.7,6.5
s1,4.8,2.7,6.5s4.1,2.7,6.5,2.7s4.8-1,6.5-2.7c1.7-1.7,2.7-4.1,2.7-6.5s-1-4.8-2.7-6.5C32.4,12.6,30,11.6,27.6,11.6z"
/>
<path
fill="#000000"
stroke="#231f20"
strokeMiterlimit="10"
d="M17.2,32.9V15h5.7v17.9H17.2z M27.3,32.9v-26H33v26H27.3z M7,32.9V21.5h5.7v11.4H7z"
/>
</SvgIcon>
);
}
export default WormholeStatsIcon;

View File

@ -1 +0,0 @@
<svg fill="none" viewBox="0 0 490 490" xmlns="http://www.w3.org/2000/svg"><path d="M245 490c135.31 0 245-109.69 245-245S380.31 0 245 0 0 109.69 0 245s109.69 245 245 245zm1-23c122.61 0 222-99.393 222-222S368.607 23 246 23 24 122.393 24 245s99.393 222 222 222zm-1-17c113.22 0 205-91.782 205-205S358.218 40 245 40 40 131.782 40 245s91.782 205 205 205zm.5-25c99.687 0 180.5-80.813 180.5-180.5S345.187 64 245.5 64 65 144.813 65 244.5 145.813 425 245.5 425zM235.313 98.66l130.68 226.7 14.012-24.31-116.66-202.39zm-125.31 201.98 111.84-194.03.231.4.22-.382 132.54 229.93h-28.025l-33.484-58.088c-15.215-4.81-31.414-7.404-48.22-7.404-8.663 0-17.117.605-25.336 1.812l16.14-27.956c3.047-.149 6.113-.224 9.196-.224 10.267 0 20.339.831 30.154 2.43l-53.195-92.284-98.05 170.1zm76.035-2.949 50.256-87.186-14.012-24.309-86.676 150.37h28.025l.266-.462c24.037-14.472 51.619-21.787 81.737-21.787 19.232 0 37.67 3.397 54.747 9.625l-18.775-32.52a187.14 187.14 0 0 0-35.972-3.472c-20.842 0-40.885 3.425-59.596 9.744z" clip-rule="evenodd" fill="url(#a)" fill-rule="evenodd"/><defs><linearGradient id="a" x1="462.5" x2="101" y1="490" y2="43.5" gradientUnits="userSpaceOnUse"><stop stop-color="#5A81FF" offset="0"/><stop stop-color="#E40C5B" offset=".524"/><stop stop-color="#FF4C3B" offset="1"/></linearGradient></defs></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 650 650"><defs><style>.cls-1{fill:#000;}</style></defs><title>ALGO_Logos_190320</title><g id="lINT7W"><polygon class="cls-1" points="444.18 444.32 406.81 444.32 382.54 354.04 330.36 444.33 288.64 444.33 369.29 304.57 356.31 256.05 247.56 444.36 205.82 444.36 343.64 205.64 380.18 205.64 396.18 264.95 433.88 264.95 408.14 309.71 444.18 444.32"/></g></svg>

Before

Width:  |  Height:  |  Size: 442 B

View File

@ -1,6 +0,0 @@
<svg width="12" height="11" viewBox="0 0 12 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 5.0409C0.110314 4.58962 0.190542 4.18848 0.300856 3.78734C0.320913 3.7372 0.451285 3.67702 0.531513 3.67702C1.29368 3.667 2.04582 3.67702 2.80799 3.67702C3.68047 3.67702 4.54292 3.667 5.4154 3.67702C5.74634 3.68705 6.00708 3.58677 6.21768 3.316C6.58874 2.85469 6.69905 2.85469 7.08013 3.30597C7.30076 3.57674 7.55148 3.68705 7.90247 3.67702C8.77496 3.65697 9.65746 3.67702 10.5299 3.667C10.6904 3.667 10.7807 3.70711 10.8107 3.86757C10.891 4.23862 10.9712 4.60968 11.0615 5.03087C10.9311 5.03087 10.8408 5.03087 10.7506 5.03087C9.52709 5.03087 8.31364 5.02085 7.09016 5.0409C6.82942 5.0409 6.64891 4.96068 6.48845 4.7601C6.1174 4.28876 5.977 4.15839 5.49563 4.73002C5.29506 4.9707 5.07443 5.0409 4.7836 5.03087C3.30941 5.02085 1.82519 5.02085 0.350998 5.02085C0.27077 5.0409 0.180513 5.0409 0 5.0409Z" fill="black"/>
<path d="M0.0800781 6.52558C0.250563 6.52558 0.350846 6.52558 0.451131 6.52558C1.46401 6.52558 2.46686 6.51555 3.47974 6.52558C3.79063 6.52558 4.05137 6.45538 4.24191 6.18461C4.29206 6.11441 4.35222 6.06427 4.4124 5.99407C4.62299 5.76341 4.81354 5.74336 4.99405 5.99407C5.29491 6.43532 5.67599 6.53561 6.18744 6.52558C7.67167 6.49549 9.15588 6.51555 10.6401 6.51555C10.7304 6.51555 10.8206 6.51555 11.0011 6.51555C10.8407 6.97686 10.7003 7.39806 10.5398 7.80923C10.5198 7.84934 10.4195 7.87943 10.3493 7.87943C8.55417 7.87943 6.75907 7.88946 4.96396 7.87943C4.84362 7.87943 4.71325 7.7992 4.623 7.71897C4.49262 7.60866 4.39234 7.45823 4.272 7.33789C4.12157 7.18746 3.95109 7.17743 3.79063 7.32786C3.7806 7.33789 3.7806 7.33789 3.77057 7.34792C3.44966 7.87943 2.95826 7.9396 2.38664 7.90952C1.85513 7.8694 1.32361 7.88946 0.782074 7.90952C0.61159 7.90952 0.521332 7.84934 0.47119 7.69892C0.350847 7.31783 0.230506 6.94678 0.0800781 6.52558Z" fill="black"/>
<path d="M1.14453 2.20271C3.24049 -0.685505 7.72324 -0.775762 9.91948 2.18265C9.52837 2.18265 9.14729 2.22277 8.77623 2.16259C8.61577 2.14254 8.47538 1.94197 8.35503 1.80157C8.00404 1.42048 7.90375 1.40043 7.57281 1.79154C7.31207 2.09239 7.03127 2.21274 6.6201 2.21274C4.90522 2.19268 3.18032 2.20271 1.46544 2.20271C1.36516 2.20271 1.26487 2.20271 1.14453 2.20271Z" fill="black"/>
<path d="M1.52539 9.24251C1.85633 9.23249 2.00676 8.9918 2.18727 8.79123C2.46807 8.47032 2.63856 8.45026 2.88927 8.79123C3.15001 9.16229 3.48095 9.25254 3.91218 9.24251C5.68722 9.22246 7.4723 9.23249 9.24735 9.23249C9.33761 9.23249 9.42786 9.23249 9.52815 9.23249C7.74307 11.4187 3.77178 11.7496 1.52539 9.24251Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,7 +0,0 @@
<svg width="18" height="21" viewBox="0 0 18 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.91443 12.9234L11.6441 17.2597L14.1658 15.7806L10.5807 10.0615L8.91443 12.9234Z" fill="black"/>
<path d="M16.4624 14.042L16.4605 12.8568L12.5432 6.68164L11.0899 9.17756L14.8715 15.3676L16.2388 14.5657C16.3729 14.4554 16.454 14.2936 16.4626 14.1191L16.4624 14.042Z" fill="black"/>
<path d="M0.046875 15.159L1.9777 16.2851L8.40306 5.85581L7.31257 5.82668C6.38342 5.8134 5.38252 6.05787 4.92314 6.81684L1.2744 12.5408L0.046875 14.4497V15.159V15.159Z" fill="black"/>
<path d="M12.1109 5.85547L9.23681 5.86603L2.7345 16.7261L5.00726 18.0505L5.62533 16.9895L12.1109 5.85547Z" fill="black"/>
<path d="M17.6733 5.84104C17.6493 5.2327 17.3239 4.6758 16.8141 4.35158L9.66906 0.192877C9.1648 -0.0640998 8.53488 -0.0644298 8.02972 0.192712C7.97001 0.223199 1.08136 4.26672 1.08136 4.26672C0.986014 4.313 0.894179 4.36812 0.807806 4.4307C0.352748 4.76078 0.0753671 5.27375 0.046875 5.83729V14.4499L1.2744 12.541L1.26368 5.8955C1.26511 5.8712 1.26812 5.84727 1.2724 5.82368C1.30016 5.66732 1.38641 5.52784 1.51628 5.43357C1.54868 5.4101 8.55501 1.30544 8.57727 1.29418C8.74194 1.21047 8.95125 1.20948 9.11613 1.29158L16.1687 5.39739C16.3356 5.50494 16.4402 5.68729 16.4495 5.88663V14.1187C16.4409 14.2933 16.3727 14.455 16.2386 14.5653L14.8713 15.3672L14.1658 15.781L11.6442 17.2601L9.08695 18.76C8.94922 18.8104 8.78817 18.8075 8.65158 18.7514L5.62595 16.9901L5.00789 18.051L7.72691 19.6354C7.81683 19.6872 7.89692 19.7331 7.96267 19.7704C8.06445 19.8282 8.13383 19.8668 8.15833 19.8789C8.35157 19.9739 8.62965 20.0292 8.88021 20.0292C9.10994 20.0292 9.33396 19.9865 9.54592 19.9024L16.9736 15.5488C17.4 15.2144 17.6508 14.7103 17.6733 14.1641V5.84104Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg enable-background="new 0 0 288 288" version="1.1" viewBox="0 0 288 288" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">
.st0{fill:#70D44B;}
.st1{fill:#FFFFFF;}
</style>
<path class="st0" d="m144 0c79.5 0 144 64.5 144 144s-64.5 144-144 144-144-64.5-144-144 64.5-144 144-144z"/>
<path class="st1" d="m144 58.8c7.6 0 14.5 4.3 17.9 11.1l56.2 112.5c4.9 9.9 0.9 21.9-9 26.8-2.8 1.4-5.8 2.1-8.9 2.1h-112.4c-11 0-20-9-20-20 0-3.1 0.7-6.2 2.1-8.9l56.2-112.5c3.4-6.9 10.3-11.2 17.9-11.1m0-13.8c-12.8 0-24.5 7.2-30.2 18.7l-56.2 112.5c-8.3 16.7-1.6 36.9 15.1 45.3 4.7 2.3 9.9 3.6 15.1 3.6h112.5c18.6 0 33.8-15.1 33.8-33.7 0-5.2-1.2-10.4-3.6-15.1l-56.3-112.6c-5.7-11.5-17.4-18.7-30.2-18.7z"/>
</svg>

Before

Width:  |  Height:  |  Size: 766 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 254 254" style="enable-background:new 0 0 254 254" xml:space="preserve"><circle cx="127" cy="127" r="127" style="fill-rule:evenodd;clip-rule:evenodd;fill:#e84142"/><path d="M171.8 130.3c4.4-7.6 11.5-7.6 15.9 0l27.4 48.1c4.4 7.6.8 13.8-8 13.8h-55.2c-8.7 0-12.3-6.2-8-13.8l27.9-48.1zm-53-92.6c4.4-7.6 11.4-7.6 15.8 0l6.1 11L155.1 74c3.5 7.2 3.5 15.7 0 22.9l-48.3 83.7c-4.4 6.8-11.7 11.1-19.8 11.6H46.9c-8.8 0-12.4-6.1-8-13.8l79.9-140.7z" style="fill:#fff"/></svg>

Before

Width:  |  Height:  |  Size: 514 B

View File

@ -1 +0,0 @@
<svg width="40" height="40" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M20.302 0 9.68 6.127l3.906 2.263 6.717-3.863L27.02 8.39l3.906-2.263L20.303 0ZM27.02 11.59l3.906 2.264v4.527l-6.718 3.863v7.727l-3.905 2.263-3.906-2.263v-7.727L9.679 18.38v-4.527l3.906-2.264 6.717 3.864 6.718-3.864Z" fill="#F0B90B"/><path d="M30.926 21.58v4.527l-3.906 2.264v-4.527l3.906-2.264Z" fill="#F0B90B"/><path d="m26.981 31.57 6.718-3.863V19.98l3.906-2.263v12.254l-10.624 6.127V31.57ZM33.7 12.254 29.792 9.99 33.7 7.727l3.906 2.263v4.527l-3.906 2.264v-4.527ZM16.397 37.737V33.21l3.905 2.263 3.906-2.263v4.527L20.303 40l-3.906-2.263ZM13.585 28.37l-3.906-2.263v-4.526l3.906 2.263v4.527ZM20.302 12.254 16.397 9.99l3.905-2.263 3.906 2.263-3.905 2.264ZM10.812 9.99l-3.906 2.264v4.527L3 14.517V9.99l3.906-2.263 3.906 2.263Z" fill="#F0B90B"/><path d="m3 17.717 3.906 2.264v7.726l6.718 3.864v4.527L3 29.97V17.717Z" fill="#F0B90B"/></svg>

Before

Width:  |  Height:  |  Size: 920 B

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Celo_Rings" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 950 950" style="enable-background:new 0 0 950 950;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FBCC5C;}
.st1{fill:#35D07F;}
.st2{fill:#5EA33B;}
</style>
<title>Artboard 1</title>
<path id="Bottom_Ring" class="st0" d="M375,850c151.9,0,275-123.1,275-275S526.9,300,375,300S100,423.1,100,575S223.1,850,375,850z
M375,950C167.9,950,0,782.1,0,575s167.9-375,375-375s375,167.9,375,375S582.1,950,375,950z"/>
<path id="Top_Ring" class="st1" d="M575,650c151.9,0,275-123.1,275-275S726.9,100,575,100S300,223.1,300,375S423.1,650,575,650z
M575,750c-207.1,0-375-167.9-375-375S367.9,0,575,0s375,167.9,375,375S782.1,750,575,750z"/>
<path id="Rings_Overlap" class="st2" d="M587.4,750c26-31.5,44.6-68.4,54.5-108.1c39.6-9.9,76.5-28.5,108.1-54.5
c-1.4,45.9-11.3,91.1-29.2,133.5C678.5,738.7,633.3,748.6,587.4,750z M308.1,308.1c-39.6,9.9-76.5,28.5-108.1,54.5
c1.4-45.9,11.3-91.1,29.2-133.4c42.3-17.8,87.6-27.7,133.4-29.2C336.6,231.5,318,268.4,308.1,308.1z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1920" xml:space="preserve"><path fill="#8A92B2" d="M959.8 80.7 420.1 976.3 959.8 731z"/><path fill="#62688F" d="M959.8 731 420.1 976.3l539.7 319.1zM1499.6 976.3 959.8 80.7V731z"/><path fill="#454A75" d="m959.8 1295.4 539.8-319.1L959.8 731z"/><path fill="#8A92B2" d="m420.1 1078.7 539.7 760.6v-441.7z"/><path fill="#62688F" d="M959.8 1397.6v441.7l540.1-760.6z"/></svg>

Before

Width:  |  Height:  |  Size: 425 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><defs><style>.cls-1{fill:#fff;fill-rule:evenodd}</style><mask id="mask" x="10" y="6" width="93.1" height="20" maskUnits="userSpaceOnUse"><path class="cls-1" d="M10 6h93.1v20H10Z" id="a"/></mask></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><circle cx="16" cy="16" r="16" style="fill:#13b5ec"/><path class="cls-1" d="m17.2 12.9 3.6-2.1V15Zm3.6 9L16 24.7l-4.8-2.8V17l4.8 2.8 4.8-2.8Zm-9.6-11.1 3.6 2.1-3.6 2.1Zm5.4 3.1 3.6 2.1-3.6 2.1Zm-1.2 4.2L11.8 16l3.6-2.1Zm4.8-8.3L16 12.2l-4.2-2.4L16 7.3ZM10 9.4v13.1l6 3.4 6-3.4V9.4L16 6Z" style="mask:url(#mask)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 662 B

View File

@ -1 +0,0 @@
<svg viewBox="0 0 32 32" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><style>.st0{fill:url(#path9_00000183207226421243246130000000649871513633350020_)}.st1{fill:url(#path11_00000111907511177103573740000007277726760468908191_)}</style><linearGradient id="a" x1="30.603" x2="9.067" y1="3.118" y2="26.527" gradientTransform="matrix(1 0 0 -1 0 34)" gradientUnits="userSpaceOnUse"><stop stop-color="#E40C5B" offset="0"/><stop stop-color="#FF4C3B" offset="1"/></linearGradient><path d="m22.1 2-8.6 7.8c-.9.8-2.1 1.3-3.3 1.3H7.6V2H3v26h2.5c1.4 0 2.7-.8 3.4-2 .5-.8.7-2.5.6-3.6-.1-2.5-.5-3.2-.5-3.2 0 1.8-1.8 1.9-1.8 1.9 1.3-1.2.6-2.2.6-2.2-2.3 1.6-2.9-.1-2.9-.3.1.1.7.6 2-1.1 1.3-1.8 3.1-4.8 4.2-5.6s2.2-.7 2.2-.7.6-.8 2.2-1.5 2.6.3 2.6.3c-1.6 1.3-3.9 3.5-4 6.6-.1 2.5 5.4 6.9 4.4 13.3.6-1.5.8-2.9.6-4.7-.2-1.5-1.1-4.5-1.1-4.5l5.2 7.2H29L17.7 12.4 29 2h-6.9z" fill="url(#a)"/><linearGradient id="b" x1="12.414" x2="10.889" y1="20.185" y2="21.855" gradientTransform="matrix(1 0 0 -1 0 34)" gradientUnits="userSpaceOnUse"><stop stop-color="#E40C5B" offset="0"/><stop stop-color="#FF4C3B" offset="1"/></linearGradient><path d="M10.9 12.8c-.4.7-.4.9-.4.9s1.5-.2 1.9-2c-.1 0-1 .3-1.5 1.1z" fill="url(#b)"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="CIRCLE_OUTLINE_BLACK" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<style type="text/css">
.st0{fill:#BD072F;}
.st1{fill:#FA4212;}
.st2{fill:#FF8000;}
.st3{fill:#B70C02;}
</style>
<path class="st0" d="M254.8,288.1c53.9,53.8,107,106.8,160,159.7c-87.9,75.2-225.9,78.4-319.7-1.2
C148.5,393.7,201.7,340.9,254.8,288.1z"/>
<path class="st1" d="M291.5,255.9c35.2-36.2,70.9-73.2,107-109.9c15-15.3,30.5-29.9,46.1-44.6c1.2-1.1,5.2-1.3,5.9-0.4
c27.4,36,47.8,75.3,53.2,120.8c8.2,69.4-7.7,132.6-51.3,188.1c-1.2,1.5-2.3,3.2-2.9,4.1C396.3,360.7,343.1,307.6,291.5,255.9z"/>
<path class="st2" d="M139.1,337.8c5.9-15.6,11.9-31.2,17.8-46.8c24.5-64.5,49-129,73.5-193.4c7.7-20,16.4-39.7,24.3-59.7
c3-7.7,5.3-15.7,7.7-23.6c1.8-5.9,5.4-8.1,11.6-7.5c24.4,2.4,48.4,6.2,70.9,16.5c13,6,26.3,11.4,38.7,18.5
c10.1,5.8,18.9,13.7,30.1,22L140.7,339L139.1,337.8z"/>
<path class="st3" d="M200.2,47.7c-45.9,121.5-91.7,242.9-138.5,366.9c-8.8-13.4-17-24.7-23.9-36.7c-15.8-27.5-25.9-57.9-29.9-89.4
c-1.9-15-1.5-30.4-1.6-45.6c0-2.4,2.2-5.3,4.1-7.2C69.5,176.4,128.8,117.3,188,58.2c3.6-3.6,7.1-7.4,10.7-11.2L200.2,47.7z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,21 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
<g id="logo-symbol" transform="translate(-83 -294)">
<rect id="Rectangle_37" data-name="Rectangle 37" width="200" height="200" transform="translate(83 294)" fill="none"/>
<g id="symbol" transform="translate(-224.332 64.165)">
<path id="Path_185" data-name="Path 185" d="M808.276,400.23A61.3,61.3,0,0,0,747,461.543h0c0,.043,0,.084,0,.128l0,.1a3.016,3.016,0,0,0,3.017,2.845H866.534a3.015,3.015,0,0,0,3.016-2.845l.005-.1c0-.044,0-.085,0-.128h0A61.3,61.3,0,0,0,808.276,400.23Z" transform="translate(-382.261 -154.395)" fill="#53cbc8"/>
<path id="Path_186" data-name="Path 186" d="M673.015,617.7a3.729,3.729,0,1,1-3.73-3.732A3.73,3.73,0,0,1,673.015,617.7Z" transform="translate(-348.846 -242.095)" fill="#e1147b"/>
<path id="Path_187" data-name="Path 187" d="M853.29,585.287H728.267a3.677,3.677,0,0,0-3.231,5.423c.02.039.041.078.062.116a3.668,3.668,0,0,0,3.232,1.924h124.9a3.669,3.669,0,0,0,3.232-1.924l.062-.116A3.678,3.678,0,0,0,853.29,585.287Z" transform="translate(-373.07 -230.326)" fill="#e1147b"/>
<path id="Path_188" data-name="Path 188" d="M869.9,527.924H728.262a3.681,3.681,0,0,0-3.666,3.887c0,.039,0,.078.006.117a3.665,3.665,0,0,0,3.667,3.459H869.9a3.666,3.666,0,0,0,3.667-3.459c0-.039,0-.078.006-.117A3.681,3.681,0,0,0,869.9,527.924Z" transform="translate(-373.068 -206.789)" fill="#e1147b"/>
<path id="Path_189" data-name="Path 189" d="M833.6,671.331H769.767a3.675,3.675,0,0,0-1.638,6.964l.234.117a3.657,3.657,0,0,0,1.637.382h63.364a3.662,3.662,0,0,0,1.638-.382l.233-.117A3.675,3.675,0,0,0,833.6,671.331Z" transform="translate(-390.097 -265.632)" fill="#e1147b"/>
<path id="Path_190" data-name="Path 190" d="M899.737,642.649H835.906a3.676,3.676,0,0,0-1.637,6.964l.233.117a3.668,3.668,0,0,0,1.638.381H899.5a3.665,3.665,0,0,0,1.638-.381l.233-.117A3.676,3.676,0,0,0,899.737,642.649Z" transform="translate(-417.236 -253.863)" fill="#e1147b"/>
<path id="Path_191" data-name="Path 191" d="M798.341,620.256l-.108-.116a3.674,3.674,0,0,1,2.694-6.173h103.6a3.674,3.674,0,0,1,2.694,6.173l-.108.116a3.7,3.7,0,0,1-2.693,1.174H801.034A3.7,3.7,0,0,1,798.341,620.256Z" transform="translate(-402.878 -242.095)" fill="#e1147b"/>
<path id="Path_192" data-name="Path 192" d="M691.913,613.968h49.472a3.676,3.676,0,0,1,1.637,6.964l-.233.117a3.657,3.657,0,0,1-1.637.382h-49a3.658,3.658,0,0,1-1.638-.382l-.233-.117A3.676,3.676,0,0,1,691.913,613.968Z" transform="translate(-358.154 -242.095)" fill="#e1147b"/>
<path id="Path_193" data-name="Path 193" d="M709.367,531.655a3.729,3.729,0,1,1-3.73-3.731A3.73,3.73,0,0,1,709.367,531.655Z" transform="translate(-363.763 -206.789)" fill="#e1147b"/>
<path id="Path_194" data-name="Path 194" d="M786.534,561.345c.01-.038.02-.078.031-.116a3.672,3.672,0,0,0-3.549-4.622H679.4a3.671,3.671,0,0,0-3.549,4.622c.01.038.021.078.032.116a3.681,3.681,0,0,0,3.547,2.724H782.986a3.683,3.683,0,0,0,3.548-2.724" transform="translate(-353.019 -218.558)" fill="#e1147b"/>
<path id="Path_195" data-name="Path 195" d="M660.5,560.337a3.729,3.729,0,1,1-3.729-3.731A3.731,3.731,0,0,1,660.5,560.337Z" transform="translate(-343.711 -218.558)" fill="#e1147b"/>
<path id="Path_196" data-name="Path 196" d="M709.367,589.018a3.729,3.729,0,1,1-3.73-3.731A3.731,3.731,0,0,1,709.367,589.018Z" transform="translate(-363.763 -230.326)" fill="#e1147b"/>
<path id="Path_197" data-name="Path 197" d="M817.008,646.381a3.729,3.729,0,1,1-3.73-3.731A3.73,3.73,0,0,1,817.008,646.381Z" transform="translate(-407.928 -253.863)" fill="#e1147b"/>
<path id="Path_198" data-name="Path 198" d="M750.868,675.063a3.729,3.729,0,1,1-3.729-3.731A3.73,3.73,0,0,1,750.868,675.063Z" transform="translate(-380.79 -265.632)" fill="#e1147b"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,7 +0,0 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 90.1 90" style="enable-background:new 0 0 90.1 90;" xml:space="preserve">
<path fill="#000" d="M72.2,4.6L53.4,32.5c-1.3,1.9,1.2,4.2,3,2.6L74.9,19c0.5-0.4,1.2-0.1,1.2,0.6v50.3c0,0.7-0.9,1-1.3,0.5l-56-67
C17,1.2,14.4,0,11.5,0h-2C4.3,0,0,4.3,0,9.6v70.8C0,85.7,4.3,90,9.6,90c3.3,0,6.4-1.7,8.2-4.6l18.8-27.9c1.3-1.9-1.2-4.2-3-2.6
l-18.5,16c-0.5,0.4-1.2,0.1-1.2-0.6V20.1c0-0.7,0.9-1,1.3-0.5l56,67c1.8,2.2,4.5,3.4,7.3,3.4h2c5.3,0,9.6-4.3,9.6-9.6V9.6
c0-5.3-4.3-9.6-9.6-9.6C77.1,0,74,1.7,72.2,4.6z"/>
</svg>

Before

Width:  |  Height:  |  Size: 644 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 128 128" style="enable-background:new 0 0 128 128" xml:space="preserve"><defs><path id="a" d="M0 0h128v128H0z"/></defs><clipPath id="b"><use xlink:href="#a" style="overflow:visible"/></clipPath><g style="clip-path:url(#b)"><path d="M115 25.4C105.3 13 91.1 5.1 75.4 5.1 45 5.1 22.7 33.4 22.7 64c0 21.3 14.2 37.3 29.9 37.3 1.5 0 2.9-.1 4.4-.4C46.4 93.2 39.7 79.2 39.7 64c0-24.2 16.4-43.1 35.7-43.1 21.1 0 35.7 20.9 35.7 43.1-.1 16.4-5.9 32.7-16.2 44.7-9 10.5-20.6 17.1-33.4 19.2 0 0 1.1.1 2.5.1 35.3 0 64-28.7 64-64 0-14.5-4.8-27.9-13-38.6zM105.3 64c0-21.3-14.2-37.3-29.9-37.3-1.5 0-2.9.1-4.4.4C81.6 34.8 88.3 48.8 88.3 64c0 24.2-16.4 43.1-35.7 43.1-21.1 0-35.7-20.9-35.7-43.1.1-16.4 5.9-32.7 16.2-44.7C42.1 8.8 53.7 2.2 66.5.1c0 0-1.1-.1-2.5-.1C28.7 0 0 28.7 0 64c0 14.5 4.8 27.9 13 38.6 9.7 12.4 23.9 20.2 39.6 20.2 30.4.1 52.7-28.2 52.7-58.8z" style="fill-rule:evenodd;clip-rule:evenodd;fill:#0089db"/></g></svg>

Before

Width:  |  Height:  |  Size: 1009 B

View File

@ -1,12 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_35_159)">
<path d="M9.85708 20C15.301 20 19.7142 15.5228 19.7142 10C19.7142 4.47715 15.301 0 9.85708 0C4.41317 0 0 4.47715 0 10C0 15.5228 4.41317 20 9.85708 20Z" fill="white"/>
<path d="M6.98406 12.6578C6.39713 12.6578 5.91626 12.5177 5.54142 12.2375C5.1715 11.9523 4.98654 11.547 4.98654 11.0216C4.98654 10.9116 4.99888 10.7764 5.02353 10.6163C5.08768 10.2561 5.17891 9.82324 5.29728 9.31788C5.63265 7.94188 6.49826 7.25388 7.89407 7.25388C8.27384 7.25388 8.61415 7.31892 8.91502 7.449C9.2159 7.57408 9.45263 7.76424 9.62525 8.01944C9.79786 8.2696 9.88421 8.56984 9.88421 8.92008C9.88421 9.02516 9.87187 9.15776 9.84719 9.31788C9.77322 9.7632 9.68443 10.196 9.58085 10.6163C9.40823 11.3018 9.10984 11.8147 8.68567 12.155C8.2615 12.4902 7.69432 12.6578 6.98406 12.6578ZM7.08764 11.577C7.36387 11.577 7.59812 11.4945 7.79049 11.3294C7.98779 11.1642 8.12835 10.9116 8.21217 10.5713C8.32561 10.101 8.41196 9.69064 8.47114 9.3404C8.49085 9.23532 8.50071 9.12776 8.50071 9.01764C8.50071 8.56232 8.26643 8.33464 7.7979 8.33464C7.52167 8.33464 7.28494 8.4172 7.08764 8.58232C6.89531 8.74748 6.75719 9.00016 6.67336 9.3404C6.58457 9.67564 6.49578 10.086 6.40703 10.5713C6.38727 10.6714 6.37742 10.7764 6.37742 10.8865C6.37742 11.3469 6.61418 11.577 7.08764 11.577Z" fill="#050813"/>
<path d="M10.2239 12.5828C10.1696 12.5828 10.1277 12.5653 10.0981 12.5302C10.0734 12.4902 10.0661 12.4452 10.0759 12.3952L11.0969 7.51656C11.1067 7.46152 11.1339 7.41648 11.1783 7.38144C11.2226 7.34644 11.2695 7.32892 11.3188 7.32892H13.2868C13.8342 7.32892 14.2732 7.444 14.6036 7.67416C14.939 7.90436 15.1067 8.23708 15.1067 8.6724C15.1067 8.79748 15.0919 8.9276 15.0623 9.06268C14.939 9.63812 14.69 10.0634 14.3151 10.3386C13.9452 10.6138 13.4372 10.7514 12.7911 10.7514H11.7923L11.452 12.3952C11.4421 12.4502 11.415 12.4952 11.3706 12.5302C11.3262 12.5653 11.2793 12.5828 11.2301 12.5828H10.2239ZM12.8429 9.71568C13.05 9.71568 13.23 9.65812 13.3829 9.54304C13.5408 9.42796 13.6443 9.26284 13.6937 9.04768C13.7084 8.9626 13.7159 8.88756 13.7159 8.82252C13.7159 8.6774 13.6739 8.56732 13.5901 8.49228C13.5062 8.4122 13.3632 8.3722 13.161 8.3722H12.2732L11.9921 9.71568H12.8429Z" fill="#050813"/>
</g>
<defs>
<clipPath id="clip0_35_159">
<rect width="19.7142" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 38.4 33.5" style="enable-background:new 0 0 38.4 33.5" xml:space="preserve"><path d="M29 10.2c-.7-.4-1.6-.4-2.4 0L21 13.5l-3.8 2.1-5.5 3.3c-.7.4-1.6.4-2.4 0L5 16.3c-.7-.4-1.2-1.2-1.2-2.1v-5c0-.8.4-1.6 1.2-2.1l4.3-2.5c.7-.4 1.6-.4 2.4 0L16 7.2c.7.4 1.2 1.2 1.2 2.1v3.3l3.8-2.2V7c0-.8-.4-1.6-1.2-2.1l-8-4.7c-.7-.4-1.6-.4-2.4 0L1.2 5C.4 5.4 0 6.2 0 7v9.4c0 .8.4 1.6 1.2 2.1l8.1 4.7c.7.4 1.6.4 2.4 0l5.5-3.2 3.8-2.2 5.5-3.2c.7-.4 1.6-.4 2.4 0l4.3 2.5c.7.4 1.2 1.2 1.2 2.1v5c0 .8-.4 1.6-1.2 2.1L29 28.8c-.7.4-1.6.4-2.4 0l-4.3-2.5c-.7-.4-1.2-1.2-1.2-2.1V21l-3.8 2.2v3.3c0 .8.4 1.6 1.2 2.1l8.1 4.7c.7.4 1.6.4 2.4 0l8.1-4.7c.7-.4 1.2-1.2 1.2-2.1V17c0-.8-.4-1.6-1.2-2.1L29 10.2z" style="fill:#8247e5"/></svg>

Before

Width:  |  Height:  |  Size: 752 B

View File

@ -1,7 +0,0 @@
<svg width="142" height="219" viewBox="0 0 142 219" xmlns="http://www.w3.org/2000/svg">
<path d="M80.4977 84.5341C85.0782 84.5361 89.5563 83.1775 93.3646 80.6305C97.1729 78.0835 100.14 74.4626 101.89 70.2267C103.64 65.9907 104.095 61.3302 103.196 56.8357C102.297 52.3411 100.085 48.2147 96.8402 44.9791C93.5956 41.7436 89.4644 39.5445 84.9699 38.6605C80.4753 37.7765 75.8197 38.2474 71.5928 40.0134C67.3658 41.7794 63.7578 44.7612 61.2258 48.5809C58.6938 52.4007 57.3517 56.8865 57.3696 61.4702C57.3987 67.5921 59.8473 73.4536 64.1806 77.775C68.514 82.0963 74.38 84.5262 80.4977 84.5341ZM80.4977 46.2581C83.4854 46.2562 86.4068 47.1399 88.893 48.7979C91.3793 50.4559 93.319 52.8138 94.4674 55.5739C95.6157 58.3341 95.9212 61.3728 95.3454 64.3066C94.7695 67.2403 93.3381 69.9376 91.2317 72.058C89.1254 74.1784 86.4385 75.6268 83.5102 76.2205C80.582 76.8143 77.5436 76.5266 74.7786 75.3939C72.0136 74.2612 69.6459 72.3342 67.9743 69.8561C66.3026 67.378 65.4021 64.46 65.3862 61.4702C65.3823 59.4809 65.7701 57.5103 66.5275 55.671C67.2849 53.8317 68.397 52.1599 69.8004 50.7509C71.2038 49.3419 72.8708 48.2234 74.7063 47.4594C76.5418 46.6954 78.5097 46.3008 80.4977 46.2982V46.2581Z" fill="#6633CC"/>
<path d="M80.4976 103.707C88.8576 103.707 97.0297 101.226 103.98 96.5772C110.93 91.9284 116.347 85.3211 119.544 77.5912C122.74 69.8613 123.574 61.3562 121.94 53.152C120.305 44.9477 116.276 37.413 110.361 31.501C104.446 25.589 96.9115 21.5655 88.711 19.9395C80.5105 18.3135 72.0122 19.1581 64.2915 22.3664C56.5708 25.5747 49.9745 31.0026 45.3371 37.9632C40.6998 44.9239 38.2299 53.1046 38.2398 61.4704V176.288C38.2398 177.352 38.6621 178.372 39.4138 179.125C40.1655 179.877 41.185 180.3 42.2481 180.3C43.3112 180.3 44.3307 179.877 45.0824 179.125C45.8341 178.372 46.2564 177.352 46.2564 176.288V86.1587C50.1678 91.5878 55.3117 96.0097 61.2646 99.0606C67.2175 102.111 73.8094 103.704 80.4976 103.707V103.707ZM80.4976 27.1653C87.2682 27.1653 93.8868 29.1739 99.5169 32.9372C105.147 36.7005 109.536 42.0497 112.129 48.3084C114.721 54.5672 115.402 61.4546 114.084 68.1002C112.766 74.7459 109.509 80.8514 104.724 85.6451C99.9392 90.4387 93.8418 93.7052 87.2023 95.0319C80.5628 96.3585 73.6793 95.6857 67.4218 93.0984C61.1644 90.5111 55.8139 86.1255 52.0466 80.496C48.2793 74.8664 46.2644 68.2456 46.2564 61.4704C46.2538 52.3782 49.8592 43.6572 56.2801 37.2243C62.701 30.7914 71.4118 27.1733 80.4976 27.1653V27.1653Z" fill="#6633CC"/>
<path d="M80.4975 0C64.2162 0.018598 48.6077 6.50246 37.0998 18.0278C25.5918 29.5531 19.1252 45.1776 19.1199 61.4703V214.514C19.1199 215.578 19.5422 216.598 20.2939 217.35C21.0456 218.103 22.0651 218.525 23.1282 218.525C24.1913 218.525 25.2108 218.103 25.9625 217.35C26.7142 216.598 27.1365 215.578 27.1365 214.514V61.4703C27.1365 50.9091 30.2661 40.5852 36.1295 31.804C41.9929 23.0227 50.3267 16.1786 60.0771 12.137C69.8276 8.09545 80.5567 7.03801 90.9077 9.09838C101.259 11.1588 110.767 16.2444 118.229 23.7122C125.692 31.1801 130.774 40.6947 132.833 51.0528C134.892 61.411 133.835 72.1475 129.797 81.9047C125.758 91.6619 118.918 100.002 110.143 105.869C101.368 111.736 91.0513 114.868 80.4975 114.868C79.4344 114.868 78.4149 115.291 77.6632 116.043C76.9115 116.795 76.4891 117.815 76.4891 118.879C76.4891 119.943 76.9115 120.963 77.6632 121.716C78.4149 122.468 79.4344 122.89 80.4975 122.89C96.7758 122.89 112.387 116.419 123.898 104.901C135.409 93.3823 141.875 77.7599 141.875 61.4703C141.875 45.1806 135.409 29.5582 123.898 18.0397C112.387 6.52115 96.7758 0.0501266 80.4975 0.0501266V0Z" fill="#6633CC"/>
<path d="M4.00833 76.512C2.94526 76.512 1.92575 76.9346 1.17404 77.6868C0.422333 78.439 0 79.4593 0 80.5231V157.065C0 158.129 0.422333 159.149 1.17404 159.901C1.92575 160.654 2.94526 161.076 4.00833 161.076C5.07141 161.076 6.09095 160.654 6.84266 159.901C7.59437 159.149 8.01667 158.129 8.01667 157.065V80.5231C8.01667 79.4593 7.59437 78.439 6.84266 77.6868C6.09095 76.9346 5.07141 76.512 4.00833 76.512Z" fill="#6633CC"/>
<path d="M61.3777 153.094C60.3146 153.094 59.2951 153.517 58.5434 154.269C57.7917 155.021 57.3694 156.041 57.3694 157.105V195.381C57.3694 196.445 57.7917 197.465 58.5434 198.217C59.2951 198.97 60.3146 199.392 61.3777 199.392C62.4408 199.392 63.4603 198.97 64.212 198.217C64.9638 197.465 65.3861 196.445 65.3861 195.381V157.105C65.3861 156.041 64.9638 155.021 64.212 154.269C63.4603 153.517 62.4408 153.094 61.3777 153.094Z" fill="#6633CC"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -1 +0,0 @@
<svg width="96" height="84" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M17.368 64.052A3.214 3.214 0 0 1 19.744 63l73.312.06a1.612 1.612 0 0 1 1.188 2.698l-15.612 17.19A3.213 3.213 0 0 1 76.254 84l-73.31-.06a1.611 1.611 0 0 1-1.188-2.698l15.612-17.19Zm76.876-14.31a1.611 1.611 0 0 1-1.188 2.698l-73.31.06a3.213 3.213 0 0 1-2.378-1.052l-15.612-17.2a1.612 1.612 0 0 1 1.188-2.698l73.312-.06a3.213 3.213 0 0 1 2.376 1.052l15.612 17.2ZM17.368 1.052A3.215 3.215 0 0 1 19.744 0l73.312.06a1.612 1.612 0 0 1 1.188 2.698l-15.612 17.19A3.213 3.213 0 0 1 76.254 21l-73.31-.06a1.611 1.611 0 0 1-1.188-2.698l15.612-17.19Z" fill="url(#b)"/></g><defs><linearGradient id="b" x1="4.168" y1="85.832" x2="91.832" y2="-1.832" gradientUnits="userSpaceOnUse"><stop stop-color="#9945FF"/><stop offset=".2" stop-color="#7962E7"/><stop offset="1" stop-color="#00D18C"/></linearGradient><clipPath id="a"><path fill="#fff" d="M0 0h96v84H0z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 1014 B

View File

@ -1,4 +0,0 @@
declare module "*.svg" {
const content: any;
export default content;
}

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 288.9 274" style="enable-background:new 0 0 288.9 274" xml:space="preserve"><path d="M151.1.3c33.7 0 64.9 12.1 88.7 32.9 31.8 24.5 22.6 113.9-9.6 90.3-70.8-.3-202.4-38.2-163.2-90.3 4-5.3 9-9.6 14.5-13.7h-.3c.9-.5 1.9-1 2.8-1.6.9-.5 1.9-1.1 2.8-1.6 2.8-1.6 5.6-3.1 8.7-4.3C112.5 4.6 131.3.3 151.1.3zm23.8 272.5c-14.2.9-42.6-21.4-50.7-50.9-15.1-55.9 107.2-84.4 118.7-85.4 31.2.9 38.9 38.2 16.1 76.7-29.7 49.4-83.5 59.6-84.1 59.6z" style="fill-rule:evenodd;clip-rule:evenodd;fill:#2849a9"/><path d="M14.8 77.9c9.9 2.8 70.5-16.5 88.4-43.8.3-.3 14.2-21.7-12.7-22-3.1 0-11.7.3-20.1 5.3-4 2.5-7.7 5-11.4 7.8-5.8 4.3-11.3 9.5-16.5 14.4l-.2.2c-5.3 5-10.2 10.9-14.5 16.8-4.3 5.9-8.3 12.4-11.7 18.9-.2.5-.4.9-.6 1.2-.3.3-.5.7-.7 1.2zm71.7 194.9c1.9-2.8 3.1-36.6 1.9-45.3-1.2-8.7-4-26.4-20.7-55.6-2.8-4.7-16.1-26.4-26-39.7-5.6-7.8-11.7-15-17.8-22.3-5.1-6-10.2-12.1-15-18.4-.3.8-.5 1.5-.8 2.2s-.5 1.4-.8 2.2C4.8 103 3 110.5 1.7 118.3S0 133.8 0 141.8c0 8.1.6 15.8 1.9 23.6s3.4 15.2 5.6 22.4c2.2 7.1 5.3 14.3 8.7 20.8s7.4 13 11.7 18.9c4.3 5.9 9.3 11.5 14.5 16.8 4.9 5.3 10.8 10.2 16.7 14.6 4.6 3.1 9.3 6.2 13.9 9 8.5 5 11.7 5 13.4 5 0-.1 0-.1.1-.1zm202.4-131c0 18.9-3.7 36.9-10.2 53.4-15.7 17-115.3-20.7-130.8-26.6-1.2-.5-2-.7-2-.8-15.8-6.8-63.3-27.9-67.7-60.8-6.2-47.5 89.6-80.7 131.9-82 4.9 0 20.4.3 29.4 7.5 30.3 26.7 49.4 65.9 49.4 109.3zM188.8 260.1c-3.7 12.1 10.2 16.5 22.6 10.6 24.7-13 45.1-33.2 59-57.1.9-1.2 0-2.5-1.5-2.2-13.3 1.2-73.3 25.1-80.1 48.7z" style="fill-rule:evenodd;clip-rule:evenodd;fill:#5795ed"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 52 49"><defs><linearGradient id="ebbb6cda-0389-45d8-bb1c-376d5bf36abc" x1="35.55" y1="48.97" x2="35.55" y2="24.51" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#4366c2"/><stop offset="0.15" stop-color="#3458b8"/><stop offset="0.4" stop-color="#2348ac"/><stop offset="0.67" stop-color="#193fa5"/><stop offset="1" stop-color="#163ca3"/></linearGradient><linearGradient id="f31efcdf-db45-41d1-9fdf-f0c98c440807" x1="28.73" y1="22.86" x2="28.73" y2="0" xlink:href="#ebbb6cda-0389-45d8-bb1c-376d5bf36abc"/><linearGradient id="fccc71bd-5025-4d54-b43c-83ee4283abe6" x1="2.68" y1="8.02" x2="19.28" y2="8.02" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#58c66b"/><stop offset="1" stop-color="#5491f6"/></linearGradient><linearGradient id="f353812a-3fef-4520-a39f-fc15f94407a0" x1="0" y1="32.68" x2="16.02" y2="32.68" xlink:href="#fccc71bd-5025-4d54-b43c-83ee4283abe6"/><linearGradient id="ae916250-479e-4f6a-ba90-eed93ba23120" x1="14.03" y1="20.12" x2="52" y2="20.12" xlink:href="#fccc71bd-5025-4d54-b43c-83ee4283abe6"/><linearGradient id="abdf6be7-d18d-4907-97fe-9b3e82537090" x1="33.83" y1="43.42" x2="48.69" y2="43.42" xlink:href="#fccc71bd-5025-4d54-b43c-83ee4283abe6"/></defs><g id="ae6f7ef0-565d-4a22-8f2c-325befffa09b" data-name="Layer 2"><g id="bcd3e4b3-41de-4e5f-b039-dc1868c624a4" data-name="Layer 6"><path d="M22.36,39.82c1.43,5.28,6.54,9.31,9.12,9.15.09,0,9.79-1.82,15.1-10.71,4.13-6.92,2.73-13.6-2.89-13.75-2,.15-24,5.25-21.33,15.31" fill="url(#ebbb6cda-0389-45d8-bb1c-376d5bf36abc)"/><path d="M43.16,5.9h0a24.63,24.63,0,0,0-26-3.76,14.3,14.3,0,0,0-1.57.77c-.34.18-.69.37-1,.57l.09,0A11.17,11.17,0,0,0,12.05,6C5,15.33,28.7,22.15,41.39,22.17,47.23,26.37,48.87,10.35,43.16,5.9Z" fill="url(#f31efcdf-db45-41d1-9fdf-f0c98c440807)"/><path d="M18.6,6.09C15.34,11,4.48,14.46,2.69,13.92v0l.22-.45A26.58,26.58,0,0,1,5,10,26.57,26.57,0,0,1,10.65,4.4,23.91,23.91,0,0,1,12.72,3a7.6,7.6,0,0,1,3.59-1c4.84.09,2.31,4,2.29,4" fill="url(#fccc71bd-5025-4d54-b43c-83ee4283abe6)"/><path d="M15.92,40.79c.24,1.54,0,7.63-.32,8.15-.27,0-.83.05-2.47-.87a24,24,0,0,1-2.48-1.62,26,26,0,0,1-3-2.6,25.13,25.13,0,0,1-2.6-3,25.44,25.44,0,0,1-3.69-7.15,25.27,25.27,0,0,1-1-4,26.42,26.42,0,0,1,0-8.45,25.81,25.81,0,0,1,1-4q.12-.39.27-.78h0c1.87,2.5,4,4.76,5.89,7.28s4.17,6.32,4.66,7.16c3,5.19,3.54,8.4,3.77,9.94" fill="url(#f353812a-3fef-4520-a39f-fc15f94407a0)"/><path d="M52,25.43A26.06,26.06,0,0,1,50.19,35c-3.06,3.29-23.71-4.82-23.91-4.91-2.83-1.24-11.42-5-12.2-10.94C13,10.62,30.23,4.68,37.82,4.45c.91,0,3.68,0,5.29,1.36A26,26,0,0,1,52,25.43" fill="url(#ae916250-479e-4f6a-ba90-eed93ba23120)"/><path d="M38,48.53c-2.25,1.06-4.73.29-4.08-1.91,1.23-4.21,12-8.54,14.41-8.77.29,0,.42.17.29.4A26.34,26.34,0,0,1,38,48.53" fill="url(#abdf6be7-d18d-4907-97fe-9b3e82537090)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,3 +0,0 @@
<svg width="1000" height="1000" viewBox="0 0 1000 1000" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M830.8 820.197C742.952 905.178 623.805 952.92 499.569 952.92C375.334 952.92 256.187 905.178 168.339 820.197C80.4911 735.217 31.1387 619.958 31.1387 499.777C31.1387 379.596 80.4911 264.338 168.339 179.357C256.187 94.3764 375.334 46.6348 499.569 46.6348C623.805 46.6348 742.952 94.3764 830.8 179.357C918.648 264.338 968 379.596 968 499.777C968 619.958 918.648 735.217 830.8 820.197ZM533.691 564.083L566.854 532.003L794.575 752.292L761.412 784.372L533.691 564.083ZM204.618 246.729L237.728 214.649L465.449 434.938L432.34 467.018L204.618 246.729ZM533.531 435.555L760.986 215.009L761.04 215.112L794.202 247.141L566.641 467.533L533.531 435.555ZM205.363 752.961L432.925 532.518L466.088 564.598L238.526 784.99L205.363 752.961Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 894 B

View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
<style type="text/css">
.st0{fill:#231F20;}
.st1{fill:#FFFFFF;}
.st2{fill:#FFFFFF;stroke:#231F20;stroke-miterlimit:10;}
</style>
<rect class="st0" width="40" height="40"/>
<path class="st1" d="M20,40c-5.3,0-10.4-2.1-14.1-5.9C2.1,30.4,0,25.3,0,20S2.1,9.6,5.9,5.9C9.6,2.1,14.7,0,20,0s10.4,2.1,14.1,5.9
C37.9,9.6,40,14.7,40,20s-2.1,10.4-5.9,14.1C30.4,37.9,25.3,40,20,40z M20,1.5c-4.9,0-9.6,1.9-13,5.4s-5.4,8.1-5.4,13s2,9.6,5.4,13
c3.5,3.5,8.2,5.4,13,5.4s9.6-1.9,13-5.4c3.5-3.5,5.4-8.1,5.4-13s-2-9.6-5.4-13S24.9,1.5,20,1.5L20,1.5z"/>
<path class="st1" d="M22.5,36.8c-4.4,0-8.6-1.7-11.7-4.9C7.7,28.8,6,24.6,6,20.2s1.8-8.6,4.9-11.7s7.3-4.8,11.7-4.9
c4.4,0,8.6,1.7,11.7,4.9c3.1,3.1,4.9,7.3,4.9,11.7s-1.8,8.6-4.9,11.7C31.1,35.1,26.9,36.8,22.5,36.8z M22.5,4.9
c-4.1,0-8,1.6-10.9,4.5s-4.5,6.8-4.5,10.9s1.6,8,4.5,10.9s6.8,4.5,10.9,4.5s8-1.6,10.9-4.5s4.5-6.8,4.5-10.9s-1.6-8-4.5-10.9
S26.6,4.9,22.5,4.9L22.5,4.9z"/>
<path class="st1" d="M25.1,33.6c-3.5,0-6.8-1.4-9.3-3.8c-2.5-2.5-3.8-5.8-3.9-9.3c0-3.5,1.4-6.8,3.9-9.3s5.8-3.8,9.3-3.8
s6.8,1.4,9.3,3.8c2.5,2.5,3.8,5.8,3.9,9.3c0,3.5-1.4,6.8-3.9,9.3S28.5,33.6,25.1,33.6L25.1,33.6z M25.1,8.2c-3.3,0-6.4,1.3-8.7,3.6
s-3.6,5.4-3.6,8.7s1.3,6.4,3.6,8.7s5.4,3.6,8.7,3.6s6.4-1.3,8.7-3.6s3.6-5.4,3.6-8.7s-1.3-6.4-3.6-8.7C31.4,9.5,28.3,8.2,25.1,8.2
L25.1,8.2z"/>
<path class="st1" d="M27.6,30.5c-2.6,0-5-1-6.9-2.8c-1.8-1.8-2.8-4.3-2.8-6.9s1-5,2.8-6.9c1.8-1.8,4.3-2.8,6.9-2.8s5,1,6.9,2.8
s2.8,4.3,2.8,6.9s-1,5-2.8,6.9C32.6,29.5,30.2,30.5,27.6,30.5L27.6,30.5z M27.6,11.6c-2.4,0-4.8,1-6.5,2.7c-1.7,1.7-2.7,4.1-2.7,6.5
s1,4.8,2.7,6.5s4.1,2.7,6.5,2.7s4.8-1,6.5-2.7c1.7-1.7,2.7-4.1,2.7-6.5s-1-4.8-2.7-6.5C32.4,12.6,30,11.6,27.6,11.6z"/>
<path class="st2" d="M17.2,32.9V15h5.7v17.9H17.2z M27.3,32.9v-26H33v26H27.3z M7,32.9V21.5h5.7v11.4H7z"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,16 +0,0 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
const root = ReactDOM.createRoot(document.getElementById("root") as any);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,13 +0,0 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@ -1,5 +0,0 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

View File

@ -1,70 +0,0 @@
import {
CHAIN_ID_ACALA,
CHAIN_ID_ALGORAND,
CHAIN_ID_APTOS,
CHAIN_ID_ARBITRUM,
CHAIN_ID_AURORA,
CHAIN_ID_AVAX,
CHAIN_ID_BSC,
CHAIN_ID_CELO,
CHAIN_ID_ETH,
CHAIN_ID_FANTOM,
CHAIN_ID_KARURA,
CHAIN_ID_KLAYTN,
CHAIN_ID_MOONBEAM,
CHAIN_ID_NEAR,
CHAIN_ID_OASIS,
CHAIN_ID_POLYGON,
CHAIN_ID_PYTHNET,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
CHAIN_ID_TERRA2,
CHAIN_ID_XPLA,
} from "@certusone/wormhole-sdk";
import acalaIcon from "../icons/acala.svg";
import algorandIcon from "../icons/algorand.svg";
import aptosIcon from "../icons/aptos.svg";
import arbitrumIcon from "../icons/arbitrum.svg";
import auroraIcon from "../icons/aurora.svg";
import avaxIcon from "../icons/avax.svg";
import bscIcon from "../icons/bsc.svg";
import celoIcon from "../icons/celo.svg";
import ethIcon from "../icons/eth.svg";
import fantomIcon from "../icons/fantom.svg";
import karuraIcon from "../icons/karura.svg";
import klaytnIcon from "../icons/klaytn.svg";
import moonbeamIcon from "../icons/moonbeam.svg";
import nearIcon from "../icons/near.svg";
import oasisIcon from "../icons/oasis.svg";
import pythnetIcon from "../icons/pythnet.svg";
import polygonIcon from "../icons/polygon.svg";
import solanaIcon from "../icons/solana.svg";
import terraIcon from "../icons/terra.svg";
import terra2Icon from "../icons/terra2.svg";
import xplaIcon from "../icons/xpla.svg";
const chainIdToIconMap: { [id: number]: string } = {
[CHAIN_ID_SOLANA]: solanaIcon,
[CHAIN_ID_ETH]: ethIcon,
[CHAIN_ID_TERRA]: terraIcon,
[CHAIN_ID_BSC]: bscIcon,
[CHAIN_ID_ACALA]: acalaIcon,
[CHAIN_ID_ALGORAND]: algorandIcon,
[CHAIN_ID_AURORA]: auroraIcon,
[CHAIN_ID_AVAX]: avaxIcon,
[CHAIN_ID_CELO]: celoIcon,
[CHAIN_ID_FANTOM]: fantomIcon,
[CHAIN_ID_TERRA2]: terra2Icon,
[CHAIN_ID_KARURA]: karuraIcon,
[CHAIN_ID_KLAYTN]: klaytnIcon,
[CHAIN_ID_MOONBEAM]: moonbeamIcon,
[CHAIN_ID_NEAR]: nearIcon,
[CHAIN_ID_OASIS]: oasisIcon,
[CHAIN_ID_POLYGON]: polygonIcon,
[CHAIN_ID_PYTHNET]: pythnetIcon,
[CHAIN_ID_APTOS]: aptosIcon,
[CHAIN_ID_XPLA]: xplaIcon,
[CHAIN_ID_ARBITRUM]: arbitrumIcon,
};
const chainIdToIcon = (chainId: number) => chainIdToIconMap[chainId] || "";
export default chainIdToIcon;

View File

@ -1,8 +0,0 @@
import { CHAINS } from "@certusone/wormhole-sdk";
const chainIdToNameMap = Object.fromEntries(
Object.entries(CHAINS).map(([key, value]) => [value, key])
);
const chainIdToName = (chainId: number) =>
chainIdToNameMap[chainId] || "Unknown";
export default chainIdToName;

View File

@ -1,300 +0,0 @@
import {
CHAIN_ID_SOLANA,
CHAIN_ID_ETH,
CHAIN_ID_TERRA,
CHAIN_ID_BSC,
CHAIN_ID_POLYGON,
CHAIN_ID_AVAX,
CHAIN_ID_OASIS,
CHAIN_ID_ALGORAND,
CHAIN_ID_AURORA,
CHAIN_ID_FANTOM,
CHAIN_ID_KARURA,
CHAIN_ID_ACALA,
CHAIN_ID_KLAYTN,
CHAIN_ID_CELO,
CHAIN_ID_TERRA2,
ChainId,
CHAIN_ID_NEAR,
CHAIN_ID_MOONBEAM,
CHAIN_ID_UNSET,
} from "@certusone/wormhole-sdk";
require("dotenv").config();
export const EXPECTED_GUARDIAN_COUNT = 19;
export const POLL_TIME = 60 * 1000;
export type CHAIN_INFO = {
name: string;
evm: boolean;
chainId: ChainId;
endpointUrl: any;
platform: string;
covalentChain: number;
explorerStem: string;
apiKey: string;
urlStem: string;
};
export const CHAIN_INFO_MAP: { [key: string]: CHAIN_INFO } = {
1: {
name: "solana",
evm: false,
chainId: CHAIN_ID_SOLANA,
urlStem: `https://public-api.solscan.io`,
endpointUrl:
process.env.REACT_APP_SOLANA_RPC || "https://api.mainnet-beta.solana.com",
apiKey: "",
platform: "solana",
covalentChain: 1399811149,
explorerStem: `https://solscan.io`,
},
2: {
name: "eth",
evm: true,
chainId: CHAIN_ID_ETH,
endpointUrl: process.env.REACT_APP_ETH_RPC || "https://rpc.ankr.com/eth",
apiKey: "",
urlStem: `https://api.etherscan.io`,
platform: "ethereum",
covalentChain: 1,
explorerStem: `https://etherscan.io`,
},
3: {
name: "terra",
evm: false,
chainId: CHAIN_ID_TERRA,
endpointUrl: "",
apiKey: "",
urlStem: "https://columbus-fcd.terra.dev",
platform: "terra",
covalentChain: 3,
explorerStem: `https://finder.terra.money/classic`,
},
4: {
name: "bsc",
evm: true,
chainId: CHAIN_ID_BSC,
endpointUrl:
process.env.REACT_APP_BSC_RPC || "https://bsc-dataseed2.defibit.io",
apiKey: "",
urlStem: `https://api.bscscan.com`,
platform: "binance-smart-chain",
covalentChain: 56,
explorerStem: `https://bscscan.com`,
},
5: {
name: "polygon",
evm: true,
chainId: CHAIN_ID_POLYGON,
endpointUrl: process.env.REACT_APP_POLYGON_RPC || "https://polygon-rpc.com",
apiKey: "",
urlStem: `https://api.polygonscan.com`,
platform: "polygon-pos", //coingecko?,
covalentChain: 137,
explorerStem: `https://polygonscan.com`,
},
6: {
name: "avalanche",
evm: true,
chainId: CHAIN_ID_AVAX,
endpointUrl:
process.env.REACT_APP_AVAX_RPC || "https://api.avax.network/ext/bc/C/rpc",
apiKey: "",
urlStem: `https://api.snowtrace.io`,
platform: "avalanche", //coingecko?
covalentChain: 43114,
explorerStem: `https://snowtrace.io`,
},
7: {
name: "oasis",
evm: true,
chainId: CHAIN_ID_OASIS,
endpointUrl: "https://emerald.oasis.dev",
apiKey: "",
urlStem: `https://explorer.emerald.oasis.dev`,
platform: "oasis", //coingecko?
covalentChain: 0,
explorerStem: `https://explorer.emerald.oasis.dev`,
},
8: {
name: "algorand",
evm: false,
chainId: CHAIN_ID_ALGORAND,
endpointUrl: "https://node.algoexplorerapi.io",
apiKey: "",
urlStem: `https://algoexplorer.io`,
platform: "algorand", //coingecko?
covalentChain: 0,
explorerStem: `https://algoexplorer.io`,
},
9: {
name: "aurora",
evm: true,
chainId: CHAIN_ID_AURORA,
endpointUrl: "https://mainnet.aurora.dev",
apiKey: "",
urlStem: `https://api.aurorascan.dev`, //?module=account&action=txlist&address={addressHash}
covalentChain: 1313161554,
platform: "aurora", //coingecko?
explorerStem: `https://aurorascan.dev`,
},
10: {
name: "fantom",
evm: true,
chainId: CHAIN_ID_FANTOM,
endpointUrl: "https://rpc.ftm.tools",
apiKey: "",
urlStem: `https://api.FtmScan.com`,
platform: "fantom", //coingecko?
covalentChain: 250,
explorerStem: `https://ftmscan.com`,
},
11: {
name: "karura",
evm: true,
chainId: CHAIN_ID_KARURA,
endpointUrl: "https://eth-rpc-karura.aca-api.network",
apiKey: "",
urlStem: `https://blockscout.karura.network`,
platform: "karura", //coingecko?
covalentChain: 0,
explorerStem: `https://blockscout.karura.network`,
},
12: {
name: "acala",
evm: true,
chainId: CHAIN_ID_ACALA,
endpointUrl: "https://eth-rpc-acala.aca-api.network",
apiKey: "",
urlStem: `https://blockscout.acala.network`,
platform: "acala", //coingecko?
covalentChain: 0,
explorerStem: `https://blockscout.acala.network`,
},
13: {
name: "klaytn",
evm: true,
chainId: CHAIN_ID_KLAYTN,
endpointUrl: "https://klaytn-mainnet-rpc.allthatnode.com:8551",
apiKey: "",
urlStem: "https://api-cypress-v2.scope.klaytn.com/v2" || "",
platform: "klay-token", //coingecko?
covalentChain: 8217,
explorerStem: `https://scope.klaytn.com`,
},
14: {
name: "celo",
evm: true,
chainId: CHAIN_ID_CELO,
endpointUrl: "https://forno.celo.org",
apiKey: "",
urlStem: `https://explorer.celo.org`,
platform: "celo", //coingecko?
covalentChain: 0,
explorerStem: `https://explorer.celo.org`,
},
15: {
name: "near",
evm: false,
chainId: CHAIN_ID_NEAR,
endpointUrl: "",
apiKey: "",
urlStem: `https://explorer.near.org`,
platform: "near", //coingecko?
covalentChain: 0,
explorerStem: `https://explorer.near.org`,
},
16: {
name: "moonbeam",
evm: true,
chainId: CHAIN_ID_MOONBEAM,
endpointUrl: "https://rpc.ankr.com/moonbeam",
apiKey: "",
urlStem: `https://api-moonbeam.moonscan.io`,
platform: "moonbeam", //coingecko?
covalentChain: 0,
explorerStem: `https://moonscan.io/`,
},
18: {
name: "terra2",
evm: false,
chainId: CHAIN_ID_TERRA2,
endpointUrl: "",
apiKey: "",
urlStem: "https://phoenix-fcd.terra.dev",
platform: "terra",
covalentChain: 3,
explorerStem: `https://finder.terra.money/mainnet`,
},
};
export const WORMHOLE_RPC_HOSTS = [
"https://wormhole-v2-mainnet-api.certus.one",
"https://wormhole.inotel.ro",
"https://wormhole-v2-mainnet-api.mcf.rocks",
"https://wormhole-v2-mainnet-api.chainlayer.network",
"https://wormhole-v2-mainnet-api.staking.fund",
"https://wormhole-v2-mainnet.01node.com",
];
export const CHAIN_ID_MAP = {
"1": CHAIN_ID_SOLANA,
"2": CHAIN_ID_ETH,
"3": CHAIN_ID_TERRA,
"4": CHAIN_ID_BSC,
"5": CHAIN_ID_POLYGON,
"6": CHAIN_ID_AVAX,
"7": CHAIN_ID_OASIS,
"8": CHAIN_ID_ALGORAND,
"9": CHAIN_ID_AURORA,
"10": CHAIN_ID_FANTOM,
"11": CHAIN_ID_KARURA,
"12": CHAIN_ID_ACALA,
"13": CHAIN_ID_KLAYTN,
"14": CHAIN_ID_CELO,
"15": CHAIN_ID_NEAR,
"16": CHAIN_ID_MOONBEAM,
"18": CHAIN_ID_TERRA2,
};
export function findChainId(chain: string) {
if (chain === "1") {
return CHAIN_ID_SOLANA;
} else if (chain === "2") {
return CHAIN_ID_ETH;
} else if (chain === "3") {
return CHAIN_ID_TERRA;
} else if (chain === "4") {
return CHAIN_ID_BSC;
} else if (chain === "5") {
return CHAIN_ID_POLYGON;
} else if (chain === "6") {
return CHAIN_ID_AVAX;
} else if (chain === "7") {
return CHAIN_ID_OASIS;
} else if (chain === "8") {
return CHAIN_ID_ALGORAND;
} else if (chain === "9") {
return CHAIN_ID_AURORA;
} else if (chain === "10") {
return CHAIN_ID_FANTOM;
} else if (chain === "11") {
return CHAIN_ID_KARURA;
} else if (chain === "12") {
return CHAIN_ID_ACALA;
} else if (chain === "13") {
return CHAIN_ID_KLAYTN;
} else if (chain === "14") {
return CHAIN_ID_CELO;
} else if (chain === "15") {
return CHAIN_ID_NEAR;
} else if (chain === "16") {
return CHAIN_ID_MOONBEAM;
} else if (chain === "18") {
return CHAIN_ID_TERRA2;
} else {
return CHAIN_ID_UNSET;
}
}

View File

@ -1,9 +0,0 @@
import { publicrpc } from "@certusone/wormhole-sdk-proto-web";
import { Network } from "../contexts/NetworkContext";
const { GrpcWebImpl, PublicRPCServiceClientImpl } = publicrpc;
export async function getGovernorAvailableNotionalByChain(network: Network) {
const rpc = new GrpcWebImpl(network.endpoint, {});
const api = new PublicRPCServiceClientImpl(rpc);
return await api.GovernorGetAvailableNotionalByChain({});
}

View File

@ -1,9 +0,0 @@
import { publicrpc } from "@certusone/wormhole-sdk-proto-web";
import { Network } from "../contexts/NetworkContext";
const { GrpcWebImpl, PublicRPCServiceClientImpl } = publicrpc;
export async function getGovernorEnqueuedVAAs(network: Network) {
const rpc = new GrpcWebImpl(network.endpoint, {});
const api = new PublicRPCServiceClientImpl(rpc);
return await api.GovernorGetEnqueuedVAAs({});
}

View File

@ -1,9 +0,0 @@
import { publicrpc } from "@certusone/wormhole-sdk-proto-web";
import { Network } from "../contexts/NetworkContext";
const { GrpcWebImpl, PublicRPCServiceClientImpl } = publicrpc;
export async function getGovernorTokenList(network: Network) {
const rpc = new GrpcWebImpl(network.endpoint, {});
const api = new PublicRPCServiceClientImpl(rpc);
return await api.GovernorGetTokenList({});
}

Some files were not shown because too many files have changed in this diff Show More