Repository: ecadlabs/taquito (ts)

Issue:
[RPC] Add a getDelegates endpoint that points to minimal stake params or options
Implement a getDelegates RPC that supports this endpoint with following params:
`/chains/main/blocks/head/context/delegates?active=true&with_minimal_stake=true`

Taquito has a `getDelegates` endpoint but it is required to put a target address.


Either:
1. Implement a new getDelegates method (with a slightly different name) or,
2. Modify the existing getDelegates method

Pull request:
feat: supporting getAllDelegates in rpc
@taquito/rpc support getAllDelegates
closes #2976 

Thank you for your contribution to Taquito.

Before submitting this PR, please make sure:

- [x] Your code builds cleanly without any errors or warnings
- [x] You have run the linter against the changes
- [x] You have added unit tests (if relevant/appropriate)
- [x] You have added integration tests (if relevant/appropriate)
- [x] All public methods or types have TypeDoc coverage with a complete description, and ideally an @example
- [ ] You have added or updated corresponding documentation
- [x] If relevant, you have written a first draft summary describing the change for inclusion in Release Notes. 

In this PR, please also make sure: 

- [ ] You have linked this PR to the issue by putting `closes #TICKETNUMBER` in the description box (when applicable)
- [x] You have added a concise description on your changes
## Release Note Draft Snippet

__If relevant, please write a summary of your change that will be suitable for
inclusion in the Release Notes for the next Taquito release.__


Patch:
diff --git a/packages/taquito-contracts-library/src/rpc-wrapper.ts b/packages/taquito-contracts-library/src/rpc-wrapper.ts
index eb026ade9..2b035d924 100644
--- a/packages/taquito-contracts-library/src/rpc-wrapper.ts
+++ b/packages/taquito-contracts-library/src/rpc-wrapper.ts
@@ -50,6 +50,7 @@ import {
   PendingOperationsQueryArguments,
   RPCSimulateOperationParam,
   AILaunchCycleResponse,
+  AllDelegatesQueryArguments,
 } from '@taquito/rpc';
 import { ContractsLibrary } from './taquito-contracts-library';
 
@@ -174,6 +175,12 @@ export class RpcWrapperContractsLibrary implements RpcClientInterface {
   ): Promise<BigMapResponse> {
     return this.rpc.getBigMapExpr(id, expr, { block });
   }
