-
Notifications
You must be signed in to change notification settings - Fork 327
Add Canton EA to read from Canton participant node #4103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
cl-mayowa
wants to merge
10
commits into
main
Choose a base branch
from
DS-1113-Canton-EA
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
3b1f95f
add canton ea endpoint and transport
cl-mayowa cc31167
add changeset
cl-mayowa c6b9360
update contract query and input params
cl-mayowa 63ae031
remove unsued functions and refactor code
cl-mayowa d652378
update integration tests for new changes
cl-mayowa 20ad77c
refactor adapter settings and tests
cl-mayowa 75522c3
add custom return handler with tests
cl-mayowa 30f8250
Merge branch 'main' into DS-1113-Canton-EA
cl-mayowa 76f845e
Merge remote-tracking branch 'origin/DS-1113-Canton-EA' into DS-1113-…
cl-mayowa 9467d0d
refactor canton to be extensible and added tests
cl-mayowa File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@chainlink/canton-functions-adapter': major | ||
| --- | ||
|
|
||
| This EA enables us to read data from Canton participant nodes via the Ledger API |
Large diffs are not rendered by default.
Oops, something went wrong.
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # Chainlink External Adapter for canton-functions | ||
|
|
||
| This README will be generated automatically when code is merged to `main`. If you would like to generate a preview of the README, please run `yarn generate:readme canton-functions`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| { | ||
| "name": "@chainlink/canton-functions-adapter", | ||
| "version": "1.0.0", | ||
| "description": "Chainlink canton-functions adapter.", | ||
| "keywords": [ | ||
| "Chainlink", | ||
| "LINK", | ||
| "blockchain", | ||
| "oracle", | ||
| "canton-functions" | ||
| ], | ||
| "main": "dist/index.js", | ||
| "types": "dist/index.d.ts", | ||
| "files": [ | ||
| "dist" | ||
| ], | ||
| "repository": { | ||
| "url": "https://github.com/smartcontractkit/external-adapters-js", | ||
| "type": "git" | ||
| }, | ||
| "license": "MIT", | ||
| "scripts": { | ||
| "clean": "rm -rf dist && rm -f tsconfig.tsbuildinfo", | ||
| "prepack": "yarn build", | ||
| "build": "tsc -b", | ||
| "server": "node -e 'require(\"./index.js\").server()'", | ||
| "server:dist": "node -e 'require(\"./dist/index.js\").server()'", | ||
| "start": "yarn server:dist" | ||
| }, | ||
| "devDependencies": { | ||
| "@sinonjs/fake-timers": "9.1.2", | ||
| "@types/jest": "^29.5.14", | ||
| "@types/node": "22.14.1", | ||
| "@types/sinonjs__fake-timers": "8.1.5", | ||
| "nock": "13.5.6", | ||
| "typescript": "5.8.3" | ||
| }, | ||
| "dependencies": { | ||
| "@chainlink/external-adapter-framework": "2.7.1", | ||
| "tslib": "2.4.1" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { AdapterConfig } from '@chainlink/external-adapter-framework/config' | ||
|
|
||
| export const config = new AdapterConfig({ | ||
| AUTH_TOKEN: { | ||
| description: 'JWT token for Canton JSON API authentication', | ||
| type: 'string', | ||
| required: true, | ||
| sensitive: true, | ||
| }, | ||
| BACKGROUND_EXECUTE_MS: { | ||
| description: | ||
| 'The amount of time the background execute should sleep before performing the next request', | ||
| type: 'number', | ||
| default: 1_000, | ||
| }, | ||
| URL: { | ||
| description: 'The Canton JSON API URL', | ||
| type: 'string', | ||
| required: true, | ||
| }, | ||
| TEMPLATE_ID: { | ||
| description: 'The template ID to query contracts for (format: packageId:Module:Template)', | ||
| type: 'string', | ||
| required: true, | ||
| }, | ||
| CHOICE: { | ||
| description: 'The non-consuming choice to exercise on the contract', | ||
| type: 'string', | ||
| required: true, | ||
| }, | ||
| ARGUMENT: { | ||
| description: 'The argument for the choice (JSON string)', | ||
| type: 'string', | ||
| required: false, | ||
| }, | ||
| CONTRACT_FILTER: { | ||
| description: 'Filter to query contracts when contractId is not provided (JSON string)', | ||
| type: 'string', | ||
| required: false, | ||
| }, | ||
| }) | ||
39 changes: 39 additions & 0 deletions
39
packages/sources/canton-functions/src/endpoint/canton-data.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter' | ||
| import { InputParameters } from '@chainlink/external-adapter-framework/validation' | ||
| import { config } from '../config' | ||
| import { cantonDataTransport } from '../transport/canton-data' | ||
|
|
||
| export const inputParameters = new InputParameters( | ||
| { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| contractId: { | ||
| description: 'The contract ID to exercise choice on', | ||
| type: 'string', | ||
| required: false, | ||
| }, | ||
| }, | ||
| [ | ||
| { | ||
| contractId: '00e1f5c6d8b9a7f4e3c2d1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0', | ||
| }, | ||
| ], | ||
| ) | ||
|
|
||
| export type BaseEndpointTypes = { | ||
| Parameters: typeof inputParameters.definition | ||
| Response: { | ||
| Data: { | ||
| result: string | ||
| exerciseResult: any | ||
| contract?: any | ||
| } | ||
| Result: string | ||
| } | ||
| Settings: typeof config.settings | ||
| } | ||
|
|
||
| export const endpoint = new AdapterEndpoint({ | ||
| name: 'canton-data', | ||
| aliases: [], | ||
| transport: cantonDataTransport, | ||
| inputParameters, | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { endpoint as cantonData } from './canton-data' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { expose, ServerInstance } from '@chainlink/external-adapter-framework' | ||
| import { Adapter } from '@chainlink/external-adapter-framework/adapter' | ||
| import { config } from './config' | ||
| import { cantonData } from './endpoint' | ||
|
|
||
| export const adapter = new Adapter({ | ||
Fletch153 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| defaultEndpoint: cantonData.name, | ||
| name: 'CANTON_FUNCTIONS', | ||
| config, | ||
| endpoints: [cantonData], | ||
| }) | ||
|
|
||
| export const server = (): Promise<ServerInstance | undefined> => expose(adapter) | ||
|
|
||
| // Export types and utilities for secondary adapters | ||
| export { BaseEndpointTypes, inputParameters } from './endpoint/canton-data' | ||
| export type { | ||
| Contract, | ||
| ExerciseChoiceRequest, | ||
| ExerciseResponse, | ||
| QueryContractByTemplateRequest, | ||
| } from './shared/canton-client' | ||
| export { CantonDataTransport, ResultHandler } from './transport/canton-data' | ||
| export { config as cantonConfig } | ||
126 changes: 126 additions & 0 deletions
126
packages/sources/canton-functions/src/shared/canton-client.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| import { Requester } from '@chainlink/external-adapter-framework/util/requester' | ||
|
|
||
| export interface CantonClientConfig { | ||
| AUTH_TOKEN: string | ||
| URL: string | ||
| } | ||
|
|
||
| export interface QueryContractByTemplateRequest { | ||
| templateIds: string[] | ||
| filter?: string | Record<string, any> | ||
| } | ||
|
|
||
| export interface ExerciseChoiceRequest { | ||
| contractId: string | ||
| templateId: string | ||
| choice: string | ||
| argument: Record<string, any> | ||
| } | ||
|
|
||
| export interface Contract { | ||
| contractId: string | ||
| templateId: string | ||
| payload: Record<string, any> | ||
| signatories: string[] | ||
| observers: string[] | ||
| agreementText: string | ||
| createdAt?: string | ||
| } | ||
|
|
||
| export interface ExerciseResult { | ||
| completionOffset: string | ||
| events: any[] | ||
| exerciseResult: any | ||
| } | ||
|
|
||
| export interface ExerciseResponse { | ||
| result: any | ||
| status: number | ||
| } | ||
|
|
||
| export class CantonClient { | ||
| private requester: Requester | ||
| private config: CantonClientConfig | ||
| private static instance: CantonClient | ||
|
|
||
| constructor(requester: Requester, config: CantonClientConfig) { | ||
| this.requester = requester | ||
| this.config = config | ||
| } | ||
|
|
||
| static getInstance(requester: Requester, config: CantonClientConfig): CantonClient { | ||
| if (!this.instance) { | ||
| this.instance = new CantonClient(requester, config) | ||
| } | ||
|
|
||
| return this.instance | ||
| } | ||
|
|
||
| /** | ||
| * Query contracts by template ID with an optional filter | ||
| */ | ||
| async queryContractsByTemplate(request: QueryContractByTemplateRequest): Promise<Contract[]> { | ||
| const baseURL = `${this.config.URL}/v1/query` | ||
|
|
||
| const requestData: any = { | ||
| templateIds: request.templateIds, | ||
| } | ||
|
|
||
| if (request.filter) { | ||
Fletch153 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| requestData.query = | ||
| typeof request.filter === 'string' ? JSON.parse(request.filter) : request.filter | ||
| } | ||
|
|
||
| const requestConfig = { | ||
| method: 'POST', | ||
| baseURL, | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| Authorization: `Bearer ${this.config.AUTH_TOKEN}`, | ||
| }, | ||
| data: requestData, | ||
| } | ||
|
|
||
| const response = await this.requester.request<{ result: Contract[] }>(baseURL, requestConfig) | ||
|
|
||
| if (response.response?.status !== 200) { | ||
| throw new Error(`Failed to query contracts: ${response.response?.statusText}`) | ||
| } | ||
|
|
||
| const contracts = response.response.data.result | ||
|
|
||
| // When a filter is provided, it should return exactly one contract | ||
| if (request.filter && contracts.length > 1) { | ||
| throw new Error( | ||
| `Filter query returned ${contracts.length} contracts, but expected exactly 1. `, | ||
| ) | ||
| } | ||
|
|
||
| return contracts | ||
| } | ||
|
|
||
| /** | ||
| * Exercise a non-consuming choice on a contract | ||
| */ | ||
| async exerciseChoice(payload: ExerciseChoiceRequest): Promise<ExerciseResult> { | ||
| const baseURL = `${this.config.URL}/v1/exercise` | ||
|
|
||
| const requestConfig = { | ||
| method: 'POST', | ||
| baseURL, | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| Authorization: `Bearer ${this.config.AUTH_TOKEN}`, | ||
| }, | ||
| data: payload, | ||
| } | ||
|
|
||
| const response = await this.requester.request<ExerciseResponse>(baseURL, requestConfig) | ||
|
|
||
| if (response.response?.status !== 200) { | ||
| throw new Error(`Failed to exercise choice: ${response.response?.statusText}`) | ||
| } | ||
|
|
||
| return response.response.data.result | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.