{"version":3,"sources":["../src/rpc.ts"],"names":["RpcServer","_RpcServer","services","domains","#definitions","event","IFrameRpcError","service","method","fn","result","error","err","KeybanBaseError","SdkError","RpcClient","_RpcClient","#iframeUrl","#iframe","#instances","#getIframeUrl","apiUrl","appId","network","iframeUrl","options","key","resolve","reject","iframe","params","channel","message"],"mappings":"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","file":"chunk-DWBGILVH.mjs","sourcesContent":["/*\n * @module RPC services\n */\n\nimport { IFrameRpcError, KeybanBaseError, SdkError } from \"~/errors\";\nimport type { AuthConnection, KeybanNetwork, KeybanUser } from \"~/index\";\n\ntype Hex = `0x${string}`;\n\nexport interface IKeybanAuth {\n  isAuthenticated(): Promise<boolean>;\n  getUser(): Promise<KeybanUser | null>;\n  getLoginUrl(connection?: AuthConnection): Promise<string>;\n  getLogoutUrl(redirect?: string): Promise<string>;\n\n  passwordLogin(username: string, password: string): Promise<void>;\n\n  passwordlessStart(\n    connection: \"email\" | \"sms\",\n    username: string,\n  ): Promise<void>;\n\n  passwordlessLogin(\n    connection: \"email\" | \"sms\",\n    username: string,\n    otp: string,\n  ): Promise<void>;\n}\n\nexport interface IKeybanSigner {\n  dkg(): Promise<string>;\n  sign(clientShare: string, message: string): Promise<Hex>;\n  publicKey(clientShare: string): Promise<Hex>;\n}\n\nexport interface IKeybanAccount {\n  getAddress(): Promise<string>;\n}\n\nexport interface IKeybanLoyalty {\n  initAccountSetup(): Promise<string>;\n  submitAccountSetup(rawTransaction: string): Promise<void>;\n}\n\nexport interface IKeybanClientShareStorage {\n  get(key: string): Promise<string | null>;\n  set(key: string, clientShare: string): Promise<void>;\n}\n\nexport interface IKeybanTpp {\n  claim(tppId: string, recipient: string): Promise<{ transactionHash: string }>;\n}\n\n/*\n * RPC types\n */\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype CastFn<T> = T extends (...args: any[]) => any ? T : never;\n\ninterface IRpc {\n  auth: IKeybanAuth;\n  ecdsa: IKeybanSigner;\n  eddsa: IKeybanSigner;\n  account: IKeybanAccount;\n  loyalty: IKeybanLoyalty;\n  clientShareStorage: IKeybanClientShareStorage;\n  tpp: IKeybanTpp;\n}\n\ntype Service = keyof IRpc;\ntype Method<S extends Service> = keyof IRpc[S] & string;\ntype ClassMethod<S extends Service, M extends Method<S>> = CastFn<IRpc[S][M]>;\n\ntype RpcCall<\n  S extends Service,\n  M extends Method<S>,\n  CM extends ClassMethod<S, M> = ClassMethod<S, M>,\n> = {\n  __KEYBAN_RPC: true;\n\n  service: S;\n  method: M;\n  params: Parameters<CM>;\n};\n\ntype RpcResult<\n  S extends Service,\n  M extends Method<S>,\n  CM extends ClassMethod<S, M> = ClassMethod<S, M>,\n> = [Awaited<ReturnType<CM>>, null] | [null, string];\n\n/*\n * RPC implementation\n */\n\nexport class RpcServer implements IRpc {\n  auth!: IKeybanAuth;\n  ecdsa!: IKeybanSigner;\n  eddsa!: IKeybanSigner;\n  account!: IKeybanAccount;\n  loyalty!: IKeybanLoyalty;\n  clientShareStorage!: IKeybanClientShareStorage;\n  tpp!: IKeybanTpp;\n\n  domains: Promise<string[]>;\n\n  constructor(services: IRpc, domains: Promise<string[]>) {\n    Object.assign(this, services);\n\n    this.domains = domains;\n\n    window.addEventListener(\"message\", this.handleMessage);\n  }\n\n  // Forced validation of service's methods, see listener below\n  static #definitions: {\n    [S in Service]: {\n      [M in Method<S>]: true;\n    };\n  } = {\n    auth: {\n      isAuthenticated: true,\n      getUser: true,\n      getLoginUrl: true,\n      getLogoutUrl: true,\n      passwordLogin: true,\n      passwordlessStart: true,\n      passwordlessLogin: true,\n    },\n    ecdsa: {\n      dkg: true,\n      sign: true,\n      publicKey: true,\n    },\n    eddsa: {\n      dkg: true,\n      sign: true,\n      publicKey: true,\n    },\n    account: {\n      getAddress: true,\n    },\n    loyalty: {\n      initAccountSetup: true,\n      submitAccountSetup: true,\n    },\n    clientShareStorage: {\n      get: true,\n      set: true,\n    },\n    tpp: {\n      claim: true,\n    },\n  };\n\n  handleMessage = async <S extends Service, M extends Method<S>>(\n    event: MessageEvent<RpcCall<S, M>>,\n  ) => {\n    if (!event.data.__KEYBAN_RPC) return;\n\n    try {\n      // Check the message originated from an allowed domain for this\n      // specific application.\n      const domains = await this.domains;\n      if (!domains.includes(event.origin))\n        throw new IFrameRpcError(\n          IFrameRpcError.types.InvalidOrigin,\n          \"RpcServer\",\n        );\n\n      const { service, method } = event.data;\n\n      // An attacker could possibly try to call a method on the service\n      // object that is not intended to be exposed. This  ensures the\n      // method is effectively allowed.\n      if (!RpcServer.#definitions[service]?.[method])\n        throw new IFrameRpcError(\n          IFrameRpcError.types.InvalidCall,\n          `RpcServer:${service}.${method}`,\n        );\n\n      const fn = this[service]?.[method] as ClassMethod<S, M>;\n      if (!fn)\n        throw new IFrameRpcError(\n          IFrameRpcError.types.InvalidCall,\n          `RpcServer:${service}.${method}`,\n        );\n\n      const result = await fn.apply(this[service], event.data.params);\n\n      event.ports[0].postMessage([result, null]);\n    } catch (error) {\n      let err = error;\n\n      if (!(err instanceof KeybanBaseError)) {\n        console.error(error);\n        err = new SdkError(\n          SdkError.types.UnknownIframeRpcError,\n          \"RpcServer\",\n          error as Error,\n        );\n      }\n\n      event.ports[0].postMessage([null, JSON.stringify(err)]);\n    }\n  };\n}\n\n/*\n * RPC client\n */\n\ntype RpcClientOptions = {\n  apiUrl: URL | string;\n  appId: string;\n  network: KeybanNetwork;\n};\n\nexport class RpcClient {\n  #iframeUrl: URL;\n  #iframe: Promise<HTMLIFrameElement>;\n\n  static #instances: Map<string, RpcClient> = new Map();\n\n  static #getIframeUrl({ apiUrl, appId, network }: RpcClientOptions) {\n    const iframeUrl = new URL(\"/signer-client/\", apiUrl);\n    iframeUrl.searchParams.set(\"appId\", appId);\n    iframeUrl.searchParams.set(\"network\", network);\n    return iframeUrl;\n  }\n\n  static getInstance(options: RpcClientOptions): RpcClient {\n    const key = RpcClient.#getIframeUrl(options).toString();\n    if (!RpcClient.#instances.has(key))\n      RpcClient.#instances.set(key, new RpcClient(options));\n\n    return RpcClient.#instances.get(key)!;\n  }\n\n  private constructor(options: RpcClientOptions) {\n    this.#iframeUrl = RpcClient.#getIframeUrl(options);\n\n    this.#iframe = new Promise((resolve, reject) => {\n      const iframe = Object.assign(document.createElement(\"iframe\"), {\n        src: this.#iframeUrl,\n        hidden: true,\n        onload: () => resolve(iframe),\n        onerror: reject,\n      });\n\n      document.body.appendChild(iframe);\n    });\n  }\n\n  destroy() {\n    this.#iframe.then((iframe) => document.body.removeChild(iframe));\n  }\n\n  async call<\n    S extends Service,\n    M extends Method<S>,\n    CM extends ClassMethod<S, M> = ClassMethod<S, M>,\n  >(\n    service: S,\n    method: M,\n    ...params: Parameters<CM>\n  ): Promise<Awaited<ReturnType<CM>>> {\n    const iframe = await this.#iframe;\n    const channel = new MessageChannel();\n\n    return await new Promise((resolve, reject) => {\n      channel.port1.onmessage = ({\n        data: [result, error],\n      }: MessageEvent<RpcResult<S, M>>) => {\n        channel.port1.close();\n        return error != null\n          ? reject(new KeybanBaseError(JSON.parse(error)))\n          : resolve(result);\n      };\n\n      const message: RpcCall<S, M> = {\n        __KEYBAN_RPC: true,\n        service,\n        method,\n        params,\n      };\n\n      iframe.contentWindow?.postMessage(message, this.#iframeUrl.origin, [\n        channel.port2,\n      ]);\n    });\n  }\n}\n"]}