@pezkuwi/extension-dapp 0.62.7 → 0.62.8

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.
Files changed (45) hide show
  1. package/README.md +1 -1
  2. package/{cjs → build}/bundle.d.ts +1 -1
  3. package/build/bundle.js +395 -0
  4. package/build/index.js +5 -0
  5. package/build/packageDetect.js +8 -0
  6. package/build/packageInfo.js +4 -0
  7. package/build/util.js +12 -0
  8. package/{cjs → build}/wrapBytes.d.ts +1 -1
  9. package/build/wrapBytes.js +9 -0
  10. package/package.json +7 -112
  11. package/src/bundle.ts +319 -0
  12. package/src/index.ts +7 -0
  13. package/src/packageDetect.ts +12 -0
  14. package/src/packageInfo.ts +6 -0
  15. package/src/util.ts +12 -0
  16. package/src/wrapBytes.spec.ts +137 -0
  17. package/{wrapBytes.js → src/wrapBytes.ts} +6 -1
  18. package/tsconfig.build.json +14 -0
  19. package/tsconfig.build.tsbuildinfo +1 -0
  20. package/tsconfig.spec.json +16 -0
  21. package/tsconfig.spec.tsbuildinfo +1 -0
  22. package/LICENSE +0 -201
  23. package/bundle-polkadot-extension-dapp.js +0 -205
  24. package/bundle.d.ts +0 -61
  25. package/bundle.js +0 -232
  26. package/cjs/bundle.js +0 -246
  27. package/cjs/index.js +0 -4
  28. package/cjs/package.json +0 -3
  29. package/cjs/packageDetect.js +0 -6
  30. package/cjs/packageInfo.js +0 -4
  31. package/cjs/util.js +0 -13
  32. package/cjs/wrapBytes.js +0 -10
  33. package/index.d.ts +0 -1
  34. package/index.js +0 -1
  35. package/packageDetect.d.ts +0 -1
  36. package/packageDetect.js +0 -4
  37. package/packageInfo.d.ts +0 -6
  38. package/packageInfo.js +0 -1
  39. package/util.d.ts +0 -1
  40. package/util.js +0 -10
  41. package/wrapBytes.d.ts +0 -7
  42. /package/{cjs → build}/index.d.ts +0 -0
  43. /package/{cjs → build}/packageDetect.d.ts +0 -0
  44. /package/{cjs → build}/packageInfo.d.ts +0 -0
  45. /package/{cjs → build}/util.d.ts +0 -0
