@0xsequence/provider 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/0xsequence-provider.cjs.d.ts +1 -0
- package/dist/0xsequence-provider.cjs.d.ts.map +1 -0
- package/dist/0xsequence-provider.cjs.dev.js +1742 -1678
- package/dist/0xsequence-provider.cjs.prod.js +1742 -1678
- package/dist/0xsequence-provider.esm.js +1735 -1676
- package/dist/declarations/src/client.d.ts +74 -0
- package/dist/declarations/src/index.d.ts +7 -5
- package/dist/declarations/src/init.d.ts +25 -0
- package/dist/declarations/src/provider.d.ts +127 -63
- package/dist/declarations/src/signer.d.ts +87 -0
- package/dist/declarations/src/transports/base-provider-transport.d.ts +1 -1
- package/dist/declarations/src/transports/base-wallet-transport.d.ts +2 -2
- package/dist/declarations/src/transports/extension-transport/base-injected-transport.d.ts +1 -1
- package/dist/declarations/src/transports/extension-transport/extension-message-handler.d.ts +3 -3
- package/dist/declarations/src/transports/extension-transport/extension-message-provider.d.ts +2 -2
- package/dist/declarations/src/transports/extension-transport/index.d.ts +3 -3
- package/dist/declarations/src/transports/index.d.ts +8 -8
- package/dist/declarations/src/transports/mux-transport/index.d.ts +1 -1
- package/dist/declarations/src/transports/mux-transport/mux-message-provider.d.ts +22 -1
- package/dist/declarations/src/transports/proxy-transport/index.d.ts +3 -3
- package/dist/declarations/src/transports/proxy-transport/proxy-message-channel.d.ts +1 -1
- package/dist/declarations/src/transports/proxy-transport/proxy-message-handler.d.ts +4 -4
- package/dist/declarations/src/transports/proxy-transport/proxy-message-provider.d.ts +3 -3
- package/dist/declarations/src/transports/unreal-transport/index.d.ts +2 -2
- package/dist/declarations/src/transports/unreal-transport/unreal-message-handler.d.ts +3 -3
- package/dist/declarations/src/transports/unreal-transport/unreal-message-provider.d.ts +2 -2
- package/dist/declarations/src/transports/wallet-request-handler.d.ts +1 -2
- package/dist/declarations/src/transports/window-transport/index.d.ts +2 -2
- package/dist/declarations/src/transports/window-transport/window-message-handler.d.ts +3 -3
- package/dist/declarations/src/transports/window-transport/window-message-provider.d.ts +2 -2
- package/dist/declarations/src/types.d.ts +25 -9
- package/dist/declarations/src/utils/index.d.ts +10 -7
- package/dist/declarations/src/utils.d.ts +21 -15
- package/package.json +19 -12
- package/src/client.ts +423 -0
- package/src/extended.ts +4 -0
- package/src/index.ts +3 -1
- package/src/init.ts +134 -0
- package/src/provider.ts +447 -424
- package/src/signer.ts +316 -0
- package/src/transports/base-wallet-transport.ts +3 -8
- package/src/transports/mux-transport/mux-message-provider.ts +86 -2
- package/src/transports/proxy-transport/proxy-message-provider.ts +1 -1
- package/src/transports/wallet-request-handler.ts +3 -33
- package/src/transports/window-transport/window-message-provider.ts +4 -2
- package/src/types.ts +41 -10
- package/src/utils/index.ts +16 -76
- package/src/utils.ts +81 -51
- package/dist/declarations/src/eip191exceptions.d.ts +0 -3
- package/dist/declarations/src/extended.d.ts +0 -6
- package/dist/declarations/src/transports/unreal-transport/overridelogs.d.ts +0 -4
- package/dist/declarations/src/wallet.d.ts +0 -105
- package/src/wallet.ts +0 -770
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { OpenWalletIntent, ProviderMessage } from
|
|
2
|
-
import { BaseProviderTransport } from
|
|
1
|
+
import { OpenWalletIntent, ProviderMessage } from "../../types.js";
|
|
2
|
+
import { BaseProviderTransport } from "../base-provider-transport.js";
|
|
3
3
|
export declare class WindowMessageProvider extends BaseProviderTransport {
|
|
4
4
|
private walletURL;
|
|
5
5
|
private walletWindow;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ETHAuthProof as AuthETHAuthProof } from '@0xsequence/auth';
|
|
2
2
|
import { commons } from '@0xsequence/core';
|
|
3
|
-
import { JsonRpcHandler, JsonRpcRequest, JsonRpcResponse, NetworkConfig, ProviderRpcError as NetworkProviderRpcError } from '@0xsequence/network';
|
|
3
|
+
import { ChainIdLike, JsonRpcHandler, JsonRpcRequest, JsonRpcResponse, NetworkConfig, ProviderRpcError as NetworkProviderRpcError } from '@0xsequence/network';
|
|
4
4
|
import { TypedData } from '@0xsequence/utils';
|
|
5
5
|
export interface ProviderTransport extends JsonRpcHandler, ProviderMessageTransport, ProviderMessageRequestHandler {
|
|
6
6
|
register(): void;
|
|
@@ -15,6 +15,7 @@ export interface ProviderTransport extends JsonRpcHandler, ProviderMessageTransp
|
|
|
15
15
|
waitUntilOpened(): Promise<WalletSession | undefined>;
|
|
16
16
|
waitUntilConnected(): Promise<ConnectDetails>;
|
|
17
17
|
}
|
|
18
|
+
export declare function isProviderTransport(transport: any): transport is ProviderTransport;
|
|
18
19
|
export interface WalletTransport extends JsonRpcHandler, ProviderMessageTransport, ProviderMessageRequestHandler {
|
|
19
20
|
register(): void;
|
|
20
21
|
unregister(): void;
|
|
@@ -103,11 +104,8 @@ export declare enum InitState {
|
|
|
103
104
|
OK = 2
|
|
104
105
|
}
|
|
105
106
|
export interface ConnectOptions {
|
|
106
|
-
/** Specifies the default network a dapp would like to connect to. This field
|
|
107
|
-
* is optional as it can be provided a number of different ways. */
|
|
108
|
-
networkId?: string | number;
|
|
109
107
|
/** app name of the dapp which will be announced to user on connect screen */
|
|
110
|
-
app
|
|
108
|
+
app: string;
|
|
111
109
|
/** custom protocol for auth redirect (unity/unreal) */
|
|
112
110
|
appProtocol?: string;
|
|
113
111
|
/** origin hint of the dapp's host opening the wallet. This value will automatically
|
|
@@ -129,6 +127,12 @@ export interface ConnectOptions {
|
|
|
129
127
|
/** Options to further customize the wallet experience. */
|
|
130
128
|
settings?: Settings;
|
|
131
129
|
}
|
|
130
|
+
export interface NetworkedConnectOptions extends ConnectOptions {
|
|
131
|
+
/** chainId is the chainId to connect to. If not specified, the default chainId
|
|
132
|
+
* will be used. This does not define a default chain id, it is only used for the connect
|
|
133
|
+
* authorization signature. */
|
|
134
|
+
networkId?: string | number;
|
|
135
|
+
}
|
|
132
136
|
/** Options to further customize the wallet experience. */
|
|
133
137
|
export interface Settings {
|
|
134
138
|
/** Specify a wallet theme. `light` and `dark` are the main themes, to use other available
|
|
@@ -192,7 +196,7 @@ export interface ConnectDetails {
|
|
|
192
196
|
export type PromptConnectDetails = Pick<ConnectDetails, 'chainId' | 'error' | 'connected' | 'proof' | 'email'>;
|
|
193
197
|
export type OpenWalletIntent = {
|
|
194
198
|
type: 'connect';
|
|
195
|
-
options?:
|
|
199
|
+
options?: NetworkedConnectOptions;
|
|
196
200
|
} | {
|
|
197
201
|
type: 'openWithOptions';
|
|
198
202
|
options?: ConnectOptions;
|
|
@@ -211,9 +215,6 @@ export interface WalletSession {
|
|
|
211
215
|
walletContext?: commons.context.VersionedContext;
|
|
212
216
|
accountAddress?: string;
|
|
213
217
|
networks?: NetworkConfig[];
|
|
214
|
-
providerCache?: {
|
|
215
|
-
[key: string]: any;
|
|
216
|
-
};
|
|
217
218
|
}
|
|
218
219
|
export declare class ProviderError extends Error {
|
|
219
220
|
constructor(message?: string);
|
|
@@ -234,4 +235,19 @@ export interface TypedEventEmitter<Events> {
|
|
|
234
235
|
listenerCount<E extends keyof Events>(event: E): number;
|
|
235
236
|
}
|
|
236
237
|
type Arguments<T> = [T] extends [(...args: infer U) => any] ? U : [T] extends [void] ? [] : [T];
|
|
238
|
+
export type OptionalChainIdLike = {
|
|
239
|
+
chainId?: ChainIdLike;
|
|
240
|
+
} | undefined;
|
|
241
|
+
export type OptionalChainId = {
|
|
242
|
+
chainId?: number;
|
|
243
|
+
} | undefined;
|
|
244
|
+
export type OptionalEIP6492 = {
|
|
245
|
+
eip6492?: boolean;
|
|
246
|
+
} | undefined;
|
|
247
|
+
export interface EIP1193Provider {
|
|
248
|
+
request: (request: {
|
|
249
|
+
method: string;
|
|
250
|
+
params?: Array<any>;
|
|
251
|
+
}) => Promise<any>;
|
|
252
|
+
}
|
|
237
253
|
export {};
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { BytesLike, TypedDataDomain, TypedDataField } from 'ethers';
|
|
2
2
|
import { ChainIdLike } from '@0xsequence/network';
|
|
3
3
|
import { TypedData } from '@0xsequence/utils';
|
|
4
|
-
import {
|
|
4
|
+
import { SequenceSigner } from "../signer.js";
|
|
5
|
+
/**
|
|
6
|
+
* This class is redundant with the SequenceSigner class, but it is here for now to
|
|
7
|
+
* maintain compatibility with the old wallet API. Eventually we should move these
|
|
8
|
+
* methods to the SequenceSigner class and deprecate this class.
|
|
9
|
+
*/
|
|
5
10
|
export declare class WalletUtils {
|
|
6
|
-
|
|
7
|
-
constructor(
|
|
8
|
-
signMessage(message: BytesLike, chainId?: ChainIdLike,
|
|
9
|
-
|
|
10
|
-
signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, message: Record<string, any>, chainId?: ChainIdLike, allSigners?: boolean): Promise<string>;
|
|
11
|
-
signAuthTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, message: Record<string, any>, allSigners?: boolean): Promise<string>;
|
|
11
|
+
signer: SequenceSigner;
|
|
12
|
+
constructor(signer: SequenceSigner);
|
|
13
|
+
signMessage(message: BytesLike, chainId?: ChainIdLike, eip6492?: boolean): Promise<string>;
|
|
14
|
+
signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, message: Record<string, any>, chainId?: ChainIdLike, eip6492?: boolean): Promise<string>;
|
|
12
15
|
isValidSignature(address: string, digest: Uint8Array, signature: string, chainId: number): Promise<boolean>;
|
|
13
16
|
isValidMessageSignature(address: string, message: string | Uint8Array, signature: string, chainId: number): Promise<boolean>;
|
|
14
17
|
isValidTypedDataSignature(address: string, typedData: TypedData, signature: string, chainId: number): Promise<boolean>;
|
|
@@ -11,21 +11,27 @@ export declare const isBrowserExtension: () => boolean;
|
|
|
11
11
|
export declare const isUnityPlugin: () => boolean;
|
|
12
12
|
export declare const isWalletUpToDate: (status: AccountStatus) => boolean;
|
|
13
13
|
export interface ItemStore {
|
|
14
|
-
getItem(key: string):
|
|
15
|
-
setItem(key: string, value: string):
|
|
16
|
-
removeItem(key: string):
|
|
14
|
+
getItem(key: string): string | null;
|
|
15
|
+
setItem(key: string, value: string): void;
|
|
16
|
+
removeItem(key: string): void;
|
|
17
|
+
onItemChange(key: string, cb: (value: string | null) => void): () => void;
|
|
17
18
|
}
|
|
18
|
-
export declare class
|
|
19
|
-
private
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
export declare class MemoryItemStore implements ItemStore {
|
|
20
|
+
private callbacks;
|
|
21
|
+
private store;
|
|
22
|
+
getItem(key: string): string | null;
|
|
23
|
+
setItem(key: string, value: string): void;
|
|
24
|
+
removeItem(key: string): void;
|
|
25
|
+
onItemChange(key: string, cb: (value: string | null) => void): () => void;
|
|
23
26
|
}
|
|
24
|
-
export declare class
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
constructor(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
export declare class LocalStorage implements ItemStore {
|
|
28
|
+
private callbacks;
|
|
29
|
+
static isAvailable(): boolean;
|
|
30
|
+
constructor();
|
|
31
|
+
getItem(key: string): string | null;
|
|
32
|
+
setItem(key: string, value: string): void;
|
|
33
|
+
removeItem(key: string): void;
|
|
34
|
+
onItemChange(key: string, cb: (value: string | null) => void): () => void;
|
|
31
35
|
}
|
|
36
|
+
export declare function useBestStore(): ItemStore;
|
|
37
|
+
export declare function resolveArrayProperties<T>(object: Readonly<ethers.utils.Deferrable<T>> | Readonly<ethers.utils.Deferrable<T>>[]): Promise<T>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xsequence/provider",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "provider sub-package for Sequence",
|
|
5
5
|
"repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/provider",
|
|
6
6
|
"source": "src/index.ts",
|
|
@@ -11,21 +11,22 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"eventemitter2": "^6.4.5",
|
|
13
13
|
"webextension-polyfill": "^0.10.0",
|
|
14
|
-
"@0xsequence/abi": "1.0
|
|
15
|
-
"@0xsequence/
|
|
16
|
-
"@0xsequence/
|
|
17
|
-
"@0xsequence/
|
|
18
|
-
"@0xsequence/
|
|
19
|
-
"@0xsequence/
|
|
20
|
-
"@0xsequence/
|
|
21
|
-
"@0xsequence/
|
|
22
|
-
"@0xsequence/
|
|
14
|
+
"@0xsequence/abi": "1.1.0",
|
|
15
|
+
"@0xsequence/account": "1.1.0",
|
|
16
|
+
"@0xsequence/auth": "1.1.0",
|
|
17
|
+
"@0xsequence/relayer": "1.1.0",
|
|
18
|
+
"@0xsequence/migration": "1.1.0",
|
|
19
|
+
"@0xsequence/core": "1.1.0",
|
|
20
|
+
"@0xsequence/network": "1.1.0",
|
|
21
|
+
"@0xsequence/wallet": "1.1.0",
|
|
22
|
+
"@0xsequence/utils": "1.1.0"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
25
|
"ethers": ">=5.5 < 6"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/webextension-polyfill": "^0.10.0",
|
|
29
|
+
"hardhat": "^2.12.2",
|
|
29
30
|
"ethers": "^5.7.2"
|
|
30
31
|
},
|
|
31
32
|
"files": [
|
|
@@ -33,8 +34,14 @@
|
|
|
33
34
|
"dist"
|
|
34
35
|
],
|
|
35
36
|
"scripts": {
|
|
36
|
-
"test": "pnpm test:
|
|
37
|
+
"test": "pnpm test:concurrently 'pnpm test:run'",
|
|
38
|
+
"test:run": "pnpm test:file tests/**/*.spec.ts",
|
|
37
39
|
"test:file": "NODE_OPTIONS='--loader tsx' mocha --timeout 30000",
|
|
38
|
-
"typecheck": "tsc --noEmit"
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"test:concurrently": "concurrently -k --success first 'pnpm start:hardhat1' 'pnpm start:hardhat2'",
|
|
42
|
+
"start:hardhat1": "pnpm start:hardhat1:verbose > /dev/null 2>&1",
|
|
43
|
+
"start:hardhat2": "pnpm start:hardhat2:verbose > /dev/null 2>&1",
|
|
44
|
+
"start:hardhat1:verbose": "hardhat node --config hardhat1.config.js --hostname 0.0.0.0 --port 9595",
|
|
45
|
+
"start:hardhat2:verbose": "hardhat node --config hardhat2.config.js --hostname 0.0.0.0 --port 8595"
|
|
39
46
|
}
|
|
40
47
|
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import { JsonRpcRequest, JsonRpcResponse, NetworkConfig } from "@0xsequence/network"
|
|
2
|
+
import { ConnectDetails, ConnectOptions, ItemStore, MuxMessageProvider, MuxTransportTemplate, OpenWalletIntent, OptionalChainId, OptionalEIP6492, ProviderTransport, WalletEventTypes, WalletSession, isMuxTransportTemplate, isProviderTransport } from "."
|
|
3
|
+
import { commons } from "@0xsequence/core"
|
|
4
|
+
import { TypedData } from "@0xsequence/utils"
|
|
5
|
+
import { toExtended } from "./extended"
|
|
6
|
+
import { ethers } from "ethers"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* This session class is meant to persist the state of the wallet connection
|
|
11
|
+
* whitin the dapp. This enables the client to retain the wallet address (and some more)
|
|
12
|
+
* even if the user refreshes the page. Otherwise we would have to open the popup again.
|
|
13
|
+
*/
|
|
14
|
+
export class SequenceClientSession {
|
|
15
|
+
static readonly SESSION_LOCALSTORE_KEY = '@sequence.session'
|
|
16
|
+
|
|
17
|
+
constructor (
|
|
18
|
+
private store: ItemStore
|
|
19
|
+
) {}
|
|
20
|
+
|
|
21
|
+
connectedSession(): Required<WalletSession> {
|
|
22
|
+
const session = this.getSession()
|
|
23
|
+
|
|
24
|
+
if (
|
|
25
|
+
session &&
|
|
26
|
+
session.accountAddress &&
|
|
27
|
+
session.walletContext &&
|
|
28
|
+
session.networks
|
|
29
|
+
) {
|
|
30
|
+
return {
|
|
31
|
+
accountAddress: session.accountAddress!,
|
|
32
|
+
walletContext: session.walletContext!,
|
|
33
|
+
networks: session.networks!
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
throw new Error('Sequence session not connected')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
hasSession(): boolean {
|
|
41
|
+
return this.getSession()?.accountAddress !== undefined
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setSession(session: WalletSession) {
|
|
45
|
+
return this.store.setItem(SequenceClientSession.SESSION_LOCALSTORE_KEY, JSON.stringify(session))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getSession(): WalletSession | undefined {
|
|
49
|
+
const session = this.store.getItem(SequenceClientSession.SESSION_LOCALSTORE_KEY)
|
|
50
|
+
|
|
51
|
+
if (session) {
|
|
52
|
+
return JSON.parse(session)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async clearSession() {
|
|
59
|
+
return this.store.removeItem(SequenceClientSession.SESSION_LOCALSTORE_KEY)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The wallet webapp doesn't really care what's the "default chain" for the user.
|
|
65
|
+
* so we don't even bother to send this information to the wallet. Instead, we
|
|
66
|
+
* track it locally using storage, that way the data stays always in sync.
|
|
67
|
+
*/
|
|
68
|
+
export class DefaultChainIdTracker {
|
|
69
|
+
static readonly SESSION_CHAIN_ID_KEY = '@sequence.session.defaultChainId'
|
|
70
|
+
|
|
71
|
+
callbacks: ((chainId: number) => void)[] = []
|
|
72
|
+
|
|
73
|
+
constructor(
|
|
74
|
+
private store: ItemStore,
|
|
75
|
+
private startingChainId: number = 1
|
|
76
|
+
) {
|
|
77
|
+
store.onItemChange(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY, (value: string | null) => {
|
|
78
|
+
if (value) {
|
|
79
|
+
const chainId = parseInt(value)
|
|
80
|
+
this.callbacks.forEach(cb => cb(chainId))
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
onDefaultChainIdChanged(callback: (chainId: number) => void) {
|
|
86
|
+
this.callbacks.push(callback)
|
|
87
|
+
return () => {
|
|
88
|
+
this.callbacks = this.callbacks.filter(cb => cb !== callback)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
setDefaultChainId(chainId: number) {
|
|
93
|
+
if (chainId !== this.getDefaultChainId()) {
|
|
94
|
+
this.store.setItem(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY, chainId.toString())
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
getDefaultChainId(): number {
|
|
99
|
+
const read = this.store.getItem(DefaultChainIdTracker.SESSION_CHAIN_ID_KEY)
|
|
100
|
+
|
|
101
|
+
if (!read || read.length === 0) {
|
|
102
|
+
return this.startingChainId
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return parseInt(read)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* This is a wallet client for sequence wallet-webapp. It connects using *some* transport
|
|
111
|
+
* and it allows to perform all sequence specific (or write) operations related to the wallet.
|
|
112
|
+
*
|
|
113
|
+
* It doesn't implement a full ethereum Provider, it doesn't include read-only methods.
|
|
114
|
+
*/
|
|
115
|
+
export class SequenceClient {
|
|
116
|
+
private readonly session: SequenceClientSession
|
|
117
|
+
private readonly defaultChainId: DefaultChainIdTracker
|
|
118
|
+
private readonly callbacks: { [K in keyof WalletEventTypes]?: WalletEventTypes[K][] } = {}
|
|
119
|
+
|
|
120
|
+
public readonly transport: ProviderTransport
|
|
121
|
+
|
|
122
|
+
constructor (
|
|
123
|
+
transport: ProviderTransport | MuxTransportTemplate,
|
|
124
|
+
store: ItemStore,
|
|
125
|
+
defaultChainId?: number
|
|
126
|
+
) {
|
|
127
|
+
if (isMuxTransportTemplate(transport)) {
|
|
128
|
+
this.transport = MuxMessageProvider.new(transport)
|
|
129
|
+
} else if (isProviderTransport(transport)) {
|
|
130
|
+
this.transport = transport
|
|
131
|
+
} else {
|
|
132
|
+
throw new Error('Invalid transport')
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.session = new SequenceClientSession(store)
|
|
136
|
+
this.defaultChainId = new DefaultChainIdTracker(store, defaultChainId)
|
|
137
|
+
|
|
138
|
+
this.transport.on('accountsChanged', (accounts: string[]) => {
|
|
139
|
+
if (accounts.length > 1) {
|
|
140
|
+
console.warn('SequenceClient: wallet-webapp returned more than one account')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.callbacks.accountsChanged?.forEach(cb => cb(accounts))
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
this.transport.on('connect', (response: ConnectDetails) => {
|
|
147
|
+
const chainIdHex = ethers.BigNumber.from(this.getChainId()).toHexString()
|
|
148
|
+
this.callbacks.connect?.forEach(cb => cb({
|
|
149
|
+
...response,
|
|
150
|
+
// Ignore the full connect response
|
|
151
|
+
// use the chainId defined locally
|
|
152
|
+
chainId: chainIdHex
|
|
153
|
+
}))
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
this.transport.on('disconnect', (error) => {
|
|
157
|
+
this.callbacks.disconnect?.forEach(cb => cb(error))
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
this.transport.on('networks', (networks) => {
|
|
161
|
+
this.callbacks.networks?.forEach(cb => cb(networks))
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
this.transport.on('walletContext', (context) => {
|
|
165
|
+
this.callbacks.walletContext?.forEach(cb => cb(context))
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
this.transport.on('open', (info) => {
|
|
169
|
+
this.callbacks.open?.forEach(cb => cb(info))
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
this.transport.on('close', () => {
|
|
173
|
+
this.callbacks.close?.forEach(cb => cb())
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
// We don't listen for the transport chainChanged event
|
|
177
|
+
// instead we handle it locally, so we listen for changes in the store
|
|
178
|
+
this.defaultChainId.onDefaultChainIdChanged((chainId: number) => {
|
|
179
|
+
const chainIdHex = ethers.BigNumber.from(chainId).toHexString()
|
|
180
|
+
this.callbacks.chainChanged?.forEach(cb => cb(chainIdHex))
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Callbacks
|
|
185
|
+
|
|
186
|
+
registerCallback<K extends keyof WalletEventTypes>(eventName: K, callback: WalletEventTypes[K]) {
|
|
187
|
+
if (!this.callbacks[eventName]) {
|
|
188
|
+
this.callbacks[eventName] = []
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
this.callbacks[eventName]!.push(callback)
|
|
192
|
+
|
|
193
|
+
return () => {
|
|
194
|
+
this.callbacks[eventName] = this.callbacks[eventName]!.filter(c => c !== callback) as any
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Individual callbacks lead to more idiomatic code
|
|
199
|
+
|
|
200
|
+
onOpen(callback: WalletEventTypes['open']) {
|
|
201
|
+
return this.registerCallback('open', callback)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
onClose(callback: WalletEventTypes['close']) {
|
|
205
|
+
return this.registerCallback('close', callback)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
onConnect(callback: WalletEventTypes['connect']) {
|
|
209
|
+
return this.registerCallback('connect', callback)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
onDisconnect(callback: WalletEventTypes['disconnect']) {
|
|
213
|
+
return this.registerCallback('disconnect', callback)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
onNetworks(callback: WalletEventTypes['networks']) {
|
|
217
|
+
return this.registerCallback('networks', callback)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
onAccountsChanged(callback: WalletEventTypes['accountsChanged']) {
|
|
221
|
+
return this.registerCallback('accountsChanged', callback)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// @deprecated
|
|
225
|
+
onWalletContext(callback: WalletEventTypes['walletContext']) {
|
|
226
|
+
return this.registerCallback('walletContext', callback)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
onDefaultChainIdChanged(callback: WalletEventTypes['chainChanged']) {
|
|
230
|
+
return this.registerCallback('chainChanged', callback)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
getChainId(): number {
|
|
234
|
+
return this.defaultChainId.getDefaultChainId()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
setDefaultChainId(chainId: number) {
|
|
238
|
+
return this.defaultChainId.setDefaultChainId(chainId)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Proxy transport methods
|
|
242
|
+
|
|
243
|
+
async openWallet(path?: string, intent?: OpenWalletIntent) {
|
|
244
|
+
this.transport.openWallet(path, intent, this.getChainId())
|
|
245
|
+
await this.transport.waitUntilOpened()
|
|
246
|
+
return this.isOpened()
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
closeWallet() {
|
|
250
|
+
return this.transport.closeWallet()
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
isOpened(): boolean {
|
|
254
|
+
return this.transport.isOpened()
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
isConnected(): boolean {
|
|
258
|
+
return this.session.hasSession()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
getSession(): WalletSession | undefined {
|
|
262
|
+
return this.session.getSession()
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Basic API
|
|
266
|
+
getAddress(): string {
|
|
267
|
+
const session = this.session.connectedSession()
|
|
268
|
+
return session.accountAddress
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async connect(options: ConnectOptions): Promise<ConnectDetails> {
|
|
272
|
+
if (options?.authorizeVersion === undefined) {
|
|
273
|
+
// Populate default authorize version if not provided
|
|
274
|
+
options.authorizeVersion = 2
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (options?.refresh === true) {
|
|
278
|
+
this.disconnect()
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (options) {
|
|
282
|
+
if (options.authorize) {
|
|
283
|
+
if (!options.app) {
|
|
284
|
+
throw new Error(`connecting with 'authorize' option also requires 'app' to be set`)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (options.authorizeVersion === undefined) {
|
|
288
|
+
options.authorizeVersion = 2
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
await this.openWallet(undefined, { type: 'connect', options: { ...options, networkId: this.getChainId() } })
|
|
294
|
+
|
|
295
|
+
const connectDetails = await this.transport.waitUntilConnected().catch((error): ConnectDetails => {
|
|
296
|
+
if (error instanceof Error) {
|
|
297
|
+
return { connected: false, error: error.message }
|
|
298
|
+
} else {
|
|
299
|
+
return { connected: false, error: JSON.stringify(error) }
|
|
300
|
+
}
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
// Normalize chainId into a decimal string
|
|
304
|
+
// TODO: Remove this once wallet-webapp returns chainId as a string
|
|
305
|
+
if (connectDetails.chainId) {
|
|
306
|
+
connectDetails.chainId = ethers.BigNumber.from(connectDetails.chainId).toString()
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (connectDetails.connected) {
|
|
310
|
+
if (!connectDetails.session) {
|
|
311
|
+
throw new Error('impossible state, connect response is missing session')
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
this.session.setSession(connectDetails.session)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return connectDetails
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
disconnect() {
|
|
321
|
+
if (this.isOpened()) {
|
|
322
|
+
this.closeWallet()
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return this.session.clearSession()
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Higher level API
|
|
329
|
+
|
|
330
|
+
// Working with sendAsync is less idiomatic
|
|
331
|
+
// but transport uses it instead of send, so we wrap it
|
|
332
|
+
send(request: JsonRpcRequest, chainId?: number): Promise<any> {
|
|
333
|
+
return new Promise((resolve, reject) => {
|
|
334
|
+
this.transport.sendAsync(request, (error, response) => {
|
|
335
|
+
if (error) {
|
|
336
|
+
reject(error)
|
|
337
|
+
} else if (response === undefined) {
|
|
338
|
+
reject(new Error(`Got undefined response for request: ${request}`))
|
|
339
|
+
} else if (typeof response === 'object' && response.error) {
|
|
340
|
+
reject(response.error)
|
|
341
|
+
} else if (typeof response === 'object' && response.result) {
|
|
342
|
+
resolve(response.result)
|
|
343
|
+
} else {
|
|
344
|
+
reject(new Error(`Got invalid response for request: ${request}`))
|
|
345
|
+
}
|
|
346
|
+
}, chainId || this.getChainId())
|
|
347
|
+
})
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async getNetworks(pull?: boolean): Promise<NetworkConfig[]> {
|
|
351
|
+
const connectedSession = this.session.connectedSession()
|
|
352
|
+
|
|
353
|
+
if (pull) {
|
|
354
|
+
connectedSession.networks = await this.send({ method: 'sequence_getNetworks' })
|
|
355
|
+
this.session.setSession(connectedSession)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return connectedSession.networks
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async signMessage(
|
|
362
|
+
message: ethers.BytesLike,
|
|
363
|
+
options?: OptionalEIP6492 & OptionalChainId
|
|
364
|
+
): Promise<string> {
|
|
365
|
+
const method = options?.eip6492 ? 'sequence_sign' : 'personal_sign'
|
|
366
|
+
|
|
367
|
+
// Address is ignored by the wallet webapp
|
|
368
|
+
return this.send({ method, params: [message, this.getAddress()] }, options?.chainId)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async signTypedData(
|
|
372
|
+
typedData: TypedData,
|
|
373
|
+
options?: OptionalEIP6492 & OptionalChainId
|
|
374
|
+
): Promise<string> {
|
|
375
|
+
const method = options?.eip6492 ? 'sequence_signTypedData_v4' : 'eth_signTypedData_v4'
|
|
376
|
+
|
|
377
|
+
// TODO: Stop using ethers for this, this is the only place where we use it
|
|
378
|
+
// and it makes the client depend on ethers.
|
|
379
|
+
const encoded = ethers.utils._TypedDataEncoder.getPayload(typedData.domain, typedData.types, typedData.message)
|
|
380
|
+
|
|
381
|
+
// The sign typed data will use one of the following chainIds, in order:
|
|
382
|
+
// - The one provided in the options
|
|
383
|
+
// - The one provided in the typedData.domain.chainId
|
|
384
|
+
// - The default chainId
|
|
385
|
+
|
|
386
|
+
return this.send({ method, params: [this.getAddress(), encoded] }, (
|
|
387
|
+
options?.chainId ||
|
|
388
|
+
(
|
|
389
|
+
typedData.domain.chainId &&
|
|
390
|
+
ethers.BigNumber.from(typedData.domain.chainId).toNumber()
|
|
391
|
+
) ||
|
|
392
|
+
this.getChainId()
|
|
393
|
+
))
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async sendTransaction(
|
|
397
|
+
tx: (
|
|
398
|
+
ethers.providers.TransactionRequest[] |
|
|
399
|
+
ethers.providers.TransactionRequest
|
|
400
|
+
),
|
|
401
|
+
options?: OptionalChainId
|
|
402
|
+
): Promise<string> {
|
|
403
|
+
const sequenceTxs = Array.isArray(tx) ? tx : [tx]
|
|
404
|
+
const extendedTxs = toExtended(sequenceTxs)
|
|
405
|
+
|
|
406
|
+
return this.send({ method: 'eth_sendTransaction', params: [extendedTxs] }, options?.chainId )
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async getWalletContext(): Promise<commons.context.VersionedContext> {
|
|
410
|
+
return this.send({ method: 'sequence_getWalletContext' })
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async getOnchainWalletConfig(options?: OptionalChainId): Promise<commons.config.Config> {
|
|
414
|
+
// NOTICE: sequence_getWalletConfig sends the chainId as a param
|
|
415
|
+
const res = await this.send({ method: 'sequence_getWalletConfig', params: [options?.chainId || this.getChainId()] }, options?.chainId)
|
|
416
|
+
return Array.isArray(res) ? res[0] : res
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// NOTICE: We are leaving out all the "regular" methods os a tipical
|
|
420
|
+
// JSON RPC Provider (eth_getBlockByNumber, eth_call, etc)
|
|
421
|
+
// wallet-webapp does implement them, but this client is meant to be
|
|
422
|
+
// exclusively used for Sequence specific methods
|
|
423
|
+
}
|
package/src/extended.ts
CHANGED
|
@@ -21,3 +21,7 @@ export function toExtended(transactions: ethers.providers.TransactionRequest[]):
|
|
|
21
21
|
export function fromExtended(transaction: ExtendedTransactionRequest): ethers.providers.TransactionRequest[] {
|
|
22
22
|
return [transaction, ...(transaction.auxiliary || [])]
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
export function isExtended(transaction: ethers.providers.TransactionRequest): transaction is ExtendedTransactionRequest {
|
|
26
|
+
return (transaction as any).auxiliary !== undefined
|
|
27
|
+
}
|