Explicit connect for light providers (#7230)
* Explicit connect for light providers * Bump deps * Adjust with explicit connect * Remove old @substrate/connect * Mock substrate-connect
This commit is contained in:
parent
ac75650d95
commit
1fb827ec34
@ -2,14 +2,17 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
import './substrate-connect';
|
||||
|
||||
import { configure } from '@testing-library/dom';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line no-global-assign
|
||||
CSS = { supports (): boolean {
|
||||
return false;
|
||||
} };
|
||||
CSS = {
|
||||
supports (): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
configure({ asyncUtilTimeout: 10000 });
|
||||
|
||||
114
jest/substrate-connect.ts
Normal file
114
jest/substrate-connect.ts
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright 2017-2022 @polkadot/apps authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// duplicated from https://github.com/polkadot-js/api/blob/bb75b422730d6bf5189a87e9b7bbd1585bd5cd26/packages/rpc-provider/src/substrate-connect/ScProvider.spec.ts#L1
|
||||
|
||||
import type { Chain, JsonRpcCallback } from '@substrate/connect';
|
||||
|
||||
type MockChain = Chain & {
|
||||
_spec: () => string
|
||||
_recevedRequests: () => string[]
|
||||
_isTerminated: () => boolean
|
||||
_triggerCallback: (response: string | {}) => void
|
||||
_setTerminateInterceptor: (fn: () => void) => void
|
||||
_setSendJsonRpcInterceptor: (fn: (rpc: string) => void) => void
|
||||
_getLatestRequest: () => string
|
||||
}
|
||||
|
||||
const getFakeChain = (spec: string, callback: JsonRpcCallback): MockChain => {
|
||||
const _receivedRequests: string[] = [];
|
||||
let _isTerminated = false;
|
||||
|
||||
let terminateInterceptor: Function = Function.prototype;
|
||||
let sendJsonRpcInterceptor: Function = Function.prototype;
|
||||
|
||||
return {
|
||||
_spec: () => spec,
|
||||
_recevedRequests: () => _receivedRequests,
|
||||
_isTerminated: () => _isTerminated,
|
||||
_triggerCallback: (response) => {
|
||||
callback(
|
||||
typeof response === 'string' ? response : JSON.stringify(response)
|
||||
);
|
||||
},
|
||||
_setSendJsonRpcInterceptor: (fn) => {
|
||||
sendJsonRpcInterceptor = fn;
|
||||
},
|
||||
_setTerminateInterceptor: (fn) => {
|
||||
terminateInterceptor = fn;
|
||||
},
|
||||
sendJsonRpc: (rpc) => {
|
||||
sendJsonRpcInterceptor(rpc);
|
||||
_receivedRequests.push(rpc);
|
||||
},
|
||||
remove: () => {
|
||||
terminateInterceptor();
|
||||
_isTerminated = true;
|
||||
},
|
||||
_getLatestRequest: () => _receivedRequests[_receivedRequests.length - 1]
|
||||
};
|
||||
};
|
||||
|
||||
const getFakeClient = () => {
|
||||
const chains: MockChain[] = [];
|
||||
let addChainInterceptor: Promise<void> = Promise.resolve();
|
||||
let addWellKnownChainInterceptor: Promise<void> = Promise.resolve();
|
||||
|
||||
return {
|
||||
_chains: () => chains,
|
||||
_setAddChainInterceptor: (interceptor: Promise<void>) => {
|
||||
addChainInterceptor = interceptor;
|
||||
},
|
||||
_setAddWellKnownChainInterceptor: (interceptor: Promise<void>) => {
|
||||
addWellKnownChainInterceptor = interceptor;
|
||||
},
|
||||
addChain: (chainSpec: string, cb: JsonRpcCallback): Promise<MockChain> =>
|
||||
addChainInterceptor.then(() => {
|
||||
const result = getFakeChain(chainSpec, cb);
|
||||
|
||||
chains.push(result);
|
||||
|
||||
return result;
|
||||
}),
|
||||
addWellKnownChain: (
|
||||
wellKnownChain: string,
|
||||
cb: JsonRpcCallback
|
||||
): Promise<MockChain> =>
|
||||
addWellKnownChainInterceptor.then(() => {
|
||||
const result = getFakeChain(wellKnownChain, cb);
|
||||
|
||||
chains.push(result);
|
||||
|
||||
return result;
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
enum WellKnownChain {
|
||||
polkadot = 'polkadot',
|
||||
ksmcc3 = 'ksmcc3',
|
||||
rococo_v2_1 = 'rococo_v2_1',
|
||||
westend2 = 'westend2'
|
||||
}
|
||||
|
||||
const connectorFactory = () => {
|
||||
const clients: ReturnType<typeof getFakeClient>[] = [];
|
||||
const latestClient = () => clients[clients.length - 1];
|
||||
|
||||
return {
|
||||
WellKnownChain,
|
||||
createScClient: () => {
|
||||
const result = getFakeClient();
|
||||
|
||||
clients.push(result);
|
||||
|
||||
return result;
|
||||
},
|
||||
_clients: () => clients,
|
||||
latestClient,
|
||||
latestChain: () =>
|
||||
latestClient()._chains()[latestClient()._chains().length - 1]
|
||||
};
|
||||
};
|
||||
|
||||
jest.mock('@substrate/connect', () => connectorFactory());
|
||||
@ -17,7 +17,7 @@
|
||||
"@polkadot/api": "^7.14.2",
|
||||
"@polkadot/extension-compat-metamask": "^0.42.10-4",
|
||||
"@polkadot/extension-dapp": "^0.42.10-4",
|
||||
"@substrate/connect": "^0.5.0",
|
||||
"@polkadot/rpc-provider": "^7.14.2",
|
||||
"fflate": "^0.7.3",
|
||||
"rxjs": "^7.5.5"
|
||||
}
|
||||
|
||||
@ -1,15 +1,13 @@
|
||||
// Copyright 2017-2022 @polkadot/react-api authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { SupportedChains } from '@substrate/connect';
|
||||
import type { LinkOption } from '@polkadot/apps-config/endpoints/types';
|
||||
import type { InjectedExtension } from '@polkadot/extension-inject/types';
|
||||
import type { ChainProperties, ChainType } from '@polkadot/types/interfaces';
|
||||
import type { KeyringStore } from '@polkadot/ui-keyring/types';
|
||||
import type { ApiProps, ApiState } from './types';
|
||||
|
||||
import { ScProvider } from '@substrate/connect';
|
||||
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import store from 'store';
|
||||
|
||||
import { ApiPromise, WsProvider } from '@polkadot/api';
|
||||
@ -20,6 +18,7 @@ import { TokenUnit } from '@polkadot/react-components/InputNumber';
|
||||
import { StatusContext } from '@polkadot/react-components/Status';
|
||||
import { useApiUrl, useEndpoint } from '@polkadot/react-hooks';
|
||||
import ApiSigner from '@polkadot/react-signer/signers/ApiSigner';
|
||||
import { ScProvider } from '@polkadot/rpc-provider/substrate-connect';
|
||||
import { keyring } from '@polkadot/ui-keyring';
|
||||
import { settings } from '@polkadot/ui-settings';
|
||||
import { formatBalance, isNumber, isTestChain, objectSpread, stringify } from '@polkadot/util';
|
||||
@ -184,6 +183,31 @@ async function loadOnReady (api: ApiPromise, endpoint: LinkOption | null, inject
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async function createApi (apiUrl: string, signer: ApiSigner): Promise<Record<string, Record<string, string>>> {
|
||||
const types = getDevTypes();
|
||||
const isLight = apiUrl.startsWith('light://');
|
||||
const provider = isLight
|
||||
? new ScProvider(apiUrl.replace('light://substrate-connect/', '') as 'polkadot')
|
||||
: new WsProvider(apiUrl);
|
||||
|
||||
api = new ApiPromise({
|
||||
provider,
|
||||
registry,
|
||||
signer,
|
||||
types,
|
||||
typesBundle,
|
||||
typesChain
|
||||
});
|
||||
|
||||
// See https://github.com/polkadot-js/api/pull/4672#issuecomment-1078843960
|
||||
if (isLight) {
|
||||
await provider.connect();
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
function Api ({ apiUrl, children, isElectron, store }: Props): React.ReactElement<Props> | null {
|
||||
const { queuePayload, queueSetTxStatus } = useContext(StatusContext);
|
||||
const [state, setState] = useState<ApiState>({ hasInjectedAccounts: false, isApiReady: false } as unknown as ApiState);
|
||||
@ -199,48 +223,43 @@ function Api ({ apiUrl, children, isElectron, store }: Props): React.ReactElemen
|
||||
[apiEndpoint]
|
||||
);
|
||||
const apiRelay = useApiUrl(relayUrls);
|
||||
|
||||
const value = useMemo<ApiProps>(
|
||||
() => objectSpread({}, state, { api, apiEndpoint, apiError, apiRelay, apiUrl, extensions, isApiConnected, isApiInitialized, isElectron, isWaitingInjected: !extensions }),
|
||||
[apiError, extensions, isApiConnected, isApiInitialized, isElectron, state, apiEndpoint, apiRelay, apiUrl]
|
||||
);
|
||||
|
||||
const onError = useCallback(
|
||||
(error: unknown): void => {
|
||||
console.error(error);
|
||||
|
||||
setApiError((error as Error).message);
|
||||
},
|
||||
[setApiError]
|
||||
);
|
||||
|
||||
// initial initialization
|
||||
useEffect((): void => {
|
||||
const types = getDevTypes();
|
||||
createApi(apiUrl, new ApiSigner(registry, queuePayload, queueSetTxStatus))
|
||||
.then((types): void => {
|
||||
api.on('connected', () => setIsApiConnected(true));
|
||||
api.on('disconnected', () => setIsApiConnected(false));
|
||||
api.on('error', onError);
|
||||
api.on('ready', (): void => {
|
||||
const injectedPromise = web3Enable('polkadot-js/apps');
|
||||
|
||||
api = new ApiPromise({
|
||||
provider: apiUrl.startsWith('light://')
|
||||
? new ScProvider(apiUrl.replace('light://substrate-connect/', '') as SupportedChains)
|
||||
: new WsProvider(apiUrl),
|
||||
registry,
|
||||
signer: new ApiSigner(registry, queuePayload, queueSetTxStatus),
|
||||
types,
|
||||
typesBundle,
|
||||
typesChain
|
||||
});
|
||||
injectedPromise
|
||||
.then(setExtensions)
|
||||
.catch(console.error);
|
||||
|
||||
api.on('connected', () => setIsApiConnected(true));
|
||||
api.on('disconnected', () => setIsApiConnected(false));
|
||||
api.on('error', (error: Error) => setApiError(error.message));
|
||||
api.on('ready', (): void => {
|
||||
const injectedPromise = web3Enable('polkadot-js/apps');
|
||||
|
||||
injectedPromise
|
||||
.then(setExtensions)
|
||||
.catch(console.error);
|
||||
|
||||
loadOnReady(api, apiEndpoint, injectedPromise, store, types)
|
||||
.then(setState)
|
||||
.catch((error): void => {
|
||||
console.error(error);
|
||||
|
||||
setApiError((error as Error).message);
|
||||
loadOnReady(api, apiEndpoint, injectedPromise, store, types)
|
||||
.then(setState)
|
||||
.catch(onError);
|
||||
});
|
||||
});
|
||||
|
||||
setIsApiInitialized(true);
|
||||
}, [apiEndpoint, apiUrl, queuePayload, queueSetTxStatus, store]);
|
||||
setIsApiInitialized(true);
|
||||
})
|
||||
.catch(onError);
|
||||
}, [apiEndpoint, apiUrl, onError, queuePayload, queueSetTxStatus, store]);
|
||||
|
||||
if (!value.isApiInitialized) {
|
||||
return null;
|
||||
|
||||
25
yarn.lock
25
yarn.lock
@ -3267,7 +3267,7 @@ __metadata:
|
||||
"@polkadot/api": ^7.14.2
|
||||
"@polkadot/extension-compat-metamask": ^0.42.10-4
|
||||
"@polkadot/extension-dapp": ^0.42.10-4
|
||||
"@substrate/connect": ^0.5.0
|
||||
"@polkadot/rpc-provider": ^7.14.2
|
||||
fflate: ^0.7.3
|
||||
rxjs: ^7.5.5
|
||||
languageName: unknown
|
||||
@ -4011,29 +4011,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@substrate/connect@npm:^0.5.0":
|
||||
version: 0.5.0
|
||||
resolution: "@substrate/connect@npm:0.5.0"
|
||||
dependencies:
|
||||
"@polkadot/rpc-provider": ^7.6.1
|
||||
"@substrate/connect-extension-protocol": ^1.0.0
|
||||
"@substrate/smoldot-light": 0.5.18
|
||||
eventemitter3: ^4.0.7
|
||||
checksum: c2d3d4b42e7859ed23aa2af7920c4ac9f97889e02bf0d71a8cf8ad5d38cd76ec64393fbe1b92470f8a2fafccbd1f8dabce776ea206b330e9e9c22b91c1fcd578
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@substrate/smoldot-light@npm:0.5.18":
|
||||
version: 0.5.18
|
||||
resolution: "@substrate/smoldot-light@npm:0.5.18"
|
||||
dependencies:
|
||||
buffer: ^6.0.1
|
||||
pako: ^2.0.4
|
||||
websocket: ^1.0.32
|
||||
checksum: 670dcfeb817aaa8098e8e303bec62d83c184b651ffb892f4b6c0698d51c2f3f1ff7240299590770b76891e89aeecaebaf12c2bfb92aa3d2b579aacae14dc062b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@substrate/smoldot-light@npm:0.6.8":
|
||||
version: 0.6.8
|
||||
resolution: "@substrate/smoldot-light@npm:0.6.8"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user