@0xsequence/provider 0.40.1 → 0.40.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,121 @@
1
+ import {
2
+ ProviderMessageRequest,
3
+ ProviderMessage,
4
+ EventType,
5
+ InitState,
6
+ WindowSessionParams,
7
+ OpenWalletIntent,
8
+ ProviderRpcError,
9
+ TransportSession
10
+ } from '../../types'
11
+ import { WalletRequestHandler } from '../wallet-request-handler'
12
+ import { BaseWalletTransport } from '../base-wallet-transport'
13
+ import { logger, base64DecodeObject } from '@0xsequence/utils'
14
+ import { overrideLogs } from './overridelogs'
15
+
16
+ // all lowercase is an annoying limitation of Unreal CEF BindUObject
17
+ interface UnrealInjectedWalletWindow {
18
+ ue?: {
19
+ sequencewallettransport?: {
20
+ onmessagefromsequencejs?: (message: ProviderMessageRequest) => void
21
+ sendmessagetosequencejs: (message: string) => void
22
+ }
23
+ }
24
+ }
25
+ declare const window: Window & typeof globalThis & UnrealInjectedWalletWindow
26
+
27
+ /**
28
+ * Initialized on Wallet side
29
+ */
30
+ export class UnrealMessageHandler extends BaseWalletTransport {
31
+ constructor(walletRequestHandler: WalletRequestHandler) {
32
+ super(walletRequestHandler)
33
+ this._init = InitState.NIL
34
+ }
35
+
36
+ async register(windowHref?: string | URL) {
37
+ if (window.ue?.sequencewallettransport === undefined) {
38
+ return
39
+ }
40
+ overrideLogs('wallet')
41
+
42
+ // record open details (sessionId + default network) from the window url
43
+ const { search: rawParams } = new URL(windowHref || window.location.href)
44
+
45
+ let session: TransportSession | null = this.getUnrealTransportSession(rawParams)
46
+
47
+ // provider should always include sid when opening a new window
48
+ const isNewWindowSession = !!session.sessionId
49
+
50
+ // attempt to restore previous session in the case of a redirect or window reload
51
+ if (!isNewWindowSession) {
52
+ session = await this.getCachedTransportSession()
53
+ }
54
+
55
+ if (!session) {
56
+ logger.error('unreal session is undefined')
57
+ return
58
+ }
59
+
60
+ // listen for window-transport requests
61
+ window.ue.sequencewallettransport.onmessagefromsequencejs = this.onMessageFromUnreal
62
+ this._registered = true
63
+
64
+ // send open event to the app which opened us
65
+ this.open(session)
66
+ .then(opened => {
67
+ if (!opened) {
68
+ const err = `failed to open to network ${session?.networkId}`
69
+ logger.error(err)
70
+ this.notifyClose({ message: err } as ProviderRpcError)
71
+ window.close()
72
+ }
73
+ })
74
+ .catch(e => {
75
+ const err = `failed to open to network ${session?.networkId}, due to: ${e}`
76
+ logger.error(err)
77
+ this.notifyClose({ message: err } as ProviderRpcError)
78
+ window.close()
79
+ })
80
+ }
81
+
82
+ unregister() {
83
+ if (window.ue?.sequencewallettransport?.onmessagefromsequencejs === this.onMessageFromUnreal) {
84
+ delete window.ue.sequencewallettransport.onmessagefromsequencejs
85
+ }
86
+ this._registered = false
87
+ }
88
+
89
+ // onmessage is called when (the wallet) receives request messages from the dapp
90
+ // over the unreal json-messaging transport
91
+ private onMessageFromUnreal = (request: ProviderMessageRequest) => {
92
+ // Wallet always expects json-rpc request messages from a dapp
93
+
94
+ logger.debug('RECEIVED MESSAGE', request)
95
+
96
+ // Handle message via the base transport
97
+ this.handleMessage(request)
98
+ }
99
+
100
+ // sendMessage sends message to the dapp window
101
+ sendMessage(message: ProviderMessage<any>) {
102
+ if (message.type !== EventType.INIT && this._init !== InitState.OK) {
103
+ logger.error('impossible state, should not be calling postMessage until inited')
104
+ return
105
+ }
106
+ // prepare payload
107
+ const payload = JSON.stringify(message)
108
+
109
+ // post-message to app.
110
+ window.ue?.sequencewallettransport?.sendmessagetosequencejs(payload)
111
+ }
112
+
113
+ private getUnrealTransportSession = (windowParams: string | undefined): TransportSession => {
114
+ const params = new WindowSessionParams(windowParams)
115
+ return {
116
+ sessionId: params.get('sid'),
117
+ networkId: params.get('net'),
118
+ intent: base64DecodeObject<OpenWalletIntent>(params.get('intent'))
119
+ }
120
+ }
121
+ }
@@ -0,0 +1,121 @@
1
+ import { OpenWalletIntent, ProviderMessage, InitState, WindowSessionParams } from '../../types'
2
+ import { BaseProviderTransport } from '../base-provider-transport'
3
+ import { base64EncodeObject } from '@0xsequence/utils'
4
+ import { overrideLogs } from './overridelogs'
5
+
6
+ let registeredUnrealMessageProvider: UnrealMessageProvider | undefined
7
+
8
+ // all lowercase is an annoying limitation of Unreal CEF BindUObject
9
+ interface UnrealInjectedSequenceJSWindow {
10
+ ue?: {
11
+ sequencewallettransport?: {
12
+ onmessagefromwallet?: (message: ProviderMessage<any>) => void
13
+ sendmessagetowallet: (message: string) => void
14
+ }
15
+ }
16
+ }
17
+
18
+ declare const window: Window & typeof globalThis & UnrealInjectedSequenceJSWindow
19
+
20
+ /**
21
+ * Initialized on dApp side
22
+ */
23
+ export class UnrealMessageProvider extends BaseProviderTransport {
24
+ private walletURL: URL
25
+
26
+ constructor(walletAppURL: string) {
27
+ super()
28
+ this.walletURL = new URL(walletAppURL)
29
+ }
30
+
31
+ register = () => {
32
+ overrideLogs('dapp')
33
+ if (registeredUnrealMessageProvider) {
34
+ // overriding the registered message provider
35
+ registeredUnrealMessageProvider.unregister()
36
+ registeredUnrealMessageProvider = this
37
+ }
38
+
39
+ // listen for incoming messages from wallet
40
+ if (window.ue?.sequencewallettransport) {
41
+ window.ue.sequencewallettransport.onmessagefromwallet = this.onUnrealCallback
42
+ }
43
+ registeredUnrealMessageProvider = this
44
+
45
+ this._registered = true
46
+ console.log('registering transport!')
47
+ }
48
+
49
+ unregister = () => {
50
+ this._registered = false
51
+ this.closeWallet()
52
+
53
+ // disable message listener
54
+ if (registeredUnrealMessageProvider === this) {
55
+ registeredUnrealMessageProvider = undefined
56
+ }
57
+ if (window.ue?.sequencewallettransport?.onmessagefromwallet === this.onUnrealCallback) {
58
+ delete window.ue.sequencewallettransport.onmessagefromwallet
59
+ }
60
+
61
+ // clear event listeners
62
+ this.events.removeAllListeners()
63
+ }
64
+
65
+ openWallet = (path?: string, intent?: OpenWalletIntent, networkId?: string | number): void => {
66
+ if (this.isOpened()) {
67
+ // TODO focus wallet
68
+ console.log('wallet already open!')
69
+ return
70
+ }
71
+
72
+ console.log('opening wallet!')
73
+ // Instantiate new walletURL for this call
74
+ const walletURL = new URL(this.walletURL.href)
75
+ const windowSessionParams = new WindowSessionParams()
76
+
77
+ if (path) {
78
+ walletURL.pathname = path.toLowerCase()
79
+ }
80
+
81
+ // Set session, intent and network id on walletURL
82
+ this._init = InitState.NIL
83
+ this._sessionId = `${performance.now()}`
84
+ windowSessionParams.set('sid', this._sessionId)
85
+
86
+ if (intent) {
87
+ // encode intent as base64 url-encoded param
88
+ windowSessionParams.set('intent', base64EncodeObject(intent))
89
+ }
90
+ if (networkId) {
91
+ windowSessionParams.set('net', `${networkId}`)
92
+ }
93
+ // serialize params
94
+ walletURL.search = windowSessionParams.toString()
95
+
96
+ console.log('opening wallet to', walletURL.href)
97
+
98
+ window.open(walletURL.href)
99
+ }
100
+
101
+ closeWallet() {
102
+ this.close()
103
+ }
104
+
105
+ // onmessage, receives ProviderMessageResponse from the wallet unreal transport
106
+ private onUnrealCallback = (message: ProviderMessage<any>) => {
107
+ if (!message) {
108
+ throw new Error('ProviderMessage object is empty')
109
+ }
110
+
111
+ // handle message with base message provider
112
+ this.handleMessage(message)
113
+ }
114
+
115
+ // all lowercase is an annoying limitation of Unreal CEF BindUObject
116
+ sendMessage(message: ProviderMessage<unknown>) {
117
+ const postedMessage = typeof message !== 'string' ? JSON.stringify(message) : message
118
+ console.log('Sending message to wallet:', postedMessage)
119
+ window.ue?.sequencewallettransport?.sendmessagetowallet(postedMessage)
120
+ }
121
+ }
@@ -300,17 +300,17 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P
300
300
 
