ethers.js icon indicating copy to clipboard operation
ethers.js copied to clipboard

NonceManager store

Open kraikov opened this issue 5 months ago • 7 comments

Describe the Feature

Currently the NonceManager handles the nonce management in memory. Would you accept a PR to allow different stores for the nonce? For example Redis. This would help to manage the nonce in horizontal scaling. Right now that's not possible.

Code Example

export class NonceManager extends AbstractSigner {

    constructor(signer: Signer, store?: Store) {
       ...
    }
}

export type Store = {
  get<T>(key: string): Promise<T | undefined>;
  set<T>(key: string, data: T): Promise<void>;
};

kraikov avatar Feb 12 '24 07:02 kraikov

I probably won’t accept a Redis-specific it into the core, but it is certainly a valid candidate for an ancillary package (in the @ethers-ext/ org) or I could link to it from the docs.

There are a lot of extras I’d love to add to the NonceManager. I’ve messed around with a few API options, but the goal is to be able to create a dependency tree of transactions and to have an option to specify recovery actions if a transaction fails.

Something generic like this might be acceptable though. I will mull it over today. :)

ricmoo avatar Feb 13 '24 16:02 ricmoo

I have a Nonce Manager I made that I think will benefit a lot of people. I'll get it prepped and make the repo public. In the future I will be reworking the code to support node graphs, but i'll share the repo later today.

niZmosis avatar Feb 13 '24 16:02 niZmosis

@ricmoo my idea was to create it with a generic store, which could be injected to the NonceManager. For example the store could be in-memory (like at the moment) or Redis or any other persistent storage. Does this make sense?

In the first case (in-memory) you'd a store looking like this:

class InMemoryStore {
   private delta = 0;

   public increment() {
      delta++;
   }

   reset() {
      delta=0;
   }
}

in the other case you'd have

class RedisStore {
   private redis: Redis;
   private key = `nonce-manager-${address}`

   public increment() {
      await this.redis.client.incr(this.key);
   }

   reset() {
      await this.redis.client.set(this.key, 0);
   }
}

one way to handle failed transactions broadcast is to call reset or decrement the delta

kraikov avatar Feb 13 '24 21:02 kraikov

My nonce manager is designed to take in any kind of store. Zustand, redis, redux, mongodb, you just pass the store to the nonce manager. I am trying to get it to a point to make it public. I do use it on my dApp already and has been solid. I also have a Provider Manager to with load balancing and health tracking per node. I have the stores made for all those mentioned. This isn't a little file, it's a library. What are you using v5 or v6?

niZmosis avatar Feb 14 '24 00:02 niZmosis

@niZmosis that's sounds really nice and useful! I'm using v6

kraikov avatar Feb 15 '24 08:02 kraikov