{"version":3,"sources":["../../../src/internal/request-batcher.ts","../../../src/api/alchemy-provider.ts"],"names":["RequestBatcher","sendBatchFn","maxBatchSize","request","inflightRequest","resolve","undefined","reject","promise","Promise","this","pendingBatch","push","length","sendBatchRequest","pendingBatchTimer","setTimeout","batch","clearTimeout","map","inflight","then","result","forEach","index","payload","error","Error","message","code","data","AlchemyProvider","config","apiKey","getApiKey","alchemyNetwork","getAlchemyNetwork","network","connection","getAlchemyConnectionInfo","url","throttleLimit","maxRetries","ethersNetwork","batchRequests","batcherConnection","headers","batcher","requests","JSON","stringify","detectNetwork","method","params","_send","methodName","forceBatch","id","_nextId","jsonrpc","enqueueRequest","emit","action","provider","cache","indexOf","_cache","getResult","response","getNetworkFromEthers","Object","values","includes","join","type","allowGzip"],"mappings":"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","file":"static/js/72.0833ade8.chunk.js","sourcesContent":["import { JsonRpcRequest, JsonRpcResponse } from './internal-types';\n\n/** Maximum size of a batch on the rpc provider. */\nconst DEFAULT_MAX_REQUEST_BATCH_SIZE = 100;\n\n/** Timeout interval before the pending batch is sent. */\nconst DEFAULT_REQUEST_BATCH_DELAY_MS = 10;\n\n/**\n * Internal class to enqueue requests and automatically send/process batches.\n *\n * The underlying batching mechanism is loosely based on ethers.js's\n * `JsonRpcBatchProvider`.\n *\n * @internal\n */\nexport class RequestBatcher {\n /** Timeout timer that periodically sends the pending batch. */\n private pendingBatchTimer: NodeJS.Timer | undefined;\n\n /**\n * Array of enqueued requests along with the constructed promise handlers for\n * each request.\n */\n private pendingBatch: Array = [];\n\n constructor(\n private readonly sendBatchFn: SendBatchFn,\n private readonly maxBatchSize = DEFAULT_MAX_REQUEST_BATCH_SIZE\n ) {}\n\n /**\n * Enqueues the provided request. The batch is immediately sent if the maximum\n * batch size is reached. Otherwise, the request is enqueued onto a batch that\n * is sent after 10ms.\n *\n * Returns a promise that resolves with the result of the request.\n */\n async enqueueRequest(request: JsonRpcRequest): Promise {\n const inflightRequest: BatchRequest = {\n request,\n resolve: undefined,\n reject: undefined\n };\n\n const promise = new Promise((resolve, reject) => {\n inflightRequest.resolve = resolve;\n inflightRequest.reject = reject;\n });\n\n this.pendingBatch.push(inflightRequest);\n\n if (this.pendingBatch.length === this.maxBatchSize) {\n // Send batch immediately if we are at the maximum batch size.\n void this.sendBatchRequest();\n } else if (!this.pendingBatchTimer) {\n // Schedule batch for next event loop + short duration\n this.pendingBatchTimer = setTimeout(\n () => this.sendBatchRequest(),\n DEFAULT_REQUEST_BATCH_DELAY_MS\n );\n }\n\n return promise;\n }\n\n /**\n * Sends the currently queued batches and resets the batch and timer. Processes\n * the batched response results back to the original promises.\n */\n private async sendBatchRequest(): Promise {\n // Get the current batch and clear it, so new requests\n // go into the next batch\n const batch = this.pendingBatch;\n this.pendingBatch = [];\n if (this.pendingBatchTimer) {\n clearTimeout(this.pendingBatchTimer);\n this.pendingBatchTimer = undefined;\n }\n\n // Get the request as an array of requests\n const request = batch.map(inflight => inflight.request);\n\n return this.sendBatchFn(request).then(\n result => {\n // For each result, feed it to the correct Promise, depending\n // on whether it was a success or error\n batch.forEach((inflightRequest, index) => {\n const payload = result[index];\n if (payload.error) {\n const error = new Error(payload.error.message);\n (error as any).code = payload.error.code;\n (error as any).data = payload.error.data;\n inflightRequest.reject!(error);\n } else {\n inflightRequest.resolve!(payload.result);\n }\n });\n },\n error => {\n batch.forEach(inflightRequest => {\n inflightRequest.reject!(error);\n });\n }\n );\n }\n}\n\n/** Function type to match the `fetchJson` function in ethers. */\ntype SendBatchFn = (reqs: JsonRpcRequest[]) => Promise;\n\n/**\n * Internal interface to represent a request on a batch along with the promises to resolve it.\n */\ninterface BatchRequest {\n request: JsonRpcRequest;\n resolve?: (result: any) => void;\n reject?: (error: Error) => void;\n}\n","import {\n Network as NetworkFromEthers,\n Networkish,\n getNetwork as getNetworkFromEthers\n} from '@ethersproject/networks';\nimport { deepCopy } from '@ethersproject/properties';\nimport {\n CommunityResourcable,\n JsonRpcProvider\n} from '@ethersproject/providers';\nimport { ConnectionInfo, fetchJson } from '@ethersproject/web';\n\nimport { JsonRpcRequest, JsonRpcResponse } from '../internal/internal-types';\nimport { RequestBatcher } from '../internal/request-batcher';\nimport { Network } from '../types/types';\nimport {\n CustomNetworks,\n DEFAULT_ALCHEMY_API_KEY,\n DEFAULT_NETWORK,\n EthersNetwork,\n getAlchemyHttpUrl,\n getAlchemyWsUrl\n} from '../util/const';\nimport { logWarn } from '../util/logger';\nimport { IS_BROWSER } from '../util/util';\nimport { VERSION } from '../version';\nimport { AlchemyConfig } from './alchemy-config';\n\n/**\n * SDK's custom implementation of ethers.js's 'AlchemyProvider'.\n *\n * Do not call this constructor directly. Instead, instantiate an instance of\n * {@link Alchemy} and call {@link Alchemy.config.getProvider()}.\n *\n * @public\n */\nexport class AlchemyProvider\n extends JsonRpcProvider\n implements CommunityResourcable\n{\n readonly apiKey: string;\n readonly maxRetries: number;\n readonly batchRequests: boolean;\n\n /**\n * VISIBLE ONLY FOR TESTING\n *@internal\n */\n readonly batcher: RequestBatcher;\n\n /** @internal */\n constructor(config: AlchemyConfig) {\n // Normalize the API Key to a string.\n const apiKey = AlchemyProvider.getApiKey(config.apiKey);\n\n // Generate our own connection info with the correct endpoint URLs.\n const alchemyNetwork = AlchemyProvider.getAlchemyNetwork(config.network);\n const connection = AlchemyProvider.getAlchemyConnectionInfo(\n alchemyNetwork,\n apiKey,\n 'http'\n );\n\n // If a hardcoded url was specified in the config, use that instead of the\n // provided apiKey or network.\n if (config.url !== undefined) {\n connection.url = config.url;\n }\n\n connection.throttleLimit = config.maxRetries;\n\n // Normalize the Alchemy named network input to the network names used by\n // ethers. This allows the parent super constructor in JsonRpcProvider to\n // correctly set the network.\n const ethersNetwork = EthersNetwork[alchemyNetwork];\n super(connection, ethersNetwork);\n\n this.apiKey = config.apiKey;\n this.maxRetries = config.maxRetries;\n this.batchRequests = config.batchRequests;\n\n // TODO: support individual headers when calling batch\n const batcherConnection = {\n ...this.connection,\n headers: {\n ...this.connection.headers,\n 'Alchemy-Ethers-Sdk-Method': 'batchSend'\n }\n };\n const sendBatchFn = (\n requests: JsonRpcRequest[]\n ): Promise => {\n return fetchJson(batcherConnection, JSON.stringify(requests));\n };\n this.batcher = new RequestBatcher(sendBatchFn);\n }\n\n /**\n * Overrides the `UrlJsonRpcProvider.getApiKey` method as implemented by\n * ethers.js. Returns the API key for an Alchemy provider.\n *\n * @internal\n * @override\n */\n static getApiKey(apiKey: any): string {\n if (apiKey == null) {\n return DEFAULT_ALCHEMY_API_KEY;\n }\n if (apiKey && typeof apiKey !== 'string') {\n throw new Error(\n `Invalid apiKey '${apiKey}' provided. apiKey must be a string.`\n );\n }\n return apiKey;\n }\n\n /**\n * Overrides the `BaseProvider.getNetwork` method as implemented by ethers.js.\n *\n * This override allows the SDK to set the provider's network to values not\n * yet supported by ethers.js.\n *\n * @internal\n * @override\n */\n static getNetwork(network: Networkish): NetworkFromEthers {\n if (typeof network === 'string' && network in CustomNetworks) {\n return CustomNetworks[network];\n }\n\n // Call the standard ethers.js getNetwork method for other networks.\n return getNetworkFromEthers(network);\n }\n\n /**\n * Converts the `Networkish` input to the network enum used by Alchemy.\n *\n * @internal\n */\n static getAlchemyNetwork(network?: Networkish): Network {\n if (network === undefined) {\n return DEFAULT_NETWORK;\n }\n\n if (typeof network === 'number') {\n throw new Error(\n `Invalid network '${network}' provided. Network must be a string.`\n );\n }\n\n // Guaranteed that `typeof network === 'string`.\n const isValidNetwork = Object.values(Network).includes(network as Network);\n if (!isValidNetwork) {\n throw new Error(\n `Invalid network '${network}' provided. Network must be one of: ` +\n `${Object.values(Network).join(', ')}.`\n );\n }\n return network as Network;\n }\n\n /**\n * Returns a {@link ConnectionInfo} object compatible with ethers that contains\n * the correct URLs for Alchemy.\n *\n * @internal\n */\n static getAlchemyConnectionInfo(\n network: Network,\n apiKey: string,\n type: 'wss' | 'http'\n ): ConnectionInfo {\n const url =\n type === 'http'\n ? getAlchemyHttpUrl(network, apiKey)\n : getAlchemyWsUrl(network, apiKey);\n return {\n headers: IS_BROWSER\n ? {\n 'Alchemy-Ethers-Sdk-Version': VERSION\n }\n : {\n 'Alchemy-Ethers-Sdk-Version': VERSION,\n 'Accept-Encoding': 'gzip'\n },\n allowGzip: true,\n url\n };\n }\n\n /**\n * Overrides the method in ethers.js's `StaticJsonRpcProvider` class. This\n * method is called when calling methods on the parent class `BaseProvider`.\n *\n * @override\n */\n async detectNetwork(): Promise {\n let network = this.network;\n if (network == null) {\n network = await super.detectNetwork();\n\n if (!network) {\n throw new Error('No network detected');\n }\n }\n return network;\n }\n\n _startPending(): void {\n logWarn('WARNING: Alchemy Provider does not support pending filters');\n }\n\n /**\n * Overrides the ether's `isCommunityResource()` method. Returns true if the\n * current api key is the default key.\n *\n * @override\n */\n isCommunityResource(): boolean {\n return this.apiKey === DEFAULT_ALCHEMY_API_KEY;\n }\n\n /**\n * Overrides the base {@link JsonRpcProvider.send} method to implement custom\n * logic for sending requests to Alchemy.\n *\n * @param method The method name to use for the request.\n * @param params The parameters to use for the request.\n * @override\n * @public\n */\n // TODO: Add headers for `perform()` override.\n send(method: string, params: Array): Promise {\n return this._send(method, params, 'send');\n }\n\n /**\n * DO NOT MODIFY.\n *\n * Original code copied over from ether.js's `JsonRpcProvider.send()`.\n *\n * This method is copied over directly in order to implement custom headers\n *\n * @internal\n */\n _send(\n method: string,\n params: Array,\n methodName: string,\n forceBatch = false\n ): Promise {\n const request = {\n method,\n params,\n id: this._nextId++,\n jsonrpc: '2.0'\n };\n\n // START MODIFIED CODE\n const connection = { ...this.connection };\n connection.headers!['Alchemy-Ethers-Sdk-Method'] = methodName;\n\n if (this.batchRequests || forceBatch) {\n return this.batcher.enqueueRequest(request as JsonRpcRequest);\n }\n // END MODIFIED CODE\n\n this.emit('debug', {\n action: 'request',\n request: deepCopy(request),\n provider: this\n });\n\n // We can expand this in the future to any call, but for now these\n // are the biggest wins and do not require any serializing parameters.\n const cache = ['eth_chainId', 'eth_blockNumber'].indexOf(method) >= 0;\n if (cache && this._cache[method]) {\n return this._cache[method];\n }\n\n const result = fetchJson(\n this.connection,\n JSON.stringify(request),\n getResult\n ).then(\n result => {\n this.emit('debug', {\n action: 'response',\n request,\n response: result,\n provider: this\n });\n\n return result;\n },\n error => {\n this.emit('debug', {\n action: 'response',\n error,\n request,\n provider: this\n });\n\n throw error;\n }\n );\n\n // Cache the fetch, but clear it on the next event loop\n if (cache) {\n this._cache[method] = result;\n setTimeout(() => {\n // @ts-ignore - This is done by ethers.\n this._cache[method] = null;\n }, 0);\n }\n\n return result;\n }\n}\n\n/**\n * DO NOT MODIFY.\n *\n * Original code copied over from ether.js's\n * `@ethersproject/web/src.ts/index.ts`. Used to support\n * {@link AlchemyProvider._send}, which is also copied over.\n */\nfunction getResult(payload: {\n error?: { code?: number; data?: any; message?: string };\n result?: any;\n}): any {\n if (payload.error) {\n const error: any = new Error(payload.error.message);\n error.code = payload.error.code;\n error.data = payload.error.data;\n throw error;\n }\n\n return payload.result;\n}\n"],"sourceRoot":""}