@kheopskit/core 0.0.1-alpha.0 → 0.0.1
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/CHANGELOG.md +14 -0
- package/dist/index.d.mts +70 -60
- package/dist/index.d.ts +70 -60
- package/dist/index.js +574 -227
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +613 -244
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -6
- package/src/api/accounts.ts +25 -12
- package/src/api/appKit.ts +122 -0
- package/src/api/config.ts +5 -4
- package/src/api/ethereum/accounts.ts +183 -62
- package/src/api/ethereum/wallets.ts +38 -24
- package/src/api/index.ts +1 -0
- package/src/api/kheopskit.ts +35 -13
- package/src/api/polkadot/accounts.ts +117 -29
- package/src/api/polkadot/wallets.ts +88 -68
- package/src/api/store.ts +6 -3
- package/src/api/types.ts +71 -54
- package/src/api/wallets.ts +16 -14
- package/src/index.ts +0 -2
- package/src/utils/{AccountId.ts → WalletAccountId.ts} +5 -5
- package/src/utils/WalletId.ts +1 -1
- package/src/utils/createStore.ts +1 -1
- package/src/utils/getAccountAddressType.ts +2 -1
- package/src/utils/getCachedObservable.ts +12 -0
- package/src/utils/getQuery.ts +72 -0
- package/src/utils/index.ts +1 -3
- package/src/utils/isEthereumAddress.ts +3 -1
- package/src/utils/isSs58Address.ts +2 -2
- package/src/utils/isWalletPlatform.ts +1 -1
- package/src/utils/logObservable.ts +21 -0
- package/src/utils/polkadotExtensions.ts +21 -0
- package/src/utils/sortAccounts.ts +36 -0
- package/src/utils/sortWallets.ts +14 -0
- package/src/utils/isTruthy.ts +0 -3
- package/src/utils/types.ts +0 -5
|
@@ -1,86 +1,207 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
EthereumAccount,
|
|
3
|
+
EthereumAppKitWallet,
|
|
4
|
+
EthereumInjectedWallet,
|
|
5
|
+
EthereumWallet,
|
|
6
|
+
} from "@/api/types";
|
|
7
|
+
import { getWalletAccountId } from "@/utils";
|
|
8
|
+
import { getCachedObservable$ } from "@/utils/getCachedObservable";
|
|
9
|
+
import type UniversalProvider from "@walletconnect/universal-provider";
|
|
3
10
|
import {
|
|
11
|
+
Observable,
|
|
12
|
+
ReplaySubject,
|
|
4
13
|
combineLatest,
|
|
14
|
+
distinctUntilChanged,
|
|
5
15
|
map,
|
|
6
|
-
Observable,
|
|
7
16
|
of,
|
|
8
17
|
shareReplay,
|
|
9
18
|
switchMap,
|
|
10
19
|
} from "rxjs";
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
provider: EIP1193Provider;
|
|
18
|
-
address: `0x${string}`;
|
|
19
|
-
walletName: string;
|
|
20
|
-
walletId: string;
|
|
21
|
-
isWalletDefault: boolean;
|
|
22
|
-
};
|
|
20
|
+
import {
|
|
21
|
+
type EIP1193Provider,
|
|
22
|
+
createWalletClient,
|
|
23
|
+
custom,
|
|
24
|
+
getAddress,
|
|
25
|
+
} from "viem";
|
|
23
26
|
|
|
24
|
-
const
|
|
25
|
-
wallet:
|
|
27
|
+
const getInjectedWalletAccounts$ = (
|
|
28
|
+
wallet: EthereumInjectedWallet,
|
|
26
29
|
): Observable<EthereumAccount[]> => {
|
|
27
|
-
if (!wallet.
|
|
28
|
-
|
|
29
|
-
return
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
isWalletDefault: i === 0,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const listener = (addresses: string[]) => {
|
|
41
|
-
subscriber.next(addresses.map(getAccount));
|
|
42
|
-
};
|
|
30
|
+
if (!wallet.isConnected) return of([]);
|
|
31
|
+
|
|
32
|
+
return getCachedObservable$(`accounts:${wallet.id}`, () =>
|
|
33
|
+
new Observable<EthereumAccount[]>((subscriber) => {
|
|
34
|
+
const getAccount = (address: string, i: number): EthereumAccount => {
|
|
35
|
+
// console.log("[Injected] createWalletClient", address);
|
|
36
|
+
const client = createWalletClient({
|
|
37
|
+
account: address as `0x${string}`,
|
|
38
|
+
transport: custom(wallet.provider as EIP1193Provider),
|
|
39
|
+
});
|
|
43
40
|
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
return {
|
|
42
|
+
id: getWalletAccountId(wallet.id, address),
|
|
43
|
+
platform: "ethereum",
|
|
44
|
+
client,
|
|
45
|
+
address: getAddress(address),
|
|
46
|
+
walletName: wallet.name,
|
|
47
|
+
walletId: wallet.id,
|
|
48
|
+
isWalletDefault: i === 0,
|
|
49
|
+
};
|
|
50
|
+
};
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
wallet.provider
|
|
49
|
-
.request({ method: "eth_accounts" })
|
|
50
|
-
.then((addresses: string[]) => {
|
|
52
|
+
const handleAccountsChanged = (addresses: string[]) => {
|
|
51
53
|
subscriber.next(addresses.map(getAccount));
|
|
52
|
-
}
|
|
53
|
-
.catch((err) => {
|
|
54
|
-
console.error("Failed to get accounts", err);
|
|
55
|
-
subscriber.next([]);
|
|
56
|
-
});
|
|
54
|
+
};
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
wallet.provider.
|
|
60
|
-
|
|
56
|
+
// subscribe to changes
|
|
57
|
+
wallet.provider.on("accountsChanged", handleAccountsChanged);
|
|
58
|
+
|
|
59
|
+
// initial value
|
|
60
|
+
wallet.provider
|
|
61
|
+
.request({ method: "eth_accounts" })
|
|
62
|
+
.then((addresses: string[]) => {
|
|
63
|
+
subscriber.next(addresses.map(getAccount));
|
|
64
|
+
})
|
|
65
|
+
.catch((err) => {
|
|
66
|
+
console.error("Failed to get accounts", err);
|
|
67
|
+
subscriber.next([]);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return () => {
|
|
71
|
+
wallet.provider.removeListener(
|
|
72
|
+
"accountsChanged",
|
|
73
|
+
handleAccountsChanged,
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
}).pipe(shareReplay({ refCount: true, bufferSize: 1 })),
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const wrapWalletConnectProvider = (
|
|
81
|
+
provider: EIP1193Provider,
|
|
82
|
+
sessionTopic: string,
|
|
83
|
+
caipNetworkId: string,
|
|
84
|
+
): EIP1193Provider => {
|
|
85
|
+
return new Proxy(provider, {
|
|
86
|
+
get(target, prop, receiver) {
|
|
87
|
+
if (prop !== "request") return Reflect.get(target, prop, receiver);
|
|
88
|
+
|
|
89
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
90
|
+
return (args: any) => {
|
|
91
|
+
// console.log("request", { target, prop, receiver, caipNetworkId, args });
|
|
92
|
+
|
|
93
|
+
if (args && typeof args === "object" && args.method) {
|
|
94
|
+
if (!args.topic) args.topic = sessionTopic;
|
|
95
|
+
if (!args.chainId) args.chainId = caipNetworkId;
|
|
96
|
+
}
|
|
97
|
+
return target.request(args);
|
|
98
|
+
};
|
|
99
|
+
},
|
|
61
100
|
});
|
|
62
101
|
};
|
|
63
102
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
103
|
+
const getAppKitAccounts$ = (
|
|
104
|
+
wallet: EthereumAppKitWallet,
|
|
105
|
+
): Observable<EthereumAccount[]> => {
|
|
106
|
+
const account = wallet.appKit.getAccount("eip155");
|
|
107
|
+
const provider = wallet.appKit.getProvider<UniversalProvider>("eip155");
|
|
108
|
+
|
|
109
|
+
if (
|
|
110
|
+
!wallet.isConnected ||
|
|
111
|
+
!wallet.appKit ||
|
|
112
|
+
!account?.allAccounts.length ||
|
|
113
|
+
!provider?.session
|
|
114
|
+
)
|
|
115
|
+
return of([]);
|
|
116
|
+
|
|
117
|
+
return getCachedObservable$("accounts:appKit", () =>
|
|
118
|
+
new Observable<EthereumAccount[]>((subscriber) => {
|
|
119
|
+
const caipNetworkId$ = new ReplaySubject<string>(1);
|
|
120
|
+
|
|
121
|
+
const handleChainChanged = (chainId: unknown) => {
|
|
122
|
+
caipNetworkId$.next(`eip155:${chainId}`);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
provider.on("chainChanged", handleChainChanged);
|
|
126
|
+
provider.request({ method: "eth_chainId" }).then(handleChainChanged);
|
|
127
|
+
|
|
128
|
+
const sub = caipNetworkId$
|
|
129
|
+
.pipe(
|
|
130
|
+
distinctUntilChanged(),
|
|
131
|
+
map((caipNetworkId) =>
|
|
132
|
+
custom(
|
|
133
|
+
wrapWalletConnectProvider(
|
|
134
|
+
provider as EIP1193Provider,
|
|
135
|
+
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
|
136
|
+
provider.session!.topic,
|
|
137
|
+
caipNetworkId,
|
|
138
|
+
),
|
|
139
|
+
),
|
|
140
|
+
),
|
|
141
|
+
map((transport) =>
|
|
142
|
+
account.allAccounts.map((acc, i): EthereumAccount => {
|
|
143
|
+
// console.log("[AppKit] createWalletClient", acc);
|
|
144
|
+
const client = createWalletClient({
|
|
145
|
+
account: acc.address as `0x${string}`,
|
|
146
|
+
transport,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
id: getWalletAccountId(wallet.id, acc.address),
|
|
151
|
+
platform: "ethereum",
|
|
152
|
+
walletName: wallet.name,
|
|
153
|
+
walletId: wallet.id,
|
|
154
|
+
address: acc.address as `0x${string}`,
|
|
155
|
+
client,
|
|
156
|
+
isWalletDefault: i === 0,
|
|
157
|
+
};
|
|
158
|
+
}),
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
.subscribe(subscriber);
|
|
162
|
+
|
|
163
|
+
return () => {
|
|
164
|
+
provider.off("chainChanged", handleChainChanged);
|
|
165
|
+
sub.unsubscribe();
|
|
166
|
+
};
|
|
167
|
+
}).pipe(shareReplay({ refCount: true, bufferSize: 1 })),
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export const getEthereumAccounts$ = (
|
|
172
|
+
ethereumWallets: Observable<EthereumWallet[]>,
|
|
173
|
+
) =>
|
|
174
|
+
new Observable<EthereumAccount[]>((subscriber) => {
|
|
175
|
+
const sub = ethereumWallets
|
|
67
176
|
.pipe(
|
|
68
|
-
map((wallets) => wallets.filter((w) => w.
|
|
69
|
-
switchMap((wallets) =>
|
|
70
|
-
wallets.length
|
|
71
|
-
? combineLatest(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
177
|
+
map((wallets) => wallets.filter((w) => w.isConnected)),
|
|
178
|
+
switchMap((wallets) => {
|
|
179
|
+
return wallets.length
|
|
180
|
+
? combineLatest([
|
|
181
|
+
...wallets
|
|
182
|
+
.filter((w) => w.type === "injected")
|
|
183
|
+
.map(getInjectedWalletAccounts$),
|
|
184
|
+
...wallets
|
|
185
|
+
.filter((w) => w.type === "appKit")
|
|
186
|
+
.map(getAppKitAccounts$),
|
|
187
|
+
// todo appkit
|
|
188
|
+
])
|
|
189
|
+
: of([]);
|
|
190
|
+
}),
|
|
191
|
+
map((accounts) => accounts.flat()),
|
|
192
|
+
distinctUntilChanged(isSameAccountsList),
|
|
75
193
|
)
|
|
76
194
|
.subscribe(subscriber);
|
|
77
195
|
|
|
78
196
|
return () => {
|
|
79
197
|
sub.unsubscribe();
|
|
80
198
|
};
|
|
81
|
-
}
|
|
82
|
-
|
|
199
|
+
}).pipe(
|
|
200
|
+
// logObservable("ethereumAccounts$", true),
|
|
201
|
+
shareReplay({ refCount: true, bufferSize: 1 }),
|
|
202
|
+
);
|
|
83
203
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
204
|
+
const isSameAccountsList = (a: EthereumAccount[], b: EthereumAccount[]) => {
|
|
205
|
+
if (a.length !== b.length) return false;
|
|
206
|
+
return a.every((account, i) => account.id === b[i]?.id);
|
|
207
|
+
};
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { store } from "@/api/store";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import type {
|
|
3
|
+
EthereumInjectedWallet,
|
|
4
|
+
EthereumWallet,
|
|
5
|
+
KheopskitConfig,
|
|
6
|
+
} from "@/api/types";
|
|
7
|
+
import { type WalletId, getWalletId } from "@/utils/WalletId";
|
|
8
|
+
import { type EIP6963ProviderDetail, createStore } from "mipd";
|
|
5
9
|
import {
|
|
6
10
|
BehaviorSubject,
|
|
11
|
+
Observable,
|
|
7
12
|
combineLatest,
|
|
8
13
|
map,
|
|
9
|
-
Observable,
|
|
10
14
|
shareReplay,
|
|
11
15
|
} from "rxjs";
|
|
12
16
|
import type { EIP1193Provider } from "viem";
|
|
17
|
+
import { getAppKitWallets$ } from "../appKit";
|
|
13
18
|
|
|
14
19
|
const providersDetails$ = new Observable<EIP6963ProviderDetail[]>(
|
|
15
20
|
(subscriber) => {
|
|
@@ -27,25 +32,21 @@ const providersDetails$ = new Observable<EIP6963ProviderDetail[]>(
|
|
|
27
32
|
unsubscribe();
|
|
28
33
|
store.destroy();
|
|
29
34
|
};
|
|
30
|
-
}
|
|
35
|
+
},
|
|
31
36
|
).pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
console.count("[kheopskit] providers$ emit");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
export const ethereumWallets$ = new Observable<EthereumWallet[]>(
|
|
38
|
+
const ethereumInjectedWallets$ = new Observable<EthereumInjectedWallet[]>(
|
|
38
39
|
(subscriber) => {
|
|
39
40
|
const enabledWalletIds$ = new BehaviorSubject<Set<WalletId>>(new Set());
|
|
40
41
|
|
|
41
42
|
const connectWallet = async (
|
|
42
43
|
walletId: WalletId,
|
|
43
|
-
provider: EIP1193Provider
|
|
44
|
+
provider: EIP1193Provider,
|
|
44
45
|
) => {
|
|
45
46
|
if (enabledWalletIds$.value.has(walletId))
|
|
46
47
|
throw new Error(`Extension ${walletId} already connected`);
|
|
47
48
|
|
|
48
|
-
provider.request({
|
|
49
|
+
await provider.request({
|
|
49
50
|
method: "eth_requestAccounts",
|
|
50
51
|
});
|
|
51
52
|
|
|
@@ -56,10 +57,7 @@ export const ethereumWallets$ = new Observable<EthereumWallet[]>(
|
|
|
56
57
|
store.addEnabledWalletId(walletId);
|
|
57
58
|
};
|
|
58
59
|
|
|
59
|
-
const disconnectWallet = async (
|
|
60
|
-
walletId: WalletId,
|
|
61
|
-
_provider: EIP1193Provider
|
|
62
|
-
) => {
|
|
60
|
+
const disconnectWallet = async (walletId: WalletId) => {
|
|
63
61
|
if (!enabledWalletIds$.value.has(walletId))
|
|
64
62
|
throw new Error(`Extension ${walletId} is not connected`);
|
|
65
63
|
const newSet = new Set(enabledWalletIds$.value);
|
|
@@ -72,32 +70,48 @@ export const ethereumWallets$ = new Observable<EthereumWallet[]>(
|
|
|
72
70
|
const sub = combineLatest([providersDetails$, enabledWalletIds$])
|
|
73
71
|
.pipe(
|
|
74
72
|
map(([providerDetails, enabledWalletIds]) => {
|
|
75
|
-
return providerDetails.map((pd):
|
|
73
|
+
return providerDetails.map((pd): EthereumInjectedWallet => {
|
|
76
74
|
const walletId = getWalletId("ethereum", pd.info.rdns);
|
|
77
75
|
const provider = pd.provider as EIP1193Provider;
|
|
78
76
|
|
|
79
77
|
return {
|
|
80
78
|
platform: "ethereum",
|
|
79
|
+
type: "injected",
|
|
81
80
|
id: walletId,
|
|
82
81
|
name: pd.info.name,
|
|
83
82
|
icon: pd.info.icon,
|
|
84
83
|
provider,
|
|
85
|
-
|
|
84
|
+
isConnected: enabledWalletIds.has(walletId),
|
|
86
85
|
providerId: pd.info.rdns,
|
|
87
86
|
connect: () => connectWallet(walletId, provider),
|
|
88
|
-
disconnect: () => disconnectWallet(walletId
|
|
87
|
+
disconnect: () => disconnectWallet(walletId),
|
|
89
88
|
};
|
|
90
89
|
});
|
|
91
|
-
})
|
|
90
|
+
}),
|
|
92
91
|
)
|
|
93
92
|
.subscribe(subscriber);
|
|
94
93
|
|
|
95
94
|
return () => {
|
|
96
95
|
sub.unsubscribe();
|
|
97
96
|
};
|
|
98
|
-
}
|
|
97
|
+
},
|
|
99
98
|
).pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
|
100
99
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
export const getEthereumWallets$ = (config: KheopskitConfig) => {
|
|
101
|
+
return new Observable<EthereumWallet[]>((subscriber) => {
|
|
102
|
+
const subscription = combineLatest([
|
|
103
|
+
ethereumInjectedWallets$,
|
|
104
|
+
getAppKitWallets$(config)?.pipe(map((w) => w.ethereum)),
|
|
105
|
+
])
|
|
106
|
+
.pipe(
|
|
107
|
+
map(([injectedWallets, appKitWallet]) =>
|
|
108
|
+
appKitWallet ? [...injectedWallets, appKitWallet] : injectedWallets,
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
.subscribe(subscriber);
|
|
112
|
+
|
|
113
|
+
return () => {
|
|
114
|
+
subscription.unsubscribe();
|
|
115
|
+
};
|
|
116
|
+
}).pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
|
117
|
+
};
|
package/src/api/index.ts
CHANGED
package/src/api/kheopskit.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { logObservable } from "@/utils/logObservable";
|
|
2
|
+
import {
|
|
3
|
+
Observable,
|
|
4
|
+
combineLatest,
|
|
5
|
+
map,
|
|
6
|
+
shareReplay,
|
|
7
|
+
throttleTime,
|
|
8
|
+
} from "rxjs";
|
|
2
9
|
import { getAccounts$ } from "./accounts";
|
|
3
10
|
import { resolveConfig } from "./config";
|
|
4
11
|
import type { KheopskitConfig, Wallet, WalletAccount } from "./types";
|
|
@@ -6,18 +13,33 @@ import { getWallets$ } from "./wallets";
|
|
|
6
13
|
|
|
7
14
|
export type { KheopskitConfig } from "./types";
|
|
8
15
|
|
|
9
|
-
export
|
|
10
|
-
|
|
16
|
+
export type KheopskitState = {
|
|
17
|
+
wallets: Wallet[];
|
|
18
|
+
accounts: WalletAccount[];
|
|
19
|
+
config: KheopskitConfig;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const getKheopskit$ = (config?: Partial<KheopskitConfig>) => {
|
|
23
|
+
const kc = resolveConfig(config);
|
|
24
|
+
|
|
25
|
+
console.debug("[kheopskit] config", kc);
|
|
26
|
+
|
|
27
|
+
return new Observable<KheopskitState>((subscriber) => {
|
|
28
|
+
const wallets$ = getWallets$(kc);
|
|
11
29
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
30
|
+
const subscription = combineLatest({
|
|
31
|
+
wallets: wallets$,
|
|
32
|
+
accounts: getAccounts$(kc, wallets$),
|
|
33
|
+
})
|
|
34
|
+
.pipe(map(({ wallets, accounts }) => ({ config: kc, wallets, accounts })))
|
|
35
|
+
.subscribe(subscriber);
|
|
17
36
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
37
|
+
return () => {
|
|
38
|
+
subscription.unsubscribe();
|
|
39
|
+
};
|
|
40
|
+
}).pipe(
|
|
41
|
+
throttleTime(50, undefined, { leading: true, trailing: true }),
|
|
42
|
+
logObservable("kheopskit$", { enabled: kc.debug, printValue: true }),
|
|
43
|
+
shareReplay({ bufferSize: 1, refCount: true }),
|
|
44
|
+
);
|
|
23
45
|
};
|
|
@@ -1,44 +1,50 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type {
|
|
2
|
+
PolkadotAccount,
|
|
3
|
+
PolkadotAppKitWallet,
|
|
4
|
+
PolkadotInjectedWallet,
|
|
5
|
+
PolkadotWallet,
|
|
6
|
+
} from "@/api/types";
|
|
7
|
+
import { getWalletAccountId } from "@/utils";
|
|
8
|
+
import type { AppKit } from "@reown/appkit/core";
|
|
9
|
+
import type UniversalProvider from "@walletconnect/universal-provider";
|
|
4
10
|
import {
|
|
11
|
+
type InjectedExtension,
|
|
12
|
+
type InjectedPolkadotAccount,
|
|
13
|
+
getPolkadotSignerFromPjs,
|
|
14
|
+
} from "polkadot-api/pjs-signer";
|
|
15
|
+
import {
|
|
16
|
+
Observable,
|
|
5
17
|
combineLatest,
|
|
18
|
+
distinctUntilChanged,
|
|
6
19
|
map,
|
|
7
|
-
Observable,
|
|
8
20
|
of,
|
|
9
21
|
shareReplay,
|
|
10
22
|
switchMap,
|
|
11
23
|
} from "rxjs";
|
|
12
|
-
import { polkadotWallets$ } from "./wallets";
|
|
13
|
-
|
|
14
|
-
export type PolkadotAccount = InjectedPolkadotAccount & {
|
|
15
|
-
id: AccountId;
|
|
16
|
-
platform: "polkadot";
|
|
17
|
-
walletName: string;
|
|
18
|
-
walletId: string;
|
|
19
|
-
};
|
|
20
24
|
|
|
21
|
-
const
|
|
22
|
-
wallet:
|
|
25
|
+
const getInjectedWalletAccounts$ = (
|
|
26
|
+
wallet: PolkadotInjectedWallet,
|
|
23
27
|
): Observable<PolkadotAccount[]> => {
|
|
24
|
-
if (!wallet.
|
|
28
|
+
if (!wallet.isConnected) return of([]);
|
|
25
29
|
|
|
26
30
|
return new Observable<PolkadotAccount[]>((subscriber) => {
|
|
27
31
|
const getAccount = (account: InjectedPolkadotAccount): PolkadotAccount => ({
|
|
28
|
-
id:
|
|
32
|
+
id: getWalletAccountId(wallet.id, account.address),
|
|
29
33
|
...account,
|
|
30
34
|
platform: "polkadot",
|
|
31
35
|
walletName: wallet.name,
|
|
32
36
|
walletId: wallet.id,
|
|
33
37
|
});
|
|
34
38
|
|
|
39
|
+
const extension = wallet.extension as InjectedExtension;
|
|
40
|
+
|
|
35
41
|
// subscribe to changes
|
|
36
|
-
const unsubscribe =
|
|
42
|
+
const unsubscribe = extension.subscribe((accounts) => {
|
|
37
43
|
subscriber.next(accounts.map(getAccount));
|
|
38
44
|
});
|
|
39
45
|
|
|
40
46
|
// initial value
|
|
41
|
-
subscriber.next(
|
|
47
|
+
subscriber.next(extension.getAccounts().map(getAccount));
|
|
42
48
|
|
|
43
49
|
return () => {
|
|
44
50
|
return unsubscribe();
|
|
@@ -46,26 +52,108 @@ const getWalletAccounts$ = (
|
|
|
46
52
|
});
|
|
47
53
|
};
|
|
48
54
|
|
|
49
|
-
|
|
50
|
-
(
|
|
55
|
+
const getAppKitPolkadotSigner = (appKit: AppKit, address: string) => {
|
|
56
|
+
const provider = appKit.getProvider<UniversalProvider>("polkadot");
|
|
57
|
+
if (!provider) throw new Error("No provider found");
|
|
58
|
+
if (!provider.session) throw new Error("No session found");
|
|
59
|
+
|
|
60
|
+
return getPolkadotSignerFromPjs(
|
|
61
|
+
address,
|
|
62
|
+
(transactionPayload) => {
|
|
63
|
+
if (!provider.session) throw new Error("No session found");
|
|
64
|
+
|
|
65
|
+
return provider.client.request({
|
|
66
|
+
topic: provider.session.topic,
|
|
67
|
+
chainId: `polkadot:${transactionPayload.genesisHash.substring(2, 34)}`,
|
|
68
|
+
request: {
|
|
69
|
+
method: "polkadot_signTransaction",
|
|
70
|
+
params: {
|
|
71
|
+
address,
|
|
72
|
+
transactionPayload,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
async ({ address, data }) => {
|
|
78
|
+
if (!provider.session) throw new Error("No session found");
|
|
79
|
+
const networks = appKit.getCaipNetworks("polkadot");
|
|
80
|
+
const chainId = networks[0]?.caipNetworkId;
|
|
81
|
+
if (!chainId) throw new Error("No chainId found");
|
|
82
|
+
|
|
83
|
+
return provider.client.request({
|
|
84
|
+
topic: provider.session.topic,
|
|
85
|
+
chainId,
|
|
86
|
+
request: {
|
|
87
|
+
method: "polkadot_signMessage",
|
|
88
|
+
params: {
|
|
89
|
+
address,
|
|
90
|
+
message: data,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const getAppKitAccounts$ = (wallet: PolkadotAppKitWallet) => {
|
|
99
|
+
const account = wallet.appKit.getAccount("polkadot");
|
|
100
|
+
const provider = wallet.appKit.getProvider<UniversalProvider>("polkadot");
|
|
101
|
+
|
|
102
|
+
if (
|
|
103
|
+
!wallet.isConnected ||
|
|
104
|
+
!wallet.appKit ||
|
|
105
|
+
!account?.allAccounts.length ||
|
|
106
|
+
!provider?.session
|
|
107
|
+
)
|
|
108
|
+
return of([]);
|
|
109
|
+
|
|
110
|
+
return of(
|
|
111
|
+
account.allAccounts.map(
|
|
112
|
+
(acc): PolkadotAccount => ({
|
|
113
|
+
id: getWalletAccountId(wallet.id, acc.address),
|
|
114
|
+
platform: "polkadot",
|
|
115
|
+
walletName: wallet.name,
|
|
116
|
+
walletId: wallet.id,
|
|
117
|
+
address: acc.address,
|
|
118
|
+
polkadotSigner: getAppKitPolkadotSigner(wallet.appKit, acc.address),
|
|
119
|
+
genesisHash: null,
|
|
120
|
+
name: `${wallet.name} Polkadot`,
|
|
121
|
+
type: "sr25519",
|
|
122
|
+
}),
|
|
123
|
+
),
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export const getPolkadotAccounts$ = (
|
|
128
|
+
polkadotWallets$: Observable<PolkadotWallet[]>,
|
|
129
|
+
) =>
|
|
130
|
+
new Observable<PolkadotAccount[]>((subscriber) => {
|
|
51
131
|
const sub = polkadotWallets$
|
|
52
132
|
.pipe(
|
|
53
|
-
map((wallets) => wallets.filter((w) => w.
|
|
133
|
+
map((wallets) => wallets.filter((w) => w.isConnected)),
|
|
54
134
|
switchMap((wallets) =>
|
|
55
135
|
wallets.length
|
|
56
|
-
? combineLatest(
|
|
57
|
-
|
|
136
|
+
? combineLatest([
|
|
137
|
+
...wallets
|
|
138
|
+
.filter((w) => w.type === "injected")
|
|
139
|
+
.map(getInjectedWalletAccounts$),
|
|
140
|
+
...wallets
|
|
141
|
+
.filter((w) => w.type === "appKit")
|
|
142
|
+
.map(getAppKitAccounts$),
|
|
143
|
+
])
|
|
144
|
+
: of([]),
|
|
58
145
|
),
|
|
59
|
-
map((accounts) => accounts.flat())
|
|
146
|
+
map((accounts) => accounts.flat()),
|
|
147
|
+
distinctUntilChanged(isSameAccountsList),
|
|
60
148
|
)
|
|
61
149
|
.subscribe(subscriber);
|
|
62
150
|
|
|
63
151
|
return () => {
|
|
64
152
|
sub.unsubscribe();
|
|
65
153
|
};
|
|
66
|
-
}
|
|
67
|
-
).pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
|
154
|
+
}).pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
|
68
155
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
156
|
+
const isSameAccountsList = (a: PolkadotAccount[], b: PolkadotAccount[]) => {
|
|
157
|
+
if (a.length !== b.length) return false;
|
|
158
|
+
return a.every((account, i) => account.id === b[i]?.id);
|
|
159
|
+
};
|