diff --git a/examples/rdt/rdt.ts b/examples/rdt/rdt.ts index 323d0628..b3b21f27 100644 --- a/examples/rdt/rdt.ts +++ b/examples/rdt/rdt.ts @@ -3,6 +3,7 @@ import { DataRequestBuilder, DataRequestStateClient, RadixDappToolkit, + RadixDappToolkitOptions, } from '../../src' import { appLogger } from '../logger/state' import { @@ -98,7 +99,7 @@ const options = { dataRequestStateClient, }, useCache: false, -} +} satisfies RadixDappToolkitOptions setTimeout(() => { appLogger.debug('RDT initialized with', options) diff --git a/src/_types.ts b/src/_types.ts index 28fafc99..d634eb86 100644 --- a/src/_types.ts +++ b/src/_types.ts @@ -82,16 +82,26 @@ export type ExplorerConfig = { accountsPath: string } +type WalletDataRequest = Parameters[0] + +export type WalletRequest = + | { type: 'sendTransaction'; payload: SendTransactionInput } + | { type: 'dataRequest'; payload: WalletDataRequest } + +export type RequestInterceptor = ( + input: T +) => Promise + export type OptionalRadixDappToolkitOptions = { logger: AppLogger onDisconnect: () => void - explorer: ExplorerConfig gatewayBaseUrl: string applicationName: string applicationVersion: string useCache: boolean providers: Partial + requestInterceptor: RequestInterceptor } export type RadixDappToolkitOptions = { @@ -106,6 +116,7 @@ export type SendTransactionResult = ResultAsync< }, { error: string + jsError?: unknown message?: string transactionIntentHash?: string status?: TransactionStatus @@ -132,14 +143,20 @@ export type ButtonApi = { status$: Observable } +export type WalletDataRequestError = { + error: string + message?: string + jsError?: unknown +} + export type WalletDataRequestResult = ResultAsync< WalletData, - { error: string; message?: string } + WalletDataRequestError > export type AwaitedWalletDataRequestResult = Result< WalletData, - { error: string; message?: string } + WalletDataRequestError > export type WalletApi = { @@ -179,3 +196,17 @@ export type WalletDataState = { proofs: SignedChallenge[] persona?: Persona } + +export type RequestInterceptorFactoryOutput = ReturnType< + typeof requestInterceptorFactory +> +export const requestInterceptorFactory = + (requestInterceptor: RequestInterceptor) => + (walletRequest: T) => + ResultAsync.fromPromise( + requestInterceptor(walletRequest), + (jsError) => ({ + error: 'requestInterceptorError', + jsError, + }) + ) diff --git a/src/data-request/data-request.ts b/src/data-request/data-request.ts index fb83ead2..4a3bc513 100644 --- a/src/data-request/data-request.ts +++ b/src/data-request/data-request.ts @@ -14,6 +14,7 @@ import { DataRequestStateClient } from './data-request-state' import { WalletData } from '../state/types' import { AwaitedWalletDataRequestResult, + RequestInterceptorFactoryOutput, WalletDataRequestResult, } from '../_types' @@ -25,12 +26,14 @@ export const DataRequestClient = ({ walletClient, useCache, dataRequestStateClient, + requestInterceptor, }: { stateClient: StateClient requestItemClient: RequestItemClient walletClient: WalletClient dataRequestStateClient: DataRequestStateClient useCache: boolean + requestInterceptor: RequestInterceptorFactoryOutput }) => { let challengeGenerator: | (() => ResultAsync) @@ -121,50 +124,59 @@ export const DataRequestClient = ({ !stateClient.getState().walletData.persona && walletDataRequest.discriminator === 'authorizedRequest' - const { id } = requestItemClient.add( - isLoginRequest ? 'loginRequest' : 'dataRequest' - ) - return walletClient - .request(walletDataRequest, id) - .mapErr( - ({ error, message }): { error: string; message?: string } => ({ - error: error, - message: message, - }) - ) - .andThen(transformWalletResponseToRdtWalletData) - .andThen((response) => { - if (dataRequestControl) - return dataRequestControl(response) - .map(() => { - requestItemClient.updateStatus({ id, status: 'success' }) - return response + return requestInterceptor({ + type: 'dataRequest', + payload: walletDataRequest, + }) + .map((walletDataRequest) => { + const { id } = requestItemClient.add( + isLoginRequest ? 'loginRequest' : 'dataRequest' + ) + return { walletDataRequest, id } + }) + .andThen(({ walletDataRequest, id }) => + walletClient + .request(walletDataRequest, id) + .mapErr( + ({ error, message }): { error: string; message?: string } => ({ + error: error, + message: message, }) - .mapErr((error) => { - requestItemClient.updateStatus({ - id, - status: 'fail', - error: error.error, + ) + .andThen(transformWalletResponseToRdtWalletData) + .andThen((response) => { + if (dataRequestControl) + return dataRequestControl(response) + .map(() => { + requestItemClient.updateStatus({ id, status: 'success' }) + return response + }) + .mapErr((error) => { + requestItemClient.updateStatus({ + id, + status: 'fail', + error: error.error, + }) + return error + }) + + requestItemClient.updateStatus({ id, status: 'success' }) + return ok(response) + }) + .map((walletData) => { + if (!oneTime) + stateClient.setState({ + loggedInTimestamp: Date.now().toString(), + walletData, + sharedData: transformWalletRequestToSharedData( + walletDataRequest, + stateClient.getState().sharedData + ), }) - return error - }) - requestItemClient.updateStatus({ id, status: 'success' }) - return ok(response) - }) - .map((walletData) => { - if (!oneTime) - stateClient.setState({ - loggedInTimestamp: Date.now().toString(), - walletData, - sharedData: transformWalletRequestToSharedData( - walletDataRequest, - stateClient.getState().sharedData - ), + return walletData }) - - return walletData - }) + ) }) const setState = (...items: DataRequestBuilderItem[]) => { diff --git a/src/radix-dapp-toolkit.ts b/src/radix-dapp-toolkit.ts index 89489e89..8742f9e5 100644 --- a/src/radix-dapp-toolkit.ts +++ b/src/radix-dapp-toolkit.ts @@ -28,7 +28,11 @@ import { ButtonApi, GatewayApi, RadixDappToolkitOptions, + SendTransactionInput, WalletApi, + WalletRequest, + requestInterceptorFactory, + RequestInterceptor, } from './_types' import { mergeMap, withLatestFrom } from 'rxjs/operators' import { WalletData } from './state/types' @@ -55,6 +59,8 @@ export const RadixDappToolkit = ( applicationName, applicationVersion, useCache = true, + requestInterceptor = (async ({ payload }: WalletRequest) => + payload) as RequestInterceptor, } = options || {} const storageKey = `rdt:${dAppDefinitionAddress}:${networkId}` @@ -119,6 +125,8 @@ export const RadixDappToolkit = ( const dataRequestStateClient = providers?.dataRequestStateClient ?? DataRequestStateClient({}) + const withInterceptor = requestInterceptorFactory(requestInterceptor) + const dataRequestClient = providers?.dataRequestClient ?? DataRequestClient({ @@ -127,6 +135,7 @@ export const RadixDappToolkit = ( walletClient, useCache, dataRequestStateClient, + requestInterceptor: withInterceptor, }) const disconnect = () => { @@ -327,7 +336,11 @@ export const RadixDappToolkit = ( dataRequestClient.provideConnectResponseCallback, updateSharedData: () => dataRequestClient.updateSharedData(), sendOneTimeRequest: dataRequestClient.sendOneTimeRequest, - sendTransaction: walletClient.sendTransaction, + sendTransaction: (input: SendTransactionInput) => + withInterceptor({ + type: 'sendTransaction', + payload: input, + }).andThen(walletClient.sendTransaction), walletData$: stateClient.walletData$, getWalletData: () => stateClient.getWalletData(), }