package/src/bundle.ts ADDED
@@ -0,0 +1,319 @@
1
+ // Copyright 2019-2025 @pezkuwi/extension-dapp authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { InjectedAccount, InjectedAccountWithMeta, InjectedExtension, InjectedProviderWithMeta, InjectedWindow, ProviderList, Unsubcall, Web3AccountsOptions } from '@pezkuwi/extension-inject/types';
5
+
6
+ import { isPromise, objectSpread, u8aEq } from '@pezkuwi/util';
7
+ import { decodeAddress, encodeAddress } from '@pezkuwi/util-crypto';
8
+
9
+ import { documentReadyPromise } from './util.js';
10
+
11
+ // expose utility functions
12
+ export { packageInfo } from './packageInfo.js';
13
+ export { unwrapBytes, wrapBytes } from './wrapBytes.js';
14
+
15
+ // just a helper (otherwise we cast all-over, so shorter and more readable)
16
+ const win = window as Window & InjectedWindow;
17
+
18
+ // don't clobber the existing object, but ensure non-undefined
19
+ win.injectedWeb3 = win.injectedWeb3 || {};
20
+
21
+ // have we found a properly constructed window.injectedWeb3
22
+ let isWeb3Injected = web3IsInjected();
23
+
24
+ // we keep the last promise created around (for queries)
25
+ let web3EnablePromise: Promise<InjectedExtension[]> | null = null;
26
+
27
+ export { isWeb3Injected, web3EnablePromise };
28
+
29
+ /** @internal true when anything has been injected and is available */
30
+ function web3IsInjected (): boolean {
31
+ return Object
32
+ .values(win.injectedWeb3)
33
+ .filter(({ connect, enable }) => !!(connect || enable))
34
+ .length !== 0;
35
+ }
36
+
37
+ /** @internal throw a consistent error when not extensions have not been enabled */
38
+ function throwError (method: string): never {
39
+ throw new Error(`${method}: web3Enable(originName) needs to be called before ${method}`);
40
+ }
41
+
42
+ /** @internal map from Array<InjectedAccount> to Array<InjectedAccountWithMeta> */
43
+ function mapAccounts (source: string, list: InjectedAccount[], ss58Format?: number): InjectedAccountWithMeta[] {
44
+ return list.map(({ address, genesisHash, name, type }): InjectedAccountWithMeta => ({
45
+ address: address.length === 42
46
+ ? address
47
+ : encodeAddress(decodeAddress(address), ss58Format),
48
+ meta: { genesisHash, name, source },
49
+ type
50
+ }));
51
+ }
52
+
53
+ /** @internal filter accounts based on genesisHash and type of account */
54
+ function filterAccounts (list: InjectedAccount[], genesisHash?: string | null, type?: string[]): InjectedAccount[] {
55
+ return list.filter((a) =>
56
+ (!a.type || !type || type.includes(a.type)) &&
57
+ (!a.genesisHash || !genesisHash || a.genesisHash === genesisHash)
58
+ );
59
+ }
60
+
61
+ /** @internal retrieves all the extensions available on the window */
62
+ function getWindowExtensions (originName: string): Promise<InjectedExtension[]> {
63
+ return Promise
64
+ .all(
65
+ Object
66
+ .entries(win.injectedWeb3)
67
+ .map(([nameOrHash, { connect, enable, version }]): Promise<(InjectedExtension | void)> =>
68
+ Promise
69
+ .resolve()
70
+ .then(() =>
71
+ connect
72
+ // new style, returning all info
73
+ ? connect(originName)
74
+ : enable
75
+ // previous interface, leakages on name/version
76
+ ? enable(originName).then((e) =>
77
+ objectSpread<InjectedExtension>({ name: nameOrHash, version: version || 'unknown' }, e)
78
+ )
79
+ : Promise.reject(new Error('No connect(..) or enable(...) hook found'))
80
+ )
81
+ .catch(({ message }: Error): void => {
82
+ console.error(`Error initializing ${nameOrHash}: ${message}`);
83
+ })
84
+ )
85
+ )
86
+ .then((exts) => exts.filter((e): e is InjectedExtension => !!e));
87
+ }
88
+
89
+ /** @internal Ensure the enable promise is resolved and filter by extensions */
90
+ async function filterEnable (caller: 'web3Accounts' | 'web3AccountsSubscribe', extensions?: string[]): Promise<InjectedExtension[]> {
91
+ if (!web3EnablePromise) {
92
+ return throwError(caller);
93
+ }
94
+
95
+ const sources = await web3EnablePromise;
96
+
97
+ return sources.filter(({ name }) =>
98
+ !extensions ||
99
+ extensions.includes(name)
100
+ );
101
+ }
102
+
103
+ /**
104
+ * @summary Enables all the providers found on the injected window interface
105
+ * @description
106
+ * Enables all injected extensions that has been found on the page. This
107
+ * should be called before making use of any other web3* functions.
108
+ */
109
+ export function web3Enable (originName: string, compatInits: (() => Promise<boolean>)[] = []): Promise<InjectedExtension[]> {
110
+ if (!originName) {
111
+ throw new Error('You must pass a name for your app to the web3Enable function');
112
+ }
113
+
114
+ const initCompat = compatInits.length
115
+ ? Promise.all(compatInits.map((c) => c().catch(() => false)))
116
+ : Promise.resolve([true]);
117
+
118
+ web3EnablePromise = documentReadyPromise(
119
+ (): Promise<InjectedExtension[]> =>
120
+ initCompat.then(() =>
121
+ getWindowExtensions(originName)
122
+ .then((values): InjectedExtension[] =>
123
+ values.map((e): InjectedExtension => {
124
+ // if we don't have an accounts subscriber, add a single-shot version
125
+ if (!e.accounts.subscribe) {
126
+ e.accounts.subscribe = (cb: (accounts: InjectedAccount[]) => void | Promise<void>): Unsubcall => {
127
+ e.accounts
128
+ .get()
129
+ .then(cb)
130
+ .catch(console.error);
131
+
132
+ return (): void => {
133
+ // no ubsubscribe needed, this is a single-shot
134
+ };
135
+ };
136
+ }
137
+
138
+ return e;
139
+ })
140
+ )
141
+ .catch((): InjectedExtension[] => [])
142
+ .then((values): InjectedExtension[] => {
143
+ const names = values.map(({ name, version }): string => `${name}/${version}`);
144
+
145
+ isWeb3Injected = web3IsInjected();
146
+ console.info(`web3Enable: Enabled ${values.length} extension${values.length !== 1 ? 's' : ''}: ${names.join(', ')}`);
147
+
148
+ return values;
149
+ })
150
+ )
151
+ );
152
+
153
+ return web3EnablePromise;
154
+ }
155
+
156
+ /**
157
+ * @summary Retrieves all the accounts across all providers
158
+ * @description
159
+ * This returns the full list of account available (across all extensions) to
160
+ * the page. Filtering options are available of a per-extension, per type and
161
+ * per-genesisHash basis. Optionally the accounts can be encoded with the provided
162
+ * ss58Format
163
+ */
164
+ export async function web3Accounts ({ accountType, extensions, genesisHash, ss58Format }: Web3AccountsOptions = {}): Promise<InjectedAccountWithMeta[]> {
165
+ const accounts: InjectedAccountWithMeta[] = [];
166
+ const sources = await filterEnable('web3Accounts', extensions);
167
+ const retrieved = await Promise.all(
168
+ sources.map(async ({ accounts, name: source }): Promise<InjectedAccountWithMeta[]> => {
169
+ try {
170
+ const list = await accounts.get();
171
+
172
+ return mapAccounts(source, filterAccounts(list, genesisHash, accountType), ss58Format);
173
+ } catch {
174
+ // cannot handle this one
175
+ return [];
176
+ }
177
+ })
178
+ );
179
+
180
+ retrieved.forEach((result): void => {
181
+ accounts.push(...result);
182
+ });
183
+
184
+ console.info(`web3Accounts: Found ${accounts.length} address${accounts.length !== 1 ? 'es' : ''}`);
185
+
186
+ return accounts;
187
+ }
188
+
189
+ /**
190
+ * @summary Subscribes to all the accounts across all providers
191
+ * @description
192
+ * This is the subscription version of the web3Accounts interface with
193
+ * updates as to when new accounts do become available. The list of filtering
194
+ * options are the same as for the web3Accounts interface.
195
+ */
196
+ export async function web3AccountsSubscribe (cb: (accounts: InjectedAccountWithMeta[]) => void | Promise<void>, { accountType, extensions, genesisHash, ss58Format }: Web3AccountsOptions = {}): Promise<Unsubcall> {
197
+ const sources = await filterEnable('web3AccountsSubscribe', extensions);
198
+ const accounts: Record<string, InjectedAccount[]> = {};
199
+
200
+ const triggerUpdate = (): void | Promise<void> =>
201
+ cb(
202
+ Object
203
+ .entries(accounts)
204
+ .reduce((result: InjectedAccountWithMeta[], [source, list]): InjectedAccountWithMeta[] => {
205
+ result.push(...mapAccounts(source, filterAccounts(list, genesisHash, accountType), ss58Format));
206
+
207
+ return result;
208
+ }, [])
209
+ );
210
+
211
+ const unsubs = sources.map(({ accounts: { subscribe }, name: source }): Unsubcall =>
212
+ subscribe((result): void => {
213
+ accounts[source] = result;
214
+
215
+ try {
216
+ const result = triggerUpdate();
217
+
218
+ if (result && isPromise(result)) {
219
+ result.catch(console.error);
220
+ }
221
+ } catch (error) {
222
+ console.error(error);
223
+ }
224
+ })
225
+ );
226
+
227
+ return (): void => {
228
+ unsubs.forEach((unsub): void => {
229
+ unsub();
230
+ });
231
+ };
232
+ }
233
+
234
+ /**
235
+ * @summary Finds a specific provider based on the name
236
+ * @description
237
+ * This retrieves a specific source (extension) based on the name. In most
238
+ * cases it should not be needed to call it directly (e.g. it is used internally
239
+ * by calls such as web3FromAddress) but would allow operation on a specific
240
+ * known extension.
241
+ */
242
+ export async function web3FromSource (source: string): Promise<InjectedExtension> {
243
+ if (!web3EnablePromise) {
244
+ return throwError('web3FromSource');
245
+ }
246
+
247
+ const sources = await web3EnablePromise;
248
+ const found = source && sources.find(({ name }) => name === source);
249
+
250
+ if (!found) {
251
+ throw new Error(`web3FromSource: Unable to find an injected ${source}`);
252
+ }
253
+
254
+ return found;
255
+ }
256
+
257
+ /**
258
+ * @summary Find a specific provider that provides a specific address
259
+ * @description
260
+ * Based on an address, return the provider that has makes this address
261
+ * available to the page.
262
+ */
263
+ export async function web3FromAddress (address: string): Promise<InjectedExtension> {
264
+ if (!web3EnablePromise) {
265
+ return throwError('web3FromAddress');
266
+ }
267
+
268
+ const accounts = await web3Accounts();
269
+ let found: InjectedAccountWithMeta | undefined;
270
+
271
+ if (address) {
272
+ const accountU8a = decodeAddress(address);
273
+
274
+ found = accounts.find((account): boolean => u8aEq(decodeAddress(account.address), accountU8a));
275
+ }
276
+
277
+ if (!found) {
278
+ throw new Error(`web3FromAddress: Unable to find injected ${address}`);
279
+ }
280
+
281
+ return web3FromSource(found.meta.source);
282
+ }
283
+
284
+ /**
285
+ * @summary List all providers exposed by one source
286
+ * @description
287
+ * For extensions that supply RPC providers, this call would return the list
288
+ * of RPC providers that any extension may supply.
289
+ */
290
+ export async function web3ListRpcProviders (source: string): Promise<ProviderList | null> {
291
+ const { provider } = await web3FromSource(source);
292
+
293
+ if (!provider) {
294
+ console.warn(`Extension ${source} does not expose any provider`);
295
+
296
+ return null;
297
+ }
298
+
299
+ return provider.listProviders();
300
+ }
301
+
302
+ /**
303
+ * @summary Start an RPC provider provider by a specific source
304
+ * @description
305
+ * For extensions that supply RPC providers, this call would return an
306
+ * enabled provider (initialized with the specific key) from the
307
+ * specified extension source.
308
+ */
309
+ export async function web3UseRpcProvider (source: string, key: string): Promise<InjectedProviderWithMeta> {
310
+ const { provider } = await web3FromSource(source);
311
+
312
+ if (!provider) {
313
+ throw new Error(`Extension ${source} does not expose any provider`);
314
+ }
315
+
316
+ const meta = await provider.startProvider(key);
317
+
318
+ return { meta, provider };
319
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ // Copyright 2019-2025 @pezkuwi/extension-dapp authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Since we inject into pages, we skip this
5
+ // import './packageDetect.js';
6
+
7
+ export * from './bundle.js';
@@ -0,0 +1,12 @@
1
+ // Copyright 2017-2025 @pezkuwi/extension-dapp authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Do not edit, auto-generated by @pezkuwi/dev
5
+ // (packageInfo imports will be kept as-is, user-editable)
6
+
7
+ import { packageInfo as injectInfo } from '@pezkuwi/extension-inject/packageInfo';
8
+ import { detectPackage } from '@pezkuwi/util';
9
+
10
+ import { packageInfo } from './packageInfo.js';
11
+
12
+ detectPackage(packageInfo, null, [injectInfo]);
@@ -0,0 +1,6 @@
1
+ // Copyright 2017-2025 @pezkuwi/extension-dapp authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Do not edit, auto-generated by @pezkuwi/dev
5
+
6
+ export const packageInfo = { name: '@pezkuwi/extension-dapp', path: 'auto', type: 'auto', version: '0.62.6' };
package/src/util.ts ADDED
@@ -0,0 +1,12 @@
1
+ // Copyright 2019-2025 @pezkuwi/extension-dapp authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export function documentReadyPromise <T> (creator: () => Promise<T>): Promise<T> {
5
+ return new Promise((resolve): void => {
6
+ if (document.readyState === 'complete') {
7
+ resolve(creator());
8
+ } else {
9
+ window.addEventListener('load', () => resolve(creator()));
10
+ }
11
+ });
12
+ }
@@ -0,0 +1,137 @@
1
+ // Copyright 2019-2025 @pezkuwi/extension authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type * as _ from '@pezkuwi/dev-test/globals.d.ts';
5
+
6
+ import { u8aConcat, u8aEq, u8aToString } from '@pezkuwi/util';
7
+
8
+ import { ETHEREUM, POSTFIX, PREFIX, unwrapBytes, wrapBytes } from './wrapBytes.js';
9
+
10
+ const TEST_DATA = 'this is just some random message that we expect to be wrapped along the way';
11
+ const TEST_ETH = u8aConcat(ETHEREUM, TEST_DATA);
12
+ const TEST_WRAP_EMPTY = `${u8aToString(PREFIX)}${u8aToString(POSTFIX)}`;
13
+ const TEST_WRAP_FULL = `${u8aToString(PREFIX)}${TEST_DATA}${u8aToString(POSTFIX)}`;
14
+ const TEST_WARP_HALF_PRE = `${u8aToString(PREFIX)}${TEST_DATA}`;
15
+ const TEST_WRAP_HALF_POST = `${TEST_DATA}${u8aToString(POSTFIX)}`;
16
+
17
+ describe('wrapBytes', (): void => {
18
+ it('wraps empty bytes', (): void => {
19
+ expect(
20
+ u8aEq(
21
+ wrapBytes(new Uint8Array()),
22
+ u8aConcat(PREFIX, POSTFIX)
23
+ )
24
+ ).toBe(true);
25
+ });
26
+
27
+ it('wraps when no wrapping is detected', (): void => {
28
+ expect(
29
+ u8aToString(
30
+ wrapBytes(TEST_DATA)
31
+ )
32
+ ).toEqual(TEST_WRAP_FULL);
33
+ });
34
+
35
+ it('wraps when only start wrap is detected', (): void => {
36
+ expect(
37
+ u8aToString(
38
+ wrapBytes(TEST_WARP_HALF_PRE)
39
+ )
40
+ ).toEqual(`${u8aToString(PREFIX)}${TEST_WARP_HALF_PRE}${u8aToString(POSTFIX)}`);
41
+ });
42
+
43
+ it('wraps when only end wrap is detected', (): void => {
44
+ expect(
45
+ u8aToString(
46
+ wrapBytes(TEST_WRAP_HALF_POST)
47
+ )
48
+ ).toEqual(`${u8aToString(PREFIX)}${TEST_WRAP_HALF_POST}${u8aToString(POSTFIX)}`);
49
+ });
50
+
51
+ it('does not re-wrap when a wrap is already present', (): void => {
52
+ expect(
53
+ u8aToString(
54
+ wrapBytes(TEST_WRAP_FULL)
55
+ )
56
+ ).toEqual(TEST_WRAP_FULL);
57
+ });
58
+
59
+ it('does not re-wrap when a wrap (empty data) is already present', (): void => {
60
+ expect(
61
+ u8aToString(
62
+ wrapBytes(TEST_WRAP_EMPTY)
63
+ )
64
+ ).toEqual(TEST_WRAP_EMPTY);
65
+ });
66
+ });
67
+
68
+ describe('unwrapBytes', (): void => {
69
+ it('unwraps empty bytes', (): void => {
70
+ expect(
71
+ u8aEq(
72
+ unwrapBytes(new Uint8Array()),
73
+ new Uint8Array()
74
+ )
75
+ ).toBe(true);
76
+ });
77
+
78
+ it('unwraps when no wrapping is detected', (): void => {
79
+ expect(
80
+ u8aToString(
81
+ unwrapBytes(TEST_DATA)
82
+ )
83
+ ).toEqual(TEST_DATA);
84
+ });
85
+
86
+ it('unwraps when no wrapping is detected (only start)', (): void => {
87
+ expect(
88
+ u8aToString(
89
+ unwrapBytes(TEST_WARP_HALF_PRE)
90
+ )
91
+ ).toEqual(TEST_WARP_HALF_PRE);
92
+ });
93
+
94
+ it('unwraps when no wrapping is detected (only end)', (): void => {
95
+ expect(
96
+ u8aToString(
97
+ unwrapBytes(TEST_WRAP_HALF_POST)
98
+ )
99
+ ).toEqual(TEST_WRAP_HALF_POST);
100
+ });
101
+
102
+ it('unwraps when a wrap is present', (): void => {
103
+ expect(
104
+ u8aToString(
105
+ unwrapBytes(TEST_WRAP_FULL)
106
+ )
107
+ ).toEqual(TEST_DATA);
108
+ });
109
+
110
+ it('unwraps when a an empty wrap is present', (): void => {
111
+ expect(
112
+ u8aToString(
113
+ unwrapBytes(TEST_WRAP_EMPTY)
114
+ )
115
+ ).toEqual('');
116
+ });
117
+
118
+ describe('Ethereum-style', (): void => {
119
+ it('does not wrap an Ethereum wrap', (): void => {
120
+ expect(
121
+ u8aEq(
122
+ wrapBytes(TEST_ETH),
123
+ TEST_ETH
124
+ )
125
+ ).toBe(true);
126
+ });
127
+
128
+ it('does not unwrap an Ethereum wrap', (): void => {
129
+ expect(
130
+ u8aEq(
131
+ unwrapBytes(TEST_ETH),
132
+ TEST_ETH
133
+ )
134
+ ).toBe(true);
135
+ });
136
+ });
137
+ });
@@ -1,7 +1,12 @@
1
- import { U8A_WRAP_ETHEREUM, U8A_WRAP_POSTFIX, U8A_WRAP_PREFIX, u8aIsWrapped, u8aUnwrapBytes, u8aWrapBytes } from '@polkadot/util';
1
+ // Copyright 2019-2025 @pezkuwi/extension authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { U8A_WRAP_ETHEREUM, U8A_WRAP_POSTFIX, U8A_WRAP_PREFIX, u8aIsWrapped, u8aUnwrapBytes, u8aWrapBytes } from '@pezkuwi/util';
5
+
2
6
  export const ETHEREUM = U8A_WRAP_ETHEREUM;
3
7
  export const POSTFIX = U8A_WRAP_POSTFIX;
4
8
  export const PREFIX = U8A_WRAP_PREFIX;
9
+
5
10
  export const isWrapped = u8aIsWrapped;
6
11
  export const unwrapBytes = u8aUnwrapBytes;
7
12
  export const wrapBytes = u8aWrapBytes;
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "baseUrl": "..",
5
+ "outDir": "./build",
6
+ "rootDir": "./src"
7
+ },
8
+ "exclude": [
9
+ "**/*.spec.ts"
10
+ ],
11
+ "references": [
12
+ { "path": "../extension-inject/tsconfig.build.json" }
13
+ ]
14
+ }