Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Imported tokens services #5225

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions frontend/src/lib/api/canisters.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
} from "$lib/canisters/nns-dapp/nns-dapp.errors";
import type {
CanisterDetails as CanisterInfo,
ImportedToken,
ImportedTokens,
SubAccountArray,
} from "$lib/canisters/nns-dapp/nns-dapp.types";
import {
Expand Down Expand Up @@ -376,3 +378,33 @@ const canisters = async (

return { cmc, icMgt, nnsDapp };
};

export const getImportedTokens = async ({
identity,
}: {
identity: Identity;
}): Promise<ImportedTokens> => {
logWithTimestamp("Getting imported tokens call...");
const { nnsDapp } = await canisters(identity);

const importedTokens = await nnsDapp.getImportedTokens();

logWithTimestamp("Getting imported tokens call complete.");

return importedTokens;
};

export const setImportedTokens = async ({
identity,
importedTokens,
}: {
identity: Identity;
importedTokens: Array<ImportedToken>;
}): Promise<void> => {
logWithTimestamp("Setting imported tokens call...");
const { nnsDapp } = await canisters(identity);

await nnsDapp.setImportedTokens(importedTokens);

logWithTimestamp("Setting imported tokens call complete.");
};
34 changes: 34 additions & 0 deletions frontend/src/lib/canisters/nns-dapp/nns-dapp.canister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import type {
CanisterDetails,
CreateSubAccountResponse,
GetAccountResponse,
ImportedToken,
ImportedTokens,
RegisterHardwareWalletRequest,
RegisterHardwareWalletResponse,
RenameSubAccountRequest,
Expand Down Expand Up @@ -325,4 +327,36 @@ export class NNSDappCanister {
errorText ?? (nonNullish(response) ? JSON.stringify(response) : undefined)
);
}

public getImportedTokens = async (): Promise<ImportedTokens> => {
const response = await this.certifiedService.get_imported_tokens();
if ("Ok" in response) {
return response.Ok;
}
if ("AccountNotFound" in response) {
throw new AccountNotFoundError("error__account.not_found");
}
// Edge case
throw new Error(
`Error getting imported tokens ${JSON.stringify(response)}`
);
};

public setImportedTokens = async (
importedTokens: Array<ImportedToken>
): Promise<void> => {
const response = await this.certifiedService.set_imported_tokens({
imported_tokens: importedTokens,
});
if ("Ok" in response) {
return;
}
if ("AccountNotFound" in response) {
throw new AccountNotFoundError("error__account.not_found");
}
// Edge case
throw new Error(
`Error setting imported tokens ${JSON.stringify(response)}`
);
};
}
21 changes: 21 additions & 0 deletions frontend/src/lib/canisters/nns-dapp/nns-dapp.certified.idl.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ export const idlFactory = ({ IDL }) => {
name: IDL.Text,
canister_id: IDL.Principal,
});
const ImportedToken = IDL.Record({
index_canister_id: IDL.Opt(IDL.Principal),
ledger_canister_id: IDL.Principal,
});
const ImportedTokens = IDL.Record({
imported_tokens: IDL.Vec(ImportedToken),
});
const GetImportedTokensResponse = IDL.Variant({
Ok: ImportedTokens,
AccountNotFound: IDL.Null,
});
const BlockHeight = IDL.Nat64;
const NeuronId = IDL.Nat64;
const GetProposalPayloadResponse = IDL.Variant({
Expand Down Expand Up @@ -108,6 +119,10 @@ export const idlFactory = ({ IDL }) => {
SubAccountNotFound: IDL.Null,
NameTooLong: IDL.Null,
});
const SetImportedTokensResponse = IDL.Variant({
Ok: IDL.Null,
AccountNotFound: IDL.Null,
});
return IDL.Service({
add_account: IDL.Func([], [AccountIdentifier], []),
add_stable_asset: IDL.Func([IDL.Vec(IDL.Nat8)], [], []),
Expand All @@ -129,6 +144,7 @@ export const idlFactory = ({ IDL }) => {
),
get_account: IDL.Func([], [GetAccountResponse], []),
get_canisters: IDL.Func([], [IDL.Vec(CanisterDetails)], []),
get_imported_tokens: IDL.Func([], [GetImportedTokensResponse], []),
get_proposal_payload: IDL.Func(
[IDL.Nat64],
[GetProposalPayloadResponse],
Expand All @@ -146,6 +162,11 @@ export const idlFactory = ({ IDL }) => {
[RenameSubAccountResponse],
[]
),
set_imported_tokens: IDL.Func(
[ImportedTokens],
[SetImportedTokensResponse],
[]
),
});
};
// Remove param `{ IDL }` because TS was complaining of unused variable
Expand Down
24 changes: 24 additions & 0 deletions frontend/src/lib/canisters/nns-dapp/nns-dapp.did
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,27 @@ type HttpResponse =
body: blob;
};