301
301
  // Message must be prefixed with "\x19Ethereum Signed Message:\n"
302
302
  // as defined by EIP-191
303
- message = prefixEIP191Message(message)
303
+ const prefixedMessage = prefixEIP191Message(message)
304
304
 
305
305
  // TODO:
306
306
  // if (process.env.TEST_MODE === 'true' && this.prompter === null) {
307
307
  if (this.prompter === null) {
308
308
  // prompter is null, so we'll sign from here
309
- sig = await signer.signMessage(ethers.utils.arrayify(message), chainId)
309
+ sig = await signer.signMessage(prefixedMessage, chainId)
310
310
  } else {
311
311
  const promptResultForDeployment = await this.handleConfirmWalletDeployPrompt(this.prompter, signer, chainId)
312
312
  if (promptResultForDeployment) {
313
- sig = await this.prompter.promptSignMessage({ chainId: chainId, message }, this.connectOptions)
313
+ sig = await this.prompter.promptSignMessage({ chainId: chainId, message: prefixedMessage }, this.connectOptions)
314
314
  }
315
315
  }
316
316
 
package/src/types.ts CHANGED
@@ -247,7 +247,7 @@ export type OpenWalletIntent =
247
247
  | { type: 'jsonRpcRequest'; method: string }
248
248
 
