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

feat: using internet identity service #90

Merged
merged 12 commits into from
May 10, 2021
319 changes: 53 additions & 266 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"build": "webpack",
"start": "webpack serve --config webpack.dev.js",
"generate_candid": "didc bind ./wallet/src/lib.did -t ts > wallet_ui/types/declaration.d.ts",
"generate_interface": "didc bind ./wallet/src/lib.did -t js > wallet_ui/canister/interface.js",
"presideload-ui": "npm run build",
"sideload-ui": "node script/upload.js",
"test": "jest --verbose"
Expand All @@ -15,8 +16,9 @@
"dependencies": {
"@babel/core": "7.9.0",
"@babel/preset-react": "7.9.0",
"@dfinity/agent": "0.8.3",
"@dfinity/authentication": "0.8.3",
"@dfinity/agent": "0.8.7",
"@dfinity/auth-client": "0.8.7",
"@dfinity/authentication": "0.8.7",
"@emotion/css": "^11.1.3",
"@material-ui/core": "^4.11.2",
"@material-ui/icons": "^4.11.2",
Expand Down
69 changes: 33 additions & 36 deletions wallet_ui/canister/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@
* It is also useful because that puts all the code in one place, including the
* authentication logic. We do not use `window.ic` anywhere in this.
*/
import { HttpAgent, Actor, Principal, ActorSubclass } from "@dfinity/agent";
import { AuthenticationClient } from "../utils/authClient";
import _SERVICE from "./wallet/wallet";
import factory, { Event } from "./wallet";
import {
HttpAgent,
Actor,
Principal,
ActorSubclass,
AnonymousIdentity,
} from "@dfinity/agent";
import { AuthClient } from "@dfinity/auth-client";
import _SERVICE, { Event } from "./wallet/wallet";
import wallet_idl from "./interface.js";
import { authClient } from "../utils/authClient";
export * from "./wallet";

function convertIdlEventMap(idlEvent: any): Event {
return {
Expand All @@ -25,18 +33,9 @@ function convertIdlEventMap(idlEvent: any): Event {
kind: idlEvent.kind,
};
}

export * from "./wallet";

// Need to export the enumeration from wallet.did
export { Principal } from "@dfinity/agent";

const authClient = new AuthenticationClient();

export async function getAgentPrincipal(): Promise<Principal> {
return authClient.getIdentity().getPrincipal();
}

function getCanisterId(): Principal {
// Check the query params.
const maybeCanisterId = new URLSearchParams(window.location.search).get(
Expand All @@ -59,21 +58,14 @@ function getCanisterId(): Principal {

throw new Error("Could not find the canister ID.");
}
let walletCanisterCache: ActorSubclass<_SERVICE> | null = null;

let walletCanisterCache: ActorSubclass<_SERVICE>;

export async function login() {
const redirectUri = `${location.origin}/${location.search}`;
await authClient.loginWithRedirect({
redirectUri,
scope: [getWalletId()],
});
}

export async function handleAuthRedirect() {
// Check if we need to parse the authentication.
if (authClient.shouldParseResult(location)) {
await authClient.handleRedirectCallback(location);
export async function getAgentPrincipal(): Promise<Principal> {
const identity = await authClient.getIdentity();
if (identity) {
return await identity.getPrincipal();
} else {
return Promise.reject("Could not find identity");
}
}

Expand All @@ -82,22 +74,23 @@ async function getWalletCanister(): Promise<ActorSubclass<_SERVICE>> {
return walletCanisterCache;
}

await handleAuthRedirect();

let walletId: Principal | null = null;
walletId = getWalletId(walletId);

const agent = new HttpAgent({ identity: authClient.getIdentity() });
if (!authClient.ready) {
return Promise.reject("not yet ready");
}

const identity = (await authClient.getIdentity()) ?? new AnonymousIdentity();
const agent = new HttpAgent({
identity,
});
if (!walletId) {
throw new Error("Need to have a wallet ID.");
} else {
walletCanisterCache = (Actor as any).createActor(factory as any, {
walletCanisterCache = Actor.createActor<_SERVICE>(wallet_idl, {
agent,
canisterId: walletId,
// Override the defaults for polling.
maxAttempts: 201,
throttleDurationInMSecs: 1500,
canisterId: (await getWalletId()) || "",
}) as ActorSubclass<_SERVICE>;
return walletCanisterCache;
}
Expand Down Expand Up @@ -142,7 +135,11 @@ export const Wallet = {
await this.balance();
},
async balance(): Promise<number> {
return Number((await (await getWalletCanister()).wallet_balance()).amount);
const walletCanister = await getWalletCanister();
return Number((await walletCanister.wallet_balance()).amount);
},
clearWalletCache() {
walletCanisterCache = null;
},
async events(from?: number, to?: number): Promise<Event[]> {
return (
Expand Down
147 changes: 147 additions & 0 deletions wallet_ui/canister/interface.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
export default ({ IDL }) => {
const Kind = IDL.Variant({
User: IDL.Null,
Canister: IDL.Null,
Unknown: IDL.Null,
});
const Role = IDL.Variant({
Custodian: IDL.Null,
Contact: IDL.Null,
Controller: IDL.Null,
});
const AddressEntry = IDL.Record({
id: IDL.Principal,
kind: Kind,
name: IDL.Opt(IDL.Text),
role: Role,
});
const EventKind = IDL.Variant({
CyclesReceived: IDL.Record({
from: IDL.Principal,
amount: IDL.Nat64,
}),
CanisterCreated: IDL.Record({
cycles: IDL.Nat64,
canister: IDL.Principal,
}),
CanisterCalled: IDL.Record({
cycles: IDL.Nat64,
method_name: IDL.Text,
canister: IDL.Principal,
}),
CyclesSent: IDL.Record({
to: IDL.Principal,
amount: IDL.Nat64,
refund: IDL.Nat64,
}),
AddressRemoved: IDL.Record({ id: IDL.Principal }),
WalletDeployed: IDL.Record({ canister: IDL.Principal }),
AddressAdded: IDL.Record({
id: IDL.Principal,
name: IDL.Opt(IDL.Text),
role: Role,
}),
});
const Event = IDL.Record({
id: IDL.Nat32,
kind: EventKind,
timestamp: IDL.Nat64,
});
const ResultCall = IDL.Variant({
Ok: IDL.Record({ return: IDL.Vec(IDL.Nat8) }),
Err: IDL.Text,
});
const ResultCreate = IDL.Variant({
Ok: IDL.Record({ canister_id: IDL.Principal }),
Err: IDL.Text,
});
const ResultSend = IDL.Variant({ Ok: IDL.Null, Err: IDL.Text });
return IDL.Service({
add_address: IDL.Func([AddressEntry], [], []),
add_controller: IDL.Func([IDL.Principal], [], []),
authorize: IDL.Func([IDL.Principal], [], []),
deauthorize: IDL.Func([IDL.Principal], [], []),
get_chart: IDL.Func(
[
IDL.Opt(
IDL.Record({
count: IDL.Opt(IDL.Nat32),
precision: IDL.Opt(IDL.Nat64),
})
),
],
[IDL.Vec(IDL.Tuple(IDL.Nat64, IDL.Nat64))],
["query"]
),
get_controllers: IDL.Func([], [IDL.Vec(IDL.Principal)], ["query"]),
get_custodians: IDL.Func([], [IDL.Vec(IDL.Principal)], ["query"]),
get_events: IDL.Func(
[
IDL.Opt(
IDL.Record({
to: IDL.Opt(IDL.Nat32),
from: IDL.Opt(IDL.Nat32),
})
),
],
[IDL.Vec(Event)],
["query"]
),
list_addresses: IDL.Func([], [IDL.Vec(AddressEntry)], ["query"]),
name: IDL.Func([], [IDL.Opt(IDL.Text)], ["query"]),
remove_address: IDL.Func([IDL.Principal], [], []),
remove_controller: IDL.Func([IDL.Principal], [], []),
set_name: IDL.Func([IDL.Text], [], []),
wallet_balance: IDL.Func(
[],
[IDL.Record({ amount: IDL.Nat64 })],
["query"]
),
wallet_call: IDL.Func(
[
IDL.Record({
args: IDL.Vec(IDL.Nat8),
cycles: IDL.Nat64,
method_name: IDL.Text,
canister: IDL.Principal,
}),
],
[ResultCall],
[]
),
wallet_create_canister: IDL.Func(
[
IDL.Record({
controller: IDL.Opt(IDL.Principal),
cycles: IDL.Nat64,
}),
],
[ResultCreate],
[]
),
wallet_create_wallet: IDL.Func(
[
IDL.Record({
controller: IDL.Opt(IDL.Principal),
cycles: IDL.Nat64,
}),
],
[ResultCreate],
[]
),
wallet_receive: IDL.Func([], [], []),
wallet_send: IDL.Func(
[IDL.Record({ canister: IDL.Principal, amount: IDL.Nat64 })],
[ResultSend],
[]
),
wallet_store_wallet_wasm: IDL.Func(
[IDL.Record({ wasm_module: IDL.Vec(IDL.Nat8) })],
[],
[]
),
});
};
export const init = ({ IDL }) => {
return [];
};
42 changes: 26 additions & 16 deletions wallet_ui/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,22 @@ import {
BrowserRouter as Router,
Switch as RouterSwitch,
Route,
Redirect,
useHistory,
} from "react-router-dom";

// For Switch Theming
import ThemeProvider from "@material-ui/styles/ThemeProvider";

// For document title setting
import { handleAuthRedirect, Wallet } from "../canister";
import { Wallet } from "../canister";

// Routes
import { Authorize } from "./routes/Authorize";
import { Dashboard } from "./routes/Dashboard";
import { useLocalStorage } from "../utils/hooks";
import generateTheme from "../utils/materialTheme";
import { authClient } from "../utils/authClient";

export function Copyright() {
return (
Expand Down Expand Up @@ -132,25 +135,23 @@ function useDarkState(): [boolean, (newState?: boolean) => void] {

export default function App() {
const [ready, setReady] = useState(false);
const [isAuthenticated, setIsAuthenticated] = useState<null | boolean>(null);
const [open, setOpen] = useLocalStorage("app-menu-open", false);
const [darkState, setDarkState] = useDarkState();
const classes = useStyles();
const theme = generateTheme(darkState);

useEffect(() => {
Wallet.name().then((name) => {
document.title = name;
if (!authClient.ready) {
return;
}
setReady(true);
authClient.isAuthenticated().then((value) => {
setIsAuthenticated(value ?? false);
});
}, []);

const theme = generateTheme(darkState);

const classes = useStyles();

// Check if we need to parse the hash.
handleAuthRedirect().then(() => setReady(true));
}, [authClient.ready]);

if (!ready) {
return <></>;
}
if (!ready) return null;

return (
<ThemeProvider theme={theme}>
Expand All @@ -176,11 +177,20 @@ export default function App() {

<RouterSwitch>
<Route path="/authorize">
<Authorize />
<Authorize setIsAuthenticated={setIsAuthenticated} />
</Route>

<Route path="/">
<Dashboard open={open} onOpenToggle={() => setOpen(!open)} />
{authClient.ready && isAuthenticated === false ? (
<Redirect
to={{
pathname: `/authorize`,
search: location.search,
}}
/>
) : (
<Dashboard open={open} onOpenToggle={() => setOpen(!open)} />
)}
</Route>
</RouterSwitch>
</div>
Expand Down
1 change: 0 additions & 1 deletion wallet_ui/components/CycleSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ function CycleSlider(props: Props) {
const sdrUsdRate = 0.69977;

function handleSlide(e: any) {
console.log("slide", e.target.value);
if (balance && e.target?.value) {
const newValue = Math.floor((balance * e.target.value) / 1000);
setCycles(newValue);
Expand Down
9 changes: 0 additions & 9 deletions wallet_ui/components/panels/Untitled-1.ts

This file was deleted.

Loading