type ImportedToken = record {
ledger_canister_id: principal;
index_canister_id: opt principal;
};

type ImportedTokens = record {
imported_tokens: vec ImportedToken;
};

type SetImportedTokensResponse =
variant {
Ok;
AccountNotFound;
};

type GetImportedTokensResponse =
variant {
Ok: ImportedTokens;
AccountNotFound;
};

service : {
get_account: () -> (GetAccountResponse) query;
add_account: () -> (AccountIdentifier);
Expand All @@ -170,4 +191,7 @@ service : {

http_request: (request: HttpRequest) -> (HttpResponse) query;
add_stable_asset: (asset: blob) -> ();

set_imported_tokens: (ImportedTokens) -> (SetImportedTokensResponse);
get_imported_tokens: () -> (GetImportedTokensResponse) query;
}
17 changes: 17 additions & 0 deletions frontend/src/lib/canisters/nns-dapp/nns-dapp.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export type DetachCanisterResponse = { Ok: null } | { CanisterNotFound: null };
export type GetAccountResponse =
| { Ok: AccountDetails }
| { AccountNotFound: null };
export type GetImportedTokensResponse =
| { Ok: ImportedTokens }
| { AccountNotFound: null };
export type GetProposalPayloadResponse = { Ok: string } | { Err: string };
export interface HardwareWalletAccountDetails {
principal: Principal;
Expand All @@ -53,6 +56,13 @@ export interface HttpResponse {
headers: Array<HeaderField>;
status_code: number;
}
export interface ImportedToken {
index_canister_id: [] | [Principal];
ledger_canister_id: Principal;
}
export interface ImportedTokens {
imported_tokens: Array<ImportedToken>;
}
export interface RegisterHardwareWalletRequest {
principal: Principal;
name: string;
Expand Down Expand Up @@ -82,6 +92,9 @@ export type RenameSubAccountResponse =
| { AccountNotFound: null }
| { SubAccountNotFound: null }
| { NameTooLong: null };
export type SetImportedTokensResponse =
| { Ok: null }
| { AccountNotFound: null };
export interface Stats {
seconds_since_last_ledger_sync: bigint;
sub_accounts_count: bigint;
Expand Down Expand Up @@ -115,6 +128,7 @@ export default interface _SERVICE {
) => Promise<DetachCanisterResponse>;
get_account: () => Promise<GetAccountResponse>;
get_canisters: () => Promise<Array<CanisterDetails>>;
get_imported_tokens: () => Promise<GetImportedTokensResponse>;
get_proposal_payload: (arg_0: bigint) => Promise<GetProposalPayloadResponse>;
get_stats: () => Promise<Stats>;
http_request: (arg_0: HttpRequest) => Promise<HttpResponse>;
Expand All @@ -124,4 +138,7 @@ export default interface _SERVICE {
rename_sub_account: (
arg_0: RenameSubAccountRequest
) => Promise<RenameSubAccountResponse>;
set_imported_tokens: (
arg_0: ImportedTokens
) => Promise<SetImportedTokensResponse>;
}
33 changes: 32 additions & 1 deletion frontend/src/tests/lib/api/canisters.api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import {
createCanister,
detachCanister,
getIcpToCyclesExchangeRate,
getImportedTokens,
queryCanisterDetails,
queryCanisters,
renameCanister,
setImportedTokens,
topUpCanister,
updateSettings,
} from "$lib/api/canisters.api";
Expand All @@ -24,7 +26,10 @@ import {
mockCanisterDetails,
mockCanisterSettings,
} from "$tests/mocks/canisters.mock";
import { mockSubAccount } from "$tests/mocks/icp-accounts.store.mock";
import {
mockImportedToken,
mockSubAccount,
} from "$tests/mocks/icp-accounts.store.mock";
import { CMCCanister, ProcessingError } from "@dfinity/cmc";
import {
AccountIdentifier,
Expand Down Expand Up @@ -431,4 +436,30 @@ describe("canisters-api", () => {
expect(mockCMCCanister.notifyTopUp).not.toBeCalled();
});
});

describe("getImportedTokens", () => {
it("should call the nns dapp canister to get the imported tokens", async () => {
expect(mockNNSDappCanister.getImportedTokens).not.toBeCalled();
await getImportedTokens({
identity: mockIdentity,
});

expect(mockNNSDappCanister.getImportedTokens).toBeCalledTimes(1);
});
});

describe("setImportedTokens", () => {
it("should call the nns dapp canister to set imported tokens", async () => {
expect(mockNNSDappCanister.setImportedTokens).not.toBeCalled();
await setImportedTokens({
identity: mockIdentity,
importedTokens: [mockImportedToken],
});

expect(mockNNSDappCanister.setImportedTokens).toBeCalledTimes(1);
expect(mockNNSDappCanister.setImportedTokens).toBeCalledWith([
mockImportedToken,
]);
});
});
});
Loading
Loading