diff --git a/packages/sdk/src/client-auth-server/Signer.ts b/packages/sdk/src/client-auth-server/Signer.ts index de702f4b..ec634133 100644 --- a/packages/sdk/src/client-auth-server/Signer.ts +++ b/packages/sdk/src/client-auth-server/Signer.ts @@ -5,7 +5,7 @@ import { createZksyncSessionClient, type ZksyncSsoSessionClient } from "../clien import type { Communicator } from "../communicator/index.js"; import { type CustomPaymasterHandler, getTransactionWithPaymasterData } from "../paymaster/index.js"; import type { SessionStateEvent } from "../utils/session.js"; -import { StorageItem } from "../utils/storage.js"; +import { StorageItem, type StorageLike } from "../utils/storage.js"; import type { AppMetadata, RequestArguments } from "./interface.js"; import type { AuthServerRpcSchema, ExtractParams, ExtractReturnType, Method, RPCRequestMessage, RPCResponseMessage, RpcSchema } from "./rpc.js"; import type { SessionPreferences } from "./session/index.js"; @@ -44,6 +44,7 @@ type SignerConstructorParams = { paymasterHandler?: CustomPaymasterHandler; onSessionStateChange?: (event: { address: Address; chainId: number; state: SessionStateEvent }) => void; skipPreTransactionStateValidation?: boolean; // Useful if you want to send session transactions really fast + storage?: StorageLike; }; type ChainsInfo = ExtractReturnType<"eth_requestAccounts", AuthServerRpcSchema>["chainsInfo"]; @@ -60,10 +61,10 @@ export class Signer implements SignerInterface { private readonly skipPreTransactionStateValidation?: boolean; private _account: StorageItem; - private _chainsInfo = new StorageItem(StorageItem.scopedStorageKey("chainsInfo"), []); + private _chainsInfo: StorageItem; private client: { instance: ZksyncSsoSessionClient; type: "session" } | { instance: WalletClient; type: "auth-server" } | undefined; - constructor({ metadata, communicator, updateListener, session, chains, transports, paymasterHandler, onSessionStateChange, skipPreTransactionStateValidation }: SignerConstructorParams) { + constructor({ metadata, communicator, updateListener, session, chains, transports, paymasterHandler, onSessionStateChange, skipPreTransactionStateValidation, storage }: SignerConstructorParams) { if (!chains.length) throw new Error("At least one chain must be included in the config"); this.getMetadata = metadata; @@ -76,6 +77,7 @@ export class Signer implements SignerInterface { this.onSessionStateChange = onSessionStateChange; this.skipPreTransactionStateValidation = skipPreTransactionStateValidation; + this._chainsInfo = new StorageItem(StorageItem.scopedStorageKey("chainsInfo"), [], { storage }); this._account = new StorageItem(StorageItem.scopedStorageKey("account"), null, { onChange: (newValue) => { if (newValue) { @@ -86,6 +88,7 @@ export class Signer implements SignerInterface { this.updateListener.onAccountsUpdate([]); } }, + storage, }); try { if (this.account) this.createWalletClient(); diff --git a/packages/sdk/src/client-auth-server/WalletProvider.ts b/packages/sdk/src/client-auth-server/WalletProvider.ts index fb4f41f7..0d579064 100644 --- a/packages/sdk/src/client-auth-server/WalletProvider.ts +++ b/packages/sdk/src/client-auth-server/WalletProvider.ts @@ -2,11 +2,13 @@ import { EventEmitter } from "eventemitter3"; import type { Address, Chain, Transport } from "viem"; import { toHex } from "viem"; +import type { Communicator } from "../communicator/index.js"; import { PopupCommunicator } from "../communicator/PopupCommunicator.js"; import { serializeError, standardErrors } from "../errors/index.js"; import type { CustomPaymasterHandler } from "../paymaster/index.js"; import { getFavicon, getWebsiteName } from "../utils/helpers.js"; import type { SessionStateEvent } from "../utils/session.js"; +import type { StorageLike } from "../utils/storage.js"; import type { AppMetadata, ProviderInterface, @@ -27,15 +29,17 @@ export type WalletProviderConstructorOptions = { paymasterHandler?: CustomPaymasterHandler; onSessionStateChange?: (state: { address: Address; chainId: number; state: SessionStateEvent }) => void; skipPreTransactionStateValidation?: boolean; // Useful if you want to send session transactions really fast + customCommunicator?: Communicator; + storage?: StorageLike; }; export class WalletProvider extends EventEmitter implements ProviderInterface { readonly isZksyncSso = true; private signer: Signer; - constructor({ metadata, chains, transports, session, authServerUrl, paymasterHandler, onSessionStateChange, skipPreTransactionStateValidation }: WalletProviderConstructorOptions) { + constructor({ metadata, chains, transports, session, authServerUrl, paymasterHandler, onSessionStateChange, skipPreTransactionStateValidation, customCommunicator, storage }: WalletProviderConstructorOptions) { super(); - const communicator = new PopupCommunicator(authServerUrl || DEFAULT_AUTH_SERVER_URL); + const communicator = customCommunicator ?? new PopupCommunicator(authServerUrl || DEFAULT_AUTH_SERVER_URL); this.signer = new Signer({ metadata: () => ({ name: metadata?.name || getWebsiteName() || "Unknown DApp", @@ -50,6 +54,7 @@ export class WalletProvider extends EventEmitter implements ProviderInterface { paymasterHandler, onSessionStateChange, skipPreTransactionStateValidation, + storage, }); } diff --git a/packages/sdk/src/communicator/PopupCommunicator.ts b/packages/sdk/src/communicator/PopupCommunicator.ts index f0f00290..b9bfb7da 100644 --- a/packages/sdk/src/communicator/PopupCommunicator.ts +++ b/packages/sdk/src/communicator/PopupCommunicator.ts @@ -4,14 +4,29 @@ import type { Communicator, Message } from "./index.js"; export interface PopupConfigMessage extends Message { event: "PopupLoaded" | "PopupUnload"; } +type PositionCalculator = (width: number, height: number) => { left: number; top: number }; export class PopupCommunicator implements Communicator { private readonly url: URL; private popup: Window | null = null; private listeners = new Map<(_: MessageEvent) => void, { reject: (_: Error) => void }>(); - constructor(url: string) { + private readonly width: number; + private readonly height: number; + private readonly calculatePosition?: PositionCalculator; + + constructor( + url: string, + options?: { + width?: number; + height?: number; + calculatePosition?: PositionCalculator; + }, + ) { this.url = new URL(url); + this.width = options?.width ?? 420; + this.height = options?.height ?? 600; + this.calculatePosition = options?.calculatePosition; } postMessage = async (message: Message) => { @@ -64,14 +79,18 @@ export class PopupCommunicator implements Communicator { }; openPopup = () => { - const width = 420; - const height = 600; + const width = this.width; + const height = this.height; const url = new URL(this.url.toString()); url.searchParams.set("origin", window.location.origin); - const left = (window.innerWidth - width) / 2 + window.screenX; - const top = (window.innerHeight - height) / 2 + window.screenY; + const { left, top } = this.calculatePosition + ? this.calculatePosition(width, height) + : { + left: (window.innerWidth - width) / 2 + window.screenX, + top: (window.innerHeight - height) / 2 + window.screenY, + }; const popup = window.open( url, diff --git a/packages/sdk/src/connector/index.ts b/packages/sdk/src/connector/index.ts index eacfb5cb..ab1ef1b7 100644 --- a/packages/sdk/src/connector/index.ts +++ b/packages/sdk/src/connector/index.ts @@ -17,6 +17,7 @@ import { } from "viem"; import type { ZksyncSsoSessionClient } from "../client/index.js"; +import type { Communicator } from "../communicator/interface.js"; import { EthereumProviderError } from "../errors/errors.js"; import { type AppMetadata, type ProviderInterface, type SessionPreferences, WalletProvider } from "../index.js"; import type { CustomPaymasterHandler } from "../paymaster/index.js"; @@ -27,6 +28,7 @@ export type ZksyncSsoConnectorOptions = { session?: SessionPreferences | (() => SessionPreferences | Promise); authServerUrl?: string; paymasterHandler?: CustomPaymasterHandler; + communicator?: Communicator; }; export const zksyncSsoConnector = (parameters: ZksyncSsoConnectorOptions) => { @@ -144,6 +146,7 @@ export const zksyncSsoConnector = (parameters: ZksyncSsoConnectorOptions) => { transports: config.transports, chains: config.chains, paymasterHandler: parameters.paymasterHandler, + customCommunicator: parameters.communicator, }); } return walletProvider; diff --git a/packages/sdk/src/utils/storage.ts b/packages/sdk/src/utils/storage.ts index 4968e01f..32c98ef4 100644 --- a/packages/sdk/src/utils/storage.ts +++ b/packages/sdk/src/utils/storage.ts @@ -1,4 +1,4 @@ -interface StorageLike { +export interface StorageLike { getItem(key: string): string | null; setItem(key: string, value: string): void; removeItem(key: string): void;