List Contracts for a Provider

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,
      }),
    });

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
}

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.

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");
    }
  }

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

1 Like

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.

Bump. Should this code work?

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: