@etherplay/connect 0.0.9 → 0.0.10
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/index.d.ts +3143 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +172 -0
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +13 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +48 -0
- package/dist/provider.js.map +1 -0
- package/package.json +5 -4
- package/src/index.ts +194 -2
- package/src/provider.ts +68 -0
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {AlchemyMechanism, OriginAccount} from '@etherplay/alchemy';
|
|
2
2
|
import {writable} from 'svelte/store';
|
|
3
3
|
import {createPopupLauncher, type PopupPromise} from './popup.js';
|
|
4
|
-
import type {EIP1193WindowWalletProvider} from 'eip-1193';
|
|
4
|
+
import type {EIP1193ChainId, EIP1193WindowWalletProvider} from 'eip-1193';
|
|
5
5
|
import {
|
|
6
6
|
fromEntropyKeyToMnemonic,
|
|
7
7
|
fromMnemonicToFirstAccount,
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
originPublicKeyPublicationMessage,
|
|
11
11
|
} from '@etherplay/alchemy';
|
|
12
12
|
import {hashMessage} from './utils.js';
|
|
13
|
+
import {createProvider} from './provider.js';
|
|
13
14
|
|
|
14
15
|
export {fromEntropyKeyToMnemonic, originPublicKeyPublicationMessage, originKeyMessage};
|
|
15
16
|
export type {OriginAccount};
|
|
@@ -84,6 +85,8 @@ export type Connection = {
|
|
|
84
85
|
provider: EIP1193WindowWalletProvider;
|
|
85
86
|
accountChanged?: `0x${string}`;
|
|
86
87
|
chainId: string;
|
|
88
|
+
invalidChainId: boolean;
|
|
89
|
+
switchingChain: 'addingChain' | 'switchingChain' | false;
|
|
87
90
|
};
|
|
88
91
|
}
|
|
89
92
|
);
|
|
@@ -106,7 +109,18 @@ export interface EIP6963AnnounceProviderEvent extends CustomEvent {
|
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
const storageAccountKey = '__origin_account';
|
|
109
|
-
export function createConnection(settings: {
|
|
112
|
+
export function createConnection(settings: {
|
|
113
|
+
walletHost: string;
|
|
114
|
+
autoConnect?: boolean;
|
|
115
|
+
node: {url: string; chainId: string; prioritizeWalletProvider?: boolean; requestsPerSecond?: number};
|
|
116
|
+
}) {
|
|
117
|
+
const alwaysOnChainId = settings.node.chainId;
|
|
118
|
+
const alwaysOnProvider = createProvider({
|
|
119
|
+
endpoint: settings.node.url,
|
|
120
|
+
chainId: settings.node.chainId,
|
|
121
|
+
prioritizeWalletProvider: settings.node.prioritizeWalletProvider,
|
|
122
|
+
requestsPerSecond: settings.node.requestsPerSecond,
|
|
123
|
+
});
|
|
110
124
|
let autoConnect = true;
|
|
111
125
|
if (typeof settings.autoConnect !== 'undefined') {
|
|
112
126
|
autoConnect = settings.autoConnect;
|
|
@@ -190,6 +204,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
|
|
|
190
204
|
const chainIdAsHex = await walletProvider.request({method: 'eth_chainId'});
|
|
191
205
|
const chainId = Number(chainIdAsHex).toString();
|
|
192
206
|
_wallet = {provider: walletProvider, chainId};
|
|
207
|
+
alwaysOnProvider.setWalletProvider(walletProvider);
|
|
193
208
|
watchForChainIdChange(_wallet.provider);
|
|
194
209
|
set({
|
|
195
210
|
step: 'SignedIn',
|
|
@@ -200,6 +215,8 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
|
|
|
200
215
|
provider: walletProvider,
|
|
201
216
|
accountChanged: undefined,
|
|
202
217
|
chainId,
|
|
218
|
+
invalidChainId: alwaysOnChainId != chainId,
|
|
219
|
+
switchingChain: false,
|
|
203
220
|
},
|
|
204
221
|
});
|
|
205
222
|
walletProvider.request({method: 'eth_accounts'}).then(onAccountChanged);
|
|
@@ -319,6 +336,8 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
|
|
|
319
336
|
chainId,
|
|
320
337
|
provider: provider,
|
|
321
338
|
accountChanged: undefined, // TODO check account list
|
|
339
|
+
invalidChainId: alwaysOnChainId != chainId,
|
|
340
|
+
switchingChain: false,
|
|
322
341
|
},
|
|
323
342
|
});
|
|
324
343
|
if (remember) {
|
|
@@ -349,6 +368,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
|
|
|
349
368
|
wallet: {
|
|
350
369
|
...$connection.wallet,
|
|
351
370
|
chainId,
|
|
371
|
+
invalidChainId: alwaysOnChainId != chainId,
|
|
352
372
|
},
|
|
353
373
|
});
|
|
354
374
|
}
|
|
@@ -420,6 +440,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
|
|
|
420
440
|
const wallet = $connection.wallets.find((v) => v.info.name == walletName || v.info.uuid == walletName);
|
|
421
441
|
if (wallet) {
|
|
422
442
|
if (_wallet) {
|
|
443
|
+
alwaysOnProvider.setWalletProvider(undefined);
|
|
423
444
|
stopatchingForAccountChange(_wallet.provider);
|
|
424
445
|
stopatchingForChainIdChange(_wallet.provider);
|
|
425
446
|
}
|
|
@@ -441,6 +462,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
|
|
|
441
462
|
chainId,
|
|
442
463
|
provider,
|
|
443
464
|
};
|
|
465
|
+
alwaysOnProvider.setWalletProvider(_wallet.provider);
|
|
444
466
|
watchForChainIdChange(_wallet.provider);
|
|
445
467
|
let accounts = await provider.request({method: 'eth_accounts'});
|
|
446
468
|
accounts = accounts.map((v) => v.toLowerCase()) as `0x${string}`[];
|
|
@@ -580,6 +602,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
|
|
|
580
602
|
function disconnect() {
|
|
581
603
|
deleteOriginAccount();
|
|
582
604
|
if (_wallet) {
|
|
605
|
+
alwaysOnProvider.setWalletProvider(undefined);
|
|
583
606
|
stopatchingForAccountChange(_wallet.provider);
|
|
584
607
|
stopatchingForChainIdChange(_wallet.provider);
|
|
585
608
|
}
|
|
@@ -693,6 +716,171 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
|
|
|
693
716
|
throw new Error(`no saved public key publication signature for ${account.address}`);
|
|
694
717
|
}
|
|
695
718
|
|
|
719
|
+
async function switchWalletChain(
|
|
720
|
+
chainId: string,
|
|
721
|
+
config?: {
|
|
722
|
+
readonly rpcUrls?: readonly string[];
|
|
723
|
+
readonly blockExplorerUrls?: readonly string[];
|
|
724
|
+
readonly chainName?: string;
|
|
725
|
+
readonly iconUrls?: readonly string[];
|
|
726
|
+
readonly nativeCurrency?: {
|
|
727
|
+
name: string;
|
|
728
|
+
symbol: string;
|
|
729
|
+
decimals: number;
|
|
730
|
+
};
|
|
731
|
+
},
|
|
732
|
+
) {
|
|
733
|
+
if ($connection.step !== 'SignedIn' || !$connection.wallet) {
|
|
734
|
+
throw new Error(`invali state`);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const wallet = $connection.wallet;
|
|
738
|
+
// if (!wallet) {
|
|
739
|
+
// throw new Error(`no wallet`);
|
|
740
|
+
// }
|
|
741
|
+
|
|
742
|
+
try {
|
|
743
|
+
// attempt to switch...
|
|
744
|
+
set({
|
|
745
|
+
...$connection,
|
|
746
|
+
wallet: {...$connection.wallet, switchingChain: 'switchingChain'},
|
|
747
|
+
});
|
|
748
|
+
const result = await wallet.provider.request({
|
|
749
|
+
method: 'wallet_switchEthereumChain',
|
|
750
|
+
params: [
|
|
751
|
+
{
|
|
752
|
+
chainId: ('0x' + parseInt(chainId).toString(16)) as EIP1193ChainId,
|
|
753
|
+
},
|
|
754
|
+
],
|
|
755
|
+
});
|
|
756
|
+
if (!result) {
|
|
757
|
+
if ($connection.wallet) {
|
|
758
|
+
set({
|
|
759
|
+
...$connection,
|
|
760
|
+
wallet: {...$connection.wallet, switchingChain: false},
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// logger.info(`wallet_switchEthereumChain: complete`);
|
|
765
|
+
// this will be taken care with `chainChanged` (but maybe it should be done there ?)
|
|
766
|
+
// handleNetwork(chainId);
|
|
767
|
+
} else {
|
|
768
|
+
if ($connection.wallet) {
|
|
769
|
+
set({
|
|
770
|
+
...$connection,
|
|
771
|
+
wallet: {...$connection.wallet, switchingChain: false},
|
|
772
|
+
error: {
|
|
773
|
+
message: `Failed to switch to ${config?.chainName || `chain with id = ${chainId}`}`,
|
|
774
|
+
cause: result,
|
|
775
|
+
},
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
throw result;
|
|
779
|
+
}
|
|
780
|
+
} catch (err) {
|
|
781
|
+
if ((err as any).code === 4001) {
|
|
782
|
+
// logger.info(`wallet_addEthereumChain: failed but error code === 4001, we ignore as user rejected it`, err);
|
|
783
|
+
if ($connection.wallet) {
|
|
784
|
+
set({
|
|
785
|
+
...$connection,
|
|
786
|
+
wallet: {...$connection.wallet, switchingChain: false},
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
// if ((err as any).code === 4902) {
|
|
792
|
+
else if (config && config.rpcUrls && config.rpcUrls.length > 0) {
|
|
793
|
+
if ($connection.wallet) {
|
|
794
|
+
set({
|
|
795
|
+
...$connection,
|
|
796
|
+
wallet: {...$connection.wallet, switchingChain: 'addingChain'},
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
// logger.info(`wallet_switchEthereumChain: could not switch, try adding the chain via "wallet_addEthereumChain"`);
|
|
800
|
+
try {
|
|
801
|
+
const result = await wallet.provider.request({
|
|
802
|
+
method: 'wallet_addEthereumChain',
|
|
803
|
+
params: [
|
|
804
|
+
{
|
|
805
|
+
chainId: ('0x' + parseInt(chainId).toString(16)) as EIP1193ChainId,
|
|
806
|
+
rpcUrls: config.rpcUrls,
|
|
807
|
+
chainName: config.chainName,
|
|
808
|
+
blockExplorerUrls: config.blockExplorerUrls,
|
|
809
|
+
iconUrls: config.iconUrls,
|
|
810
|
+
nativeCurrency: config.nativeCurrency,
|
|
811
|
+
},
|
|
812
|
+
],
|
|
813
|
+
});
|
|
814
|
+
if (!result) {
|
|
815
|
+
if ($connection.wallet) {
|
|
816
|
+
set({
|
|
817
|
+
...$connection,
|
|
818
|
+
wallet: {...$connection.wallet, switchingChain: false},
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
// this will be taken care with `chainChanged` (but maybe it should be done there ?)
|
|
822
|
+
// handleNetwork(chainId);
|
|
823
|
+
} else {
|
|
824
|
+
if ($connection.wallet) {
|
|
825
|
+
set({
|
|
826
|
+
...$connection,
|
|
827
|
+
wallet: {...$connection.wallet, switchingChain: false},
|
|
828
|
+
error: {
|
|
829
|
+
message: `Failed to add new chain: ${config?.chainName || `chain with id = ${chainId}`}`,
|
|
830
|
+
cause: result,
|
|
831
|
+
},
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
// logger.info(`wallet_addEthereumChain: a non-undefinded result means an error`, result);
|
|
835
|
+
throw result;
|
|
836
|
+
}
|
|
837
|
+
} catch (err) {
|
|
838
|
+
if ((err as any).code !== 4001) {
|
|
839
|
+
if ($connection.wallet) {
|
|
840
|
+
set({
|
|
841
|
+
...$connection,
|
|
842
|
+
wallet: {...$connection.wallet, switchingChain: false},
|
|
843
|
+
error: {
|
|
844
|
+
message: `Failed to add new chain: ${config?.chainName || `chain with id = ${chainId}`}`,
|
|
845
|
+
cause: err,
|
|
846
|
+
},
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
// logger.info(`wallet_addEthereumChain: failed`, err);
|
|
850
|
+
// TODO ?
|
|
851
|
+
// set({
|
|
852
|
+
// error: {message: `Failed to add new chain`, cause: err},
|
|
853
|
+
// });
|
|
854
|
+
// for now:
|
|
855
|
+
throw err;
|
|
856
|
+
} else {
|
|
857
|
+
if ($connection.wallet) {
|
|
858
|
+
set({
|
|
859
|
+
...$connection,
|
|
860
|
+
wallet: {...$connection.wallet, switchingChain: false},
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
// logger.info(`wallet_addEthereumChain: failed but error code === 4001, we ignore as user rejected it`, err);
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
} else {
|
|
868
|
+
const errorMessage = `Chain "${config?.chainName || `with chainId = ${chainId}`} " is not available on your wallet`;
|
|
869
|
+
if ($connection.wallet) {
|
|
870
|
+
set({
|
|
871
|
+
...$connection,
|
|
872
|
+
wallet: {...$connection.wallet, switchingChain: false},
|
|
873
|
+
error: {
|
|
874
|
+
message: errorMessage,
|
|
875
|
+
},
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
throw new Error(errorMessage);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
696
884
|
return {
|
|
697
885
|
subscribe: _store.subscribe,
|
|
698
886
|
connect,
|
|
@@ -702,5 +890,9 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
|
|
|
702
890
|
connectOnCurrentWalletAccount,
|
|
703
891
|
disconnect,
|
|
704
892
|
getSignatureForPublicKeyPublication,
|
|
893
|
+
switchWalletChain,
|
|
894
|
+
provider: alwaysOnProvider,
|
|
705
895
|
};
|
|
706
896
|
}
|
|
897
|
+
|
|
898
|
+
export type ConnectionStore = ReturnType<typeof createConnection>;
|
package/src/provider.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type {EIP1193WalletProvider, EIP1193WindowWalletProvider, Methods} from 'eip-1193';
|
|
2
|
+
import {createCurriedJSONRPC, CurriedRPC} from 'remote-procedure-call';
|
|
3
|
+
|
|
4
|
+
const signerMethods = [
|
|
5
|
+
'eth_accounts',
|
|
6
|
+
'eth_sign',
|
|
7
|
+
'eth_signTransaction',
|
|
8
|
+
'personal_sign',
|
|
9
|
+
'eth_signTypedData_v4',
|
|
10
|
+
'eth_signTypedData',
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const connectedAccountMethods = ['eth_sendTransaction'];
|
|
14
|
+
|
|
15
|
+
const walletOnlyMethods = ['eth_requestAccounts', 'wallet_switchEthereumChain', 'wallet_addEthereumChain'];
|
|
16
|
+
|
|
17
|
+
export function createProvider(params: {
|
|
18
|
+
endpoint: string;
|
|
19
|
+
chainId: string;
|
|
20
|
+
prioritizeWalletProvider?: boolean;
|
|
21
|
+
requestsPerSecond?: number;
|
|
22
|
+
}): CurriedRPC<Methods> & {setWalletProvider: (walletProvider: EIP1193WindowWalletProvider | undefined) => void} & {
|
|
23
|
+
chainId: string;
|
|
24
|
+
} {
|
|
25
|
+
const {endpoint, chainId, prioritizeWalletProvider, requestsPerSecond} = params;
|
|
26
|
+
const jsonRPC = createCurriedJSONRPC(endpoint);
|
|
27
|
+
|
|
28
|
+
let walletProvider: EIP1193WindowWalletProvider | undefined;
|
|
29
|
+
|
|
30
|
+
function setWalletProvider(walletProvider: EIP1193WindowWalletProvider | undefined) {
|
|
31
|
+
walletProvider = walletProvider;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const provider = {
|
|
35
|
+
request(req: {method: string; params?: any[]}) {
|
|
36
|
+
if (walletProvider) {
|
|
37
|
+
const signingMethod =
|
|
38
|
+
signerMethods.includes(req.method) ||
|
|
39
|
+
connectedAccountMethods.includes(req.method) ||
|
|
40
|
+
walletOnlyMethods.includes(req.method) ||
|
|
41
|
+
req.method.indexOf('sign') != -1;
|
|
42
|
+
if (prioritizeWalletProvider || signingMethod) {
|
|
43
|
+
const currentChainIdAsHex = walletProvider.request({
|
|
44
|
+
method: 'eth_chainId',
|
|
45
|
+
});
|
|
46
|
+
const currentChainId = Number(currentChainIdAsHex).toString();
|
|
47
|
+
if (chainId !== currentChainId) {
|
|
48
|
+
if (signingMethod) {
|
|
49
|
+
return Promise.reject(
|
|
50
|
+
new Error(
|
|
51
|
+
`wallet provider is connected to a different chain, expected ${chainId} but got ${currentChainId}`,
|
|
52
|
+
),
|
|
53
|
+
);
|
|
54
|
+
} else {
|
|
55
|
+
// we fallback on jsonRPc if invalid chain and not a signing method
|
|
56
|
+
return jsonRPC.request(req);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return walletProvider.request(req as any);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return jsonRPC.request(req);
|
|
64
|
+
},
|
|
65
|
+
} as unknown as EIP1193WalletProvider;
|
|
66
|
+
|
|
67
|
+
return {...createCurriedJSONRPC<Methods>(provider as any, {requestsPerSecond}), setWalletProvider, chainId};
|
|
68
|
+
}
|