Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/cre-sdk-examples/secrets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ secretsNames:
- SECRET_ADDRESS_ALL
CHARACTER_ID:
- SECRET_CHARACTER_ID
SECRET_HEADER:
- SECRET_HEADER_VALUE
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"schedule": "0 */1 * * * *",
"url": "https://api.mathjs.org/v4?expr=randomInt(1,101)"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {
type ConfidentialHTTPSendRequester,
consensusMedianAggregation,
cre,
ok,
Runner,
type Runtime,
text,
} from '@chainlink/cre-sdk'
import { z } from 'zod'

const configSchema = z.object({
schedule: z.string(),
url: z.string(),
})

type Config = z.infer<typeof configSchema>

const fetchMathResult = (sendRequester: ConfidentialHTTPSendRequester, config: Config) => {
const { responses } = sendRequester
.sendRequests({
input: {
requests: [
{
url: config.url,
method: 'GET',
},
],
},
})
.result()
const response = responses[0]

if (!ok(response)) {
throw new Error(`HTTP request failed with status: ${response.statusCode}`)
}

// Convert response body to text using the helper function
const responseText = text(response)

return Number.parseFloat(responseText)
}

const onCronTrigger = (runtime: Runtime<Config>) => {
runtime.log('Confidential HTTP workflow triggered.')

const confHTTPClient = new cre.capabilities.ConfidentialHTTPClient()
const result = confHTTPClient
.sendRequests(
runtime,
fetchMathResult,
consensusMedianAggregation(),
)(runtime.config)
.result()

runtime.log(`Successfully fetched result: ${result}`)

return {
result,
}
}

const initWorkflow = (config: Config) => {
const cron = new cre.capabilities.CronCapability()

return [cre.handler(cron.trigger({ schedule: config.schedule }), onCronTrigger)]
}

export async function main() {
const runner = await Runner.newRunner<Config>({ configSchema })

await runner.run(initWorkflow)
}

main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# ==========================================================================
# CRE WORKFLOW SETTINGS FILE
# ==========================================================================
# This file defines environment-specific workflow settings used by the CRE CLI.
#
# Each top-level key is a target (e.g., `production`, `production-testnet`, etc.).
# You can also define your own custom targets, such as `my-target`, and
# point the CLI to it via an environment variable.
#
# Note: If any setting in this file conflicts with a setting in the CRE Project Settings File,
# the value defined here in the workflow settings file will take precedence.
#
# Below is an example `my-target`:
#
# my-target:
# user-workflow:
# # Optional: The address of the workflow owner (wallet or MSIG contract).
# # Used to establish ownership for encrypting the workflow's secrets.
# # If omitted, defaults to an empty string.
# workflow-owner-address: "0x1234567890abcdef1234567890abcdef12345678"
#
# # Required: The name of the workflow to register with the Workflow Registry contract.
# workflow-name: "MyExampleWorkflow"

# ==========================================================================
local-simulation:
user-workflow:
workflow-owner-address: "(optional) Multi-signature contract address"
workflow-name: "http-confidential-fetch"
workflow-artifacts:
workflow-path: "./index.ts"
config-path: "./config.json"

