Skip to content
Discussions/App Development/List Contracts for a ProviderForum ↗

List Contracts for a Provider

App Development8 posts206 views1 likesLast activity Jun 2025
OH
ohthepainOP
Jun 2025

Hi all,
I can list active contracts for users but I am getting 403 errors when trying to list active contracts for a provider. Could somebody check my token and request for me?

  message: 'Request failed with HTTP status code 403\n' +
    'Response body: {"code":"NA","cause":"A security-sensitive error has been received","correlationId":"2f2e9919c18d434fbbd03abb0a5801e8","traceId":"2f2e9919c18d434fbbd03abb0a5801e8","context":{},"resources":[],"errorCategory":-1,"grpcCodeValue":7,"retryInfo":null,"definiteAnswer":null}',
Decoded token payload: {
  exp: 1749708419,
  iat: 1749708119,
  jti: 'e4b7b1b9-ca78-4856-ac39-97713de5a15d',
  iss: 'http://keycloak.localhost:8082/realms/AppProvider',
  aud: [ 'https://canton.network.global', 'account' ],
  sub: 'c87743ab-80e0-4b83-935a-4c0582226691',
  typ: 'Bearer',
  azp: 'app-provider-validator',
  acr: '1',
  'allowed-origins': [ '/*' ],
  realm_access: {
    roles: [
      'offline_access',
      'default-roles-quickstart',
      'uma_authorization'
    ]
  },
  resource_access: { account: { roles: [Array] } },
  scope: 'email profile',
  clientHost: '192.168.65.1',
  email_verified: false,
  preferred_username: 'service-account-app-provider-validator',
  clientAddress: '192.168.65.1',
  client_id: 'app-provider-validator'
}
Token verified successfully

    const response = await fetch(`http://localhost:37575/v2/state/active-contracts`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        filter: {
          filtersByParty: {
            [providerPartyId]: {},
          },
          filtersForAnyParty: {
            cumulative: [
              {
                identifierFilter: {
                  WildcardFilter: {
                    value: {
                      includeCreatedEventBlob: true,
                    },
                  },
                },
              },
            ],
          },
        },
        verbose: true,
        activeAtOffset: 0,
        eventFormat: null,
      }),
    });
OH
ohthepain
Jun 2025

BTW I also tried with a generated ts client but got the same result.

router.get("/provider/:providerPartyId", async (req, res) => {
  try {
    const { providerPartyId } = req.params;
    const token = await TokenGenerator.generateProviderValidatorToken();

    const filter: TransactionFilter = {
      filtersByParty: {
        [providerPartyId]: {
          cumulative: [
            {
              identifierFilter: {
                wildcardFilter: {
                  value: {
                    includeCreatedEventBlob: true,
                  },
                },
              },
            },
          ],
        },
      },
    };

    const request: GetActiveContractsRequest = {
      verbose: true, // Required boolean
      activeAtOffset: 0, // Required number - use 0 for current state
      filter: filter,
    };

    const api = new DefaultApi(
      new Configuration({
        basePath: "http://localhost:37575",
        accessToken: async () => token,
      })
    );

    // Make the request
    const response = await api.postV2StateActiveContracts({
      getActiveContractsRequest: request,
      limit: 100, // Optional: limit the number of results
      streamIdleTimeoutMs: 5000, // Optional: timeout for streaming
    });

    console.log("Response:", JSON.stringify(response, null, 2));

Here’s the body constructed by the generated client:

body {
  filter: {
    filtersByParty: {
      'user_pat_at_app-provider_dot_localhost::12202e0571cc7fc76ccc5cf4dd75f6cbdec6aeef12ee910f91b0c1ee90b692be00dc': [Object]
    },
    filtersForAnyParty: undefined
  },
  verbose: true,
  activeAtOffset: 0,
  eventFormat: undefined
}
BE
bernhard
Jun 2025

My first guess would be that you need to grant your user readAs rights to the provider party.
You can configure users dynamically using the UserManagementService (eg via the Canton Console) or via a config file.

OH
ohthepain
Jun 2025

So now I can create the contracts and execute choices but I still can’t get a list of active contracts.

To your comment, here is how I am granting rights:

  const grantRightsResponse = await fetch(`http://localhost:27575/v2/users/${userId}/rights`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${userToken}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      userId: userId,
      identityProviderId: "",
      rights: [
        { kind: { CanActAs: { value: { party: partyId } } } },
        { kind: { CanReadAs: { value: { party: partyId } } } },
      ],
    }),
  });

I am able to create contracts but not list them.

Here is my method using the generated ts client. It’s returning an empty list.

  static async getContracts(templateId: string, userPartyId?: string): Promise<any> {
    const token = await TokenGenerator.generateUserValidatorToken();
    const config = new Configuration({
      basePath: UserLedgerService.BASE_URL,
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });
    const api = new DefaultApi(config);

    const request: GetActiveContractsRequest = {
      verbose: true,
      activeAtOffset: 0,
      filter: {
        filtersByParty: {
          [userPartyId || ""]: {
            cumulative: [
              {
                identifierFilter: {
                  templateFilter: {
                    value: {
                      templateId,
                      includeCreatedEventBlob: true,
                    },
                  },
                },
              },
            ],
          },
        },
      },
    };

    try {
      const response = await api.postV2StateActiveContracts({ getActiveContractsRequest: request });
      console.log("Response:", JSON.stringify(response, null, 2));
      return response;
    } catch (error: unknown) {
      console.error("Error fetching contracts:", error);
      if (error instanceof Error) {
        throw new Error(`Failed to fetch contracts: ${error.message}`);
      }
      throw new Error("Failed to fetch contracts: Unknown error");
    }
  }
B_
b_heather
Jun 2025
ohthepain:

activeAtOffset: 0,

I suspect the above is a problem. Essentially you’re asking for the active contracts at ledger genesis which will be none.

To get the latest offset you can query

/v2/state/ledger-end:
      get:
        description: Get ledger end
        operationId: getV2StateLedger-end

OH
ohthepain
Jun 2025
ohthepain:

postV2StateActiveContracts

Thank you! I am now getting contracts.

Unfortunately the list is a bunch of empty tables. How do get the actual data?

Contract entry: {}
{}
Contract entry: {}
{}
Contract entry: {}
{}
Contract entry: {}
{}
Contract entry: {}
{}
Contract entry: {}

I am working with the generated typescript client.

OH
ohthepain
Jun 2025

Bump. Should this code work?

B_
b_heather
Jun 2025

Hi @ohthepain.

From the above it looks like the contracts are indeed being returned. I suspect It’s how you are then processing the data that is leading to what appears to empty contracts.

Here’s a link to what the expected return should look like which may help:

docs.digitalasset.com

JSON Ledger API Service V2 — Digital Asset’s platform documentation

← Back to Discussions