From b007c7f40cef7a78796ffda301a5ec2f83b93aa3 Mon Sep 17 00:00:00 2001 From: tolgahan-arikan Date: Fri, 5 Aug 2022 00:58:55 +0300 Subject: [PATCH 01/10] prototyping --- .../browser/mock-wallet/mock-wallet.test.ts | 8 +- .../tests/browser/url-transport/dapp.test.ts | 74 +++++++++++++++++++ .../0xsequence/tests/url-transport.spec.ts | 3 + packages/provider/src/transports/index.ts | 1 + .../url-transport/browser-redirect-hooks.ts | 20 +++++ .../src/transports/url-transport/index.ts | 3 + .../url-transport/url-message-handler.ts | 56 ++++++++++++++ .../url-transport/url-message-provider.ts | 67 +++++++++++++++++ packages/provider/src/wallet.ts | 19 ++++- 9 files changed, 248 insertions(+), 3 deletions(-) create mode 100644 packages/0xsequence/tests/browser/url-transport/dapp.test.ts create mode 100644 packages/0xsequence/tests/url-transport.spec.ts create mode 100644 packages/provider/src/transports/url-transport/browser-redirect-hooks.ts create mode 100644 packages/provider/src/transports/url-transport/index.ts create mode 100644 packages/provider/src/transports/url-transport/url-message-handler.ts create mode 100644 packages/provider/src/transports/url-transport/url-message-provider.ts diff --git a/packages/0xsequence/tests/browser/mock-wallet/mock-wallet.test.ts b/packages/0xsequence/tests/browser/mock-wallet/mock-wallet.test.ts index f2ab6bd9f..9ca7a450a 100644 --- a/packages/0xsequence/tests/browser/mock-wallet/mock-wallet.test.ts +++ b/packages/0xsequence/tests/browser/mock-wallet/mock-wallet.test.ts @@ -1,5 +1,5 @@ import { ethers } from 'ethers' -import { WalletRequestHandler, WindowMessageHandler } from '@0xsequence/provider' +import { WalletRequestHandler, WindowMessageHandler, UrlMessageHandler } from '@0xsequence/provider' import { Wallet, Account } from '@0xsequence/wallet' import { NetworkConfig } from '@0xsequence/network' import { LocalRelayer } from '@0xsequence/relayer' @@ -11,7 +11,7 @@ import { test, assert } from '../../utils/assert' configureLogger({ logLevel: 'DEBUG', silence: false }) // -// Wallet, a test wallet +// Wallet, a test wallet, just like a super lightweight wallet-webapp // const main = async () => { @@ -94,6 +94,10 @@ const main = async () => { // setup and register window message transport const windowHandler = new WindowMessageHandler(walletRequestHandler) windowHandler.register() + + // setup and register url message transport + const urlMessageHandler = new UrlMessageHandler(walletRequestHandler, '/mock-wallet/mock-wallet.test.html') + urlMessageHandler.register() } main() diff --git a/packages/0xsequence/tests/browser/url-transport/dapp.test.ts b/packages/0xsequence/tests/browser/url-transport/dapp.test.ts new file mode 100644 index 000000000..bc0651108 --- /dev/null +++ b/packages/0xsequence/tests/browser/url-transport/dapp.test.ts @@ -0,0 +1,74 @@ +import { test, assert } from '../../utils/assert' +import { Wallet, DefaultProviderConfig, BrowserRedirectMessageHooks } from '@0xsequence/provider' +import { configureLogger } from '@0xsequence/utils' +import { testWalletContext, } from '../testutils' + +configureLogger({ logLevel: 'DEBUG', silence: false }) + +export const tests = async () => { + // + // Deploy Sequence WalletContext (deterministic). We skip deployment + // as we rely on mock-wallet to deploy it. + // + const deployedWalletContext = testWalletContext + console.log('walletContext:', deployedWalletContext) + + // + // Setup + // + const providerConfig = { ...DefaultProviderConfig } + providerConfig.walletAppURL = 'http://localhost:9999/mock-wallet/mock-wallet.test.html' + providerConfig.transports = { + windowTransport: { + enabled: false + }, + urlTransport: { + enabled: true, + redirectUrl: 'http://localhost:9999/url-transport/dapp.test.html', + hooks: new BrowserRedirectMessageHooks() + } + } + + const wallet = new Wallet('hardhat', providerConfig) + + // provider + signer, by default if a chainId is not specified it will direct + // requests to the defaultChain + const provider = wallet.getProvider() + const signer = wallet.getSigner() + + console.log('hiii?') + + // clear it in case we're testing in browser session + wallet.disconnect() + + console.log('hiii?2') + + await test('is logged out', async () => { + console.log('a') + assert.false(wallet.isConnected(), 'is logged out') + }) + + await test('is disconnected', async () => { + console.log('b') + assert.false(wallet.isConnected(), 'is disconnnected') + }) + + await test('connect / login', async () => { + console.log('c') + + const { connected } = await wallet.connect({ + keepWalletOpened: true + }) + + console.log('sup???') + + assert.true(connected, 'is connected') + }) + + await test('isConnected', async () => { + console.log('d') + + assert.true(wallet.isConnected(), 'is connected') + }) + +} diff --git a/packages/0xsequence/tests/url-transport.spec.ts b/packages/0xsequence/tests/url-transport.spec.ts new file mode 100644 index 000000000..209eec080 --- /dev/null +++ b/packages/0xsequence/tests/url-transport.spec.ts @@ -0,0 +1,3 @@ +import { runBrowserTests } from './utils/browser-test-runner' + +runBrowserTests('url-transport/dapp', 'url-transport/dapp.test.html') diff --git a/packages/provider/src/transports/index.ts b/packages/provider/src/transports/index.ts index 35f06a3af..bd2c4657d 100644 --- a/packages/provider/src/transports/index.ts +++ b/packages/provider/src/transports/index.ts @@ -6,3 +6,4 @@ export * from './window-transport' export * from './wallet-request-handler' export * from './extension-transport' export * from './unreal-transport' +export * from './url-transport' diff --git a/packages/provider/src/transports/url-transport/browser-redirect-hooks.ts b/packages/provider/src/transports/url-transport/browser-redirect-hooks.ts new file mode 100644 index 000000000..31f9c407f --- /dev/null +++ b/packages/provider/src/transports/url-transport/browser-redirect-hooks.ts @@ -0,0 +1,20 @@ +import { OpenWalletIntent, ProviderRpcError, ProviderMessageResponse } from '../../types' +import { UrlMessageProviderHooks } from './url-message-provider' + +export class BrowserRedirectMessageHooks implements UrlMessageProviderHooks { + openWallet = (walletBaseUrl: string, path?: string, intent?: OpenWalletIntent, networkId?: string | number): void => { + console.log('yeayyyyyyyyyyyyy') + window.location.href = walletBaseUrl + + // on 0xsequence/react-native, we have to implement UrlMessageHooks + // which will call InAppBrowser.open() etc......... + } + + openWallet2(walletUrl: string): void { + + } + + async fetchResponseFromRedirectUrl(): Promise<{error?: ProviderRpcError, response?: ProviderMessageResponse}> { + return {} + } +} diff --git a/packages/provider/src/transports/url-transport/index.ts b/packages/provider/src/transports/url-transport/index.ts new file mode 100644 index 000000000..b471be7ed --- /dev/null +++ b/packages/provider/src/transports/url-transport/index.ts @@ -0,0 +1,3 @@ +export * from './url-message-provider' +export * from './url-message-handler' +export * from './browser-redirect-hooks' diff --git a/packages/provider/src/transports/url-transport/url-message-handler.ts b/packages/provider/src/transports/url-transport/url-message-handler.ts new file mode 100644 index 000000000..e2f0e7a73 --- /dev/null +++ b/packages/provider/src/transports/url-transport/url-message-handler.ts @@ -0,0 +1,56 @@ +import { BaseWalletTransport } from '../base-wallet-transport' +import { WalletRequestHandler } from '../wallet-request-handler' +import { InitState, ProviderMessage } from '../../types' +import { base64DecodeObject } from '@0xsequence/utils' + +export class UrlMessageHandler extends BaseWalletTransport { + + private _pathname: string + + constructor(walletRequestHandler: WalletRequestHandler, pathname: string) { + super(walletRequestHandler) + this._init = InitState.NIL + this._pathname = pathname + } + + register() { + const { pathname, search: rawParams } = new URL(window.location.href) + if (pathname !== this._pathname) { + return + } + + console.log('weeeeeeeeeee') + + const params = new URLSearchParams(rawParams) + // const request = base64DecodeObject(params.get('request')) + const redirectUrl = params.get('redirectUrl') + + // TODO: ensure we have both of these, otherwise just return and skip, + // maybe though console.warn + + console.log('=====> redirecting to.....', redirectUrl) + { + (window.location as any).href = redirectUrl + } + + // this.walletRequestHandler.sendMessageRequest(request).then(response => { + // console.log('got the response.......', response) + // }) + + } + + unregister() { + } + + sendMessage(message: ProviderMessage) { + // TODO... encode the response and then, call the redirect url.. + + console.log('urlMessageHandler sendMessage', message) + + const redirectUrl = 'etc.etc.' + + // TODO: check that window.location exists.. + + window.location.href = redirectUrl + } +} \ No newline at end of file diff --git a/packages/provider/src/transports/url-transport/url-message-provider.ts b/packages/provider/src/transports/url-transport/url-message-provider.ts new file mode 100644 index 000000000..6ad7f2e53 --- /dev/null +++ b/packages/provider/src/transports/url-transport/url-message-provider.ts @@ -0,0 +1,67 @@ +import { BaseProviderTransport } from '../base-provider-transport' +import { ProviderMessage, OpenWalletIntent, ProviderRpcError, ProviderMessageResponse } from '../../types' +import { base64EncodeObject } from '@0xsequence/utils' + +export interface UrlMessageProviderHooks { + openWallet(walletBaseUrl: string, path?: string, intent?: OpenWalletIntent, networkId?: string | number): void + // ... + + openWallet2(walletUrl: string): void + + // TODO: this is not quite right....... + fetchResponseFromRedirectUrl(): Promise<{error?: ProviderRpcError, response?: ProviderMessageResponse}> +} + +export class UrlMessageProvider extends BaseProviderTransport { + private _walletBaseUrl: string + private _redirectUrl: string + private _hooks: UrlMessageProviderHooks + + constructor(walletBaseUrl: string, redirectUrl: string, hooks: UrlMessageProviderHooks) { + super() + this._walletBaseUrl = walletBaseUrl + this._redirectUrl = redirectUrl + this._hooks = hooks + } + + register = async () => { + // TODO: setup listener on the redirect url... so we can handle the response back.... + // ......... handleRedirectResponse() + // on the deep-link which matches + + // TODO ..... not quite right.. but here for brainstorming / prototyping ...... + const { error, response } = await this._hooks.fetchResponseFromRedirectUrl() + console.log('...', error, response) + } + + unregister = () => { + } + + openWallet = (path?: string, intent?: OpenWalletIntent, networkId?: string | number): void => { + console.log('url message handler......... openWallet', path, intent) + + const walletRequestUrl = this._walletBaseUrl + '?request=XX&redirectUrl=' + this._redirectUrl + this._hooks.openWallet(walletRequestUrl, path, intent, networkId) + } + + closeWallet() { + } + + sendMessage(message: ProviderMessage) { + if (!message.idx) { + throw new Error('message idx is empty') + } + + const encodedRequest = base64EncodeObject(message) + const walletUrl = new URL(this._walletBaseUrl) + walletUrl.searchParams.set('request', encodedRequest) + walletUrl.searchParams.set('redirectUrl', this._redirectUrl) + console.log('.... walletURL ..', walletUrl) + + this._hooks.openWallet2(`${walletUrl}`) + } + + private buildWalletRequestUrl() { + + } +} diff --git a/packages/provider/src/wallet.ts b/packages/provider/src/wallet.ts index 90b0f3a88..7ac6201bf 100644 --- a/packages/provider/src/wallet.ts +++ b/packages/provider/src/wallet.ts @@ -26,7 +26,9 @@ import { WindowMessageProvider, ProxyMessageProvider, ProxyMessageChannelPort, - UnrealMessageProvider + UnrealMessageProvider, + UrlMessageProvider, + UrlMessageProviderHooks } from './transports' import { WalletSession, ProviderEventTypes, ConnectOptions, OpenWalletIntent, ConnectDetails } from './types' import { ethers, providers } from 'ethers' @@ -89,6 +91,7 @@ export class Wallet implements WalletProvider { // message communication messageProvider?: MuxMessageProvider windowMessageProvider?: WindowMessageProvider + urlMessageProvider?: UrlMessageProvider proxyMessageProvider?: ProxyMessageProvider extensionMessageProvider?: ExtensionMessageProvider unrealMessageProvider?: UnrealMessageProvider @@ -137,6 +140,14 @@ export class Wallet implements WalletProvider { this.transport.windowMessageProvider = new WindowMessageProvider(this.config.walletAppURL) this.transport.messageProvider.add(this.transport.windowMessageProvider) } + if (this.config.transports?.urlTransport?.enabled) { + this.transport.urlMessageProvider = new UrlMessageProvider( + this.config.walletAppURL, + this.config.transports?.urlTransport?.redirectUrl, + this.config.transports?.urlTransport?.hooks + ) + this.transport.messageProvider.add(this.transport.urlMessageProvider) + } if (this.config.transports?.proxyTransport?.enabled) { this.transport.proxyMessageProvider = new ProxyMessageProvider(this.config.transports.proxyTransport.appPort!) this.transport.messageProvider.add(this.transport.proxyMessageProvider) @@ -714,6 +725,12 @@ export interface ProviderConfig { enabled: boolean } + urlTransport?: { + enabled: boolean + redirectUrl: string + hooks: UrlMessageProviderHooks + } + // ProxyMessage transport (optional) proxyTransport?: { enabled: boolean From 47622305cfd745cbd82a60f0f8413ada5f7ff7bd Mon Sep 17 00:00:00 2001 From: tolgahan-arikan Date: Fri, 5 Aug 2022 00:59:54 +0300 Subject: [PATCH 02/10] prototype: make connect work with finalizeConnect --- .../tests/browser/url-transport/dapp.test.ts | 61 +++++++--- .../url-transport/browser-redirect-hooks.ts | 25 ++-- .../url-transport/url-message-handler.ts | 88 ++++++++++---- .../url-transport/url-message-provider.ts | 112 +++++++++++++++--- packages/provider/src/wallet.ts | 21 +++- 5 files changed, 239 insertions(+), 68 deletions(-) diff --git a/packages/0xsequence/tests/browser/url-transport/dapp.test.ts b/packages/0xsequence/tests/browser/url-transport/dapp.test.ts index bc0651108..005b145ff 100644 --- a/packages/0xsequence/tests/browser/url-transport/dapp.test.ts +++ b/packages/0xsequence/tests/browser/url-transport/dapp.test.ts @@ -1,7 +1,10 @@ import { test, assert } from '../../utils/assert' -import { Wallet, DefaultProviderConfig, BrowserRedirectMessageHooks } from '@0xsequence/provider' +import { Wallet, DefaultProviderConfig, BrowserRedirectMessageHooks, ProviderMessage } from '@0xsequence/provider' import { configureLogger } from '@0xsequence/utils' -import { testWalletContext, } from '../testutils' +import { testWalletContext } from '../testutils' +import { ethers } from 'ethers' +import { base64DecodeObject } from '../../../../utils/src/base64' +import { ConnectDetails } from '../../../../provider/src/types' configureLogger({ logLevel: 'DEBUG', silence: false }) @@ -36,25 +39,50 @@ export const tests = async () => { const provider = wallet.getProvider() const signer = wallet.getSigner() - console.log('hiii?') + const windowURL = new URL(window.location.href) + const response = windowURL.searchParams.get('response') + if (response) { + const decoded = base64DecodeObject(response) as ProviderMessage + console.log('... we have a response...', decoded) + wallet.finalizeConnect(decoded.data as ConnectDetails) + + await test('isConnected', async () => { + console.log('d isConnected', wallet.isConnected()) + + assert.true(wallet.isConnected(), 'is connected') + }) + + await test('getAddress', async () => { + console.log('getAddress start') + console.log('wallet.getAddress', await wallet.getAddress()) + const address = await signer.getAddress() + console.log('signer.getAddress', address) + assert.equal(address, ethers.utils.getAddress('0xa91Ab3C5390A408DDB4a322510A4290363efcEE9'), 'wallet address') + }) + + // await test('getWalletConfig', async () => { + // console.log('start f') + // const allWalletConfigs = await wallet.getWalletConfig() + // console.log('allWalletConfigs') + // }) + return + } // clear it in case we're testing in browser session wallet.disconnect() - console.log('hiii?2') - await test('is logged out', async () => { - console.log('a') + // console.log('a') assert.false(wallet.isConnected(), 'is logged out') }) await test('is disconnected', async () => { - console.log('b') + // console.log('b') assert.false(wallet.isConnected(), 'is disconnnected') }) await test('connect / login', async () => { - console.log('c') + // console.log('c') const { connected } = await wallet.connect({ keepWalletOpened: true @@ -65,10 +93,15 @@ export const tests = async () => { assert.true(connected, 'is connected') }) - await test('isConnected', async () => { - console.log('d') - - assert.true(wallet.isConnected(), 'is connected') - }) - + // await test('sending a json-rpc request', async () => { + // await walletProvider.sendAsync({ jsonrpc: '2.0', id: 88, method: 'eth_accounts', params: [] }, (err, resp) => { + // assert.true(!err, 'error is empty') + // assert.true(!!resp, 'response successful') + // assert.true(resp.result[0] === address, 'response address check') + // }) + + // const resp = await provider.send('eth_accounts', []) + // assert.true(!!resp, 'response successful') + // assert.true(resp[0] === address, 'response address check') + // }) } diff --git a/packages/provider/src/transports/url-transport/browser-redirect-hooks.ts b/packages/provider/src/transports/url-transport/browser-redirect-hooks.ts index 31f9c407f..e4e125b9a 100644 --- a/packages/provider/src/transports/url-transport/browser-redirect-hooks.ts +++ b/packages/provider/src/transports/url-transport/browser-redirect-hooks.ts @@ -1,20 +1,23 @@ -import { OpenWalletIntent, ProviderRpcError, ProviderMessageResponse } from '../../types' +import { OpenWalletIntent, ProviderRpcError, ProviderMessageResponse, ProviderMessage } from '../../types' import { UrlMessageProviderHooks } from './url-message-provider' export class BrowserRedirectMessageHooks implements UrlMessageProviderHooks { - openWallet = (walletBaseUrl: string, path?: string, intent?: OpenWalletIntent, networkId?: string | number): void => { - console.log('yeayyyyyyyyyyyyy') + openWallet = (walletBaseUrl: string): void => { + console.log('BrowserRedirectMessageHooks openWallet', walletBaseUrl) window.location.href = walletBaseUrl - + // on 0xsequence/react-native, we have to implement UrlMessageHooks // which will call InAppBrowser.open() etc......... } - openWallet2(walletUrl: string): void { - - } - - async fetchResponseFromRedirectUrl(): Promise<{error?: ProviderRpcError, response?: ProviderMessageResponse}> { - return {} - } + // listenResponseFromRedirectUrl(callback: (response: ProviderMessage) => void): void { + // console.log('listenResponseFromRedirectUrl', window.location.href) + // // callback({ + // // idx: 1, + // // type: 'something', + // // data: {}, + // // chainId: undefined, + // // origin: undefined + // // }) + // } } diff --git a/packages/provider/src/transports/url-transport/url-message-handler.ts b/packages/provider/src/transports/url-transport/url-message-handler.ts index e2f0e7a73..deb2d8071 100644 --- a/packages/provider/src/transports/url-transport/url-message-handler.ts +++ b/packages/provider/src/transports/url-transport/url-message-handler.ts @@ -1,56 +1,100 @@ import { BaseWalletTransport } from '../base-wallet-transport' import { WalletRequestHandler } from '../wallet-request-handler' -import { InitState, ProviderMessage } from '../../types' -import { base64DecodeObject } from '@0xsequence/utils' +import { + EventType, + InitState, + OpenWalletIntent, + ProviderMessage, + ProviderRpcError, + TransportSession, + WindowSessionParams +} from '../../types' +import { base64DecodeObject, base64EncodeObject, logger } from '@0xsequence/utils' export class UrlMessageHandler extends BaseWalletTransport { - private _pathname: string + private _redirectUrl: string = '' + private _messages: ProviderMessage[] = [] + + private _lastMessageAt: number = 0 constructor(walletRequestHandler: WalletRequestHandler, pathname: string) { super(walletRequestHandler) - this._init = InitState.NIL + this._init = InitState.OK this._pathname = pathname } - register() { + async register() { const { pathname, search: rawParams } = new URL(window.location.href) if (pathname !== this._pathname) { return } - console.log('weeeeeeeeeee') + console.log('... url message handler register ...') const params = new URLSearchParams(rawParams) - // const request = base64DecodeObject(params.get('request')) const redirectUrl = params.get('redirectUrl') + const intent = base64DecodeObject(params.get('intent')) as OpenWalletIntent + this._redirectUrl = redirectUrl! + + console.log('intent', intent) // TODO: ensure we have both of these, otherwise just return and skip, // maybe though console.warn - console.log('=====> redirecting to.....', redirectUrl) - { - (window.location as any).href = redirectUrl + let session: TransportSession | null = this.getWindowTransportSession(rawParams) + + // // provider should always include sid when opening a new window + const isNewWindowSession = !!session.sessionId + + // // attempt to restore previous session in the case of a redirect or window reload + if (!isNewWindowSession) { + session = await this.getCachedTransportSession() } - // this.walletRequestHandler.sendMessageRequest(request).then(response => { - // console.log('got the response.......', response) - // }) + if (!session) { + logger.error('window session is undefined') + return + } + this._registered = true + this.open(session).catch(e => { + const err = `failed to open to network ${session?.networkId}, due to: ${e}` + logger.error(err) + this.notifyClose({ message: err } as ProviderRpcError) + // redirect? + // window.close() + }) } - unregister() { - } + unregister() {} sendMessage(message: ProviderMessage) { - // TODO... encode the response and then, call the redirect url.. - - console.log('urlMessageHandler sendMessage', message) + console.log('... sendMessage in url-message-handler ...', message) - const redirectUrl = 'etc.etc.' + this._messages.push(message) + this._lastMessageAt = Date.now() - // TODO: check that window.location exists.. + // temporary, just to send back connect so that we don't navigate for INIT etc. + setTimeout(() => { + if (this._lastMessageAt == 0 || this._lastMessageAt + 1000 > Date.now()) { + return + } else { + this._lastMessageAt = 0 + const connect = this._messages.find(m => m.type === EventType.CONNECT) + const redirectUrl = new URL(this._redirectUrl) + redirectUrl.searchParams.set('response', base64EncodeObject(connect)) + window.location.href = redirectUrl.href + } + }, 1000) + } - window.location.href = redirectUrl + private getWindowTransportSession = (windowParams: string | undefined): TransportSession => { + const params = new WindowSessionParams(windowParams) + return { + sessionId: params.get('sid'), + networkId: params.get('net'), + intent: base64DecodeObject(params.get('intent')) + } } -} \ No newline at end of file +} diff --git a/packages/provider/src/transports/url-transport/url-message-provider.ts b/packages/provider/src/transports/url-transport/url-message-provider.ts index 6ad7f2e53..0d620a0f7 100644 --- a/packages/provider/src/transports/url-transport/url-message-provider.ts +++ b/packages/provider/src/transports/url-transport/url-message-provider.ts @@ -1,22 +1,27 @@ -import { BaseProviderTransport } from '../base-provider-transport' -import { ProviderMessage, OpenWalletIntent, ProviderRpcError, ProviderMessageResponse } from '../../types' +import { BaseProviderTransport, nextMessageIdx } from '../base-provider-transport' +import { + ProviderMessage, + OpenWalletIntent, + ProviderRpcError, + ProviderMessageResponse, + EventType, + ProviderMessageRequest +} from '../../types' import { base64EncodeObject } from '@0xsequence/utils' +import { JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network' export interface UrlMessageProviderHooks { - openWallet(walletBaseUrl: string, path?: string, intent?: OpenWalletIntent, networkId?: string | number): void - // ... - - openWallet2(walletUrl: string): void + openWallet(walletUrl: string): void // TODO: this is not quite right....... - fetchResponseFromRedirectUrl(): Promise<{error?: ProviderRpcError, response?: ProviderMessageResponse}> + // listenResponseFromRedirectUrl(callback: (response: ProviderMessage) => void): void } export class UrlMessageProvider extends BaseProviderTransport { private _walletBaseUrl: string private _redirectUrl: string private _hooks: UrlMessageProviderHooks - + constructor(walletBaseUrl: string, redirectUrl: string, hooks: UrlMessageProviderHooks) { super() this._walletBaseUrl = walletBaseUrl @@ -27,24 +32,26 @@ export class UrlMessageProvider extends BaseProviderTransport { register = async () => { // TODO: setup listener on the redirect url... so we can handle the response back.... // ......... handleRedirectResponse() - // on the deep-link which matches + // on the deep-link which matches - // TODO ..... not quite right.. but here for brainstorming / prototyping ...... - const { error, response } = await this._hooks.fetchResponseFromRedirectUrl() - console.log('...', error, response) + this._registered = true } - unregister = () => { - } + unregister = () => {} openWallet = (path?: string, intent?: OpenWalletIntent, networkId?: string | number): void => { console.log('url message handler......... openWallet', path, intent) - const walletRequestUrl = this._walletBaseUrl + '?request=XX&redirectUrl=' + this._redirectUrl - this._hooks.openWallet(walletRequestUrl, path, intent, networkId) + this._sessionId = `${performance.now()}` + + const openUrl = this.buildWalletOpenUrl(this._sessionId, path, intent, networkId) + + // const walletRequestUrl = this._walletBaseUrl + '?request=XX&redirectUrl=' + this._redirectUrl + this._hooks.openWallet(openUrl.toString()) } closeWallet() { + // this._hooks.closeWallet() } sendMessage(message: ProviderMessage) { @@ -52,16 +59,85 @@ export class UrlMessageProvider extends BaseProviderTransport { throw new Error('message idx is empty') } + console.log('url message provider......... sendMessage', message) + const encodedRequest = base64EncodeObject(message) const walletUrl = new URL(this._walletBaseUrl) walletUrl.searchParams.set('request', encodedRequest) walletUrl.searchParams.set('redirectUrl', this._redirectUrl) console.log('.... walletURL ..', walletUrl) - this._hooks.openWallet2(`${walletUrl}`) + this._hooks.openWallet(walletUrl.toString()) } - private buildWalletRequestUrl() { + // sendAsync: (request: JsonRpcRequest, callback: JsonRpcResponseCallback, chainId?: number | undefined) => Promise { + // this.sendMessageRequest({ + // idx: nextMessageIdx(), + // type: EventType.MESSAGE, + // data: request, + // chainId: chainId + // }) + // } + + private buildWalletOpenUrl(sessionId: string, path?: string, intent?: OpenWalletIntent, networkId?: string | number) { + const walletURL = new URL(this._walletBaseUrl) + if (path && path !== '') { + walletURL.pathname = path.toLowerCase() + } + + // Make sure at least the app name is forced on Mobile SDK and intent is never undefined + walletURL.searchParams.set('sid', sessionId) + walletURL.searchParams.set('intent', base64EncodeObject(intent)) + walletURL.searchParams.set('redirectUrl', this._redirectUrl) + if (networkId) { + walletURL.searchParams.set('net', `${networkId}`) + } + + console.log('.... walletURL ..', walletURL.toString()) + + return walletURL } + + // sendAsync = async (request: JsonRpcRequest, callback: JsonRpcResponseCallback, chainId?: number) => { + // await this.sendMessageRequest({ + // idx: nextMessageIdx(), + // type: EventType.MESSAGE, + // data: request, + // chainId: chainId + // }) + // } + + // // sendMessageRequest sends a ProviderMessageRequest over the wire to the wallet + // sendMessageRequest = async (message: ProviderMessageRequest): Promise => { + // return new Promise((resolve, reject) => { + // if ((!message.idx || message.idx <= 0) && message.type !== 'init') { + // reject(new Error('message idx not set')) + // } + + // // const responseCallback: ProviderMessageResponseCallback = (error: ProviderRpcError, response?: ProviderMessageResponse) => { + // // if (error) { + // // reject(error) + // // } else if (response) { + // // resolve(response) + // // } else { + // // throw new Error('no valid response to return') + // // } + // // } + + // // const idx = message.idx + // // if (!this.responseCallbacks.get(idx)) { + // // this.responseCallbacks.set(idx, responseCallback) + // // } else { + // // reject(new Error('duplicate message idx, should never happen')) + // // } + + // // if (!this.isOpened()) { + // // logger.debug('pushing to pending requests', message) + // // this.pendingMessageRequests.push(message) + // // } else { + // this.sendMessage(message) + // // } + // }) + // } } diff --git a/packages/provider/src/wallet.ts b/packages/provider/src/wallet.ts index 7ac6201bf..e29046f32 100644 --- a/packages/provider/src/wallet.ts +++ b/packages/provider/src/wallet.ts @@ -340,6 +340,20 @@ export class Wallet implements WalletProvider { return connectDetails } + finalizeConnect(connectDetails: ConnectDetails) { + if (connectDetails.connected) { + if (!!connectDetails.session) { + this.useSession(connectDetails.session, true) + + // this.addConnectedSite(options?.origin) + } else { + throw new Error('impossible state, connect response is missing session') + } + } + + return connectDetails + } + async addConnectedSite(origin: string | undefined) { origin = origin || window.location.origin @@ -727,8 +741,8 @@ export interface ProviderConfig { urlTransport?: { enabled: boolean - redirectUrl: string - hooks: UrlMessageProviderHooks + redirectUrl?: string + hooks?: UrlMessageProviderHooks } // ProxyMessage transport (optional) @@ -763,7 +777,8 @@ export const DefaultProviderConfig: ProviderConfig = { transports: { windowTransport: { enabled: true }, - proxyTransport: { enabled: false } + proxyTransport: { enabled: false }, + urlTransport: { enabled: false } } } From c4241c4a1d2866cf5aa15d8306914001c47d2fe9 Mon Sep 17 00:00:00 2001 From: tolgahan-arikan Date: Tue, 2 Aug 2022 23:33:34 +0300 Subject: [PATCH 03/10] prototype: connecting and getting wallet config --- .../tests/browser/url-transport/dapp.test.ts | 74 ++++++++----------- .../src/transports/base-wallet-transport.ts | 8 ++ .../url-transport/url-message-handler.ts | 18 ++++- .../url-transport/url-message-provider.ts | 37 +++++++--- packages/provider/src/wallet.ts | 3 + 5 files changed, 83 insertions(+), 57 deletions(-) diff --git a/packages/0xsequence/tests/browser/url-transport/dapp.test.ts b/packages/0xsequence/tests/browser/url-transport/dapp.test.ts index 005b145ff..40187b5aa 100644 --- a/packages/0xsequence/tests/browser/url-transport/dapp.test.ts +++ b/packages/0xsequence/tests/browser/url-transport/dapp.test.ts @@ -39,51 +39,49 @@ export const tests = async () => { const provider = wallet.getProvider() const signer = wallet.getSigner() + const session = localStorage.getItem('@sequence.session') + + // If we have a session, test getting config + if (session) { + const sessionObj = JSON.parse(session) + const connectDetails: ConnectDetails = { + connected: true, + chainId: sessionObj.providerCache.eth_chainId, + session: sessionObj + } + wallet.finalizeConnect(connectDetails) + + await test('getWalletConfig', async () => { + console.log('... getWalletConfig test') + const allWalletConfigs = await wallet.getWalletConfig() + console.log('allWalletConfigs', allWalletConfigs) + }) + + return + } + const windowURL = new URL(window.location.href) const response = windowURL.searchParams.get('response') + if (response) { const decoded = base64DecodeObject(response) as ProviderMessage console.log('... we have a response...', decoded) - wallet.finalizeConnect(decoded.data as ConnectDetails) + console.log('... data', JSON.stringify(decoded.data, null, 2)) - await test('isConnected', async () => { - console.log('d isConnected', wallet.isConnected()) + if (decoded.type === 'connect') { + wallet.finalizeConnect(decoded.data as ConnectDetails) - assert.true(wallet.isConnected(), 'is connected') - }) + await test('isConnected', async () => { + console.log('d isConnected', wallet.isConnected()) - await test('getAddress', async () => { - console.log('getAddress start') - console.log('wallet.getAddress', await wallet.getAddress()) - const address = await signer.getAddress() - console.log('signer.getAddress', address) - assert.equal(address, ethers.utils.getAddress('0xa91Ab3C5390A408DDB4a322510A4290363efcEE9'), 'wallet address') - }) + assert.true(wallet.isConnected(), 'is connected') + }) + } - // await test('getWalletConfig', async () => { - // console.log('start f') - // const allWalletConfigs = await wallet.getWalletConfig() - // console.log('allWalletConfigs') - // }) return } - // clear it in case we're testing in browser session - wallet.disconnect() - - await test('is logged out', async () => { - // console.log('a') - assert.false(wallet.isConnected(), 'is logged out') - }) - - await test('is disconnected', async () => { - // console.log('b') - assert.false(wallet.isConnected(), 'is disconnnected') - }) - await test('connect / login', async () => { - // console.log('c') - const { connected } = await wallet.connect({ keepWalletOpened: true }) @@ -92,16 +90,4 @@ export const tests = async () => { assert.true(connected, 'is connected') }) - - // await test('sending a json-rpc request', async () => { - // await walletProvider.sendAsync({ jsonrpc: '2.0', id: 88, method: 'eth_accounts', params: [] }, (err, resp) => { - // assert.true(!err, 'error is empty') - // assert.true(!!resp, 'response successful') - // assert.true(resp.result[0] === address, 'response address check') - // }) - - // const resp = await provider.send('eth_accounts', []) - // assert.true(!!resp, 'response successful') - // assert.true(resp[0] === address, 'response address check') - // }) } diff --git a/packages/provider/src/transports/base-wallet-transport.ts b/packages/provider/src/transports/base-wallet-transport.ts index 7bb8b6456..fe8ef5512 100644 --- a/packages/provider/src/transports/base-wallet-transport.ts +++ b/packages/provider/src/transports/base-wallet-transport.ts @@ -98,6 +98,8 @@ export abstract class BaseWalletTransport implements WalletTransport { } handleMessage = async (message: ProviderMessage) => { + console.log('... base-wallet-transport ... handleMessage ...', message) + const request = message // ensure initial handshake is complete before accepting @@ -161,6 +163,7 @@ export abstract class BaseWalletTransport implements WalletTransport { // sendMessageRequest sends a ProviderMessageRequest to the wallet post-message transport sendMessageRequest = async (message: ProviderMessageRequest): Promise => { + console.log('... base-wallet-transport ... sendMessageRequest ...', message) return this.walletRequestHandler.sendMessageRequest(message) } @@ -170,6 +173,7 @@ export abstract class BaseWalletTransport implements WalletTransport { notifyOpen(openInfo: { chainId?: string; sessionId?: string; session?: WalletSession; error?: string }) { const { chainId, sessionId, session, error } = openInfo + console.log('... base-wallet-transport ... notifyOpen ... error?', openInfo.error) this.sendMessage({ idx: -1, type: EventType.OPEN, @@ -372,6 +376,7 @@ export abstract class BaseWalletTransport implements WalletTransport { // Notify open and proceed to prompt for connection if intended if (!(await this.walletRequestHandler.isSignedIn())) { + console.log('... not signed in???') // open wallet without a specific connected chainId, as the user is not signed in this.notifyOpen({ sessionId: this._sessionId @@ -400,6 +405,8 @@ export abstract class BaseWalletTransport implements WalletTransport { console.log('Failed to set default network on open') } + console.log('intent connect ????') + // notify wallet is opened, without session details this.notifyOpen({ sessionId: this._sessionId @@ -443,6 +450,7 @@ export abstract class BaseWalletTransport implements WalletTransport { // user is already connected, notify session details. // TODO: in future, keep list if 'connected' dapps / sessions in the session // controller, and only sync with allowed apps + console.log('... already connected') this.notifyOpen({ sessionId: this._sessionId, chainId: `${chainId}`, diff --git a/packages/provider/src/transports/url-transport/url-message-handler.ts b/packages/provider/src/transports/url-transport/url-message-handler.ts index deb2d8071..820884b2c 100644 --- a/packages/provider/src/transports/url-transport/url-message-handler.ts +++ b/packages/provider/src/transports/url-transport/url-message-handler.ts @@ -35,9 +35,12 @@ export class UrlMessageHandler extends BaseWalletTransport { const params = new URLSearchParams(rawParams) const redirectUrl = params.get('redirectUrl') const intent = base64DecodeObject(params.get('intent')) as OpenWalletIntent + const request = base64DecodeObject(params.get('request')) as ProviderMessage this._redirectUrl = redirectUrl! console.log('intent', intent) + console.log('request', request) + console.log('request data', request?.data) // TODO: ensure we have both of these, otherwise just return and skip, // maybe though console.warn @@ -65,12 +68,20 @@ export class UrlMessageHandler extends BaseWalletTransport { // redirect? // window.close() }) + + if (request) { + const response = await this.sendMessageRequest(request) + this.sendMessage(response) + console.log('... sendMessageRequest response ...', response) + console.log('... sendMessageRequest response data ...', JSON.stringify(response, null, 2)) + } } unregister() {} sendMessage(message: ProviderMessage) { console.log('... sendMessage in url-message-handler ...', message) + console.log('... sendMessage data ...', message.data) this._messages.push(message) this._lastMessageAt = Date.now() @@ -81,9 +92,12 @@ export class UrlMessageHandler extends BaseWalletTransport { return } else { this._lastMessageAt = 0 - const connect = this._messages.find(m => m.type === EventType.CONNECT) + const connectMessage = this._messages.find(m => m.type === EventType.CONNECT) + const lastMessage = this._messages[this._messages.length - 1] const redirectUrl = new URL(this._redirectUrl) - redirectUrl.searchParams.set('response', base64EncodeObject(connect)) + + redirectUrl.searchParams.set('response', base64EncodeObject(connectMessage ?? lastMessage)) + window.location.href = redirectUrl.href } }, 1000) diff --git a/packages/provider/src/transports/url-transport/url-message-provider.ts b/packages/provider/src/transports/url-transport/url-message-provider.ts index 0d620a0f7..801e6ced0 100644 --- a/packages/provider/src/transports/url-transport/url-message-provider.ts +++ b/packages/provider/src/transports/url-transport/url-message-provider.ts @@ -40,7 +40,7 @@ export class UrlMessageProvider extends BaseProviderTransport { unregister = () => {} openWallet = (path?: string, intent?: OpenWalletIntent, networkId?: string | number): void => { - console.log('url message handler......... openWallet', path, intent) + console.log('url message provider......... openWallet', path, intent) this._sessionId = `${performance.now()}` @@ -79,7 +79,13 @@ export class UrlMessageProvider extends BaseProviderTransport { // }) // } - private buildWalletOpenUrl(sessionId: string, path?: string, intent?: OpenWalletIntent, networkId?: string | number) { + private buildWalletOpenUrl( + sessionId: string, + path?: string, + intent?: OpenWalletIntent, + networkId?: string | number, + request?: string + ): URL { const walletURL = new URL(this._walletBaseUrl) if (path && path !== '') { walletURL.pathname = path.toLowerCase() @@ -87,8 +93,13 @@ export class UrlMessageProvider extends BaseProviderTransport { // Make sure at least the app name is forced on Mobile SDK and intent is never undefined walletURL.searchParams.set('sid', sessionId) - walletURL.searchParams.set('intent', base64EncodeObject(intent)) + if (intent) { + walletURL.searchParams.set('intent', base64EncodeObject(intent)) + } walletURL.searchParams.set('redirectUrl', this._redirectUrl) + if (request) { + walletURL.searchParams.set('request', request) + } if (networkId) { walletURL.searchParams.set('net', `${networkId}`) @@ -99,14 +110,18 @@ export class UrlMessageProvider extends BaseProviderTransport { return walletURL } - // sendAsync = async (request: JsonRpcRequest, callback: JsonRpcResponseCallback, chainId?: number) => { - // await this.sendMessageRequest({ - // idx: nextMessageIdx(), - // type: EventType.MESSAGE, - // data: request, - // chainId: chainId - // }) - // } + sendAsync = async (request: JsonRpcRequest, callback: JsonRpcResponseCallback, chainId?: number) => { + console.log('... url message provider......... sendAsync', request) + const encodedRequest = base64EncodeObject({ + idx: nextMessageIdx(), + type: EventType.MESSAGE, + data: request, + chainId: chainId + }) + + const openUrl = this.buildWalletOpenUrl(this._sessionId!, undefined, undefined, chainId, encodedRequest) + this._hooks.openWallet(openUrl.href) + } // // sendMessageRequest sends a ProviderMessageRequest over the wire to the wallet // sendMessageRequest = async (message: ProviderMessageRequest): Promise => { diff --git a/packages/provider/src/wallet.ts b/packages/provider/src/wallet.ts index e29046f32..5091ae427 100644 --- a/packages/provider/src/wallet.ts +++ b/packages/provider/src/wallet.ts @@ -410,6 +410,9 @@ export class Wallet implements WalletProvider { } isConnected(): boolean { + // log all checks below + console.log('this.session', this.session) + console.log('this.networks', this.networks) return ( this.session !== undefined && this.session.networks !== undefined && From 161d81f4284d63e80543dd01f1d4df8b2ccbb478 Mon Sep 17 00:00:00 2001 From: tolgahan-arikan Date: Thu, 4 Aug 2022 17:59:38 +0300 Subject: [PATCH 04/10] prototype: signTypedData with additional param for address --- .../tests/browser/url-transport/dapp.test.ts | 58 +++++++++++++++++-- packages/provider/src/provider.ts | 5 +- .../url-transport/url-message-handler.ts | 2 +- 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/packages/0xsequence/tests/browser/url-transport/dapp.test.ts b/packages/0xsequence/tests/browser/url-transport/dapp.test.ts index 40187b5aa..ecb385185 100644 --- a/packages/0xsequence/tests/browser/url-transport/dapp.test.ts +++ b/packages/0xsequence/tests/browser/url-transport/dapp.test.ts @@ -1,6 +1,6 @@ import { test, assert } from '../../utils/assert' import { Wallet, DefaultProviderConfig, BrowserRedirectMessageHooks, ProviderMessage } from '@0xsequence/provider' -import { configureLogger } from '@0xsequence/utils' +import { configureLogger, TypedDataDomain, TypedDataField } from '@0xsequence/utils' import { testWalletContext } from '../testutils' import { ethers } from 'ethers' import { base64DecodeObject } from '../../../../utils/src/base64' @@ -46,15 +46,61 @@ export const tests = async () => { const sessionObj = JSON.parse(session) const connectDetails: ConnectDetails = { connected: true, - chainId: sessionObj.providerCache.eth_chainId, + chainId: sessionObj.networks.find(n => n.isDefaultChain).chainId, session: sessionObj } wallet.finalizeConnect(connectDetails) - await test('getWalletConfig', async () => { - console.log('... getWalletConfig test') - const allWalletConfigs = await wallet.getWalletConfig() - console.log('allWalletConfigs', allWalletConfigs) + // await test('getWalletConfig', async () => { + // console.log('... getWalletConfig test') + // const allWalletConfigs = await wallet.getWalletConfig() + // console.log('allWalletConfigs', allWalletConfigs) + // }) + + await test('signTypedData on defaultChain', async () => { + const address = await wallet.getAddress() + console.log('... signTypedData on defaultChain ... getAddress', address) + const chainId = await wallet.getChainId() + console.log('... signTypedData on defaultChain ... getChainId', chainId) + + const domain: TypedDataDomain = { + name: 'Ether Mail', + version: '1', + chainId: chainId, + verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' + } + + const types: { [key: string]: TypedDataField[] } = { + Person: [ + { name: 'name', type: 'string' }, + { name: 'wallet', type: 'address' } + ] + } + + const message = { + name: 'Bob', + wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB' + } + + console.log('... signTypedData on defaultChain ... sig step') + + const sig = await signer.signTypedData(domain, types, message, undefined, undefined, address) + assert.equal( + sig, + '0x00010001c25b59035ea662350e08f41b5087fc49a98b94936826b61a226f97e400c6ce290b8dfa09e3b0df82288fbc599d5b1a023a864bbd876bc67ec1f94c5f2fc4e6101b02', + 'signature match typed-data' + ) + + // // Verify typed data + // const isValid = await wallet.utils.isValidTypedDataSignature(address, { domain, types, message }, sig, chainId) + // assert.true(isValid, 'signature is valid - 3') + + // // Recover config / address + // const walletConfig = await wallet.utils.recoverWalletConfigFromTypedData(address, { domain, types, message }, sig, chainId) + // assert.true(walletConfig.address === address, 'recover address - 3') + + // const singleSignerAddress = '0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853' // expected from mock-wallet owner + // assert.true(singleSignerAddress === walletConfig.signers[0].address, 'owner address check') }) return diff --git a/packages/provider/src/provider.ts b/packages/provider/src/provider.ts index c53eb03cc..0bdb3d17c 100644 --- a/packages/provider/src/provider.ts +++ b/packages/provider/src/provider.ts @@ -242,7 +242,8 @@ export class Web3Signer extends Signer implements TypedDataSigner { types: Record>, message: Record, chainId?: ChainIdLike, - allSigners?: boolean + allSigners?: boolean, + address?: string ): Promise { // Populate any ENS names (in-place) // const populated = await ethers.utils._TypedDataEncoder.resolveNames(domain, types, message, (name: string) => { @@ -251,7 +252,7 @@ export class Web3Signer extends Signer implements TypedDataSigner { return await this.provider.send( 'eth_signTypedData_v4', - [await this.getAddress(), ethers.utils._TypedDataEncoder.getPayload(domain, types, message)], + [address ?? (await this.getAddress()), ethers.utils._TypedDataEncoder.getPayload(domain, types, message)], maybeChainId(chainId) || this.defaultChainId ) } diff --git a/packages/provider/src/transports/url-transport/url-message-handler.ts b/packages/provider/src/transports/url-transport/url-message-handler.ts index 820884b2c..11abc4d89 100644 --- a/packages/provider/src/transports/url-transport/url-message-handler.ts +++ b/packages/provider/src/transports/url-transport/url-message-handler.ts @@ -40,7 +40,7 @@ export class UrlMessageHandler extends BaseWalletTransport { console.log('intent', intent) console.log('request', request) - console.log('request data', request?.data) + console.log('request data', JSON.stringify(request?.data, null, 2)) // TODO: ensure we have both of these, otherwise just return and skip, // maybe though console.warn From 2a673bcb2f0f900327e6d2529885192b7b118bea Mon Sep 17 00:00:00 2001 From: tolgahan-arikan Date: Fri, 5 Aug 2022 15:37:00 +0300 Subject: [PATCH 05/10] prototype: run test automatically if there is response param in url --- .../tests/browser/url-transport/dapp.test.ts | 119 ++++++++++-------- packages/0xsequence/tests/webpack.config.js | 50 +++++--- 2 files changed, 96 insertions(+), 73 deletions(-) diff --git a/packages/0xsequence/tests/browser/url-transport/dapp.test.ts b/packages/0xsequence/tests/browser/url-transport/dapp.test.ts index ecb385185..cce70f3dc 100644 --- a/packages/0xsequence/tests/browser/url-transport/dapp.test.ts +++ b/packages/0xsequence/tests/browser/url-transport/dapp.test.ts @@ -39,6 +39,10 @@ export const tests = async () => { const provider = wallet.getProvider() const signer = wallet.getSigner() + const windowURL = new URL(window.location.href) + const response = windowURL.searchParams.get('response') + const decodedResponse = base64DecodeObject(response) as ProviderMessage + const session = localStorage.getItem('@sequence.session') // If we have a session, test getting config @@ -57,76 +61,83 @@ export const tests = async () => { // console.log('allWalletConfigs', allWalletConfigs) // }) - await test('signTypedData on defaultChain', async () => { - const address = await wallet.getAddress() - console.log('... signTypedData on defaultChain ... getAddress', address) - const chainId = await wallet.getChainId() - console.log('... signTypedData on defaultChain ... getChainId', chainId) - - const domain: TypedDataDomain = { - name: 'Ether Mail', - version: '1', - chainId: chainId, - verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' - } - - const types: { [key: string]: TypedDataField[] } = { - Person: [ - { name: 'name', type: 'string' }, - { name: 'wallet', type: 'address' } - ] - } - - const message = { - name: 'Bob', - wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB' - } + // signTypedData on defaultChain test prep + console.log('... signTypedData on defaultChain ... prep step') - console.log('... signTypedData on defaultChain ... sig step') - - const sig = await signer.signTypedData(domain, types, message, undefined, undefined, address) - assert.equal( - sig, - '0x00010001c25b59035ea662350e08f41b5087fc49a98b94936826b61a226f97e400c6ce290b8dfa09e3b0df82288fbc599d5b1a023a864bbd876bc67ec1f94c5f2fc4e6101b02', - 'signature match typed-data' - ) + const address = await wallet.getAddress() + console.log('... signTypedData on defaultChain ... getAddress', address) + const chainId = await wallet.getChainId() + console.log('... signTypedData on defaultChain ... getChainId', chainId) - // // Verify typed data - // const isValid = await wallet.utils.isValidTypedDataSignature(address, { domain, types, message }, sig, chainId) - // assert.true(isValid, 'signature is valid - 3') + const domain: TypedDataDomain = { + name: 'Ether Mail', + version: '1', + chainId: chainId, + verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' + } - // // Recover config / address - // const walletConfig = await wallet.utils.recoverWalletConfigFromTypedData(address, { domain, types, message }, sig, chainId) - // assert.true(walletConfig.address === address, 'recover address - 3') + const types: { [key: string]: TypedDataField[] } = { + Person: [ + { name: 'name', type: 'string' }, + { name: 'wallet', type: 'address' } + ] + } - // const singleSignerAddress = '0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853' // expected from mock-wallet owner - // assert.true(singleSignerAddress === walletConfig.signers[0].address, 'owner address check') - }) + const message = { + name: 'Bob', + wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB' + } - return - } + if (!response) { + console.log('... signTypedData on defaultChain ... sig step') + await signer.signTypedData(domain, types, message, undefined, undefined, address) + } - const windowURL = new URL(window.location.href) - const response = windowURL.searchParams.get('response') + const sig = decodedResponse.data.result - if (response) { - const decoded = base64DecodeObject(response) as ProviderMessage - console.log('... we have a response...', decoded) - console.log('... data', JSON.stringify(decoded.data, null, 2)) + if (sig) { + await test('signTypedData on defaultChain', async () => { + assert.equal( + sig, + '0x00010001c25b59035ea662350e08f41b5087fc49a98b94936826b61a226f97e400c6ce290b8dfa09e3b0df82288fbc599d5b1a023a864bbd876bc67ec1f94c5f2fc4e6101b02', + 'signature match typed-data' + ) - if (decoded.type === 'connect') { - wallet.finalizeConnect(decoded.data as ConnectDetails) + // // Verify typed data + // const isValid = await wallet.utils.isValidTypedDataSignature(address, { domain, types, message }, sig, chainId) + // assert.true(isValid, 'signature is valid - 3') - await test('isConnected', async () => { - console.log('d isConnected', wallet.isConnected()) + // // Recover config / address + // const walletConfig = await wallet.utils.recoverWalletConfigFromTypedData(address, { domain, types, message }, sig, chainId) + // assert.true(walletConfig.address === address, 'recover address - 3') - assert.true(wallet.isConnected(), 'is connected') + // const singleSignerAddress = '0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853' // expected from mock-wallet owner + // assert.true(singleSignerAddress === walletConfig.signers[0].address, 'owner address check') }) } return } + // Connect test + // if (response) { + + // console.log('... we have a response...', decoded) + // console.log('... data', JSON.stringify(decoded.data, null, 2)) + + // if (decoded.type === 'connect') { + // wallet.finalizeConnect(decoded.data as ConnectDetails) + + // await test('isConnected', async () => { + // console.log('d isConnected', wallet.isConnected()) + + // assert.true(wallet.isConnected(), 'is connected') + // }) + // } + + // return + // } + await test('connect / login', async () => { const { connected } = await wallet.connect({ keepWalletOpened: true diff --git a/packages/0xsequence/tests/webpack.config.js b/packages/0xsequence/tests/webpack.config.js index 0a9b7ccbd..bc4ed111f 100644 --- a/packages/0xsequence/tests/webpack.config.js +++ b/packages/0xsequence/tests/webpack.config.js @@ -6,7 +6,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin') const port = process.env['PORT'] || 9999 const appDirectory = fs.realpathSync(process.cwd()) -const resolveCwd = (relativePath) => path.resolve(appDirectory, relativePath) +const resolveCwd = relativePath => path.resolve(appDirectory, relativePath) const resolvePackages = () => { const pkgs = path.resolve(fs.realpathSync(process.cwd()), '..') @@ -21,7 +21,7 @@ const resolvePackages = () => { // Include extra sources for compilation. // -// NOTE: if you experience an error in your webpack builder such as, +// NOTE: if you experience an error in your webpack builder such as, // Module parse failed: Unexpected token (11:20) // You may need an appropriate loader to handle this file type, currently no loaders are // configured to process this file. See https://webpack.js.org/concepts#loaders @@ -34,13 +34,13 @@ const resolveExtras = [ resolveCwd('../../node_modules/@0xsequence/wallet-contracts/gen') ] -const resolveTestEntries = (location) => { +const resolveTestEntries = location => { return fs.readdirSync(location).reduce((list, f) => { const n = path.join(location, f) if (fs.lstatSync(n).isDirectory()) { list.push(...resolveTestEntries(n)) } else { - if (n.endsWith(".test.ts") > 0) list.push(n) + if (n.endsWith('.test.ts') > 0) list.push(n) } return list }, []) @@ -48,26 +48,28 @@ const resolveTestEntries = (location) => { const resolveEntry = () => { const browserTestRoot = fs.realpathSync(path.join(process.cwd(), 'tests', 'browser')) - const entry = { 'lib': './src/index.ts' } + const entry = { lib: './src/index.ts' } const testEntries = resolveTestEntries(browserTestRoot) - testEntries.forEach(v => entry[v.slice(browserTestRoot.length+1, v.length-3)] = v) + testEntries.forEach(v => (entry[v.slice(browserTestRoot.length + 1, v.length - 3)] = v)) return entry } -const resolveHtmlPlugins = (entry) => { +const resolveHtmlPlugins = entry => { const plugins = [] for (let k in entry) { if (k === 'lib') continue - plugins.push(new HtmlWebpackPlugin({ - inject: false, - filename: `${k}.html`, - templateContent: htmlTemplate(k) - })) + plugins.push( + new HtmlWebpackPlugin({ + inject: false, + filename: `${k}.html`, + templateContent: htmlTemplate(k) + }) + ) } return plugins } -const htmlTemplate = (k) => ` +const htmlTemplate = k => ` @@ -84,7 +86,19 @@ const htmlTemplate = (k) => ` ` diff --git a/packages/provider/src/transports/base-provider-transport.ts b/packages/provider/src/transports/base-provider-transport.ts index 81575426f..ff44d55a9 100644 --- a/packages/provider/src/transports/base-provider-transport.ts +++ b/packages/provider/src/transports/base-provider-transport.ts @@ -125,7 +125,14 @@ export abstract class BaseProviderTransport implements ProviderTransport { // handleMessage will handle message received from the remote wallet handleMessage(message: ProviderMessage) { + console.log('yesssss...............................', message) + console.log('yesssss...............................', message) + console.log('yesssss...............................', message) + console.log('yesssss...............................', message) + console.log('yesssss...............................', message) + console.log('yesssss...............................', message) console.log('handleMessage', message.type) + // init incoming for initial handshake with transport. // always respond to INIT messages, e.g. on popup window reload if (message.type === EventType.INIT) { @@ -208,19 +215,19 @@ export abstract class BaseProviderTransport implements ProviderTransport { }, 500) // TODO: be smarter about timer as we're processing the response callbacks.. } - // if (!responseCallback) { - // // NOTE: this would occur if 'idx' isn't set, which should never happen - // // or when we register two handler, or duplicate messages with the same idx are sent, - // // all of which should be prevented prior to getting to this point - // throw new Error('impossible state, no response callback for message') - // } + if (!responseCallback) { + // NOTE: this would occur if 'idx' isn't set, which should never happen + // or when we register two handler, or duplicate messages with the same idx are sent, + // all of which should be prevented prior to getting to this point + throw new Error('impossible state, no response callback for message') + } // Callback to original caller - // if (responseCallback) { - this.events.emit('message', message) - // responseCallback((message as ProviderMessageResponse).data.error, message) - return - // } + if (responseCallback) { + this.events.emit('message', message) + responseCallback((message as ProviderMessageResponse).data.error, message) + return + } } // ACCOUNTS_CHANGED -- when a user logs in or out @@ -269,6 +276,7 @@ export abstract class BaseProviderTransport implements ProviderTransport { if (message.type === EventType.CONNECT) { console.log('message.type === EventType.CONNECT') this.connectPayload = message.data + console.log('..???? paload??????????????? connnect...???', this.connectPayload) this.events.emit('connect', this.connectPayload!) } diff --git a/packages/provider/src/transports/base-wallet-transport.ts b/packages/provider/src/transports/base-wallet-transport.ts index fe8ef5512..fc45ea182 100644 --- a/packages/provider/src/transports/base-wallet-transport.ts +++ b/packages/provider/src/transports/base-wallet-transport.ts @@ -332,6 +332,8 @@ export abstract class BaseWalletTransport implements WalletTransport { // origin host of the dapp. await this.init() + console.log('waaa?', intent) + // Prepare connect options from intent if (intent && intent.type === 'connect' && intent.options) { const connectOptions = intent.options @@ -374,6 +376,8 @@ export abstract class BaseWalletTransport implements WalletTransport { // ensure signer is ready await this.walletRequestHandler.getSigner() + console.log('wazzzzzzzzzzup???????????????????????????????????????') + // Notify open and proceed to prompt for connection if intended if (!(await this.walletRequestHandler.isSignedIn())) { console.log('... not signed in???') diff --git a/packages/provider/src/transports/url-transport/browser-redirect-hooks.ts b/packages/provider/src/transports/url-transport/browser-redirect-hooks.ts index c1c7dcd31..7b960baae 100644 --- a/packages/provider/src/transports/url-transport/browser-redirect-hooks.ts +++ b/packages/provider/src/transports/url-transport/browser-redirect-hooks.ts @@ -3,6 +3,7 @@ import { UrlMessageProviderHooks } from './url-message-provider' export class BrowserRedirectMessageHooks implements UrlMessageProviderHooks { openWallet = (walletBaseUrl: string): void => { + console.log('BrowserRedirectMessageHooks ....???... here...?') console.log('BrowserRedirectMessageHooks openWallet', walletBaseUrl) window.location.href = walletBaseUrl @@ -10,5 +11,6 @@ export class BrowserRedirectMessageHooks implements UrlMessageProviderHooks { // which will call InAppBrowser.open() etc......... } - responseFromRedirectUrl = (callback: (response: string) => void): void => {} + // responseFromRedirectUrl = (callback: (response: string) => void): void => {} + responseFromRedirectUrl = (response: string) => {} } diff --git a/packages/provider/src/transports/url-transport/url-message-handler.ts b/packages/provider/src/transports/url-transport/url-message-handler.ts index f6a459719..79bc1c87e 100644 --- a/packages/provider/src/transports/url-transport/url-message-handler.ts +++ b/packages/provider/src/transports/url-transport/url-message-handler.ts @@ -14,6 +14,7 @@ import { base64DecodeObject, base64EncodeObject, logger } from '@0xsequence/util export class UrlMessageHandler extends BaseWalletTransport { private _pathname: string private _redirectUrl: string = '' + private _redirecting: boolean = false private _messages: ProviderMessage[] = [] private _lastMessageAt: number = 0 @@ -47,16 +48,16 @@ export class UrlMessageHandler extends BaseWalletTransport { let session: TransportSession | null = this.getWindowTransportSession(rawParams) - // // provider should always include sid when opening a new window + // provider should always include sid when opening a new window const isNewWindowSession = !!session.sessionId - // // attempt to restore previous session in the case of a redirect or window reload + // attempt to restore previous session in the case of a redirect or window reload if (!isNewWindowSession) { session = await this.getCachedTransportSession() } if (!session) { - logger.error('window session is undefined') + logger.error('window session is undefined') //... return } @@ -64,12 +65,13 @@ export class UrlMessageHandler extends BaseWalletTransport { this.open(session).catch(e => { const err = `failed to open to network ${session?.networkId}, due to: ${e}` logger.error(err) - this.notifyClose({ message: err } as ProviderRpcError) - // redirect? + // this.notifyClose({ message: err } as ProviderRpcError) + // redirect?sup....??? lalala // window.close() }) if (request) { + console.log('requesting..............', request) const response = await this.sendMessageRequest(request) this.sendMessage(response) console.log('... sendMessageRequest response ...', response) @@ -77,30 +79,67 @@ export class UrlMessageHandler extends BaseWalletTransport { } } - unregister() {} + unregister() { + this._registered = false + } sendMessage(message: ProviderMessage) { - console.log('... sendMessage in url-message-handler ...', message) - console.log('... sendMessage data ...', message.data) - - this._messages.push(message) - this._lastMessageAt = Date.now() - - // temporary, just to send back connect so that we don't navigate for INIT etc. - setTimeout(() => { - if (this._lastMessageAt == 0 || this._lastMessageAt + 1000 > Date.now()) { - return - } else { - this._lastMessageAt = 0 - const connectMessage = this._messages.find(m => m.type === EventType.CONNECT) - const lastMessage = this._messages[this._messages.length - 1] - const redirectUrl = new URL(this._redirectUrl) - - redirectUrl.searchParams.set('response', base64EncodeObject(connectMessage ?? lastMessage)) - - window.location.href = redirectUrl.href - } - }, 3000) + if (this._redirecting) { + return + } + + console.log('...URL-HANDLER (wallet) sendMessage in url-message-handler ...', message) + console.log('...URL-HANDLER (wallet) sendMessage data ...', message.data) + + if (message.type === EventType.OPEN) { + console.log('open message, but well skip it..') + return + } + + // TODO: remove this.. we want to keep close.. + if (message.type === EventType.CLOSE) { + console.log('close message, but well skip it..') + return + } + + // NOTE: maybe we check the 'connect' object, and we shorten it..? it will be quite long + // .. we can just rely on the lib to have all of the networks listed..? if anything + // we can just return a list like networks: [1, 137, etc..] + + // respond with the first message, no waiting around.. take the first message + this._redirecting = true + const redirectUrl = new URL(this._redirectUrl) + redirectUrl.hash = 'response='+base64EncodeObject(message) + window.location.href = redirectUrl.href + + + // this._messages.push(message) + // this._lastMessageAt = Date.now() + + // // temporary, just to send back connect so that we don't navigate for INIT etc. + // //.. + // setTimeout(() => { + // // if (this._lastMessageAt == 0 || this._lastMessageAt + 1000 > Date.now()) { + // // return + // // } else { + // // this._lastMessageAt = 0 + // const connectMessage = this._messages.find(m => m.type === EventType.CONNECT) + // if (connectMessage) { + // this._redirecting = true + // const redirectUrl = new URL(this._redirectUrl) + // redirectUrl.hash = 'response='+base64EncodeObject(message) + // window.location.href = redirectUrl.href + // } + // // const lastMessage = this._messages[this._messages.length - 1] + // // const redirectUrl = new URL(this._redirectUrl) + + // // redirectUrl.hash = 'response='+base64EncodeObject(connectMessage ?? lastMessage) + + // // // redirectUrl.searchParams.set('response', base64EncodeObject(connectMessage ?? lastMessage)) + + // // window.location.href = redirectUrl.href + // // } + // }, 3000) } private getWindowTransportSession = (windowParams: string | undefined): TransportSession => { diff --git a/packages/provider/src/transports/url-transport/url-message-provider.ts b/packages/provider/src/transports/url-transport/url-message-provider.ts index 4615beb5a..9f1363632 100644 --- a/packages/provider/src/transports/url-transport/url-message-provider.ts +++ b/packages/provider/src/transports/url-transport/url-message-provider.ts @@ -1,38 +1,87 @@ import { BaseProviderTransport, nextMessageIdx } from '../base-provider-transport' -import { ProviderMessage, OpenWalletIntent, EventType, WalletSession, InitState } from '../../types' +import { Wallet } from '../../wallet' +import { ProviderMessage, OpenWalletIntent, EventType, WalletSession, InitState, ConnectDetails, ProviderEventTypes } from '../../types' import { base64DecodeObject, base64EncodeObject } from '@0xsequence/utils' import { JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network' export interface UrlMessageProviderHooks { openWallet(walletUrl: string): void - responseFromRedirectUrl(callback: (response: string) => void): void + // responseFromRedirectUrl(callback: (response: string) => void): void + responseFromRedirectUrl(response: string): void } export class UrlMessageProvider extends BaseProviderTransport { + private _wallet: Wallet private _walletBaseUrl: string private _redirectUrl: string private _hooks: UrlMessageProviderHooks + private _connectDetails: ConnectDetails | undefined - constructor(walletBaseUrl: string, redirectUrl: string, hooks: UrlMessageProviderHooks) { + constructor(wallet: Wallet, walletBaseUrl: string, redirectUrl: string, hooks: UrlMessageProviderHooks) { super() + console.log('UrlMessageProvider walletBaseUrl:', walletBaseUrl) this._init = InitState.OK + this._wallet = wallet this._walletBaseUrl = walletBaseUrl this._redirectUrl = redirectUrl this._hooks = hooks } + on(event: K, fn: ProviderEventTypes[K]) { + this.events.on(event, fn as any) + if (event === 'connect' && this._connectDetails) { + this.events.emit('connect', this._connectDetails) + } + } + + once(event: K, fn: ProviderEventTypes[K]) { + this.events.once(event, fn as any) + if (event === 'connect' && this._connectDetails) { + this.events.emit('connect', this._connectDetails) + } + } + register = async () => { - this._hooks.responseFromRedirectUrl((response: string) => { + console.log('... URL MESSAGE PROVIDER ... register...????') + + this.events.on('connect', connectDetails => { + console.log('url messagep provider got connect, connectDetails..', connectDetails) + this._connectDetails = connectDetails + + if (connectDetails.connected) { + if (!!connectDetails.session) { + this._wallet.useSession(connectDetails.session, true) + // this.addConnectedSite(options?.origin) + } else { + throw new Error('impossible state, connect response is missing session') + } + } + }) + + this._hooks.responseFromRedirectUrl = (response: string) => { const decodedResponse = base64DecodeObject(response) as ProviderMessage - console.log('... we have a response...', decodedResponse) + console.log('... we have a response...zzzzzz', decodedResponse) this.handleMessage(decodedResponse) - }) + } this._registered = true + + // const windowURL = new URL(window.location.href) + const prefix = '#response=' + console.log('sup....??? lalala', window.location.hash) + if (window.location.hash.startsWith(prefix)) { + const response = window.location.hash.substring(prefix.length) + console.log('=> response', response) + window.location.hash = '' + this._hooks.responseFromRedirectUrl(response) + } } - unregister = () => {} + unregister = () => { + this._registered = false + // this._hooks ..? + } openWallet = (path?: string, intent?: OpenWalletIntent, networkId?: string | number): void => { console.log('url message provider......... openWallet', path, intent) @@ -110,39 +159,8 @@ export class UrlMessageProvider extends BaseProviderTransport { } waitUntilOpened = async (openTimeout = 0): Promise => { + // noop return undefined } - // // sendMessageRequest sends a ProviderMessageRequest over the wire to the wallet - // sendMessageRequest = async (message: ProviderMessageRequest): Promise => { - // return new Promise((resolve, reject) => { - // if ((!message.idx || message.idx <= 0) && message.type !== 'init') { - // reject(new Error('message idx not set')) - // } - - // // const responseCallback: ProviderMessageResponseCallback = (error: ProviderRpcError, response?: ProviderMessageResponse) => { - // // if (error) { - // // reject(error) - // // } else if (response) { - // // resolve(response) - // // } else { - // // throw new Error('no valid response to return') - // // } - // // } - - // // const idx = message.idx - // // if (!this.responseCallbacks.get(idx)) { - // // this.responseCallbacks.set(idx, responseCallback) - // // } else { - // // reject(new Error('duplicate message idx, should never happen')) - // // } - - // // if (!this.isOpened()) { - // // logger.debug('pushing to pending requests', message) - // // this.pendingMessageRequests.push(message) - // // } else { - // this.sendMessage(message) - // // } - // }) - // } } diff --git a/packages/provider/src/transports/wallet-request-handler.ts b/packages/provider/src/transports/wallet-request-handler.ts index ea7c1b6ec..53d8555e7 100644 --- a/packages/provider/src/transports/wallet-request-handler.ts +++ b/packages/provider/src/transports/wallet-request-handler.ts @@ -103,6 +103,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P // for this dapp, so its safe to authorize in the connect() method without the prompt. // // NOTE: signIn can optionally connect and notify dapp at this time for new signIn flows + console.log('...? sup...? connect?', connect) if (connect) { const connectOptions = this._connectOptions @@ -715,6 +716,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P } notifyConnect(connectDetails: ConnectDetails, origin?: string) { + console.log('hess........ notifyCOnnect from wallet-request-handler', connectDetails) this.events.emit('connect', connectDetails) if (connectDetails.session?.accountAddress) { this.events.emit('accountsChanged', [connectDetails.session?.accountAddress], origin) diff --git a/packages/provider/src/utils.ts b/packages/provider/src/utils.ts index 707d57108..7b49f1d56 100644 --- a/packages/provider/src/utils.ts +++ b/packages/provider/src/utils.ts @@ -160,7 +160,7 @@ export class LocalStorage { } } -// window.localstorage helper +// window.localStorage helper export class LocalStore { readonly key: string diff --git a/packages/provider/src/wallet.ts b/packages/provider/src/wallet.ts index c9a8ca0ff..5cd7a9abe 100644 --- a/packages/provider/src/wallet.ts +++ b/packages/provider/src/wallet.ts @@ -38,8 +38,11 @@ import { WalletUtils } from './utils/index' import { Runtime } from 'webextension-polyfill-ts' + export interface WalletProvider { + // connect(options?: M): M extends RedirectMode ? void : Promise connect(options?: ConnectOptions): Promise + connectWithRedirect(options?: ConnectOptions): void disconnect(): void isConnected(): boolean @@ -144,7 +147,7 @@ export class Wallet implements WalletProvider { const redirectUrl = this.config.transports?.urlTransport?.redirectUrl const hooks = this.config.transports?.urlTransport?.hooks if (redirectUrl && hooks) { - this.transport.urlMessageProvider = new UrlMessageProvider(this.config.walletAppURL, redirectUrl, hooks) + this.transport.urlMessageProvider = new UrlMessageProvider(this, this.config.walletAppURL, redirectUrl, hooks) this.transport.messageProvider.add(this.transport.urlMessageProvider) } } @@ -169,6 +172,7 @@ export class Wallet implements WalletProvider { this.transport.unrealMessageProvider = new UnrealMessageProvider(this.config.walletAppURL) this.transport.messageProvider.add(this.transport.unrealMessageProvider) } + this.transport.messageProvider.register() // ..... @@ -193,6 +197,7 @@ export class Wallet implements WalletProvider { // Provider proxy to support middleware stack of logging, caching and read-only rpc calls this.transport.cachedProvider = new CachedProvider() + console.log('cachdproviderrrrr:', this.transport.cachedProvider) this.transport.cachedProvider.onUpdate(() => { if (!this.session) this.session = { providerCache: {} } this.session.providerCache = this.transport.cachedProvider!.getCache() @@ -292,6 +297,19 @@ export class Wallet implements WalletProvider { } } + // connect(options?: M): M extends RedirectMode ? void : Promise + // connect(options?: ConnectOptions): void | Promise { + // if (options?.refresh === true) { + // this.disconnect() + // } + // if (options?.redirectMode) { + // this._connectWithRedirect(options) + // return + // } else { + // return this._connect(options) + // } + // } + connect = async (options?: ConnectOptions): Promise => { if (options?.refresh === true) { this.disconnect() @@ -317,7 +335,12 @@ export class Wallet implements WalletProvider { } } - await this.openWallet(undefined, { type: 'connect', options }) + if (options?.redirectMode) { + this.openWallet(undefined, { type: 'connect', options }) + return { connected: false } + } else { + await this.openWallet(undefined, { type: 'connect', options }) + } const connectDetails = await this.transport.messageProvider!.waitUntilConnected().catch((error): ConnectDetails => { if (error instanceof Error) { @@ -340,18 +363,8 @@ export class Wallet implements WalletProvider { return connectDetails } - finalizeConnect(connectDetails: ConnectDetails) { - if (connectDetails.connected) { - if (!!connectDetails.session) { - this.useSession(connectDetails.session, true) - - // this.addConnectedSite(options?.origin) - } else { - throw new Error('impossible state, connect response is missing session') - } - } - - return connectDetails + connectWithRedirect = (options?: ConnectOptions): void => { + this.connect({ ...options, redirectMode: true }) } async addConnectedSite(origin: string | undefined) { @@ -622,7 +635,7 @@ export class Wallet implements WalletProvider { await LocalStorage.getInstance().setItem('@sequence.session', data) } - private useSession = async (session: WalletSession, autoSave: boolean = true) => { + useSession = async (session: WalletSession, autoSave: boolean = true) => { if (!this.session) this.session = {} // setup wallet context @@ -643,10 +656,18 @@ export class Wallet implements WalletProvider { } // setup provider cache - if (session.providerCache) { - this.transport.cachedProvider!.setCache(session.providerCache) + console.log('???? session.providerCache', session.providerCache) + console.log('???? this.transport.cachedProvider', this.transport.cachedProvider) + + if (this.transport.cachedProvider) { + if (session.providerCache) { + this.transport.cachedProvider.setCache(session.providerCache) + } else { + console.log('therooo...........', this.transport.cachedProvider!.clearCache) + this.transport.cachedProvider.clearCache() + } } else { - this.transport.cachedProvider!.clearCache() + console.log('noooo???????????????????????? CACHE???????????????????') } // persist @@ -709,11 +730,22 @@ export class Wallet implements WalletProvider { this.providers = {} this.transport.cachedProvider?.clearCache() } + + static load = async (network?: string | number, config?: Partial) => { + if (walletInstance) { + return walletInstance + } else { + walletInstance = new Wallet(network, config) + await walletInstance.loadSession(network) + return walletInstance + } + } } export interface ProviderConfig { // The local storage dependency for the wallet provider, defaults to window.localStorage. // For example, this option should be used when using React Native since window.localStorage is not available. + // TODO: rename localStorag to like cacheStore or sessionStore localStorage?: ItemStore // Sequence Wallet App URL, default: https://sequence.app