diff --git a/api-specs/openrpc-dapp-api.json b/api-specs/openrpc-dapp-api.json index 3b9d71e70..330630765 100644 --- a/api-specs/openrpc-dapp-api.json +++ b/api-specs/openrpc-dapp-api.json @@ -175,6 +175,7 @@ "title": "KernelInfo", "type": "object", "description": "Represents a Wallet Gateway.", + "additionalProperties": false, "properties": { "id": { "title": "id", @@ -202,6 +203,7 @@ "title": "LedgerApiResult", "type": "object", "description": "Ledger Api configuration options", + "additionalProperties": false, "properties": { "response": { "title": "response", @@ -220,6 +222,7 @@ "title": "JsPrepareSubmissionRequest", "type": "object", "description": "Structure representing the request for prepare and execute calls", + "additionalProperties": false, "properties": { "commandId": { "$ref": "api-specs/openrpc-dapp-api.json#/components/schemas/CommandId" @@ -274,6 +277,7 @@ "title": "DisclosedContract", "type": "object", "description": "Structure representing a disclosed contract for transaction execution", + "additionalProperties": false, "properties": { "templateId": { "title": "templateId", @@ -503,6 +507,7 @@ "StatusEvent": { "title": "StatusEvent", "type": "object", + "additionalProperties": false, "properties": { "kernel": { "$ref": "api-specs/openrpc-dapp-api.json#/components/schemas/KernelInfo" @@ -525,6 +530,7 @@ "network": { "title": "network", "type": "object", + "additionalProperties": false, "description": "Network information, if connected to a network.", "properties": { "networkId": { @@ -536,6 +542,7 @@ "title": "LedgerApiConfig", "type": "object", "description": "Ledger API configuration.", + "additionalProperties": false, "properties": { "baseUrl": { "title": "baseUrl", @@ -553,6 +560,7 @@ "title": "session", "type": "object", "description": "Session information, if authenticated.", + "additionalProperties": false, "properties": { "accessToken": { "title": "accessToken", diff --git a/core/rpc-generator/src/components/client.ts b/core/rpc-generator/src/components/client.ts index 9af036aca..7c809ed4b 100644 --- a/core/rpc-generator/src/components/client.ts +++ b/core/rpc-generator/src/components/client.ts @@ -21,6 +21,21 @@ import { RpcTransport } from '@canton-network/core-rpc-transport' <%= methodTypings.toString("typescript").replace(/export type AnyOf[A-Za-z0-9]+ =(?:[\\r\\n]|.)*?;/gm, "") %> +type AsyncReturnType = T extends (...args: any[]) => Promise + ? R + : never + +export type RpcMethods = { + <% openrpcDocument.methods.forEach((method) => { %> + <%= method.name %>: { + params: Parameters<<%= _.upperFirst(method.name) %>> + result: AsyncReturnType<<%= _.upperFirst(method.name) %>> + } + <% }); %> +} + +export type RpcClientRequest = (method: M, params?: RpcMethods[M]['params']) => Promise + export class <%= className %> { public transport: RpcTransport; @@ -33,15 +48,21 @@ export class <%= className %> { * <%= method.summary %> */ // tslint:disable-next-line:max-line-length - public async request(method: "<%= method.name %>", ...params: Parameters<<%= _.upperFirst(method.name) %>>): ReturnType<<%= _.upperFirst(method.name) %>> + // public async request(method: "<%= method.name %>", ...params: Parameters<<%= _.upperFirst(method.name) %>>): ReturnType<<%= _.upperFirst(method.name) %>> <% }); %> - public async request(method: string, params?: RequestPayload['params']): Promise { - const response = await this.transport.submit({ method, params }); + + public async request(method: M, params?: RpcMethods[M]['params'][0]): Promise { + const submitParams = params ? { method, params } : { method } + + const response = await this.transport.submit({ + method, + params: submitParams, + }) if ('error' in response) { throw new Error('RPC error: ' + response.error.code + ' - ' + response.error.message); } else { - return response.result; + return response.result as RpcMethods[M]['result']; } } } diff --git a/core/splice-provider/src/SpliceProvider.ts b/core/splice-provider/src/SpliceProvider.ts index 1aee511b2..acf5f4f76 100644 --- a/core/splice-provider/src/SpliceProvider.ts +++ b/core/splice-provider/src/SpliceProvider.ts @@ -1,12 +1,17 @@ // Copyright (c) 2025-2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { RequestPayload } from '@canton-network/core-types' +import SpliceWalletJSONRPCDAppAPI, { + RpcMethods, +} from '@canton-network/core-wallet-dapp-rpc-client' export type EventListener = (...args: T[]) => void export interface SpliceProvider { - request(args: RequestPayload): Promise + request( + method: M, + params?: RpcMethods[M]['params'][0] + ): Promise on(event: string, listener: EventListener): SpliceProvider emit(event: string, ...args: T[]): boolean removeListener( @@ -17,12 +22,19 @@ export interface SpliceProvider { export abstract class SpliceProviderBase implements SpliceProvider { listeners: { [event: string]: EventListener[] } + _client: SpliceWalletJSONRPCDAppAPI - constructor() { + constructor(client: SpliceWalletJSONRPCDAppAPI) { this.listeners = {} // Event listeners + this._client = client } - abstract request(args: RequestPayload): Promise + public async request( + method: keyof RpcMethods, + params?: RpcMethods[typeof method]['params'][0] + ): Promise { + return this._client.request(method, params) + } // Event handling public on(event: string, listener: EventListener): SpliceProvider { diff --git a/core/splice-provider/src/SpliceProviderHttp.ts b/core/splice-provider/src/SpliceProviderHttp.ts index cf9216662..b681f8aea 100644 --- a/core/splice-provider/src/SpliceProviderHttp.ts +++ b/core/splice-provider/src/SpliceProviderHttp.ts @@ -1,11 +1,7 @@ // Copyright (c) 2025-2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { - isSpliceMessageEvent, - RequestPayload, - WalletEvent, -} from '@canton-network/core-types' +import { isSpliceMessageEvent, WalletEvent } from '@canton-network/core-types' import { HttpTransport } from '@canton-network/core-rpc-transport' import SpliceWalletJSONRPCDAppAPI from '@canton-network/core-wallet-dapp-rpc-client' import { SpliceProviderBase } from './SpliceProvider' @@ -23,7 +19,6 @@ let connection: GatewaySocket = null export class SpliceProviderHttp extends SpliceProviderBase { private sessionToken?: string - private client: SpliceWalletJSONRPCDAppAPI private createClient(sessionToken?: string): SpliceWalletJSONRPCDAppAPI { const transport = new HttpTransport(this.url, sessionToken) @@ -61,18 +56,17 @@ export class SpliceProviderHttp extends SpliceProviderBase { } constructor( + client: SpliceWalletJSONRPCDAppAPI, private url: URL, sessionToken?: string ) { - super() + super(client) if (sessionToken) { this.sessionToken = sessionToken this.openSocket(url, sessionToken) } - this.client = this.createClient(sessionToken) - // Listen for the auth success event sent from the WK UI popup to the SDK running in the parent window. window.addEventListener('message', async (event) => { if (!isSpliceMessageEvent(event)) return @@ -81,13 +75,13 @@ export class SpliceProviderHttp extends SpliceProviderBase { event.data.type === WalletEvent.SPLICE_WALLET_IDP_AUTH_SUCCESS ) { this.sessionToken = event.data.token - this.client = this.createClient(this.sessionToken) + this._client = this.createClient(this.sessionToken) this.openSocket(this.url, event.data.token) // We requery the status explicitly here, as it's not guaranteed that the socket will be open & authenticated // before the `onConnected` event is fired from the `addSession` RPC call. The dappApi.StatusResult and // dappApi.OnConnectedEvent are mapped manually to avoid dependency. - this.request({ method: 'status' }) + this.request('status') .then((status) => { this.emit('onConnected', status) }) @@ -100,13 +94,4 @@ export class SpliceProviderHttp extends SpliceProviderBase { } }) } - - public async request({ method, params }: RequestPayload): Promise { - return (await ( - this.client.request as ( - method: string, - params?: RequestPayload['params'] - ) => Promise - )(method, params)) as T - } } diff --git a/core/splice-provider/src/SpliceProviderWindow.ts b/core/splice-provider/src/SpliceProviderWindow.ts index 62008a0c3..995bc46b2 100644 --- a/core/splice-provider/src/SpliceProviderWindow.ts +++ b/core/splice-provider/src/SpliceProviderWindow.ts @@ -1,26 +1,15 @@ // Copyright (c) 2025-2026 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { RequestPayload } from '@canton-network/core-types' -import { WindowTransport } from '@canton-network/core-rpc-transport' +// import { RequestPayload } from '@canton-network/core-types' +// import { WindowTransport } from '@canton-network/core-rpc-transport' import SpliceWalletJSONRPCDAppAPI from '@canton-network/core-wallet-dapp-rpc-client' import { SpliceProviderBase } from './SpliceProvider.js' export class SpliceProviderWindow extends SpliceProviderBase { - private client: SpliceWalletJSONRPCDAppAPI + // private client: SpliceWalletJSONRPCDAppAPI - constructor() { - super() - const transport = new WindowTransport(window) - this.client = new SpliceWalletJSONRPCDAppAPI(transport) - } - - public async request({ method, params }: RequestPayload): Promise { - return (await ( - this.client.request as ( - method: string, - params?: RequestPayload['params'] - ) => Promise - )(method, params)) as T + constructor(client: SpliceWalletJSONRPCDAppAPI) { + super(client) } } diff --git a/core/types/src/index.ts b/core/types/src/index.ts index 6e879c232..e7b8f0fed 100644 --- a/core/types/src/index.ts +++ b/core/types/src/index.ts @@ -19,9 +19,7 @@ export type PartyId = z.infer */ export const RequestPayload = z.object({ method: z.string(), - params: z.optional( - z.union([z.array(z.unknown()), z.record(z.string(), z.unknown())]) - ), + params: z.optional(z.union([z.array(z.unknown()), z.looseObject({})])), }) export type RequestPayload = z.infer diff --git a/core/wallet-dapp-remote-rpc-client/src/index.ts b/core/wallet-dapp-remote-rpc-client/src/index.ts index b29417784..9dbc6eee1 100644 --- a/core/wallet-dapp-remote-rpc-client/src/index.ts +++ b/core/wallet-dapp-remote-rpc-client/src/index.ts @@ -70,7 +70,6 @@ export interface DisclosedContract { contractId?: ContractId createdEventBlob: CreatedEventBlob synchronizerId?: SynchronizerId - [k: string]: any } /** * @@ -122,7 +121,6 @@ export interface KernelInfo { clientType: ClientType url?: Url userUrl?: UserUrl - [k: string]: any } /** * @@ -161,7 +159,6 @@ export type BaseUrl = string */ export interface LedgerApiConfig { baseUrl: BaseUrl - [k: string]: any } /** * @@ -171,7 +168,6 @@ export interface LedgerApiConfig { export interface Network { networkId: NetworkId ledgerApi?: LedgerApiConfig - [k: string]: any } /** * @@ -193,7 +189,6 @@ export type UserId = string export interface Session { accessToken: AccessToken userId: UserId - [k: string]: any } export interface StatusEvent { kernel: KernelInfo @@ -202,7 +197,6 @@ export interface StatusEvent { networkReason?: NetworkReason network?: Network session?: Session - [k: string]: any } export interface ConnectResult { userUrl: UserUrl @@ -418,7 +412,6 @@ export interface PrepareReturnParams { disclosedContracts?: DisclosedContracts synchronizerId?: SynchronizerId packageIdSelectionPreference?: PackageIdSelectionPreference - [k: string]: any } /** * @@ -433,7 +426,6 @@ export interface PrepareExecuteParams { disclosedContracts?: DisclosedContracts synchronizerId?: SynchronizerId packageIdSelectionPreference?: PackageIdSelectionPreference - [k: string]: any } /** * @@ -469,7 +461,6 @@ export interface PrepareExecuteResult { */ export interface LedgerApiResult { response: Response - [k: string]: any } /** * @@ -516,6 +507,77 @@ export type OnAccountsChanged = () => Promise export type RequestAccounts = () => Promise export type OnTxChanged = () => Promise +type AsyncReturnType = T extends (...args: any[]) => Promise + ? R + : never + +export type RpcMethods = { + status: { + params: Parameters + result: AsyncReturnType + } + + connect: { + params: Parameters + result: AsyncReturnType + } + + disconnect: { + params: Parameters + result: AsyncReturnType + } + + darsAvailable: { + params: Parameters + result: AsyncReturnType + } + + prepareReturn: { + params: Parameters + result: AsyncReturnType + } + + prepareExecute: { + params: Parameters + result: AsyncReturnType + } + + ledgerApi: { + params: Parameters + result: AsyncReturnType + } + + onConnected: { + params: Parameters + result: AsyncReturnType + } + + onStatusChanged: { + params: Parameters + result: AsyncReturnType + } + + onAccountsChanged: { + params: Parameters + result: AsyncReturnType + } + + requestAccounts: { + params: Parameters + result: AsyncReturnType + } + + onTxChanged: { + params: Parameters + result: AsyncReturnType + } +} + +export type RpcClientRequest = ( + method: M, + params?: RpcMethods[M]['params'] +) => Promise + export class SpliceWalletJSONRPCRemoteDAppAPI { public transport: RpcTransport @@ -527,115 +589,84 @@ export class SpliceWalletJSONRPCRemoteDAppAPI { * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'status', - ...params: Parameters - ): ReturnType + // public async request(method: "status", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'connect', - ...params: Parameters - ): ReturnType + // public async request(method: "connect", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'disconnect', - ...params: Parameters - ): ReturnType + // public async request(method: "disconnect", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'darsAvailable', - ...params: Parameters - ): ReturnType + // public async request(method: "darsAvailable", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'prepareReturn', - ...params: Parameters - ): ReturnType + // public async request(method: "prepareReturn", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'prepareExecute', - ...params: Parameters - ): ReturnType + // public async request(method: "prepareExecute", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'ledgerApi', - ...params: Parameters - ): ReturnType + // public async request(method: "ledgerApi", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'onConnected', - ...params: Parameters - ): ReturnType + // public async request(method: "onConnected", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'onStatusChanged', - ...params: Parameters - ): ReturnType + // public async request(method: "onStatusChanged", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'onAccountsChanged', - ...params: Parameters - ): ReturnType + // public async request(method: "onAccountsChanged", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'requestAccounts', - ...params: Parameters - ): ReturnType + // public async request(method: "requestAccounts", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'onTxChanged', - ...params: Parameters - ): ReturnType + // public async request(method: "onTxChanged", ...params: Parameters): ReturnType + + public async request( + method: M, + params?: RpcMethods[M]['params'][0] + ): Promise { + const submitParams = params ? { method, params } : { method } - public async request( - method: string, - params?: RequestPayload['params'] - ): Promise { - const response = await this.transport.submit({ method, params }) + const response = await this.transport.submit({ + method, + params: submitParams, + }) if ('error' in response) { throw new Error( @@ -645,7 +676,7 @@ export class SpliceWalletJSONRPCRemoteDAppAPI { response.error.message ) } else { - return response.result + return response.result as RpcMethods[M]['result'] } } } diff --git a/core/wallet-dapp-rpc-client/src/index.ts b/core/wallet-dapp-rpc-client/src/index.ts index 4d1eab651..79c7491c1 100644 --- a/core/wallet-dapp-rpc-client/src/index.ts +++ b/core/wallet-dapp-rpc-client/src/index.ts @@ -70,7 +70,6 @@ export interface DisclosedContract { contractId?: ContractId createdEventBlob: CreatedEventBlob synchronizerId?: SynchronizerId - [k: string]: any } /** * @@ -122,7 +121,6 @@ export interface KernelInfo { clientType: ClientType url?: Url userUrl?: UserUrl - [k: string]: any } /** * @@ -161,7 +159,6 @@ export type BaseUrl = string */ export interface LedgerApiConfig { baseUrl: BaseUrl - [k: string]: any } /** * @@ -171,7 +168,6 @@ export interface LedgerApiConfig { export interface Network { networkId: NetworkId ledgerApi?: LedgerApiConfig - [k: string]: any } /** * @@ -193,7 +189,6 @@ export type UserId = string export interface Session { accessToken: AccessToken userId: UserId - [k: string]: any } export type Dar = string export type Dars = Dar[] @@ -405,7 +400,6 @@ export interface PrepareReturnParams { disclosedContracts?: DisclosedContracts synchronizerId?: SynchronizerId packageIdSelectionPreference?: PackageIdSelectionPreference - [k: string]: any } /** * @@ -420,7 +414,6 @@ export interface PrepareExecuteParams { disclosedContracts?: DisclosedContracts synchronizerId?: SynchronizerId packageIdSelectionPreference?: PackageIdSelectionPreference - [k: string]: any } /** * @@ -440,7 +433,6 @@ export interface StatusEvent { networkReason?: NetworkReason network?: Network session?: Session - [k: string]: any } /** * @@ -464,7 +456,6 @@ export interface PrepareExecuteResult { */ export interface LedgerApiResult { response: Response - [k: string]: any } /** * @@ -509,6 +500,67 @@ export type OnAccountsChanged = () => Promise export type RequestAccounts = () => Promise export type OnTxChanged = () => Promise +type AsyncReturnType = T extends (...args: any[]) => Promise + ? R + : never + +export type RpcMethods = { + status: { + params: Parameters + result: AsyncReturnType + } + + connect: { + params: Parameters + result: AsyncReturnType + } + + disconnect: { + params: Parameters + result: AsyncReturnType + } + + darsAvailable: { + params: Parameters + result: AsyncReturnType + } + + prepareReturn: { + params: Parameters + result: AsyncReturnType + } + + prepareExecute: { + params: Parameters + result: AsyncReturnType + } + + ledgerApi: { + params: Parameters + result: AsyncReturnType + } + + onAccountsChanged: { + params: Parameters + result: AsyncReturnType + } + + requestAccounts: { + params: Parameters + result: AsyncReturnType + } + + onTxChanged: { + params: Parameters + result: AsyncReturnType + } +} + +export type RpcClientRequest = ( + method: M, + params?: RpcMethods[M]['params'] +) => Promise + export class SpliceWalletJSONRPCDAppAPI { public transport: RpcTransport @@ -520,97 +572,72 @@ export class SpliceWalletJSONRPCDAppAPI { * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'status', - ...params: Parameters - ): ReturnType + // public async request(method: "status", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'connect', - ...params: Parameters - ): ReturnType + // public async request(method: "connect", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'disconnect', - ...params: Parameters - ): ReturnType + // public async request(method: "disconnect", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'darsAvailable', - ...params: Parameters - ): ReturnType + // public async request(method: "darsAvailable", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'prepareReturn', - ...params: Parameters - ): ReturnType + // public async request(method: "prepareReturn", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'prepareExecute', - ...params: Parameters - ): ReturnType + // public async request(method: "prepareExecute", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'ledgerApi', - ...params: Parameters - ): ReturnType + // public async request(method: "ledgerApi", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'onAccountsChanged', - ...params: Parameters - ): ReturnType + // public async request(method: "onAccountsChanged", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'requestAccounts', - ...params: Parameters - ): ReturnType + // public async request(method: "requestAccounts", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'onTxChanged', - ...params: Parameters - ): ReturnType + // public async request(method: "onTxChanged", ...params: Parameters): ReturnType + + public async request( + method: M, + params?: RpcMethods[M]['params'][0] + ): Promise { + const submitParams = params ? { method, params } : { method } - public async request( - method: string, - params?: RequestPayload['params'] - ): Promise { - const response = await this.transport.submit({ method, params }) + const response = await this.transport.submit({ + method, + params: submitParams, + }) if ('error' in response) { throw new Error( @@ -620,7 +647,7 @@ export class SpliceWalletJSONRPCDAppAPI { response.error.message ) } else { - return response.result + return response.result as RpcMethods[M]['result'] } } } diff --git a/core/wallet-dapp-rpc-client/src/openrpc.json b/core/wallet-dapp-rpc-client/src/openrpc.json index 3b9d71e70..330630765 100644 --- a/core/wallet-dapp-rpc-client/src/openrpc.json +++ b/core/wallet-dapp-rpc-client/src/openrpc.json @@ -175,6 +175,7 @@ "title": "KernelInfo", "type": "object", "description": "Represents a Wallet Gateway.", + "additionalProperties": false, "properties": { "id": { "title": "id", @@ -202,6 +203,7 @@ "title": "LedgerApiResult", "type": "object", "description": "Ledger Api configuration options", + "additionalProperties": false, "properties": { "response": { "title": "response", @@ -220,6 +222,7 @@ "title": "JsPrepareSubmissionRequest", "type": "object", "description": "Structure representing the request for prepare and execute calls", + "additionalProperties": false, "properties": { "commandId": { "$ref": "api-specs/openrpc-dapp-api.json#/components/schemas/CommandId" @@ -274,6 +277,7 @@ "title": "DisclosedContract", "type": "object", "description": "Structure representing a disclosed contract for transaction execution", + "additionalProperties": false, "properties": { "templateId": { "title": "templateId", @@ -503,6 +507,7 @@ "StatusEvent": { "title": "StatusEvent", "type": "object", + "additionalProperties": false, "properties": { "kernel": { "$ref": "api-specs/openrpc-dapp-api.json#/components/schemas/KernelInfo" @@ -525,6 +530,7 @@ "network": { "title": "network", "type": "object", + "additionalProperties": false, "description": "Network information, if connected to a network.", "properties": { "networkId": { @@ -536,6 +542,7 @@ "title": "LedgerApiConfig", "type": "object", "description": "Ledger API configuration.", + "additionalProperties": false, "properties": { "baseUrl": { "title": "baseUrl", @@ -553,6 +560,7 @@ "title": "session", "type": "object", "description": "Session information, if authenticated.", + "additionalProperties": false, "properties": { "accessToken": { "title": "accessToken", diff --git a/core/wallet-user-rpc-client/src/index.ts b/core/wallet-user-rpc-client/src/index.ts index 58937ac56..3a7adc763 100644 --- a/core/wallet-user-rpc-client/src/index.ts +++ b/core/wallet-user-rpc-client/src/index.ts @@ -466,6 +466,107 @@ export type GetTransaction = ( ) => Promise export type ListTransactions = () => Promise +type AsyncReturnType = T extends (...args: any[]) => Promise + ? R + : never + +export type RpcMethods = { + addNetwork: { + params: Parameters + result: AsyncReturnType + } + + removeNetwork: { + params: Parameters + result: AsyncReturnType + } + + listNetworks: { + params: Parameters + result: AsyncReturnType + } + + addIdp: { + params: Parameters + result: AsyncReturnType + } + + removeIdp: { + params: Parameters + result: AsyncReturnType + } + + listIdps: { + params: Parameters + result: AsyncReturnType + } + + createWallet: { + params: Parameters + result: AsyncReturnType + } + + setPrimaryWallet: { + params: Parameters + result: AsyncReturnType + } + + removeWallet: { + params: Parameters + result: AsyncReturnType + } + + listWallets: { + params: Parameters + result: AsyncReturnType + } + + syncWallets: { + params: Parameters + result: AsyncReturnType + } + + sign: { + params: Parameters + result: AsyncReturnType + } + + execute: { + params: Parameters + result: AsyncReturnType + } + + addSession: { + params: Parameters + result: AsyncReturnType + } + + removeSession: { + params: Parameters + result: AsyncReturnType + } + + listSessions: { + params: Parameters + result: AsyncReturnType + } + + getTransaction: { + params: Parameters + result: AsyncReturnType + } + + listTransactions: { + params: Parameters + result: AsyncReturnType + } +} + +export type RpcClientRequest = ( + method: M, + params?: RpcMethods[M]['params'] +) => Promise + export class SpliceWalletJSONRPCUserAPI { public transport: RpcTransport @@ -477,169 +578,120 @@ export class SpliceWalletJSONRPCUserAPI { * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'addNetwork', - ...params: Parameters - ): ReturnType + // public async request(method: "addNetwork", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'removeNetwork', - ...params: Parameters - ): ReturnType + // public async request(method: "removeNetwork", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'listNetworks', - ...params: Parameters - ): ReturnType + // public async request(method: "listNetworks", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'addIdp', - ...params: Parameters - ): ReturnType + // public async request(method: "addIdp", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'removeIdp', - ...params: Parameters - ): ReturnType + // public async request(method: "removeIdp", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'listIdps', - ...params: Parameters - ): ReturnType + // public async request(method: "listIdps", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'createWallet', - ...params: Parameters - ): ReturnType + // public async request(method: "createWallet", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'setPrimaryWallet', - ...params: Parameters - ): ReturnType + // public async request(method: "setPrimaryWallet", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'removeWallet', - ...params: Parameters - ): ReturnType + // public async request(method: "removeWallet", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'listWallets', - ...params: Parameters - ): ReturnType + // public async request(method: "listWallets", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'syncWallets', - ...params: Parameters - ): ReturnType + // public async request(method: "syncWallets", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'sign', - ...params: Parameters - ): ReturnType + // public async request(method: "sign", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'execute', - ...params: Parameters - ): ReturnType + // public async request(method: "execute", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'addSession', - ...params: Parameters - ): ReturnType + // public async request(method: "addSession", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'removeSession', - ...params: Parameters - ): ReturnType + // public async request(method: "removeSession", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'listSessions', - ...params: Parameters - ): ReturnType + // public async request(method: "listSessions", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'getTransaction', - ...params: Parameters - ): ReturnType + // public async request(method: "getTransaction", ...params: Parameters): ReturnType /** * */ // tslint:disable-next-line:max-line-length - public async request( - method: 'listTransactions', - ...params: Parameters - ): ReturnType + // public async request(method: "listTransactions", ...params: Parameters): ReturnType + + public async request( + method: M, + params?: RpcMethods[M]['params'][0] + ): Promise { + const submitParams = params ? { method, params } : { method } - public async request( - method: string, - params?: RequestPayload['params'] - ): Promise { - const response = await this.transport.submit({ method, params }) + const response = await this.transport.submit({ + method, + params: submitParams, + }) if ('error' in response) { throw new Error( @@ -649,7 +701,7 @@ export class SpliceWalletJSONRPCUserAPI { response.error.message ) } else { - return response.result + return response.result as RpcMethods[M]['result'] } } } diff --git a/examples/portfolio/src/contexts/ConnectionProvider.tsx b/examples/portfolio/src/contexts/ConnectionProvider.tsx index 703cde9ab..ee94c68b5 100644 --- a/examples/portfolio/src/contexts/ConnectionProvider.tsx +++ b/examples/portfolio/src/contexts/ConnectionProvider.tsx @@ -48,7 +48,7 @@ export const ConnectionProvider: React.FC<{ children: React.ReactNode }> = ({ const provider = window.canton if (!provider) return provider - .request({ method: 'status' }) + .request('status') .then((result) => setConnectionStatus((c) => ({ ...c, @@ -85,9 +85,7 @@ export const ConnectionProvider: React.FC<{ children: React.ReactNode }> = ({ const provider = window.canton if (!provider || !connectionStatus.connected) return provider - .request({ - method: 'requestAccounts', - }) + .request('requestAccounts') .then((wallets) => { const requestedAccounts = wallets as sdk.dappAPI.RequestAccountsResult diff --git a/examples/portfolio/src/services/portfolio-service-implementation.ts b/examples/portfolio/src/services/portfolio-service-implementation.ts index 0c8c5e44c..b9fc3df6e 100644 --- a/examples/portfolio/src/services/portfolio-service-implementation.ts +++ b/examples/portfolio/src/services/portfolio-service-implementation.ts @@ -107,10 +107,7 @@ export const createTransfer = async ({ const provider = window.canton // TODO: check success - await provider?.request({ - method: 'prepareExecute', - params: request, - }) + await provider?.request('prepareExecute', request) } export const exerciseTransfer = async ({ @@ -149,10 +146,7 @@ export const exerciseTransfer = async ({ const provider = window.canton // TODO: check success - await provider?.request({ - method: 'prepareExecute', - params: request, - }) + await provider?.request('prepareExecute', request) } export const listPendingTransfers = async ({ @@ -214,10 +208,7 @@ export const createAllocation = async ({ const provider = window.canton // TODO: check success - await provider?.request({ - method: 'prepareExecute', - params: request, - }) + await provider?.request('prepareExecute', request) } export const listAllocations = async ({ @@ -267,10 +258,7 @@ export const withdrawAllocation = async ({ const provider = window.canton // TODO: check success - await provider?.request({ - method: 'prepareExecute', - params: request, - }) + await provider?.request('prepareExecute', request) } export const listAllocationInstructions = async ({ @@ -339,8 +327,5 @@ export const tap = async ({ const provider = window.canton // TODO: check success - await provider?.request({ - method: 'prepareExecute', - params: request, - }) + await provider?.request('prepareExecute', request) } diff --git a/sdk/dapp-sdk/package.json b/sdk/dapp-sdk/package.json index cb82b90e4..a900c59f1 100644 --- a/sdk/dapp-sdk/package.json +++ b/sdk/dapp-sdk/package.json @@ -24,6 +24,7 @@ "test": "echo \"Warning: no test specified\"" }, "dependencies": { + "@canton-network/core-rpc-transport": "workspace:^", "@canton-network/core-splice-provider": "workspace:^", "@canton-network/core-types": "workspace:^", "@canton-network/core-wallet-dapp-remote-rpc-client": "workspace:^", diff --git a/sdk/dapp-sdk/src/dapp-api/rpc-gen/typings.ts b/sdk/dapp-sdk/src/dapp-api/rpc-gen/typings.ts index 15211b0c4..de0360d84 100644 --- a/sdk/dapp-sdk/src/dapp-api/rpc-gen/typings.ts +++ b/sdk/dapp-sdk/src/dapp-api/rpc-gen/typings.ts @@ -69,7 +69,6 @@ export interface DisclosedContract { contractId?: ContractId createdEventBlob: CreatedEventBlob synchronizerId?: SynchronizerId - [k: string]: any } /** * @@ -121,7 +120,6 @@ export interface KernelInfo { clientType: ClientType url?: Url userUrl?: UserUrl - [k: string]: any } /** * @@ -160,7 +158,6 @@ export type BaseUrl = string */ export interface LedgerApiConfig { baseUrl: BaseUrl - [k: string]: any } /** * @@ -170,7 +167,6 @@ export interface LedgerApiConfig { export interface Network { networkId: NetworkId ledgerApi?: LedgerApiConfig - [k: string]: any } /** * @@ -192,7 +188,6 @@ export type UserId = string export interface Session { accessToken: AccessToken userId: UserId - [k: string]: any } export type Dar = string export type Dars = Dar[] @@ -404,7 +399,6 @@ export interface PrepareReturnParams { disclosedContracts?: DisclosedContracts synchronizerId?: SynchronizerId packageIdSelectionPreference?: PackageIdSelectionPreference - [k: string]: any } /** * @@ -419,7 +413,6 @@ export interface PrepareExecuteParams { disclosedContracts?: DisclosedContracts synchronizerId?: SynchronizerId packageIdSelectionPreference?: PackageIdSelectionPreference - [k: string]: any } /** * @@ -439,7 +432,6 @@ export interface StatusEvent { networkReason?: NetworkReason network?: Network session?: Session - [k: string]: any } /** * @@ -463,7 +455,6 @@ export interface PrepareExecuteResult { */ export interface LedgerApiResult { response: Response - [k: string]: any } /** * diff --git a/sdk/dapp-sdk/src/provider.ts b/sdk/dapp-sdk/src/provider.ts index a73c65c29..30751655a 100644 --- a/sdk/dapp-sdk/src/provider.ts +++ b/sdk/dapp-sdk/src/provider.ts @@ -6,7 +6,7 @@ import { SpliceProvider, EventListener, } from '@canton-network/core-splice-provider' -import { DiscoverResult, RequestPayload } from '@canton-network/core-types' +import { DiscoverResult } from '@canton-network/core-types' import { SpliceProviderHttp, SpliceProviderWindow, @@ -17,10 +17,20 @@ import * as dappRemoteAPI from '@canton-network/core-wallet-dapp-remote-rpc-clie import { LedgerApiParams, PrepareExecuteParams, + RpcMethods, } from '@canton-network/core-wallet-dapp-rpc-client' import { ErrorCode } from './error.js' -import { Session, StatusEvent } from './dapp-api/rpc-gen/typings' +import { + PrepareReturnParams, + Session, + StatusEvent, +} from './dapp-api/rpc-gen/typings' import { popup } from '@canton-network/core-wallet-ui-components' +import { + HttpTransport, + WindowTransport, +} from '@canton-network/core-rpc-transport' +import SpliceWalletJSONRPCDAppAPI from '@canton-network/core-wallet-dapp-rpc-client' /** * The Provider class abstracts over the different types of SpliceProviders (Window and HTTP). @@ -32,11 +42,21 @@ export class Provider implements SpliceProvider { constructor({ walletType, url }: DiscoverResult, session?: Session) { if (walletType == 'extension') { + const windowTransport = new WindowTransport(window) + const windowClient = new SpliceWalletJSONRPCDAppAPI(windowTransport) + this.providerType = ProviderType.WINDOW - this.provider = new SpliceProviderWindow() + this.provider = new SpliceProviderWindow(windowClient) } else if (walletType == 'remote') { + const remoteTransport = new HttpTransport( + new URL(url), + session?.accessToken + ) + const remoteClient = new SpliceWalletJSONRPCDAppAPI(remoteTransport) + this.providerType = ProviderType.HTTP this.provider = new SpliceProviderHttp( + remoteClient, new URL(url), session?.accessToken ) @@ -45,36 +65,52 @@ export class Provider implements SpliceProvider { } } - request(args: RequestPayload): Promise { + request( + method: M, + params: RpcMethods[M]['params'][0] + ) { if (this.providerType === ProviderType.WINDOW) - return this.provider.request(args) + return this.provider.request(method, params) const controller = dappController(this.provider) - switch (args.method) { - case 'status': - return controller.status() as Promise - case 'connect': - return controller.connect() as Promise - case 'disconnect': - return controller.disconnect() as Promise - case 'darsAvailable': - return controller.darsAvailable() as Promise - case 'ledgerApi': - return controller.ledgerApi( - args.params as LedgerApiParams - ) as Promise - case 'prepareExecute': - return controller.prepareExecute( - args.params as PrepareExecuteParams - ) as Promise - case 'prepareReturn': - return controller.prepareReturn( - args.params as dappAPI.PrepareReturnParams - ) as Promise - case 'requestAccounts': - return controller.requestAccounts() as Promise - default: + + switch (method) { + case 'status': { + return controller.status() + } + case 'connect': { + return controller.connect() + } + case 'disconnect': { + return controller.disconnect() + } + case 'darsAvailable': { + return controller.darsAvailable() + } + case 'ledgerApi': { + if (!params) { + throw new Error('Missing parameters for ledgerApi') + } + return controller.ledgerApi(params as LedgerApiParams) + } + case 'prepareExecute': { + if (!params) { + throw new Error('Missing parameters for prepareExecute') + } + return controller.prepareExecute(params as PrepareExecuteParams) + } + case 'prepareReturn': { + if (!params) { + throw new Error('Missing parameters for prepareReturn') + } + return controller.prepareReturn(params as PrepareReturnParams) + } + case 'requestAccounts': { + return controller.requestAccounts() + } + default: { throw new Error('Unsupported method') + } } } @@ -112,14 +148,12 @@ const withTimeout = ( export const dappController = (provider: SpliceProvider) => buildController({ connect: async (): Promise => { - const response = await provider.request({ - method: 'connect', - }) + const response = await provider.request('connect') if (response.session) { return response } else { - popup.open(response.userUrl ?? '') + popup.open(response.kernel.userUrl ?? '') const promise = new Promise( (resolve, reject) => { // 5 minutes timeout @@ -141,27 +175,12 @@ export const dappController = (provider: SpliceProvider) => return promise } }, - disconnect: async () => { - return await provider.request({ - method: 'disconnect', - }) - }, - darsAvailable: async () => { - return provider.request({ - method: 'darsAvailable', - }) - }, + disconnect: async () => provider.request('disconnect'), + darsAvailable: async () => provider.request('darsAvailable'), ledgerApi: async (params: LedgerApiParams) => - provider.request({ - method: 'ledgerApi', - params, - }), + provider.request('ledgerApi', params), prepareExecute: async (params: PrepareExecuteParams) => { - const response = - await provider.request({ - method: 'prepareExecute', - params, - }) + const response = await provider.request('prepareExecute', params) if (response.userUrl) popup.open(response.userUrl) @@ -191,18 +210,10 @@ export const dappController = (provider: SpliceProvider) => return promise }, - prepareReturn: async (params: dappAPI.PrepareReturnParams) => - provider.request({ - method: 'prepareReturn', - params, - }), - status: async () => { - return provider.request({ method: 'status' }) - }, - requestAccounts: async () => - provider.request({ - method: 'requestAccounts', - }), + prepareReturn: async (params: PrepareReturnParams) => + provider.request('prepareReturn', params), + status: async () => provider.request('status'), + requestAccounts: async () => provider.request('requestAccounts'), onAccountsChanged: async () => { throw new Error('Only for events.') }, diff --git a/sdk/dapp-sdk/src/provider/open.ts b/sdk/dapp-sdk/src/provider/open.ts index e212c1e47..742803de0 100644 --- a/sdk/dapp-sdk/src/provider/open.ts +++ b/sdk/dapp-sdk/src/provider/open.ts @@ -16,7 +16,7 @@ export async function open(): Promise { throw new Error('No previous session found') } - const userUrl = session.userUrl ?? '' + const userUrl = session.kernel.userUrl ?? '' if (discovery.walletType === 'remote') { popup.open(userUrl) diff --git a/sdk/dapp-sdk/src/provider/request.ts b/sdk/dapp-sdk/src/provider/request.ts index 2b6579e4e..adc583885 100644 --- a/sdk/dapp-sdk/src/provider/request.ts +++ b/sdk/dapp-sdk/src/provider/request.ts @@ -34,9 +34,7 @@ export async function connect( storage.setKernelDiscovery(result) const provider = injectProvider(result) - const response = await provider.request({ - method: 'connect', - }) + const response = await provider.request('connect') if (response.session) { storage.setKernelSession(response) @@ -72,9 +70,7 @@ export async function connect( export async function disconnect(): Promise { try { const provider = assertProvider() - await provider.request({ - method: 'disconnect', - }) + await provider.request('disconnect') } finally { clearAllLocalState({ closePopup: true }) } @@ -83,37 +79,25 @@ export async function disconnect(): Promise { } export async function status(): Promise { - return await assertProvider().request({ - method: 'status', - }) + return await assertProvider().request('status') } export async function darsAvailable(): Promise { - return await assertProvider().request({ - method: 'darsAvailable', - }) + return await assertProvider().request('darsAvailable') } export async function requestAccounts(): Promise { - return await assertProvider().request({ - method: 'requestAccounts', - }) + return await assertProvider().request('requestAccounts') } export async function prepareExecute( params: dappAPI.PrepareExecuteParams ): Promise { - return await assertProvider().request({ - method: 'prepareExecute', - params, - }) + return await assertProvider().request('prepareExecute', params) } export async function ledgerApi( params: dappAPI.LedgerApiParams ): Promise { - return await assertProvider().request({ - method: 'ledgerApi', - params, - }) + return await assertProvider().request('ledgerApi', params) } diff --git a/wallet-gateway/extension/src/dapp-api/rpc-gen/typings.ts b/wallet-gateway/extension/src/dapp-api/rpc-gen/typings.ts index 15211b0c4..de0360d84 100644 --- a/wallet-gateway/extension/src/dapp-api/rpc-gen/typings.ts +++ b/wallet-gateway/extension/src/dapp-api/rpc-gen/typings.ts @@ -69,7 +69,6 @@ export interface DisclosedContract { contractId?: ContractId createdEventBlob: CreatedEventBlob synchronizerId?: SynchronizerId - [k: string]: any } /** * @@ -121,7 +120,6 @@ export interface KernelInfo { clientType: ClientType url?: Url userUrl?: UserUrl - [k: string]: any } /** * @@ -160,7 +158,6 @@ export type BaseUrl = string */ export interface LedgerApiConfig { baseUrl: BaseUrl - [k: string]: any } /** * @@ -170,7 +167,6 @@ export interface LedgerApiConfig { export interface Network { networkId: NetworkId ledgerApi?: LedgerApiConfig - [k: string]: any } /** * @@ -192,7 +188,6 @@ export type UserId = string export interface Session { accessToken: AccessToken userId: UserId - [k: string]: any } export type Dar = string export type Dars = Dar[] @@ -404,7 +399,6 @@ export interface PrepareReturnParams { disclosedContracts?: DisclosedContracts synchronizerId?: SynchronizerId packageIdSelectionPreference?: PackageIdSelectionPreference - [k: string]: any } /** * @@ -419,7 +413,6 @@ export interface PrepareExecuteParams { disclosedContracts?: DisclosedContracts synchronizerId?: SynchronizerId packageIdSelectionPreference?: PackageIdSelectionPreference - [k: string]: any } /** * @@ -439,7 +432,6 @@ export interface StatusEvent { networkReason?: NetworkReason network?: Network session?: Session - [k: string]: any } /** * @@ -463,7 +455,6 @@ export interface PrepareExecuteResult { */ export interface LedgerApiResult { response: Response - [k: string]: any } /** * diff --git a/wallet-gateway/remote/src/dapp-api/controller.ts b/wallet-gateway/remote/src/dapp-api/controller.ts index 50d6389df..a1a6e5728 100644 --- a/wallet-gateway/remote/src/dapp-api/controller.ts +++ b/wallet-gateway/remote/src/dapp-api/controller.ts @@ -268,7 +268,7 @@ async function prepareSubmission( userId: string, partyId: string, synchronizerId: string, - params: PrepareExecuteParams | PrepareReturnParams, + params: PrepareReturnParams, ledgerClient: LedgerClient ): Promise> { return await ledgerClient.postWithRetry( diff --git a/wallet-gateway/remote/src/dapp-api/rpc-gen/typings.ts b/wallet-gateway/remote/src/dapp-api/rpc-gen/typings.ts index 029aacdb2..b1cd2a541 100644 --- a/wallet-gateway/remote/src/dapp-api/rpc-gen/typings.ts +++ b/wallet-gateway/remote/src/dapp-api/rpc-gen/typings.ts @@ -69,7 +69,6 @@ export interface DisclosedContract { contractId?: ContractId createdEventBlob: CreatedEventBlob synchronizerId?: SynchronizerId - [k: string]: any } /** * @@ -121,7 +120,6 @@ export interface KernelInfo { clientType: ClientType url?: Url userUrl?: UserUrl - [k: string]: any } /** * @@ -160,7 +158,6 @@ export type BaseUrl = string */ export interface LedgerApiConfig { baseUrl: BaseUrl - [k: string]: any } /** * @@ -170,7 +167,6 @@ export interface LedgerApiConfig { export interface Network { networkId: NetworkId ledgerApi?: LedgerApiConfig - [k: string]: any } /** * @@ -192,7 +188,6 @@ export type UserId = string export interface Session { accessToken: AccessToken userId: UserId - [k: string]: any } export interface StatusEvent { kernel: KernelInfo @@ -201,7 +196,6 @@ export interface StatusEvent { networkReason?: NetworkReason network?: Network session?: Session - [k: string]: any } export interface ConnectResult { userUrl: UserUrl @@ -417,7 +411,6 @@ export interface PrepareReturnParams { disclosedContracts?: DisclosedContracts synchronizerId?: SynchronizerId packageIdSelectionPreference?: PackageIdSelectionPreference - [k: string]: any } /** * @@ -432,7 +425,6 @@ export interface PrepareExecuteParams { disclosedContracts?: DisclosedContracts synchronizerId?: SynchronizerId packageIdSelectionPreference?: PackageIdSelectionPreference - [k: string]: any } /** * @@ -468,7 +460,6 @@ export interface PrepareExecuteResult { */ export interface LedgerApiResult { response: Response - [k: string]: any } /** * diff --git a/wallet-gateway/remote/src/utils.ts b/wallet-gateway/remote/src/utils.ts index 573a0675e..d22c43cf7 100644 --- a/wallet-gateway/remote/src/utils.ts +++ b/wallet-gateway/remote/src/utils.ts @@ -38,7 +38,6 @@ export interface PrepareParams { readAs?: string[] disclosedContracts?: DisclosedContracts packageIdSelectionPreference?: PackageIdSelectionPreference - [k: string]: unknown } export function ledgerPrepareParams( diff --git a/yarn.lock b/yarn.lock index eb4677cc3..9a44cdfa1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1986,6 +1986,7 @@ __metadata: version: 0.0.0-use.local resolution: "@canton-network/dapp-sdk@workspace:sdk/dapp-sdk" dependencies: + "@canton-network/core-rpc-transport": "workspace:^" "@canton-network/core-splice-provider": "workspace:^" "@canton-network/core-types": "workspace:^" "@canton-network/core-wallet-dapp-remote-rpc-client": "workspace:^"