249
249
  export interface MessageToSign {
250
- message?: string
250
+ message?: Uint8Array
251
251
  typedData?: TypedData
252
252
  chainId?: number
253
253
  }
package/src/wallet.ts CHANGED
@@ -19,9 +19,15 @@ import {
19
19
  } from '@0xsequence/network'
20
20
  import { WalletConfig, WalletState } from '@0xsequence/config'
21
21
  import { logger } from '@0xsequence/utils'
22
- import { JsonRpcProvider, JsonRpcSigner, ExternalProvider } from '@ethersproject/providers'
22
+ import { JsonRpcProvider } from '@ethersproject/providers'
23
23
  import { Web3Provider, Web3Signer } from './provider'
24
- import { MuxMessageProvider, WindowMessageProvider, ProxyMessageProvider, ProxyMessageChannelPort } from './transports'
24
+ import {
25
+ MuxMessageProvider,
26
+ WindowMessageProvider,
27
+ ProxyMessageProvider,
28
+ ProxyMessageChannelPort,
29
+ UnrealMessageProvider
30
+ } from './transports'
25
31
  import { WalletSession, ProviderEventTypes, ConnectOptions, OpenWalletIntent, ConnectDetails } from './types'
26
32
  import { ethers } from 'ethers'
27
33
  import { ExtensionMessageProvider } from './transports/extension-transport/extension-message-provider'
@@ -29,14 +35,11 @@ import { LocalStore, ItemStore, LocalStorage } from './utils'
29
35
  import { WalletUtils } from './utils/index'
30
36
 
31
37
  import { Runtime } from 'webextension-polyfill-ts'
32
- import { sequence } from '../../0xsequence/src'
33
38
 
