Skip to content
Merged
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
10 changes: 6 additions & 4 deletions api/background/background-service.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { connectHandler } from "@/api/background/handlers/connect";
import { getAccountHandler } from "@/api/background/handlers/getAccount";
import { signAndBroadcastTxHandler } from "@/api/background/handlers/signAndBroadcastTx";
import { signTxHandler } from "@/api/background/handlers/signTx";
import { connectHandler } from "@/api/background/handlers/kaspa/connect";
import { getAccountHandler } from "@/api/background/handlers/kaspa/getAccount";
import { signAndBroadcastTxHandler } from "@/api/background/handlers/kaspa/signAndBroadcastTx";
import { signTxHandler } from "@/api/background/handlers/kaspa/signTx";
import {
Action,
ApiRequestWithHostSchema,
ApiResponseSchema,
} from "@/api/message";
import { getNetwork } from "@/api/background/handlers/get-network.ts";
import { ethereumRequestHandler } from "@/api/background/handlers/ethereum/request";
import { signMessageHandler } from "@/api/background/handlers/kaspa/signMessage";

export class BackgroundService {
public listen(): void {
Expand Down Expand Up @@ -59,6 +60,7 @@ export class BackgroundService {
[Action.SIGN_TX]: signTxHandler,
[Action.GET_NETWORK]: getNetwork,
[Action.ETHEREUM_REQUEST]: ethereumRequestHandler,
[Action.SIGN_MESSAGE]: signMessageHandler,
};

return handlers[action];
Expand Down
20 changes: 19 additions & 1 deletion api/background/handlers/ethereum/sendTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,28 @@ import {
RpcError,
RPC_ERRORS,
RpcRequestSchema,
ethereumTransactionRequestSchema,
} from "@/api/message";
import { ApiUtils } from "@/api/background/utils";
import { isMatchCurrentAddress, isUserDeniedResponse } from "./utils";
import { isAddress, isHex } from "viem";
import { z } from "zod";

export const ethereumTransactionRequestSchema = z.object({
from: z.string().refine(isAddress, "Must be a valid Ethereum address"),
to: z.string().refine(isAddress, "Must be a valid Ethereum address"),
value: z.string().refine(isHex, "Value must be a hex string").optional(),
data: z.string().refine(isHex, "Data must be a hex string").optional(),
maxFeePerGas: z
.string()
.refine(isHex, "Max fee per gas must be a hex string")
.optional(),
maxPriorityFeePerGas: z
.string()
.refine(isHex, "Max priority fee must be a hex string")
.optional(),
});

// ================================================================================

export const sendTransactionHandler = async (
tabId: number,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { ApiRequestWithHost, ConnectPayloadSchema } from "@/api/message";
import { ApiRequestWithHost } from "@/api/message";
import { ApiUtils, Handler } from "@/api/background/utils";
import { NetworkType } from "@/contexts/SettingsContext";
import { z } from "zod";

export const ConnectPayloadSchema = z.object({
networkId: z.nativeEnum(NetworkType),
name: z.string(),
icon: z.string().optional(),
});

export type ConnectPayload = z.infer<typeof ConnectPayloadSchema>;

/** Connect handler to serve BrowserMessageType.CONNECT message */
export const connectHandler: Handler = async (
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Handler } from "@/api/background/utils";
import { ApiRequestWithHost, SignTxPayloadSchema } from "@/api/message";
import { ApiRequestWithHost } from "@/api/message";
import { ApiUtils } from "@/api/background/utils";
import { SignTxPayloadSchema } from "./utils";

/** signAndBroadcastTx handler to serve BrowserMessageType.SIGN_AND_BROADCAST_TX message */
export const signAndBroadcastTxHandler: Handler = async (
tabId: number,
Expand Down
64 changes: 64 additions & 0 deletions api/background/handlers/kaspa/signMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Handler } from "@/api/background/utils";
import { ApiRequestWithHost, ApiResponse } from "@/api/message";

Check warning on line 2 in api/background/handlers/kaspa/signMessage.ts

View workflow job for this annotation

GitHub Actions / submit

'ApiResponse' is defined but never used
import { ApiUtils } from "@/api/background/utils";
import { z } from "zod";

export const SignMessagePayloadSchema = z.object({
message: z.string(),
});

export type SignMessagePayload = z.infer<typeof SignMessagePayloadSchema>;

// ================================================================================

/** signMessageHandler to serve BrowserMessageType.SIGN_MESSAGE message */
export const signMessageHandler: Handler = async (
tabId: number,
message: ApiRequestWithHost,
sendResponse: any,
) => {
if (!message.host) {
sendResponse(
ApiUtils.createApiResponse(message.id, null, "Host is required"),
);
return;
}

// Check if extension is initialized
if (!(await ApiUtils.isInitialized())) {
sendResponse(
ApiUtils.createApiResponse(
message.id,
null,
"Extension is not initialized",
),
);
return;
}

// Check if host is connected, if not, return error
if (!(await ApiUtils.isHostConnected(message.host))) {
sendResponse(
ApiUtils.createApiResponse(message.id, null, "Host not connected"),
);
return;
}

const result = SignMessagePayloadSchema.safeParse(message.payload);
if (!result.success) {
sendResponse(
ApiUtils.createApiResponse(message.id, null, "Invalid transaction data"),
);
return;
}

const url = new URL(browser.runtime.getURL("/popup.html"));
url.hash = "/sign-message";
url.searchParams.set("requestId", message.id);
url.searchParams.set("payload", JSON.stringify(result.data));

ApiUtils.openPopup(tabId, url.toString());

const response = await ApiUtils.receiveExtensionMessage(message.id);
sendResponse(response);
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { Handler } from "@/api/background/utils";
import {
ApiRequestWithHost,
ApiResponse,
SignTxPayloadSchema,
} from "@/api/message";
import { ApiRequestWithHost } from "@/api/message";
import { ApiUtils } from "@/api/background/utils";
import { SignTxPayloadSchema } from "./utils";

/** signTxHandler to serve BrowserMessageType.SIGN_TX message */
export const signTxHandler: Handler = async (
Expand Down
10 changes: 10 additions & 0 deletions api/background/handlers/kaspa/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { z } from "zod";
import { ScriptOption } from "@/lib/wallet/wallet-interface.ts";

export const SignTxPayloadSchema = z.object({
networkId: z.string(),
txJson: z.string(),
scripts: z.array(z.custom<ScriptOption>()).optional(),
});

export type SignTxPayload = z.infer<typeof SignTxPayloadSchema>;
33 changes: 19 additions & 14 deletions api/browser.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { v4 as uuid } from "uuid";
import {
Action,
ApiRequest,
ApiResponseSchema,
ConnectPayloadSchema,
SignTxPayloadSchema,
} from "@/api/message";
import { Action, ApiRequest, ApiResponseSchema } from "@/api/message";
import { ScriptOption } from "@/lib/wallet/wallet-interface.ts";
import { EthereumBrowserAPI } from "./ethereum";
import { ApiUtils } from "@/api/background/utils.ts";
import { ConnectPayloadSchema } from "@/api/background/handlers/kaspa/connect";
import { SignTxPayloadSchema } from "@/api/background/handlers/kaspa/utils";
import { SignMessagePayloadSchema } from "@/api/background/handlers/kaspa/signMessage";

function createApiRequest(
action: Action,
Expand All @@ -27,12 +23,7 @@ function createApiRequest(
export class KastleBrowserAPI {
public readonly ethereum = new EthereumBrowserAPI();

constructor() {
window.postMessage(
ApiUtils.createApiResponse("kastle_installed", []),
window.location.origin,
);
}
constructor() {}

async connect(
networkId: "mainnet" | "testnet-10" = "mainnet",
Expand Down Expand Up @@ -132,6 +123,20 @@ export class KastleBrowserAPI {
return await this.receiveMessageWithTimeout(requestId);
}

async signMessage(message: string): Promise<string> {
const requestId = uuid();
const request = createApiRequest(
Action.SIGN_MESSAGE,
requestId,
SignMessagePayloadSchema.parse({
message,
}),
);
window.postMessage(request, "*");

return await this.receiveMessageWithTimeout(requestId);
}

private createReceiveCallback<T>(id: string) {
return (event: MessageEvent<unknown>) => {
if (event.origin !== window.location.origin) {
Expand Down
37 changes: 1 addition & 36 deletions api/message.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { ScriptOption } from "@/lib/wallet/wallet-interface.ts";
import { z } from "zod";
import { NetworkType } from "@/contexts/SettingsContext.tsx";
import { isAddress, isHex } from "viem";

export enum Action {
CONNECT,
Expand All @@ -10,16 +7,9 @@ export enum Action {
SIGN_TX,
GET_NETWORK,
ETHEREUM_REQUEST,
SIGN_MESSAGE,
}

export const SignTxPayloadSchema = z.object({
networkId: z.string(),
txJson: z.string(),
scripts: z.array(z.custom<ScriptOption>()).optional(),
});

export type SignTxPayload = z.infer<typeof SignTxPayloadSchema>;

// ================================================================================================

export const RpcRequestSchema = z.object({
Expand Down Expand Up @@ -83,31 +73,6 @@ export enum ETHEREUM_METHODS {
WALLET_SWITCH_ETHEREUM_NETWORK = "wallet_switchEthereumChain",
}

export const ethereumTransactionRequestSchema = z.object({
from: z.string().refine(isAddress, "Must be a valid Ethereum address"),
to: z.string().refine(isAddress, "Must be a valid Ethereum address"),
value: z.string().refine(isHex, "Value must be a hex string").optional(),
data: z.string().refine(isHex, "Data must be a hex string").optional(),
maxFeePerGas: z
.string()
.refine(isHex, "Max fee per gas must be a hex string")
.optional(),
maxPriorityFeePerGas: z
.string()
.refine(isHex, "Max priority fee must be a hex string")
.optional(),
});

// ================================================================================================

export const ConnectPayloadSchema = z.object({
networkId: z.nativeEnum(NetworkType),
name: z.string(),
icon: z.string().optional(),
});

export type ConnectPayload = z.infer<typeof ConnectPayloadSchema>;

// ================================================================================================

export const ApiRequestSchema = z.object({
Expand Down
7 changes: 4 additions & 3 deletions components/screens/browser-api/ConnectConfirm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ApiExtensionUtils } from "@/api/extension";
import { useSettings } from "@/hooks/useSettings";
import { useEffect } from "react";
import { NetworkType } from "@/contexts/SettingsContext.tsx";
import Header from "@/components/GeneralHeader";
import Link from "@/assets/images/link.svg";
Expand All @@ -23,7 +22,7 @@ export default function ConnectConfirm() {
const urlSearchParams = new URLSearchParams(window.location.search);
const requestId = urlSearchParams.get("requestId") ?? "";
const host = urlSearchParams.get("host") ?? "";
const network = urlSearchParams.get("network") ?? "";
const network = urlSearchParams.get("network") ?? settings?.networkId;
const tabName = urlSearchParams.get("name") ?? "Unknown";
const icon = urlSearchParams.get("icon") ?? undefined;

Expand All @@ -47,7 +46,8 @@ export default function ConnectConfirm() {
}

const walletConnections = settings.walletConnections ?? {};
const targetNetwork = (network as NetworkType) ?? settings.networkId;
const targetNetwork = (network ?? settings.networkId) as NetworkType;

const connections =
walletConnections[selectedWalletId]?.[selectedAccountIndex]?.[
targetNetwork
Expand Down Expand Up @@ -110,6 +110,7 @@ export default function ConnectConfirm() {
background: "bg-yellow-800",
},
];

const selectedNetwork = networks.find(
(n) => n.id === (network ?? settings?.networkId),
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SignTxPayloadSchema } from "@/api/message";
import { SignTxPayloadSchema } from "@/api/background/handlers/kaspa/utils";
import HotWalletSignAndBroadcast from "@/components/screens/browser-api/sign-and-broadcast/HotWalletSignAndBroadcast";
import LedgerSignAndBroadcast from "@/components/screens/browser-api/sign-and-broadcast/LedgerSignAndBroadcast";
import useWalletManager from "@/hooks/useWalletManager.ts";
Expand Down
42 changes: 42 additions & 0 deletions components/screens/browser-api/SignMessageConfirm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { SignMessagePayloadSchema } from "@/api/background/handlers/kaspa/signMessage";
import HotWalletSignMessage from "@/components/screens/browser-api/sign-message/HotWalletSignMessage";
import LedgerSignMessage from "@/components/screens/browser-api/sign-message/LedgerSignMessage";
import useWalletManager from "@/hooks/useWalletManager.ts";
import Splash from "@/components/screens/Splash";

export default function SignMessageConfirm() {
const { wallet } = useWalletManager();
const requestId =
new URLSearchParams(window.location.search).get("requestId") ?? "";
const encodedPayload = new URLSearchParams(window.location.search).get(
"payload",
);

const payload = encodedPayload
? JSON.parse(decodeURIComponent(encodedPayload))
: null;

const parsedPayload = payload
? SignMessagePayloadSchema.parse(payload)
: null;

const loading = !wallet || !requestId || !parsedPayload;

return (
<div className="h-screen p-4">
{loading && <Splash />}
{!loading && wallet.type !== "ledger" && (
<HotWalletSignMessage
requestId={requestId}
payload={SignMessagePayloadSchema.parse(parsedPayload)}
/>
)}
{!loading && wallet.type === "ledger" && (
<LedgerSignMessage
requestId={requestId}
payload={SignMessagePayloadSchema.parse(parsedPayload)}
/>
)}
</div>
);
}
2 changes: 1 addition & 1 deletion components/screens/browser-api/SignTxConfirm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SignTxPayloadSchema } from "@/api/message";
import { SignTxPayloadSchema } from "@/api/background/handlers/kaspa/utils";
import HotWalletSignTx from "@/components/screens/browser-api/sign-tx/HotWalletSignTx";
import LedgerSignTx from "@/components/screens/browser-api/sign-tx/LedgerSignTx";
import useWalletManager from "@/hooks/useWalletManager.ts";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import HotWalletSignMessage from "@/components/screens/browser-api/ethereum/sign-message/HotWalletSignMessage";
import useWalletManager from "@/hooks/useWalletManager.ts";
import { useEffect } from "react";
import { ApiExtensionUtils } from "@/api/extension";
import { RPC_ERRORS } from "@/api/message";
import Splash from "@/components/screens/Splash";
import { ApiUtils } from "@/api/background/utils";

export default function EthereumSignMessageConfirm() {
const { wallet } = useWalletManager();
Expand Down
Loading
Loading