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: add disconnect account approval step #1101

Merged
merged 14 commits into from
Sep 13, 2024
5 changes: 5 additions & 0 deletions apps/extension/src/Approvals/Approvals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AccountType, TxDetails } from "@namada/types";
import { AppHeader } from "App/Common/AppHeader";
import { TopLevelRoute } from "Approvals/types";
import { ApproveConnection } from "./ApproveConnection";
import { ApproveDisconnection } from "./ApproveDisconnection";
import { ApproveSignArbitrary } from "./ApproveSignArbitrary";
import { ApproveSignTx } from "./ApproveSignTx";
import { ConfirmSignature } from "./ConfirmSignArbitrary";
Expand Down Expand Up @@ -65,6 +66,10 @@ export const Approvals: React.FC = () => {
path={TopLevelRoute.ApproveConnection}
element={<ApproveConnection />}
/>
<Route
path={TopLevelRoute.ApproveDisconnection}
element={<ApproveDisconnection />}
/>
<Route
path={`${TopLevelRoute.ApproveSignArbitrary}/:signer`}
element={
Expand Down
9 changes: 2 additions & 7 deletions apps/extension/src/Approvals/ApproveConnection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,12 @@ export const ApproveConnection: React.FC = () => {
const requester = useRequester();
const params = useQuery();
const interfaceOrigin = params.get("interfaceOrigin");
const interfaceTabId = params.get("interfaceTabId");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interfaceTabId wasn't used anymore, it was replaced by popupTabId directly on the service


const handleResponse = async (allowConnection: boolean): Promise<void> => {
if (interfaceTabId && interfaceOrigin) {
if (interfaceOrigin) {
await requester.sendMessage(
Ports.Background,
new ConnectInterfaceResponseMsg(
parseInt(interfaceTabId),
interfaceOrigin,
allowConnection
)
new ConnectInterfaceResponseMsg(interfaceOrigin, allowConnection)
);
await closeCurrentTab();
}
Expand Down
45 changes: 45 additions & 0 deletions apps/extension/src/Approvals/ApproveDisconnection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ActionButton, Alert, GapPatterns, Stack } from "@namada/components";
import { PageHeader } from "App/Common";
import { DisconnectInterfaceResponseMsg } from "background/approvals";
import { useQuery } from "hooks";
import { useRequester } from "hooks/useRequester";
import { Ports } from "router";
import { closeCurrentTab } from "utils";

export const ApproveDisconnection: React.FC = () => {
const requester = useRequester();
const params = useQuery();
const interfaceOrigin = params.get("interfaceOrigin");

const handleResponse = async (revokeConnection: boolean): Promise<void> => {
if (interfaceOrigin) {
await requester.sendMessage(
Ports.Background,
new DisconnectInterfaceResponseMsg(interfaceOrigin, revokeConnection)
);
await closeCurrentTab();
}
};

return (
<Stack full gap={GapPatterns.TitleContent} className="pt-4 pb-8">
<PageHeader title="Approve Request" />
<Stack full className="justify-between" gap={12}>
<Alert type="warning">
Approve disconnect for <strong>{interfaceOrigin}</strong>?
</Alert>
<Stack gap={2}>
<ActionButton onClick={() => handleResponse(true)}>
Approve
</ActionButton>
<ActionButton
outlineColor="yellow"
onClick={() => handleResponse(false)}
>
Reject
</ActionButton>
</Stack>
</Stack>
</Stack>
);
};
1 change: 1 addition & 0 deletions apps/extension/src/Approvals/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export enum TopLevelRoute {

// Connection approval
ApproveConnection = "/approve-connection",
ApproveDisconnection = "/approve-disconnection",

// Sign Tx approval
ApproveSignTx = "/approve-sign-tx",
Expand Down
9 changes: 8 additions & 1 deletion apps/extension/src/background/approvals/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Message } from "router";
import { getHandler } from "./handler";
import {
ConnectInterfaceResponseMsg,
DisconnectInterfaceResponseMsg,
RejectSignArbitraryMsg,
RejectSignTxMsg,
RevokeConnectionMsg,
Expand Down Expand Up @@ -80,13 +81,19 @@ describe("approvals handler", () => {
expect(service.approveConnection).toBeCalled();

const connectInterfaceResponseMsg = new ConnectInterfaceResponseMsg(
0,
"",
true
);
handler(env, connectInterfaceResponseMsg);
expect(service.approveConnectionResponse).toBeCalled();

const disconnectInterfaceResponseMsg = new DisconnectInterfaceResponseMsg(
"",
true
);
handler(env, disconnectInterfaceResponseMsg);
expect(service.approveDisconnectionResponse).toBeCalled();

const revokeConnectionMsg = new RevokeConnectionMsg("");
handler(env, revokeConnectionMsg);
expect(service.revokeConnection).toBeCalled();
Expand Down
46 changes: 40 additions & 6 deletions apps/extension/src/background/approvals/handler.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {
ApproveConnectInterfaceMsg,
ApproveDisconnectInterfaceMsg,
ApproveSignArbitraryMsg,
ApproveSignTxMsg,
IsConnectionApprovedMsg,
} from "provider";
import { Env, Handler, InternalHandler, Message } from "router";
import {
ConnectInterfaceResponseMsg,
DisconnectInterfaceResponseMsg,
QueryPendingTxBytesMsg,
QuerySignArbitraryDataMsg,
QueryTxDetailsMsg,
Expand Down Expand Up @@ -37,6 +39,16 @@ export const getHandler: (service: ApprovalsService) => Handler = (service) => {
env,
msg as ConnectInterfaceResponseMsg
);
case ApproveDisconnectInterfaceMsg:
return handleApproveDisconnectInterfaceMsg(service)(
env,
msg as ApproveDisconnectInterfaceMsg
);
case DisconnectInterfaceResponseMsg:
return handleDisconnectInterfaceResponseMsg(service)(
env,
msg as DisconnectInterfaceResponseMsg
);
case RevokeConnectionMsg:
return handleRevokeConnectionMsg(service)(
env,
Expand Down Expand Up @@ -101,8 +113,8 @@ const handleIsConnectionApprovedMsg: (
const handleApproveConnectInterfaceMsg: (
service: ApprovalsService
) => InternalHandler<ApproveConnectInterfaceMsg> = (service) => {
return async ({ senderTabId: interfaceTabId }, { origin }) => {
return await service.approveConnection(interfaceTabId, origin);
return async (_, { origin }) => {
return await service.approveConnection(origin);
};
};

Expand All @@ -111,13 +123,35 @@ const handleConnectInterfaceResponseMsg: (
) => InternalHandler<ConnectInterfaceResponseMsg> = (service) => {
return async (
{ senderTabId: popupTabId },
{ interfaceTabId, interfaceOrigin, allowConnection }
{ interfaceOrigin, allowConnection }
) => {
return await service.approveConnectionResponse(
interfaceTabId,
popupTabId,
interfaceOrigin,
allowConnection
);
};
};

const handleApproveDisconnectInterfaceMsg: (
service: ApprovalsService
) => InternalHandler<ApproveDisconnectInterfaceMsg> = (service) => {
return async (_, { origin }) => {
return await service.approveDisconnection(origin);
};
};

const handleDisconnectInterfaceResponseMsg: (
service: ApprovalsService
) => InternalHandler<DisconnectInterfaceResponseMsg> = (service) => {
return async (
{ senderTabId: popupTabId },
{ interfaceOrigin, revokeConnection }
) => {
return await service.approveDisconnectionResponse(
popupTabId,
interfaceOrigin,
allowConnection,
popupTabId
revokeConnection
);
};
};
Expand Down
4 changes: 4 additions & 0 deletions apps/extension/src/background/approvals/init.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {
ApproveConnectInterfaceMsg,
ApproveDisconnectInterfaceMsg,
ApproveSignArbitraryMsg,
ApproveSignTxMsg,
IsConnectionApprovedMsg,
} from "provider";
import { Router } from "router";
import {
ConnectInterfaceResponseMsg,
DisconnectInterfaceResponseMsg,
QueryPendingTxBytesMsg,
QuerySignArbitraryDataMsg,
QueryTxDetailsMsg,
Expand All @@ -33,6 +35,8 @@ export function init(router: Router, service: ApprovalsService): void {
router.registerMessage(IsConnectionApprovedMsg);
router.registerMessage(ApproveConnectInterfaceMsg);
router.registerMessage(ConnectInterfaceResponseMsg);
router.registerMessage(ApproveDisconnectInterfaceMsg);
router.registerMessage(DisconnectInterfaceResponseMsg);
router.registerMessage(RevokeConnectionMsg);
router.registerMessage(QueryTxDetailsMsg);
router.registerMessage(QuerySignArbitraryDataMsg);
Expand Down
16 changes: 5 additions & 11 deletions apps/extension/src/background/approvals/messages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,29 +63,23 @@ describe("approvals messages", () => {
});

test("valid ConnectInterfaceResponseMsg", () => {
const msg = new ConnectInterfaceResponseMsg(0, "interface", true);
const msg = new ConnectInterfaceResponseMsg("interface", true);

expect(msg.type()).toBe(MessageType.ConnectInterfaceResponse);
expect(msg.route()).toBe(ROUTE);
expect(msg.validate()).toBeUndefined();
});

test("invalid ConnectInterfaceResponseMsg", () => {
const msg = new ConnectInterfaceResponseMsg(0, "interface", true);

(msg as any).interfaceTabId = undefined;
const msg = new ConnectInterfaceResponseMsg("interface", true);
(msg as any).interfaceOrigin = undefined;

expect(() => msg.validate()).toThrow();

const msg2 = new ConnectInterfaceResponseMsg(0, "interface", true);
(msg2 as any).interfaceOrigin = undefined;
const msg2 = new ConnectInterfaceResponseMsg("interface", true);
(msg2 as any).allowConnection = undefined;

expect(() => msg2.validate()).toThrow();

const msg3 = new ConnectInterfaceResponseMsg(0, "interface", true);
(msg3 as any).allowConnection = undefined;

expect(() => msg3.validate()).toThrow();
});

test("valid RevokeConnectionMsg", () => {
Expand Down
33 changes: 27 additions & 6 deletions apps/extension/src/background/approvals/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum MessageType {
SubmitApprovedSignLedgerTx = "submit-approved-sign-ledger-tx",
RejectSignArbitrary = "reject-sign-arbitrary",
ConnectInterfaceResponse = "connect-interface-response",
DisconnectInterfaceResponse = "disconnect-interface-response",
RevokeConnection = "revoke-connection",
QueryTxDetails = "query-tx-details",
QuerySignArbitraryData = "query-sign-arbitrary-data",
Expand Down Expand Up @@ -144,19 +145,14 @@ export class ConnectInterfaceResponseMsg extends Message<void> {
}

constructor(
public readonly interfaceTabId: number,
public readonly interfaceOrigin: string,
public readonly allowConnection: boolean
) {
super();
}

validate(): void {
validateProps(this, [
"interfaceTabId",
"interfaceOrigin",
"allowConnection",
]);
validateProps(this, ["interfaceOrigin", "allowConnection"]);
}

route(): string {
Expand All @@ -168,6 +164,31 @@ export class ConnectInterfaceResponseMsg extends Message<void> {
}
}

export class DisconnectInterfaceResponseMsg extends Message<void> {
public static type(): MessageType {
return MessageType.DisconnectInterfaceResponse;
}

constructor(
public readonly interfaceOrigin: string,
public readonly revokeConnection: boolean
) {
super();
}

validate(): void {
validateProps(this, ["interfaceOrigin", "revokeConnection"]);
}

route(): string {
return ROUTE;
}

type(): string {
return DisconnectInterfaceResponseMsg.type();
}
}

export class RevokeConnectionMsg extends Message<void> {
public static type(): MessageType {
return MessageType.RevokeConnection;
Expand Down
Loading
Loading