From e060434e9aeffbf09666c68cebbc33189666ca57 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Fri, 26 Apr 2024 16:49:30 +0100 Subject: [PATCH] code: update rcr flow --- .../simple-dapp/public/.well-known/radix.json | 2 +- examples/simple-dapp/src/main.ts | 14 +- examples/simple-dapp/src/sst-env.d.ts | 6 +- examples/simple-dapp/sst.config.ts | 18 +- .../dapp-toolkit/src/radix-dapp-toolkit.ts | 1 + .../request-items/request-item-client.ts | 41 +- .../wallet-request/request-items/subjects.ts | 1 - .../transport/radix-connect-relay/api.ts | 212 +++++++++ .../radix-connect-relay/deep-link.ts | 2 +- .../radix-connect-relay-client.ts | 420 ++++++------------ 10 files changed, 423 insertions(+), 294 deletions(-) create mode 100644 packages/dapp-toolkit/src/wallet-request/transport/radix-connect-relay/api.ts diff --git a/examples/simple-dapp/public/.well-known/radix.json b/examples/simple-dapp/public/.well-known/radix.json index 587a44c8..2a1b0c64 100644 --- a/examples/simple-dapp/public/.well-known/radix.json +++ b/examples/simple-dapp/public/.well-known/radix.json @@ -1,5 +1,5 @@ { - "callbackPath": "#connect", + "callbackPath": "/connect", "dApps": [ { "dAppDefinitionAddress": "account_tdx_2_12yf9gd53yfep7a669fv2t3wm7nz9zeezwd04n02a433ker8vza6rhe" diff --git a/examples/simple-dapp/src/main.ts b/examples/simple-dapp/src/main.ts index dcb21b8a..20a25e3c 100644 --- a/examples/simple-dapp/src/main.ts +++ b/examples/simple-dapp/src/main.ts @@ -23,9 +23,7 @@ const stateStore = storageClient.getPartition('state') const content = document.getElementById('app')! -content.innerHTML = ` - - +content.innerHTML = `
@@ -115,11 +113,11 @@ resetButton.onclick = () => { window.location.replace(window.location.origin) } -continueButton.onclick = () => { - requestItemClient.getPendingItems().map((items) => { - if (items[0]) rcr.resume(items[0].interactionId) - }) -} +// continueButton.onclick = () => { +// requestItemClient.getPendingItems().map((items) => { +// if (items[0]) rcr.resume(items[0].interactionId) +// }) +// } oneTimeRequest.onclick = () => { dAppToolkit.walletApi.sendOneTimeRequest( diff --git a/examples/simple-dapp/src/sst-env.d.ts b/examples/simple-dapp/src/sst-env.d.ts index 5a221983..7696eae3 100644 --- a/examples/simple-dapp/src/sst-env.d.ts +++ b/examples/simple-dapp/src/sst-env.d.ts @@ -1,5 +1,7 @@ /// -interface ImportMetaEnv {} +interface ImportMetaEnv { + +} interface ImportMeta { readonly env: ImportMetaEnv -} +} \ No newline at end of file diff --git a/examples/simple-dapp/sst.config.ts b/examples/simple-dapp/sst.config.ts index 62a12e80..66738f6b 100644 --- a/examples/simple-dapp/sst.config.ts +++ b/examples/simple-dapp/sst.config.ts @@ -1,10 +1,26 @@ import type { SSTConfig } from 'sst' import { StaticSite } from 'sst/constructs' +import { exec } from 'child_process' + +const getBranchName = () => + new Promise((resolve, reject) => { + exec('git branch --show-current', (err, stdout) => { + if (err) { + reject(err) + } else { + const branchName = stdout.trim().split('/').slice(-1)[0] + resolve(branchName) + } + }) + }) + +const branchName = await getBranchName() + export default { config() { return { - name: 'simple-dapp', + name: `simple-dapp-${branchName}`, region: process.env.AWS_REGION, profile: process.env.AWS_PROFILE, stage: process.env.AWS_STAGE, diff --git a/packages/dapp-toolkit/src/radix-dapp-toolkit.ts b/packages/dapp-toolkit/src/radix-dapp-toolkit.ts index 2db52ae2..53998833 100644 --- a/packages/dapp-toolkit/src/radix-dapp-toolkit.ts +++ b/packages/dapp-toolkit/src/radix-dapp-toolkit.ts @@ -74,6 +74,7 @@ export const RadixDappToolkit = ( storageClient, gatewayClient, transports: options.providers?.transports, + requestItemClient: options.providers?.requestItemClient, }, }) diff --git a/packages/dapp-toolkit/src/wallet-request/request-items/request-item-client.ts b/packages/dapp-toolkit/src/wallet-request/request-items/request-item-client.ts index 9d112650..5834082d 100644 --- a/packages/dapp-toolkit/src/wallet-request/request-items/request-item-client.ts +++ b/packages/dapp-toolkit/src/wallet-request/request-items/request-item-client.ts @@ -1,11 +1,11 @@ import type { RequestItem, RequestStatusTypes } from 'radix-connect-common' -import { Subscription } from 'rxjs' +import { Subscription, filter, firstValueFrom, merge, mergeMap, of } from 'rxjs' import { RequestItemSubjects } from './subjects' import { Logger } from '../../helpers' -import { ErrorType } from '../../error' +import { ErrorType, SdkError } from '../../error' import { WalletInteraction } from '../../schemas' import { StorageProvider } from '../../storage' -import { ResultAsync, errAsync } from 'neverthrow' +import { Result, ResultAsync, errAsync } from 'neverthrow' export type RequestItemClientInput = { logger?: Logger subjects?: RequestItemSubjects @@ -15,13 +15,11 @@ export type RequestItemClient = ReturnType export const RequestItemClient = (input: RequestItemClientInput) => { const logger = input?.logger?.getSubLogger({ name: 'RequestItemClient' }) const subscriptions = new Subscription() - const subjects = input.subjects || RequestItemSubjects() const storageClient = input.providers.storageClient - storageClient.getItemList().map((items) => { - logger?.debug({ method: 'initRequestItems', items }) - subjects.items.next(items) - }) + storageClient.getItemList().map((items) => { + logger?.debug({ method: 'initRequestItems', items }) + }) const createItem = ({ type, @@ -114,6 +112,31 @@ export const RequestItemClient = (input: RequestItemClientInput) => { Object.values(items).filter((item) => !item.walletResponse), ) + const storeChange$ = merge(storageClient.storage$, of(null)) + + const waitForWalletResponse = ( + interactionId: string, + ): ResultAsync => + ResultAsync.fromPromise( + firstValueFrom( + storeChange$.pipe( + mergeMap(() => + storageClient + .getItemById(interactionId) + .mapErr(() => SdkError('FailedToGetRequestItem', interactionId)), + ), + filter((result): result is Result => { + if (result.isErr()) return false + return ( + result.value?.interactionId === interactionId && + ['success', 'fail'].includes(result.value.status) + ) + }), + ), + ), + () => SdkError('FailedToListenForWalletResponse', interactionId), + ).andThen((result) => result) + return { add, cancel, @@ -122,6 +145,8 @@ export const RequestItemClient = (input: RequestItemClientInput) => { getById: (id: string) => storageClient.getItemById(id), getPendingItems, store: storageClient, + waitForWalletResponse, + storeChange$, destroy: () => { subscriptions.unsubscribe() }, diff --git a/packages/dapp-toolkit/src/wallet-request/request-items/subjects.ts b/packages/dapp-toolkit/src/wallet-request/request-items/subjects.ts index 0872374a..cb59500a 100644 --- a/packages/dapp-toolkit/src/wallet-request/request-items/subjects.ts +++ b/packages/dapp-toolkit/src/wallet-request/request-items/subjects.ts @@ -10,5 +10,4 @@ export type RequestItemSubjects = ReturnType export const RequestItemSubjects = () => ({ initialized: new BehaviorSubject(false), onChange: new Subject(), - items: new BehaviorSubject([]), }) diff --git a/packages/dapp-toolkit/src/wallet-request/transport/radix-connect-relay/api.ts b/packages/dapp-toolkit/src/wallet-request/transport/radix-connect-relay/api.ts new file mode 100644 index 00000000..738f0698 --- /dev/null +++ b/packages/dapp-toolkit/src/wallet-request/transport/radix-connect-relay/api.ts @@ -0,0 +1,212 @@ +import { Ok, Result, ResultAsync, err, ok } from 'neverthrow' +import { SdkError } from '../../../error' +import { Logger, fetchWrapper, parseJSON } from '../../../helpers' +import { WalletInteraction, WalletInteractionResponse } from '../../../schemas' +import { EncryptionClient, transformBufferToSealbox } from '../../encryption' +import { ActiveSession } from '../../../../dist' +import { + Subject, + filter, + first, + firstValueFrom, + merge, + of, + switchMap, +} from 'rxjs' +import { Buffer } from 'buffer' + +export type RadixConnectRelayApi = ReturnType +export const RadixConnectRelayApi = (input: { + baseUrl: string + logger?: Logger + providers: { encryptionClient: EncryptionClient } +}) => { + const baseUrl = input.baseUrl + const logger = input.logger?.getSubLogger({ name: 'RadixConnectRelayApi' }) + const encryptionClient = input.providers.encryptionClient + + const decryptResponse = ( + secretHex: string, + value: string, + ): ResultAsync => + transformBufferToSealbox(Buffer.from(value, 'hex')) + .asyncAndThen(({ ciphertextAndAuthTag, iv }) => + encryptionClient.decrypt( + ciphertextAndAuthTag, + Buffer.from(secretHex, 'hex'), + iv, + ), + ) + .andThen((decrypted) => + parseJSON(decrypted.toString('utf-8')), + ) + .mapErr(() => SdkError('FailedToDecrypt', '')) + + const encryptWalletInteraction = ( + walletInteraction: WalletInteraction, + secret: Buffer, + ): ResultAsync => + encryptionClient + .encrypt(Buffer.from(JSON.stringify(walletInteraction), 'utf-8'), secret) + .mapErr(() => + SdkError( + 'FailEncryptWalletInteraction', + walletInteraction.interactionId, + ), + ) + .map((sealedBoxProps) => sealedBoxProps.combined.toString('hex')) + + const sendRequest = ( + sessionId: string, + secretHex: string, + walletInteraction: WalletInteraction, + ): ResultAsync => { + logger?.debug({ method: 'sendRequest', sessionId, walletInteraction }) + return encryptWalletInteraction( + walletInteraction, + Buffer.from(secretHex, 'hex'), + ).andThen((encryptedWalletInteraction) => + fetchWrapper( + fetch(baseUrl, { + method: 'POST', + body: JSON.stringify({ + method: 'sendRequest', + sessionId, + data: encryptedWalletInteraction, + }), + }), + ) + .map(() => undefined) + .mapErr(() => SdkError('FailedToSendRequestToRadixConnectRelay', '')), + ) + } + + const getResponses = (session: ActiveSession) => + fetchWrapper( + fetch(baseUrl, { + method: 'POST', + body: JSON.stringify({ + method: 'getResponses', + sessionId: session.sessionId, + }), + }), + ) + .mapErr(() => SdkError('FailedToGetRequestsFromRadixConnectRelay', '')) + .andThen((value) => + ResultAsync.combine( + value.data.map((encryptedWalletInteraction) => + decryptResponse(session.sharedSecret, encryptedWalletInteraction), + ), + ), + ) + + const sendHandshakeRequest = ( + sessionId: string, + publicKeyHex: string, + ): ResultAsync => { + logger?.debug({ method: 'sendHandshakeRequest', sessionId, publicKeyHex }) + return fetchWrapper( + fetch(baseUrl, { + method: 'POST', + body: JSON.stringify({ + method: 'sendHandshakeRequest', + sessionId, + data: publicKeyHex, + }), + }), + ) + .map(() => { + logger?.debug({ + method: 'sendHandshakeRequestToRadixConnectRelay.success', + }) + }) + .mapErr(() => { + logger?.debug({ + method: 'sendHandshakeRequestToRadixConnectRelay.error', + }) + return SdkError('FailedToSendHandshakeRequestToRadixConnectRelay', '') + }) + } + + const getHandshakeResponse = ( + sessionId: string, + ): ResultAsync => { + const getPublicKeyFromData = (data: { + publicKey: string + }): Result => { + const publicKeyRaw = data?.publicKey + + if (!publicKeyRaw) err({ reason: 'NotFound' }) + + const publicKey = Buffer.from(publicKeyRaw, 'hex').toString('utf-8') + + return parseJSON(publicKey) + .mapErr(() => ({ reason: 'FailedToParsePublicKey' })) + .andThen((parsed): Result => { + logger?.debug({ parsed }) + return parsed?.publicKey + ? ok(parsed.publicKey) + : err({ reason: 'NotFound' }) + }) + } + + const sendApiRequest = (retry: number) => { + logger?.debug({ method: 'getHandshakeResponse', sessionId, retry }) + return fetchWrapper<{ publicKey: string }>( + fetch(baseUrl, { + method: 'POST', + body: JSON.stringify({ + method: 'getHandshakeResponse', + sessionId, + }), + }), + ).andThen(({ data }) => getPublicKeyFromData(data)) + } + + return ResultAsync.fromPromise( + firstValueFrom( + of(null).pipe( + switchMap(() => { + const trigger = new Subject() + return merge(trigger, of(0)).pipe( + switchMap((retry) => + sendApiRequest(retry).mapErr((err) => { + trigger.next(retry + 1) + return err + }), + ), + filter((result): result is Ok => { + return result.isOk() + }), + first(), + ) + }), + ), + ), + (error) => { + return SdkError('FailedToGetHandshakeResponseToRadixConnectRelay', '') + }, + ).andThen((result) => result) + + // return fetchWrapper<{ publicKey: string }>( + // fetch(baseUrl, { + // method: 'POST', + // body: JSON.stringify({ + // method: 'getHandshakeResponse', + // sessionId, + // }), + // }), + // ) + // .andThen(({ data }) => getPublicKeyFromData(data)) + // .mapErr((err) => + // SdkError('FailedToGetHandshakeResponseToRadixConnectRelay', ''), + // ) + } + + return { + sendRequest, + getResponses, + sendHandshakeRequest, + getHandshakeResponse, + } +} diff --git a/packages/dapp-toolkit/src/wallet-request/transport/radix-connect-relay/deep-link.ts b/packages/dapp-toolkit/src/wallet-request/transport/radix-connect-relay/deep-link.ts index ec021631..28b3e5af 100644 --- a/packages/dapp-toolkit/src/wallet-request/transport/radix-connect-relay/deep-link.ts +++ b/packages/dapp-toolkit/src/wallet-request/transport/radix-connect-relay/deep-link.ts @@ -17,7 +17,7 @@ export const DeepLinkClient = (input: { const walletResponseSubject = new BehaviorSubject>({}) - const isCallbackUrl = () => window.location.hash.includes(callBackPath) + const isCallbackUrl = () => window.location.href.includes(callBackPath) const shouldHandleWalletCallback = () => platform.type === 'mobile' && isCallbackUrl() diff --git a/packages/dapp-toolkit/src/wallet-request/transport/radix-connect-relay/radix-connect-relay-client.ts b/packages/dapp-toolkit/src/wallet-request/transport/radix-connect-relay/radix-connect-relay-client.ts index 54538e86..a26ae5d3 100644 --- a/packages/dapp-toolkit/src/wallet-request/transport/radix-connect-relay/radix-connect-relay-client.ts +++ b/packages/dapp-toolkit/src/wallet-request/transport/radix-connect-relay/radix-connect-relay-client.ts @@ -1,32 +1,17 @@ -import { Result, ResultAsync, errAsync, okAsync } from 'neverthrow' -import { fetchWrapper } from '../../../helpers/fetch-wrapper' -import { - BehaviorSubject, - Subscription, - filter, - firstValueFrom, - merge, - mergeMap, - switchMap, - tap, - delay, - of, - Subject, -} from 'rxjs' -import { EncryptionClient, transformBufferToSealbox } from '../../encryption' +import { ResultAsync, err, errAsync, ok, okAsync } from 'neverthrow' +import { Subscription, filter, switchMap, tap } from 'rxjs' +import { EncryptionClient } from '../../encryption' import { ActiveSession, PendingSession, - Session, SessionClient, } from '../../session/session' -import { Buffer } from 'buffer' import type { CallbackFns, WalletInteraction, WalletInteractionResponse, } from '../../../schemas' -import { Logger, isMobile, parseJSON } from '../../../helpers' +import { Logger, isMobile } from '../../../helpers' import { SdkError } from '../../../error' import { DeepLinkClient } from './deep-link' import { IdentityClient } from '../../identity/identity' @@ -34,7 +19,7 @@ import { RequestItemClient } from '../../request-items/request-item-client' import Bowser from 'bowser' import { StorageProvider } from '../../../storage' import { Curve25519 } from '../../crypto' -import { RequestItem } from 'radix-connect-common' +import { RadixConnectRelayApi } from './api' export type RadixConnectRelayClient = ReturnType export const RadixConnectRelayClient = (input: { @@ -64,7 +49,7 @@ export const RadixConnectRelayClient = (input: { logger, origin, walletUrl, - callBackPath: '#connect', + callBackPath: '/connect', userAgent, }) @@ -86,190 +71,61 @@ export const RadixConnectRelayClient = (input: { }, }) - const apiV1Url = `${baseUrl}/api/v1` - - const sendHandshakeRequestToRadixConnectRelay = ( - sessionId: string, - publicKeyHex: string, - ): ResultAsync => - fetchWrapper( - fetch(apiV1Url, { - method: 'POST', - body: JSON.stringify({ - method: 'sendHandshakeRequest', - sessionId, - data: publicKeyHex, - }), - }), - ) - .map(() => { - logger?.debug({ - method: 'sendHandshakeRequestToRadixConnectRelay.success', - }) - }) - .mapErr(() => { - logger?.debug({ - method: 'sendHandshakeRequestToRadixConnectRelay.error', - }) - return SdkError('FailedToSendHandshakeRequestToRadixConnectRelay', '') - }) - - const getHandshakeResponseFromRadixConnectRelay = (sessionId: string) => - fetchWrapper( - fetch(apiV1Url, { - method: 'POST', - body: JSON.stringify({ - method: 'getHandshakeResponse', - sessionId, - }), - }), - ) - .map(({ data }) => { - return parseJSON( - // @ts-ignore - Buffer.from(data.publicKey, 'hex').toString('utf-8'), - )._unsafeUnwrap().publicKey - }) - .mapErr(() => - SdkError('FailedToGetHandshakeResponseToRadixConnectRelay', ''), - ) - - const sendRequestToRadixConnectRelay = ( - sessionId: string, - data: any, - ): ResultAsync => - fetchWrapper( - fetch(apiV1Url, { - method: 'POST', - body: JSON.stringify({ - method: 'sendRequest', - sessionId, - data, - }), - }), - ) - .map(() => undefined) - .mapErr(() => SdkError('FailedToSendRequestToRadixConnectRelay', '')) - - const getResponsesFromRadixConnectRelay = ( - sessionId: string, - ): ResultAsync< - { - status: number - data: unknown[] - }, - SdkError - > => - fetchWrapper( - fetch(apiV1Url, { - method: 'POST', - body: JSON.stringify({ - method: 'getResponses', - sessionId: sessionId, - }), - }), - ).mapErr(() => SdkError('FailedToGetRequestsFromRadixConnectRelay', '')) - - const decryptResponseFactory = - (secret: Buffer) => - ( - value: string, - ): ResultAsync< - WalletInteractionResponse, - { reason: string; shouldRetry: boolean } - > => - transformBufferToSealbox(Buffer.from(value, 'hex')) - .asyncAndThen(({ ciphertextAndAuthTag, iv }) => - encryptionClient.decrypt(ciphertextAndAuthTag, secret, iv), - ) - .andThen((decrypted) => - parseJSON(decrypted.toString('utf-8')), - ) - .mapErr(() => ({ reason: 'FailedToDecrypt', shouldRetry: true })) + const radixConnectRelayApi = RadixConnectRelayApi({ + baseUrl: `${baseUrl}/api/v1`, + logger, + providers: { encryptionClient }, + }) const subscriptions = new Subscription() - const waitForWalletResponse = ( - interactionId: string, - ): ResultAsync => - ResultAsync.fromPromise( - firstValueFrom( - merge(requestItemClient.store.storage$, of(null)).pipe( - mergeMap(() => - requestItemClient.store - .getItemById(interactionId) - .mapErr(() => SdkError('FailedToGetRequestItem', interactionId)), - ), - filter((result): result is Result => { - if (result.isErr()) return false - return ( - result.value?.interactionId === interactionId && - ['success', 'fail'].includes(result.value.status) - ) - }), - ), - ), - () => SdkError('FailedToListenForWalletResponse', interactionId), - ).andThen((result) => result) - - const encryptWalletInteraction = ( - walletInteraction: WalletInteraction, - sharedSecret: Buffer, - ): ResultAsync => - encryptionClient - .encrypt( - Buffer.from(JSON.stringify(walletInteraction), 'utf-8'), - sharedSecret, - ) - .mapErr(() => - SdkError( - 'FailEncryptWalletInteraction', - walletInteraction.interactionId, - ), - ) - .map((sealedBoxProps) => sealedBoxProps.combined.toString('hex')) - const handleLinkingRequest = ( session: PendingSession, walletInteraction: WalletInteraction, ) => { const { sessionId } = session - const url = new URL(origin) - url.hash = 'connect' + const url = new URL(`${origin}/connect`) url.searchParams.set('sessionId', sessionId) const childWindow = window.open(url.toString())! + if (!childWindow) { + logger?.debug({ + method: 'handleLinkingRequest.error', + reason: 'FailedToChildOpenWindow', + }) + return err( + SdkError('FailedToChildOpenWindow', walletInteraction.interactionId), + ) + } + return identityClient .get('dApp') - .mapErr(() => - SdkError('FailedToGetDappIdentity', walletInteraction.interactionId), - ) .andThen((dAppIdentity) => - sendHandshakeRequestToRadixConnectRelay( - sessionId, - dAppIdentity.getPublicKey(), - ), - ) - .andThen(() => - sessionClient - .patchSession(sessionId, { sentToWallet: true }) - .andThen(() => - deepLinkClient.deepLinkToWallet( - { - sessionId, - origin, - }, - childWindow, - ), - ) - .mapErr(() => - SdkError('FailedToUpdateSession', walletInteraction.interactionId), + ResultAsync.combine([ + sessionClient.patchSession(sessionId, { sentToWallet: true }), + radixConnectRelayApi.sendHandshakeRequest( + sessionId, + dAppIdentity.getPublicKey(), + ), + deepLinkClient.deepLinkToWallet( + { + sessionId, + origin, + }, + childWindow, ), + ]), ) + .mapErr(() => { + return SdkError( + 'FailedToUpdateSession', + walletInteraction.interactionId, + ) + }) .andThen(() => - waitForWalletResponse(walletInteraction.interactionId).map( - (item) => item.walletResponse!, - ), + requestItemClient + .waitForWalletResponse(walletInteraction.interactionId) + .map((item) => item.walletResponse!), ) } @@ -277,17 +133,13 @@ export const RadixConnectRelayClient = (input: { activeSession: ActiveSession, walletInteraction: WalletInteraction, ) => - encryptWalletInteraction( + radixConnectRelayApi.sendRequest( + activeSession.sessionId, + activeSession.sharedSecret, walletInteraction, - Buffer.from(activeSession.sharedSecret, 'hex'), - ).andThen((encryptedWalletInteraction) => - sendRequestToRadixConnectRelay( - activeSession.sessionId, - encryptedWalletInteraction, - ), ) - const send = ( + const sendToWallet = ( walletInteraction: WalletInteraction, callbackFns: Partial, ): ResultAsync => @@ -299,115 +151,139 @@ export const RadixConnectRelayClient = (input: { .andThen((session) => { return session.status === 'Pending' ? handleLinkingRequest(session, walletInteraction) - : resume(walletInteraction.interactionId) + : resume(session, walletInteraction.interactionId) }) const handleWalletCallback = (values: Record) => { - const { sessionId, interactionId } = values + const { sessionId } = values if (sessionId) { return sessionClient.getSessionById(sessionId).andThen((session) => { if (session?.status === 'Pending' && session.sentToWallet) { - return getHandshakeResponseFromRadixConnectRelay(session.sessionId) - .andThen((walletPublicKey) => - walletPublicKey - ? sessionClient.convertToActiveSession( + return radixConnectRelayApi + .getHandshakeResponse(session.sessionId) + .andThen((walletPublicKey) => { + return sessionClient + .convertToActiveSession(sessionId, walletPublicKey) + .mapErr((err) => { + alert(JSON.stringify({ err })) + return err + }) + }) + .andThen((activeSession) => { + return requestItemClient.getPendingItems().andThen(([item]) => + ResultAsync.combine([ + deepLinkClient.deepLinkToWallet({ + sessionId, + interactionId: item.interactionId, + }), + radixConnectRelayApi.sendRequest( sessionId, - walletPublicKey, - ) - : errAsync(SdkError('WalletPublicKeyUnavailable', '')), - ) - .map(() => { - window.close() + activeSession.sharedSecret, + item.walletInteraction, + ), + requestItemClient.patch( + item.walletInteraction.interactionId, + { + sentToWallet: true, + }, + ), + ]), + ) }) - } else if (session?.status === 'Active' && interactionId) { - return requestItemClient.getPendingItems().map((pendingItems) => { - const sendToWallet = pendingItems.filter( - (item) => item.sentToWallet, - ) - if (sendToWallet.length) { - const decryptWalletInteraction = decryptResponseFactory( - Buffer.from(session.sharedSecret, 'hex'), + } else if (session?.status === 'Active') { + return requestItemClient.getPendingItems().andThen((items) => { + const pendingIds = new Set(items.map((item) => item.interactionId)) + return radixConnectRelayApi + .getResponses(session) + .map((items) => + items.filter((item) => pendingIds.has(item.interactionId)), ) - getResponsesFromRadixConnectRelay(session.sessionId).map( - (value) => { - for (const encryptedWalletInteraction of value.data) { - decryptWalletInteraction( - encryptedWalletInteraction as string, - ).map((decrypted) => { - requestItemClient - .patch(decrypted.interactionId, { - walletResponse: decrypted, - }) - .map(() => { - window.close() - }) - }) - } - }, + .andThen((walletResponses) => + ResultAsync.combine( + walletResponses.map((walletResponse) => + requestItemClient.patch(walletResponse.interactionId, { + walletResponse, + }), + ), + ).map(() => { + if (walletResponses.length) window.close() + }), ) - } }) } return errAsync(SdkError('SessionNotFound', sessionId)) }) } + return okAsync(undefined) } subscriptions.add( deepLinkClient.walletResponse$ .pipe( filter((values) => Object.values(values).length > 0), - tap((item) => handleWalletCallback(item)), + switchMap((item) => handleWalletCallback(item)), ) .subscribe(), ) deepLinkClient.handleWalletCallback() - const resume = (interactionId: string) => { - sessionClient.findActiveSession().andThen((session) => { - if (session) { - const url = new URL(origin) - url.hash = 'connect' - url.searchParams.set('sessionId', session.sessionId) - url.searchParams.set('interactionId', interactionId) - const childWindow = window.open(url.toString())! + const resume = (session: ActiveSession, interactionId: string) => { + const url = new URL(`${origin}/connect`) + // url.hash = 'connect' + url.searchParams.set('sessionId', session.sessionId) + url.searchParams.set('interactionId', interactionId) + const childWindow = window.open(url.toString())! - return requestItemClient.getPendingItems().andThen((pendingItems) => { - const pendingItem = pendingItems.find( - (item) => item.interactionId === interactionId, - ) - if (pendingItem) { - return requestItemClient - .patch(interactionId, { sentToWallet: true }) - .andThen(() => - sendEncryptedRequest(session, pendingItem.walletInteraction), - ) - .andThen(() => - deepLinkClient.deepLinkToWallet( - { - sessionId: session.sessionId, - interactionId: pendingItem.interactionId, - }, - childWindow, - ), - ) - } - return errAsync(SdkError('PendingItemNotFound', '')) - }) - } - return okAsync(undefined) - }) - return waitForWalletResponse(interactionId).map( - (item) => item.walletResponse!, - ) + return requestItemClient + .getPendingItems() + .mapErr(() => SdkError('FailedToGetPendingItems', interactionId)) + .andThen((pendingItems) => { + const pendingItem = pendingItems.find( + (item) => item.interactionId === interactionId, + ) + if (pendingItem) { + return ResultAsync.combine([ + requestItemClient.patch(interactionId, { sentToWallet: true }), + sendEncryptedRequest(session, pendingItem.walletInteraction), + deepLinkClient.deepLinkToWallet( + { + sessionId: session.sessionId, + interactionId: pendingItem.interactionId, + }, + childWindow, + ), + ]) + + // requestItemClient + // .patch(interactionId, { sentToWallet: true }) + // .andThen(() => + // sendEncryptedRequest(session, pendingItem.walletInteraction), + // ) + // .andThen(() => + // deepLinkClient.deepLinkToWallet( + // { + // sessionId: session.sessionId, + // interactionId: pendingItem.interactionId, + // }, + // childWindow, + // ), + // ) + } + return errAsync(SdkError('PendingItemNotFound', '')) + }) + .mapErr(() => SdkError('FailedToSendDappRequest', interactionId)) + .andThen(() => + requestItemClient + .waitForWalletResponse(interactionId) + .map((item) => item.walletResponse!), + ) } return { isSupported: () => isMobile(), - send, - resume, + send: sendToWallet, disconnect: () => {}, destroy: () => { subscriptions.unsubscribe()