Skip to content

Commit

Permalink
Get nervous system data from the Registry (#4575)
Browse files Browse the repository at this point in the history
  • Loading branch information
hpeebles authored Oct 13, 2023
1 parent 2cab295 commit 0b60bb1
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 103 deletions.
3 changes: 1 addition & 2 deletions frontend/app/src/components/home/AccessGateIcon.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
const dispatch = createEventDispatcher();
$: params = formatParams(gate);
$: cryptoLookup = client.cryptoLookup;
$: tokenDetails = client.getTokenDetailsForSnsAccessGate(gate, $cryptoLookup);
$: tokenDetails = client.getTokenDetailsForSnsAccessGate(gate);
function formatParams(gate: AccessGate): string {
const parts = [];
Expand Down
3 changes: 1 addition & 2 deletions frontend/app/src/components/home/AccessGateParameters.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
const client = getContext<OpenChat>("client");
export let gate: SNSAccessGate | CredentialGate;
$: cryptoLookup = client.cryptoLookup;
$: tokenDetails = client.getTokenDetailsForSnsAccessGate(gate, $cryptoLookup);
$: tokenDetails = client.getTokenDetailsForSnsAccessGate(gate);
</script>

{#if gate.kind === "credential_gate"}
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/src/components/home/VisibilityControl.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
}
function snsHolderParams(gate: SNSAccessGate): InterpolationValues {
const tokenDetails = client.getTokenDetailsForSnsAccessGate(gate, $cryptoLookup);
const tokenDetails = client.getTokenDetailsForSnsAccessGate(gate);
return tokenDetails ? { token: tokenDetails.symbol } : undefined;
}
</script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
let showPayload = false;
$: tokenDetails = client.getTokenByGovernanceCanister(content.governanceCanisterId);
$: rootCanister = tokenDetails.rootCanister ?? "";
$: rootCanister = tokenDetails.nervousSystem?.rootCanisterId ?? "";
$: proposalTopicsStore = client.proposalTopicsStore;
$: isNns = content.proposal.kind === "nns";
$: voteStatus =
Expand Down
26 changes: 16 additions & 10 deletions frontend/app/src/utils/access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,22 @@ export type GateBinding = {
};

function getSnsGateBindings(cryptoLookup: Record<string, CryptocurrencyDetails>): GateBinding[] {
return Object.values(cryptoLookup).map((v) => {
return {
label: "access.snsHolder",
gate: { kind: "sns_gate", governanceCanister: v.governanceCanister! },
key: "sns_gate",
enabled: true,
cssClass: v.symbol.toLowerCase(),
labelParams: { token: v.symbol },
};
});
return Object.values(cryptoLookup).reduce((gates, next) => {
if (next.nervousSystem !== undefined) {
gates.push({
label: "access.snsHolder",
gate: {
kind: "sns_gate",
governanceCanister: next.nervousSystem.governanceCanisterId,
},
key: "sns_gate",
enabled: true,
cssClass: next.symbol.toLowerCase(),
labelParams: { token: next.symbol },
});
}
return gates;
}, [] as GateBinding[]);
}

const noGate: GateBinding = {
Expand Down
4 changes: 2 additions & 2 deletions frontend/openchat-agent/src/services/openchatAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2481,12 +2481,12 @@ export class OpenChatAgent extends EventTarget {

const updates = await this._registryClient.updates(current?.lastUpdated);

if (updates.kind === "success" && updates.tokenDetails !== undefined) {
if (updates.kind === "success") {
const updated = {
lastUpdated: updates.lastUpdated,
tokenDetails: distinctBy(
[...updates.tokenDetails, ...(current?.tokenDetails ?? [])],
(t) => t.ledgerCanisterId,
(t) => t.ledger,
),
nervousSystemDetails: distinctBy(
[...updates.nervousSystemDetails, ...(current?.nervousSystemDetails ?? [])],
Expand Down
9 changes: 3 additions & 6 deletions frontend/openchat-agent/src/services/registry/candid/idl.d.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import type { IDL } from "@dfinity/candid";
import {
TokenDetails,
UpdatesResponse,
_SERVICE
} from "./types";
import { NervousSystemSummary, TokenDetails, UpdatesResponse, _SERVICE } from "./types";
export {
NervousSystemSummary as ApiNervousSystemSummary,
TokenDetails as ApiTokenDetails,
UpdatesResponse as ApiUpdatesResponse,
_SERVICE as RegistryService
_SERVICE as RegistryService,
};

export const idlFactory: IDL.InterfaceFactory;
43 changes: 25 additions & 18 deletions frontend/openchat-agent/src/services/registry/mappers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { ApiTokenDetails, ApiUpdatesResponse } from "./candid/idl";
import type { RegistryUpdatesResponse, TokenDetails } from "openchat-shared";
import type { ApiNervousSystemSummary, ApiTokenDetails, ApiUpdatesResponse } from "./candid/idl";
import type {
NervousSystemSummary,
RegistryUpdatesResponse,
RegistryTokenDetails,
} from "openchat-shared";
import { optional } from "../../utils/mapping";
import { UnsupportedValueError } from "openchat-shared";

Expand All @@ -8,15 +12,11 @@ export function updatesResponse(candid: ApiUpdatesResponse): RegistryUpdatesResp
return {
kind: "success",
lastUpdated: candid.Success.last_updated,
tokenDetails: optional(candid.Success.token_details, (t) => t.map(tokenDetails)) ?? [],
nervousSystemDetails: candid.Success.nervous_system_details.map((ns) => ({
rootCanisterId: ns.root_canister_id.toString(),
governanceCanisterId: ns.governance_canister_id.toString(),
ledgerCanisterId: ns.ledger_canister_id.toString(),
isNns: ns.is_nns,
proposalRejectionFee: ns.proposal_rejection_fee,
submittingProposalsEnabled: ns.submitting_proposals_enabled,
})),
tokenDetails:
optional(candid.Success.token_details, (tokens) =>
tokens.map((t) => tokenDetails(t)),
) ?? [],
nervousSystemDetails: candid.Success.nervous_system_details.map(nervousSystemSummary),
};
}
if ("SuccessNoUpdates" in candid) {
Expand All @@ -27,22 +27,29 @@ export function updatesResponse(candid: ApiUpdatesResponse): RegistryUpdatesResp
throw new UnsupportedValueError("Unexpected ApiUpdatesResponse type received", candid);
}

function tokenDetails(candid: ApiTokenDetails): TokenDetails {
function tokenDetails(candid: ApiTokenDetails): RegistryTokenDetails {
return {
ledgerCanisterId: candid.ledger_canister_id.toString(),
ledger: candid.ledger_canister_id.toString(),
name: candid.name,
symbol: candid.symbol,
decimals: candid.decimals,
fee: candid.fee,
transferFee: candid.fee,
logo: candid.logo,
nervousSystem: optional(candid.nervous_system, (ns) => ({
root: ns.root.toString(),
governance: ns.governance.toString(),
})),
infoUrl: candid.info_url,
howToBuyUrl: candid.how_to_buy_url,
transactionUrlFormat: candid.transaction_url_format,
added: candid.added,
lastUpdated: candid.last_updated,
};
}

function nervousSystemSummary(candid: ApiNervousSystemSummary): NervousSystemSummary {
return {
rootCanisterId: candid.root_canister_id.toString(),
governanceCanisterId: candid.governance_canister_id.toString(),
ledgerCanisterId: candid.ledger_canister_id.toString(),
isNns: candid.is_nns,
proposalRejectionFee: candid.proposal_rejection_fee,
submittingProposalsEnabled: candid.submitting_proposals_enabled,
};
}
58 changes: 28 additions & 30 deletions frontend/openchat-client/src/openchat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ import {
} from "./utils/date";
import formatFileSize from "./utils/fileSize";
import { calculateMediaDimensions } from "./utils/layout";
import { findLast, groupBy, groupWhile, keepMax, toRecord2 } from "./utils/list";
import { findLast, groupBy, groupWhile, keepMax, toRecord, toRecord2 } from "./utils/list";
import {
audioRecordingMimeType,
containsSocialVideoLink,
Expand Down Expand Up @@ -2281,14 +2281,9 @@ export class OpenChat extends OpenChatAgentWorker {
return false;
}

getTokenDetailsForSnsAccessGate(
gate: AccessGate,
cryptoLookup: Record<string, CryptocurrencyDetails>,
): CryptocurrencyDetails | undefined {
getTokenDetailsForSnsAccessGate(gate: AccessGate): CryptocurrencyDetails | undefined {
if (gate.kind !== "sns_gate") return undefined;
return Object.values(cryptoLookup).find(
(td) => td.governanceCanister === gate.governanceCanister,
);
return this.tryGetTokenDetailsByGovernanceCanister(gate.governanceCanister);
}

getMinDissolveDelayDays(gate: AccessGate): number | undefined {
Expand Down Expand Up @@ -3989,9 +3984,7 @@ export class OpenChat extends OpenChatAgentWorker {
}

getTokenByGovernanceCanister(governanceCanister: string): CryptocurrencyDetails {
const tokenDetails = Object.values(get(cryptoLookup)).find(
(t) => t.governanceCanister === governanceCanister,
);
const tokenDetails = this.tryGetTokenDetailsByGovernanceCanister(governanceCanister);
if (tokenDetails === undefined) {
throw new Error(`Unknown governance canister: ${governanceCanister}`);
} else {
Expand Down Expand Up @@ -4917,32 +4910,37 @@ export class OpenChat extends OpenChatAgentWorker {
kind: "updateRegistry",
});

const nervousSystemLookup = toRecord(
registry.nervousSystemDetails,
(ns) => ns.ledgerCanisterId,
);

cryptoLookup.set(
toRecord2(
registry.tokenDetails,
(t) => t.ledgerCanisterId,
(t) => ({
name: t.name,
symbol: t.symbol,
ledger: t.ledgerCanisterId,
decimals: t.decimals,
transferFee: t.fee,
logo: t.logo,
howToBuyUrl: t.howToBuyUrl,
infoUrl: t.infoUrl,
transactionUrlFormat: t.transactionUrlFormat,
rootCanister: t.nervousSystem?.root,
governanceCanister: t.nervousSystem?.governance,
lastUpdated: t.lastUpdated,
}),
registry.tokenDetails.reduce(
(results, next) => {
results[next.ledger] = {
...next,
nervousSystem: nervousSystemLookup[next.ledger],
};
return results;
},
{} as Record<string, CryptocurrencyDetails>,
),
);
}

private getSnsLogo(governanceCanisterId: string): string | undefined {
return this.tryGetTokenDetailsByGovernanceCanister(governanceCanisterId)?.logo;
}

private tryGetTokenDetailsByGovernanceCanister(
governanceCanisterId: string,
): CryptocurrencyDetails | undefined {
return Object.values(get(cryptoLookup)).find(
(t) => t.governanceCanister === governanceCanisterId,
)?.logo;
(t) =>
t.nervousSystem !== undefined &&
t.nervousSystem.governanceCanisterId === governanceCanisterId,
);
}

// the key might be a username or it might be a user group name
Expand Down
13 changes: 11 additions & 2 deletions frontend/openchat-shared/src/domain/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,20 @@ export type CryptocurrencyDetails = {
howToBuyUrl: string;
infoUrl: string;
transactionUrlFormat: string;
governanceCanister: string | undefined;
rootCanister: string | undefined;
nervousSystem: NervousSystemSummary | undefined;
added: bigint;
lastUpdated: bigint;
};

export type NervousSystemSummary = {
rootCanisterId: string;
governanceCanisterId: string;
ledgerCanisterId: string;
isNns: boolean;
proposalRejectionFee: bigint;
submittingProposalsEnabled: boolean;
};

// approximate dollar exchange rates - until we come up with something better
const dollarToICP = 0.34;

Expand Down
35 changes: 6 additions & 29 deletions frontend/openchat-shared/src/domain/registry/index.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,24 @@
import type { CryptocurrencyDetails, NervousSystemSummary } from "../crypto";

export type RegistryUpdatesResponse =
| RegistryUpdatesResponseSuccess
| RegistryUpdatesResponseSuccessNoUpdates;

export type RegistryValue = {
lastUpdated: bigint;
tokenDetails: TokenDetails[];
tokenDetails: RegistryTokenDetails[];
nervousSystemDetails: NervousSystemSummary[];
};

export type RegistryUpdatesResponseSuccess = {
kind: "success";
lastUpdated: bigint;
tokenDetails: TokenDetails[];
tokenDetails: RegistryTokenDetails[];
nervousSystemDetails: NervousSystemSummary[];
};

export type RegistryTokenDetails = Omit<CryptocurrencyDetails, "nervousSystem">;

export type RegistryUpdatesResponseSuccessNoUpdates = {
kind: "success_no_updates";
};

export type TokenDetails = {
ledgerCanisterId: string;
name: string;
symbol: string;
decimals: number;
fee: bigint;
logo: string;
nervousSystem?: {
root: string;
governance: string;
};
infoUrl: string;
howToBuyUrl: string;
transactionUrlFormat: string;
added: bigint;
lastUpdated: bigint;
};

export type NervousSystemSummary = {
rootCanisterId: string;
governanceCanisterId: string;
ledgerCanisterId: string;
isNns: boolean;
proposalRejectionFee: bigint;
submittingProposalsEnabled: boolean;
};

0 comments on commit 0b60bb1

Please sign in to comment.