Skip to content

Commit

Permalink
fead: add bonded token for sui
Browse files Browse the repository at this point in the history
  • Loading branch information
icfor committed Nov 29, 2023
1 parent 3f0f8f0 commit 2d08b90
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 28 deletions.
2 changes: 2 additions & 0 deletions .env.defaults
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
MAX_OVER_TIME_DURATION=2d
NODE_ENV=development
PROM_QUERY_URL=http://localhost:9090

RADIX_URL=...
DEVTOOLS_API_KEY=...
4 changes: 3 additions & 1 deletion nodemon.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{
"exec": "ts-node --files --project tsconfig.json src/index.ts"
"exec": "ts-node --files --project tsconfig.json src/index.ts",
"ext": "js, ts",
"watch": ["src/"]
}
29 changes: 29 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"dependencies": {
"@apollo/datasource-rest": "^6.2.2",
"@apollo/server": "^4.0.2",
"@apollo/server-plugin-response-cache": "^4.1.3",
"body-parser": "^1.20.1",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
Expand Down
15 changes: 12 additions & 3 deletions src/graphql/resolvers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,11 +400,9 @@ export const resolvers = {

let validator_address: string | undefined = "";
let TVL = 0;
result.map((res, i: number) => {
result.forEach((res, i: number) => {
TVL += parseInt(res.value[1]);
if (i === 1) ({ validator_address } = res.metric);

return { validator_address, TVL };
});

return { metric: { validator_address, instance: "solana" }, TVL };
Expand Down Expand Up @@ -545,5 +543,16 @@ export const resolvers = {
usersCount: res.value[1],
}));
},
suiBondedToken: async (
_: unknown,
__: unknown,
{ dataSources }: ContextValue,
) => {
const response = await dataSources.suiAPI.getSuiBondedToken();

if (response.status === "error") return;

return response.data;
},
},
};
11 changes: 10 additions & 1 deletion src/graphql/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,14 @@ import { OasisAPI } from "./oasis-api";
import { RadixAPI } from "./radix-api";
import { RadixPromAPI } from "./radix-prom-api";
import { SolanaAPI } from "./solana-api";
import { SuiAPI } from "./sui-api";

export { CosmosAPI, ElrondAPI, RadixAPI, SolanaAPI, OasisAPI, RadixPromAPI };
export {
CosmosAPI,
ElrondAPI,
OasisAPI,
RadixAPI,
RadixPromAPI,
SolanaAPI,
SuiAPI,
};
81 changes: 81 additions & 0 deletions src/graphql/routes/sui-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { RESTDataSource } from "@apollo/datasource-rest";
// KeyValueCache is the type of Apollo server's default cache
import type { KeyValueCache } from "@apollo/utils.keyvaluecache";

// eslint-disable-next-line @typescript-eslint/no-var-requires
require("dotenv").config();

if (!process.env.DEVTOOLS_API_KEY) {
throw new Error("DEVTOOLS_API_KEY is not set");
}

// https://suiexplorer.com/validator/0x1e1985024aafe50a8e4eafc5a89eb7ecd58ba08c39f37688bee00bd55c8b2059
const validatorAddress =
"0x1e1985024aafe50a8e4eafc5a89eb7ecd58ba08c39f37688bee00bd55c8b2059";

