Clique Docs
  • What Is Clique
    • TEE Network
    • Compute Coordination Network
  • Build with Clique
    • Clique Application Structure
    • Clique CLI
      • Installation
      • Develop Task
      • Build Task
      • Test Task
      • Deploy Task
    • Clique Client SDK
    • Smart Contract SDK
      • Smart Contract Integration
      • Clique Official Tasks
  • References
    • Clique Manifest
    • Clique Query
    • Verification
  • Sample Task Tutorials
    • Data Attestation
    • Social Verification
      • Github
      • Twitter
    • Making Arbitrary TLS Calls (TLS Oracle)
    • Custom Executor
  • Toolchain
    • Clique Pipelines SDK
    • Clique Attestation SDK
      • Attestation Protocols
      • Reading Attestations On-chain
      • Reading Attestations Off-Chain
      • What are Attestors ?
        • Data Sources
    • Clique Browser Extension
  • FAQ
  • Glossaries
  • Socials
Powered by GitBook
On this page
  1. Sample Task Tutorials

Custom Executor

PreviousMaking Arbitrary TLS Calls (TLS Oracle)NextClique Pipelines SDK

Last updated 8 months ago

Clique offers a wide range of tasks officially for various use cases. If these do not meet your requirements, you can create your own executors to cater to specific tasks seamlessly.

Development Tools

To assist developers in quickly implementing a custom executor, Clique provides Rust support. Rust developers can directly use the procedural macros we provide to encapsulate the custom executor. Developers do not need to be concerned with how the executor interacts with the Clique network; they only need to focus on their own computational logic.

First, include the following dependencies in your Rust project:

clique-proc-macro = { git = "https://github.com/CliqueOfficial/clique-protocol-sdk" }
clique = { git = "https://github.com/CliqueOfficial/clique-protocol-sdk" }

Clique provides the following procedural macros:

  • #[namespace(name = ...)]

    • The namespace macro ensures that all tasks within the annotated module are grouped under a specific namespace, and the namespace is corresbonding to the name in the Clique Task Manifest. For example, if the task's name is "clique_httpsRequest," then the namespace should be specified as "clique."

  • #[task]

    • The task macro is used to define a task struct. This macro annotates a struct that represents a task, allowing it to be recognized and managed by the task execution system.

    • You need to implement the clique::Task trait for the struct marked with the task macro, and within the execute method, implement your actual computational logic. Note that the input and output of the execute method are Rust struct generated based on the input and output section of your Clique Task Manifest file. You can directly use the field name from the manifest to access the corresbonding field in the input / output.

  • #[clique::async_trait]

    • This macro is used to annotate the implementation of clique::Task trait, since this trait is async

  • #[executor(with(...))]

    • The executor macro is used to define an executor struct that can handle the execution of multiple task modules. In the with field, fill in the name of the module decorated with the namespace macro. The tasks within these modules will be imported into your custom executor.

Here is an example:

use clique_proc_macro::{executor, namespace};

#[namespace(name = "yourOrganization")]
pub mod task_mod1 {
    #[task]
    pub struct HttpsRequest;

    #[clique::async_trait]
    impl clique::Task for HttpsRequest {
        type Error = anyhow::Error;

        async fn execute(
            input: Self::Input,
        ) -> Result<Self::Output, Self::Error> {
            let url = input.url;
            let encoding = input.encoding;

            for transformation in encoding.into_iter() {
                let json_ptr_str: String = transformation.from;
                let soltype_str: String = transformation.soltype;
                // ...
            }

            let response: Vec<u8> = // ...
            let output = HttpsRequestOutput { response };
            Ok(output)
        }
    }
    
    #[task]
    pub struct HttpsRequest2;

    #[clique::async_trait]
    impl clique::Task for HttpsRequest2 {
        type Error = anyhow::Error;

        async fn execute(
            input: Self::Input,
        ) -> Result<Self::Output, Self::Error> {
            // ...
        }
    }
}

#[namespace(name = "yourOrganization")]
pub mod task_mod2 {
    #[task]
    pub struct AnotherTask;

    #[clique::async_trait]
    impl clique::Task for AnotherTask {
        type Error = anyhow::Error;

        async fn execute(
            input: Self::Input,
        ) -> Result<Self::Output, Self::Error> {
            // ...
        }
    }
}

#[executor(with(
    task_mod1,
    task_mod2,
))]
pub struct MyCustomExecutor;
procedural macro