34
39
  export interface WalletProvider {
35
40
  connect(options?: ConnectOptions): Promise<ConnectDetails>
36
- // authorize(options?: ConnectOptions): Promise<ConnectDetails>
37
41
  disconnect(): void
38
42
 
39
- isOpened(): boolean
40
43
  isConnected(): boolean
41
44
  getSession(): WalletSession | undefined
42
45
 
@@ -45,6 +48,7 @@ export interface WalletProvider {
45
48
  getChainId(): Promise<number>
46
49
  getAuthChainId(): Promise<number>
47
50
 
51
+ isOpened(): boolean
48
52
  openWallet(path?: string, intent?: OpenWalletIntent, networkId?: string | number): Promise<boolean>
49
53
  closeWallet(): void
50
54
 
@@ -87,6 +91,7 @@ export class Wallet implements WalletProvider {
87
91
  windowMessageProvider?: WindowMessageProvider
88
92
  proxyMessageProvider?: ProxyMessageProvider
89
93
  extensionMessageProvider?: ExtensionMessageProvider
94
+ unrealMessageProvider?: UnrealMessageProvider
90
95
  }
91
96
 
92
97
  private networks: NetworkConfig[]
@@ -149,6 +154,10 @@ export class Wallet implements WalletProvider {
149
154
  //
150
155
  // this.transport.messageProvider.add(this.transport.extensionMessageProvider)
151
156
  }
157
+ if (this.config.transports?.unrealTransport?.enabled) {
158
+ this.transport.unrealMessageProvider = new UnrealMessageProvider(this.config.walletAppURL)
159
+ this.transport.messageProvider.add(this.transport.unrealMessageProvider)
160
+ }
152
161
  this.transport.messageProvider.register()
153
162
 
154
163
  // .....
@@ -238,13 +247,23 @@ export class Wallet implements WalletProvider {
238
247
  this.transport.messageProvider.on('walletContext', (walletContext: WalletContext) => {
239
248
  this.useSession({ walletContext: walletContext }, true)
240
249
  })
250
+ }
241
251
 
242
- // Load existing session from localStorage
243
- this.loadSession().then(session => {
252
+ loadSession = async (): Promise<WalletSession | undefined> => {
253
+ const data = await LocalStorage.getInstance().getItem('@sequence.session')
254
+ if (!data || data === '') {
255
+ return undefined
256
+ }
257
+ try {
258
+ const session = JSON.parse(data) as WalletSession
244
259
  if (session) {
245
260
  this.useSession(session, false)
246
261
  }
247
- })
262
+ return session
263
+ } catch (err) {
264
+ logger.warn('loadSession failed, unable to parse session payload from storage.')
265
+ return undefined
266
+ }
248
267
  }
249
268
 
250
269
  connect = async (options?: ConnectOptions): Promise<ConnectDetails> => {
@@ -549,24 +568,10 @@ export class Wallet implements WalletProvider {
549
568
  this.transport.messageProvider!.once(event, fn)
550
569
  }
551
570
 
552
- private loadSession = async (): Promise<WalletSession | undefined> => {
553
- const data = await LocalStorage.getInstance().getItem('@sequence.session')
554
- if (!data || data === '') {
555
- return undefined
556
- }
557
- try {
558
- const session = JSON.parse(data) as WalletSession
559
- return session
560
- } catch (err) {
561
- logger.warn('loadSession failed, unable to parse session payload from localStorage.')
562
- return undefined
563
- }
564
- }
565
-
566
- private saveSession = (session: WalletSession) => {
571
+ private saveSession = async (session: WalletSession) => {
567
572
  logger.debug('wallet provider: saving session')
568
573
  const data = JSON.stringify(session)
569
- LocalStorage.getInstance().setItem('@sequence.session', data)
574
+ await LocalStorage.getInstance().setItem('@sequence.session', data)
570
575
  }
571
576
 
572
577
  private useSession = async (session: WalletSession, autoSave: boolean = true) => {
@@ -698,6 +703,11 @@ export interface ProviderConfig {
698
703
  enabled: boolean
699
704
  runtime: Runtime.Static
700
705
  }
706
+
707
+ // Unreal Engine transport (optional)
708
+ unrealTransport?: {
709
+ enabled: boolean
710
+ }
701
711
  }
702
712
 
703
713
  // Sequence Wallet Modules Context override. By default (and recommended), the
@@ -720,11 +730,12 @@ export const DefaultProviderConfig: ProviderConfig = {
720
730
 
721
731
  let walletInstance: Wallet | undefined
722
732
 
723
- export const initWallet = (network?: string | number, config?: Partial<ProviderConfig>) => {
733
+ export const initWallet = async (network?: string | number, config?: Partial<ProviderConfig>) => {
724
734
  if (walletInstance && walletInstance.isOpened()) {
725
735
  walletInstance.closeWallet()
726
736
  }
727
737
  walletInstance = new Wallet(network, config)
738
+ await walletInstance.loadSession()
728
739
  return walletInstance
729
740
  }
730
741