0

What the best practice is in terms of using the ethers provider as either a singleton (or persistent instance) in a module or to create a new instance every time a request is made. For example, for a provider that is listening for block events with something like provider.on('block', async (blockNumber) => { it makes sense to use a single instance like the following:

import { ethers } from 'ethers';
import { NETWORK_ADDRESS, NETWORK_ADDRESS_WS } from './config';
import logger from './logger';

// Create the new provider
const provider = new ethers.providers.JsonRpcProvider(NETWORK_ADDRESS);

/**
 * Get the network provider
 * @return {ethers.provider} network provider
 */
export const getNetworkProvider = () => {
  return provider;
};

But let's say that our application also has an express endpoint that can query balances through something like await provider.getBalance(address); or sign and send a transaction with a signer like:

const provider = getNetworkProvider();
const wallet = new ethers.Wallet(senderPrivateKey, provider);
const tx = {
  ...
};
// Send transaction
return wallet.sendTransaction(tx);

Is it easier/harder to have create a new instance of the provider for these operations with const provider = new ethers.providers.JsonRpcProvider(NETWORK_ADDRESS); or use the ethers.getDefaultProvider(NETWORK_ADDRESS); each time?

It seems like that in situations where using you're performing event monitoring in the blockchain for blocks, transactions and contracts, the single persistent connection via one provider is the right answer. So, for the other types of queries should I be creating new instance of a provider each time? Or is there some magic inside the provider that is occurring where what I'm describing doesn't matter and everything can be done through one provider regardless?

Also, does the ethers.getDefaultProvider(NETWORK_ADDRESS) function return a new instance provider each time it is called or a shared instance? e.g.

const myProvider = ethers.getDefaultProvider(NETWORK_ADDRESS);

1 Answer 1

0

In your express router, you must be having a scope.

route(async() => {
  const provider = getDefaultProvider(); // memory is allocated
  const wallet = new ethers.Wallet(senderPrivateKey, provider); // memory is allocated
  const tx = {
    ...
  };
  // Send transaction
  return wallet.sendTransaction(tx);
  // memory is freed
})

When the scope is closed, the garbage collector does a great job at clearing the memory. If you are having a small to medium traffic application, this doesn't really matter performance-wise.

However, if your application is expected to be overloaded with requests, then your app would continuously keep adding memory. This would just increase garbage collector activity to free the memory, which can consume some performance that would otherwise be used to process requests. To reduce that, you might want to reuse the provider across requests, by declaring it above.

const provider = getDefaultProvider(); // memory allocated once

route(async() => {
  const wallet = new ethers.Wallet(senderPrivateKey, provider); // memory is allocated
  const tx = {
    ...
  };
  // Send transaction
  return wallet.sendTransaction(tx);
})

Also, does the ethers.getDefaultProvider(NETWORK_ADDRESS) function return a new instance provider each time it is called or a shared instance? e.g.

the constructor is called, hence it returns a new instance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.