Developers
Understanding Blueprint Macros
Job Macro

Blueprint Jobs Macro System Documentation

The goal with the job macro is to provide an easy and intuitive interface for creating a task based AVS service.

Developers can specify jobs for their AVS to complete and streamline on-chain transaction submissions to protocols like Tangle or Eigenlayer. The system is flexible and allows developers to listen to events from a variety of sources such as Tangle's job submission/execution system, an EVM smart contract's events, periodic timers for cron-job like tasks, as well as custom event listeners.

These job functions are triggered every time the respective event listener fires, and can be synchronous or asynchronous.

The jobs system is continuously being improved and the API may continue to change. We welcome you to share feature requests and PRs on our github (opens in a new tab).

The #[job] Macro

The #[job] macro is used to define individual tasks or jobs within a blueprint. It allows you to specify various parameters and metadata for each job.

#[job(
    id = <job_id>,
    params(<param_list>),
    result(<result_type>),
    event_listener(TangleJobCallListener)
    verifier(evm = "<contract_name>")
)]
pub fn job_name(<parameters>) -> Result<<return_type>, <error_type>> {
    // Job implementation
}
  • id: A unique identifier for the job.
  • params: A list of parameters that the job accepts.
  • result: The type of result the job produces (often represented by an underscore _).
  • event_listener: Specifies the event listener that triggers the job.
  • verifier: Specifies an EVM contract for job verification (for use w/ Tangle).

Tangle Blueprint Jobs

The default implementation targets deployment on Tangle and specifies an EVM contract for verification specified with the verifier parameter.

The contract name is cross-referenced with the name of a contract in a foundry (opens in a new tab) project and should be identical in order to integrate seamlessly to Tangle.

For other restaking protocols and job verification mechanisms, developers should use or implement custom handlers.

An example of doing this to replicate Tangle's default implementation is as follows:

struct CustomTangleJobCallListener {
    instance: <contract_instance>,
}
 
impl EventListener for CustomJobCallListener {
    fn next_event();
    fn handle_event();
}
 
#[job(
    id = <job_id>,
    params(<param_list>),
    result(<result_type>),
    event_listener(CustomTangleJobCallListener)
)]
pub fn job_name(<parameters>) -> Result<<return_type>, <error_type>> {
    // Job implementation
}

Examples

Keygen Job

#[job(id = 0, params(n, t), result(_))]
pub fn keygen(ctx: &MyContext, n: u16, t: u16) -> Result<Vec<u8>, Error> {
    let _ = (n, t, ctx);
    Ok(vec![0; 33])
}

Sign Job

#[job(id = 1, params(keygen_id, data), result(_))]
pub async fn sign(keygen_id: u64, data: Vec<u8>) -> Result<Vec<u8>, Error> {
    let _ = (keygen_id, data);
    Ok(vec![0; 65])
}

Run Gaia Node Job

#[sdk::job(id = 1, params(data), result(_), verifier(evm = "GaiaAiAgentBlueprint"))]
pub async fn run_gaia_node_job(data: Vec<u8>) -> Result<String, Infallible> {
    let (_, outputs) = runner::run_gaia_node().await?;
    Ok(serde_json::to_string(&outputs)?)
}