Skip to content

Conversation

@cl-mayowa
Copy link
Collaborator

Closes #DS-1113

https://smartcontract-it.atlassian.net/browse/DS-1113

Description

This is a general-purpose Chainlink External Adapter that enables smart contracts to read data from Canton participant nodes via the Ledger API. The adapter should be pluggable and configurable to support application-specific queries while maintaining a standard interface.

We should prioritise using the JSON Ledger API interface, although there is an alternative gRPC interface. The ledger API is the interface to the participant node.

This can be used in a more specific way to read from individual DPs, who may have contracts with different getter functions.
......

Changes

  • High level
  • changes that
  • you made

Steps to Test

  1. Steps
  2. to
  3. test

Quality Assurance

  • If a new adapter was made, or an existing one was modified so that its environment variables have changed, update the relevant infra-k8s configuration file.
  • If a new adapter was made, or an existing one was modified so that its environment variables have changed, update the relevant adapter-secrets configuration file or update the soak testing blacklist.
  • If a new adapter was made, or a new endpoint was added, update the test-payload.json file with relevant requests.
  • The branch naming follows git flow (feature/x, chore/x, release/x, hotfix/x, fix/x) or is created from Jira.
  • This is related to a maximum of one Jira story or GitHub issue.
  • Types are safe (avoid TypeScript/TSLint features like any and disable, instead use more specific types).
  • All code changes have 100% unit and integration test coverage. If testing is not applicable or too difficult to justify doing, the reasoning should be documented explicitly in the PR.

@changeset-bot
Copy link

changeset-bot bot commented Oct 14, 2025

🦋 Changeset detected

Latest commit: d652378

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@chainlink/canton-functions-adapter Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cl-mayowa cl-mayowa requested a review from Fletch153 October 14, 2025 16:31
required: true,
sensitive: true,
},
BACKGROUND_EXECUTE_MS: {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to override the default here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can always export a different BACKGROUND_EXECUTE_MS env variable. But if we want to do that programmatically, we might need to create in input param for it.

@cl-mayowa cl-mayowa changed the title Add Canton EA to reach from Canton participant node Add Canton EA to read from Canton participant node Oct 20, 2025
@cl-mayowa cl-mayowa requested a review from Fletch153 October 20, 2025 10:04
throw new Error(`Failed to query contracts: ${response.response?.statusText}`)
}

const contracts = response.response.data.result
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this actually return multiple contracts? i thought contractIds were unique

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the query did not factor in the contract Id. But the function has been removed now because there is no use case for it in the code any longer

}

// Find the latest contract by createdAt
contract = this.findLatestContract(contracts)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think each eturned contract may have a flag associated with it, which says where it's "ACTIVE" or "ARCHIVED" - it might be useful to check this and log an error

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc for this query is actually for active contracts. So all the contracts returned are active by default.

}

// Sort by createdAt in descending order (latest first)
return contracts.sort((a, b) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Canton not have an API to return the latest contract? And also perhaps even filter by flag ACTIVE as i mentioned above

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Canton does not have the API for that. The only thing that comes close to this is the created_at property that is attached to a query response from a gRPC call. However, when you query active contracts via the JSON api, it returns the active contracts in order of creation. So the first contract In the list is the latest, while the last contract in the list is the oldest.

import { cantonDataTransport } from '../transport/canton-data'

export const inputParameters = new InputParameters(
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted: these should be ENV variables specified in the implementing project, most of these are fixed and do not need to be in the request

templateIds: request.templateIds,
}

if (request.filter) {
Copy link
Collaborator

@Fletch153 Fletch153 Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can either

  • Return an error if duplicates are found
  • We could also return the full response (all the contracts) and pass this down to the implementing project to handle the return values. This might be preferable

import { config } from './config'
import { cantonData } from './endpoint'

export const adapter = new Adapter({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Base contract should not have an instance, instance should be configured within implementing contracts as discussed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants