@0xsequence/provider
Advanced tools
Comparing version 0.18.0 to 0.19.0
# @0xsequence/provider | ||
## 0.19.0 | ||
### Minor Changes | ||
- - provider, improve dapp / wallet transport io | ||
### Patch Changes | ||
- Updated dependencies [undefined] | ||
- @0xsequence/abi@0.19.0 | ||
- @0xsequence/auth@0.19.0 | ||
- @0xsequence/config@0.19.0 | ||
- @0xsequence/network@0.19.0 | ||
- @0xsequence/transactions@0.19.0 | ||
- @0xsequence/utils@0.19.0 | ||
- @0xsequence/wallet@0.19.0 | ||
## 0.18.0 | ||
@@ -4,0 +21,0 @@ |
@@ -12,3 +12,5 @@ import { ethers } from 'ethers'; | ||
export declare class Web3Provider extends EthersWeb3Provider implements JsonRpcHandler { | ||
static isSequenceProvider(cand: any): cand is Web3Provider; | ||
readonly _sender: JsonRpcSender; | ||
readonly _isSequenceProvider: boolean; | ||
readonly _defaultChainId?: number; | ||
@@ -21,2 +23,3 @@ constructor(provider: JsonRpcProvider | JsonRpcHandler | JsonRpcFetchFunc, defaultChainId?: ChainId); | ||
} | ||
export declare function isSequenceProvider(provider: any): provider is Web3Provider; | ||
export declare class LocalWeb3Provider extends Web3Provider { | ||
@@ -23,0 +26,0 @@ constructor(signer: Signer, networks?: Networks); |
import EventEmitter from 'eventemitter3'; | ||
import { ProviderTransport, ProviderMessage, ProviderMessageRequest, ProviderMessageEvent, ProviderMessageResponse, ProviderMessageResponseCallback, WalletSession, OpenState, OpenWalletIntent } from '../types'; | ||
import { ProviderTransport, ProviderMessage, ProviderMessageRequest, ProviderEventTypes, ProviderMessageResponse, ProviderMessageResponseCallback, OpenState, OpenWalletIntent, ConnectDetails, WalletSession, InitState } from '../types'; | ||
import { NetworkConfig, WalletContext, JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network'; | ||
@@ -11,7 +11,13 @@ export declare const PROVIDER_OPEN_TIMEOUT = 30000; | ||
protected confirmationOnly: boolean; | ||
protected events: EventEmitter<ProviderMessageEvent, any>; | ||
protected accountPayload: string | undefined; | ||
protected events: EventEmitter<ProviderEventTypes, any>; | ||
protected openPayload: { | ||
sessionId?: string; | ||
session?: WalletSession; | ||
} | undefined; | ||
protected connectPayload: ConnectDetails | undefined; | ||
protected accountsChangedPayload: string | undefined; | ||
protected networksPayload: NetworkConfig[] | undefined; | ||
protected walletContextPayload: WalletContext | undefined; | ||
protected _sessionId?: string; | ||
protected _init: InitState; | ||
protected _registered: boolean; | ||
@@ -22,3 +28,3 @@ constructor(); | ||
unregister(): void; | ||
openWallet(path?: string, intent?: OpenWalletIntent, defaultNetworkId?: string | number): void; | ||
openWallet(path?: string, intent?: OpenWalletIntent, networkId?: string | number): void; | ||
closeWallet(): void; | ||
@@ -31,8 +37,8 @@ isOpened(): boolean; | ||
sendMessage(message: ProviderMessage<any>): void; | ||
on(event: ProviderMessageEvent, fn: (...args: any[]) => void): void; | ||
once(event: ProviderMessageEvent, fn: (...args: any[]) => void): void; | ||
waitUntilOpened: (openTimeout?: number) => Promise<boolean>; | ||
waitUntilConnected: () => Promise<WalletSession>; | ||
protected open: () => Promise<boolean>; | ||
on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void; | ||
once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void; | ||
emit<K extends keyof ProviderEventTypes>(event: K, ...args: Parameters<ProviderEventTypes[K]>): boolean; | ||
waitUntilOpened: (openTimeout?: number) => Promise<WalletSession | undefined>; | ||
waitUntilConnected: () => Promise<ConnectDetails>; | ||
protected close(): void; | ||
} |
@@ -1,2 +0,2 @@ | ||
import { WalletTransport, ProviderMessage, ProviderMessageRequest, ProviderMessageResponse, ProviderConnectInfo, ProviderRpcError, InitState } from '../types'; | ||
import { WalletTransport, ProviderMessage, ProviderMessageRequest, ProviderMessageResponse, ProviderRpcError, InitState, ConnectDetails, OpenWalletIntent, WalletSession } from '../types'; | ||
import { WalletRequestHandler } from './wallet-request-handler'; | ||
@@ -9,2 +9,5 @@ import { NetworkConfig, WalletContext, JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network'; | ||
protected _init: InitState; | ||
protected _initNonce: string; | ||
protected _initCallback?: (error?: string) => void; | ||
protected appOrigin?: string; | ||
constructor(walletRequestHandler: WalletRequestHandler); | ||
@@ -21,14 +24,15 @@ get registered(): boolean; | ||
sessionId?: string; | ||
session?: WalletSession; | ||
error?: string; | ||
}): void; | ||
notifyClose(): void; | ||
notifyConnect(connectInfo: ProviderConnectInfo & { | ||
error?: string; | ||
}): void; | ||
notifyClose(error?: ProviderRpcError): void; | ||
notifyConnect(connectDetails: ConnectDetails): void; | ||
notifyDisconnect(error?: ProviderRpcError): void; | ||
notifyAccountsChanged(accounts: string[]): void; | ||
notifyChainChanged(hexChainId: string): void; | ||
notifyChainChanged(chainIdHex: string): void; | ||
notifyNetworks(networks: NetworkConfig[]): void; | ||
notifyWalletContext(walletContext: WalletContext): void; | ||
protected open: (defaultNetworkId?: string | number | undefined) => Promise<boolean>; | ||
protected isValidInitAck(message: ProviderMessage<any>): boolean; | ||
private init; | ||
protected open: (intent?: OpenWalletIntent | undefined, networkId?: string | number | undefined) => Promise<boolean>; | ||
} |
@@ -1,2 +0,2 @@ | ||
import { ProviderMessage, ProviderTransport, ProviderMessageEvent, ProviderMessageRequest, ProviderMessageResponse, WalletSession, OpenWalletIntent } from '../../types'; | ||
import { ProviderMessage, ProviderTransport, ProviderEventTypes, ProviderMessageRequest, ProviderMessageResponse, WalletSession, OpenWalletIntent, ConnectDetails } from '../../types'; | ||
import { JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network'; | ||
@@ -10,8 +10,9 @@ export declare class MuxMessageProvider implements ProviderTransport { | ||
unregister: () => void; | ||
openWallet: (path?: string | undefined, intent?: OpenWalletIntent | undefined, defaultNetworkId?: string | number | undefined) => void; | ||
openWallet: (path?: string | undefined, intent?: OpenWalletIntent | undefined, networkId?: string | number | undefined) => void; | ||
closeWallet(): void; | ||
isOpened(): boolean; | ||
isConnected(): boolean; | ||
on(event: ProviderMessageEvent, fn: (...args: any[]) => void): void; | ||
once(event: ProviderMessageEvent, fn: (...args: any[]) => void): void; | ||
on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void; | ||
once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void; | ||
emit<K extends keyof ProviderEventTypes>(event: K, ...args: Parameters<ProviderEventTypes[K]>): boolean; | ||
sendAsync: (request: JsonRpcRequest, callback: JsonRpcResponseCallback, chainId?: number | undefined) => Promise<void>; | ||
@@ -21,4 +22,4 @@ sendMessage(message: ProviderMessage<any>): void; | ||
handleMessage(message: ProviderMessage<any>): void; | ||
waitUntilOpened: () => Promise<boolean>; | ||
waitUntilConnected: () => Promise<WalletSession>; | ||
waitUntilOpened: () => Promise<WalletSession | undefined>; | ||
waitUntilConnected: () => Promise<ConnectDetails>; | ||
} |
import EventEmitter from 'eventemitter3'; | ||
import { ProviderMessage, ProviderMessageTransport } from '../../types'; | ||
import { ProviderMessage, ProviderMessageTransport, ProviderEventTypes } from '../../types'; | ||
export declare class ProxyMessageChannel { | ||
@@ -10,9 +10,8 @@ app: ProxyMessageChannelPort; | ||
conn: ProviderMessageTransport; | ||
events: EventEmitter<ProxyMessageEvent, any>; | ||
events: EventEmitter<ProxyEventTypes, any>; | ||
handleMessage: (message: ProviderMessage<any>) => void; | ||
sendMessage: (message: ProviderMessage<any>) => void; | ||
on(event: ProxyMessageEvent, fn: (...args: any[]) => void): void; | ||
once(event: ProxyMessageEvent, fn: (...args: any[]) => void): void; | ||
on<K extends keyof ProxyEventTypes>(event: K, fn: ProxyEventTypes[K]): void; | ||
once<K extends keyof ProxyEventTypes>(event: K, fn: ProxyEventTypes[K]): void; | ||
} | ||
declare type ProxyMessageEvent = 'open' | 'close' | 'connect' | 'disconnect'; | ||
export {}; | ||
export declare type ProxyEventTypes = Pick<ProviderEventTypes, 'open' | 'close' | 'connect' | 'disconnect'>; |
@@ -9,5 +9,5 @@ import { BaseProviderTransport } from '../base-provider-transport'; | ||
unregister: () => void; | ||
openWallet: (path?: string | undefined, intent?: OpenWalletIntent | undefined, defaultNetworkId?: string | number | undefined) => void; | ||
openWallet: (path?: string | undefined, intent?: OpenWalletIntent | undefined, networkId?: string | number | undefined) => void; | ||
closeWallet(): void; | ||
sendMessage(message: ProviderMessage<any>): void; | ||
} |
@@ -1,2 +0,2 @@ | ||
import { ProviderMessageRequest, ProviderMessageResponse, WalletMessageEvent, ProviderMessageRequestHandler, MessageToSign } from '../types'; | ||
import { ProviderMessageRequest, ProviderMessageResponse, ProviderMessageRequestHandler, MessageToSign, ProviderRpcError, ConnectOptions, ConnectDetails, PromptConnectDetails, WalletSession, ProviderEventTypes } from '../types'; | ||
import { ExternalProvider } from '@ethersproject/providers'; | ||
@@ -6,2 +6,8 @@ import { Networks, NetworkConfig, JsonRpcHandler, JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network'; | ||
import { TransactionRequest } from '@0xsequence/transactions'; | ||
export interface WalletSignInOptions { | ||
connect?: boolean; | ||
mainnetNetworks?: Networks; | ||
testnetNetworks?: Networks; | ||
defaultNetworkId?: string | number; | ||
} | ||
export declare class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, ProviderMessageRequestHandler { | ||
@@ -12,2 +18,3 @@ private signer; | ||
private testnetNetworks; | ||
private _connectOptions?; | ||
private _defaultNetworkId?; | ||
@@ -17,16 +24,23 @@ private _chainId?; | ||
constructor(signer: Signer | null, prompter: WalletUserPrompter | null, mainnetNetworks: Networks, testnetNetworks?: Networks); | ||
connect(signer: Signer | null, mainnetNetworks?: Networks, testnetNetworks?: Networks): Promise<void>; | ||
signIn(signer: Signer | null, options?: WalletSignInOptions): Promise<void>; | ||
connect(options?: ConnectOptions): Promise<ConnectDetails>; | ||
promptConnect: (options?: ConnectOptions | undefined) => Promise<ConnectDetails>; | ||
sendMessageRequest(message: ProviderMessageRequest): Promise<ProviderMessageResponse>; | ||
sendAsync: (request: JsonRpcRequest, callback: JsonRpcResponseCallback, chainId?: number | undefined) => Promise<void>; | ||
on: (event: WalletMessageEvent, fn: (...args: any[]) => void) => void; | ||
once: (event: WalletMessageEvent, fn: (...args: any[]) => void) => void; | ||
on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void; | ||
once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void; | ||
getAddress(): Promise<string>; | ||
getChainId(): Promise<number>; | ||
get connectOptions(): ConnectOptions | undefined; | ||
setConnectOptions(options: ConnectOptions | undefined): void; | ||
get defaultNetworkId(): string | number | undefined; | ||
setDefaultNetwork(chainId: string | number, notifyNetworks?: boolean): Promise<number | undefined>; | ||
get defaultNetworkId(): string | number | undefined; | ||
getNetworks(jsonRpcResponse?: boolean): Promise<NetworkConfig[]>; | ||
notifyConnect(accountAddress: string, chainId: number): void; | ||
walletSession(): Promise<WalletSession | undefined>; | ||
notifyConnect(connectDetails: ConnectDetails): void; | ||
notifyDisconnect(): void; | ||
notifyNetworks(networks?: NetworkConfig[]): Promise<void>; | ||
notifyWalletContext(): Promise<void>; | ||
notifyClose(error?: ProviderRpcError): void; | ||
isSignedIn(): boolean; | ||
getSigner(): Signer | null; | ||
@@ -36,2 +50,3 @@ setSigner(signer: Signer | null): void; | ||
export interface WalletUserPrompter { | ||
promptConnect(options?: ConnectOptions): Promise<PromptConnectDetails>; | ||
promptSignMessage(message: MessageToSign): Promise<string>; | ||
@@ -38,0 +53,0 @@ promptSignTransaction(txn: TransactionRequest, chaindId?: number): Promise<string>; |
import { ProviderMessage } from '../../types'; | ||
import { WalletRequestHandler } from '../wallet-request-handler'; | ||
import { BaseWalletTransport } from '../base-wallet-transport'; | ||
export interface RegisterOptions { | ||
loadingPath: string; | ||
} | ||
export declare class WindowMessageHandler extends BaseWalletTransport { | ||
protected parentWindow: Window; | ||
protected parentOrigin?: string; | ||
private _isPopup; | ||
private _initNonce; | ||
private _postMessageQueue; | ||
constructor(walletRequestHandler: WalletRequestHandler); | ||
register(options?: RegisterOptions): void; | ||
register(): void; | ||
unregister(): void; | ||
@@ -19,4 +13,3 @@ private onWindowEvent; | ||
get isPopup(): boolean; | ||
private flushPostMessageQueue; | ||
private postMessage; | ||
} |
@@ -6,10 +6,9 @@ import { OpenWalletIntent, ProviderMessage } from '../../types'; | ||
private walletWindow; | ||
private _init; | ||
constructor(walletAppURL: string); | ||
register: () => void; | ||
unregister: () => void; | ||
openWallet: (path?: string | undefined, intent?: OpenWalletIntent | undefined, defaultNetworkId?: string | number | undefined) => void; | ||
openWallet: (path?: string | undefined, intent?: OpenWalletIntent | undefined, networkId?: string | number | undefined) => void; | ||
closeWallet(): void; | ||
private onWindowEvent; | ||
sendMessage(message: ProviderMessage<any>, skipIdx?: boolean): void; | ||
sendMessage(message: ProviderMessage<any>): void; | ||
} |
import { NetworkConfig, WalletContext, JsonRpcRequest, JsonRpcResponse, JsonRpcHandler } from '@0xsequence/network'; | ||
import { TypedData } from '@0xsequence/utils'; | ||
export interface WalletSession { | ||
walletContext?: WalletContext; | ||
accountAddress?: string; | ||
networks?: NetworkConfig[]; | ||
providerCache?: { | ||
[key: string]: any; | ||
}; | ||
} | ||
export interface ProviderTransport extends JsonRpcHandler, ProviderMessageTransport, ProviderMessageRequestHandler { | ||
register(): void; | ||
unregister(): void; | ||
openWallet(path?: string, intent?: OpenWalletIntent, defaultNetworkId?: string | number): void; | ||
openWallet(path?: string, intent?: OpenWalletIntent, networkId?: string | number): void; | ||
closeWallet(): void; | ||
isOpened(): boolean; | ||
isConnected(): boolean; | ||
on(event: ProviderMessageEvent, fn: (...args: any[]) => void): void; | ||
once(event: ProviderMessageEvent, fn: (...args: any[]) => void): void; | ||
waitUntilOpened(): Promise<boolean>; | ||
waitUntilConnected(): Promise<WalletSession>; | ||
on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void; | ||
once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void; | ||
emit<K extends keyof ProviderEventTypes>(event: K, ...args: Parameters<ProviderEventTypes[K]>): boolean; | ||
waitUntilOpened(): Promise<WalletSession | undefined>; | ||
waitUntilConnected(): Promise<ConnectDetails>; | ||
} | ||
@@ -29,9 +22,9 @@ export interface WalletTransport extends JsonRpcHandler, ProviderMessageTransport, ProviderMessageRequestHandler { | ||
sessionId?: string; | ||
session?: WalletSession; | ||
error?: string; | ||
}): void; | ||
notifyClose(): void; | ||
notifyConnect(connectInfo: { | ||
chainId?: string; | ||
}): void; | ||
notifyClose(error?: ProviderRpcError): void; | ||
notifyConnect(connectDetails: ConnectDetails): void; | ||
notifyAccountsChanged(accounts: string[]): void; | ||
notifyChainChanged(connectInfo: any): void; | ||
notifyChainChanged(chainIdHex: string): void; | ||
notifyNetworks(networks: NetworkConfig[]): void; | ||
@@ -48,5 +41,2 @@ } | ||
export declare type ProviderMessageResponseCallback = (error: any, response?: ProviderMessageResponse) => void; | ||
export interface ProviderConnectInfo { | ||
chainId: string; | ||
} | ||
export interface ProviderRpcError extends Error { | ||
@@ -66,5 +56,3 @@ message: string; | ||
} | ||
export declare type WalletMessageEvent = 'open' | 'close' | 'connect' | 'disconnect' | 'chainChanged' | 'accountsChanged' | 'networks' | 'walletContext' | 'init' | '_debug'; | ||
export declare type ProviderMessageEvent = 'message' | WalletMessageEvent; | ||
export declare enum ProviderMessageType { | ||
export declare enum EventType { | ||
OPEN = "open", | ||
@@ -75,4 +63,4 @@ CLOSE = "close", | ||
DISCONNECT = "disconnect", | ||
ACCOUNTS_CHANGED = "accountsChanged", | ||
CHAIN_CHANGED = "chainChanged", | ||
ACCOUNTS_CHANGED = "accountsChanged", | ||
NETWORKS = "networks", | ||
@@ -83,2 +71,20 @@ WALLET_CONTEXT = "walletContext", | ||
} | ||
export interface WalletEventTypes { | ||
'open': (openInfo: { | ||
chainId?: string; | ||
sessionId?: string; | ||
session?: WalletSession; | ||
error?: string; | ||
}) => void; | ||
'close': (error?: ProviderRpcError) => void; | ||
'connect': (connectDetails: ConnectDetails) => void; | ||
'disconnect': (error?: ProviderRpcError) => void; | ||
'accountsChanged': (accounts: string[]) => void; | ||
'chainChanged': (chainIdHex: string) => void; | ||
'networks': (networks: NetworkConfig[]) => void; | ||
'walletContext': (walletContext: WalletContext) => void; | ||
} | ||
export interface ProviderEventTypes extends WalletEventTypes { | ||
'message': (message: ProviderMessageResponse) => void; | ||
} | ||
export declare enum OpenState { | ||
@@ -94,16 +100,21 @@ CLOSED = 0, | ||
} | ||
export declare type NetworkEventPayload = NetworkConfig; | ||
export interface ConnectOptions { | ||
networkId?: string | number; | ||
app?: string; | ||
origin?: string; | ||
expiry?: number; | ||
authorize?: boolean; | ||
askForEmail?: boolean; | ||
refresh?: boolean; | ||
requestAuthorization?: boolean; | ||
requestEmail?: boolean; | ||
keepWalletOpened?: boolean; | ||
} | ||
export interface ConnectDetails { | ||
success: boolean; | ||
proof?: { | ||
type?: string; | ||
sig: string; | ||
}; | ||
chainId?: string; | ||
error?: string; | ||
connected: boolean; | ||
session?: WalletSession; | ||
proof?: ETHAuthProof; | ||
email?: string; | ||
} | ||
export declare type PromptConnectDetails = Pick<ConnectDetails, 'connected' | 'proof' | 'email'>; | ||
export declare type OpenWalletIntent = { | ||
@@ -121,1 +132,17 @@ type: 'connect'; | ||
} | ||
export interface ETHAuthProof { | ||
typedData: TypedData; | ||
proofString: string; | ||
} | ||
export interface WalletSession { | ||
walletContext?: WalletContext; | ||
accountAddress?: string; | ||
networks?: NetworkConfig[]; | ||
providerCache?: { | ||
[key: string]: any; | ||
}; | ||
} | ||
export declare class ProviderError extends Error { | ||
constructor(message?: string); | ||
} | ||
export declare const ErrSignedInRequired: ProviderError; |
@@ -5,6 +5,6 @@ import { NetworkConfig, WalletContext, ChainId } from '@0xsequence/network'; | ||
import { ProxyMessageChannelPort } from './transports'; | ||
import { WalletSession, ProviderMessageEvent, ConnectOptions, OpenWalletIntent } from './types'; | ||
import { WalletSession, ProviderEventTypes, ConnectOptions, OpenWalletIntent, ConnectDetails } from './types'; | ||
import { WalletCommands } from './commands'; | ||
export interface WalletProvider { | ||
connect(options?: ConnectOptions): Promise<boolean>; | ||
connect(options?: ConnectOptions): Promise<ConnectDetails>; | ||
disconnect(): void; | ||
@@ -18,3 +18,3 @@ isOpened(): boolean; | ||
getAuthChainId(): Promise<number>; | ||
openWallet(path?: string, intent?: OpenWalletIntent): Promise<boolean>; | ||
openWallet(path?: string, intent?: OpenWalletIntent, networkId?: string | number): Promise<boolean>; | ||
closeWallet(): void; | ||
@@ -28,4 +28,4 @@ getProvider(chainId?: ChainId): Web3Provider | undefined; | ||
getProviderConfig(): ProviderConfig; | ||
on(event: ProviderMessageEvent, fn: (...args: any[]) => void): void; | ||
once(event: ProviderMessageEvent, fn: (...args: any[]) => void): void; | ||
on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void; | ||
once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void; | ||
commands: WalletCommands; | ||
@@ -42,3 +42,4 @@ } | ||
private init; | ||
connect: (options?: ConnectOptions | undefined) => Promise<boolean>; | ||
connect: (options?: ConnectOptions | undefined) => Promise<ConnectDetails>; | ||
authorize: (options?: ConnectOptions | undefined) => Promise<ConnectDetails>; | ||
disconnect(): void; | ||
@@ -53,3 +54,3 @@ getProviderConfig(): ProviderConfig; | ||
getAuthChainId: () => Promise<number>; | ||
openWallet: (path?: string | undefined, intent?: OpenWalletIntent | undefined) => Promise<boolean>; | ||
openWallet: (path?: string | undefined, intent?: OpenWalletIntent | undefined, networkId?: string | number | undefined) => Promise<boolean>; | ||
closeWallet: () => void; | ||
@@ -68,4 +69,4 @@ getProvider(chainId?: ChainId): Web3Provider | undefined; | ||
isDeployed(chainId?: ChainId): Promise<boolean>; | ||
on(event: ProviderMessageEvent, fn: (...args: any[]) => void): void; | ||
once(event: ProviderMessageEvent, fn: (...args: any[]) => void): void; | ||
on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void; | ||
once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void; | ||
private loadSession; | ||
@@ -72,0 +73,0 @@ private saveSession; |
{ | ||
"name": "@0xsequence/provider", | ||
"version": "0.18.0", | ||
"version": "0.19.0", | ||
"description": "provider sub-package for Sequence", | ||
@@ -16,9 +16,9 @@ "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/provider", | ||
"dependencies": { | ||
"@0xsequence/abi": "^0.18.0", | ||
"@0xsequence/auth": "^0.18.0", | ||
"@0xsequence/config": "^0.18.0", | ||
"@0xsequence/network": "^0.18.0", | ||
"@0xsequence/transactions": "^0.18.0", | ||
"@0xsequence/utils": "^0.18.0", | ||
"@0xsequence/wallet": "^0.18.0", | ||
"@0xsequence/abi": "^0.19.0", | ||
"@0xsequence/auth": "^0.19.0", | ||
"@0xsequence/config": "^0.19.0", | ||
"@0xsequence/network": "^0.19.0", | ||
"@0xsequence/transactions": "^0.19.0", | ||
"@0xsequence/utils": "^0.19.0", | ||
"@0xsequence/wallet": "^0.19.0", | ||
"@ethersproject/abstract-signer": "5.0.14", | ||
@@ -25,0 +25,0 @@ "@ethersproject/hash": "^5.0.12", |
@@ -13,17 +13,13 @@ import { ethers } from 'ethers' | ||
// naming..? | ||
// Web3Provider, Web3Signer, Web3Relayer, Web3Indexer | ||
// | ||
//.. or.... SequenceProvider, SequenceSigner, SequenceRelayer, SequenceIndexer | ||
// | ||
export class Web3Provider extends EthersWeb3Provider implements JsonRpcHandler { | ||
// also, make to separate util method | ||
// static isSequenceProvider(cand: any): cand is Web3Provider { | ||
// } | ||
static isSequenceProvider(cand: any): cand is Web3Provider { | ||
return isSequenceProvider(cand) | ||
} | ||
readonly _sender: JsonRpcSender | ||
readonly _isSequenceProvider: boolean | ||
// defaultChainId is the default chainId to use with requests, but may be | ||
@@ -40,2 +36,3 @@ // overridden by passing chainId argument to a specific request | ||
this._sender = sender | ||
this._isSequenceProvider = true | ||
this._defaultChainId = maybeNetworkId(defaultChainId) | ||
@@ -68,2 +65,7 @@ } | ||
export function isSequenceProvider(provider: any): provider is Web3Provider { | ||
const cand = provider as Web3Provider | ||
return cand && cand.send !== undefined && cand._isSequenceProvider === true | ||
} | ||
export class LocalWeb3Provider extends Web3Provider { | ||
@@ -70,0 +72,0 @@ constructor(signer: Signer, networks?: Networks) { |
import EventEmitter from 'eventemitter3' | ||
import { | ||
ProviderTransport, ProviderMessage, ProviderMessageRequest, | ||
ProviderMessageType, ProviderMessageEvent, ProviderMessageResponse, | ||
ProviderMessageResponseCallback, ProviderMessageTransport, | ||
WalletSession, OpenState, OpenWalletIntent | ||
ProviderTransport, | ||
ProviderMessage, | ||
ProviderMessageRequest, | ||
EventType, | ||
ProviderEventTypes, | ||
ProviderMessageResponse, | ||
ProviderMessageResponseCallback, | ||
OpenState, | ||
OpenWalletIntent, | ||
ConnectDetails, | ||
WalletSession, | ||
ProviderRpcError, | ||
InitState | ||
} from '../types' | ||
@@ -21,3 +30,2 @@ | ||
export abstract class BaseProviderTransport implements ProviderTransport { | ||
protected pendingMessageRequests: ProviderMessageRequest[] = [] | ||
@@ -28,5 +36,7 @@ protected responseCallbacks = new Map<number, ProviderMessageResponseCallback>() | ||
protected confirmationOnly: boolean = false | ||
protected events: EventEmitter<ProviderMessageEvent, any> = new EventEmitter() | ||
protected accountPayload: string | undefined | ||
protected events: EventEmitter<ProviderEventTypes, any> = new EventEmitter() | ||
protected openPayload: { sessionId?: string; session?: WalletSession } | undefined | ||
protected connectPayload: ConnectDetails | undefined | ||
protected accountsChangedPayload: string | undefined | ||
protected networksPayload: NetworkConfig[] | undefined | ||
@@ -36,2 +46,3 @@ protected walletContextPayload: WalletContext | undefined | ||
protected _sessionId?: string | ||
protected _init: InitState | ||
protected _registered: boolean | ||
@@ -42,2 +53,3 @@ | ||
this._registered = false | ||
this._init = InitState.NIL | ||
} | ||
@@ -57,3 +69,3 @@ | ||
openWallet(path?: string, intent?: OpenWalletIntent, defaultNetworkId?: string | number) { | ||
openWallet(path?: string, intent?: OpenWalletIntent, networkId?: string | number) { | ||
throw new Error('abstract method') | ||
@@ -72,6 +84,7 @@ } | ||
// if we're registered, and we have the account details, then we are connected | ||
const session = this.openPayload?.session | ||
return ( | ||
this.registered && | ||
!!this.accountPayload && this.accountPayload.length === 42 && | ||
!!this.networksPayload && this.networksPayload.length > 0 | ||
this.registered && session !== undefined && | ||
!!session.accountAddress && session.accountAddress.length === 42 && | ||
!!session.networks && session.networks.length > 0 | ||
) | ||
@@ -91,3 +104,7 @@ } | ||
// automatically open the wallet when a provider request makes it here. | ||
this.openWallet(undefined, { type: 'jsonRpcRequest', method: request.method }) | ||
// | ||
// NOTE: if we're not signed in, then the provider will fail, users must first connect+sign in. | ||
// | ||
// TODO: how does this behave with a session has expired? | ||
this.openWallet(undefined, { type: 'jsonRpcRequest', method: request.method }, chainId) | ||
@@ -102,3 +119,3 @@ // send message request, await, and then execute callback after receiving the response | ||
idx: nextMessageIdx(), | ||
type: ProviderMessageType.MESSAGE, | ||
type: EventType.MESSAGE, | ||
data: request, | ||
@@ -116,4 +133,28 @@ chainId: chainId | ||
// init incoming for initial handshake with transport. | ||
if (this._init !== InitState.OK) { | ||
// if provider is not init'd, then we drop any received messages. the only | ||
// message we will process is of event type 'init', as our acknowledgement | ||
if (message.type === EventType.INIT) { | ||
logger.debug('MessageProvider, received INIT message', message) | ||
const { nonce } = message.data as { nonce: string } | ||
if (!nonce || nonce.length == 0) { | ||
logger.error('invalid init nonce') | ||
return | ||
} | ||
this._init = InitState.OK | ||
this.sendMessage({ | ||
idx: -1, | ||
type: EventType.INIT, | ||
data: { | ||
sessionId: this._sessionId, | ||
nonce: nonce | ||
} | ||
}) | ||
} | ||
return | ||
} | ||
// message is either a notification, or its a ProviderMessageResponse | ||
logger.debug("RECEIVED MESSAGE FROM WALLET", message.idx, message) | ||
logger.debug('RECEIVED MESSAGE FROM WALLET', message.idx, message) | ||
@@ -128,4 +169,4 @@ const requestIdx = message.idx | ||
// | ||
// Flip opened flag, and flush the pending queue | ||
if (message.type === ProviderMessageType.OPEN && !this.isOpened()) { | ||
// Flip opened flag, and flush the pending queue | ||
if (message.type === EventType.OPEN && !this.isOpened()) { | ||
if (this._sessionId && this._sessionId !== message.data?.sessionId) { | ||
@@ -146,3 +187,4 @@ logger.debug('open event received from wallet, but does not match sessionId', this._sessionId) | ||
this.state = OpenState.OPENED | ||
this.events.emit('open') | ||
this.openPayload = message.data | ||
this.events.emit('open', this.openPayload!) | ||
@@ -162,4 +204,3 @@ // flush pending requests when connected | ||
// MESSAGE resposne | ||
if (message.type === ProviderMessageType.MESSAGE) { | ||
if (message.type === EventType.MESSAGE) { | ||
// Require user confirmation, bring up wallet to prompt for input then close | ||
@@ -173,3 +214,3 @@ // TODO: perhaps apply technique like in multicall to queue messages within | ||
} | ||
}, 1500) // TODO: be smarter about timer as we're processing the response callbacks.. | ||
}, 500) // TODO: be smarter about timer as we're processing the response callbacks.. | ||
} | ||
@@ -186,2 +227,3 @@ | ||
if (responseCallback) { | ||
this.events.emit('message', message) | ||
responseCallback(undefined, message) | ||
@@ -193,7 +235,7 @@ return | ||
// ACCOUNTS_CHANGED -- when a user logs in or out | ||
if (message.type === ProviderMessageType.ACCOUNTS_CHANGED) { | ||
this.accountPayload = undefined | ||
if (message.type === EventType.ACCOUNTS_CHANGED) { | ||
this.accountsChangedPayload = undefined | ||
if (message.data && message.data.length > 0) { | ||
this.accountPayload = ethers.utils.getAddress(message.data[0]) | ||
this.events.emit('accountsChanged', [this.accountPayload]) | ||
this.accountsChangedPayload = ethers.utils.getAddress(message.data[0]) | ||
this.events.emit('accountsChanged', [this.accountsChangedPayload]) | ||
} else { | ||
@@ -206,3 +248,3 @@ this.events.emit('accountsChanged', []) | ||
// CHAIN_CHANGED -- when a user changes their default chain | ||
if (message.type === ProviderMessageType.CHAIN_CHANGED) { | ||
if (message.type === EventType.CHAIN_CHANGED) { | ||
this.events.emit('chainChanged', message.data) | ||
@@ -213,5 +255,5 @@ return | ||
// NOTIFY NETWORKS -- when a user connects or logs in | ||
if (message.type === ProviderMessageType.NETWORKS) { | ||
if (message.type === EventType.NETWORKS) { | ||
this.networksPayload = message.data | ||
this.events.emit('networks', this.networksPayload) | ||
this.events.emit('networks', this.networksPayload!) | ||
return | ||
@@ -221,5 +263,5 @@ } | ||
// NOTIFY WALLET_CONTEXT -- when a user connects or logs in | ||
if (message.type === ProviderMessageType.WALLET_CONTEXT) { | ||
if (message.type === EventType.WALLET_CONTEXT) { | ||
this.walletContextPayload = message.data | ||
this.events.emit('walletContext', this.walletContextPayload) | ||
this.events.emit('walletContext', this.walletContextPayload!) | ||
return | ||
@@ -229,3 +271,3 @@ } | ||
// NOTIFY CLOSE -- when wallet instructs to close | ||
if (message.type === ProviderMessageType.CLOSE) { | ||
if (message.type === EventType.CLOSE) { | ||
if (this.state !== OpenState.CLOSED) { | ||
@@ -236,4 +278,10 @@ this.close() | ||
// NOTIFY CONNECT -- when wallet instructs we've connected | ||
if (message.type === EventType.CONNECT) { | ||
this.connectPayload = message.data | ||
this.events.emit('connect', this.connectPayload!) | ||
} | ||
// NOTIFY DISCONNECT -- when wallet instructs to disconnect | ||
if (message.type === ProviderMessageType.DISCONNECT) { | ||
if (message.type === EventType.DISCONNECT) { | ||
if (this.isConnected()) { | ||
@@ -249,3 +297,3 @@ this.events.emit('disconnect', message.data) | ||
return new Promise((resolve, reject) => { | ||
if (!message.idx || message.idx <= 0) { | ||
if ((!message.idx || message.idx <= 0) && message.type !== 'init') { | ||
reject(new Error('message idx not set')) | ||
@@ -284,14 +332,18 @@ } | ||
on(event: ProviderMessageEvent, fn: (...args: any[]) => void) { | ||
this.events.on(event, fn) | ||
on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]) { | ||
this.events.on(event, fn as any) | ||
} | ||
once(event: ProviderMessageEvent, fn: (...args: any[]) => void) { | ||
this.events.once(event, fn) | ||
once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]) { | ||
this.events.once(event, fn as any) | ||
} | ||
waitUntilOpened = async (openTimeout = PROVIDER_OPEN_TIMEOUT): Promise<boolean> => { | ||
emit<K extends keyof ProviderEventTypes>(event: K, ...args: Parameters<ProviderEventTypes[K]>): boolean { | ||
return this.events.emit(event, ...args as any) | ||
} | ||
waitUntilOpened = async (openTimeout = PROVIDER_OPEN_TIMEOUT): Promise<WalletSession | undefined> => { | ||
let opened = false | ||
return Promise.race([ | ||
new Promise<boolean>((_, reject) => { | ||
new Promise<WalletSession | undefined>((_, reject) => { | ||
const timeout = setTimeout(() => { | ||
@@ -302,3 +354,3 @@ clearTimeout(timeout) | ||
this.state = OpenState.CLOSED | ||
this.events.emit('close') | ||
this.events.emit('close', { code: 1005, message: 'opening wallet timed out' } as ProviderRpcError) | ||
} | ||
@@ -308,11 +360,12 @@ reject(new Error('opening wallet timed out')) | ||
}), | ||
new Promise<boolean>(resolve => { | ||
new Promise<WalletSession | undefined>(resolve => { | ||
if (this.isOpened()) { | ||
opened = true | ||
resolve(true) | ||
resolve(this.openPayload?.session) | ||
return | ||
} | ||
this.events.once('open', () => { | ||
this.events.once('open', (openInfo: { session?: WalletSession }) => { | ||
this.openPayload = openInfo | ||
opened = true | ||
resolve(true) | ||
resolve(openInfo.session) | ||
}) | ||
@@ -323,46 +376,18 @@ }) | ||
waitUntilConnected = async (): Promise<WalletSession> => { | ||
waitUntilConnected = async (): Promise<ConnectDetails> => { | ||
await this.waitUntilOpened() | ||
const connect = Promise.all([ | ||
new Promise<string | undefined>(resolve => { | ||
if (this.accountPayload) { | ||
resolve(this.accountPayload) | ||
return | ||
} | ||
this.events.once('accountsChanged', (accounts) => { | ||
if (accounts && accounts.length > 0) { | ||
// account logged in | ||
resolve(accounts[0]) | ||
} else { | ||
// account logged out | ||
resolve(undefined) | ||
} | ||
}) | ||
}), | ||
new Promise<NetworkConfig[]>(resolve => { | ||
if (this.networksPayload) { | ||
resolve(this.networksPayload) | ||
return | ||
} | ||
this.events.once('networks', (networks) => { | ||
resolve(networks) | ||
}) | ||
}), | ||
new Promise<WalletContext>(resolve => { | ||
if (this.walletContextPayload) { | ||
resolve(this.walletContextPayload) | ||
return | ||
} | ||
this.events.once('walletContext', (walletContext) => { | ||
resolve(walletContext) | ||
}) | ||
const connect = new Promise<ConnectDetails>(resolve => { | ||
if (this.connectPayload) { | ||
resolve(this.connectPayload) | ||
return | ||
} | ||
this.events.once('connect', connectDetails => { | ||
this.connectPayload = connectDetails | ||
resolve(connectDetails) | ||
}) | ||
]).then(values => { | ||
const [ accountAddress, networks, walletContext ] = values | ||
return { accountAddress, networks, walletContext } | ||
}) | ||
const closeWallet = new Promise<WalletSession>((_, reject) => { | ||
const closeWallet = new Promise<ConnectDetails>((_, reject) => { | ||
this.events.once('close', () => { | ||
@@ -373,25 +398,8 @@ reject(new Error('user closed the wallet')) | ||
return Promise.race<WalletSession>([ | ||
connect, | ||
closeWallet | ||
]) | ||
return Promise.race<ConnectDetails>([ connect, closeWallet ]) | ||
} | ||
protected open = async (): Promise<boolean> => { | ||
if (this.isOpened()) return true | ||
// Set to opening state | ||
this.state = OpenState.OPENING | ||
// Wait for open response from wallet, or timeout | ||
let opened: boolean | undefined = undefined | ||
try { | ||
opened = await this.waitUntilOpened() | ||
} catch (err) { | ||
opened = false | ||
} | ||
return opened | ||
} | ||
protected close() { | ||
if (this.state === OpenState.CLOSED) return | ||
this.state = OpenState.CLOSED | ||
@@ -409,3 +417,5 @@ this.confirmationOnly = false | ||
this.accountPayload = undefined | ||
this.connectPayload = undefined | ||
this.openPayload = undefined | ||
this.accountsChangedPayload = undefined | ||
this.networksPayload = undefined | ||
@@ -412,0 +422,0 @@ this.walletContextPayload = undefined |
import { ethers } from 'ethers' | ||
import { | ||
WalletTransport, ProviderMessage, ProviderMessageRequest, | ||
ProviderMessageType, ProviderMessageResponse, ProviderMessageTransport, | ||
ProviderConnectInfo, ProviderRpcError, InitState | ||
EventType, ProviderMessageResponse, ProviderMessageTransport, | ||
ProviderRpcError, InitState, ConnectDetails, OpenWalletIntent, WalletSession | ||
} from '../types' | ||
@@ -11,4 +11,7 @@ | ||
import { NetworkConfig, WalletContext, JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network' | ||
import { logger } from '@0xsequence/utils' | ||
import { logger, sanitizeAlphanumeric, sanitizeHost } from '@0xsequence/utils' | ||
import { AuthorizationOptions } from '@0xsequence/auth' | ||
import { PROVIDER_OPEN_TIMEOUT } from './base-provider-transport' | ||
export abstract class BaseWalletTransport implements WalletTransport { | ||
@@ -19,4 +22,12 @@ | ||
protected _registered: boolean | ||
protected _init: InitState | ||
protected _initNonce: string | ||
protected _initCallback?: (error?: string) => void | ||
// appOrigin identifies the dapp's origin which opened the app. A transport | ||
// will auto-detect and set this value if it can. This is determined | ||
// as the parent app/window which opened the wallet. | ||
protected appOrigin?: string | ||
constructor(walletRequestHandler: WalletRequestHandler) { | ||
@@ -26,6 +37,6 @@ this.walletRequestHandler = walletRequestHandler | ||
this.walletRequestHandler.on('connect', (connectInfo: any) => { | ||
this.walletRequestHandler.on('connect', (connectDetails: ConnectDetails) => { | ||
if (!this.registered) return | ||
// means user has logged in and wallet is connected to the app | ||
this.notifyConnect(connectInfo) | ||
this.notifyConnect(connectDetails) | ||
}) | ||
@@ -58,2 +69,7 @@ | ||
}) | ||
this.walletRequestHandler.on('close', (error?: ProviderRpcError) => { | ||
if (!this.registered) return | ||
this.notifyClose(error) | ||
}) | ||
} | ||
@@ -80,19 +96,37 @@ | ||
// ensure initial handshake is complete before accepting | ||
// other kinds of messages. | ||
if (this._init !== InitState.OK) { | ||
if (request.type === EventType.INIT) { | ||
if (this.isValidInitAck(message)) { | ||
// successful init | ||
if (this._initCallback) this._initCallback() | ||
} else { | ||
// failed init | ||
if (this._initCallback) this._initCallback('invalid init') | ||
return | ||
} | ||
} else { | ||
// we expect init message first. do nothing here. | ||
} | ||
return | ||
} | ||
// handle request | ||
switch (request.type) { | ||
case ProviderMessageType.OPEN: { | ||
case EventType.OPEN: { | ||
if (this._init !== InitState.OK) return | ||
const { defaultNetworkId } = request.data | ||
this.open(defaultNetworkId) | ||
const { intent, networkId } = request.data | ||
await this.open(intent, networkId) | ||
return | ||
} | ||
// case ProviderMessageType.CLOSE: { | ||
// if (this._init !== InitState.OK) return | ||
// // we echo back to close, confirming wallet close request | ||
// this.notifyClose() | ||
// return | ||
// } | ||
case EventType.CLOSE: { | ||
if (this._init !== InitState.OK) return | ||
// noop. just here to capture the message so event emitters may be notified | ||
return | ||
} | ||
case ProviderMessageType.MESSAGE: { | ||
case EventType.MESSAGE: { | ||
const response = await this.walletRequestHandler.sendMessageRequest(request) | ||
@@ -123,9 +157,9 @@ this.sendMessage(response) | ||
notifyOpen(openInfo: { chainId?: string, sessionId?: string, error?: string }) { | ||
const { chainId, sessionId, error } = openInfo | ||
notifyOpen(openInfo: { chainId?: string, sessionId?: string, session?: WalletSession, error?: string }) { | ||
const { chainId, sessionId, session, error } = openInfo | ||
this.sendMessage({ | ||
idx: -1, | ||
type: ProviderMessageType.OPEN, | ||
type: EventType.OPEN, | ||
data: { | ||
chainId, sessionId, error | ||
chainId, sessionId, session, error | ||
} | ||
@@ -135,18 +169,15 @@ }) | ||
notifyClose() { | ||
notifyClose(error?: ProviderRpcError) { | ||
this.sendMessage({ | ||
idx: -1, | ||
type: ProviderMessageType.CLOSE, | ||
data: null | ||
type: EventType.CLOSE, | ||
data: error | ||
}) | ||
} | ||
notifyConnect(connectInfo: ProviderConnectInfo & { error?: string }) { | ||
const { chainId, error } = connectInfo | ||
notifyConnect(connectDetails: ConnectDetails) { | ||
this.sendMessage({ | ||
idx: -1, | ||
type: ProviderMessageType.CONNECT, | ||
data: { | ||
chainId, error | ||
} | ||
type: EventType.CONNECT, | ||
data: connectDetails | ||
}) | ||
@@ -158,3 +189,3 @@ } | ||
idx: -1, | ||
type: ProviderMessageType.DISCONNECT, | ||
type: EventType.DISCONNECT, | ||
data: error | ||
@@ -167,3 +198,3 @@ }) | ||
idx: -1, | ||
type: ProviderMessageType.ACCOUNTS_CHANGED, | ||
type: EventType.ACCOUNTS_CHANGED, | ||
data: accounts | ||
@@ -173,7 +204,7 @@ }) | ||
notifyChainChanged(hexChainId: string) { | ||
notifyChainChanged(chainIdHex: string) { | ||
this.sendMessage({ | ||
idx: -1, | ||
type: ProviderMessageType.CHAIN_CHANGED, | ||
data: hexChainId | ||
type: EventType.CHAIN_CHANGED, | ||
data: chainIdHex | ||
}) | ||
@@ -185,3 +216,3 @@ } | ||
idx: -1, | ||
type: ProviderMessageType.NETWORKS, | ||
type: EventType.NETWORKS, | ||
data: networks | ||
@@ -194,3 +225,3 @@ }) | ||
idx: -1, | ||
type: ProviderMessageType.WALLET_CONTEXT, | ||
type: EventType.WALLET_CONTEXT, | ||
data: walletContext | ||
@@ -200,61 +231,186 @@ }) | ||
protected open = async (defaultNetworkId?: string | number): Promise<boolean> => { | ||
let loggedIn = false | ||
const accountAddress = await this.walletRequestHandler.getAddress() | ||
if (accountAddress && accountAddress.startsWith('0x') && accountAddress.length === 42) { | ||
loggedIn = true | ||
protected isValidInitAck(message: ProviderMessage<any>): boolean { | ||
if (this._init === InitState.OK) { | ||
// we're already in init state, we shouldn't handle this message | ||
logger.warn('isValidInitAck, already in init\'d state, so inquiry is invalid.') | ||
return false | ||
} | ||
if (message.type !== EventType.INIT) { | ||
logger.warn('isValidInitAck, invalid message type, expecting init') | ||
return false | ||
} | ||
if (!loggedIn) { | ||
// open wallet without a specific connected chainId, as the user is not logged in | ||
this.notifyOpen({ | ||
sessionId: this._sessionId | ||
}) | ||
// this.notifyAccountsChanged([]) | ||
return true | ||
const { sessionId, nonce } = (message.data as any) as { sessionId: string; nonce: string } | ||
if (!sessionId || sessionId.length === 0 || !nonce || nonce.length === 0) { | ||
logger.error('invalid init ack') | ||
return false | ||
} | ||
if (sessionId !== this._sessionId || nonce !== this._initNonce) { | ||
logger.error('invalid init ack match') | ||
return false | ||
} | ||
// account is logged in, lets return chainId information | ||
let chainId: number | undefined = undefined | ||
try { | ||
if (defaultNetworkId) { | ||
chainId = await this.walletRequestHandler.setDefaultNetwork(defaultNetworkId, false) | ||
} else { | ||
chainId = await this.walletRequestHandler.getChainId() | ||
// all checks pass, its true | ||
return true | ||
} | ||
private init(): Promise<void> { | ||
return new Promise<void>((resolve, reject) => { | ||
// avoid re-init`ing, or if there is a transport which doesn't require | ||
// it, then it may set this._init to OK in its constructor. | ||
if (this._init === InitState.OK) { | ||
resolve() | ||
return | ||
} | ||
} catch (err) { | ||
if (this._init !== InitState.NIL || this._initCallback) { | ||
reject('transport init is in progress') | ||
return | ||
} | ||
// start init timeout, if we don't receive confirmation | ||
// from provider within this amount of time, then we timeout | ||
const initTimeout = setTimeout(() => { | ||
logger.warn('transport init timed out') | ||
if (this._initCallback) { | ||
this._initCallback('transport init timed out') | ||
} | ||
}, PROVIDER_OPEN_TIMEOUT / 2) | ||
// setup callback as we receive the init message async in the handleMessage function | ||
this._initCallback = (error?: string) => { | ||
this._initCallback = undefined // reset | ||
clearTimeout(initTimeout) | ||
if (error) { | ||
reject(error) | ||
} else { | ||
this._init = InitState.OK | ||
resolve() | ||
} | ||
} | ||
// send init request with random nonce to the provider, where we expect | ||
// for the provider to echo it back to us as complete handshake | ||
this._initNonce = `${performance.now()}` | ||
this.sendMessage({ | ||
idx: -1, | ||
type: EventType.INIT, | ||
data: { nonce: this._initNonce } | ||
}) | ||
this._init = InitState.SENT_NONCE | ||
// NOTE: the promise will resolve in the _initCallback method | ||
// which will be called from either handleMessage or the initTimeout | ||
}) | ||
} | ||
protected open = async (intent?: OpenWalletIntent, networkId?: string | number): Promise<boolean> => { | ||
// init handshake for certain transports, before we can open the communication. | ||
// | ||
// for example, with the window-transport, we have to exchange messages to determine the | ||
// origin host of the dapp. | ||
await this.init() | ||
// Prepare connect options from intent | ||
if (intent && intent.type === 'connect' && intent.options) { | ||
const connectOptions = intent.options | ||
const authorizeOptions: AuthorizationOptions = connectOptions // overlapping types | ||
// Sanity/integrity check the intent payload, and set authorization origin | ||
// if its been determined as part of the init handshake from earlier. | ||
if (this.appOrigin && authorizeOptions?.origin) { | ||
if (authorizeOptions.origin !== this.appOrigin) { | ||
throw new Error('origin is invalid') | ||
} else { | ||
// request origin and derived origins match, lets carry on | ||
} | ||
} else if (!this.appOrigin && authorizeOptions?.origin) { | ||
// ie. when we can't determine the origin in our transport, but dapp provides it to us. | ||
// we just sanitize the origin host. | ||
connectOptions.origin = sanitizeHost(authorizeOptions.origin) | ||
} else if (this.appOrigin) { | ||
// ie. when we auto-determine the origin such as in window-transport | ||
connectOptions.origin = this.appOrigin | ||
} | ||
if (connectOptions.app) { | ||
connectOptions.app = sanitizeAlphanumeric(connectOptions.app) | ||
} | ||
// Set connect options on the walletRequestHandler as our primary | ||
// wallet controller | ||
this.walletRequestHandler.setConnectOptions(connectOptions) | ||
if (connectOptions.networkId) { | ||
networkId = connectOptions.networkId | ||
} | ||
} else { | ||
this.walletRequestHandler.setConnectOptions(undefined) | ||
} | ||
// failed to set default network or open | ||
if (!chainId || chainId <= 0) { | ||
// Notify open and proceed to prompt for connection if intended | ||
if (!this.walletRequestHandler.isSignedIn()) { | ||
// open wallet without a specific connected chainId, as the user is not signed in | ||
this.notifyOpen({ | ||
sessionId: this._sessionId, | ||
error: `failed to open wallet on network ${defaultNetworkId}` | ||
sessionId: this._sessionId | ||
}) | ||
return false | ||
} | ||
return true | ||
// successfully opened wallet to the default network | ||
this.notifyOpen({ | ||
chainId: `${chainId}`, | ||
sessionId: this._sessionId | ||
}) | ||
} else { | ||
// notify wallet context each time wallet is opened, to ensure latest | ||
// context is always provided | ||
await this.walletRequestHandler.notifyWalletContext() | ||
// notify networks | ||
await this.walletRequestHandler.notifyNetworks() | ||
// Set default network, in case of error chainId will be undefined or 0 | ||
let chainId: number | undefined = undefined | ||
try { | ||
if (networkId) { | ||
chainId = await this.walletRequestHandler.setDefaultNetwork(networkId, false) | ||
} else { | ||
chainId = await this.walletRequestHandler.getChainId() | ||
} | ||
} catch (err) { | ||
} | ||
// notify account address | ||
this.notifyAccountsChanged([accountAddress]) | ||
// Failed to set default network on open -- quit + close | ||
if (!chainId || chainId <= 0) { | ||
this.notifyOpen({ | ||
sessionId: this._sessionId, | ||
error: `failed to open wallet on network ${networkId}` | ||
}) | ||
return false | ||
} | ||
// notify connect | ||
// NOTE: we don't send 'connect' event to app from here, as it's handled | ||
// by the WalletRequestHandler as it may occur outside of the open() call in | ||
// certain cases (ie. wallet opens which isnt logged in, then signs in after) | ||
// prompt user with a connect request. the options will be used as previously set above. | ||
// upon success, the walletRequestHandler will notify the dapp with the ConnectDetails. | ||
// upon cancellation by user, the walletRequestHandler will throw an error | ||
if (intent && intent.type === 'connect') { | ||
// notify wallet is opened, without session details | ||
this.notifyOpen({ | ||
sessionId: this._sessionId | ||
}) | ||
const connectDetails = await this.walletRequestHandler.promptConnect() | ||
this.walletRequestHandler.notifyConnect(connectDetails) | ||
// auto-close by default, unless intent is to keep open | ||
if (!intent.options || intent.options.keepWalletOpened !== true) { | ||
this.notifyClose() | ||
} | ||
} else { | ||
// 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 | ||
this.notifyOpen({ | ||
sessionId: this._sessionId, | ||
chainId: `${chainId}`, | ||
session: await this.walletRequestHandler.walletSession() | ||
}) | ||
} | ||
} | ||
return true | ||
} | ||
} |
import { | ||
ProviderMessage, ProviderMessageType, ProviderTransport, | ||
ProviderMessageEvent, ProviderMessageRequest, ProviderMessageResponse, WalletSession, OpenWalletIntent | ||
ProviderMessage, EventType, ProviderTransport, | ||
ProviderEventTypes, ProviderMessageRequest, ProviderMessageResponse, WalletSession, OpenWalletIntent, ConnectDetails | ||
} from '../../types' | ||
@@ -53,8 +53,8 @@ | ||
openWallet = (path?: string, intent?: OpenWalletIntent, defaultNetworkId?: string | number): void => { | ||
openWallet = (path?: string, intent?: OpenWalletIntent, networkId?: string | number): void => { | ||
if (this.provider) { | ||
this.provider.openWallet(path, intent, defaultNetworkId) | ||
this.provider.openWallet(path, intent, networkId) | ||
return | ||
} | ||
this.messageProviders.forEach(m => m.openWallet(path, intent, defaultNetworkId)) | ||
this.messageProviders.forEach(m => m.openWallet(path, intent, networkId)) | ||
} | ||
@@ -82,3 +82,3 @@ | ||
on(event: ProviderMessageEvent, fn: (...args: any[]) => void) { | ||
on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]) { | ||
if (this.provider) { | ||
@@ -93,3 +93,3 @@ this.provider.on(event, fn) | ||
once(event: ProviderMessageEvent, fn: (...args: any[]) => void) { | ||
once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]) { | ||
if (this.provider) { | ||
@@ -104,2 +104,12 @@ this.provider.once(event, fn) | ||
emit<K extends keyof ProviderEventTypes>(event: K, ...args: Parameters<ProviderEventTypes[K]>): boolean { | ||
if (this.provider) { | ||
return this.provider.emit(event, ...args) | ||
} | ||
for (let i=0; i < this.messageProviders.length; i++) { | ||
this.messageProviders[i].emit(event, ...args) | ||
} | ||
return true | ||
} | ||
sendAsync = async (request: JsonRpcRequest, callback: JsonRpcResponseCallback, chainId?: number) => { | ||
@@ -140,3 +150,3 @@ if (this.provider) { | ||
waitUntilOpened = async (): Promise<boolean> => { | ||
waitUntilOpened = async (): Promise<WalletSession | undefined> => { | ||
if (this.provider) { | ||
@@ -148,3 +158,3 @@ return this.provider.waitUntilOpened() | ||
waitUntilConnected = async (): Promise<WalletSession> => { | ||
waitUntilConnected = async (): Promise<ConnectDetails> => { | ||
if (this.provider) { | ||
@@ -151,0 +161,0 @@ return this.provider.waitUntilConnected() |
import EventEmitter from 'eventemitter3' | ||
import { ProviderMessage, ProviderMessageTransport, ProviderMessageEvent } from '../../types' | ||
import { ProviderMessage, ProviderMessageTransport, ProviderEventTypes } from '../../types' | ||
@@ -22,3 +22,3 @@ export class ProxyMessageChannel { | ||
conn: ProviderMessageTransport | ||
events: EventEmitter<ProxyMessageEvent, any> = new EventEmitter() | ||
events: EventEmitter<ProxyEventTypes, any> = new EventEmitter() | ||
@@ -36,24 +36,24 @@ // handle messages which hit this port | ||
if (message.type === 'open') { | ||
this.events.emit('open', message) | ||
this.events.emit('open', message as any) | ||
} | ||
if (message.type === 'close') { | ||
this.events.emit('close', message) | ||
this.events.emit('close', message as any) | ||
} | ||
if (message.type === 'connect') { | ||
this.events.emit('connect', message) | ||
this.events.emit('connect', message as any) | ||
} | ||
if (message.type === 'disconnect') { | ||
this.events.emit('disconnect', message) | ||
this.events.emit('disconnect', message as any) | ||
} | ||
} | ||
on(event: ProxyMessageEvent, fn: (...args: any[]) => void) { | ||
this.events.on(event, fn) | ||
on<K extends keyof ProxyEventTypes>(event: K, fn: ProxyEventTypes[K]) { | ||
this.events.on(event, fn as any) | ||
} | ||
once(event: ProxyMessageEvent, fn: (...args: any[]) => void) { | ||
this.events.once(event, fn) | ||
once<K extends keyof ProxyEventTypes>(event: K, fn: ProxyEventTypes[K]) { | ||
this.events.once(event, fn as any) | ||
} | ||
} | ||
type ProxyMessageEvent = 'open' | 'close' | 'connect' | 'disconnect' | ||
export type ProxyEventTypes = Pick<ProviderEventTypes, 'open' | 'close' | 'connect' | 'disconnect'> |
import { BaseProviderTransport } from '../base-provider-transport' | ||
import { | ||
ProviderMessage, OpenState, OpenWalletIntent, ProviderMessageType | ||
ProviderMessage, OpenState, OpenWalletIntent, EventType, InitState | ||
} from '../../types' | ||
import { ProxyMessageChannelPort } from './proxy-message-channel' | ||
import { ProxyMessageChannelPort, ProxyEventTypes } from './proxy-message-channel' | ||
@@ -16,6 +16,10 @@ export class ProxyMessageProvider extends BaseProviderTransport { | ||
this.state = OpenState.CLOSED | ||
this.port = port | ||
this.port = port | ||
if (!port) { | ||
throw new Error('port argument cannot be empty') | ||
} | ||
// disable init handshake for proxy-transport, we set it to OK, to | ||
// consider it in completed state. | ||
this._init = InitState.OK | ||
} | ||
@@ -28,13 +32,13 @@ | ||
this.on('open', (...args: any[]) => { | ||
this.port.events.emit('open', args) | ||
this.on('open', (...args: Parameters<ProxyEventTypes['open']>) => { | ||
this.port.events.emit('open', ...args) | ||
}) | ||
this.on('close', (...args: any[]) => { | ||
this.port.events.emit('close', args) | ||
this.on('close', (...args: Parameters<ProxyEventTypes['close']>) => { | ||
this.port.events.emit('close', ...args) | ||
}) | ||
this.on('connect', (...args: any[]) => { | ||
this.port.events.emit('connect', args) | ||
this.on('connect', (...args: Parameters<ProxyEventTypes['connect']>) => { | ||
this.port.events.emit('connect', ...args) | ||
}) | ||
this.on('disconnect', (...args: any[]) => { | ||
this.port.events.emit('disconnect', args) | ||
this.on('disconnect', (...args: Parameters<ProxyEventTypes['disconnect']>) => { | ||
this.port.events.emit('disconnect', ...args) | ||
}) | ||
@@ -53,8 +57,8 @@ | ||
openWallet = (path?: string, intent?: OpenWalletIntent, defaultNetworkId?: string | number): void => { | ||
openWallet = (path?: string, intent?: OpenWalletIntent, networkId?: string | number): void => { | ||
if (this.state === OpenState.CLOSED) { | ||
this.state = OpenState.OPENING | ||
this.sendMessage({ | ||
idx: -1, type: ProviderMessageType.OPEN, data: { | ||
path, intent, defaultNetworkId | ||
idx: -1, type: EventType.OPEN, data: { | ||
path, intent, networkId | ||
} | ||
@@ -67,3 +71,3 @@ }) | ||
this.sendMessage({ | ||
idx: -1, type: ProviderMessageType.CLOSE, data: null | ||
idx: -1, type: EventType.CLOSE, data: null | ||
}) | ||
@@ -70,0 +74,0 @@ this.close() |
@@ -5,5 +5,6 @@ import EventEmitter from 'eventemitter3' | ||
ProviderMessage, ProviderMessageRequest, ProviderMessageResponse, | ||
WalletMessageEvent, ProviderMessageResponseCallback, | ||
ProviderMessageResponseCallback, | ||
ProviderMessageRequestHandler, | ||
MessageToSign, ProviderRpcError, ProviderConnectInfo, ConnectOptions, ConnectDetails | ||
MessageToSign, ProviderRpcError, ConnectOptions, ConnectDetails, PromptConnectDetails, WalletSession, | ||
ErrSignedInRequired, ProviderEventTypes | ||
} from '../types' | ||
@@ -19,3 +20,13 @@ | ||
import { signAuthorization, AuthorizationOptions } from '@0xsequence/auth' | ||
import { logger, TypedData } from '@0xsequence/utils' | ||
export interface WalletSignInOptions { | ||
connect?: boolean | ||
mainnetNetworks?: Networks | ||
testnetNetworks?: Networks | ||
defaultNetworkId?: string | number | ||
} | ||
export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, ProviderMessageRequestHandler { | ||
@@ -27,6 +38,7 @@ private signer: Signer | null | ||
private _connectOptions?: ConnectOptions | ||
private _defaultNetworkId?: string | number | ||
private _chainId?: number | ||
private events: EventEmitter<WalletMessageEvent, any> = new EventEmitter() | ||
private events: EventEmitter<ProviderEventTypes, any> = new EventEmitter() | ||
@@ -38,11 +50,9 @@ constructor(signer: Signer | null, prompter: WalletUserPrompter | null, mainnetNetworks: Networks, testnetNetworks: Networks = []) { | ||
this.testnetNetworks = testnetNetworks | ||
// if (!signer.provider) { | ||
// throw new Error('wallet.provider is undefined') | ||
// } | ||
} | ||
async connect(signer: Signer | null, mainnetNetworks: Networks = [], testnetNetworks: Networks = []) { | ||
async signIn(signer: Signer | null, options: WalletSignInOptions = {}) { | ||
this.signer = signer | ||
const { connect, mainnetNetworks, testnetNetworks, defaultNetworkId } = options | ||
if (mainnetNetworks && mainnetNetworks.length > 0) { | ||
@@ -54,12 +64,92 @@ this.mainnetNetworks = mainnetNetworks | ||
} | ||
if ((!this.mainnetNetworks || this.mainnetNetworks.length === 0) && (!this.testnetNetworks || this.testnetNetworks.length === 0)) { | ||
throw new Error('signIn failed as network configuration is empty') | ||
} | ||
if (this._defaultNetworkId) { | ||
if (!(await this.setDefaultNetwork(this._defaultNetworkId, false))) { | ||
throw new Error(`WalletRequestHandler setup unable to set defaultNetworkId ${this._defaultNetworkId}`) | ||
const networkId = defaultNetworkId || this._defaultNetworkId | ||
if (networkId) { | ||
if (!(await this.setDefaultNetwork(networkId, false))) { | ||
throw new Error(`WalletRequestHandler setup unable to set defaultNetworkId ${networkId}`) | ||
} | ||
} | ||
this.notifyConnect(await this.signer!.getAddress(), await this.getChainId()) | ||
// Optionally, connect the dapp and wallet. In case connectOptions are provided, we will perform | ||
// necessary auth request, and then notify the dapp of the 'connect' details. | ||
// | ||
// NOTE: if a user is signing into a dapp from a fresh state, and and auth request is made | ||
// we don't trigger the promptConnect flow, as we consider the user just authenticated | ||
// 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 | ||
if (connect) { | ||
const connectOptions = this._connectOptions | ||
const connectDetails = await this.connect(connectOptions) | ||
this.notifyConnect(connectDetails) | ||
if (!connectOptions || connectOptions.keepWalletOpened !== true) { | ||
this.notifyClose() | ||
} | ||
} | ||
} | ||
async connect(options?: ConnectOptions): Promise<ConnectDetails> { | ||
if (!this.signer) { | ||
return { | ||
connected: false, chainId: '0x0', error: 'unable to connect without signed in account', | ||
} | ||
} | ||
const connectDetails: ConnectDetails = { | ||
connected: true, | ||
chainId: ethers.utils.hexlify(await this.getChainId()) | ||
} | ||
if (options && options.authorize) { | ||
// Perform ethauth eip712 request and construct the ConnectDetails response | ||
// including the auth proof | ||
const authOptions: AuthorizationOptions = { | ||
app: options.app, origin: options.origin, expiry: options.expiry | ||
} | ||
// if (typeof(options.authorize) === 'object') { | ||
// authOptions = { ...authOptions, ...options.authorize } | ||
// } | ||
try { | ||
connectDetails.proof = await signAuthorization(this.signer, authOptions) | ||
} catch (err) { | ||
logger.warn(`connect, signAuthorization failed for options: ${options}, due to: ${err.message}`) | ||
return { | ||
connected: false, chainId: '0x0', error: 'signAuthorization failed' | ||
} | ||
} | ||
} | ||
// Build session response for connect details | ||
connectDetails.session = await this.walletSession() | ||
return connectDetails | ||
} | ||
promptConnect = async (options?: ConnectOptions): Promise<ConnectDetails> => { | ||
if (!options && !this._connectOptions) { | ||
// this is an unexpected state and should not happen | ||
throw new Error('prompter connect options are empty') | ||
} | ||
if (!this.prompter) { | ||
// if prompter is null, we'll auto connect | ||
return this.connect(options) | ||
} | ||
const promptConnectDetails = await this.prompter.promptConnect(options || this._connectOptions) | ||
const connectDetails: ConnectDetails = promptConnectDetails | ||
if (!connectDetails.session) { | ||
connectDetails.session = await this.walletSession() | ||
} | ||
return promptConnectDetails | ||
} | ||
// sendMessageRequest will unwrap the ProviderMessageRequest and send it to the JsonRpcHandler | ||
@@ -98,3 +188,4 @@ // (aka, the signer in this instance) and then responds with a wrapped response of | ||
if ((!this.signer || this.signer === null) && !permittedJsonRpcMethods.includes(request.method)) { | ||
throw new Error(`not logged in. ${request.method} is unavailable`) | ||
// throw new Error(`not logged in. ${request.method} is unavailable`) | ||
throw ErrSignedInRequired | ||
} | ||
@@ -143,3 +234,3 @@ | ||
// TODO: | ||
// if (process.env.DEBUG_MODE === 'true' && this.prompter === null) { | ||
// if (process.env.TEST_MODE === 'true' && this.prompter === null) { | ||
if (this.prompter === null) { | ||
@@ -428,8 +519,8 @@ // prompter is null, so we'll sign from here | ||
on = (event: WalletMessageEvent, fn: (...args: any[]) => void) => { | ||
this.events.on(event, fn) | ||
on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]) { | ||
this.events.on(event, fn as any) | ||
} | ||
once = (event: WalletMessageEvent, fn: (...args: any[]) => void) => { | ||
this.events.once(event, fn) | ||
once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]) { | ||
this.events.once(event, fn as any) | ||
} | ||
@@ -455,2 +546,14 @@ | ||
get connectOptions(): ConnectOptions | undefined { | ||
return this._connectOptions | ||
} | ||
setConnectOptions(options: ConnectOptions | undefined) { | ||
this._connectOptions = options | ||
} | ||
get defaultNetworkId(): string | number | undefined { | ||
return this._defaultNetworkId | ||
} | ||
async setDefaultNetwork(chainId: string | number, notifyNetworks: boolean = true): Promise<number | undefined> { | ||
@@ -473,6 +576,2 @@ if (!chainId) return undefined | ||
get defaultNetworkId(): string | number | undefined { | ||
return this._defaultNetworkId | ||
} | ||
async getNetworks(jsonRpcResponse?: boolean): Promise<NetworkConfig[]> { | ||
@@ -499,14 +598,15 @@ if (!this.signer) { | ||
notifyConnect(accountAddress: string, chainId: number) { | ||
if (!accountAddress || accountAddress.length === 0) { | ||
this.events.emit('accountsChanged', []) | ||
} else { | ||
this.events.emit('accountsChanged', [accountAddress]) | ||
async walletSession(): Promise<WalletSession | undefined> { | ||
return !this.signer ? undefined : { | ||
walletContext: await this.signer.getWalletContext(), | ||
accountAddress: await this.signer.getAddress(), | ||
networks: await this.getNetworks(true) | ||
} | ||
this.notifyNetworks() | ||
this.notifyWalletContext() | ||
} | ||
this.events.emit('connect', { | ||
chainId: `${chainId}` | ||
} as ProviderConnectInfo) | ||
notifyConnect(connectDetails: ConnectDetails) { | ||
this.events.emit('connect', connectDetails) | ||
if (connectDetails.session?.accountAddress) { | ||
this.events.emit('accountsChanged', [connectDetails.session?.accountAddress]) | ||
} | ||
} | ||
@@ -517,3 +617,3 @@ | ||
this.events.emit('networks', []) | ||
this.events.emit('disconnect', { code: 4900 } as ProviderRpcError) | ||
this.events.emit('disconnect') | ||
} | ||
@@ -540,2 +640,10 @@ | ||
notifyClose(error?: ProviderRpcError) { | ||
this.events.emit('close', error) | ||
} | ||
isSignedIn(): boolean { | ||
return !!this.signer | ||
} | ||
getSigner(): Signer | null { | ||
@@ -551,3 +659,3 @@ return this.signer | ||
export interface WalletUserPrompter { | ||
// promptConnect(options?: ConnectOptions): Promise<ConnectDetails> | ||
promptConnect(options?: ConnectOptions): Promise<PromptConnectDetails> | ||
promptSignMessage(message: MessageToSign): Promise<string> | ||
@@ -554,0 +662,0 @@ promptSignTransaction(txn: TransactionRequest, chaindId?: number): Promise<string> |
@@ -1,19 +0,19 @@ | ||
import { ProviderMessageRequest, ProviderMessage, ProviderMessageType, ProviderMessageResponse, InitState } from '../../types' | ||
import { | ||
ProviderMessageRequest, | ||
ProviderMessage, | ||
EventType, | ||
ProviderMessageResponse, | ||
InitState, | ||
ConnectDetails, | ||
OpenWalletIntent | ||
} from '../../types' | ||
import { WalletRequestHandler } from '../wallet-request-handler' | ||
import { BaseWalletTransport } from '../base-wallet-transport' | ||
import { logger, sanitizeNumberString } from '@0xsequence/utils' | ||
import { logger, sanitizeNumberString, base64DecodeObject } from '@0xsequence/utils' | ||
export interface RegisterOptions { | ||
loadingPath: string | ||
} | ||
export class WindowMessageHandler extends BaseWalletTransport { | ||
protected parentWindow: Window | ||
protected parentOrigin?: string | ||
private _isPopup: boolean = false | ||
private _initNonce: string | ||
private _postMessageQueue: Array<any> = [] | ||
constructor(walletRequestHandler: WalletRequestHandler) { | ||
@@ -24,3 +24,3 @@ super(walletRequestHandler) | ||
register(options?: RegisterOptions) { | ||
register() { | ||
const isPopup = parent.window.opener !== null | ||
@@ -34,5 +34,5 @@ this._isPopup = isPopup | ||
const location = new URL(window.location.href) | ||
this._sessionId = sanitizeNumberString(location.searchParams.get('sid')!) | ||
location.searchParams.delete('sid') | ||
const params = new URLSearchParams(location.search) | ||
this._sessionId = sanitizeNumberString(params.get('sid')!) | ||
if (this._sessionId.length === 0) { | ||
@@ -43,12 +43,10 @@ logger.error('invalid sessionId') | ||
const defaultNetwork = location.searchParams.get('net')! | ||
location.searchParams.delete('net') | ||
const intent = base64DecodeObject<OpenWalletIntent>(params.get('intent')!) | ||
const networkId = params.get('net')! | ||
const jsonRpcRequest = location.searchParams.get('jsonRpcRequest') | ||
if (options?.loadingPath && !!jsonRpcRequest) { | ||
window.history.replaceState({}, document.title, options.loadingPath) | ||
} else { | ||
window.history.replaceState({}, document.title, location.pathname) | ||
} | ||
// TODO: review how we should be intefacing with window.history, so we can route | ||
// to the correct destination based on 'intent' ie. 'connect' or 'jsonRpcRequest' | ||
// ie.. maybe.. | ||
// window.history.replaceState(params['jsonRpcRequest'] ? { jsonRpcRequest: true } : {}, document.title, location.pathname) | ||
@@ -63,11 +61,19 @@ // record parent window instance for communication | ||
// send open event to the app which opened us | ||
this.open(defaultNetwork).then(opened => { | ||
if (!opened) { | ||
logger.error(`failed to open to network ${defaultNetwork}`) | ||
this.open(intent, networkId) | ||
.then(opened => { | ||
if (!opened) { | ||
const err = `failed to open to network ${networkId}` | ||
logger.error(err) | ||
// TODO? | ||
// this.notifyOpen({ error: err }) // or notifyClose({ message: err }) | ||
window.close() | ||
} | ||
}) | ||
.catch(e => { | ||
const err = `failed to open to network ${networkId}, due to: ${e}` | ||
logger.error(err) | ||
// TODO? | ||
// this.notifyOpen({ error: err }) // or notifyClose({ message: err }) | ||
window.close() | ||
} | ||
}).catch(err => { | ||
logger.error(`failed to open to network ${defaultNetwork}, due to: ${err}`) | ||
window.close() | ||
}) | ||
}) | ||
} | ||
@@ -87,11 +93,6 @@ | ||
} | ||
if (this.parentOrigin && event.origin !== this.parentOrigin) { | ||
if (this.appOrigin && event.origin !== this.appOrigin) { | ||
// skip message as not from expected app origin | ||
return | ||
} | ||
if (this._init === InitState.OK && (!this.parentOrigin || this.parentOrigin.length < 8)) { | ||
// impossible state | ||
logger.error('impossible state, init.OK and parentOrigin required') | ||
return | ||
} | ||
@@ -109,20 +110,9 @@ // Wallet always expects json-rpc request messages from a dapp | ||
// Record the parent origin url on init | ||
if (this._init !== InitState.OK) { | ||
if (request.type === ProviderMessageType.INIT) { | ||
const { sessionId, nonce } = request.data as any as { sessionId: string, nonce: string } | ||
if (!sessionId || sessionId.length === 0 || !nonce || nonce.length === 0) { | ||
logger.error('invalid init response') | ||
return | ||
} | ||
if (sessionId !== this._sessionId || nonce !== this._initNonce) { | ||
logger.error('invalid init match') | ||
return | ||
} | ||
this._init = InitState.OK | ||
this.parentOrigin = event.origin | ||
this.flushPostMessageQueue() | ||
} else { | ||
// we expect init message first | ||
} | ||
// Record event origin for valid init ack | ||
if (this._init !== InitState.OK && this.isValidInitAck(request)) { | ||
this.appOrigin = event.origin | ||
} | ||
if (this._init === InitState.OK && (!this.appOrigin || this.appOrigin.length < 8)) { | ||
// impossible state | ||
logger.error('impossible state, init.OK and appOrigin required') | ||
return | ||
@@ -137,29 +127,12 @@ } | ||
sendMessage(message: ProviderMessage<any>) { | ||
if (message.type === ProviderMessageType.INIT) { | ||
// clients should not send init requests directly | ||
return | ||
} | ||
// prepare payload | ||
const payload = JSON.stringify(message) | ||
// queue sending messages until we're inited | ||
if (this._init !== InitState.OK) { | ||
this._postMessageQueue.push(payload) | ||
// post-message to app. | ||
// only for init requests, we send to '*' origin | ||
if (message.type === EventType.INIT) { | ||
this.postMessage(payload, true) | ||
} else { | ||
this.postMessage(payload) | ||
} | ||
// init stage + check | ||
if (this._init === InitState.NIL) { | ||
this._initNonce = `${performance.now()}` | ||
this.parentWindow.postMessage(JSON.stringify({ | ||
idx: -1, type: ProviderMessageType.INIT, data: { nonce: this._initNonce } | ||
} as ProviderMessage<any>), '*') | ||
this._init = InitState.SENT_NONCE | ||
return | ||
} else if (this._init !== InitState.OK) { | ||
return | ||
} | ||
// post-message to app | ||
this.postMessage(payload) | ||
} | ||
@@ -171,23 +144,22 @@ | ||
private flushPostMessageQueue() { | ||
if (this._postMessageQueue.length === 0) return | ||
// logger.debug(`flushPostMessageQueue # of messages, ${this._postMessageQueue.length}`) | ||
for (let i=0; i < this._postMessageQueue.length; i++) { | ||
this.postMessage(this._postMessageQueue[i]) | ||
} | ||
this._postMessageQueue.length = 0 | ||
} | ||
private postMessage(message: any) { | ||
if (this._init !== InitState.OK) { | ||
private postMessage(message: any, init = false) { | ||
if (init !== true && this._init !== InitState.OK) { | ||
logger.error('impossible state, should not be calling postMessage until inited') | ||
return | ||
} | ||
if (this.parentOrigin && this.parentOrigin.length > 8) { | ||
this.parentWindow.postMessage(message, this.parentOrigin) | ||
if (init) { | ||
// init message transmission to global target -- for 'init' payloads only | ||
this.parentWindow.postMessage(message, '*') | ||
} else { | ||
logger.error('unable to postMessage as parentOrigin is invalid') | ||
// open message transmission | ||
if (this.appOrigin && this.appOrigin.length > 4) { // just above '.com' | ||
this.parentWindow.postMessage(message, this.appOrigin) | ||
} else { | ||
logger.error('unable to postMessage as parentOrigin is invalid') | ||
} | ||
} | ||
} | ||
} |
@@ -1,4 +0,4 @@ | ||
import { OpenWalletIntent, ProviderMessage, InitState, ProviderMessageType } from '../../types' | ||
import { OpenWalletIntent, ProviderMessage, InitState, EventType } from '../../types' | ||
import { BaseProviderTransport } from '../base-provider-transport' | ||
import { logger } from '@0xsequence/utils' | ||
import { logger, base64EncodeObject } from '@0xsequence/utils' | ||
@@ -11,3 +11,2 @@ // .. | ||
private walletWindow: Window | null | ||
private _init: InitState | ||
@@ -37,3 +36,3 @@ constructor(walletAppURL: string) { | ||
} | ||
}, 1250) | ||
}, 500) | ||
}) | ||
@@ -66,3 +65,3 @@ | ||
openWallet = (path?: string, intent?: OpenWalletIntent, defaultNetworkId?: string | number): void => { | ||
openWallet = (path?: string, intent?: OpenWalletIntent, networkId?: string | number): void => { | ||
if (this.walletWindow && this.isOpened()) { | ||
@@ -74,11 +73,2 @@ // TODO: update the location of window to path | ||
// Set session and network id on class instance walletURL | ||
this._init = InitState.NIL | ||
this._sessionId = `${performance.now()}` | ||
this.walletURL.searchParams.set('sid', this._sessionId) | ||
if (defaultNetworkId) { | ||
this.walletURL.searchParams.set('net', `${defaultNetworkId}`) | ||
} | ||
// Instantiate new walletURL for this call | ||
@@ -90,6 +80,20 @@ const walletURL = new URL(this.walletURL.href) | ||
// set intent of wallet opening due to jsonRpcRequest send by provider | ||
if (intent?.type === 'jsonRpcRequest') { | ||
walletURL.searchParams.set('jsonRpcRequest', intent.method) | ||
// Set session, intent and network id on walletURL | ||
this._init = InitState.NIL | ||
this._sessionId = `${performance.now()}` | ||
walletURL.searchParams.set('sid', this._sessionId) | ||
if (intent) { | ||
// for the window-transport, we eagerly/optimistically set the origin host | ||
// when connecting to the wallet, however, this will be verified and enforced | ||
// on the wallet-side, so if a dapp provides the wrong origin, it will be dropped. | ||
if (intent.type === 'connect') { | ||
if (!intent.options) intent.options = {} | ||
intent.options.origin = window.location.origin | ||
} | ||
// encode intent as base6 url-encoded param | ||
walletURL.searchParams.set('intent', base64EncodeObject(intent)) | ||
} | ||
if (networkId) { | ||
walletURL.searchParams.set('net', `${networkId}`) | ||
} | ||
@@ -161,23 +165,2 @@ // Open popup window on center of the app window | ||
// window init | ||
if (this._init !== InitState.OK) { | ||
if (message.type === ProviderMessageType.INIT) { | ||
const { nonce } = message.data as { nonce: string } | ||
if (!nonce || nonce.length === 0) { | ||
logger.error('invalid init nonce') | ||
return | ||
} | ||
this._init = InitState.OK | ||
this.sendMessage({ | ||
idx: -1, | ||
type: ProviderMessageType.INIT, | ||
data: { | ||
sessionId: this._sessionId, | ||
nonce: nonce | ||
} | ||
}, true) | ||
} | ||
return | ||
} | ||
// handle message with base message provider | ||
@@ -187,6 +170,3 @@ this.handleMessage(message) | ||
sendMessage(message: ProviderMessage<any>, skipIdx = false) { | ||
if (!skipIdx && (!message.idx || message.idx <= 0)) { | ||
throw new Error('message idx is empty') | ||
} | ||
sendMessage(message: ProviderMessage<any>) { | ||
if (!this.walletWindow) { | ||
@@ -193,0 +173,0 @@ logger.warn('WindowMessageProvider: sendMessage failed as walletWindow is unavailable') |
155
src/types.ts
import { NetworkConfig, WalletContext, JsonRpcRequest, JsonRpcResponse, JsonRpcHandler } from '@0xsequence/network' | ||
import { TypedData } from '@0xsequence/utils' | ||
// export class SequenceError extends Error {} | ||
export interface WalletSession { | ||
// Wallet context | ||
walletContext?: WalletContext | ||
// Account address of the wallet | ||
accountAddress?: string | ||
// Networks in use for the session. The default/dapp network will show | ||
// up as the first one in the list as the "main chain" | ||
networks?: NetworkConfig[] | ||
// Caching provider responses for things such as account and chainId | ||
providerCache?: {[key: string]: any} | ||
} | ||
export interface ProviderTransport extends JsonRpcHandler, ProviderMessageTransport, ProviderMessageRequestHandler { | ||
@@ -25,3 +8,3 @@ register(): void | ||
openWallet(path?: string, intent?: OpenWalletIntent, defaultNetworkId?: string | number): void | ||
openWallet(path?: string, intent?: OpenWalletIntent, networkId?: string | number): void | ||
closeWallet(): void | ||
@@ -32,7 +15,8 @@ | ||
on(event: ProviderMessageEvent, fn: (...args: any[]) => void): void | ||
once(event: ProviderMessageEvent, fn: (...args: any[]) => void): void | ||
on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void | ||
once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void | ||
emit<K extends keyof ProviderEventTypes>(event: K, ...args: Parameters<ProviderEventTypes[K]>): boolean | ||
waitUntilOpened(): Promise<boolean> | ||
waitUntilConnected(): Promise<WalletSession> | ||
waitUntilOpened(): Promise<WalletSession | undefined> | ||
waitUntilConnected(): Promise<ConnectDetails> | ||
} | ||
@@ -44,8 +28,8 @@ | ||
notifyOpen(openInfo: { chainId?: string, sessionId?: string }): void | ||
notifyClose(): void | ||
notifyOpen(openInfo: { chainId?: string, sessionId?: string, session?: WalletSession, error?: string }): void | ||
notifyClose(error?: ProviderRpcError): void | ||
notifyConnect(connectInfo: { chainId?: string }): void | ||
notifyConnect(connectDetails: ConnectDetails): void | ||
notifyAccountsChanged(accounts: string[]): void | ||
notifyChainChanged(connectInfo: any): void | ||
notifyChainChanged(chainIdHex: string): void | ||
notifyNetworks(networks: NetworkConfig[]): void | ||
@@ -55,3 +39,3 @@ } | ||
export interface ProviderMessage<T> { | ||
idx: number // message id sequence number | ||
idx: number // message id number | ||
type: string // message type | ||
@@ -71,6 +55,2 @@ data: T // the ethereum json-rpc payload | ||
export interface ProviderConnectInfo { | ||
chainId: string | ||
} | ||
export interface ProviderRpcError extends Error { | ||
@@ -96,7 +76,3 @@ message: string | ||
export type WalletMessageEvent = 'open' | 'close' | 'connect' | 'disconnect' | 'chainChanged' | 'accountsChanged' | 'networks' | 'walletContext' | 'init' | '_debug' | ||
export type ProviderMessageEvent = 'message' | WalletMessageEvent | ||
export enum ProviderMessageType { | ||
export enum EventType { | ||
OPEN = 'open', | ||
@@ -108,4 +84,4 @@ CLOSE = 'close', | ||
DISCONNECT = 'disconnect', | ||
ACCOUNTS_CHANGED = 'accountsChanged', | ||
CHAIN_CHANGED = 'chainChanged', | ||
ACCOUNTS_CHANGED = 'accountsChanged', | ||
@@ -119,2 +95,20 @@ NETWORKS = 'networks', | ||
export interface WalletEventTypes { | ||
'open': (openInfo: { chainId?: string, sessionId?: string, session?: WalletSession, error?: string }) => void | ||
'close': (error?: ProviderRpcError) => void | ||
'connect': (connectDetails: ConnectDetails) => void | ||
'disconnect': (error?: ProviderRpcError) => void | ||
'accountsChanged': (accounts: string[]) => void | ||
'chainChanged': (chainIdHex: string) => void | ||
'networks': (networks: NetworkConfig[]) => void | ||
'walletContext': (walletContext: WalletContext) => void | ||
} | ||
export interface ProviderEventTypes extends WalletEventTypes { | ||
'message': (message: ProviderMessageResponse) => void | ||
} | ||
export enum OpenState { | ||
@@ -132,19 +126,54 @@ CLOSED = 0, | ||
export type NetworkEventPayload = NetworkConfig | ||
export interface ConnectOptions { | ||
// networkId specifics the default network a dapp would like to connect to. This field | ||
// is optional as it can be provided a number of different ways. | ||
networkId?: string | number | ||
export interface ConnectOptions { | ||
// app name of the dapp which will be announced to user on connect screen | ||
app?: string | ||
// origin hint of the dapp's host opening the wallet. This value will automatically | ||
// be determined and verified for integrity, and can be omitted. | ||
origin?: string | ||
// expiry number (in seconds) to expire connect session. default is 1 week of seconds. | ||
expiry?: number | ||
// authorize will perform an ETHAuth eip712 signing and return the proof to the dapp. | ||
authorize?: boolean | ||
// askForEmail will prompt to give permission to the dapp to access email address | ||
// TODO: this feature is currently not used as the wallet does not report emails yet | ||
askForEmail?: boolean | ||
// refresh flag will force a full re-connect (ie. disconnect then connect again) | ||
refresh?: boolean | ||
requestAuthorization?: boolean | ||
requestEmail?: boolean | ||
// keepWalletOpened will keep the wallet window opened after connecting. The default | ||
// is to automatically close the wallet after connecting. | ||
keepWalletOpened?: boolean | ||
} | ||
export interface ConnectDetails { | ||
success: boolean | ||
proof?: { | ||
type?: string | ||
sig: string | ||
} | ||
// chainId (in hex) and error are defined by EIP-1193 expected fields | ||
chainId?: string | ||
error?: string | ||
// connected flag denotes user-accepted the connect request | ||
connected: boolean | ||
// session include account and network information needed by the dapp wallet provider. | ||
session?: WalletSession | ||
// proof is a signed typedData (EIP-712) payload using ETHAuth domain. | ||
// NOTE: the proof is signed to the `authChainId`, as the canonical auth chain. | ||
proof?: ETHAuthProof | ||
// email address provided from wallet to the dapp, as request + accepted | ||
// by a user during a connect request | ||
email?: string | ||
} | ||
export type PromptConnectDetails = Pick<ConnectDetails, 'connected' | 'proof' | 'email'> | ||
export type OpenWalletIntent = | ||
@@ -159,1 +188,35 @@ { type: 'connect'; options?: ConnectOptions } | | ||
} | ||
export interface ETHAuthProof { | ||
// eip712 typed-data payload for ETHAuth domain as input | ||
typedData: TypedData | ||
// signature encoded in an ETHAuth proof string | ||
proofString: string | ||
} | ||
export interface WalletSession { | ||
// Wallet context | ||
walletContext?: WalletContext | ||
// Account address of the wallet | ||
accountAddress?: string | ||
// Networks in use for the session. The default/dapp network will show | ||
// up as the first one in the list as the "main chain" | ||
networks?: NetworkConfig[] | ||
// Caching provider responses for things such as account and chainId | ||
providerCache?: {[key: string]: any} | ||
} | ||
export class ProviderError extends Error { | ||
constructor(message?: string) { | ||
super(message) | ||
this.name = 'ProviderError' | ||
} | ||
} | ||
export const ErrSignedInRequired = new ProviderError('Wallet is not signed in. Connect a wallet and try again.') | ||
// TODO: lets build some nice error handling tools, prob in /utils ... |
@@ -11,3 +11,3 @@ import { Networks, NetworkConfig, WalletContext, sequenceContext, ChainId, getNetworkId, JsonRpcSender, | ||
import { MuxMessageProvider, WindowMessageProvider, ProxyMessageProvider, ProxyMessageChannelPort } from './transports' | ||
import { WalletSession, ProviderMessageEvent, ConnectOptions, OpenWalletIntent, ConnectDetails } from './types' | ||
import { WalletSession, ProviderEventTypes, ConnectOptions, OpenWalletIntent, ConnectDetails } from './types' | ||
import { WalletCommands } from './commands' | ||
@@ -17,5 +17,4 @@ import { ethers } from 'ethers' | ||
export interface WalletProvider { | ||
// connect(options?: ConnectOptions): Promise<ConnectDetails> | ||
connect(options?: ConnectOptions): Promise<ConnectDetails> | ||
// authorize(options?: ConnectOptions): Promise<ConnectDetails> | ||
connect(options?: ConnectOptions): Promise<boolean> | ||
disconnect(): void | ||
@@ -32,3 +31,3 @@ | ||
openWallet(path?: string, intent?: OpenWalletIntent): Promise<boolean> | ||
openWallet(path?: string, intent?: OpenWalletIntent, networkId?: string | number): Promise<boolean> | ||
closeWallet(): void | ||
@@ -46,4 +45,4 @@ | ||
on(event: ProviderMessageEvent, fn: (...args: any[]) => void): void | ||
once(event: ProviderMessageEvent, fn: (...args: any[]) => void): void | ||
on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void | ||
once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]): void | ||
@@ -160,2 +159,20 @@ commands: WalletCommands | ||
// NOTE: we don't listen on 'connect' even here as we handle it within connect() method | ||
// in more synchronous flow. | ||
// below will update the wallet session object and persist it. In case the session | ||
// is undefined, we consider the session to have been removed by the user, so we clear it. | ||
this.transport.messageProvider.on('open', (openInfo: { session?: WalletSession }) => { | ||
const { session } = openInfo | ||
if (!session) { | ||
if (this.session && this.session.accountAddress) { | ||
// emit disconnect even if previously we had a session, and now we don't. | ||
this.transport.messageProvider!.emit('disconnect') | ||
} | ||
this.clearSession() | ||
} else { | ||
this.useSession(session, true) | ||
} | ||
}) | ||
// below will update the account upon wallet connect/disconnect (aka, login/logout) | ||
@@ -187,32 +204,32 @@ this.transport.messageProvider.on('accountsChanged', (accounts: string[]) => { | ||
connect = async (options?: ConnectOptions): Promise<boolean> => { | ||
connect = async (options?: ConnectOptions): Promise<ConnectDetails> => { | ||
if (options?.refresh === true) { | ||
this.disconnect() | ||
} | ||
if (this.isConnected() && !options?.requestAuthorization && !options?.requestEmail) { | ||
return this.isConnected() | ||
// return { | ||
// success: this.isConnected() | ||
// } | ||
if (this.isConnected() && !!this.session && !options?.authorize && !options?.askForEmail) { | ||
return { | ||
connected: true, | ||
session: this.session, | ||
chainId: ethers.utils.hexlify(await this.getChainId()) | ||
} | ||
} | ||
await this.openWallet(undefined, { type: 'connect', options }) | ||
const sessionPayload = await this.transport.messageProvider!.waitUntilConnected() | ||
this.useSession(sessionPayload, true) | ||
const connectDetails = await this.transport.messageProvider!.waitUntilConnected() | ||
return this.isConnected() | ||
if (connectDetails.connected) { | ||
if (!!connectDetails.session) { | ||
this.useSession(connectDetails.session, true) | ||
} else { | ||
throw new Error('impossible state, connect response is missing session') | ||
} | ||
} | ||
// TODO: the wallet-webapp itself will handle the open request.. | ||
// prob with window, etc.. or other proxy-message | ||
return connectDetails | ||
} | ||
// return { | ||
// success: this.isConnected() | ||
// // TODO: .. | ||
// } | ||
authorize = async (options?: ConnectOptions): Promise<ConnectDetails> => { | ||
return this.connect({ ...options, authorize: true }) | ||
} | ||
// authorize = async (options?: ConnectOptions): Promise<ConnectDetails> => { | ||
// return this.connect({ ...options, requestAuthorization: true }) | ||
// } | ||
disconnect(): void { | ||
@@ -298,3 +315,3 @@ if (this.isOpened()) { | ||
openWallet = async (path?: string, intent?: OpenWalletIntent): Promise<boolean> => { | ||
openWallet = async (path?: string, intent?: OpenWalletIntent, networkId?: string | number): Promise<boolean> => { | ||
if (intent?.type !== 'connect' && !this.isConnected()) { | ||
@@ -304,3 +321,3 @@ throw new Error('connect first') | ||
this.transport.messageProvider!.openWallet(path, intent, this.config.defaultNetworkId) | ||
this.transport.messageProvider!.openWallet(path, intent, networkId || this.config.defaultNetworkId) | ||
await this.transport.messageProvider!.waitUntilOpened() | ||
@@ -411,7 +428,7 @@ | ||
on(event: ProviderMessageEvent, fn: (...args: any[]) => void) { | ||
on<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]) { | ||
this.transport.messageProvider!.on(event, fn) | ||
} | ||
once(event: ProviderMessageEvent, fn: (...args: any[]) => void) { | ||
once<K extends keyof ProviderEventTypes>(event: K, fn: ProviderEventTypes[K]) { | ||
this.transport.messageProvider!.once(event, fn) | ||
@@ -435,2 +452,3 @@ } | ||
private saveSession = (session: WalletSession) => { | ||
logger.debug('wallet provider: saving session') | ||
const data = JSON.stringify(session) | ||
@@ -483,2 +501,3 @@ window.localStorage.setItem('@sequence.session', data) | ||
if (!checkNetworkConfig(networks[0], this.config.defaultNetworkId)) { | ||
// TODO: what is the correct behaviour here we want for dapps? | ||
throw new Error(`expecting defaultNetworkId '${this.config.defaultNetworkId}' but is set to '${networks[0].name}'`) | ||
@@ -520,2 +539,3 @@ } | ||
private clearSession(): void { | ||
logger.debug('wallet provider: clearing session') | ||
window.localStorage.removeItem('@sequence.session') | ||
@@ -533,2 +553,5 @@ this.session = undefined | ||
// Sequence Wallet Session URL, default: https://session.sequence.app | ||
// walletSessionURL: string | ||
// networks is a configuration list of networks used by the wallet. This list | ||
@@ -568,3 +591,2 @@ // is combined with the network list supplied from the wallet upon login, | ||
walletContext?: WalletContext | ||
} | ||
@@ -575,2 +597,4 @@ | ||
// walletSessionURL: 'https://session.sequence.app', | ||
transports: { | ||
@@ -580,2 +604,2 @@ windowTransport: { enabled: true }, | ||
} | ||
} | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
459873
11129
+ Added@0xsequence/abi@0.19.3(transitive)
+ Added@0xsequence/api@0.19.3(transitive)
+ Added@0xsequence/auth@0.19.3(transitive)
+ Added@0xsequence/chaind@0.19.3(transitive)
+ Added@0xsequence/config@0.19.3(transitive)
+ Added@0xsequence/guard@0.19.3(transitive)
+ Added@0xsequence/network@0.19.3(transitive)
+ Added@0xsequence/relayer@0.19.3(transitive)
+ Added@0xsequence/transactions@0.19.3(transitive)
+ Added@0xsequence/utils@0.19.3(transitive)
+ Added@0xsequence/wallet@0.19.3(transitive)
- Removed@0xsequence/abi@0.18.0(transitive)
- Removed@0xsequence/api@0.18.0(transitive)
- Removed@0xsequence/auth@0.18.0(transitive)
- Removed@0xsequence/chaind@0.18.0(transitive)
- Removed@0xsequence/config@0.18.0(transitive)
- Removed@0xsequence/guard@0.18.0(transitive)
- Removed@0xsequence/network@0.18.0(transitive)
- Removed@0xsequence/relayer@0.18.0(transitive)
- Removed@0xsequence/transactions@0.18.0(transitive)
- Removed@0xsequence/utils@0.18.0(transitive)
- Removed@0xsequence/wallet@0.18.0(transitive)
- Removedjwt-decode@3.1.2(transitive)
Updated@0xsequence/abi@^0.19.0
Updated@0xsequence/auth@^0.19.0
Updated@0xsequence/config@^0.19.0
Updated@0xsequence/network@^0.19.0
Updated@0xsequence/utils@^0.19.0
Updated@0xsequence/wallet@^0.19.0