Skip to content

Commit

Permalink
feat: use TVL canister (#1953)
Browse files Browse the repository at this point in the history
# Motivation

Replace web2 calls to display TVL in $ with [TVL
canister](https://github.com/dfinity/ic/tree/master/rs/rosetta-api/tvl).

# Changes

- remove Binance and Governance `/metrics` rest API
- implement TVL canister
- add API and services to query TVL
- integrate TVL in worker and post message
- uses new TVL object in component
- update periodicity to one hour to not refresh the value dynamically
for now

# Screenshot

No visual UI changes.
  • Loading branch information
peterpeterparker authored Feb 28, 2023
1 parent e2748a6 commit 3c5dc7c
Show file tree
Hide file tree
Showing 26 changed files with 378 additions and 385 deletions.
1 change: 1 addition & 0 deletions frontend/jest-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jest.mock("./src/lib/constants/canister-ids.constants.ts", () => ({
CKBTC_LEDGER_CANISTER_ID: Principal.fromText("mc6ru-gyaaa-aaaar-qaaaq-cai"),
CKBTC_INDEX_CANISTER_ID: Principal.fromText("si2b5-pyaaa-aaaaa-aaaja-cai"),
CKBTC_UNIVERSE_CANISTER_ID: Principal.fromText("mc6ru-gyaaa-aaaar-qaaaq-cai"),
TVL_CANISTER_ID: Principal.fromText("ewh3f-3qaaa-aaaap-aazjq-cai"),
}));

jest.mock("./src/lib/constants/environment.constants.ts", () => ({
Expand Down
49 changes: 49 additions & 0 deletions frontend/src/lib/api/tvl.api.cjs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { TVLCanister } from "$lib/canisters/tvl/tvl.canister";
import type { TvlResult } from "$lib/canisters/tvl/tvl.types";
import { TVL_CANISTER_ID } from "$lib/constants/canister-ids.constants";
import { HOST_IC0_APP } from "$lib/constants/environment.constants";
import { logWithTimestamp } from "$lib/utils/dev.utils";
import type { Identity } from "@dfinity/agent";
import type { Principal } from "@dfinity/principal";
/**
* HTTP-Agent explicit CJS import for compatibility with web worker - avoid Error [RollupError]: Unexpected token (Note that you need plugins to import files that are not JavaScript)
*/
import { HttpAgent } from "@dfinity/agent/lib/cjs/index";

export const queryTVL = async ({
identity,
certified,
}: {
identity: Identity;
certified: boolean;
}): Promise<TvlResult> => {
logWithTimestamp(`Getting canister ${TVL_CANISTER_ID.toText()} TVL call...`);

const { getTVL } = await canister({ identity, canisterId: TVL_CANISTER_ID });

const result = getTVL({ certified });

logWithTimestamp(
`Getting canister ${TVL_CANISTER_ID.toText()} TVL complete.`
);

return result;
};

const canister = async ({
identity,
canisterId,
}: {
identity: Identity;
canisterId: Principal;
}): Promise<TVLCanister> => {
const agent = new HttpAgent({
identity,
host: HOST_IC0_APP,
});

return TVLCanister.create({
agent,
canisterId,
});
};
50 changes: 50 additions & 0 deletions frontend/src/lib/canisters/tvl/tvl.canister.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { TVLCanisterOptions } from "$lib/canisters/tvl/tvl.canister.types";
import { Actor } from "@dfinity/agent";
import { idlFactory as certifiedIdlFactory } from "./tvl.certified.idl";
import { idlFactory } from "./tvl.idl";
import type { TvlResult, _SERVICE as TVLService } from "./tvl.types";

export class TVLCanister {
private constructor(
private readonly service: TVLService,
private readonly certifiedService: TVLService
) {
this.service = service;
this.certifiedService = certifiedService;
}

public static create(options: TVLCanisterOptions) {
const agent = options.agent;
const canisterId = options.canisterId;

const service = Actor.createActor<TVLService>(idlFactory, {
agent,
canisterId,
});

const certifiedService = Actor.createActor<TVLService>(
certifiedIdlFactory,
{
agent,
canisterId,
}
);

return new TVLCanister(service, certifiedService);
}

private caller = ({ certified = true }: { certified: boolean }): TVLService =>
certified ? this.certifiedService : this.service;

public getTVL = async (params: {
certified: boolean;
}): Promise<TvlResult> => {
const response = await this.caller(params).get_tvl();

if ("Err" in response) {
throw new Error(response.Err.message);
}

return response.Ok;
};
}
9 changes: 9 additions & 0 deletions frontend/src/lib/canisters/tvl/tvl.canister.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Agent } from "@dfinity/agent";
import type { Principal } from "@dfinity/principal";

export interface TVLCanisterOptions {
// The agent to use when communicating with the governance canister.
agent: Agent;
// The TVL canister's ID.
canisterId: Principal;
}
2 changes: 2 additions & 0 deletions frontend/src/lib/canisters/tvl/tvl.certified.idl.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import type { IDL } from "@dfinity/candid";
export const idlFactory: IDL.InterfaceFactory;
33 changes: 33 additions & 0 deletions frontend/src/lib/canisters/tvl/tvl.certified.idl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* Do not edit. Compiled with ./scripts/compile-idl-js from packages/tvl/candid/tvl.did */
export const idlFactory = ({ IDL }) => {
const InitArgs = IDL.Record({
governance_id: IDL.Principal,
update_period: IDL.Nat64,
xrc_id: IDL.Principal,
});
const TimeseriesEntry = IDL.Record({
value: IDL.Nat,
time_sec: IDL.Nat,
});
const TimeseriesResult = IDL.Record({
timeseries: IDL.Vec(TimeseriesEntry),
});
const TvlResult = IDL.Record({ tvl: IDL.Nat, time_sec: IDL.Nat });
const TvlResultError = IDL.Record({ message: IDL.Text });
const Result_tvl = IDL.Variant({ Ok: TvlResult, Err: TvlResultError });
const TvlTimeseriesResult = IDL.Record({ timeseries: IDL.Vec(TvlResult) });
return IDL.Service({
get_locked_e8s_timeseries: IDL.Func([], [TimeseriesResult], []),
get_tvl: IDL.Func([], [Result_tvl], []),
get_tvl_timeseries: IDL.Func([], [TvlTimeseriesResult], []),
get_xr_timeseries: IDL.Func([], [TimeseriesResult], []),
});
};
export const init = ({ IDL }) => {
const InitArgs = IDL.Record({
governance_id: IDL.Principal,
update_period: IDL.Nat64,
xrc_id: IDL.Principal,
});
return [InitArgs];
};
36 changes: 36 additions & 0 deletions frontend/src/lib/canisters/tvl/tvl.did
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
type InitArgs = record {
governance_id : principal;
xrc_id : principal;
update_period: nat64;
};

type TimeseriesEntry = record {
time_sec: nat;
value: nat;
};

type TimeseriesResult = record {
timeseries: vec TimeseriesEntry;
};

type TvlResult = record {
time_sec: nat;
tvl: nat;
};

type TvlTimeseriesResult = record {
timeseries: vec TvlResult;
};

type TvlResultError = record {
message: text;
};

type Result_tvl = variant { Ok : TvlResult; Err : TvlResultError };

service : (InitArgs) -> {
get_tvl : () -> (Result_tvl) query;
get_tvl_timeseries : () -> (TvlTimeseriesResult);
get_xr_timeseries : () -> (TimeseriesResult);
get_locked_e8s_timeseries : () -> (TimeseriesResult);
}
2 changes: 2 additions & 0 deletions frontend/src/lib/canisters/tvl/tvl.idl.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import type { IDL } from "@dfinity/candid";
export const idlFactory: IDL.InterfaceFactory;
33 changes: 33 additions & 0 deletions frontend/src/lib/canisters/tvl/tvl.idl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* Do not edit. Compiled with ./scripts/compile-idl-js from packages/tvl/candid/tvl.did */
export const idlFactory = ({ IDL }) => {
const InitArgs = IDL.Record({
governance_id: IDL.Principal,
update_period: IDL.Nat64,
xrc_id: IDL.Principal,
});
const TimeseriesEntry = IDL.Record({
value: IDL.Nat,
time_sec: IDL.Nat,
});
const TimeseriesResult = IDL.Record({
timeseries: IDL.Vec(TimeseriesEntry),
});
const TvlResult = IDL.Record({ tvl: IDL.Nat, time_sec: IDL.Nat });
const TvlResultError = IDL.Record({ message: IDL.Text });
const Result_tvl = IDL.Variant({ Ok: TvlResult, Err: TvlResultError });
const TvlTimeseriesResult = IDL.Record({ timeseries: IDL.Vec(TvlResult) });
return IDL.Service({
get_locked_e8s_timeseries: IDL.Func([], [TimeseriesResult], []),
get_tvl: IDL.Func([], [Result_tvl], ["query"]),
get_tvl_timeseries: IDL.Func([], [TvlTimeseriesResult], []),
get_xr_timeseries: IDL.Func([], [TimeseriesResult], []),
});
};
export const init = ({ IDL }) => {
const InitArgs = IDL.Record({
governance_id: IDL.Principal,
update_period: IDL.Nat64,
xrc_id: IDL.Principal,
});
return [InitArgs];
};
32 changes: 32 additions & 0 deletions frontend/src/lib/canisters/tvl/tvl.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { ActorMethod } from "@dfinity/agent";
import type { Principal } from "@dfinity/principal";

export interface InitArgs {
governance_id: Principal;
update_period: bigint;
xrc_id: Principal;
}
export type Result_tvl = { Ok: TvlResult } | { Err: TvlResultError };
export interface TimeseriesEntry {
value: bigint;
time_sec: bigint;
}
export interface TimeseriesResult {
timeseries: Array<TimeseriesEntry>;
}
export interface TvlResult {
tvl: bigint;
time_sec: bigint;
}
export interface TvlResultError {
message: string;
}
export interface TvlTimeseriesResult {
timeseries: Array<TvlResult>;
}
export interface _SERVICE {
get_locked_e8s_timeseries: ActorMethod<[], TimeseriesResult>;
get_tvl: ActorMethod<[], Result_tvl>;
get_tvl_timeseries: ActorMethod<[], TvlTimeseriesResult>;
get_xr_timeseries: ActorMethod<[], TimeseriesResult>;
}
10 changes: 1 addition & 9 deletions frontend/src/lib/components/metrics/TotalValueLocked.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import { fade } from "svelte/transition";
import { nonNullish } from "@dfinity/utils";
import { metricsStore } from "$lib/stores/metrics.store";
import { E8S_PER_ICP } from "$lib/constants/icp.constants";
import { formatNumber } from "$lib/utils/format.utils";
export let layout: "inline" | "stacked" = "inline";
Expand All @@ -34,15 +33,8 @@
const syncMetrics = ({ metrics: data }: PostMessageDataResponse) =>
metricsStore.set(data);
let totalNeurons: number | undefined;
$: totalNeurons =
($metricsStore?.dissolvingNeurons?.totalDissolvingNeurons ?? 0) +
($metricsStore?.dissolvingNeurons?.totalNotDissolvingNeurons ?? 0);
let total: number | undefined;
$: total =
((totalNeurons ?? 0) / E8S_PER_ICP) *
Number($metricsStore?.avgPrice?.price ?? "0");
$: total = Number($metricsStore?.tvl?.tvl ?? "0");
const format = (n: number): string =>
formatNumber(n, {
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/lib/constants/canister-ids.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,9 @@ export const CKBTC_INDEX_CANISTER_ID = Principal.fromText(
: MAINNET_CKBTC_INDEX_CANISTER_ID
);
export const CKBTC_UNIVERSE_CANISTER_ID = CKBTC_LEDGER_CANISTER_ID;

// TVL Canister ID on mainnet. Use for readonly.

export const TVL_CANISTER_ID = Principal.fromText(
"ewh3f-3qaaa-aaaap-aazjq-cai"
);
2 changes: 2 additions & 0 deletions frontend/src/lib/constants/environment.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export const DEV = import.meta.env.DEV;
export const FETCH_ROOT_KEY: boolean =
import.meta.env.VITE_FETCH_ROOT_KEY === "true";

export const HOST_IC0_APP = "https://ic0.app";

// TODO: Add as env var https://dfinity.atlassian.net/browse/GIX-1245
// Local development needs `.raw` to avoid CORS issues for now.
// TODO: Fix CORS issues
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/lib/constants/metrics.constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { SECONDS_IN_MINUTE } from "$lib/constants/constants";

// Workers
export const SYNC_METRICS_TIMER_INTERVAL = SECONDS_IN_MINUTE * 1000; // 1 minute
// Workers periodicity
// 60 minutes - i.e. currently longer than a session therefore not refreshed
// We might revert this to a more dynamic data in the future e.g. every minute, that's why we keep the feature
export const SYNC_METRICS_TIMER_INTERVAL = SECONDS_IN_MINUTE * 60000;
31 changes: 0 additions & 31 deletions frontend/src/lib/rest/binance.rest.ts

This file was deleted.

23 changes: 0 additions & 23 deletions frontend/src/lib/rest/governance-metrics.rest.ts

This file was deleted.

Loading

0 comments on commit 3c5dc7c

Please sign in to comment.