# ==========================================================================
production-testnet:
user-workflow:
workflow-owner-address: "(optional) Multi-signature contract address"
workflow-name: "http-confidential-fetch"
Binary file modified packages/cre-sdk-javy-plugin/dist/javy_chainlink_sdk.wasm
Binary file not shown.
3 changes: 3 additions & 0 deletions packages/cre-sdk/scripts/src/generate-sdks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { file_capabilities_internal_basicaction_v1_basic_action } from '@cre/gen
import { file_capabilities_internal_basictrigger_v1_basic_trigger } from '@cre/generated/capabilities/internal/basictrigger/v1/basic_trigger_pb'
import { file_capabilities_internal_consensus_v1alpha_consensus } from '@cre/generated/capabilities/internal/consensus/v1alpha/consensus_pb'
import { file_capabilities_internal_nodeaction_v1_node_action } from '@cre/generated/capabilities/internal/nodeaction/v1/node_action_pb'
import { file_capabilities_networking_confidentialhttp_v1alpha_client } from '@cre/generated/capabilities/networking/confidentialhttp/v1alpha/client_pb'
import { file_capabilities_networking_http_v1alpha_client } from '@cre/generated/capabilities/networking/http/v1alpha/client_pb'
import { file_capabilities_networking_http_v1alpha_trigger } from '@cre/generated/capabilities/networking/http/v1alpha/trigger_pb'
import { file_capabilities_scheduler_cron_v1_trigger } from '@cre/generated/capabilities/scheduler/cron/v1/trigger_pb'
Expand Down Expand Up @@ -33,4 +34,6 @@ export const main = () => {
generateSdk(file_capabilities_networking_http_v1alpha_trigger, './src/generated-sdk')

generateSdk(file_capabilities_scheduler_cron_v1_trigger, './src/generated-sdk')

generateSdk(file_capabilities_networking_confidentialhttp_v1alpha_client, './src/generated-sdk')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { fromJson } from '@bufbuild/protobuf'
import {
type EnclaveActionInput,
type EnclaveActionInputJson,
EnclaveActionInputSchema,
type HTTPEnclaveResponseData,
HTTPEnclaveResponseDataSchema,
} from '@cre/generated/capabilities/networking/confidentialhttp/v1alpha/client_pb'
import { type NodeRuntime, type Runtime } from '@cre/sdk'
import { Report } from '@cre/sdk/report'
import type { ConsensusAggregation, PrimitiveTypes, UnwrapOptions } from '@cre/sdk/utils'

export class SendRequestser {
constructor(
private readonly runtime: NodeRuntime<unknown>,
private readonly client: ClientCapability,
) {}
sendRequests(input: EnclaveActionInput | EnclaveActionInputJson): {
result: () => HTTPEnclaveResponseData
} {
return this.client.sendRequests(this.runtime, input)
}
}

/**
* Client Capability
*
* Capability ID: [email protected]
* Capability Name: confidential-http
* Capability Version: 1.0.0-alpha
*/
export class ClientCapability {
/** The capability ID for this service */
static readonly CAPABILITY_ID = '[email protected]'

static readonly CAPABILITY_NAME = 'confidential-http'
static readonly CAPABILITY_VERSION = '1.0.0-alpha'

sendRequests(
runtime: NodeRuntime<unknown>,
input: EnclaveActionInput | EnclaveActionInputJson,
): { result: () => HTTPEnclaveResponseData }
sendRequests<TArgs extends unknown[], TOutput>(
runtime: Runtime<unknown>,
fn: (sendRequestser: SendRequestser, ...args: TArgs) => TOutput,
consensusAggregation: ConsensusAggregation<TOutput, true>,
unwrapOptions?: TOutput extends PrimitiveTypes ? never : UnwrapOptions<TOutput>,
): (...args: TArgs) => { result: () => TOutput }
sendRequests(...args: unknown[]): unknown {
// Check if this is the sugar syntax overload (has function parameter)
if (typeof args[1] === 'function') {
const [runtime, fn, consensusAggregation, unwrapOptions] = args as [
Runtime<unknown>,
(sendRequestser: SendRequestser, ...args: unknown[]) => unknown,
ConsensusAggregation<unknown, true>,
UnwrapOptions<unknown> | undefined,
]
return this.sendRequestsSugarHelper(runtime, fn, consensusAggregation, unwrapOptions)
}
// Otherwise, this is the basic call overload
const [runtime, input] = args as [
NodeRuntime<unknown>,
EnclaveActionInput | EnclaveActionInputJson,
]
return this.sendRequestsCallHelper(runtime, input)
}
private sendRequestsCallHelper(
runtime: NodeRuntime<unknown>,
input: EnclaveActionInput | EnclaveActionInputJson,
): { result: () => HTTPEnclaveResponseData } {
// Handle input conversion - unwrap if it's a wrapped type, convert from JSON if needed
let payload: EnclaveActionInput

if ((input as unknown as { $typeName?: string }).$typeName) {
// It's the original protobuf type
payload = input as EnclaveActionInput
} else {
// It's regular JSON, convert using fromJson
payload = fromJson(EnclaveActionInputSchema, input as EnclaveActionInputJson)
}

const capabilityId = ClientCapability.CAPABILITY_ID

const capabilityResponse = runtime.callCapability<EnclaveActionInput, HTTPEnclaveResponseData>({
capabilityId,
method: 'SendRequests',
payload,
inputSchema: EnclaveActionInputSchema,
outputSchema: HTTPEnclaveResponseDataSchema,
})

return {
result: () => {
const result = capabilityResponse.result()

return result
},
}
}
private sendRequestsSugarHelper<TArgs extends unknown[], TOutput>(
runtime: Runtime<unknown>,
fn: (sendRequestser: SendRequestser, ...args: TArgs) => TOutput,
consensusAggregation: ConsensusAggregation<TOutput, true>,
unwrapOptions?: TOutput extends PrimitiveTypes ? never : UnwrapOptions<TOutput>,
): (...args: TArgs) => { result: () => TOutput } {
const wrappedFn = (runtime: NodeRuntime<unknown>, ...args: TArgs) => {
const sendRequestser = new SendRequestser(runtime, this)
return fn(sendRequestser, ...args)
}
return runtime.runInNodeMode(wrappedFn, consensusAggregation, unwrapOptions)
}
}
1 change: 1 addition & 0 deletions packages/cre-sdk/src/pb.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * as EVM_PB from '@cre/generated/capabilities/blockchain/evm/v1alpha/client_pb'
export * as CONFIDENTIAL_HTTP_CLIENT_PB from '@cre/generated/capabilities/networking/confidentialhttp/v1alpha/client_pb'
export * as HTTP_CLIENT_PB from '@cre/generated/capabilities/networking/http/v1alpha/client_pb'
export * as HTTP_TRIGGER_PB from '@cre/generated/capabilities/networking/http/v1alpha/trigger_pb'
export * as CRON_TRIGGER_PB from '@cre/generated/capabilities/scheduler/cron/v1/trigger_pb'
Expand Down
8 changes: 7 additions & 1 deletion packages/cre-sdk/src/sdk/cre/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import { ClientCapability as EVMClient } from '@cre/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen'
import { ClientCapability as ConfidentialHTTPClient } from '@cre/generated-sdk/capabilities/networking/confidentialhttp/v1alpha/client_sdk_gen'
import { ClientCapability as HTTPClient } from '@cre/generated-sdk/capabilities/networking/http/v1alpha/client_sdk_gen'
import { HTTPCapability } from '@cre/generated-sdk/capabilities/networking/http/v1alpha/http_sdk_gen'
import { CronCapability } from '@cre/generated-sdk/capabilities/scheduler/cron/v1/cron_sdk_gen'
Expand All @@ -26,7 +27,11 @@ export {
type WriteCreReportRequest,
type WriteCreReportRequestJson,
} from '@cre/generated-sdk/capabilities/blockchain/evm/v1alpha/client_sdk_gen'

// Confidential HTTP Capability
export {
ClientCapability as ConfidentialHTTPClient,
type SendRequestser as ConfidentialHTTPSendRequester,
} from '@cre/generated-sdk/capabilities/networking/confidentialhttp/v1alpha/client_sdk_gen'
// HTTP Capability
export {
ClientCapability as HTTPClient,
Expand All @@ -47,6 +52,7 @@ export const cre = {
capabilities: {
CronCapability,
HTTPCapability,
ConfidentialHTTPClient,
HTTPClient,
EVMClient,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ResponseTemplate } from '@cre/generated/capabilities/networking/confidentialhttp/v1alpha/client_pb'
import type {
Request,
RequestJson,
Expand Down Expand Up @@ -61,17 +62,17 @@ import { decodeJson } from '@cre/sdk/utils/decode-json'
* @param response - The Response object
* @returns The body as a trimmed string
*/
export function text(response: Response): string
export function text(response: Response | ResponseTemplate): string
/**
* Returns the response body as a UTF-8 string, automatically trimmed
* @param responseFn - Function that returns an object with result function that returns Response
* @returns Object with result function that returns the body as a trimmed string
*/
export function text(responseFn: () => { result: Response }): {
export function text(responseFn: () => { result: Response | ResponseTemplate }): {
result: () => string
}
export function text(
responseOrFn: Response | (() => { result: Response }),
responseOrFn: Response | ResponseTemplate | (() => { result: Response | ResponseTemplate }),
): string | { result: () => string } {
if (typeof responseOrFn === 'function') {
return {
Expand Down Expand Up @@ -149,17 +150,17 @@ export function getHeader(
* @param response - The Response object
* @returns True if the status code is in the 200-299 range
*/
export function ok(response: Response): boolean
export function ok(response: Response | ResponseTemplate): boolean
/**
* Checks if the response status indicates success (200-299)
* @param responseFn - Function that returns an object with result function that returns Response
* @returns Object with result function that returns true if the status code is in the 200-299 range
*/
export function ok(responseFn: () => { result: Response }): {
export function ok(responseFn: () => { result: Response | ResponseTemplate }): {
result: () => boolean
}
export function ok(
responseOrFn: Response | (() => { result: Response }),
responseOrFn: Response | ResponseTemplate | (() => { result: Response | ResponseTemplate }),
): boolean | { result: () => boolean } {
if (typeof responseOrFn === 'function') {
return {
Expand Down