+  async getAllDelegates(
+    args: AllDelegatesQueryArguments,
+    { block }: RPCOptions = defaultRPCOptions
+  ): Promise<string[]> {
+    return this.rpc.getAllDelegates(args, { block });
+  }
   async getDelegates(
     address: string,
     { block }: RPCOptions = defaultRPCOptions
diff --git a/packages/taquito-local-forging/src/protocols.ts b/packages/taquito-local-forging/src/protocols.ts
index 0c1617c23..57ef66740 100644
--- a/packages/taquito-local-forging/src/protocols.ts
+++ b/packages/taquito-local-forging/src/protocols.ts
@@ -17,6 +17,7 @@ export enum ProtocolsHash {
   PtNairobi = 'PtNairobiyssHuh87hEhfVBGCVrK3WnS8Z2FT4ymB5tAa4r1nQf',
   ProxfordY = 'ProxfordYmVfjWnRcgjWH36fW6PArwqykTFzotUxRs6gmTcZDuH',
   PtParisBQ = 'PtParisBQscdCm6Cfow6ndeU6wKJyA3aV1j4D3gQBQMsTQyJCrz',
+  PtParisBx = 'PtParisBxoLz5gzMmn3d9WBQNoPSZakgnkMC2VNuQ3KXfUtUQeZ',
   ProtoALpha = 'ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK',
 }
 
@@ -39,6 +40,7 @@ const protoLevel: Record<ProtocolsHash, number> = {
   PtNairobiyssHuh87hEhfVBGCVrK3WnS8Z2FT4ymB5tAa4r1nQf: 17,
   ProxfordYmVfjWnRcgjWH36fW6PArwqykTFzotUxRs6gmTcZDuH: 19,
   PtParisBQscdCm6Cfow6ndeU6wKJyA3aV1j4D3gQBQMsTQyJCrz: 20,
+  PtParisBxoLz5gzMmn3d9WBQNoPSZakgnkMC2VNuQ3KXfUtUQeZ: 20,
   ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK: 21,
 };
 
diff --git a/packages/taquito-michel-codec/src/michelson-types.ts b/packages/taquito-michel-codec/src/michelson-types.ts
index c7d4f252b..3e55746ba 100644
--- a/packages/taquito-michel-codec/src/michelson-types.ts
+++ b/packages/taquito-michel-codec/src/michelson-types.ts
@@ -405,6 +405,7 @@ export enum Protocol {
   PtNairobi = 'PtNairobiyssHuh87hEhfVBGCVrK3WnS8Z2FT4ymB5tAa4r1nQf',
   ProxfordY = 'ProxfordYmVfjWnRcgjWH36fW6PArwqykTFzotUxRs6gmTcZDuH',
   PtParisBQ = 'PtParisBQscdCm6Cfow6ndeU6wKJyA3aV1j4D3gQBQMsTQyJCrz',
+  PtParisBx = 'PtParisBxoLz5gzMmn3d9WBQNoPSZakgnkMC2VNuQ3KXfUtUQeZ',
   ProtoALpha = 'ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK', // temporary protocol hash
 }
 
@@ -439,6 +440,7 @@ const protoLevel: Record<ProtocolID, number> = {
   PtNairobiyssHuh87hEhfVBGCVrK3WnS8Z2FT4ymB5tAa4r1nQf: 17,
   ProxfordYmVfjWnRcgjWH36fW6PArwqykTFzotUxRs6gmTcZDuH: 19,
   PtParisBQscdCm6Cfow6ndeU6wKJyA3aV1j4D3gQBQMsTQyJCrz: 20,
+  PtParisBxoLz5gzMmn3d9WBQNoPSZakgnkMC2VNuQ3KXfUtUQeZ: 20,
   ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK: 21,
 };
 
@@ -452,4 +454,4 @@ export function ProtoInferiorTo(a: ProtocolID, b: ProtocolID): boolean {
 
 export interface ProtocolOptions {
   protocol?: ProtocolID;
-}
+}
\ No newline at end of file
diff --git a/packages/taquito-rpc/src/rpc-client-interface.ts b/packages/taquito-rpc/src/rpc-client-interface.ts
index 72e56d5f0..e0d58341c 100644
--- a/packages/taquito-rpc/src/rpc-client-interface.ts
+++ b/packages/taquito-rpc/src/rpc-client-interface.ts
@@ -50,6 +50,7 @@ import {
   PendingOperationsQueryArguments,
   RPCSimulateOperationParam,
   AILaunchCycleResponse,
+  AllDelegatesQueryArguments,
 } from './types';
 
 export interface RPCOptions {
@@ -81,6 +82,7 @@ export interface RpcClientInterface {
   getDelegate(address: string, options?: RPCOptions): Promise<DelegateResponse>;
   getBigMapKey(address: string, key: BigMapKey, options?: RPCOptions): Promise<BigMapGetResponse>;
   getBigMapExpr(id: string, expr: string, options?: RPCOptions): Promise<BigMapResponse>;
+  getAllDelegates(args: AllDelegatesQueryArguments, options?: RPCOptions): Promise<string[]>;
   getDelegates(address: string, options?: RPCOptions): Promise<DelegatesResponse>;
   getVotingInfo(address: string, options?: RPCOptions): Promise<VotingInfoResponse>;
   getConstants(options?: RPCOptions): Promise<ConstantsResponse>;
@@ -161,6 +163,7 @@ export enum RPCMethodName {
   GET_CURRENT_PROPOSAL = 'getCurrentProposal',
   GET_CURRENT_QUORUM = 'getCurrentQuorum',
   GET_DELEGATE = 'getDelegate',
+  GET_ALL_DELEGATES = 'getAllDelegates',
   GET_DELEGATES = 'getDelegates',
   GET_VOTING_INFO = 'getVotingInfo',
   GET_ATTESTATION_RIGHTS = 'getAttestationRights',
diff --git a/packages/taquito-rpc/src/rpc-client-modules/rpc-cache.ts b/packages/taquito-rpc/src/rpc-client-modules/rpc-cache.ts
index 1b4eea2ed..fe28642e2 100644
--- a/packages/taquito-rpc/src/rpc-client-modules/rpc-cache.ts
+++ b/packages/taquito-rpc/src/rpc-client-modules/rpc-cache.ts
@@ -52,6 +52,7 @@ import {
   PendingOperationsV2,
   RPCSimulateOperationParam,
   AILaunchCycleResponse,
+  AllDelegatesQueryArguments,
 } from '../types';
 import { InvalidAddressError, InvalidContractAddressError } from '@taquito/core';
 import {
@@ -73,6 +74,7 @@ type RpcMethodParam =
   | UnparsingMode
   | BigMapKey
   | BakingRightsQueryArguments
+  | AllDelegatesQueryArguments
   | PendingOperationsQueryArguments
   | AttestationRightsQueryArguments;
 
@@ -546,6 +548,29 @@ export class RpcClientCache implements RpcClientInterface {
     }
   }
 
+  /**
+   * @param args contains optional query arguments (active, inactive, with_minimal_stake, without_minimal_stake)
+   * @param options contains generic configuration for rpc calls to specified block (default to head)
+   * @description Lists all registered delegates by default with query arguments to filter unneeded values.
+   * @see https://tezos.gitlab.io/active/rpc.html#get-block-id-context-delegates-pkh
+   */
+  async getAllDelegates(
+    args: AllDelegatesQueryArguments = {},
+    { block }: { block: string } = defaultRPCOptions
+  ): Promise<string[]> {
+    const key = this.formatCacheKey(this.rpcClient.getRpcUrl(), RPCMethodName.GET_ALL_DELEGATES, [
+      block,
+      args,
+    ]);
+    if (this.has(key)) {
+      return this.get(key);
+    } else {
+      const response = this.rpcClient.getAllDelegates(args, { block });
+      this.put(key, response);
+      return response;
+    }
+  }
+
   /**
    * @param address delegate address which we want to retrieve
    * @param options contains generic configuration for rpc calls to specified block (default to head)
diff --git a/packages/taquito-rpc/src/taquito-rpc.ts b/packages/taquito-rpc/src/taquito-rpc.ts
index cb67f9710..fb862c0af 100644
--- a/packages/taquito-rpc/src/taquito-rpc.ts
+++ b/packages/taquito-rpc/src/taquito-rpc.ts
@@ -68,6 +68,7 @@ import {
   PendingOperationsV2,
   RPCSimulateOperationParam,
   AILaunchCycleResponse,
+  AllDelegatesQueryArguments,
 } from './types';
 import { castToBigNumber } from './utils/utils';
 import {
@@ -477,6 +478,23 @@ export class RpcClient implements RpcClientInterface {
     });
   }
 
+  /**
+   * @param args contains optional query arguments (active, inactive, with_minimal_stake, without_minimal_stake)
+   * @param options contains generic configuration for rpc calls to specified block (default to head)
+   * @description Lists all registered delegates by default with query arguments to filter unneeded values.
+   * @see https://tezos.gitlab.io/active/rpc.html#get-block-id-context-delegates-pkh
+   */
+  async getAllDelegates(
+    args: AllDelegatesQueryArguments = {},
+    { block }: { block: string } = defaultRPCOptions
+  ): Promise<string[]> {
+    return await this.httpBackend.createRequest<string[]>({
+      url: this.createURL(`/chains/${this.chain}/blocks/${block}/context/delegates`),
+      method: 'GET',
+      query: args,
+    });
+  }
+
   /**
    * @param address delegate address which we want to retrieve
    * @param options contains generic configuration for rpc calls to specified block (default to head)
diff --git a/packages/taquito-rpc/src/types.ts b/packages/taquito-rpc/src/types.ts
index 7e6d789df..930905bfe 100644
--- a/packages/taquito-rpc/src/types.ts
+++ b/packages/taquito-rpc/src/types.ts
@@ -1125,6 +1125,13 @@ export type BakingRightsArgumentsDelegate = string | string[];
 export type BakingRightsArgumentsCycle = number | number[];
 export type BakingRightsArgumentsLevel = number | number[];
 
+export type AllDelegatesQueryArguments = {
+  active?: boolean;
+  inactive?: boolean;
+  with_minimal_stake?: boolean;
+  without_minimal_stake?: boolean;
+};
+
 export type BakingRightsQueryArguments = BakingRightsQueryArgumentsBase;
 
 export interface BakingRightsQueryArgumentsBase {
diff --git a/packages/taquito/src/constants.ts b/packages/taquito/src/constants.ts
index f543d7b7c..a912d5302 100644
--- a/packages/taquito/src/constants.ts
+++ b/packages/taquito/src/constants.ts
@@ -69,6 +69,7 @@ export enum Protocols {
   PtNairobi = 'PtNairobiyssHuh87hEhfVBGCVrK3WnS8Z2FT4ymB5tAa4r1nQf',
   ProxfordY = 'ProxfordYmVfjWnRcgjWH36fW6PArwqykTFzotUxRs6gmTcZDuH',
   PtParisBQ = 'PtParisBQscdCm6Cfow6ndeU6wKJyA3aV1j4D3gQBQMsTQyJCrz',
+  PtParisBx = 'PtParisBxoLz5gzMmn3d9WBQNoPSZakgnkMC2VNuQ3KXfUtUQeZ',
   ProtoALpha = 'ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK',
 }
 


Test patch:
diff --git a/docs/rpc_nodes_integration_test.md b/docs/rpc_nodes_integration_test.md
index a811b85c7..76416813a 100644
--- a/docs/rpc_nodes_integration_test.md
+++ b/docs/rpc_nodes_integration_test.md
@@ -29,6 +29,7 @@ Remove `./rpc-nodes.spec.ts` from `"testPathIgnorePatterns"` in the package.json
     ✓ Verify that rpcClient.getManagerKey for known baker returns the manager key of the contract (30 ms)
     ✓ Verify that rpcClient.getDelegate for known baker returns the delegate of the contract (30 ms)
     ✓ Verify that rpcClient.getBigMapExpr for encoded expression returns the value associated with a key in a big map (151 ms)
+    ✓ Verify that rpcClient.getAllDelegates returns all delegates from RPC (132 ms)
     ✓ Verify that rpcClient.getDelegates for known baker returns information about a delegate from RPC (32 ms)
     ✓ Verify that rpc.getVotingInfo for known baker returns voting information about a delegate from RPC (30 ms)
     ✓ Verify that rpcClient.getConstants returns all constants from RPC (33 ms)
@@ -86,6 +87,7 @@ Remove `./rpc-nodes.spec.ts` from `"testPathIgnorePatterns"` in the package.json
     ✓ Verify that rpcClient.getManagerKey for known baker returns the manager key of the contract (11 ms)
     ✓ Verify that rpcClient.getDelegate for known baker returns the delegate of the contract (8 ms)
     ✓ Verify that rpcClient.getBigMapExpr for encoded expression returns the value associated with a key in a big map (93 ms)
+    ✓ Verify that rpcClient.getAllDelegates returns all delegates from RPC (132 ms)
     ✓ Verify that rpcClient.getDelegates for known baker returns information about a delegate from RPC (10 ms)
     ✓ Verify that rpc.getVotingInfo for known baker returns voting information about a delegate from RPC (7 ms)
     ✓ Verify that rpcClient.getConstants returns all constants from RPC (10 ms)
diff --git a/integration-tests/__tests__/rpc/nodes.spec.ts b/integration-tests/__tests__/rpc/nodes.spec.ts
index 5a0fd6acf..55024b52d 100644
--- a/integration-tests/__tests__/rpc/nodes.spec.ts
+++ b/integration-tests/__tests__/rpc/nodes.spec.ts
@@ -152,6 +152,16 @@ CONFIGS().forEach(
           expect(bigMapValue).toBeDefined();
         });
 
+        it(`Verify that rpcClient.getAllDelegates returns all delegates from RPC`, async () => {
+          const allDelegates = await rpcClient.getAllDelegates();
+          expect(allDelegates).toBeDefined();
+
+          const allViableDelegates =  await rpcClient.getAllDelegates({active: true, with_minimal_stake: true});
+          expect(allViableDelegates).toBeDefined();
+
+          expect(allViableDelegates.length).toBeLessThanOrEqual(allDelegates.length);
+        });
+
         it(`Verify that rpcClient.getDelegates for known baker returns information about a delegate from RPC`, async () => {
           const delegates = await rpcClient.getDelegates(knownBaker);
           expect(delegates).toBeDefined();
diff --git a/packages/taquito-rpc/test/rpc-cache.spec.ts b/packages/taquito-rpc/test/rpc-cache.spec.ts
index 2a6c775a3..d06ffc1d8 100644
--- a/packages/taquito-rpc/test/rpc-cache.spec.ts
+++ b/packages/taquito-rpc/test/rpc-cache.spec.ts
@@ -72,6 +72,7 @@ describe('RpcClientCache test', () => {
       getManagerKey: jest.fn(),
       getDelegate: jest.fn(),
       getBigMapExpr: jest.fn(),
+      getAllDelegates: jest.fn(),
       getDelegates: jest.fn(),
       getVotingInfo: jest.fn(),
       getConstants: jest.fn(),
@@ -117,6 +118,7 @@ describe('RpcClientCache test', () => {
     mockRpcClient.getManagerKey.mockReturnValue(managerKey);
     mockRpcClient.getDelegate.mockReturnValue(delegate);
     mockRpcClient.getBigMapExpr.mockReturnValue(bigmapValue);
+    mockRpcClient.getAllDelegates.mockReturnValue([delegate]);
     mockRpcClient.getDelegates.mockReturnValue(delegates);
     mockRpcClient.getVotingInfo.mockReturnValue(votingInfo);
     mockRpcClient.getConstants.mockReturnValue(constants);
@@ -167,6 +169,7 @@ describe('RpcClientCache test', () => {
     await rpcCache.getManagerKey(contractAddress);
     await rpcCache.getDelegate(address);
     await rpcCache.getBigMapExpr('72', 'expruPtxxirR4BVqFH43VcmEFZqHaQHJhZQDRVTMgSYAGGgBhBRxfp');
+    await rpcCache.getAllDelegates();
     await rpcCache.getDelegates(address);
     await rpcCache.getVotingInfo(address);
     await rpcCache.getConstants();
@@ -250,6 +253,9 @@ describe('RpcClientCache test', () => {
         'rpcTest/getBigMapExpr/head/72/expruPtxxirR4BVqFH43VcmEFZqHaQHJhZQDRVTMgSYAGGgBhBRxfp/'
       ].response
     ).toEqual(bigmapValue);
+    expect(rpcCache.getAllCachedData()[`rpcTest/getAllDelegates/head/{}/`].response).toEqual([
+      delegate,
+    ]);
     expect(rpcCache.getAllCachedData()[`rpcTest/getDelegates/head/${address}/`].response).toEqual(
       delegates
     );
@@ -335,6 +341,7 @@ describe('RpcClientCache test', () => {
       'expruPtxxirR4BVqFH43VcmEFZqHaQHJhZQDRVTMgSYAGGgBhBRxfp',
       block
     );
+    await rpcCache.getAllDelegates({}, block);
     await rpcCache.getDelegates(address, block);
     await rpcCache.getVotingInfo(address, block);
     await rpcCache.getConstants(block);
@@ -433,6 +440,10 @@ describe('RpcClientCache test', () => {
         `rpcTest/getBigMapExpr/${block.block}/72/expruPtxxirR4BVqFH43VcmEFZqHaQHJhZQDRVTMgSYAGGgBhBRxfp/`
       ].response
     ).toEqual(bigmapValue);
+    console.log(`rpcTest/getAllDelegates/${block.block}/{}`);
+    expect(
+      rpcCache.getAllCachedData()[`rpcTest/getAllDelegates/${block.block}/{}/`].response
+    ).toEqual([delegate]);
     expect(
       rpcCache.getAllCachedData()[`rpcTest/getDelegates/${block.block}/${address}/`].response
     ).toEqual(delegates);
@@ -520,6 +531,7 @@ describe('RpcClientCache test', () => {
     await rpcCache.getManagerKey(contractAddress);
     await rpcCache.getDelegate(address);
     await rpcCache.getBigMapExpr('72', 'expruPtxxirR4BVqFH43VcmEFZqHaQHJhZQDRVTMgSYAGGgBhBRxfp');
+    await rpcCache.getAllDelegates();
     await rpcCache.getDelegates(address);
     await rpcCache.getVotingInfo(address);
     await rpcCache.getConstants();
diff --git a/packages/taquito-rpc/test/taquito-rpc.spec.ts b/packages/taquito-rpc/test/taquito-rpc.spec.ts
index e35bc95ca..97ad9451d 100644
--- a/packages/taquito-rpc/test/taquito-rpc.spec.ts
+++ b/packages/taquito-rpc/test/taquito-rpc.spec.ts
@@ -323,6 +323,21 @@ describe('RpcClient test', () => {
     });
   });
 
+  describe('getAllDelegates', () => {
+    it('should query the right url and data', async () => {
+      httpBackend.createRequest.mockResolvedValue(['tz1cjyja1TU6fiyiFav3mFAdnDsCReJ12hPD']);
+      const result = await client.getAllDelegates({ active: true, with_minimal_stake: true });
+      await client.getAllDelegates({ active: true, with_minimal_stake: true });
+      expect(httpBackend.createRequest.mock.calls[0][0]).toEqual({
+        method: 'GET',
+        url: 'root/chains/test/blocks/head/context/delegates',
+        query: { active: true, with_minimal_stake: true },
+      });
+
+      expect(result).toEqual(['tz1cjyja1TU6fiyiFav3mFAdnDsCReJ12hPD']);
+    });
+  });
+
   describe('getDelegates', () => {
     const sampleResponse = {
       full_balance: new BigNumber('10289576365'),