// https://docs.sui.io/
export class SuiAPI extends RESTDataSource {
override baseURL = `https://rpc-mainnet-sui.forbole.com`;

constructor(options: { cache: KeyValueCache }) {
super(options); // this sends our server's `cache` through
}

private getRequestContent(body: unknown) {
return {
headers: {
"apikey": `${process.env.DEVTOOLS_API_KEY}`,
"content-type": "application/json",
},
body: JSON.stringify(body),
};
}

async getSuiBondedToken() {
const [response, coinData] = await Promise.all([
this.post(
`/`,
this.getRequestContent({
id: 1,
jsonrpc: "2.0",
// This is the same method used by the official explorer
method: "suix_getLatestSuiSystemState",
params: [],
}),
),
this.post(
`/`,
this.getRequestContent({
id: 1,
jsonrpc: "2.0",
method: "suix_getCoinMetadata",
params: ["0x2::sui::SUI"],
}),
),
]);

const validator = response.result.activeValidators.find(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(v: any) => v.suiAddress === validatorAddress,
);

const decimals = coinData?.result?.decimals;
const stakingPoolSuiBalance = validator?.stakingPoolSuiBalance;

if (!decimals || !stakingPoolSuiBalance) {
return {
status: "error",
};
}

// https://github.com/MystenLabs/sui/blob/cdcfa76c4304caff2496e7b9ba535086704585d5/apps/explorer/src/components/validator/ValidatorStats.tsx#L31
const bondedToken = validator?.stakingPoolSuiBalance / 10 ** decimals;

return {
status: "success",
data: {
bondedToken,
},
};
}
}
52 changes: 34 additions & 18 deletions src/graphql/typedefs/index.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,49 @@
// The GraphQL schema
export const typeDefs = `#graphql
enum CacheControlScope {
PUBLIC
PRIVATE
}
directive @cacheControl(
maxAge: Int
scope: CacheControlScope
inheritMaxAge: Boolean
) on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
type Query {
cosmosUsersCount: [UsersCountResult]
eachCosmosTVL: [EachCosmosTVLResult]
allCosmosTVL: [CosmosTVLResult]
allRadixStakedTokens: [RadixStakedTokensResult]
allRadixTotalSupply: RadixTokenSupplyResult
cosmosUsersCount: [UsersCountResult]
eachCosmosAPY: [EachCosmosAPYResult]
eachCosmosBondedToken: [EachCosmosBondedTokenResult]
eachCosmosCommission: [EachCosmosCommissionResult]
eachCosmosUnbondingTime: [EachCosmosUnbondingTimeResult]
eachCosmosAPY: [EachCosmosAPYResult]
eachCosmosTokenSupply: [EachCosmosTokenSupplyResult]
eachCosmosInflationRate: [EachCosmosInflationRateResult]
allRadixStakedTokens: [RadixStakedTokensResult]
allRadixTotalSupply: RadixTokenSupplyResult
radixUnbondingTime: RadixUnbondingTimeResult
eachCosmosTVL: [EachCosmosTVLResult]
eachCosmosTokenSupply: [EachCosmosTokenSupplyResult]
eachCosmosUnbondingTime: [EachCosmosUnbondingTimeResult]
elrondAPY: [ElrondAPYResult]
elrondTVL: [ElrondTVLResult]
elrondCommission: [ElrondCommissionResult]
elrondBondedToken: [ElrondBondedTokenResult]
elrondTotalSupply: [ElrondTotalSupplyResult]
elrondCirculatingSupply: [ElrondCirculatingSupplyResult]
elrondUsers: [ElrondUsersResult]
elrondCommission: [ElrondCommissionResult]
elrondTVL: [ElrondTVLResult]
elrondTotalSupply: [ElrondTotalSupplyResult]
elrondUnbondingTime: ElrondUnbondingTimeResult
solanaUsers: SolanaUsersResult
solanaBondedToken: SolanaBondedTokenResult
solanaTVL: SolanaTVLResult
solanaCommission: SolanaCommissionResult
solanaUnbondingTime: SolanaUnbondingTimeResult
oasisUsers: [OasisUsersResult]
elrondUsers: [ElrondUsersResult]
oasisBondedToken: [OasisBondedTokenResult]
oasisCommission: [OasisCommissionResult]
oasisTVL: [OasisTVLResult]
oasisUsers: [OasisUsersResult]
radixTVL: [RadixTVLResult]
radixUnbondingTime: RadixUnbondingTimeResult
radixUsers: [RadixUsersResult]
solanaBondedToken: SolanaBondedTokenResult
solanaCommission: SolanaCommissionResult
solanaTVL: SolanaTVLResult
solanaUnbondingTime: SolanaUnbondingTimeResult
solanaUsers: SolanaUsersResult
suiBondedToken: SuiBondedTokenResult
}
type UsersCountResult {
Expand Down Expand Up @@ -174,6 +186,10 @@ export const typeDefs = `#graphql
TVL: String
}
type SuiBondedTokenResult @cacheControl(maxAge: 3600) {
bondedToken: String
}
type SolanaCommissionResult {
metric: AddressAndInstanceMetric
commissionRate: String
Expand Down
6 changes: 4 additions & 2 deletions src/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import type {
RadixAPI,
RadixPromAPI,
SolanaAPI,
SuiAPI,
} from "./routes";

type DataSources = {
cosmosAPI: CosmosAPI;
radixAPI: RadixAPI;
elrondAPI: ElrondAPI;
solanaAPI: SolanaAPI;
oasisAPI: OasisAPI;
radixAPI: RadixAPI;
radixPromAPI: RadixPromAPI;
solanaAPI: SolanaAPI;
suiAPI: SuiAPI;
};

export interface ContextValue {
Expand Down
12 changes: 9 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ApolloServer } from "@apollo/server";
import responseCachePlugin from "@apollo/server-plugin-response-cache";
import { expressMiddleware } from "@apollo/server/express4";
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
import bodyParser from "body-parser";
Expand All @@ -15,6 +16,7 @@ import {
RadixAPI,
RadixPromAPI,
SolanaAPI,
SuiAPI,
} from "./graphql/routes";
import { typeDefs } from "./graphql/typedefs";
import type { ContextValue } from "./graphql/types";
Expand All @@ -29,7 +31,10 @@ require("dotenv").config();
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
responseCachePlugin(),
],
});
await server.start();

Expand All @@ -48,11 +53,12 @@ require("dotenv").config();
const context: ContextValue = {
dataSources: {
cosmosAPI: new CosmosAPI({ cache }),
radixAPI: new RadixAPI({ cache }),
elrondAPI: new ElrondAPI({ cache }),
solanaAPI: new SolanaAPI({ cache }),
oasisAPI: new OasisAPI({ cache }),
radixAPI: new RadixAPI({ cache }),
radixPromAPI: new RadixPromAPI({ cache }),
solanaAPI: new SolanaAPI({ cache }),
suiAPI: new SuiAPI({ cache }),
},
};

Expand Down

0 comments on commit 2d08b90

Please sign in to comment.