Skip to content

Commit

Permalink
feat: providers loading ready
Browse files Browse the repository at this point in the history
  • Loading branch information
vvava committed Aug 6, 2024
1 parent 4fd8b31 commit d8c204f
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 42 deletions.
36 changes: 31 additions & 5 deletions src/background/providers/ChainAgnosticProvider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import EventEmitter from 'events';
import {
DAppProviderRequest,
JsonRpcRequest,
JsonRpcRequestPayload,
} from '../connections/dAppConnection/models';
Expand All @@ -8,9 +9,18 @@ import { ethErrors, serializeError } from 'eth-rpc-errors';
import AbstractConnection from '../utils/messaging/AbstractConnection';
import { ChainId } from '@avalabs/core-chains-sdk';
import RequestRatelimiter from './utils/RequestRatelimiter';
import {
InitializationStep,
ProviderReadyPromise,
} from './utils/ProviderReadyPromise';
import onDomReady from './utils/onDomReady';
import { getSiteMetadata } from './utils/getSiteMetadata';

export class ChainAgnosticProvider extends EventEmitter {
#contentScriptConnection: AbstractConnection;
#providerReadyPromise = new ProviderReadyPromise([
InitializationStep.DOMAIN_METADATA_SENT,
]);

#requestRateLimiter = new RequestRatelimiter([
'eth_requestAccounts',
Expand All @@ -19,12 +29,26 @@ export class ChainAgnosticProvider extends EventEmitter {

constructor(connection) {
super();
connection.connect();
this.#contentScriptConnection = connection;
this.#init();
}

async #init() {
await this.#contentScriptConnection.connect();

onDomReady(async () => {
const domainMetadata = await getSiteMetadata();

this.#request({
data: {
method: DAppProviderRequest.DOMAIN_METADATA_METHOD,
params: domainMetadata,
},
});

this.#providerReadyPromise.check(InitializationStep.DOMAIN_METADATA_SENT);
});
}

#request = async ({
Expand Down Expand Up @@ -73,12 +97,14 @@ export class ChainAgnosticProvider extends EventEmitter {
chainId,
}: {
data: PartialBy<JsonRpcRequestPayload, 'id' | 'params'>;
sessionId: string;
chainId: string | null;
sessionId?: string;
chainId?: string | null;
}) => {
return this.#requestRateLimiter.call(data.method, () =>
this.#request({ data, chainId, sessionId })
);
return this.#providerReadyPromise.call(() => {
return this.#requestRateLimiter.call(data.method, () =>
this.#request({ data, chainId, sessionId })
);
});
};

subscribeToMessage = (callback) => {
Expand Down
30 changes: 4 additions & 26 deletions src/background/providers/CoreProvider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { ethErrors } from 'eth-rpc-errors';
import EventEmitter from 'events';
import { getSiteMetadata } from './utils/getSiteMetadata';
import onDomReady from './utils/onDomReady';
import {
EventNames,
type AccountsChangedEventData,
Expand All @@ -18,7 +16,6 @@ import {
} from '../connections/dAppConnection/models';
import type { PartialBy, ProviderInfo } from '../models';
import { ChainAgnosticProvider } from './ChainAgnosticProvider';
import AbstractConnection from '../utils/messaging/AbstractConnection';

interface ProviderState {
accounts: string[] | null;
Expand All @@ -28,8 +25,9 @@ interface ProviderState {
}

export class CoreProvider extends EventEmitter {
#providerReadyPromise = new ProviderReadyPromise();
#contentScriptConnection: AbstractConnection;
#providerReadyPromise = new ProviderReadyPromise([
InitializationStep.PROVIDER_STATE_LOADED,
]);
#chainagnosticProvider?: ChainAgnosticProvider;

readonly info: ProviderInfo = {
Expand Down Expand Up @@ -68,16 +66,9 @@ export class CoreProvider extends EventEmitter {
isUnlocked: () => Promise.resolve(this._isUnlocked),
};

constructor({
connection,
maxListeners = 100,
}: {
connection: AbstractConnection;
maxListeners?: number;
}) {
constructor({ maxListeners = 100 }: { maxListeners?: number }) {
super();
this.setMaxListeners(maxListeners);
this.#contentScriptConnection = connection;
this.#subscribe();
}

Expand All @@ -104,19 +95,6 @@ export class CoreProvider extends EventEmitter {
* Initializes provider state, and collects dApp information
*/
#init = async () => {
await this.#contentScriptConnection.connect();

onDomReady(async () => {
const domainMetadata = await getSiteMetadata();

this.#request({
method: DAppProviderRequest.DOMAIN_METADATA_METHOD,
params: domainMetadata,
});

this.#providerReadyPromise.check(InitializationStep.DOMAIN_METADATA_SENT);
});

try {
const response = await this.#request({
method: DAppProviderRequest.INIT_DAPP_STATE,
Expand Down
2 changes: 1 addition & 1 deletion src/background/providers/initializeInpageProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function initializeProvider(
}
);

const provider = new Proxy(new CoreProvider({ maxListeners, connection }), {
const provider = new Proxy(new CoreProvider({ maxListeners }), {
// some common libraries, e.g. [email protected], mess with our API
deleteProperty: () => true,
});
Expand Down
25 changes: 15 additions & 10 deletions src/background/providers/utils/ProviderReadyPromise.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
export enum InitializationStep {
DOMAIN_METADATA_SENT,
PROVIDER_STATE_LOADED,
DOMAIN_METADATA_SENT = 'domain_metadata_sent',
PROVIDER_STATE_LOADED = 'provider_state_loaded',
}

export class ProviderReadyPromise {
#steps: boolean[] = [];
#unpreparedSteps: InitializationStep[] = [];
#inflightRequests: {
resolve(value: unknown): void;
fn(): Promise<any>;
}[] = [];

constructor() {
// length / 2 is required since InitializationStep is an enum
// enums generate objects like this: { key0: 0, key1: 1, 0: key0, 1: key1 }
this.#steps = Array(Object.keys(InitializationStep).length / 2).fill(false);
constructor(steps: InitializationStep[]) {
this.#unpreparedSteps = Object.values(steps);
}

check = (step: InitializationStep) => {
this.#steps[step] = true;
const stepIndex = this.#unpreparedSteps.findIndex(
(currentStep) => step === currentStep
);

if (stepIndex > -1) {
this.#unpreparedSteps.splice(stepIndex, 1);
}

this._proceed();
};

uncheck = (step: InitializationStep) => {
this.#steps[step] = false;
this.#unpreparedSteps[step] = step;
};

private _proceed = () => {
if (this.#steps.some((step) => !step)) {
if (this.#unpreparedSteps.length) {
return;
}

Expand Down

0 comments on commit d8c204f

Please sign in to comment.