diff --git a/.github/workflows/code-scan.yaml b/.github/workflows/code-scan.yaml index ba28f2dc..be49bdaf 100644 --- a/.github/workflows/code-scan.yaml +++ b/.github/workflows/code-scan.yaml @@ -34,8 +34,8 @@ jobs: node: - cmd: cd programs/network && soteria -analyzeAll . path: programs/network - - cmd: cd programs/thread && soteria -analyzeAll . - path: programs/thread + - cmd: cd programs/automation && soteria -analyzeAll . + path: programs/automation - cmd: cd programs/webhook && soteria -analyzeAll . path: programs/webhook steps: diff --git a/Anchor.toml b/Anchor.toml index f2d57255..a3213865 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -4,17 +4,17 @@ wallet = "~/.config/solana/id.json" [programs.localnet] clockwork_network_program = "F8dKseqmBoAkHx3c58Lmb9TgJv5qeTf3BbtZZSEzYvUa" -clockwork_thread_program = "3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv" +clockwork_automation_program = "3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv" clockwork_webhook_program = "E7p5KFo8kKCDm6BUnWtnVFkQSYh6ZA6xaGAuvpv8NXTa" [programs.testnet] clockwork_network_program = "F8dKseqmBoAkHx3c58Lmb9TgJv5qeTf3BbtZZSEzYvUa" -clockwork_thread_program = "3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv" +clockwork_automation_program = "3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv" clockwork_webhook_program = "E7p5KFo8kKCDm6BUnWtnVFkQSYh6ZA6xaGAuvpv8NXTa" [programs.mainnet] clockwork_network_program = "F8dKseqmBoAkHx3c58Lmb9TgJv5qeTf3BbtZZSEzYvUa" -clockwork_thread_program = "3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv" +clockwork_automation_program = "3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv" clockwork_webhook_program = "E7p5KFo8kKCDm6BUnWtnVFkQSYh6ZA6xaGAuvpv8NXTa" [registry] diff --git a/Cargo.lock b/Cargo.lock index 7dd91100..9f249489 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -779,6 +779,20 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "clockwork-automation-program" +version = "1.4.2" +dependencies = [ + "anchor-lang", + "chrono", + "clockwork-cron", + "clockwork-macros", + "clockwork-network-program", + "clockwork-utils", + "static-pubkey", + "version", +] + [[package]] name = "clockwork-cli" version = "1.4.2" @@ -815,8 +829,8 @@ dependencies = [ "anchor-spl", "bincode", "borsh", + "clockwork-automation-program", "clockwork-network-program", - "clockwork-thread-program", "clockwork-utils", "clockwork-webhook-program", "solana-client", @@ -859,25 +873,11 @@ version = "1.4.2" dependencies = [ "anchor-lang", "chrono", - "clockwork-thread-program", + "clockwork-automation-program", "nom", "once_cell", ] -[[package]] -name = "clockwork-thread-program" -version = "1.4.2" -dependencies = [ - "anchor-lang", - "chrono", - "clockwork-cron", - "clockwork-macros", - "clockwork-network-program", - "clockwork-utils", - "static-pubkey", - "version", -] - [[package]] name = "clockwork-utils" version = "1.4.2" diff --git a/README.md b/README.md index 27f978d4..a0dd334e 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ | Program | Address| Devnet | Mainnet | | ------- | ------ | ------ | ------- | | Network | `F8dKseqmBoAkHx3c58Lmb9TgJv5qeTf3BbtZZSEzYvUa` | [v1.4.0](https://explorer.solana.com/address/F8dKseqmBoAkHx3c58Lmb9TgJv5qeTf3BbtZZSEzYvUa?cluster=devnet) | [v1.4.0](https://explorer.solana.com/address/F8dKseqmBoAkHx3c58Lmb9TgJv5qeTf3BbtZZSEzYvUa) | -| Thread | `3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv` | [v1.4.0](https://explorer.solana.com/address/3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv?cluster=devnet) | [v1.4.0](https://explorer.solana.com/address/3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv) | +| Automation | `3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv` | [v1.4.0](https://explorer.solana.com/address/3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv?cluster=devnet) | [v1.4.0](https://explorer.solana.com/address/3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv) | # SDKs | Language | Description | Lib | Examples | diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 5b871c13..1605b7db 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -1,7 +1,7 @@ use crate::parser::ProgramInfo; use clap::{Arg, ArgGroup, Command}; use clockwork_client::{ - thread::state::{InstructionData, Trigger}, + automation::state::{InstructionData, Trigger}, webhook::state::HttpMethod, }; use solana_sdk::{pubkey::Pubkey, signature::Keypair}; @@ -18,8 +18,8 @@ pub enum CliCommand { ConfigGet, ConfigSet { admin: Option, - epoch_thread: Option, - hasher_thread: Option, + epoch_automation: Option, + hasher_automation: Option, }, // Crontab @@ -46,7 +46,7 @@ pub enum CliCommand { worker_id: u64, }, - ExplorerGetThread { + ExplorerGetAutomation { id: Option, address: Option, }, @@ -72,30 +72,30 @@ pub enum CliCommand { size: usize, }, - // Thread commands - ThreadCrateInfo, - ThreadCreate { + // Automation commands + AutomationCrateInfo, + AutomationCreate { id: String, kickoff_instruction: InstructionData, trigger: Trigger, }, - ThreadDelete { + AutomationDelete { id: String, }, - ThreadGet { + AutomationGet { id: Option, address: Option, }, - ThreadPause { + AutomationPause { id: String, }, - ThreadResume { + AutomationResume { id: String, }, - ThreadStop { + AutomationStop { id: String, }, - ThreadUpdate { + AutomationUpdate { id: String, rate_limit: Option, schedule: Option, @@ -147,20 +147,20 @@ pub fn app() -> Command<'static> { .takes_value(true), ) .arg( - Arg::new("epoch_thread") - .long("epoch_thread") + Arg::new("epoch_automation") + .long("epoch_automation") .value_name("ADDRESS") .takes_value(true), ) .arg( - Arg::new("hasher_thread") - .long("hasher_thread") + Arg::new("hasher_automation") + .long("hasher_automation") .value_name("ADDRESS") .takes_value(true), ) .group( ArgGroup::new("config_settings") - .args(&["admin", "epoch_thread", "hasher_thread"]) + .args(&["admin", "epoch_automation", "hasher_automation"]) .multiple(true), ), ), @@ -279,22 +279,22 @@ pub fn app() -> Command<'static> { .arg_required_else_help(true) .subcommand( Command::new("get") - .about("Prints thread explorer url") + .about("Prints automation explorer url") .arg_required_else_help(true) .arg( Arg::new("id") .index(1) .takes_value(true) .required(false) - .help("The label of the thread to lookup (only works if you \ - are the signer of that thread)") + .help("The label of the automation to lookup (only works if you \ + are the signer of that automation)") ) .arg( Arg::new("address") .short('k') .long("address") .takes_value(true) - .help("The address of the thread to lookup"), + .help("The address of the automation to lookup"), ), ) ) @@ -385,8 +385,8 @@ pub fn app() -> Command<'static> { ), ) .subcommand( - Command::new("thread") - .about("Manage your transaction threads") + Command::new("automation") + .about("Manage your transaction automations") .arg_required_else_help(true) .subcommand( Command::new("crate-info") @@ -394,7 +394,7 @@ pub fn app() -> Command<'static> { ) .subcommand( Command::new("create") - .about("Create a new thread") + .about("Create a new automation") .arg_required_else_help(true) .arg( Arg::new("id") @@ -403,7 +403,7 @@ pub fn app() -> Command<'static> { .value_name("ID") .takes_value(true) .required(true) - .help("The ID of the thread to be created"), + .help("The ID of the automation to be created"), ) .arg( Arg::new("kickoff_instruction") @@ -445,76 +445,76 @@ pub fn app() -> Command<'static> { ) .subcommand( Command::new("delete") - .about("Delete a thread") + .about("Delete a automation") .arg_required_else_help(true) .arg( Arg::new("id") .index(1) .takes_value(true) .required(false) - .help("The id of the thread to delete"), + .help("The id of the automation to delete"), ), ) .subcommand( Command::new("get") - .about("Lookup a thread") + .about("Lookup a automation") .arg_required_else_help(true) .arg( Arg::new("id") .index(1) .takes_value(true) .required(false) - .help("The label of the thread to lookup (only works if you \ - are the signer of that thread)") + .help("The label of the automation to lookup (only works if you \ + are the signer of that automation)") ) .arg( Arg::new("address") .short('k') .long("address") .takes_value(true) - .help("The address of the thread to lookup"), + .help("The address of the automation to lookup"), ) ) .subcommand( Command::new("pause") - .about("Pause a thread") + .about("Pause a automation") .arg_required_else_help(true) .arg( Arg::new("id") .index(1) .takes_value(true) .required(false) - .help("The id of the thread to pause"), + .help("The id of the automation to pause"), ), ) .subcommand( - Command::new("resume").about("Resume a thread").arg( + Command::new("resume").about("Resume a automation").arg( Arg::new("id") .index(1) .takes_value(true) .required(false) - .help("The id of the thread to resume"), + .help("The id of the automation to resume"), ), ) .subcommand( - Command::new("stop").about("Stop a thread").arg( + Command::new("stop").about("Stop a automation").arg( Arg::new("id") .index(1) .takes_value(true) .required(false) - .help("The id of the thread to stop"), + .help("The id of the automation to stop"), ), ) .subcommand( Command::new("update") - .about("Update a property of a thread") + .about("Update a property of a automation") .arg_required_else_help(true) .arg( Arg::new("id") .index(1) .takes_value(true) .required(false) - .help("The id of the thread to lookup"), + .help("The id of the automation to lookup"), ) .arg( Arg::new("rate_limit") @@ -523,7 +523,7 @@ pub fn app() -> Command<'static> { .takes_value(true) .required(false) .help( - "The maximum number of instructions this thread can execute per slot", + "The maximum number of instructions this automation can execute per slot", ), ) .arg( @@ -532,7 +532,7 @@ pub fn app() -> Command<'static> { .short('s') .takes_value(true) .required(false) - .help("The cron schedule of the thread"), + .help("The cron schedule of the automation"), ), ), ) diff --git a/cli/src/parser.rs b/cli/src/parser.rs index 6f97d427..e5fe7d8d 100644 --- a/cli/src/parser.rs +++ b/cli/src/parser.rs @@ -1,7 +1,7 @@ use crate::{cli::CliCommand, errors::CliError}; use clap::ArgMatches; use clockwork_client::{ - thread::state::{AccountMetaData, InstructionData, Trigger}, + automation::state::{AccountMetaData, InstructionData, Trigger}, webhook::state::HttpMethod, }; use serde::{Deserialize as JsonDeserialize, Serialize as JsonSerialize}; @@ -25,7 +25,7 @@ impl TryFrom<&ArgMatches> for CliCommand { Some(("initialize", matches)) => parse_initialize_command(matches), Some(("localnet", matches)) => parse_bpf_command(matches), Some(("pool", matches)) => parse_pool_command(matches), - Some(("thread", matches)) => parse_thread_command(matches), + Some(("automation", matches)) => parse_automation_command(matches), Some(("registry", matches)) => parse_registry_command(matches), Some(("webhook", matches)) => parse_webhook_command(matches), Some(("worker", matches)) => parse_worker_command(matches), @@ -105,8 +105,8 @@ fn parse_config_command(matches: &ArgMatches) -> Result { Some(("get", _)) => Ok(CliCommand::ConfigGet {}), Some(("set", matches)) => Ok(CliCommand::ConfigSet { admin: parse_pubkey("admin", matches).ok(), - epoch_thread: parse_pubkey("epoch_thread", matches).ok(), - hasher_thread: parse_pubkey("hasher_thread", matches).ok(), + epoch_automation: parse_pubkey("epoch_automation", matches).ok(), + hasher_automation: parse_pubkey("hasher_automation", matches).ok(), }), _ => Err(CliError::CommandNotRecognized( matches.subcommand().unwrap().0.into(), @@ -147,7 +147,7 @@ fn parse_delegation_command(matches: &ArgMatches) -> Result Result { match matches.subcommand() { - Some(("get", matches)) => Ok(CliCommand::ExplorerGetThread { + Some(("get", matches)) => Ok(CliCommand::ExplorerGetAutomation { id: parse_string("id", matches).ok(), address: parse_pubkey("address", matches).ok(), }), @@ -179,31 +179,31 @@ fn parse_pool_command(matches: &ArgMatches) -> Result { } } -fn parse_thread_command(matches: &ArgMatches) -> Result { +fn parse_automation_command(matches: &ArgMatches) -> Result { match matches.subcommand() { - Some(("crate-info", _)) => Ok(CliCommand::ThreadCrateInfo {}), - Some(("create", matches)) => Ok(CliCommand::ThreadCreate { + Some(("crate-info", _)) => Ok(CliCommand::AutomationCrateInfo {}), + Some(("create", matches)) => Ok(CliCommand::AutomationCreate { id: parse_string("id", matches)?, kickoff_instruction: parse_instruction_file("kickoff_instruction", matches)?, trigger: parse_trigger(matches)?, }), - Some(("delete", matches)) => Ok(CliCommand::ThreadDelete { + Some(("delete", matches)) => Ok(CliCommand::AutomationDelete { id: parse_string("id", matches)?, }), - Some(("get", matches)) => Ok(CliCommand::ThreadGet { + Some(("get", matches)) => Ok(CliCommand::AutomationGet { id: parse_string("id", matches).ok(), address: parse_pubkey("address", matches).ok(), }), - Some(("pause", matches)) => Ok(CliCommand::ThreadPause { + Some(("pause", matches)) => Ok(CliCommand::AutomationPause { id: parse_string("id", matches)?, }), - Some(("resume", matches)) => Ok(CliCommand::ThreadResume { + Some(("resume", matches)) => Ok(CliCommand::AutomationResume { id: parse_string("id", matches)?, }), - Some(("stop", matches)) => Ok(CliCommand::ThreadStop { + Some(("stop", matches)) => Ok(CliCommand::AutomationStop { id: parse_string("id", matches)?, }), - Some(("update", matches)) => Ok(CliCommand::ThreadUpdate { + Some(("update", matches)) => Ok(CliCommand::AutomationUpdate { id: parse_string("id", matches)?, rate_limit: parse_u64("rate_limit", matches).ok(), schedule: parse_string("schedule", matches).ok(), diff --git a/cli/src/processor/thread.rs b/cli/src/processor/automation.rs similarity index 56% rename from cli/src/processor/thread.rs rename to cli/src/processor/automation.rs index a5a5ec9b..f715807b 100644 --- a/cli/src/processor/thread.rs +++ b/cli/src/processor/automation.rs @@ -1,7 +1,7 @@ use { crate::errors::CliError, clockwork_client::{ - thread::state::{InstructionData, Thread, ThreadSettings, Trigger}, + automation::state::{InstructionData, Automation, AutomationSettings, Trigger}, Client, }, clockwork_utils::CrateInfo, @@ -9,7 +9,7 @@ use { }; pub fn crate_info(client: &Client) -> Result<(), CliError> { - let ix = clockwork_client::thread::instruction::get_crate_info(); + let ix = clockwork_client::automation::instruction::get_crate_info(); let crate_info: CrateInfo = client.get_return_data(ix).unwrap(); println!("{:#?}", crate_info); Ok(()) @@ -21,64 +21,64 @@ pub fn create( instructions: Vec, trigger: Trigger, ) -> Result<(), CliError> { - let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.clone().into_bytes()); - let ix = clockwork_client::thread::instruction::thread_create( + let automation_pubkey = Automation::pubkey(client.payer_pubkey(), id.clone().into_bytes()); + let ix = clockwork_client::automation::instruction::automation_create( 0, client.payer_pubkey(), id.into_bytes(), instructions, client.payer_pubkey(), - thread_pubkey, + automation_pubkey, trigger, ); client.send_and_confirm(&[ix], &[client.payer()]).unwrap(); - get(client, thread_pubkey)?; + get(client, automation_pubkey)?; Ok(()) } pub fn delete(client: &Client, id: String) -> Result<(), CliError> { - let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes()); - let ix = clockwork_client::thread::instruction::thread_delete( + let automation_pubkey = Automation::pubkey(client.payer_pubkey(), id.into_bytes()); + let ix = clockwork_client::automation::instruction::automation_delete( client.payer_pubkey(), client.payer_pubkey(), - thread_pubkey, + automation_pubkey, ); client.send_and_confirm(&[ix], &[client.payer()]).unwrap(); Ok(()) } pub fn get(client: &Client, address: Pubkey) -> Result<(), CliError> { - let thread = client - .get::(&address) + let automation = client + .get::(&address) .map_err(|_err| CliError::AccountDataNotParsable(address.to_string()))?; - println!("Address: {}\n{:#?}", address, thread); + println!("Address: {}\n{:#?}", address, automation); Ok(()) } pub fn pause(client: &Client, id: String) -> Result<(), CliError> { - let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes()); + let automation_pubkey = Automation::pubkey(client.payer_pubkey(), id.into_bytes()); let ix = - clockwork_client::thread::instruction::thread_pause(client.payer_pubkey(), thread_pubkey); + clockwork_client::automation::instruction::automation_pause(client.payer_pubkey(), automation_pubkey); client.send_and_confirm(&[ix], &[client.payer()]).unwrap(); - get(client, thread_pubkey)?; + get(client, automation_pubkey)?; Ok(()) } pub fn resume(client: &Client, id: String) -> Result<(), CliError> { - let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes()); + let automation_pubkey = Automation::pubkey(client.payer_pubkey(), id.into_bytes()); let ix = - clockwork_client::thread::instruction::thread_resume(client.payer_pubkey(), thread_pubkey); + clockwork_client::automation::instruction::automation_resume(client.payer_pubkey(), automation_pubkey); client.send_and_confirm(&[ix], &[client.payer()]).unwrap(); - get(client, thread_pubkey)?; + get(client, automation_pubkey)?; Ok(()) } pub fn stop(client: &Client, id: String) -> Result<(), CliError> { - let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes()); + let automation_pubkey = Automation::pubkey(client.payer_pubkey(), id.into_bytes()); let ix = - clockwork_client::thread::instruction::thread_stop(client.payer_pubkey(), thread_pubkey); + clockwork_client::automation::instruction::automation_stop(client.payer_pubkey(), automation_pubkey); client.send_and_confirm(&[ix], &[client.payer()]).unwrap(); - get(client, thread_pubkey)?; + get(client, automation_pubkey)?; Ok(()) } @@ -88,7 +88,7 @@ pub fn update( rate_limit: Option, schedule: Option, ) -> Result<(), CliError> { - let thread_pubkey = Thread::pubkey(client.payer_pubkey(), id.into_bytes()); + let automation_pubkey = Automation::pubkey(client.payer_pubkey(), id.into_bytes()); let trigger = if let Some(schedule) = schedule { Some(Trigger::Cron { schedule, @@ -97,20 +97,20 @@ pub fn update( } else { None }; - let settings = ThreadSettings { + let settings = AutomationSettings { fee: None, instructions: None, name: None, rate_limit, trigger, }; - let ix = clockwork_client::thread::instruction::thread_update( + let ix = clockwork_client::automation::instruction::automation_update( client.payer_pubkey(), - thread_pubkey, + automation_pubkey, settings, ); client.send_and_confirm(&[ix], &[client.payer()]).unwrap(); - get(client, thread_pubkey)?; + get(client, automation_pubkey)?; Ok(()) } @@ -119,6 +119,6 @@ pub fn parse_pubkey_from_id_or_address( id: Option, address: Option, ) -> Result { - let address_from_id = id.map(|str| Thread::pubkey(authority, str.into())); + let address_from_id = id.map(|str| Automation::pubkey(authority, str.into())); address.or(address_from_id).ok_or(CliError::InvalidAddress) } diff --git a/cli/src/processor/config.rs b/cli/src/processor/config.rs index d9a7c71b..5f2397a5 100644 --- a/cli/src/processor/config.rs +++ b/cli/src/processor/config.rs @@ -18,8 +18,8 @@ pub fn get(client: &Client) -> Result<(), CliError> { pub fn set( client: &Client, admin: Option, - epoch_thread: Option, - hasher_thread: Option, + epoch_automation: Option, + hasher_automation: Option, ) -> Result<(), CliError> { // Get the current config. let config = client @@ -29,8 +29,8 @@ pub fn set( // Build new config. settings let settings = ConfigSettings { admin: admin.unwrap_or(config.admin), - epoch_thread: epoch_thread.unwrap_or(config.epoch_thread), - hasher_thread: hasher_thread.unwrap_or(config.hasher_thread), + epoch_automation: epoch_automation.unwrap_or(config.epoch_automation), + hasher_automation: hasher_automation.unwrap_or(config.hasher_automation), mint: config.mint, }; diff --git a/cli/src/processor/explorer.rs b/cli/src/processor/explorer.rs index e1994f70..077ec635 100644 --- a/cli/src/processor/explorer.rs +++ b/cli/src/processor/explorer.rs @@ -5,10 +5,10 @@ use { }; -pub fn thread_url(thread: T, config: CliConfig) -> Result<(), +pub fn automation_url(automation: T, config: CliConfig) -> Result<(), CliError> { - println!("thread: {}", explorer(config).thread_url(thread, - clockwork_client::thread::ID)); + println!("automation: {}", explorer(config).automation_url(automation, + clockwork_client::automation::ID)); Ok(()) } diff --git a/cli/src/processor/localnet.rs b/cli/src/processor/localnet.rs index 924d72fe..c1ac6068 100644 --- a/cli/src/processor/localnet.rs +++ b/cli/src/processor/localnet.rs @@ -6,7 +6,7 @@ use { anyhow::Result, clockwork_client::{ network::state::ConfigSettings, - thread::state::{Thread, Trigger}, + automation::state::{Automation, Trigger}, Client, }, regex::Regex, @@ -42,7 +42,7 @@ pub fn start( mint_clockwork_token(client).map_err(|err| CliError::FailedTransaction(err.to_string()))?; super::initialize::initialize(client, mint_pubkey)?; register_worker(client).map_err(|err| CliError::FailedTransaction(err.to_string()))?; - create_threads(client, mint_pubkey) + create_automations(client, mint_pubkey) .map_err(|err| CliError::FailedTransaction(err.to_string()))?; // Wait for process to be killed. @@ -158,56 +158,56 @@ fn register_worker(client: &Client) -> Result<()> { Ok(()) } -fn create_threads(client: &Client, mint_pubkey: Pubkey) -> Result<()> { - // Create epoch thread. - let epoch_thread_id = "clockwork.network.epoch"; - let epoch_thread_pubkey = Thread::pubkey(client.payer_pubkey(), epoch_thread_id.into()); - let ix_a = clockwork_client::thread::instruction::thread_create( +fn create_automations(client: &Client, mint_pubkey: Pubkey) -> Result<()> { + // Create epoch automation. + let epoch_automation_id = "clockwork.network.epoch"; + let epoch_automation_pubkey = Automation::pubkey(client.payer_pubkey(), epoch_automation_id.into()); + let ix_a = clockwork_client::automation::instruction::automation_create( LAMPORTS_PER_SOL, client.payer_pubkey(), - epoch_thread_id.into(), + epoch_automation_id.into(), vec![ - clockwork_client::network::job::distribute_fees(epoch_thread_pubkey).into(), - clockwork_client::network::job::process_unstakes(epoch_thread_pubkey).into(), - clockwork_client::network::job::stake_delegations(epoch_thread_pubkey).into(), - clockwork_client::network::job::take_snapshot(epoch_thread_pubkey).into(), - clockwork_client::network::job::increment_epoch(epoch_thread_pubkey).into(), - clockwork_client::network::job::delete_snapshot(epoch_thread_pubkey).into(), + clockwork_client::network::job::distribute_fees(epoch_automation_pubkey).into(), + clockwork_client::network::job::process_unstakes(epoch_automation_pubkey).into(), + clockwork_client::network::job::stake_delegations(epoch_automation_pubkey).into(), + clockwork_client::network::job::take_snapshot(epoch_automation_pubkey).into(), + clockwork_client::network::job::increment_epoch(epoch_automation_pubkey).into(), + clockwork_client::network::job::delete_snapshot(epoch_automation_pubkey).into(), ], client.payer_pubkey(), - epoch_thread_pubkey, + epoch_automation_pubkey, Trigger::Cron { schedule: "0 * * * * * *".into(), skippable: true, }, ); - // Create hasher thread. - let hasher_thread_id = "clockwork.network.hasher"; - let hasher_thread_pubkey = Thread::pubkey(client.payer_pubkey(), hasher_thread_id.into()); - let ix_b = clockwork_client::thread::instruction::thread_create( + // Create hasher automation. + let hasher_automation_id = "clockwork.network.hasher"; + let hasher_automation_pubkey = Automation::pubkey(client.payer_pubkey(), hasher_automation_id.into()); + let ix_b = clockwork_client::automation::instruction::automation_create( LAMPORTS_PER_SOL, client.payer_pubkey(), - hasher_thread_id.into(), + hasher_automation_id.into(), vec![ - clockwork_client::network::instruction::registry_nonce_hash(hasher_thread_pubkey) + clockwork_client::network::instruction::registry_nonce_hash(hasher_automation_pubkey) .into(), ], client.payer_pubkey(), - hasher_thread_pubkey, + hasher_automation_pubkey, Trigger::Cron { schedule: "*/15 * * * * * *".into(), skippable: true, }, ); - // Update config with thread pubkeys + // Update config with automation pubkeys let ix_c = clockwork_client::network::instruction::config_update( client.payer_pubkey(), ConfigSettings { admin: client.payer_pubkey(), - epoch_thread: epoch_thread_pubkey, - hasher_thread: hasher_thread_pubkey, + epoch_automation: epoch_automation_pubkey, + hasher_automation: hasher_automation_pubkey, mint: mint_pubkey, }, ); @@ -234,7 +234,7 @@ fn start_test_validator( let mut process = Command::new("solana-test-validator") .arg("-r") .bpf_program(home_dir, clockwork_client::network::ID, "network") - .bpf_program(home_dir, clockwork_client::thread::ID, "thread") + .bpf_program(home_dir, clockwork_client::automation::ID, "automation") .bpf_program(home_dir, clockwork_client::webhook::ID, "webhook") .network_url(network_url) .clone_addresses(clone_addresses) diff --git a/cli/src/processor/mod.rs b/cli/src/processor/mod.rs index 9d45d1a8..20465a02 100644 --- a/cli/src/processor/mod.rs +++ b/cli/src/processor/mod.rs @@ -8,7 +8,7 @@ mod localnet; mod pool; mod process; mod registry; -mod thread; +mod automation; mod webhook; mod worker; diff --git a/cli/src/processor/process.rs b/cli/src/processor/process.rs index 2c996584..07aa147d 100644 --- a/cli/src/processor/process.rs +++ b/cli/src/processor/process.rs @@ -1,6 +1,6 @@ use crate::{ cli::CliCommand, config::CliConfig, errors::CliError, - processor::thread::parse_pubkey_from_id_or_address, + processor::automation::parse_pubkey_from_id_or_address, }; use anyhow::Result; use clap::ArgMatches; @@ -41,9 +41,9 @@ pub fn process(matches: &ArgMatches) -> Result<(), CliError> { CliCommand::ConfigGet => super::config::get(&client), CliCommand::ConfigSet { admin, - epoch_thread, - hasher_thread, - } => super::config::set(&client, admin, epoch_thread, hasher_thread), + epoch_automation, + hasher_automation, + } => super::config::set(&client, admin, epoch_automation, hasher_automation), CliCommand::Crontab { schedule } => super::crontab::get(&client, schedule), CliCommand::DelegationCreate { worker_id } => super::delegation::create(&client, worker_id), CliCommand::DelegationDeposit { @@ -60,9 +60,9 @@ pub fn process(matches: &ArgMatches) -> Result<(), CliError> { delegation_id, worker_id, } => super::delegation::withdraw(&client, amount, delegation_id, worker_id), - CliCommand::ExplorerGetThread { id, address } => { + CliCommand::ExplorerGetAutomation { id, address } => { let pubkey = parse_pubkey_from_id_or_address(client.payer_pubkey(), id, address)?; - super::explorer::thread_url(pubkey, config) + super::explorer::automation_url(pubkey, config) } CliCommand::Initialize { mint } => super::initialize::initialize(&client, mint), CliCommand::Localnet { @@ -73,25 +73,25 @@ pub fn process(matches: &ArgMatches) -> Result<(), CliError> { CliCommand::PoolGet { id } => super::pool::get(&client, id), CliCommand::PoolList {} => super::pool::list(&client), CliCommand::PoolUpdate { id, size } => super::pool::update(&client, id, size), - CliCommand::ThreadCrateInfo {} => super::thread::crate_info(&client), - CliCommand::ThreadCreate { + CliCommand::AutomationCrateInfo {} => super::automation::crate_info(&client), + CliCommand::AutomationCreate { id, kickoff_instruction, trigger, - } => super::thread::create(&client, id, vec![kickoff_instruction], trigger), - CliCommand::ThreadDelete { id } => super::thread::delete(&client, id), - CliCommand::ThreadPause { id } => super::thread::pause(&client, id), - CliCommand::ThreadResume { id } => super::thread::resume(&client, id), - CliCommand::ThreadStop { id } => super::thread::stop(&client, id), - CliCommand::ThreadGet { id, address } => { + } => super::automation::create(&client, id, vec![kickoff_instruction], trigger), + CliCommand::AutomationDelete { id } => super::automation::delete(&client, id), + CliCommand::AutomationPause { id } => super::automation::pause(&client, id), + CliCommand::AutomationResume { id } => super::automation::resume(&client, id), + CliCommand::AutomationStop { id } => super::automation::stop(&client, id), + CliCommand::AutomationGet { id, address } => { let pubkey = parse_pubkey_from_id_or_address(client.payer_pubkey(), id, address)?; - super::thread::get(&client, pubkey) + super::automation::get(&client, pubkey) } - CliCommand::ThreadUpdate { + CliCommand::AutomationUpdate { id, rate_limit, schedule, - } => super::thread::update(&client, id, rate_limit, schedule), + } => super::automation::update(&client, id, rate_limit, schedule), CliCommand::RegistryGet => super::registry::get(&client), CliCommand::RegistryUnlock => super::registry::unlock(&client), CliCommand::WebhookRequestNew { diff --git a/client/Cargo.toml b/client/Cargo.toml index f9052617..b111787f 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -20,7 +20,7 @@ anchor-spl = { features = ["mint", "token"], version = "0.26.0" } bincode = "1.3.3" borsh = "0.9.3" clockwork-network-program = { path = "../programs/network", features = ["no-entrypoint"], version = "1.4.0" } -clockwork-thread-program = { path = "../programs/thread", features = ["no-entrypoint"], version = "1.4.0" } +clockwork-automation-program = { path = "../programs/automation", features = ["no-entrypoint"], version = "1.4.0" } clockwork-utils = { path = "../utils", version = "1.4.0" } clockwork-webhook-program = { path = "../programs/webhook", features = ["no-entrypoint"], version = "1.4.0" } solana-client = "~1.14.12" diff --git a/client/src/thread/instruction/thread_create.rs b/client/src/automation/instruction/automation_create.rs similarity index 68% rename from client/src/thread/instruction/thread_create.rs rename to client/src/automation/instruction/automation_create.rs index a377b9c0..d98763e0 100644 --- a/client/src/thread/instruction/thread_create.rs +++ b/client/src/automation/instruction/automation_create.rs @@ -7,27 +7,27 @@ use { }, InstructionData, }, - clockwork_thread_program::state::{InstructionData as ClockworkInstructionData, Trigger}, + clockwork_automation_program::state::{InstructionData as ClockworkInstructionData, Trigger}, }; -pub fn thread_create( +pub fn automation_create( amount: u64, authority: Pubkey, id: Vec, instructions: Vec, payer: Pubkey, - thread: Pubkey, + automation: Pubkey, trigger: Trigger, ) -> Instruction { Instruction { - program_id: clockwork_thread_program::ID, + program_id: clockwork_automation_program::ID, accounts: vec![ AccountMeta::new_readonly(authority, true), AccountMeta::new(payer, true), AccountMeta::new_readonly(system_program::ID, false), - AccountMeta::new(thread, false), + AccountMeta::new(automation, false), ], - data: clockwork_thread_program::instruction::ThreadCreate { + data: clockwork_automation_program::instruction::AutomationCreate { amount, id, instructions, diff --git a/client/src/thread/instruction/thread_delete.rs b/client/src/automation/instruction/automation_delete.rs similarity index 51% rename from client/src/thread/instruction/thread_delete.rs rename to client/src/automation/instruction/automation_delete.rs index 294465ae..2aeac86c 100644 --- a/client/src/thread/instruction/thread_delete.rs +++ b/client/src/automation/instruction/automation_delete.rs @@ -6,14 +6,14 @@ use anchor_lang::{ InstructionData, }; -pub fn thread_delete(authority: Pubkey, close_to: Pubkey, thread: Pubkey) -> Instruction { +pub fn automation_delete(authority: Pubkey, close_to: Pubkey, automation: Pubkey) -> Instruction { Instruction { - program_id: clockwork_thread_program::ID, + program_id: clockwork_automation_program::ID, accounts: vec![ AccountMeta::new_readonly(authority, true), AccountMeta::new(close_to, true), - AccountMeta::new(thread, false), + AccountMeta::new(automation, false), ], - data: clockwork_thread_program::instruction::ThreadDelete {}.data(), + data: clockwork_automation_program::instruction::AutomationDelete {}.data(), } } diff --git a/client/src/thread/instruction/thread_exec.rs b/client/src/automation/instruction/automation_exec.rs similarity index 63% rename from client/src/thread/instruction/thread_exec.rs rename to client/src/automation/instruction/automation_exec.rs index 45dc4d51..ecd26313 100644 --- a/client/src/thread/instruction/thread_exec.rs +++ b/client/src/automation/instruction/automation_exec.rs @@ -7,16 +7,16 @@ use anchor_lang::{ }; use clockwork_network_program::state::{Fee, Pool}; -pub fn thread_exec(signatory: Pubkey, thread: Pubkey, worker: Pubkey) -> Instruction { +pub fn automation_exec(signatory: Pubkey, automation: Pubkey, worker: Pubkey) -> Instruction { Instruction { - program_id: clockwork_thread_program::ID, + program_id: clockwork_automation_program::ID, accounts: vec![ AccountMeta::new(Fee::pubkey(worker), false), AccountMeta::new_readonly(Pool::pubkey(0), false), AccountMeta::new(signatory, true), - AccountMeta::new(thread, false), + AccountMeta::new(automation, false), AccountMeta::new_readonly(worker, false), ], - data: clockwork_thread_program::instruction::ThreadExec {}.data(), + data: clockwork_automation_program::instruction::AutomationExec {}.data(), } } diff --git a/client/src/thread/instruction/thread_kickoff.rs b/client/src/automation/instruction/automation_kickoff.rs similarity index 51% rename from client/src/thread/instruction/thread_kickoff.rs rename to client/src/automation/instruction/automation_kickoff.rs index cd0ffdce..430fc1c9 100644 --- a/client/src/thread/instruction/thread_kickoff.rs +++ b/client/src/automation/instruction/automation_kickoff.rs @@ -6,14 +6,14 @@ use anchor_lang::{ InstructionData, }; -pub fn thread_kickoff(signatory: Pubkey, thread: Pubkey, worker: Pubkey) -> Instruction { +pub fn automation_kickoff(signatory: Pubkey, automation: Pubkey, worker: Pubkey) -> Instruction { Instruction { - program_id: clockwork_thread_program::ID, + program_id: clockwork_automation_program::ID, accounts: vec![ AccountMeta::new(signatory, true), - AccountMeta::new(thread, false), + AccountMeta::new(automation, false), AccountMeta::new_readonly(worker, false), ], - data: clockwork_thread_program::instruction::ThreadKickoff {}.data(), + data: clockwork_automation_program::instruction::AutomationKickoff {}.data(), } } diff --git a/client/src/automation/instruction/automation_pause.rs b/client/src/automation/instruction/automation_pause.rs new file mode 100644 index 00000000..8e226ac2 --- /dev/null +++ b/client/src/automation/instruction/automation_pause.rs @@ -0,0 +1,18 @@ +use anchor_lang::{ + solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + }, + InstructionData, +}; + +pub fn automation_pause(authority: Pubkey, automation: Pubkey) -> Instruction { + Instruction { + program_id: clockwork_automation_program::ID, + accounts: vec![ + AccountMeta::new_readonly(authority, true), + AccountMeta::new(automation, false), + ], + data: clockwork_automation_program::instruction::AutomationPause {}.data(), + } +} diff --git a/client/src/automation/instruction/automation_resume.rs b/client/src/automation/instruction/automation_resume.rs new file mode 100644 index 00000000..4719cd86 --- /dev/null +++ b/client/src/automation/instruction/automation_resume.rs @@ -0,0 +1,18 @@ +use anchor_lang::{ + solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + }, + InstructionData, +}; + +pub fn automation_resume(authority: Pubkey, automation: Pubkey) -> Instruction { + Instruction { + program_id: clockwork_automation_program::ID, + accounts: vec![ + AccountMeta::new_readonly(authority, true), + AccountMeta::new(automation, false), + ], + data: clockwork_automation_program::instruction::AutomationResume {}.data(), + } +} diff --git a/client/src/automation/instruction/automation_stop.rs b/client/src/automation/instruction/automation_stop.rs new file mode 100644 index 00000000..67628aac --- /dev/null +++ b/client/src/automation/instruction/automation_stop.rs @@ -0,0 +1,18 @@ +use anchor_lang::{ + solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + }, + InstructionData, +}; + +pub fn automation_stop(authority: Pubkey, automation: Pubkey) -> Instruction { + Instruction { + program_id: clockwork_automation_program::ID, + accounts: vec![ + AccountMeta::new_readonly(authority, true), + AccountMeta::new(automation, false), + ], + data: clockwork_automation_program::instruction::AutomationStop {}.data(), + } +} diff --git a/client/src/automation/instruction/automation_update.rs b/client/src/automation/instruction/automation_update.rs new file mode 100644 index 00000000..c16f3c06 --- /dev/null +++ b/client/src/automation/instruction/automation_update.rs @@ -0,0 +1,22 @@ +use clockwork_automation_program::state::AutomationSettings; + +use anchor_lang::{ + solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + system_program, + }, + InstructionData, +}; + +pub fn automation_update(authority: Pubkey, automation: Pubkey, settings: AutomationSettings) -> Instruction { + Instruction { + program_id: clockwork_automation_program::ID, + accounts: vec![ + AccountMeta::new_readonly(authority, true), + AccountMeta::new_readonly(system_program::ID, false), + AccountMeta::new(automation, false), + ], + data: clockwork_automation_program::instruction::AutomationUpdate { settings }.data(), + } +} diff --git a/client/src/thread/instruction/get_crate_info.rs b/client/src/automation/instruction/get_crate_info.rs similarity index 68% rename from client/src/thread/instruction/get_crate_info.rs rename to client/src/automation/instruction/get_crate_info.rs index 2d4e0faa..4b4c2af3 100644 --- a/client/src/thread/instruction/get_crate_info.rs +++ b/client/src/automation/instruction/get_crate_info.rs @@ -8,8 +8,8 @@ use anchor_lang::{ pub fn get_crate_info() -> Instruction { Instruction { - program_id: clockwork_thread_program::ID, + program_id: clockwork_automation_program::ID, accounts: vec![AccountMeta::new_readonly(system_program::ID, false)], - data: clockwork_thread_program::instruction::GetCrateInfo {}.data(), + data: clockwork_automation_program::instruction::GetCrateInfo {}.data(), } } diff --git a/client/src/automation/instruction/mod.rs b/client/src/automation/instruction/mod.rs new file mode 100644 index 00000000..58d09270 --- /dev/null +++ b/client/src/automation/instruction/mod.rs @@ -0,0 +1,19 @@ +mod get_crate_info; +mod automation_create; +mod automation_delete; +mod automation_exec; +mod automation_kickoff; +mod automation_pause; +mod automation_resume; +mod automation_stop; +mod automation_update; + +pub use get_crate_info::*; +pub use automation_create::*; +pub use automation_delete::*; +pub use automation_exec::*; +pub use automation_kickoff::*; +pub use automation_pause::*; +pub use automation_resume::*; +pub use automation_stop::*; +pub use automation_update::*; diff --git a/client/src/automation/mod.rs b/client/src/automation/mod.rs new file mode 100644 index 00000000..b7896244 --- /dev/null +++ b/client/src/automation/mod.rs @@ -0,0 +1,5 @@ +pub mod instruction; + +pub use clockwork_automation_program::errors; +pub use clockwork_automation_program::state; +pub use clockwork_automation_program::ID; diff --git a/client/src/lib.rs b/client/src/lib.rs index 8032bbad..755c19ad 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1,5 +1,5 @@ pub mod network; -pub mod thread; +pub mod automation; pub mod webhook; mod client; diff --git a/client/src/network/instruction/registry_nonce_hash.rs b/client/src/network/instruction/registry_nonce_hash.rs index 006a332c..f80548b3 100644 --- a/client/src/network/instruction/registry_nonce_hash.rs +++ b/client/src/network/instruction/registry_nonce_hash.rs @@ -9,13 +9,13 @@ use { clockwork_network_program::state::*, }; -pub fn registry_nonce_hash(thread: Pubkey) -> Instruction { +pub fn registry_nonce_hash(automation: Pubkey) -> Instruction { Instruction { program_id: clockwork_network_program::ID, accounts: vec![ AccountMeta::new_readonly(Config::pubkey(), false), AccountMeta::new(Registry::pubkey(), false), - AccountMeta::new_readonly(thread, true), + AccountMeta::new_readonly(automation, true), ], data: clockwork_network_program::instruction::RegistryNonceHash {}.data(), } diff --git a/client/src/network/job/delete_snapshot.rs b/client/src/network/job/delete_snapshot.rs index 0cf08d85..6f424896 100644 --- a/client/src/network/job/delete_snapshot.rs +++ b/client/src/network/job/delete_snapshot.rs @@ -9,13 +9,13 @@ use { clockwork_network_program::state::*, }; -pub fn delete_snapshot(thread: Pubkey) -> Instruction { +pub fn delete_snapshot(automation: Pubkey) -> Instruction { Instruction { program_id: clockwork_network_program::ID, accounts: vec![ AccountMeta::new_readonly(Config::pubkey(), false), AccountMeta::new(Registry::pubkey(), false), - AccountMeta::new_readonly(thread, true), + AccountMeta::new_readonly(automation, true), ], data: clockwork_network_program::instruction::DeleteSnapshotJob {}.data(), } diff --git a/client/src/network/job/distribute_fees.rs b/client/src/network/job/distribute_fees.rs index 43061cce..b1079200 100644 --- a/client/src/network/job/distribute_fees.rs +++ b/client/src/network/job/distribute_fees.rs @@ -9,13 +9,13 @@ use { clockwork_network_program::state::*, }; -pub fn distribute_fees(thread: Pubkey) -> Instruction { +pub fn distribute_fees(automation: Pubkey) -> Instruction { Instruction { program_id: clockwork_network_program::ID, accounts: vec![ AccountMeta::new_readonly(Config::pubkey(), false), AccountMeta::new(Registry::pubkey(), false), - AccountMeta::new_readonly(thread, true), + AccountMeta::new_readonly(automation, true), ], data: clockwork_network_program::instruction::DistributeFeesJob {}.data(), } diff --git a/client/src/network/job/increment_epoch.rs b/client/src/network/job/increment_epoch.rs index 9e44fce2..2d0ea825 100644 --- a/client/src/network/job/increment_epoch.rs +++ b/client/src/network/job/increment_epoch.rs @@ -9,13 +9,13 @@ use { clockwork_network_program::state::*, }; -pub fn increment_epoch(thread: Pubkey) -> Instruction { +pub fn increment_epoch(automation: Pubkey) -> Instruction { Instruction { program_id: clockwork_network_program::ID, accounts: vec![ AccountMeta::new_readonly(Config::pubkey(), false), AccountMeta::new(Registry::pubkey(), false), - AccountMeta::new_readonly(thread, true), + AccountMeta::new_readonly(automation, true), ], data: clockwork_network_program::instruction::IncrementEpoch {}.data(), } diff --git a/client/src/network/job/process_unstakes.rs b/client/src/network/job/process_unstakes.rs index 5bb315ba..7e576603 100644 --- a/client/src/network/job/process_unstakes.rs +++ b/client/src/network/job/process_unstakes.rs @@ -9,13 +9,13 @@ use { clockwork_network_program::state::*, }; -pub fn process_unstakes(thread: Pubkey) -> Instruction { +pub fn process_unstakes(automation: Pubkey) -> Instruction { Instruction { program_id: clockwork_network_program::ID, accounts: vec![ AccountMeta::new_readonly(Config::pubkey(), false), AccountMeta::new_readonly(Registry::pubkey(), false), - AccountMeta::new_readonly(thread, true), + AccountMeta::new_readonly(automation, true), ], data: clockwork_network_program::instruction::ProcessUnstakesJob {}.data(), } diff --git a/client/src/network/job/stake_delegations.rs b/client/src/network/job/stake_delegations.rs index 6ba9a618..a4d22865 100644 --- a/client/src/network/job/stake_delegations.rs +++ b/client/src/network/job/stake_delegations.rs @@ -9,13 +9,13 @@ use { clockwork_network_program::state::*, }; -pub fn stake_delegations(thread: Pubkey) -> Instruction { +pub fn stake_delegations(automation: Pubkey) -> Instruction { Instruction { program_id: clockwork_network_program::ID, accounts: vec![ AccountMeta::new_readonly(Config::pubkey(), false), AccountMeta::new(Registry::pubkey(), false), - AccountMeta::new_readonly(thread, true), + AccountMeta::new_readonly(automation, true), ], data: clockwork_network_program::instruction::StakeDelegationsJob {}.data(), } diff --git a/client/src/network/job/take_snapshot.rs b/client/src/network/job/take_snapshot.rs index f33cebdf..50b7fdbe 100644 --- a/client/src/network/job/take_snapshot.rs +++ b/client/src/network/job/take_snapshot.rs @@ -9,13 +9,13 @@ use { clockwork_network_program::state::*, }; -pub fn take_snapshot(thread: Pubkey) -> Instruction { +pub fn take_snapshot(automation: Pubkey) -> Instruction { Instruction { program_id: clockwork_network_program::ID, accounts: vec![ AccountMeta::new_readonly(Config::pubkey(), false), AccountMeta::new(Registry::pubkey(), false), - AccountMeta::new_readonly(thread, true), + AccountMeta::new_readonly(automation, true), ], data: clockwork_network_program::instruction::TakeSnapshotJob {}.data(), } diff --git a/client/src/thread/instruction/mod.rs b/client/src/thread/instruction/mod.rs deleted file mode 100644 index 1adb372c..00000000 --- a/client/src/thread/instruction/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod get_crate_info; -mod thread_create; -mod thread_delete; -mod thread_exec; -mod thread_kickoff; -mod thread_pause; -mod thread_resume; -mod thread_stop; -mod thread_update; - -pub use get_crate_info::*; -pub use thread_create::*; -pub use thread_delete::*; -pub use thread_exec::*; -pub use thread_kickoff::*; -pub use thread_pause::*; -pub use thread_resume::*; -pub use thread_stop::*; -pub use thread_update::*; diff --git a/client/src/thread/instruction/thread_pause.rs b/client/src/thread/instruction/thread_pause.rs deleted file mode 100644 index 48a4248c..00000000 --- a/client/src/thread/instruction/thread_pause.rs +++ /dev/null @@ -1,18 +0,0 @@ -use anchor_lang::{ - solana_program::{ - instruction::{AccountMeta, Instruction}, - pubkey::Pubkey, - }, - InstructionData, -}; - -pub fn thread_pause(authority: Pubkey, thread: Pubkey) -> Instruction { - Instruction { - program_id: clockwork_thread_program::ID, - accounts: vec![ - AccountMeta::new_readonly(authority, true), - AccountMeta::new(thread, false), - ], - data: clockwork_thread_program::instruction::ThreadPause {}.data(), - } -} diff --git a/client/src/thread/instruction/thread_resume.rs b/client/src/thread/instruction/thread_resume.rs deleted file mode 100644 index 41cb026a..00000000 --- a/client/src/thread/instruction/thread_resume.rs +++ /dev/null @@ -1,18 +0,0 @@ -use anchor_lang::{ - solana_program::{ - instruction::{AccountMeta, Instruction}, - pubkey::Pubkey, - }, - InstructionData, -}; - -pub fn thread_resume(authority: Pubkey, thread: Pubkey) -> Instruction { - Instruction { - program_id: clockwork_thread_program::ID, - accounts: vec![ - AccountMeta::new_readonly(authority, true), - AccountMeta::new(thread, false), - ], - data: clockwork_thread_program::instruction::ThreadResume {}.data(), - } -} diff --git a/client/src/thread/instruction/thread_stop.rs b/client/src/thread/instruction/thread_stop.rs deleted file mode 100644 index 4b2c0255..00000000 --- a/client/src/thread/instruction/thread_stop.rs +++ /dev/null @@ -1,18 +0,0 @@ -use anchor_lang::{ - solana_program::{ - instruction::{AccountMeta, Instruction}, - pubkey::Pubkey, - }, - InstructionData, -}; - -pub fn thread_stop(authority: Pubkey, thread: Pubkey) -> Instruction { - Instruction { - program_id: clockwork_thread_program::ID, - accounts: vec![ - AccountMeta::new_readonly(authority, true), - AccountMeta::new(thread, false), - ], - data: clockwork_thread_program::instruction::ThreadStop {}.data(), - } -} diff --git a/client/src/thread/instruction/thread_update.rs b/client/src/thread/instruction/thread_update.rs deleted file mode 100644 index 95cbd207..00000000 --- a/client/src/thread/instruction/thread_update.rs +++ /dev/null @@ -1,22 +0,0 @@ -use clockwork_thread_program::state::ThreadSettings; - -use anchor_lang::{ - solana_program::{ - instruction::{AccountMeta, Instruction}, - pubkey::Pubkey, - system_program, - }, - InstructionData, -}; - -pub fn thread_update(authority: Pubkey, thread: Pubkey, settings: ThreadSettings) -> Instruction { - Instruction { - program_id: clockwork_thread_program::ID, - accounts: vec![ - AccountMeta::new_readonly(authority, true), - AccountMeta::new_readonly(system_program::ID, false), - AccountMeta::new(thread, false), - ], - data: clockwork_thread_program::instruction::ThreadUpdate { settings }.data(), - } -} diff --git a/client/src/thread/mod.rs b/client/src/thread/mod.rs deleted file mode 100644 index ca960d33..00000000 --- a/client/src/thread/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod instruction; - -pub use clockwork_thread_program::errors; -pub use clockwork_thread_program::state; -pub use clockwork_thread_program::ID; diff --git a/plugin/config.json b/plugin/config.json index 81d29d63..932594c7 100644 --- a/plugin/config.json +++ b/plugin/config.json @@ -2,5 +2,5 @@ "libpath": "../target/debug/libclockwork_plugin.dylib", "keypath": "./test-ledger/validator-keypair.json", "slot_timeout_threshold": 150, - "worker_threads": 10 + "worker_automations": 10 } \ No newline at end of file diff --git a/plugin/src/builders/thread_exec.rs b/plugin/src/builders/automation_exec.rs similarity index 77% rename from plugin/src/builders/thread_exec.rs rename to plugin/src/builders/automation_exec.rs index 64ca4b92..3fb16b4c 100644 --- a/plugin/src/builders/thread_exec.rs +++ b/plugin/src/builders/automation_exec.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use clockwork_client::{ network::state::Worker, - thread::state::{Thread, Trigger}, + automation::state::{Automation, Trigger}, }; use clockwork_utils::automation::PAYER_PUBKEY; use log::info; @@ -30,23 +30,23 @@ static TRANSACTION_COMPUTE_UNIT_LIMIT: u32 = 1_400_000; /// The buffer amount to add to transactions' compute units in case on-chain PDA derivations take more CUs than used in simulation. static TRANSACTION_COMPUTE_UNIT_BUFFER: u32 = 1000; -pub async fn build_thread_exec_tx( +pub async fn build_automation_exec_tx( client: Arc, payer: &Keypair, - thread: Thread, - thread_pubkey: Pubkey, + automation: Automation, + automation_pubkey: Pubkey, worker_id: u64, ) -> Option { - // Grab the thread and relevant data. + // Grab the automation and relevant data. let now = std::time::Instant::now(); let blockhash = client.get_latest_blockhash().await.unwrap(); let signatory_pubkey = payer.pubkey(); // Build the first instruction of the transaction. - let first_instruction = if thread.next_instruction.is_some() { - build_exec_ix(thread, signatory_pubkey, worker_id) + let first_instruction = if automation.next_instruction.is_some() { + build_exec_ix(automation, signatory_pubkey, worker_id) } else { - build_kickoff_ix(thread, signatory_pubkey, worker_id) + build_kickoff_ix(automation, signatory_pubkey, worker_id) }; // Simulate the transactino and pack as many instructions as possible until we hit mem/cpu limits. @@ -75,7 +75,7 @@ pub async fn build_thread_exec_tx( commitment: Some(CommitmentConfig::processed()), accounts: Some(RpcSimulateTransactionAccountsConfig { encoding: Some(UiAccountEncoding::Base64Zstd), - addresses: vec![thread_pubkey.to_string()], + addresses: vec![automation_pubkey.to_string()], }), ..RpcSimulateTransactionConfig::default() }, @@ -92,8 +92,8 @@ pub async fn build_thread_exec_tx( if response.value.err.is_some() { if successful_ixs.is_empty() { info!( - "thread: {} simulation_error: \"{}\" logs: {:?}", - thread_pubkey, + "automation: {} simulation_error: \"{}\" logs: {:?}", + automation_pubkey, response.value.err.unwrap(), response.value.logs.unwrap_or(vec![]) ); @@ -109,22 +109,22 @@ pub async fn build_thread_exec_tx( units_consumed = response.value.units_consumed; } - // Parse the resulting thread account for the next instruction to simulate. + // Parse the resulting automation account for the next instruction to simulate. if let Some(ui_accounts) = response.value.accounts { if let Some(Some(ui_account)) = ui_accounts.get(0) { if let Some(account) = ui_account.decode::() { - if let Ok(sim_thread) = Thread::try_from(account.data) { - if sim_thread.next_instruction.is_some() { - if let Some(exec_context) = sim_thread.exec_context { - if exec_context.execs_since_slot.lt(&sim_thread.rate_limit) + if let Ok(sim_automation) = Automation::try_from(account.data) { + if sim_automation.next_instruction.is_some() { + if let Some(exec_context) = sim_automation.exec_context { + if exec_context.execs_since_slot.lt(&sim_automation.rate_limit) { ixs.push(build_exec_ix( - sim_thread, + sim_automation, signatory_pubkey, worker_id, )); } else { - // Exit early if the thread has reached its rate limit. + // Exit early if the automation has reached its rate limit. break; } } @@ -160,8 +160,8 @@ pub async fn build_thread_exec_tx( let mut tx = Transaction::new_with_payer(&successful_ixs, Some(&signatory_pubkey)); tx.sign(&[payer], blockhash); info!( - "thread: {:?} sim_duration: {:?} instruction_count: {:?} compute_units: {:?} tx_sig: {:?}", - thread_pubkey, + "automation: {:?} sim_duration: {:?} instruction_count: {:?} compute_units: {:?} tx_sig: {:?}", + automation_pubkey, now.elapsed(), successful_ixs.len(), units_consumed, @@ -170,17 +170,17 @@ pub async fn build_thread_exec_tx( Some(tx) } -fn build_kickoff_ix(thread: Thread, signatory_pubkey: Pubkey, worker_id: u64) -> Instruction { +fn build_kickoff_ix(automation: Automation, signatory_pubkey: Pubkey, worker_id: u64) -> Instruction { // Build the instruction. - let thread_pubkey = Thread::pubkey(thread.authority, thread.id); - let mut kickoff_ix = clockwork_client::thread::instruction::thread_kickoff( + let automation_pubkey = Automation::pubkey(automation.authority, automation.id); + let mut kickoff_ix = clockwork_client::automation::instruction::automation_kickoff( signatory_pubkey, - thread_pubkey, + automation_pubkey, Worker::pubkey(worker_id), ); - // If the thread's trigger is account-based, inject the triggering account. - match thread.trigger { + // If the automation's trigger is account-based, inject the triggering account. + match automation.trigger { Trigger::Account { address, offset: _, @@ -196,16 +196,16 @@ fn build_kickoff_ix(thread: Thread, signatory_pubkey: Pubkey, worker_id: u64) -> kickoff_ix } -fn build_exec_ix(thread: Thread, signatory_pubkey: Pubkey, worker_id: u64) -> Instruction { +fn build_exec_ix(automation: Automation, signatory_pubkey: Pubkey, worker_id: u64) -> Instruction { // Build the instruction. - let thread_pubkey = Thread::pubkey(thread.authority, thread.id); - let mut exec_ix = clockwork_client::thread::instruction::thread_exec( + let automation_pubkey = Automation::pubkey(automation.authority, automation.id); + let mut exec_ix = clockwork_client::automation::instruction::automation_exec( signatory_pubkey, - thread_pubkey, + automation_pubkey, Worker::pubkey(worker_id), ); - if let Some(next_instruction) = thread.next_instruction { + if let Some(next_instruction) = automation.next_instruction { // Inject the target program account. exec_ix.accounts.push(AccountMeta::new_readonly( next_instruction.program_id, diff --git a/plugin/src/builders/mod.rs b/plugin/src/builders/mod.rs index a0cf122f..3f57d36a 100644 --- a/plugin/src/builders/mod.rs +++ b/plugin/src/builders/mod.rs @@ -1,5 +1,5 @@ mod pool_rotation; -mod thread_exec; +mod automation_exec; pub use pool_rotation::*; -pub use thread_exec::*; +pub use automation_exec::*; diff --git a/plugin/src/events.rs b/plugin/src/events.rs index b4074b62..04960bb2 100644 --- a/plugin/src/events.rs +++ b/plugin/src/events.rs @@ -1,6 +1,6 @@ use anchor_lang::Discriminator; use bincode::deserialize; -use clockwork_client::{thread::state::Thread, webhook::state::Request}; +use clockwork_client::{automation::state::Automation, webhook::state::Request}; use log::info; use solana_geyser_plugin_interface::geyser_plugin_interface::{ GeyserPluginError, ReplicaAccountInfo, @@ -11,7 +11,7 @@ use solana_program::{clock::Clock, pubkey::Pubkey, sysvar}; pub enum AccountUpdateEvent { Clock { clock: Clock }, HttpRequest { request: Request }, - Thread { thread: Thread }, + Automation { automation: Automation }, } impl TryFrom> for AccountUpdateEvent { @@ -41,14 +41,14 @@ impl TryFrom> for AccountUpdateEvent { }); } - // If the account belongs to the thread program, parse it. - if owner_pubkey.eq(&clockwork_client::thread::ID) && account_info.data.len() > 8 { + // If the account belongs to the automation program, parse it. + if owner_pubkey.eq(&clockwork_client::automation::ID) && account_info.data.len() > 8 { let d = &account_info.data[..8]; - if d.eq(&Thread::discriminator()) { - return Ok(AccountUpdateEvent::Thread { - thread: Thread::try_from(account_info.data.to_vec()).map_err(|_| { + if d.eq(&Automation::discriminator()) { + return Ok(AccountUpdateEvent::Automation { + automation: Automation::try_from(account_info.data.to_vec()).map_err(|_| { GeyserPluginError::AccountsUpdateError { - msg: "Failed to parse Clockwork thread account".into(), + msg: "Failed to parse Clockwork automation account".into(), } })?, }); diff --git a/plugin/src/executors/mod.rs b/plugin/src/executors/mod.rs index ba4f6a5f..7f5ed0a6 100644 --- a/plugin/src/executors/mod.rs +++ b/plugin/src/executors/mod.rs @@ -81,14 +81,14 @@ impl Executors { } // Process the slot on the observers. - let executable_threads = observers.thread.clone().process_slot(slot).await?; + let executable_automations = observers.automation.clone().process_slot(slot).await?; // Process the slot in the transaction executor. self.tx .clone() .execute_txs( self.client.clone(), - executable_threads, + executable_automations, slot, runtime.clone(), ) diff --git a/plugin/src/executors/tx.rs b/plugin/src/executors/tx.rs index 4d5f0c6a..d4890a8e 100644 --- a/plugin/src/executors/tx.rs +++ b/plugin/src/executors/tx.rs @@ -11,7 +11,7 @@ use async_once::AsyncOnce; use bincode::serialize; use clockwork_client::{ network::state::{Pool, Registry, Snapshot, SnapshotFrame, Worker}, - thread::state::Thread, + automation::state::Automation, }; use lazy_static::lazy_static; use log::info; @@ -38,11 +38,11 @@ use super::AccountGet; /// Number of slots to wait before checking for a confirmed transaction. static TRANSACTION_CONFIRMATION_PERIOD: u64 = 10; -/// Number of slots to wait before trying to execute a thread while not in the pool. -static THREAD_TIMEOUT_WINDOW: u64 = 8; +/// Number of slots to wait before trying to execute a automation while not in the pool. +static AUTOMATION_TIMEOUT_WINDOW: u64 = 8; -/// Number of times to retry a thread simulation. -static MAX_THREAD_SIMULATION_FAILURES: u32 = 5; +/// Number of times to retry a automation simulation. +static MAX_AUTOMATION_SIMULATION_FAILURES: u32 = 5; /// The constant of the exponential backoff function. static EXPONENTIAL_BACKOFF_CONSTANT: u32 = 2; @@ -50,14 +50,14 @@ static EXPONENTIAL_BACKOFF_CONSTANT: u32 = 2; /// TxExecutor pub struct TxExecutor { pub config: PluginConfig, - pub executable_threads: RwLock>, + pub executable_automations: RwLock>, pub transaction_history: RwLock>, - pub dropped_threads: AtomicU64, + pub dropped_automations: AtomicU64, pub keypair: Keypair, } #[derive(Debug)] -pub struct ExecutableThreadMetadata { +pub struct ExecutableAutomationMetadata { pub due_slot: u64, pub simulation_failures: u32, } @@ -72,9 +72,9 @@ impl TxExecutor { pub fn new(config: PluginConfig) -> Self { Self { config: config.clone(), - executable_threads: RwLock::new(HashMap::new()), + executable_automations: RwLock::new(HashMap::new()), transaction_history: RwLock::new(HashMap::new()), - dropped_threads: AtomicU64::new(0), + dropped_automations: AtomicU64::new(0), keypair: read_or_new_keypair(config.keypath), } } @@ -82,37 +82,37 @@ impl TxExecutor { pub async fn execute_txs( self: Arc, client: Arc, - thread_pubkeys: HashSet, + automation_pubkeys: HashSet, slot: u64, runtime: Arc, ) -> PluginResult<()> { - // Index the provided threads as executable. - let mut w_executable_threads = self.executable_threads.write().await; - thread_pubkeys.iter().for_each(|pubkey| { - w_executable_threads.insert( + // Index the provided automations as executable. + let mut w_executable_automations = self.executable_automations.write().await; + automation_pubkeys.iter().for_each(|pubkey| { + w_executable_automations.insert( *pubkey, - ExecutableThreadMetadata { + ExecutableAutomationMetadata { due_slot: slot, simulation_failures: 0, }, ); }); - // Drop threads that cross the simulation failure threshold. - w_executable_threads.retain(|_thread_pubkey, metadata| { - if metadata.simulation_failures > MAX_THREAD_SIMULATION_FAILURES { - self.dropped_threads.fetch_add(1, Ordering::Relaxed); + // Drop automations that cross the simulation failure threshold. + w_executable_automations.retain(|_automation_pubkey, metadata| { + if metadata.simulation_failures > MAX_AUTOMATION_SIMULATION_FAILURES { + self.dropped_automations.fetch_add(1, Ordering::Relaxed); false } else { true } }); info!( - "dropped_threads: {:?} executable_threads: {:?}", - self.dropped_threads.load(Ordering::Relaxed), - *w_executable_threads + "dropped_automations: {:?} executable_automations: {:?}", + self.dropped_automations.load(Ordering::Relaxed), + *w_executable_automations ); - drop(w_executable_threads); + drop(w_executable_automations); // Process retries. self.clone() @@ -141,9 +141,9 @@ impl TxExecutor { .ok(); } - // Execute thread transactions. + // Execute automation transactions. self.clone() - .execute_thread_exec_txs(client.clone(), slot, pool_position, runtime.clone()) + .execute_automation_exec_txs(client.clone(), slot, pool_position, runtime.clone()) .await .ok(); } @@ -156,9 +156,9 @@ impl TxExecutor { client: Arc, slot: u64, ) -> PluginResult<()> { - // Get transaction signatures and corresponding threads to check. + // Get transaction signatures and corresponding automations to check. struct CheckableTransaction { - thread_pubkey: Pubkey, + automation_pubkey: Pubkey, signature: Signature, } let r_transaction_history = self.transaction_history.read().await; @@ -166,15 +166,15 @@ impl TxExecutor { .iter() .filter(|(_, metadata)| slot > metadata.slot_sent + TRANSACTION_CONFIRMATION_PERIOD) .map(|(pubkey, metadata)| CheckableTransaction { - thread_pubkey: *pubkey, + automation_pubkey: *pubkey, signature: metadata.signature, }) .collect::>(); drop(r_transaction_history); - // Lookup transaction statuses and track which threads are successful / retriable. - let mut retriable_threads: HashSet = HashSet::new(); - let mut successful_threads: HashSet = HashSet::new(); + // Lookup transaction statuses and track which automations are successful / retriable. + let mut retriable_automations: HashSet = HashSet::new(); + let mut successful_automations: HashSet = HashSet::new(); for data in checkable_transactions { match client .get_signature_status_with_commitment( @@ -186,38 +186,38 @@ impl TxExecutor { Err(_err) => {} Ok(status) => match status { None => { - retriable_threads.insert(data.thread_pubkey); + retriable_automations.insert(data.automation_pubkey); } Some(status) => match status { Err(_err) => { - retriable_threads.insert(data.thread_pubkey); + retriable_automations.insert(data.automation_pubkey); } Ok(()) => { - successful_threads.insert(data.thread_pubkey); + successful_automations.insert(data.automation_pubkey); } }, }, } } - // Requeue retriable threads and drop transactions from history. + // Requeue retriable automations and drop transactions from history. let mut w_transaction_history = self.transaction_history.write().await; - let mut w_executable_threads = self.executable_threads.write().await; - for pubkey in successful_threads { + let mut w_executable_automations = self.executable_automations.write().await; + for pubkey in successful_automations { w_transaction_history.remove(&pubkey); } - for pubkey in retriable_threads { + for pubkey in retriable_automations { w_transaction_history.remove(&pubkey); - w_executable_threads.insert( + w_executable_automations.insert( pubkey, - ExecutableThreadMetadata { + ExecutableAutomationMetadata { due_slot: slot, simulation_failures: 0, }, ); } info!("transaction_history: {:?}", *w_transaction_history); - drop(w_executable_threads); + drop(w_executable_automations); drop(w_transaction_history); Ok(()) } @@ -252,20 +252,20 @@ impl TxExecutor { Ok(()) } - async fn get_executable_threads( + async fn get_executable_automations( self: Arc, pool_position: PoolPosition, slot: u64, ) -> PluginResult> { - // Get the set of thread pubkeys that are executable. + // Get the set of automation pubkeys that are executable. // Note we parallelize using rayon because this work is CPU heavy. - let r_executable_threads = self.executable_threads.read().await; - let thread_pubkeys = + let r_executable_automations = self.executable_automations.read().await; + let automation_pubkeys = if pool_position.current_position.is_none() && !pool_position.workers.is_empty() { - // This worker is not in the pool. Get pubkeys of threads that are beyond the timeout window. - r_executable_threads + // This worker is not in the pool. Get pubkeys of automations that are beyond the timeout window. + r_executable_automations .iter() - .filter(|(_pubkey, metadata)| slot > metadata.due_slot + THREAD_TIMEOUT_WINDOW) + .filter(|(_pubkey, metadata)| slot > metadata.due_slot + AUTOMATION_TIMEOUT_WINDOW) .filter(|(_pubkey, metadata)| { slot >= metadata.due_slot + EXPONENTIAL_BACKOFF_CONSTANT.pow(metadata.simulation_failures) as u64 @@ -274,8 +274,8 @@ impl TxExecutor { .map(|(pubkey, _metadata)| *pubkey) .collect::>() } else { - // This worker is in the pool. Get pubkeys executable threads. - r_executable_threads + // This worker is in the pool. Get pubkeys executable automations. + r_executable_automations .iter() .filter(|(_pubkey, metadata)| { slot >= metadata.due_slot @@ -285,38 +285,38 @@ impl TxExecutor { .map(|(pubkey, _metadata)| *pubkey) .collect::>() }; - drop(r_executable_threads); - Ok(thread_pubkeys) + drop(r_executable_automations); + Ok(automation_pubkeys) } - async fn execute_thread_exec_txs( + async fn execute_automation_exec_txs( self: Arc, client: Arc, slot: u64, pool_position: PoolPosition, runtime: Arc, ) -> PluginResult<()> { - let executable_threads = self + let executable_automations = self .clone() - .get_executable_threads(pool_position, slot) + .get_executable_automations(pool_position, slot) .await?; - if executable_threads.is_empty() { + if executable_automations.is_empty() { return Ok(()); } // Build transactions in parallel. // Note we parallelize using tokio because this work is IO heavy (RPC simulation calls). - let tasks: Vec<_> = executable_threads + let tasks: Vec<_> = executable_automations .iter() - .map(|thread_pubkey| { - runtime.spawn(self.clone().try_build_thread_exec_tx( + .map(|automation_pubkey| { + runtime.spawn(self.clone().try_build_automation_exec_tx( client.clone(), slot, - *thread_pubkey, + *automation_pubkey, )) }) .collect(); - let mut executed_threads: HashMap = HashMap::new(); + let mut executed_automations: HashMap = HashMap::new(); // Serialize to wire transactions. let wire_txs = futures::future::join_all(tasks) @@ -327,7 +327,7 @@ impl TxExecutor { Ok(res) => match res { None => None, Some((pubkey, tx)) => { - executed_threads.insert(*pubkey, tx.signatures[0]); + executed_automations.insert(*pubkey, tx.signatures[0]); Some(tx) } }, @@ -337,7 +337,7 @@ impl TxExecutor { // Batch submit transactions to the leader. // TODO Explore rewriting the TPU client for optimized performance. - // This currently is by far the most expensive part of processing threads. + // This currently is by far the most expensive part of processing automations. // Submitting transactions takes 8x longer (>200ms) than simulating and building transactions. match TPU_CLIENT .get() @@ -349,10 +349,10 @@ impl TxExecutor { info!("Failed to sent transaction batch: {:?}", err); } Ok(()) => { - let mut w_executable_threads = self.executable_threads.write().await; + let mut w_executable_automations = self.executable_automations.write().await; let mut w_transaction_history = self.transaction_history.write().await; - for (pubkey, signature) in executed_threads { - w_executable_threads.remove(&pubkey); + for (pubkey, signature) in executed_automations { + w_executable_automations.remove(&pubkey); w_transaction_history.insert( pubkey, TransactionMetadata { @@ -361,7 +361,7 @@ impl TxExecutor { }, ); } - drop(w_executable_threads); + drop(w_executable_automations); drop(w_transaction_history); } } @@ -369,61 +369,61 @@ impl TxExecutor { Ok(()) } - pub async fn try_build_thread_exec_tx( + pub async fn try_build_automation_exec_tx( self: Arc, client: Arc, slot: u64, - thread_pubkey: Pubkey, + automation_pubkey: Pubkey, ) -> Option<(Pubkey, Transaction)> { - let thread = match client.clone().get::(&thread_pubkey).await { + let automation = match client.clone().get::(&automation_pubkey).await { Err(_err) => { - self.increment_simulation_failure(thread_pubkey).await; + self.increment_simulation_failure(automation_pubkey).await; return None; } - Ok(thread) => thread, + Ok(automation) => automation, }; - if let Some(tx) = crate::builders::build_thread_exec_tx( + if let Some(tx) = crate::builders::build_automation_exec_tx( client.clone(), &self.keypair, - thread.clone(), - thread_pubkey, + automation.clone(), + automation_pubkey, self.config.worker_id, ) .await { if self .clone() - .dedupe_tx(slot, thread_pubkey, &tx) + .dedupe_tx(slot, automation_pubkey, &tx) .await .is_ok() { - Some((thread_pubkey, tx)) + Some((automation_pubkey, tx)) } else { None } } else { - self.increment_simulation_failure(thread_pubkey).await; + self.increment_simulation_failure(automation_pubkey).await; None } } - pub async fn increment_simulation_failure(self: Arc, thread_pubkey: Pubkey) { - let mut w_executable_threads = self.executable_threads.write().await; - w_executable_threads - .entry(thread_pubkey) + pub async fn increment_simulation_failure(self: Arc, automation_pubkey: Pubkey) { + let mut w_executable_automations = self.executable_automations.write().await; + w_executable_automations + .entry(automation_pubkey) .and_modify(|metadata| metadata.simulation_failures += 1); - drop(w_executable_threads); + drop(w_executable_automations); } pub async fn dedupe_tx( self: Arc, slot: u64, - thread_pubkey: Pubkey, + automation_pubkey: Pubkey, tx: &Transaction, ) -> PluginResult<()> { let r_transaction_history = self.transaction_history.read().await; - if let Some(metadata) = r_transaction_history.get(&thread_pubkey) { + if let Some(metadata) = r_transaction_history.get(&automation_pubkey) { if metadata.signature.eq(&tx.signatures[0]) && metadata.slot_sent.le(&slot) { return Err(GeyserPluginError::Custom(format!("Transaction signature is a duplicate of a previously submitted transaction").into())); } diff --git a/plugin/src/observers/thread.rs b/plugin/src/observers/automation.rs similarity index 50% rename from plugin/src/observers/thread.rs rename to plugin/src/observers/automation.rs index 85b6d99b..dbd4b814 100644 --- a/plugin/src/observers/thread.rs +++ b/plugin/src/observers/automation.rs @@ -6,7 +6,7 @@ use std::{ }; use chrono::{DateTime, NaiveDateTime, Utc}; -use clockwork_client::thread::state::{Thread, Trigger, TriggerContext}; +use clockwork_client::automation::state::{Automation, Trigger, TriggerContext}; use clockwork_cron::Schedule; use log::info; use solana_geyser_plugin_interface::geyser_plugin_interface::{ @@ -15,84 +15,84 @@ use solana_geyser_plugin_interface::geyser_plugin_interface::{ use solana_program::{clock::Clock, pubkey::Pubkey}; use tokio::sync::RwLock; -pub struct ThreadObserver { +pub struct AutomationObserver { // Map from slot numbers to the sysvar clock data for that slot. pub clocks: RwLock>, - // The set of threads with an account trigger. - // Map from account pubkeys to the set of threads listening for an account update. - pub account_threads: RwLock>>, + // The set of automations with an account trigger. + // Map from account pubkeys to the set of automations listening for an account update. + pub account_automations: RwLock>>, - // The set of threads with a cront trigger. - // Map from unix timestamps to the list of threads scheduled for that moment. - pub cron_threads: RwLock>>, + // The set of automations with a cront trigger. + // Map from unix timestamps to the list of automations scheduled for that moment. + pub cron_automations: RwLock>>, - // The set of threads with an immediate trigger. - pub immediate_threads: RwLock>, + // The set of automations with an immediate trigger. + pub immediate_automations: RwLock>, // The set of accounts that have updated. pub updated_accounts: RwLock>, } -impl ThreadObserver { +impl AutomationObserver { pub fn new() -> Self { Self { clocks: RwLock::new(HashMap::new()), - account_threads: RwLock::new(HashMap::new()), - cron_threads: RwLock::new(HashMap::new()), - immediate_threads: RwLock::new(HashSet::new()), + account_automations: RwLock::new(HashMap::new()), + cron_automations: RwLock::new(HashMap::new()), + immediate_automations: RwLock::new(HashSet::new()), updated_accounts: RwLock::new(HashSet::new()), } } pub async fn process_slot(self: Arc, slot: u64) -> PluginResult> { - let mut executable_threads: HashSet = HashSet::new(); + let mut executable_automations: HashSet = HashSet::new(); // Drop old clocks. let mut w_clocks = self.clocks.write().await; w_clocks.retain(|cached_slot, _clock| *cached_slot >= slot); drop(w_clocks); - // Get the set of threads that were triggered by the current clock. + // Get the set of automations that were triggered by the current clock. let r_clocks = self.clocks.read().await; if let Some(clock) = r_clocks.get(&slot) { - let mut w_cron_threads = self.cron_threads.write().await; - w_cron_threads.retain(|target_timestamp, thread_pubkeys| { + let mut w_cron_automations = self.cron_automations.write().await; + w_cron_automations.retain(|target_timestamp, automation_pubkeys| { let is_due = clock.unix_timestamp >= *target_timestamp; if is_due { - for pubkey in thread_pubkeys.iter() { - executable_threads.insert(*pubkey); + for pubkey in automation_pubkeys.iter() { + executable_automations.insert(*pubkey); } } !is_due }); - drop(w_cron_threads); + drop(w_cron_automations); } - // Get the set of threads were triggered by an account update. - let mut w_account_threads = self.account_threads.write().await; + // Get the set of automations were triggered by an account update. + let mut w_account_automations = self.account_automations.write().await; let mut w_updated_accounts = self.updated_accounts.write().await; w_updated_accounts.iter().for_each(|account_pubkey| { - if let Some(thread_pubkeys) = w_account_threads.get(&account_pubkey) { - thread_pubkeys.iter().for_each(|pubkey| { - executable_threads.insert(*pubkey); + if let Some(automation_pubkeys) = w_account_automations.get(&account_pubkey) { + automation_pubkeys.iter().for_each(|pubkey| { + executable_automations.insert(*pubkey); }); - w_account_threads.remove(&account_pubkey); + w_account_automations.remove(&account_pubkey); } }); w_updated_accounts.clear(); - drop(w_account_threads); + drop(w_account_automations); drop(w_updated_accounts); - // Get the set of immediate threads. - let mut w_immediate_threads = self.immediate_threads.write().await; - w_immediate_threads.iter().for_each(|pubkey| { - executable_threads.insert(*pubkey); + // Get the set of immediate automations. + let mut w_immediate_automations = self.immediate_automations.write().await; + w_immediate_automations.iter().for_each(|pubkey| { + executable_automations.insert(*pubkey); }); - w_immediate_threads.clear(); - drop(w_immediate_threads); + w_immediate_automations.clear(); + drop(w_immediate_automations); - Ok(executable_threads) + Ok(executable_automations) } pub async fn observe_clock(self: Arc, clock: Clock) -> PluginResult<()> { @@ -102,68 +102,68 @@ impl ThreadObserver { Ok(()) } - /// Move all threads listening to this account into the executable set. + /// Move all automations listening to this account into the executable set. pub async fn observe_account( self: Arc, account_pubkey: Pubkey, _slot: u64, ) -> PluginResult<()> { - let r_account_threads = self.account_threads.read().await; - if r_account_threads.contains_key(&account_pubkey) { + let r_account_automations = self.account_automations.read().await; + if r_account_automations.contains_key(&account_pubkey) { let mut w_updated_accounts = self.updated_accounts.write().await; w_updated_accounts.insert(account_pubkey); drop(w_updated_accounts); } - drop(r_account_threads); + drop(r_account_automations); Ok(()) } - pub async fn observe_thread( + pub async fn observe_automation( self: Arc, - thread: Thread, - thread_pubkey: Pubkey, + automation: Automation, + automation_pubkey: Pubkey, slot: u64, ) -> PluginResult<()> { - // If the thread is paused, just return without indexing - if thread.paused { + // If the automation is paused, just return without indexing + if automation.paused { return Ok(()); } - info!("indexing thread: {:?} slot: {}", thread_pubkey, slot); - if thread.next_instruction.is_some() { - // If the thread has a next instruction, index it as executable. - let mut w_immediate_threads = self.immediate_threads.write().await; - w_immediate_threads.insert(thread_pubkey); - drop(w_immediate_threads); + info!("indexing automation: {:?} slot: {}", automation_pubkey, slot); + if automation.next_instruction.is_some() { + // If the automation has a next instruction, index it as executable. + let mut w_immediate_automations = self.immediate_automations.write().await; + w_immediate_automations.insert(automation_pubkey); + drop(w_immediate_automations); } else { - // Otherwise, index the thread according to its trigger type. - match thread.trigger { + // Otherwise, index the automation according to its trigger type. + match automation.trigger { Trigger::Account { address, offset: _, size: _, } => { - // Index the thread by its trigger's account pubkey. - let mut w_account_threads = self.account_threads.write().await; - w_account_threads + // Index the automation by its trigger's account pubkey. + let mut w_account_automations = self.account_automations.write().await; + w_account_automations .entry(address) .and_modify(|v| { - v.insert(thread_pubkey); + v.insert(automation_pubkey); }) .or_insert_with(|| { let mut v = HashSet::new(); - v.insert(thread_pubkey); + v.insert(automation_pubkey); v }); - drop(w_account_threads); + drop(w_account_automations); } Trigger::Cron { schedule, skippable: _, } => { - // Find a reference timestamp for calculating the thread's upcoming target time. - let reference_timestamp = match thread.exec_context { - None => thread.created_at.unix_timestamp, + // Find a reference timestamp for calculating the automation's upcoming target time. + let reference_timestamp = match automation.exec_context { + None => automation.created_at.unix_timestamp, Some(exec_context) => match exec_context.trigger_context { TriggerContext::Cron { started_at } => started_at, _ => { @@ -174,29 +174,29 @@ impl ThreadObserver { }, }; - // Index the thread to its target timestamp + // Index the automation to its target timestamp match next_moment(reference_timestamp, schedule) { - None => {} // The thread does not have any upcoming scheduled target time + None => {} // The automation does not have any upcoming scheduled target time Some(target_timestamp) => { - let mut w_cron_threads = self.cron_threads.write().await; - w_cron_threads + let mut w_cron_automations = self.cron_automations.write().await; + w_cron_automations .entry(target_timestamp) .and_modify(|v| { - v.insert(thread_pubkey); + v.insert(automation_pubkey); }) .or_insert_with(|| { let mut v = HashSet::new(); - v.insert(thread_pubkey); + v.insert(automation_pubkey); v }); - drop(w_cron_threads); + drop(w_cron_automations); } } } Trigger::Immediate => { - let mut w_immediate_threads = self.immediate_threads.write().await; - w_immediate_threads.insert(thread_pubkey); - drop(w_immediate_threads); + let mut w_immediate_automations = self.immediate_automations.write().await; + w_immediate_automations.insert(automation_pubkey); + drop(w_immediate_automations); } } } @@ -205,9 +205,9 @@ impl ThreadObserver { } } -impl Debug for ThreadObserver { +impl Debug for AutomationObserver { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "thread-observer") + write!(f, "automation-observer") } } diff --git a/plugin/src/observers/mod.rs b/plugin/src/observers/mod.rs index 525d2d56..cd55d4ab 100644 --- a/plugin/src/observers/mod.rs +++ b/plugin/src/observers/mod.rs @@ -1,20 +1,20 @@ -pub mod thread; +pub mod automation; pub mod webhook; use std::{fmt::Debug, sync::Arc}; -use thread::ThreadObserver; +use automation::AutomationObserver; use webhook::WebhookObserver; pub struct Observers { - pub thread: Arc, + pub automation: Arc, pub webhook: Arc, } impl Observers { pub fn new() -> Self { Observers { - thread: Arc::new(ThreadObserver::new()), + automation: Arc::new(AutomationObserver::new()), webhook: Arc::new(WebhookObserver::new()), } } diff --git a/plugin/src/plugin.rs b/plugin/src/plugin.rs index f3186c5e..6dbd2e19 100644 --- a/plugin/src/plugin.rs +++ b/plugin/src/plugin.rs @@ -92,12 +92,12 @@ impl GeyserPlugin for ClockworkPlugin { // Process event on tokio task. self.inner.clone().spawn(|inner| async move { - // Send all account updates to the thread observer for account listeners. + // Send all account updates to the automation observer for account listeners. // Only process account updates if we're past the startup phase. if !is_startup { inner .observers - .thread + .automation .clone() .observe_account(account_pubkey, slot) .await?; @@ -109,7 +109,7 @@ impl GeyserPlugin for ClockworkPlugin { AccountUpdateEvent::Clock { clock } => { inner .observers - .thread + .automation .clone() .observe_clock(clock) .await @@ -127,12 +127,12 @@ impl GeyserPlugin for ClockworkPlugin { .await .ok(); } - AccountUpdateEvent::Thread { thread } => { + AccountUpdateEvent::Automation { automation } => { inner .observers - .thread + .automation .clone() - .observe_thread(thread, account_pubkey, slot) + .observe_automation(automation, account_pubkey, slot) .await .ok(); } diff --git a/plugin/vector.toml b/plugin/vector.toml index 59a73929..eae7b0b9 100644 --- a/plugin/vector.toml +++ b/plugin/vector.toml @@ -49,7 +49,7 @@ source = ''' inputs = ["parse_solana_logs"] type = "filter" condition = ''' - .source == "clockwork_plugin::builders::thread_exec" || + .source == "clockwork_plugin::builders::automation_exec" || .source == "clockwork_plugin::executors::tx" || .source == "solana_validator" || .source == "solana_core::validator" @@ -60,17 +60,17 @@ condition = ''' type = "remap" inputs = ["filter_solana_logs"] source = ''' - if .source == "clockwork_plugin::builders::thread_exec" { + if .source == "clockwork_plugin::builders::automation_exec" { .grok_results = parse_groks!( .message, - patterns: ["thread: %{_thread} simulation_error: %{_error} logs: %{_logs}"], + patterns: ["automation: %{_automation} simulation_error: %{_error} logs: %{_logs}"], aliases: { - "_thread": "%{DATA:thread}", + "_automation": "%{DATA:automation}", "_error": "%{QUOTEDSTRING:error}", "_logs": "%{GREEDYDATA:logs}" } ) - .thread = .grok_results.thread + .automation = .grok_results.automation .error_msg = .grok_results.error .program_logs = parse_json!(.grok_results.logs) del(.grok_results) @@ -82,7 +82,7 @@ source = ''' type = "filter" inputs = ["parse_clockwork_logs"] condition = ''' - .source == "clockwork_plugin::builders::thread_exec" && + .source == "clockwork_plugin::builders::automation_exec" && !is_null(.error_msg) ''' @@ -90,7 +90,7 @@ condition = ''' [transforms.throttle_clockwork_simulation_logs] type = "throttle" inputs = ["filter_clockwork_simulation_logs"] -key_field = "{{ thread }}" +key_field = "{{ automation }}" threshold = 1 window_secs = 30 @@ -131,7 +131,7 @@ name = "clockwork_simulation_test" [[tests.inputs]] insert_at = "parse_solana_logs" type = "raw" -value = '[2023-01-11T04:37:09.059509973Z INFO clockwork_plugin::builders::thread_exec] thread: 9K4g3LYdwKhTJVQv85EvsAn7uHo5pSyq7qNs2FqrsD1K simulation_error: "Transaction results in an account (1) without insufficient funds for rent" logs: ["Program ComputeBudget111111111111111111111111111111 invoke [1]", "Program ComputeBudget111111111111111111111111111111 success", "Program 3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv invoke [1]", "Program log: Instruction: ThreadKickoff", "Program 3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv consumed 83495 of 1400000 compute units", "Program 3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv success"]' +value = '[2023-01-11T04:37:09.059509973Z INFO clockwork_plugin::builders::automation_exec] automation: 9K4g3LYdwKhTJVQv85EvsAn7uHo5pSyq7qNs2FqrsD1K simulation_error: "Transaction results in an account (1) without insufficient funds for rent" logs: ["Program ComputeBudget111111111111111111111111111111 invoke [1]", "Program ComputeBudget111111111111111111111111111111 success", "Program 3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv invoke [1]", "Program log: Instruction: AutomationKickoff", "Program 3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv consumed 83495 of 1400000 compute units", "Program 3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv success"]' [[tests.outputs]] extract_from = "parse_clockwork_logs" @@ -141,8 +141,8 @@ type = "vrl" source = ''' assert_eq!(.dt, "2023-01-11T04:37:09.059509973Z") assert_eq!(.level, "INFO") - assert_eq!(.source, "clockwork_plugin::builders::thread_exec") - assert_eq!(.thread, "9K4g3LYdwKhTJVQv85EvsAn7uHo5pSyq7qNs2FqrsD1K") + assert_eq!(.source, "clockwork_plugin::builders::automation_exec") + assert_eq!(.automation, "9K4g3LYdwKhTJVQv85EvsAn7uHo5pSyq7qNs2FqrsD1K") assert_eq!(.error_msg, "\"Transaction results in an account (1) without insufficient funds for rent\"") - assert_eq!(.program_logs, ["Program ComputeBudget111111111111111111111111111111 invoke [1]", "Program ComputeBudget111111111111111111111111111111 success", "Program 3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv invoke [1]", "Program log: Instruction: ThreadKickoff", "Program 3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv consumed 83495 of 1400000 compute units", "Program 3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv success"]) + assert_eq!(.program_logs, ["Program ComputeBudget111111111111111111111111111111 invoke [1]", "Program ComputeBudget111111111111111111111111111111 success", "Program 3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv invoke [1]", "Program log: Instruction: AutomationKickoff", "Program 3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv consumed 83495 of 1400000 compute units", "Program 3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv success"]) ''' diff --git a/programs/thread/Cargo.toml b/programs/automation/Cargo.toml similarity index 87% rename from programs/thread/Cargo.toml rename to programs/automation/Cargo.toml index 6487524b..00a78c18 100644 --- a/programs/thread/Cargo.toml +++ b/programs/automation/Cargo.toml @@ -1,7 +1,7 @@ [package] -name = "clockwork-thread-program" +name = "clockwork-automation-program" version = "1.4.2" -description = "Clockwork thread program" +description = "Clockwork automation program" edition = "2021" license = "AGPL-3.0-or-later" homepage = "https://clockwork.xyz" @@ -12,7 +12,7 @@ keywords = ["solana"] [lib] crate-type = ["cdylib", "lib"] -name = "clockwork_thread_program" +name = "clockwork_automation_program" [features] no-entrypoint = [] diff --git a/programs/thread/README.md b/programs/automation/README.md similarity index 100% rename from programs/thread/README.md rename to programs/automation/README.md diff --git a/programs/automation/src/errors.rs b/programs/automation/src/errors.rs new file mode 100644 index 00000000..adb8e4e9 --- /dev/null +++ b/programs/automation/src/errors.rs @@ -0,0 +1,46 @@ +//! Errors thrown by the program. + +use anchor_lang::prelude::*; + +/// Errors for the the Clockwork automation program. +#[error_code] +pub enum ClockworkError { + /// Thrown if a exec response has an invalid program ID or cannot be parsed. + #[msg("The exec response could not be parsed")] + InvalidAutomationResponse, + + /// Thrown if a automation has an invalid state and cannot complete the operation. + #[msg("The automation is in an invalid state")] + InvalidAutomationState, + + /// TThe provided trigger variant is invalid. + #[msg("The trigger variant cannot be changed")] + InvalidTriggerVariant, + + /// Thrown if a exec instruction is invalid because the automation's trigger condition has not been met. + #[msg("The trigger condition has not been activated")] + TriggerNotActive, + + #[msg("This operation cannot be processes because the automation is currently busy")] + AutomationBusy, + + /// Thrown if a request is invalid because the automation is currently paused. + #[msg("The automation is currently paused")] + AutomationPaused, + + /// Thrown if a exec instruction would cause a automation to exceed its rate limit. + #[msg("The automation's rate limit has been reached")] + RateLimitExeceeded, + + /// Thrown if a automation authority attempts to set a rate limit above the maximum allowed value. + #[msg("Automation rate limits cannot exceed the maximum allowed value")] + MaxRateLimitExceeded, + + /// Thrown if an inner instruction attempted to write to an unauthorized address. + #[msg("Inner instruction attempted to write to an unauthorized address")] + UnauthorizedWrite, + + /// Thrown if the user attempts to withdraw SOL that would put a automation below it's minimum rent threshold. + #[msg("Withdrawing this amount would leave the automation with less than the minimum required SOL for rent exemption")] + WithdrawalTooLarge, +} diff --git a/programs/thread/src/instructions/thread_create.rs b/programs/automation/src/instructions/automation_create.rs similarity index 53% rename from programs/thread/src/instructions/thread_create.rs rename to programs/automation/src/instructions/automation_create.rs index 72cc125c..d6c35550 100644 --- a/programs/thread/src/instructions/thread_create.rs +++ b/programs/automation/src/instructions/automation_create.rs @@ -9,14 +9,14 @@ use clockwork_utils::automation::{Trigger, InstructionData}; use crate::state::*; -/// The minimum exec fee that may be set on a thread. +/// The minimum exec fee that may be set on a automation. const MINIMUM_FEE: u64 = 1000; -/// Accounts required by the `thread_create` instruction. +/// Accounts required by the `automation_create` instruction. #[derive(Accounts)] #[instruction(amount: u64, id: Vec, instructions: Vec, trigger: Trigger)] -pub struct ThreadCreate<'info> { - /// The authority (owner) of the thread. +pub struct AutomationCreate<'info> { + /// The authority (owner) of the automation. #[account()] pub authority: Signer<'info>, @@ -28,11 +28,11 @@ pub struct ThreadCreate<'info> { #[account(address = system_program::ID)] pub system_program: Program<'info, System>, - /// The thread to be created. + /// The automation to be created. #[account( init, seeds = [ - SEED_THREAD, + SEED_AUTOMATION, authority.key().as_ref(), id.as_slice(), ], @@ -40,44 +40,44 @@ pub struct ThreadCreate<'info> { payer = payer, space = vec![ 8, - size_of::(), + size_of::(), id.len(), instructions.try_to_vec()?.len(), trigger.try_to_vec()?.len() ].iter().sum() )] - pub thread: Account<'info, Thread>, + pub automation: Account<'info, Automation>, } -pub fn handler(ctx: Context, amount: u64, id: Vec, instructions: Vec, trigger: Trigger) -> Result<()> { +pub fn handler(ctx: Context, amount: u64, id: Vec, instructions: Vec, trigger: Trigger) -> Result<()> { // Get accounts let authority = &ctx.accounts.authority; let payer = &ctx.accounts.payer; let system_program = &ctx.accounts.system_program; - let thread = &mut ctx.accounts.thread; + let automation = &mut ctx.accounts.automation; - // Initialize the thread - let bump = *ctx.bumps.get("thread").unwrap(); - thread.authority = authority.key(); - thread.bump = bump; - thread.created_at = Clock::get().unwrap().into(); - thread.exec_context = None; - thread.fee = MINIMUM_FEE; - thread.id = id; - thread.instructions = instructions; - thread.name = String::new(); - thread.next_instruction = None; - thread.paused = false; - thread.rate_limit = u64::MAX; - thread.trigger = trigger; + // Initialize the automation + let bump = *ctx.bumps.get("automation").unwrap(); + automation.authority = authority.key(); + automation.bump = bump; + automation.created_at = Clock::get().unwrap().into(); + automation.exec_context = None; + automation.fee = MINIMUM_FEE; + automation.id = id; + automation.instructions = instructions; + automation.name = String::new(); + automation.next_instruction = None; + automation.paused = false; + automation.rate_limit = u64::MAX; + automation.trigger = trigger; - // Transfer SOL from payer to the thread. + // Transfer SOL from payer to the automation. transfer( CpiContext::new( system_program.to_account_info(), Transfer { from: payer.to_account_info(), - to: thread.to_account_info(), + to: automation.to_account_info(), }, ), amount diff --git a/programs/automation/src/instructions/automation_delete.rs b/programs/automation/src/instructions/automation_delete.rs new file mode 100644 index 00000000..5d991557 --- /dev/null +++ b/programs/automation/src/instructions/automation_delete.rs @@ -0,0 +1,31 @@ +use {crate::state::*, anchor_lang::prelude::*}; + +/// Accounts required by the `automation_delete` instruction. +#[derive(Accounts)] +pub struct AutomationDelete<'info> { + /// The authority (owner) of the automation. + #[account()] + pub authority: Signer<'info>, + + /// The address to return the data rent lamports to. + #[account(mut)] + pub close_to: SystemAccount<'info>, + + /// The automation to be delete. + #[account( + mut, + seeds = [ + SEED_AUTOMATION, + automation.authority.as_ref(), + automation.id.as_slice(), + ], + bump = automation.bump, + has_one = authority, + close = close_to + )] + pub automation: Account<'info, Automation>, +} + +pub fn handler(_ctx: Context) -> Result<()> { + Ok(()) +} diff --git a/programs/thread/src/instructions/thread_exec.rs b/programs/automation/src/instructions/automation_exec.rs similarity index 64% rename from programs/thread/src/instructions/thread_exec.rs rename to programs/automation/src/instructions/automation_exec.rs index 7345de99..b592e957 100644 --- a/programs/thread/src/instructions/thread_exec.rs +++ b/programs/automation/src/instructions/automation_exec.rs @@ -7,7 +7,7 @@ use anchor_lang::{ AnchorDeserialize, }; use clockwork_network_program::state::{Fee, Pool, Worker, WorkerAccount}; -use clockwork_utils::automation::{InstructionData, ThreadResponse, PAYER_PUBKEY}; +use clockwork_utils::automation::{InstructionData, AutomationResponse, PAYER_PUBKEY}; use crate::{errors::ClockworkError, state::*}; @@ -17,9 +17,9 @@ const POOL_ID: u64 = 0; /// The number of lamports to reimburse the worker with after they've submitted a transaction's worth of exec instructions. const TRANSACTION_BASE_FEE_REIMBURSEMENT: u64 = 5_000; -/// Accounts required by the `thread_exec` instruction. +/// Accounts required by the `automation_exec` instruction. #[derive(Accounts)] -pub struct ThreadExec<'info> { +pub struct AutomationExec<'info> { /// The worker's fee account. #[account( mut, @@ -41,37 +41,37 @@ pub struct ThreadExec<'info> { #[account(mut)] pub signatory: Signer<'info>, - /// The thread to execute. + /// The automation to execute. #[account( mut, seeds = [ - SEED_THREAD, - thread.authority.as_ref(), - thread.id.as_slice(), + SEED_AUTOMATION, + automation.authority.as_ref(), + automation.id.as_slice(), ], - bump = thread.bump, - constraint = !thread.paused @ ClockworkError::ThreadPaused, - constraint = thread.next_instruction.is_some(), - constraint = thread.exec_context.is_some() + bump = automation.bump, + constraint = !automation.paused @ ClockworkError::AutomationPaused, + constraint = automation.next_instruction.is_some(), + constraint = automation.exec_context.is_some() )] - pub thread: Box>, + pub automation: Box>, /// The worker. #[account(address = worker.pubkey())] pub worker: Account<'info, Worker>, } -pub fn handler(ctx: Context) -> Result<()> { +pub fn handler(ctx: Context) -> Result<()> { // Get accounts let fee = &mut ctx.accounts.fee; let pool = &ctx.accounts.pool; let signatory = &mut ctx.accounts.signatory; - let thread = &mut ctx.accounts.thread; + let automation = &mut ctx.accounts.automation; let worker = &ctx.accounts.worker; // If the rate limit has been met, exit early. - if thread.exec_context.unwrap().last_exec_at == Clock::get().unwrap().slot - && thread.exec_context.unwrap().execs_since_slot >= thread.rate_limit + if automation.exec_context.unwrap().last_exec_at == Clock::get().unwrap().slot + && automation.exec_context.unwrap().execs_since_slot >= automation.rate_limit { return Err(ClockworkError::RateLimitExeceeded.into()); } @@ -81,7 +81,7 @@ pub fn handler(ctx: Context) -> Result<()> { // Get the instruction to execute. // We have already verified that it is not null during account validation. - let next_instruction: &Option = &thread.clone().next_instruction; + let next_instruction: &Option = &automation.clone().next_instruction; let instruction = next_instruction.as_ref().unwrap(); // Inject the signatory's pubkey for the Clockwork payer ID. @@ -108,67 +108,67 @@ pub fn handler(ctx: Context) -> Result<()> { }, ctx.remaining_accounts, &[&[ - SEED_THREAD, - thread.authority.as_ref(), - thread.id.as_slice(), - &[thread.bump], + SEED_AUTOMATION, + automation.authority.as_ref(), + automation.id.as_slice(), + &[automation.bump], ]], )?; // Verify the inner instruction did not write data to the signatory address. require!(signatory.data_is_empty(), ClockworkError::UnauthorizedWrite); - // Parse the thread response - let thread_response: Option = match get_return_data() { + // Parse the automation response + let automation_response: Option = match get_return_data() { None => None, Some((program_id, return_data)) => { require!( program_id.eq(&instruction.program_id), - ClockworkError::InvalidThreadResponse + ClockworkError::InvalidAutomationResponse ); - ThreadResponse::try_from_slice(return_data.as_slice()).ok() + AutomationResponse::try_from_slice(return_data.as_slice()).ok() } }; - // Grab the next instruction from the thread response. + // Grab the next instruction from the automation response. let mut next_instruction = None; - if let Some(thread_response) = thread_response { - next_instruction = thread_response.next_instruction; + if let Some(automation_response) = automation_response { + next_instruction = automation_response.next_instruction; // Update the trigger. - if let Some(trigger) = thread_response.trigger { + if let Some(trigger) = automation_response.trigger { require!( - std::mem::discriminant(&thread.trigger) == std::mem::discriminant(&trigger), + std::mem::discriminant(&automation.trigger) == std::mem::discriminant(&trigger), ClockworkError::InvalidTriggerVariant ); - thread.trigger = trigger; + automation.trigger = trigger; } } // If there is no dynamic next instruction, get the next instruction from the instruction set. - let mut exec_index = thread.exec_context.unwrap().exec_index; + let mut exec_index = automation.exec_context.unwrap().exec_index; if next_instruction.is_none() { - if let Some(ix) = thread.instructions.get((exec_index + 1) as usize) { + if let Some(ix) = automation.instructions.get((exec_index + 1) as usize) { next_instruction = Some(ix.clone()); exec_index = exec_index + 1; } } // Update the next instruction. - thread.next_instruction = next_instruction; + automation.next_instruction = next_instruction; // Update the exec context. let current_slot = Clock::get().unwrap().slot; - thread.exec_context = Some(ExecContext { + automation.exec_context = Some(ExecContext { exec_index, - execs_since_reimbursement: thread + execs_since_reimbursement: automation .exec_context .unwrap() .execs_since_reimbursement .checked_add(1) .unwrap(), - execs_since_slot: if current_slot == thread.exec_context.unwrap().last_exec_at { - thread + execs_since_slot: if current_slot == automation.exec_context.unwrap().last_exec_at { + automation .exec_context .unwrap() .execs_since_slot @@ -178,17 +178,17 @@ pub fn handler(ctx: Context) -> Result<()> { 1 }, last_exec_at: current_slot, - ..thread.exec_context.unwrap() + ..automation.exec_context.unwrap() }); - // Realloc memory for the thread account. - thread.realloc()?; + // Realloc memory for the automation account. + automation.realloc()?; // Reimbursement signatory for lamports paid during inner ix. let signatory_lamports_post = signatory.lamports(); let signatory_reimbursement = signatory_lamports_pre.saturating_sub(signatory_lamports_post); if signatory_reimbursement.gt(&0) { - **thread.to_account_info().try_borrow_mut_lamports()? = thread + **automation.to_account_info().try_borrow_mut_lamports()? = automation .to_account_info() .lamports() .checked_sub(signatory_reimbursement) @@ -200,27 +200,27 @@ pub fn handler(ctx: Context) -> Result<()> { .unwrap(); } - // If the worker is in the pool, debit from the thread account and payout to the worker's fee account. + // If the worker is in the pool, debit from the automation account and payout to the worker's fee account. if pool.clone().into_inner().workers.contains(&worker.key()) { - **thread.to_account_info().try_borrow_mut_lamports()? = thread + **automation.to_account_info().try_borrow_mut_lamports()? = automation .to_account_info() .lamports() - .checked_sub(thread.fee) + .checked_sub(automation.fee) .unwrap(); **fee.to_account_info().try_borrow_mut_lamports()? = fee .to_account_info() .lamports() - .checked_add(thread.fee) + .checked_add(automation.fee) .unwrap(); } - // If the thread has no more work or the number of execs since the last payout has reached the rate limit, + // If the automation has no more work or the number of execs since the last payout has reached the rate limit, // reimburse the worker for the transaction base fee. - if thread.next_instruction.is_none() - || thread.exec_context.unwrap().execs_since_reimbursement >= thread.rate_limit + if automation.next_instruction.is_none() + || automation.exec_context.unwrap().execs_since_reimbursement >= automation.rate_limit { // Pay reimbursment for base transaction fee. - **thread.to_account_info().try_borrow_mut_lamports()? = thread + **automation.to_account_info().try_borrow_mut_lamports()? = automation .to_account_info() .lamports() .checked_sub(TRANSACTION_BASE_FEE_REIMBURSEMENT) @@ -232,9 +232,9 @@ pub fn handler(ctx: Context) -> Result<()> { .unwrap(); // Update the exec context to mark that a reimbursement happened this slot. - thread.exec_context = Some(ExecContext { + automation.exec_context = Some(ExecContext { execs_since_reimbursement: 0, - ..thread.exec_context.unwrap() + ..automation.exec_context.unwrap() }); } diff --git a/programs/thread/src/instructions/thread_kickoff.rs b/programs/automation/src/instructions/automation_kickoff.rs similarity index 75% rename from programs/thread/src/instructions/thread_kickoff.rs rename to programs/automation/src/instructions/automation_kickoff.rs index 63b68f35..c331beed 100644 --- a/programs/thread/src/instructions/thread_kickoff.rs +++ b/programs/automation/src/instructions/automation_kickoff.rs @@ -12,38 +12,38 @@ use clockwork_utils::automation::Trigger; use crate::{errors::*, state::*}; -/// Accounts required by the `thread_kickoff` instruction. +/// Accounts required by the `automation_kickoff` instruction. #[derive(Accounts)] -pub struct ThreadKickoff<'info> { +pub struct AutomationKickoff<'info> { /// The signatory. #[account(mut)] pub signatory: Signer<'info>, - /// The thread to kickoff. + /// The automation to kickoff. #[account( mut, seeds = [ - SEED_THREAD, - thread.authority.as_ref(), - thread.id.as_slice(), + SEED_AUTOMATION, + automation.authority.as_ref(), + automation.id.as_slice(), ], - bump = thread.bump, - constraint = !thread.paused @ ClockworkError::ThreadPaused, - constraint = thread.next_instruction.is_none() @ ClockworkError::ThreadBusy, + bump = automation.bump, + constraint = !automation.paused @ ClockworkError::AutomationPaused, + constraint = automation.next_instruction.is_none() @ ClockworkError::AutomationBusy, )] - pub thread: Box>, + pub automation: Box>, /// The worker. #[account(address = worker.pubkey())] pub worker: Account<'info, Worker>, } -pub fn handler(ctx: Context) -> Result<()> { +pub fn handler(ctx: Context) -> Result<()> { // Get accounts. - let thread = &mut ctx.accounts.thread; + let automation = &mut ctx.accounts.automation; let clock = Clock::get().unwrap(); - match thread.trigger.clone() { + match automation.trigger.clone() { Trigger::Account { address, offset, @@ -53,7 +53,7 @@ pub fn handler(ctx: Context) -> Result<()> { match ctx.remaining_accounts.first() { None => {} Some(account_info) => { - // Verify the remaining account is the account this thread is listening for. + // Verify the remaining account is the account this automation is listening for. require!( address.eq(account_info.key), ClockworkError::TriggerNotActive @@ -72,7 +72,7 @@ pub fn handler(ctx: Context) -> Result<()> { let data_hash = hasher.finish(); // Verify the data hash is different than the prior data hash. - if let Some(exec_context) = thread.exec_context { + if let Some(exec_context) = automation.exec_context { match exec_context.trigger_context { TriggerContext::Account { data_hash: prior_data_hash, @@ -82,12 +82,12 @@ pub fn handler(ctx: Context) -> Result<()> { ClockworkError::TriggerNotActive ) } - _ => return Err(ClockworkError::InvalidThreadState.into()), + _ => return Err(ClockworkError::InvalidAutomationState.into()), } } // Set a new exec context with the new data hash and slot number. - thread.exec_context = Some(ExecContext { + automation.exec_context = Some(ExecContext { exec_index: 0, execs_since_reimbursement: 0, execs_since_slot: 0, @@ -101,12 +101,12 @@ pub fn handler(ctx: Context) -> Result<()> { schedule, skippable, } => { - // Get the reference timestamp for calculating the thread's scheduled target timestamp. - let reference_timestamp = match thread.exec_context.clone() { - None => thread.created_at.unix_timestamp, + // Get the reference timestamp for calculating the automation's scheduled target timestamp. + let reference_timestamp = match automation.exec_context.clone() { + None => automation.created_at.unix_timestamp, Some(exec_context) => match exec_context.trigger_context { TriggerContext::Cron { started_at } => started_at, - _ => return Err(ClockworkError::InvalidThreadState.into()), + _ => return Err(ClockworkError::InvalidAutomationState.into()), }, }; @@ -127,7 +127,7 @@ pub fn handler(ctx: Context) -> Result<()> { }; // Set the exec context. - thread.exec_context = Some(ExecContext { + automation.exec_context = Some(ExecContext { exec_index: 0, execs_since_reimbursement: 0, execs_since_slot: 0, @@ -138,10 +138,10 @@ pub fn handler(ctx: Context) -> Result<()> { Trigger::Immediate => { // Set the exec context. require!( - thread.exec_context.is_none(), - ClockworkError::InvalidThreadState + automation.exec_context.is_none(), + ClockworkError::InvalidAutomationState ); - thread.exec_context = Some(ExecContext { + automation.exec_context = Some(ExecContext { exec_index: 0, execs_since_reimbursement: 0, execs_since_slot: 0, @@ -152,12 +152,12 @@ pub fn handler(ctx: Context) -> Result<()> { } // If we make it here, the trigger is active. Update the next instruction and be done. - if let Some(kickoff_instruction) = thread.instructions.first() { - thread.next_instruction = Some(kickoff_instruction.clone()); + if let Some(kickoff_instruction) = automation.instructions.first() { + automation.next_instruction = Some(kickoff_instruction.clone()); } - // Realloc the thread account - thread.realloc()?; + // Realloc the automation account + automation.realloc()?; Ok(()) } diff --git a/programs/automation/src/instructions/automation_pause.rs b/programs/automation/src/instructions/automation_pause.rs new file mode 100644 index 00000000..0ac9939f --- /dev/null +++ b/programs/automation/src/instructions/automation_pause.rs @@ -0,0 +1,32 @@ +use {crate::state::*, anchor_lang::prelude::*}; + +/// Accounts required by the `automation_delete` instruction. +#[derive(Accounts)] +pub struct AutomationPause<'info> { + /// The authority (owner) of the automation. + #[account()] + pub authority: Signer<'info>, + + /// The automation to be paused. + #[account( + mut, + seeds = [ + SEED_AUTOMATION, + automation.authority.as_ref(), + automation.id.as_slice(), + ], + bump = automation.bump, + has_one = authority + )] + pub automation: Account<'info, Automation>, +} + +pub fn handler(ctx: Context) -> Result<()> { + // Get accounts + let automation = &mut ctx.accounts.automation; + + // Pause the automation + automation.paused = true; + + Ok(()) +} diff --git a/programs/thread/src/instructions/thread_resume.rs b/programs/automation/src/instructions/automation_resume.rs similarity index 60% rename from programs/thread/src/instructions/thread_resume.rs rename to programs/automation/src/instructions/automation_resume.rs index 1d159eb6..b2ac3603 100644 --- a/programs/thread/src/instructions/thread_resume.rs +++ b/programs/automation/src/instructions/automation_resume.rs @@ -1,35 +1,35 @@ use {crate::state::*, anchor_lang::prelude::*}; -/// Accounts required by the `thread_resume` instruction. +/// Accounts required by the `automation_resume` instruction. #[derive(Accounts)] -pub struct ThreadResume<'info> { - /// The authority (owner) of the thread. +pub struct AutomationResume<'info> { + /// The authority (owner) of the automation. #[account()] pub authority: Signer<'info>, - /// The thread to be resumed. + /// The automation to be resumed. #[account( mut, seeds = [ - SEED_THREAD, - thread.authority.as_ref(), - thread.id.as_slice(), + SEED_AUTOMATION, + automation.authority.as_ref(), + automation.id.as_slice(), ], - bump = thread.bump, + bump = automation.bump, has_one = authority )] - pub thread: Account<'info, Thread>, + pub automation: Account<'info, Automation>, } -pub fn handler(ctx: Context) -> Result<()> { +pub fn handler(ctx: Context) -> Result<()> { // Get accounts - let thread = &mut ctx.accounts.thread; + let automation = &mut ctx.accounts.automation; - // Resume the thread - thread.paused = false; + // Resume the automation + automation.paused = false; // Update the exec context - match thread.exec_context { + match automation.exec_context { None => {} Some(exec_context) => { match exec_context.trigger_context { @@ -38,7 +38,7 @@ pub fn handler(ctx: Context) -> Result<()> { } TriggerContext::Cron { started_at: _ } => { // Jump ahead to the current timestamp - thread.exec_context = Some(ExecContext { + automation.exec_context = Some(ExecContext { trigger_context: TriggerContext::Cron { started_at: Clock::get().unwrap().unix_timestamp, }, diff --git a/programs/automation/src/instructions/automation_stop.rs b/programs/automation/src/instructions/automation_stop.rs new file mode 100644 index 00000000..c920aa02 --- /dev/null +++ b/programs/automation/src/instructions/automation_stop.rs @@ -0,0 +1,32 @@ +use {crate::state::*, anchor_lang::prelude::*}; + +/// Accounts required by the `automation_delete` instruction. +#[derive(Accounts)] +pub struct AutomationStop<'info> { + /// The authority (owner) of the automation. + #[account()] + pub authority: Signer<'info>, + + /// The automation to be paused. + #[account( + mut, + seeds = [ + SEED_AUTOMATION, + automation.authority.as_ref(), + automation.id.as_slice(), + ], + bump = automation.bump, + has_one = authority + )] + pub automation: Account<'info, Automation>, +} + +pub fn handler(ctx: Context) -> Result<()> { + // Get accounts + let automation = &mut ctx.accounts.automation; + + // Pause the automation + automation.next_instruction = None; + + Ok(()) +} diff --git a/programs/automation/src/instructions/automation_update.rs b/programs/automation/src/instructions/automation_update.rs new file mode 100644 index 00000000..9cb215eb --- /dev/null +++ b/programs/automation/src/instructions/automation_update.rs @@ -0,0 +1,88 @@ +use crate::{errors::ClockworkError, state::*}; + +use anchor_lang::{ + prelude::*, + solana_program::system_program, + system_program::{transfer, Transfer}, +}; + +/// Accounts required by the `automation_update` instruction. +#[derive(Accounts)] +#[instruction(settings: AutomationSettings)] +pub struct AutomationUpdate<'info> { + /// The authority (owner) of the automation. + #[account(mut)] + pub authority: Signer<'info>, + + /// The Solana system program + #[account(address = system_program::ID)] + pub system_program: Program<'info, System>, + + /// The automation to be updated. + #[account( + mut, + seeds = [ + SEED_AUTOMATION, + automation.authority.as_ref(), + automation.id.as_slice(), + ], + bump = automation.bump, + has_one = authority, + )] + pub automation: Account<'info, Automation>, +} + +pub fn handler(ctx: Context, settings: AutomationSettings) -> Result<()> { + // Get accounts + let authority = &ctx.accounts.authority; + let automation = &mut ctx.accounts.automation; + let system_program = &ctx.accounts.system_program; + + // Update the automation. + if let Some(fee) = settings.fee { + automation.fee = fee; + } + + // If provided, update the automation's instruction set. + if let Some(instructions) = settings.instructions { + automation.instructions = instructions; + } + + // If provided, update the rate limit. + if let Some(rate_limit) = settings.rate_limit { + automation.rate_limit = rate_limit; + } + + // If provided, update the automation's trigger and reset the exec context. + if let Some(trigger) = settings.trigger { + // Require the automation is not in the middle of processing. + require!( + std::mem::discriminant(&automation.trigger) == std::mem::discriminant(&trigger), + ClockworkError::InvalidTriggerVariant + ); + automation.trigger = trigger; + } + + // Reallocate mem for the automation account + automation.realloc()?; + + // If lamports are required to maintain rent-exemption, pay them + let data_len = 8 + automation.try_to_vec()?.len(); + let minimum_rent = Rent::get().unwrap().minimum_balance(data_len); + if minimum_rent > automation.to_account_info().lamports() { + transfer( + CpiContext::new( + system_program.to_account_info(), + Transfer { + from: authority.to_account_info(), + to: automation.to_account_info(), + }, + ), + minimum_rent + .checked_sub(automation.to_account_info().lamports()) + .unwrap(), + )?; + } + + Ok(()) +} diff --git a/programs/thread/src/instructions/thread_withdraw.rs b/programs/automation/src/instructions/automation_withdraw.rs similarity index 58% rename from programs/thread/src/instructions/thread_withdraw.rs rename to programs/automation/src/instructions/automation_withdraw.rs index c798ac37..98403fa0 100644 --- a/programs/thread/src/instructions/thread_withdraw.rs +++ b/programs/automation/src/instructions/automation_withdraw.rs @@ -3,11 +3,11 @@ use { anchor_lang::prelude::*, }; -/// Accounts required by the `thread_withdraw` instruction. +/// Accounts required by the `automation_withdraw` instruction. #[derive(Accounts)] #[instruction(amount: u64)] -pub struct ThreadWithdraw<'info> { - /// The authority (owner) of the thread. +pub struct AutomationWithdraw<'info> { + /// The authority (owner) of the automation. #[account()] pub authority: Signer<'info>, @@ -15,29 +15,29 @@ pub struct ThreadWithdraw<'info> { #[account(mut)] pub pay_to: SystemAccount<'info>, - /// The thread to be. + /// The automation to be. #[account( mut, seeds = [ - SEED_THREAD, - thread.authority.as_ref(), - thread.id.as_slice(), + SEED_AUTOMATION, + automation.authority.as_ref(), + automation.id.as_slice(), ], - bump = thread.bump, + bump = automation.bump, has_one = authority, )] - pub thread: Account<'info, Thread>, + pub automation: Account<'info, Automation>, } -pub fn handler(ctx: Context, amount: u64) -> Result<()> { +pub fn handler(ctx: Context, amount: u64) -> Result<()> { // Get accounts let pay_to = &mut ctx.accounts.pay_to; - let thread = &mut ctx.accounts.thread; + let automation = &mut ctx.accounts.automation; // Calculate the minimum rent threshold - let data_len = 8 + thread.try_to_vec()?.len(); + let data_len = 8 + automation.try_to_vec()?.len(); let minimum_rent = Rent::get().unwrap().minimum_balance(data_len); - let post_balance = thread + let post_balance = automation .to_account_info() .lamports() .checked_sub(amount) @@ -47,8 +47,8 @@ pub fn handler(ctx: Context, amount: u64) -> Result<()> { ClockworkError::WithdrawalTooLarge ); - // Withdraw balance from thread to the pay_to account - **thread.to_account_info().try_borrow_mut_lamports()? = thread + // Withdraw balance from automation to the pay_to account + **automation.to_account_info().try_borrow_mut_lamports()? = automation .to_account_info() .lamports() .checked_sub(amount) diff --git a/programs/thread/src/instructions/get_crate_info.rs b/programs/automation/src/instructions/get_crate_info.rs similarity index 96% rename from programs/thread/src/instructions/get_crate_info.rs rename to programs/automation/src/instructions/get_crate_info.rs index d3b66abf..8bd69a47 100644 --- a/programs/thread/src/instructions/get_crate_info.rs +++ b/programs/automation/src/instructions/get_crate_info.rs @@ -14,7 +14,7 @@ pub struct GetCrateInfo<'info> { pub fn handler(_ctx: Context) -> Result { let spec = format!( - "https://github.com/clockwork-xyz/clockwork/blob/v{}/programs/thread/Cargo.toml", + "https://github.com/clockwork-xyz/clockwork/blob/v{}/programs/automation/Cargo.toml", version!() ); let blob = ""; diff --git a/programs/automation/src/instructions/mod.rs b/programs/automation/src/instructions/mod.rs new file mode 100644 index 00000000..d9b62916 --- /dev/null +++ b/programs/automation/src/instructions/mod.rs @@ -0,0 +1,21 @@ +pub mod get_crate_info; +pub mod automation_create; +pub mod automation_delete; +pub mod automation_exec; +pub mod automation_kickoff; +pub mod automation_pause; +pub mod automation_resume; +pub mod automation_stop; +pub mod automation_update; +pub mod automation_withdraw; + +pub use get_crate_info::*; +pub use automation_create::*; +pub use automation_delete::*; +pub use automation_exec::*; +pub use automation_kickoff::*; +pub use automation_pause::*; +pub use automation_resume::*; +pub use automation_stop::*; +pub use automation_update::*; +pub use automation_withdraw::*; diff --git a/programs/automation/src/lib.rs b/programs/automation/src/lib.rs new file mode 100644 index 00000000..11a17661 --- /dev/null +++ b/programs/automation/src/lib.rs @@ -0,0 +1,82 @@ +//! This program allows users to create transaction automations on Solana. Automations are dynamic, long-running +//! transaction automations that can persist across blocks and even run indefinitely. Developers can use automations +//! to schedule transactions and automate smart-contracts without relying on centralized infrastructure. +#[macro_use] +extern crate version; + +pub mod errors; +pub mod state; + +mod instructions; + +use anchor_lang::prelude::*; +use clockwork_utils::{ + automation::{InstructionData, Trigger}, + CrateInfo, +}; +use instructions::*; +use state::*; + +declare_id!("3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv"); + +/// Program for creating transaction automations on Solana. +#[program] +pub mod automation_program { + use super::*; + + /// Return the crate information via `sol_set_return_data/sol_get_return_data` + pub fn get_crate_info(ctx: Context) -> Result { + get_crate_info::handler(ctx) + } + + /// Executes the next instruction on automation. + pub fn automation_exec(ctx: Context) -> Result<()> { + automation_exec::handler(ctx) + } + + /// Creates a new transaction automation. + pub fn automation_create( + ctx: Context, + amount: u64, + id: Vec, + instructions: Vec, + trigger: Trigger, + ) -> Result<()> { + automation_create::handler(ctx, amount, id, instructions, trigger) + } + + /// Closes an existing automation account and returns the lamports to the owner. + pub fn automation_delete(ctx: Context) -> Result<()> { + automation_delete::handler(ctx) + } + + /// Kicks off a automation if its trigger condition is active. + pub fn automation_kickoff(ctx: Context) -> Result<()> { + automation_kickoff::handler(ctx) + } + + /// Pauses an active automation. + pub fn automation_pause(ctx: Context) -> Result<()> { + automation_pause::handler(ctx) + } + + /// Resumes a paused automation. + pub fn automation_resume(ctx: Context) -> Result<()> { + automation_resume::handler(ctx) + } + + /// Resumes a paused automation. + pub fn automation_stop(ctx: Context) -> Result<()> { + automation_stop::handler(ctx) + } + + /// Allows an owner to update the mutable properties of a automation. + pub fn automation_update(ctx: Context, settings: AutomationSettings) -> Result<()> { + automation_update::handler(ctx, settings) + } + + /// Allows an owner to withdraw from a automation's lamport balance. + pub fn automation_withdraw(ctx: Context, amount: u64) -> Result<()> { + automation_withdraw::handler(ctx, amount) + } +} diff --git a/programs/thread/src/state/thread.rs b/programs/automation/src/state/automation.rs similarity index 67% rename from programs/thread/src/state/thread.rs rename to programs/automation/src/state/automation.rs index 3df9c464..ec38c2f9 100644 --- a/programs/thread/src/state/thread.rs +++ b/programs/automation/src/state/automation.rs @@ -2,80 +2,80 @@ use anchor_lang::{prelude::*, AnchorDeserialize, AnchorSerialize}; use clockwork_macros::TryFromData; use clockwork_utils::automation::{ClockData, InstructionData, Trigger}; -pub const SEED_THREAD: &[u8] = b"thread"; +pub const SEED_AUTOMATION: &[u8] = b"automation"; -/// Tracks the current state of a transaction thread on Solana. +/// Tracks the current state of a transaction automation on Solana. #[account] #[derive(Debug, TryFromData)] -pub struct Thread { - /// The owner of this thread. +pub struct Automation { + /// The owner of this automation. pub authority: Pubkey, /// The bump, used for PDA validation. pub bump: u8, - /// The cluster clock at the moment the thread was created. + /// The cluster clock at the moment the automation was created. pub created_at: ClockData, - /// The context of the thread's current execution state. + /// The context of the automation's current execution state. pub exec_context: Option, /// The number of lamports to payout to workers per execution. pub fee: u64, - /// The id of the thread, given by the authority. + /// The id of the automation, given by the authority. pub id: Vec, /// The instructions to be executed. pub instructions: Vec, - /// The name of the thread. + /// The name of the automation. pub name: String, /// The next instruction to be executed. pub next_instruction: Option, - /// Whether or not the thread is currently paused. + /// Whether or not the automation is currently paused. pub paused: bool, /// The maximum number of execs allowed per slot. pub rate_limit: u64, - /// The triggering event to kickoff a thread. + /// The triggering event to kickoff a automation. pub trigger: Trigger, } -impl Thread { - /// Derive the pubkey of a thread account. +impl Automation { + /// Derive the pubkey of a automation account. pub fn pubkey(authority: Pubkey, id: Vec) -> Pubkey { Pubkey::find_program_address( - &[SEED_THREAD, authority.as_ref(), id.as_slice()], + &[SEED_AUTOMATION, authority.as_ref(), id.as_slice()], &crate::ID, ) .0 } } -impl PartialEq for Thread { +impl PartialEq for Automation { fn eq(&self, other: &Self) -> bool { self.authority.eq(&other.authority) && self.id.eq(&other.id) } } -impl Eq for Thread {} +impl Eq for Automation {} -/// Trait for reading and writing to a thread account. -pub trait ThreadAccount { - /// Get the pubkey of the thread account. +/// Trait for reading and writing to a automation account. +pub trait AutomationAccount { + /// Get the pubkey of the automation account. fn pubkey(&self) -> Pubkey; /// Allocate more memory for the account. fn realloc(&mut self) -> Result<()>; } -impl ThreadAccount for Account<'_, Thread> { +impl AutomationAccount for Account<'_, Automation> { fn pubkey(&self) -> Pubkey { - Thread::pubkey(self.authority, self.id.clone()) + Automation::pubkey(self.authority, self.id.clone()) } fn realloc(&mut self) -> Result<()> { - // Realloc memory for the thread account + // Realloc memory for the automation account let data_len = 8 + self.try_to_vec()?.len(); self.to_account_info().realloc(data_len, false)?; Ok(()) } } -/// The execution context of a particular transaction thread. +/// The execution context of a particular transaction automation. #[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy, Debug, PartialEq, Eq)] pub struct ExecContext { /// Index of the next instruction to be executed. @@ -94,7 +94,7 @@ pub struct ExecContext { pub trigger_context: TriggerContext, } -/// The event which allowed a particular transaction thread to be triggered. +/// The event which allowed a particular transaction automation to be triggered. #[derive(AnchorDeserialize, AnchorSerialize, Clone, Copy, Debug, PartialEq, Eq)] pub enum TriggerContext { /// A running hash of the observed account data. @@ -113,9 +113,9 @@ pub enum TriggerContext { Immediate, } -/// The properties of threads which are updatable. +/// The properties of automations which are updatable. #[derive(AnchorSerialize, AnchorDeserialize)] -pub struct ThreadSettings { +pub struct AutomationSettings { pub fee: Option, pub instructions: Option>, pub name: Option, diff --git a/programs/thread/src/state/crate_info.rs b/programs/automation/src/state/crate_info.rs similarity index 100% rename from programs/thread/src/state/crate_info.rs rename to programs/automation/src/state/crate_info.rs diff --git a/programs/thread/src/state/mod.rs b/programs/automation/src/state/mod.rs similarity index 73% rename from programs/thread/src/state/mod.rs rename to programs/automation/src/state/mod.rs index aff48a26..2df66c81 100644 --- a/programs/thread/src/state/mod.rs +++ b/programs/automation/src/state/mod.rs @@ -1,6 +1,6 @@ //! All objects needed to describe and manage the program's state. -mod thread; +mod automation; pub use clockwork_utils::automation::*; -pub use thread::*; +pub use automation::*; diff --git a/programs/network/src/instructions/registry_nonce_hash.rs b/programs/network/src/instructions/registry_nonce_hash.rs index a4ecf5bc..a2f2be64 100644 --- a/programs/network/src/instructions/registry_nonce_hash.rs +++ b/programs/network/src/instructions/registry_nonce_hash.rs @@ -1,4 +1,4 @@ -use clockwork_utils::automation::ThreadResponse; +use clockwork_utils::automation::AutomationResponse; use {crate::state::*, anchor_lang::prelude::*}; @@ -14,12 +14,12 @@ pub struct RegistryNonceHash<'info> { )] pub registry: Account<'info, Registry>, - #[account(address = config.hasher_thread)] - pub thread: Signer<'info>, + #[account(address = config.hasher_automation)] + pub automation: Signer<'info>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { let registry = &mut ctx.accounts.registry; registry.hash_nonce()?; - Ok(ThreadResponse::default()) + Ok(AutomationResponse::default()) } diff --git a/programs/network/src/jobs/delete_snapshot/job.rs b/programs/network/src/jobs/delete_snapshot/job.rs index 8373d586..f237eabd 100644 --- a/programs/network/src/jobs/delete_snapshot/job.rs +++ b/programs/network/src/jobs/delete_snapshot/job.rs @@ -2,7 +2,7 @@ use { crate::state::*, anchor_lang::prelude::*, clockwork_utils::automation::{ - anchor_sighash, AccountMetaData, InstructionData, ThreadResponse, + anchor_sighash, AccountMetaData, InstructionData, AutomationResponse, }, }; @@ -17,16 +17,16 @@ pub struct DeleteSnapshotJob<'info> { )] pub registry: Account<'info, Registry>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { let config = &ctx.accounts.config; let registry = &ctx.accounts.registry; - let thread = &mut ctx.accounts.thread; + let automation = &mut ctx.accounts.automation; - Ok(ThreadResponse { + Ok(AutomationResponse { next_instruction: Some(InstructionData { program_id: crate::ID, accounts: vec![ @@ -36,7 +36,7 @@ pub fn handler(ctx: Context) -> Result { Snapshot::pubkey(registry.current_epoch.checked_sub(1).unwrap()), false, ), - AccountMetaData::new(thread.key(), true), + AccountMetaData::new(automation.key(), true), ], data: anchor_sighash("delete_snapshot_process_snapshot").to_vec(), }), diff --git a/programs/network/src/jobs/delete_snapshot/process_entry.rs b/programs/network/src/jobs/delete_snapshot/process_entry.rs index e52e2f8f..0cedac9d 100644 --- a/programs/network/src/jobs/delete_snapshot/process_entry.rs +++ b/programs/network/src/jobs/delete_snapshot/process_entry.rs @@ -1,4 +1,4 @@ -use {crate::state::*, anchor_lang::prelude::*, clockwork_utils::automation::{InstructionData, AccountMetaData, anchor_sighash, ThreadResponse}}; +use {crate::state::*, anchor_lang::prelude::*, clockwork_utils::automation::{InstructionData, AccountMetaData, anchor_sighash, AutomationResponse}}; #[derive(Accounts)] pub struct DeleteSnapshotProcessEntry<'info> { @@ -48,24 +48,24 @@ pub struct DeleteSnapshotProcessEntry<'info> { #[account( mut, - address = config.epoch_thread + address = config.epoch_automation )] - pub thread: Signer<'info>, + pub automation: Signer<'info>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts let config = &ctx.accounts.config; let registry = &ctx.accounts.registry; let snapshot = &mut ctx.accounts.snapshot; let snapshot_entry = &mut ctx.accounts.snapshot_entry; let snapshot_frame = &mut ctx.accounts.snapshot_frame; - let thread = &mut ctx.accounts.thread; + let automation = &mut ctx.accounts.automation; // Close the snapshot entry account. let snapshot_entry_lamports = snapshot_entry.to_account_info().lamports(); **snapshot_entry.to_account_info().lamports.borrow_mut() = 0; - **thread.to_account_info().lamports.borrow_mut() = thread + **automation.to_account_info().lamports.borrow_mut() = automation .to_account_info() .lamports() .checked_add(snapshot_entry_lamports) @@ -75,7 +75,7 @@ pub fn handler(ctx: Context) -> Result) -> Result) -> Result) -> Result) -> Result { #[account( mut, - address = config.epoch_thread + address = config.epoch_automation )] - pub thread: Signer<'info>, + pub automation: Signer<'info>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts let config = &ctx.accounts.config; let registry = &ctx.accounts.registry; let snapshot = &mut ctx.accounts.snapshot; let snapshot_frame = &mut ctx.accounts.snapshot_frame; - let thread = &mut ctx.accounts.thread; + let automation = &mut ctx.accounts.automation; // If this frame has no entries, then close the frame account. if snapshot_frame.total_entries.eq(&0) { let snapshot_frame_lamports = snapshot_frame.to_account_info().lamports(); **snapshot_frame.to_account_info().lamports.borrow_mut() = 0; - **thread.to_account_info().lamports.borrow_mut() = thread + **automation.to_account_info().lamports.borrow_mut() = automation .to_account_info() .lamports() .checked_add(snapshot_frame_lamports) @@ -66,7 +66,7 @@ pub fn handler(ctx: Context) -> Result) -> Result) -> Result) -> Result { #[account( mut, - address = config.epoch_thread + address = config.epoch_automation )] - pub thread: Signer<'info>, + pub automation: Signer<'info>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts let config = &ctx.accounts.config; let registry = &ctx.accounts.registry; let snapshot = &mut ctx.accounts.snapshot; - let thread = &mut ctx.accounts.thread; + let automation = &mut ctx.accounts.automation; // If this snapshot has no entries, then close immediately if snapshot.total_frames.eq(&0) { let snapshot_lamports = snapshot.to_account_info().lamports(); **snapshot.to_account_info().lamports.borrow_mut() = 0; - **thread.to_account_info().lamports.borrow_mut() = thread + **automation.to_account_info().lamports.borrow_mut() = automation .to_account_info() .lamports() .checked_add(snapshot_lamports) .unwrap(); } - // Build next instruction the thread. + // Build next instruction the automation. let next_instruction = if snapshot.total_frames.gt(&0) { // There are frames in this snapshot. Delete them. Some(InstructionData { @@ -59,7 +59,7 @@ pub fn handler(ctx: Context) -> Result) -> Result { )] pub registry: Account<'info, Registry>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts. let config = &ctx.accounts.config; let registry = &mut ctx.accounts.registry; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; // Lock the registry. registry.locked = true; // Process the snapshot. - Ok(ThreadResponse { + Ok(AutomationResponse { next_instruction: Some(InstructionData { program_id: crate::ID, accounts: vec![ AccountMetaData::new_readonly(config.key(), false), AccountMetaData::new_readonly(registry.key(), false), AccountMetaData::new_readonly(Snapshot::pubkey(registry.current_epoch), false), - AccountMetaData::new_readonly(thread.key(), true), + AccountMetaData::new_readonly(automation.key(), true), ], data: anchor_sighash("distribute_fees_process_snapshot").to_vec(), }), diff --git a/programs/network/src/jobs/distribute_fees/process_entry.rs b/programs/network/src/jobs/distribute_fees/process_entry.rs index 1fb09020..ee725f21 100644 --- a/programs/network/src/jobs/distribute_fees/process_entry.rs +++ b/programs/network/src/jobs/distribute_fees/process_entry.rs @@ -1,6 +1,6 @@ use anchor_lang::prelude::*; use clockwork_utils::automation::{ - anchor_sighash, AccountMetaData, InstructionData, ThreadResponse, + anchor_sighash, AccountMetaData, InstructionData, AutomationResponse, }; use crate::state::*; @@ -56,14 +56,14 @@ pub struct DistributeFeesProcessEntry<'info> { )] pub snapshot_frame: Account<'info, SnapshotFrame>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, #[account(address = worker.pubkey())] pub worker: Account<'info, Worker>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts let config = &ctx.accounts.config; let delegation = &mut ctx.accounts.delegation; @@ -72,7 +72,7 @@ pub fn handler(ctx: Context) -> Result) -> Result) -> Result) -> Result) -> Result { )] pub snapshot_frame: Account<'info, SnapshotFrame>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, #[account(mut)] pub worker: Account<'info, Worker>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts. let config = &ctx.accounts.config; let fee = &mut ctx.accounts.fee; let registry = &ctx.accounts.registry; let snapshot = &ctx.accounts.snapshot; let snapshot_frame = &ctx.accounts.snapshot_frame; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; let worker = &mut ctx.accounts.worker; // Calculate the fee account's usuable balance. @@ -88,7 +88,7 @@ pub fn handler(ctx: Context) -> Result) -> Result) -> Result) -> Result { )] pub snapshot: Account<'info, Snapshot>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { let config = &ctx.accounts.config; let registry = &mut ctx.accounts.registry; let snapshot = &ctx.accounts.snapshot; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; - Ok(ThreadResponse { + Ok(AutomationResponse { next_instruction: if snapshot.total_frames.gt(&0) { Some(InstructionData { program_id: crate::ID, @@ -57,7 +57,7 @@ pub fn handler(ctx: Context) -> Result { )] pub registry: Account<'info, Registry>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { let registry = &mut ctx.accounts.registry; registry.current_epoch = registry.current_epoch.checked_add(1).unwrap(); registry.locked = false; - Ok(ThreadResponse { + Ok(AutomationResponse { next_instruction: None, trigger: None, }) diff --git a/programs/network/src/jobs/process_unstakes/job.rs b/programs/network/src/jobs/process_unstakes/job.rs index 22f2457f..f0058957 100644 --- a/programs/network/src/jobs/process_unstakes/job.rs +++ b/programs/network/src/jobs/process_unstakes/job.rs @@ -1,6 +1,6 @@ use anchor_lang::prelude::*; use clockwork_utils::automation::{ - anchor_sighash, AccountMetaData, InstructionData, ThreadResponse, + anchor_sighash, AccountMetaData, InstructionData, AutomationResponse, }; use crate::state::*; @@ -16,25 +16,25 @@ pub struct ProcessUnstakesJob<'info> { )] pub registry: Account<'info, Registry>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts. let config = &ctx.accounts.config; let registry = &ctx.accounts.registry; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; - // Return next instruction for thread. - Ok(ThreadResponse { + // Return next instruction for automation. + Ok(AutomationResponse { next_instruction: if registry.total_unstakes.gt(&0) { Some(InstructionData { program_id: crate::ID, accounts: vec![ AccountMetaData::new_readonly(config.key(), false), AccountMetaData::new_readonly(registry.key(), false), - AccountMetaData::new_readonly(thread.key(), true), + AccountMetaData::new_readonly(automation.key(), true), AccountMetaData::new_readonly(Unstake::pubkey(0), false), ], data: anchor_sighash("unstake_preprocess").to_vec(), diff --git a/programs/network/src/jobs/process_unstakes/unstake_preprocess.rs b/programs/network/src/jobs/process_unstakes/unstake_preprocess.rs index 0b40ebbb..0ef6cb35 100644 --- a/programs/network/src/jobs/process_unstakes/unstake_preprocess.rs +++ b/programs/network/src/jobs/process_unstakes/unstake_preprocess.rs @@ -1,7 +1,7 @@ use anchor_lang::prelude::*; use anchor_spl::associated_token::get_associated_token_address; use clockwork_utils::automation::{ - anchor_sighash, AccountMetaData, InstructionData, ThreadResponse, + anchor_sighash, AccountMetaData, InstructionData, AutomationResponse, }; use crate::state::*; @@ -17,22 +17,22 @@ pub struct UnstakePreprocess<'info> { )] pub registry: Account<'info, Registry>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, #[account(address = unstake.pubkey())] pub unstake: Account<'info, Unstake>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts. let config = &ctx.accounts.config; let registry = &ctx.accounts.registry; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; let unstake = &ctx.accounts.unstake; - // Return next instruction for thread. - Ok(ThreadResponse { + // Return next instruction for automation. + Ok(AutomationResponse { next_instruction: Some(InstructionData { program_id: crate::ID, accounts: vec![ @@ -40,7 +40,7 @@ pub fn handler(ctx: Context) -> Result { AccountMetaData::new_readonly(config.key(), false), AccountMetaData::new(unstake.delegation, false), AccountMetaData::new(registry.key(), false), - AccountMetaData::new_readonly(thread.key(), true), + AccountMetaData::new_readonly(automation.key(), true), AccountMetaData::new_readonly(anchor_spl::token::ID, false), AccountMetaData::new(unstake.key(), false), AccountMetaData::new_readonly(unstake.worker, false), @@ -51,6 +51,6 @@ pub fn handler(ctx: Context) -> Result { ], data: anchor_sighash("unstake_process").to_vec(), }), - ..ThreadResponse::default() + ..AutomationResponse::default() }) } diff --git a/programs/network/src/jobs/process_unstakes/unstake_process.rs b/programs/network/src/jobs/process_unstakes/unstake_process.rs index 70978d2d..4d1781ad 100644 --- a/programs/network/src/jobs/process_unstakes/unstake_process.rs +++ b/programs/network/src/jobs/process_unstakes/unstake_process.rs @@ -1,7 +1,7 @@ use anchor_lang::prelude::*; use anchor_spl::token::{transfer, Token, TokenAccount, Transfer}; use clockwork_utils::automation::{ - anchor_sighash, AccountMetaData, InstructionData, ThreadResponse, + anchor_sighash, AccountMetaData, InstructionData, AutomationResponse, }; use crate::{errors::*, state::*}; @@ -41,8 +41,8 @@ pub struct UnstakeProcess<'info> { )] pub registry: Box>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, #[account(address = anchor_spl::token::ID)] pub token_program: Program<'info, Token>, @@ -70,14 +70,14 @@ pub struct UnstakeProcess<'info> { pub worker_tokens: Box>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts. let authority = &ctx.accounts.authority; let authority_tokens = &ctx.accounts.authority_tokens; let config = &ctx.accounts.config; let delegation = &mut ctx.accounts.delegation; let registry = &mut ctx.accounts.registry; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; let token_program = &ctx.accounts.token_program; let unstake = &ctx.accounts.unstake; let worker = &ctx.accounts.worker; @@ -129,7 +129,7 @@ pub fn handler(ctx: Context) -> Result { registry.total_unstakes = 0; } - // Build next instruction for the thread. + // Build next instruction for the automation. let next_instruction = if unstake .id .checked_add(1) @@ -142,7 +142,7 @@ pub fn handler(ctx: Context) -> Result { accounts: vec![ AccountMetaData::new_readonly(config.key(), false), AccountMetaData::new_readonly(registry.key(), false), - AccountMetaData::new_readonly(thread.key(), true), + AccountMetaData::new_readonly(automation.key(), true), AccountMetaData::new_readonly(next_unstake_pubkey, false), ], data: anchor_sighash("unstake_preprocess").to_vec(), @@ -151,7 +151,7 @@ pub fn handler(ctx: Context) -> Result { None }; - Ok(ThreadResponse { + Ok(AutomationResponse { next_instruction, trigger: None, }) diff --git a/programs/network/src/jobs/stake_delegations/job.rs b/programs/network/src/jobs/stake_delegations/job.rs index b6d8aea6..fed22cf2 100644 --- a/programs/network/src/jobs/stake_delegations/job.rs +++ b/programs/network/src/jobs/stake_delegations/job.rs @@ -1,6 +1,6 @@ use anchor_lang::prelude::*; use clockwork_utils::automation::{ - anchor_sighash, AccountMetaData, InstructionData, ThreadResponse, + anchor_sighash, AccountMetaData, InstructionData, AutomationResponse, }; use crate::state::*; @@ -16,23 +16,23 @@ pub struct StakeDelegationsJob<'info> { )] pub registry: Account<'info, Registry>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { let config = &ctx.accounts.config; let registry = &ctx.accounts.registry; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; - Ok(ThreadResponse { + Ok(AutomationResponse { next_instruction: if registry.total_workers.gt(&0) { Some(InstructionData { program_id: crate::ID, accounts: vec![ AccountMetaData::new_readonly(config.key(), false), AccountMetaData::new_readonly(registry.key(), false), - AccountMetaData::new_readonly(thread.key(), true), + AccountMetaData::new_readonly(automation.key(), true), AccountMetaData::new_readonly(Worker::pubkey(0), false), ], data: anchor_sighash("stake_delegations_process_worker").to_vec(), diff --git a/programs/network/src/jobs/stake_delegations/process_delegation.rs b/programs/network/src/jobs/stake_delegations/process_delegation.rs index 012e936a..87084614 100644 --- a/programs/network/src/jobs/stake_delegations/process_delegation.rs +++ b/programs/network/src/jobs/stake_delegations/process_delegation.rs @@ -4,7 +4,7 @@ use anchor_spl::{ token::{transfer, Token, TokenAccount, Transfer}, }; use clockwork_utils::automation::{ - anchor_sighash, AccountMetaData, InstructionData, ThreadResponse, + anchor_sighash, AccountMetaData, InstructionData, AutomationResponse, }; use crate::state::*; @@ -38,8 +38,8 @@ pub struct StakeDelegationsProcessDelegation<'info> { )] pub registry: Account<'info, Registry>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, #[account(address = anchor_spl::token::ID)] pub token_program: Program<'info, Token>, @@ -54,13 +54,13 @@ pub struct StakeDelegationsProcessDelegation<'info> { pub worker_stake: Account<'info, TokenAccount>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts. let config = &ctx.accounts.config; let delegation = &mut ctx.accounts.delegation; let delegation_stake = &mut ctx.accounts.delegation_stake; let registry = &ctx.accounts.registry; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; let token_program = &ctx.accounts.token_program; let worker = &ctx.accounts.worker; let worker_stake = &ctx.accounts.worker_stake; @@ -89,7 +89,7 @@ pub fn handler(ctx: Context) -> Result) -> Result) -> Result) -> Result { )] pub registry: Account<'info, Registry>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, #[account(address = worker.pubkey())] pub worker: Account<'info, Worker>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts. let config = &ctx.accounts.config; let registry = &ctx.accounts.registry; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; let worker = &ctx.accounts.worker; - // Build the next instruction for the thread. + // Build the next instruction for the automation. let next_instruction = if worker.total_delegations.gt(&0) { // This worker has delegations. Stake their deposits. let delegation_pubkey = Delegation::pubkey(worker.key(), 0); @@ -45,7 +45,7 @@ pub fn handler(ctx: Context) -> Result) -> Result) -> Result { #[account(address = system_program::ID)] pub system_program: Program<'info, System>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, #[account( address = worker.pubkey(), @@ -73,7 +73,7 @@ pub struct TakeSnapshotCreateEntry<'info> { pub worker: Box>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts. let config = &ctx.accounts.config; let delegation = &ctx.accounts.delegation; @@ -82,7 +82,7 @@ pub fn handler(ctx: Context) -> Result let snapshot_entry = &mut ctx.accounts.snapshot_entry; let snapshot_frame = &mut ctx.accounts.snapshot_frame; let system_program = &ctx.accounts.system_program; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; let worker = &ctx.accounts.worker; // Initialize snapshot entry account. @@ -96,7 +96,7 @@ pub fn handler(ctx: Context) -> Result // Update the snapshot frame. snapshot_frame.total_entries = snapshot_frame.total_entries.checked_add(1).unwrap(); - // Build the next instruction for the thread. + // Build the next instruction for the automation. let next_instruction = if snapshot_frame.total_entries.lt(&worker.total_delegations) { // Create a snapshot entry for the next delegation. let next_delegation_pubkey = @@ -111,7 +111,7 @@ pub fn handler(ctx: Context) -> Result AccountMetaData::new_readonly(config.key(), false), AccountMetaData::new_readonly(next_delegation_pubkey, false), AccountMetaData::new(PAYER_PUBKEY, true), - AccountMetaData::new_readonly(thread.key(), true), + AccountMetaData::new_readonly(automation.key(), true), AccountMetaData::new_readonly(registry.key(), false), AccountMetaData::new_readonly(snapshot.key(), false), AccountMetaData::new(next_snapshot_entry_pubkey, false), @@ -135,7 +135,7 @@ pub fn handler(ctx: Context) -> Result AccountMetaData::new(snapshot.key(), false), AccountMetaData::new(next_snapshot_frame_pubkey, false), AccountMetaData::new_readonly(system_program.key(), false), - AccountMetaData::new_readonly(thread.key(), true), + AccountMetaData::new_readonly(automation.key(), true), AccountMetaData::new_readonly(next_worker_pubkey, false), AccountMetaData::new_readonly( get_associated_token_address(&next_worker_pubkey, &config.mint), @@ -148,8 +148,8 @@ pub fn handler(ctx: Context) -> Result None }; - Ok(ThreadResponse { + Ok(AutomationResponse { next_instruction, - ..ThreadResponse::default() + ..AutomationResponse::default() }) } diff --git a/programs/network/src/jobs/take_snapshot/create_frame.rs b/programs/network/src/jobs/take_snapshot/create_frame.rs index f4db123d..5a1246ac 100644 --- a/programs/network/src/jobs/take_snapshot/create_frame.rs +++ b/programs/network/src/jobs/take_snapshot/create_frame.rs @@ -1,7 +1,7 @@ use anchor_lang::{prelude::*, solana_program::system_program}; use anchor_spl::{associated_token::get_associated_token_address, token::TokenAccount}; use clockwork_utils::automation::{ - anchor_sighash, AccountMetaData, InstructionData, ThreadResponse, PAYER_PUBKEY, + anchor_sighash, AccountMetaData, InstructionData, AutomationResponse, PAYER_PUBKEY, }; use std::mem::size_of; @@ -49,8 +49,8 @@ pub struct TakeSnapshotCreateFrame<'info> { #[account(address = system_program::ID)] pub system_program: Program<'info, System>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, #[account( address = worker.pubkey(), @@ -65,14 +65,14 @@ pub struct TakeSnapshotCreateFrame<'info> { pub worker_stake: Account<'info, TokenAccount>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts. let config = &ctx.accounts.config; let registry = &ctx.accounts.registry; let snapshot = &mut ctx.accounts.snapshot; let snapshot_frame = &mut ctx.accounts.snapshot_frame; let system_program = &ctx.accounts.system_program; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; let worker = &ctx.accounts.worker; let worker_stake = &ctx.accounts.worker_stake; @@ -92,7 +92,7 @@ pub fn handler(ctx: Context) -> Result .unwrap(); snapshot.total_frames = snapshot.total_frames.checked_add(1).unwrap(); - // Build the next instruction for the thread. + // Build the next instruction for the automation. let next_instruction = if worker.total_delegations.gt(&0) { // This worker has delegations. Create a snapshot entry for each delegation associated with this worker. let zeroth_delegation_pubkey = Delegation::pubkey(worker.pubkey(), 0); @@ -108,7 +108,7 @@ pub fn handler(ctx: Context) -> Result AccountMetaData::new(zeroth_snapshot_entry_pubkey, false), AccountMetaData::new(snapshot_frame.key(), false), AccountMetaData::new_readonly(system_program.key(), false), - AccountMetaData::new_readonly(thread.key(), true), + AccountMetaData::new_readonly(automation.key(), true), AccountMetaData::new_readonly(worker.key(), false), ], data: anchor_sighash("take_snapshot_create_entry").to_vec(), @@ -127,7 +127,7 @@ pub fn handler(ctx: Context) -> Result AccountMetaData::new(snapshot.key(), false), AccountMetaData::new(next_snapshot_frame_pubkey, false), AccountMetaData::new_readonly(system_program.key(), false), - AccountMetaData::new_readonly(thread.key(), true), + AccountMetaData::new_readonly(automation.key(), true), AccountMetaData::new_readonly(next_worker_pubkey, false), AccountMetaData::new_readonly( get_associated_token_address(&next_worker_pubkey, &config.mint), @@ -140,7 +140,7 @@ pub fn handler(ctx: Context) -> Result None }; - Ok(ThreadResponse { + Ok(AutomationResponse { next_instruction, trigger: None, }) diff --git a/programs/network/src/jobs/take_snapshot/create_snapshot.rs b/programs/network/src/jobs/take_snapshot/create_snapshot.rs index 58528319..22b8a743 100644 --- a/programs/network/src/jobs/take_snapshot/create_snapshot.rs +++ b/programs/network/src/jobs/take_snapshot/create_snapshot.rs @@ -1,7 +1,7 @@ use anchor_lang::{prelude::*, solana_program::system_program}; use anchor_spl::associated_token::get_associated_token_address; use clockwork_utils::automation::{ - anchor_sighash, AccountMetaData, InstructionData, ThreadResponse, PAYER_PUBKEY, + anchor_sighash, AccountMetaData, InstructionData, AutomationResponse, PAYER_PUBKEY, }; use std::mem::size_of; @@ -36,22 +36,22 @@ pub struct TakeSnapshotCreateSnapshot<'info> { #[account(address = system_program::ID)] pub system_program: Program<'info, System>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts let config = &ctx.accounts.config; let registry = &ctx.accounts.registry; let snapshot = &mut ctx.accounts.snapshot; let system_program = &ctx.accounts.system_program; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; // Start a new snapshot. snapshot.init(registry.current_epoch.checked_add(1).unwrap())?; - Ok(ThreadResponse { + Ok(AutomationResponse { next_instruction: if registry.total_workers.gt(&0) { // The registry has workers. Create a snapshot frame for the zeroth worker. let snapshot_frame_pubkey = SnapshotFrame::pubkey(snapshot.key(), 0); @@ -65,7 +65,7 @@ pub fn handler(ctx: Context) -> Result { )] pub registry: Account<'info, Registry>, - #[account(address = config.epoch_thread)] - pub thread: Signer<'info>, + #[account(address = config.epoch_automation)] + pub automation: Signer<'info>, } -pub fn handler(ctx: Context) -> Result { +pub fn handler(ctx: Context) -> Result { // Get accounts let config = &ctx.accounts.config; let registry = &ctx.accounts.registry; - let thread = &ctx.accounts.thread; + let automation = &ctx.accounts.automation; - Ok(ThreadResponse { + Ok(AutomationResponse { next_instruction: Some(InstructionData { program_id: crate::ID, accounts: vec![ @@ -38,7 +38,7 @@ pub fn handler(ctx: Context) -> Result { false, ), AccountMetaData::new_readonly(system_program::ID, false), - AccountMetaData::new_readonly(thread.key(), true), + AccountMetaData::new_readonly(automation.key(), true), ], data: anchor_sighash("take_snapshot_create_snapshot").to_vec(), }), diff --git a/programs/network/src/lib.rs b/programs/network/src/lib.rs index bc5b434b..dc678027 100644 --- a/programs/network/src/lib.rs +++ b/programs/network/src/lib.rs @@ -61,7 +61,7 @@ pub mod network_program { pool_update::handler(ctx, settings) } - pub fn registry_nonce_hash(ctx: Context) -> Result { + pub fn registry_nonce_hash(ctx: Context) -> Result { registry_nonce_hash::handler(ctx) } @@ -87,111 +87,111 @@ pub mod network_program { // DistributeFees job - pub fn distribute_fees_job(ctx: Context) -> Result { + pub fn distribute_fees_job(ctx: Context) -> Result { jobs::distribute_fees::job::handler(ctx) } pub fn distribute_fees_process_entry( ctx: Context, - ) -> Result { + ) -> Result { jobs::distribute_fees::process_entry::handler(ctx) } pub fn distribute_fees_process_frame( ctx: Context, - ) -> Result { + ) -> Result { jobs::distribute_fees::process_frame::handler(ctx) } pub fn distribute_fees_process_snapshot( ctx: Context, - ) -> Result { + ) -> Result { jobs::distribute_fees::process_snapshot::handler(ctx) } // StakeDelegations job - pub fn stake_delegations_job(ctx: Context) -> Result { + pub fn stake_delegations_job(ctx: Context) -> Result { jobs::stake_delegations::job::handler(ctx) } pub fn stake_delegations_process_worker( ctx: Context, - ) -> Result { + ) -> Result { jobs::stake_delegations::process_worker::handler(ctx) } pub fn stake_delegations_process_delegation( ctx: Context, - ) -> Result { + ) -> Result { jobs::stake_delegations::process_delegation::handler(ctx) } // TakeSnapshot job - pub fn take_snapshot_job(ctx: Context) -> Result { + pub fn take_snapshot_job(ctx: Context) -> Result { jobs::take_snapshot::job::handler(ctx) } pub fn take_snapshot_create_entry( ctx: Context, - ) -> Result { + ) -> Result { jobs::take_snapshot::create_entry::handler(ctx) } pub fn take_snapshot_create_frame( ctx: Context, - ) -> Result { + ) -> Result { jobs::take_snapshot::create_frame::handler(ctx) } pub fn take_snapshot_create_snapshot( ctx: Context, - ) -> Result { + ) -> Result { jobs::take_snapshot::create_snapshot::handler(ctx) } // IncrementEpoch job - pub fn increment_epoch(ctx: Context) -> Result { + pub fn increment_epoch(ctx: Context) -> Result { jobs::increment_epoch::job::handler(ctx) } // Delete snapshot - pub fn delete_snapshot_job(ctx: Context) -> Result { + pub fn delete_snapshot_job(ctx: Context) -> Result { jobs::delete_snapshot::job::handler(ctx) } pub fn delete_snapshot_process_snapshot( ctx: Context, - ) -> Result { + ) -> Result { jobs::delete_snapshot::process_snapshot::handler(ctx) } pub fn delete_snapshot_process_frame( ctx: Context, - ) -> Result { + ) -> Result { jobs::delete_snapshot::process_frame::handler(ctx) } pub fn delete_snapshot_process_entry( ctx: Context, - ) -> Result { + ) -> Result { jobs::delete_snapshot::process_entry::handler(ctx) } // ProcessUnstakes job - pub fn process_unstakes_job(ctx: Context) -> Result { + pub fn process_unstakes_job(ctx: Context) -> Result { jobs::process_unstakes::job::handler(ctx) } - pub fn unstake_preprocess(ctx: Context) -> Result { + pub fn unstake_preprocess(ctx: Context) -> Result { jobs::process_unstakes::unstake_preprocess::handler(ctx) } - pub fn unstake_process(ctx: Context) -> Result { + pub fn unstake_process(ctx: Context) -> Result { jobs::process_unstakes::unstake_process::handler(ctx) } } diff --git a/programs/network/src/state/config.rs b/programs/network/src/state/config.rs index 50baae8f..b425b3e9 100644 --- a/programs/network/src/state/config.rs +++ b/programs/network/src/state/config.rs @@ -11,8 +11,8 @@ pub const SEED_CONFIG: &[u8] = b"config"; #[derive(Debug, TryFromData)] pub struct Config { pub admin: Pubkey, - pub epoch_thread: Pubkey, - pub hasher_thread: Pubkey, + pub epoch_automation: Pubkey, + pub hasher_automation: Pubkey, pub mint: Pubkey, } @@ -29,8 +29,8 @@ impl Config { #[derive(AnchorSerialize, AnchorDeserialize)] pub struct ConfigSettings { pub admin: Pubkey, - pub epoch_thread: Pubkey, - pub hasher_thread: Pubkey, + pub epoch_automation: Pubkey, + pub hasher_automation: Pubkey, pub mint: Pubkey, } @@ -53,8 +53,8 @@ impl ConfigAccount for Account<'_, Config> { fn update(&mut self, settings: ConfigSettings) -> Result<()> { self.admin = settings.admin; - self.epoch_thread = settings.epoch_thread; - self.hasher_thread = settings.hasher_thread; + self.epoch_automation = settings.epoch_automation; + self.hasher_automation = settings.hasher_automation; self.mint = settings.mint; Ok(()) } diff --git a/programs/thread/src/errors.rs b/programs/thread/src/errors.rs deleted file mode 100644 index a1bf95fe..00000000 --- a/programs/thread/src/errors.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! Errors thrown by the program. - -use anchor_lang::prelude::*; - -/// Errors for the the Clockwork thread program. -#[error_code] -pub enum ClockworkError { - /// Thrown if a exec response has an invalid program ID or cannot be parsed. - #[msg("The exec response could not be parsed")] - InvalidThreadResponse, - - /// Thrown if a thread has an invalid state and cannot complete the operation. - #[msg("The thread is in an invalid state")] - InvalidThreadState, - - /// TThe provided trigger variant is invalid. - #[msg("The trigger variant cannot be changed")] - InvalidTriggerVariant, - - /// Thrown if a exec instruction is invalid because the thread's trigger condition has not been met. - #[msg("The trigger condition has not been activated")] - TriggerNotActive, - - #[msg("This operation cannot be processes because the thread is currently busy")] - ThreadBusy, - - /// Thrown if a request is invalid because the thread is currently paused. - #[msg("The thread is currently paused")] - ThreadPaused, - - /// Thrown if a exec instruction would cause a thread to exceed its rate limit. - #[msg("The thread's rate limit has been reached")] - RateLimitExeceeded, - - /// Thrown if a thread authority attempts to set a rate limit above the maximum allowed value. - #[msg("Thread rate limits cannot exceed the maximum allowed value")] - MaxRateLimitExceeded, - - /// Thrown if an inner instruction attempted to write to an unauthorized address. - #[msg("Inner instruction attempted to write to an unauthorized address")] - UnauthorizedWrite, - - /// Thrown if the user attempts to withdraw SOL that would put a thread below it's minimum rent threshold. - #[msg("Withdrawing this amount would leave the thread with less than the minimum required SOL for rent exemption")] - WithdrawalTooLarge, -} diff --git a/programs/thread/src/instructions/mod.rs b/programs/thread/src/instructions/mod.rs deleted file mode 100644 index be734b82..00000000 --- a/programs/thread/src/instructions/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub mod get_crate_info; -pub mod thread_create; -pub mod thread_delete; -pub mod thread_exec; -pub mod thread_kickoff; -pub mod thread_pause; -pub mod thread_resume; -pub mod thread_stop; -pub mod thread_update; -pub mod thread_withdraw; - -pub use get_crate_info::*; -pub use thread_create::*; -pub use thread_delete::*; -pub use thread_exec::*; -pub use thread_kickoff::*; -pub use thread_pause::*; -pub use thread_resume::*; -pub use thread_stop::*; -pub use thread_update::*; -pub use thread_withdraw::*; diff --git a/programs/thread/src/instructions/thread_delete.rs b/programs/thread/src/instructions/thread_delete.rs deleted file mode 100644 index 257554ea..00000000 --- a/programs/thread/src/instructions/thread_delete.rs +++ /dev/null @@ -1,31 +0,0 @@ -use {crate::state::*, anchor_lang::prelude::*}; - -/// Accounts required by the `thread_delete` instruction. -#[derive(Accounts)] -pub struct ThreadDelete<'info> { - /// The authority (owner) of the thread. - #[account()] - pub authority: Signer<'info>, - - /// The address to return the data rent lamports to. - #[account(mut)] - pub close_to: SystemAccount<'info>, - - /// The thread to be delete. - #[account( - mut, - seeds = [ - SEED_THREAD, - thread.authority.as_ref(), - thread.id.as_slice(), - ], - bump = thread.bump, - has_one = authority, - close = close_to - )] - pub thread: Account<'info, Thread>, -} - -pub fn handler(_ctx: Context) -> Result<()> { - Ok(()) -} diff --git a/programs/thread/src/instructions/thread_pause.rs b/programs/thread/src/instructions/thread_pause.rs deleted file mode 100644 index 10acb487..00000000 --- a/programs/thread/src/instructions/thread_pause.rs +++ /dev/null @@ -1,32 +0,0 @@ -use {crate::state::*, anchor_lang::prelude::*}; - -/// Accounts required by the `thread_delete` instruction. -#[derive(Accounts)] -pub struct ThreadPause<'info> { - /// The authority (owner) of the thread. - #[account()] - pub authority: Signer<'info>, - - /// The thread to be paused. - #[account( - mut, - seeds = [ - SEED_THREAD, - thread.authority.as_ref(), - thread.id.as_slice(), - ], - bump = thread.bump, - has_one = authority - )] - pub thread: Account<'info, Thread>, -} - -pub fn handler(ctx: Context) -> Result<()> { - // Get accounts - let thread = &mut ctx.accounts.thread; - - // Pause the thread - thread.paused = true; - - Ok(()) -} diff --git a/programs/thread/src/instructions/thread_stop.rs b/programs/thread/src/instructions/thread_stop.rs deleted file mode 100644 index 0fe61862..00000000 --- a/programs/thread/src/instructions/thread_stop.rs +++ /dev/null @@ -1,32 +0,0 @@ -use {crate::state::*, anchor_lang::prelude::*}; - -/// Accounts required by the `thread_delete` instruction. -#[derive(Accounts)] -pub struct ThreadStop<'info> { - /// The authority (owner) of the thread. - #[account()] - pub authority: Signer<'info>, - - /// The thread to be paused. - #[account( - mut, - seeds = [ - SEED_THREAD, - thread.authority.as_ref(), - thread.id.as_slice(), - ], - bump = thread.bump, - has_one = authority - )] - pub thread: Account<'info, Thread>, -} - -pub fn handler(ctx: Context) -> Result<()> { - // Get accounts - let thread = &mut ctx.accounts.thread; - - // Pause the thread - thread.next_instruction = None; - - Ok(()) -} diff --git a/programs/thread/src/instructions/thread_update.rs b/programs/thread/src/instructions/thread_update.rs deleted file mode 100644 index f272449c..00000000 --- a/programs/thread/src/instructions/thread_update.rs +++ /dev/null @@ -1,88 +0,0 @@ -use crate::{errors::ClockworkError, state::*}; - -use anchor_lang::{ - prelude::*, - solana_program::system_program, - system_program::{transfer, Transfer}, -}; - -/// Accounts required by the `thread_update` instruction. -#[derive(Accounts)] -#[instruction(settings: ThreadSettings)] -pub struct ThreadUpdate<'info> { - /// The authority (owner) of the thread. - #[account(mut)] - pub authority: Signer<'info>, - - /// The Solana system program - #[account(address = system_program::ID)] - pub system_program: Program<'info, System>, - - /// The thread to be updated. - #[account( - mut, - seeds = [ - SEED_THREAD, - thread.authority.as_ref(), - thread.id.as_slice(), - ], - bump = thread.bump, - has_one = authority, - )] - pub thread: Account<'info, Thread>, -} - -pub fn handler(ctx: Context, settings: ThreadSettings) -> Result<()> { - // Get accounts - let authority = &ctx.accounts.authority; - let thread = &mut ctx.accounts.thread; - let system_program = &ctx.accounts.system_program; - - // Update the thread. - if let Some(fee) = settings.fee { - thread.fee = fee; - } - - // If provided, update the thread's instruction set. - if let Some(instructions) = settings.instructions { - thread.instructions = instructions; - } - - // If provided, update the rate limit. - if let Some(rate_limit) = settings.rate_limit { - thread.rate_limit = rate_limit; - } - - // If provided, update the thread's trigger and reset the exec context. - if let Some(trigger) = settings.trigger { - // Require the thread is not in the middle of processing. - require!( - std::mem::discriminant(&thread.trigger) == std::mem::discriminant(&trigger), - ClockworkError::InvalidTriggerVariant - ); - thread.trigger = trigger; - } - - // Reallocate mem for the thread account - thread.realloc()?; - - // If lamports are required to maintain rent-exemption, pay them - let data_len = 8 + thread.try_to_vec()?.len(); - let minimum_rent = Rent::get().unwrap().minimum_balance(data_len); - if minimum_rent > thread.to_account_info().lamports() { - transfer( - CpiContext::new( - system_program.to_account_info(), - Transfer { - from: authority.to_account_info(), - to: thread.to_account_info(), - }, - ), - minimum_rent - .checked_sub(thread.to_account_info().lamports()) - .unwrap(), - )?; - } - - Ok(()) -} diff --git a/programs/thread/src/lib.rs b/programs/thread/src/lib.rs deleted file mode 100644 index 2e19ac4c..00000000 --- a/programs/thread/src/lib.rs +++ /dev/null @@ -1,82 +0,0 @@ -//! This program allows users to create transaction threads on Solana. Threads are dynamic, long-running -//! transaction threads that can persist across blocks and even run indefinitely. Developers can use threads -//! to schedule transactions and automate smart-contracts without relying on centralized infrastructure. -#[macro_use] -extern crate version; - -pub mod errors; -pub mod state; - -mod instructions; - -use anchor_lang::prelude::*; -use clockwork_utils::{ - automation::{InstructionData, Trigger}, - CrateInfo, -}; -use instructions::*; -use state::*; - -declare_id!("3XXuUFfweXBwFgFfYaejLvZE4cGZiHgKiGfMtdxNzYmv"); - -/// Program for creating transaction threads on Solana. -#[program] -pub mod thread_program { - use super::*; - - /// Return the crate information via `sol_set_return_data/sol_get_return_data` - pub fn get_crate_info(ctx: Context) -> Result { - get_crate_info::handler(ctx) - } - - /// Executes the next instruction on thread. - pub fn thread_exec(ctx: Context) -> Result<()> { - thread_exec::handler(ctx) - } - - /// Creates a new transaction thread. - pub fn thread_create( - ctx: Context, - amount: u64, - id: Vec, - instructions: Vec, - trigger: Trigger, - ) -> Result<()> { - thread_create::handler(ctx, amount, id, instructions, trigger) - } - - /// Closes an existing thread account and returns the lamports to the owner. - pub fn thread_delete(ctx: Context) -> Result<()> { - thread_delete::handler(ctx) - } - - /// Kicks off a thread if its trigger condition is active. - pub fn thread_kickoff(ctx: Context) -> Result<()> { - thread_kickoff::handler(ctx) - } - - /// Pauses an active thread. - pub fn thread_pause(ctx: Context) -> Result<()> { - thread_pause::handler(ctx) - } - - /// Resumes a paused thread. - pub fn thread_resume(ctx: Context) -> Result<()> { - thread_resume::handler(ctx) - } - - /// Resumes a paused thread. - pub fn thread_stop(ctx: Context) -> Result<()> { - thread_stop::handler(ctx) - } - - /// Allows an owner to update the mutable properties of a thread. - pub fn thread_update(ctx: Context, settings: ThreadSettings) -> Result<()> { - thread_update::handler(ctx, settings) - } - - /// Allows an owner to withdraw from a thread's lamport balance. - pub fn thread_withdraw(ctx: Context, amount: u64) -> Result<()> { - thread_withdraw::handler(ctx, amount) - } -} diff --git a/scripts/build-all.sh b/scripts/build-all.sh index 01eb9c1a..f33e8775 100755 --- a/scripts/build-all.sh +++ b/scripts/build-all.sh @@ -93,7 +93,7 @@ if command -v anchor &> /dev/null; then # Copy program binaries into lib folder cp -fv "target/deploy/clockwork_network_program.so" "$installDir"/lib - cp -fv "target/deploy/clockwork_thread_program.so" "$installDir"/lib + cp -fv "target/deploy/clockwork_automation_program.so" "$installDir"/lib cp -fv "target/deploy/clockwork_webhook_program.so" "$installDir"/lib fi diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh index d0752758..c2656c4f 100755 --- a/scripts/bump-version.sh +++ b/scripts/bump-version.sh @@ -16,18 +16,18 @@ sed -i '' -e '3s/^version = "'${current_version}'"/version = "'${new_version}'"/ # Bump programs sed -i '' -e '3s/^version = "'${current_version}'"/version = "'${new_version}'"/g' programs/network/Cargo.toml -sed -i '' -e '3s/^version = "'${current_version}'"/version = "'${new_version}'"/g' programs/thread/Cargo.toml +sed -i '' -e '3s/^version = "'${current_version}'"/version = "'${new_version}'"/g' programs/automation/Cargo.toml sed -i '' -e '3s/^version = "'${current_version}'"/version = "'${new_version}'"/g' programs/webhook/Cargo.toml # Bump network program dependencies sed -i '' -e 's/^clockwork-utils =.*/clockwork-utils = { path = "..\/..\/utils", version = "'${new_version}'" }/g' programs/network/Cargo.toml sed -i '' -e 's/^clockwork-macros =.*/clockwork-macros = { path = "..\/..\/macros", version = "'${new_version}'" }/g' programs/network/Cargo.toml -# Bump thread program dependencies -sed -i '' -e 's/^clockwork-cron =.*/clockwork-cron = { path = "..\/..\/cron", version = "'${new_version}'" }/g' programs/thread/Cargo.toml -sed -i '' -e 's/^clockwork-macros =.*/clockwork-macros = { path = "..\/..\/macros", version = "'${new_version}'" }/g' programs/thread/Cargo.toml -sed -i '' -e 's/^clockwork-network-program =.*/clockwork-network-program = { path = "..\/network", features = ["cpi"], version = "'${new_version}'" }/g' programs/thread/Cargo.toml -sed -i '' -e 's/^clockwork-utils =.*/clockwork-utils = { path = "..\/..\/utils", version = "'${new_version}'" }/g' programs/thread/Cargo.toml +# Bump automation program dependencies +sed -i '' -e 's/^clockwork-cron =.*/clockwork-cron = { path = "..\/..\/cron", version = "'${new_version}'" }/g' programs/automation/Cargo.toml +sed -i '' -e 's/^clockwork-macros =.*/clockwork-macros = { path = "..\/..\/macros", version = "'${new_version}'" }/g' programs/automation/Cargo.toml +sed -i '' -e 's/^clockwork-network-program =.*/clockwork-network-program = { path = "..\/network", features = ["cpi"], version = "'${new_version}'" }/g' programs/automation/Cargo.toml +sed -i '' -e 's/^clockwork-utils =.*/clockwork-utils = { path = "..\/..\/utils", version = "'${new_version}'" }/g' programs/automation/Cargo.toml # Bump webhook program dependencies sed -i '' -e 's/^clockwork-network-program =.*/clockwork-network-program = { path = "..\/network", features = ["cpi"], version = "'${new_version}'" }/g' programs/webhook/Cargo.toml @@ -35,7 +35,7 @@ sed -i '' -e 's/^clockwork-macros =.*/clockwork-macros = { path = "..\/..\/macro # Bump clockwork-client sed -i '' -e 's/^clockwork-network-program =.*/clockwork-network-program = { path = "..\/programs\/network", features = ["no-entrypoint"], version = "'${new_version}'" }/g' client/Cargo.toml -sed -i '' -e 's/^clockwork-thread-program =.*/clockwork-thread-program = { path = "..\/programs\/thread", features = ["no-entrypoint"], version = "'${new_version}'" }/g' client/Cargo.toml +sed -i '' -e 's/^clockwork-automation-program =.*/clockwork-automation-program = { path = "..\/programs\/automation", features = ["no-entrypoint"], version = "'${new_version}'" }/g' client/Cargo.toml sed -i '' -e 's/^clockwork-utils =.*/clockwork-utils = { path = "..\/utils", version = "'${new_version}'" }/g' client/Cargo.toml sed -i '' -e 's/^clockwork-webhook-program =.*/clockwork-webhook-program = { path = "..\/programs\/webhook", features = ["no-entrypoint"], version = "'${new_version}'" }/g' client/Cargo.toml sed -i '' -e '3s/^version = "'${current_version}'"/version = "'${new_version}'"/g' client/Cargo.toml @@ -53,7 +53,7 @@ sed -i '' -e '3s/^version = "'${current_version}'"/version = "'${new_version}'"/ # Bump clockwork-sdk sed -i '' -e 's/^clockwork-client =.*/clockwork-client = { path = "..\/client", version = "'${new_version}'", optional = true }/g' sdk/Cargo.toml -sed -i '' -e 's/^clockwork-thread-program =.*/clockwork-thread-program = { path = "..\/programs\/thread", features = ["cpi"], version = "'${new_version}'" }/g' sdk/Cargo.toml +sed -i '' -e 's/^clockwork-automation-program =.*/clockwork-automation-program = { path = "..\/programs\/automation", features = ["cpi"], version = "'${new_version}'" }/g' sdk/Cargo.toml sed -i '' -e '3s/^version = "'${current_version}'"/version = "'${new_version}'"/g' sdk/Cargo.toml # Bump clockwork-utils diff --git a/scripts/cargo-publish.sh b/scripts/cargo-publish.sh index 5642e762..fa2978a1 100755 --- a/scripts/cargo-publish.sh +++ b/scripts/cargo-publish.sh @@ -11,7 +11,7 @@ sleep 25 # Publish programs cargo publish -p clockwork-network-program sleep 25 -cargo publish -p clockwork-thread-program +cargo publish -p clockwork-automation-program sleep 25 cargo publish -p clockwork-webhook-program sleep 25 diff --git a/scripts/debug_plugin.sh b/scripts/debug_plugin.sh index bf948a25..a2a0efd9 100755 --- a/scripts/debug_plugin.sh +++ b/scripts/debug_plugin.sh @@ -3,9 +3,9 @@ set -e # Rebuid programs -rm -rf lib/clockwork_thread_program.so -cd programs/thread && anchor build; cd -; -cp -fv target/deploy/clockwork_thread_program.so lib/ +rm -rf lib/clockwork_automation_program.so +cd programs/automation && anchor build; cd -; +cp -fv target/deploy/clockwork_automation_program.so lib/ # Rebuild plugin rm -rf lib/libclockwork_plugin.dylib diff --git a/scripts/refresh-program-ids.sh b/scripts/refresh-program-ids.sh index a32269e9..4a2bbea6 100755 --- a/scripts/refresh-program-ids.sh +++ b/scripts/refresh-program-ids.sh @@ -8,17 +8,17 @@ anchor build # Get pubkey addresses program_id_network=$(solana address -k target/deploy/clockwork_network_program-keypair.json) -program_id_thread=$(solana address -k target/deploy/clockwork_thread_program-keypair.json) +program_id_automation=$(solana address -k target/deploy/clockwork_automation_program-keypair.json) program_id_webhook=$(solana address -k target/deploy/clockwork_webhook_program-keypair.json) # Update declared program IDs sed -i '' -e 's/^declare_id!(".*");/declare_id!("'${program_id_network}'");/g' programs/network/src/lib.rs -sed -i '' -e 's/^declare_id!(".*");/declare_id!("'${program_id_thread}'");/g' programs/thread/src/lib.rs +sed -i '' -e 's/^declare_id!(".*");/declare_id!("'${program_id_automation}'");/g' programs/automation/src/lib.rs sed -i '' -e 's/^declare_id!(".*");/declare_id!("'${program_id_webhook}'");/g' programs/webhook/src/lib.rs # Update Anchor config sed -i '' -e 's/^clockwork_network_program = ".*"/clockwork_network_program = "'${program_id_network}'"/g' Anchor.toml -sed -i '' -e 's/^clockwork_thread_program = ".*"/clockwork_thread_program = "'${program_id_thread}'"/g' Anchor.toml +sed -i '' -e 's/^clockwork_automation_program = ".*"/clockwork_automation_program = "'${program_id_automation}'"/g' Anchor.toml sed -i '' -e 's/^clockwork_webhook_program = ".*"/clockwork_webhook_program = "'${program_id_webhook}'"/g' Anchor.toml # Rebuild diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 0dca5881..13f4ac34 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -16,7 +16,7 @@ name = "clockwork_sdk" [dependencies] anchor-lang = "0.26.0" chrono = { version = "0.4.19", default-features = false, features = ["alloc"] } -clockwork-thread-program = { path = "../programs/thread", features = ["cpi"], version = "1.4.2" } +clockwork-automation-program = { path = "../programs/automation", features = ["cpi"], version = "1.4.2" } nom = "~7" once_cell = "1.5.2" diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index f6b09690..6e04f34b 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -1,70 +1,70 @@ -pub use clockwork_thread_program::errors; -pub use clockwork_thread_program::program::ThreadProgram; -pub use clockwork_thread_program::ID; +pub use clockwork_automation_program::errors; +pub use clockwork_automation_program::program::AutomationProgram; +pub use clockwork_automation_program::ID; pub mod state { - pub use clockwork_thread_program::state::{ - AccountMetaData, ClockData, ExecContext, InstructionData, Thread, ThreadAccount, - ThreadResponse, ThreadSettings, Trigger, TriggerContext, + pub use clockwork_automation_program::state::{ + AccountMetaData, ClockData, ExecContext, InstructionData, Automation, AutomationAccount, + AutomationResponse, AutomationSettings, Trigger, TriggerContext, }; } pub mod utils { - pub use clockwork_thread_program::state::anchor_sighash; - pub use clockwork_thread_program::state::PAYER_PUBKEY; + pub use clockwork_automation_program::state::anchor_sighash; + pub use clockwork_automation_program::state::PAYER_PUBKEY; } pub mod cpi { use anchor_lang::prelude::{CpiContext, Result}; - pub use clockwork_thread_program::cpi::accounts::{ - ThreadCreate, ThreadDelete, ThreadPause, ThreadResume, ThreadStop, ThreadUpdate, - ThreadWithdraw, + pub use clockwork_automation_program::cpi::accounts::{ + AutomationCreate, AutomationDelete, AutomationPause, AutomationResume, AutomationStop, AutomationUpdate, + AutomationWithdraw, }; - pub fn thread_create<'info>( - ctx: CpiContext<'_, '_, '_, 'info, ThreadCreate<'info>>, + pub fn automation_create<'info>( + ctx: CpiContext<'_, '_, '_, 'info, AutomationCreate<'info>>, amount: u64, id: Vec, instructions: Vec, trigger: crate::state::Trigger, ) -> Result<()> { - clockwork_thread_program::cpi::thread_create(ctx, amount, id, instructions, trigger) + clockwork_automation_program::cpi::automation_create(ctx, amount, id, instructions, trigger) } - pub fn thread_delete<'info>( - ctx: CpiContext<'_, '_, '_, 'info, ThreadDelete<'info>>, + pub fn automation_delete<'info>( + ctx: CpiContext<'_, '_, '_, 'info, AutomationDelete<'info>>, ) -> Result<()> { - clockwork_thread_program::cpi::thread_delete(ctx) + clockwork_automation_program::cpi::automation_delete(ctx) } - pub fn thread_pause<'info>( - ctx: CpiContext<'_, '_, '_, 'info, ThreadPause<'info>>, + pub fn automation_pause<'info>( + ctx: CpiContext<'_, '_, '_, 'info, AutomationPause<'info>>, ) -> Result<()> { - clockwork_thread_program::cpi::thread_pause(ctx) + clockwork_automation_program::cpi::automation_pause(ctx) } - pub fn thread_resume<'info>( - ctx: CpiContext<'_, '_, '_, 'info, ThreadResume<'info>>, + pub fn automation_resume<'info>( + ctx: CpiContext<'_, '_, '_, 'info, AutomationResume<'info>>, ) -> Result<()> { - clockwork_thread_program::cpi::thread_resume(ctx) + clockwork_automation_program::cpi::automation_resume(ctx) } - pub fn thread_stop<'info>(ctx: CpiContext<'_, '_, '_, 'info, ThreadStop<'info>>) -> Result<()> { - clockwork_thread_program::cpi::thread_stop(ctx) + pub fn automation_stop<'info>(ctx: CpiContext<'_, '_, '_, 'info, AutomationStop<'info>>) -> Result<()> { + clockwork_automation_program::cpi::automation_stop(ctx) } - pub fn thread_update<'info>( - ctx: CpiContext<'_, '_, '_, 'info, ThreadUpdate<'info>>, - settings: crate::state::ThreadSettings, + pub fn automation_update<'info>( + ctx: CpiContext<'_, '_, '_, 'info, AutomationUpdate<'info>>, + settings: crate::state::AutomationSettings, ) -> Result<()> { - clockwork_thread_program::cpi::thread_update(ctx, settings) + clockwork_automation_program::cpi::automation_update(ctx, settings) } - pub fn thread_withdraw<'info>( - ctx: CpiContext<'_, '_, '_, 'info, ThreadWithdraw<'info>>, + pub fn automation_withdraw<'info>( + ctx: CpiContext<'_, '_, '_, 'info, AutomationWithdraw<'info>>, amount: u64, ) -> Result<()> { - clockwork_thread_program::cpi::thread_withdraw(ctx, amount) + clockwork_automation_program::cpi::automation_withdraw(ctx, amount) } } diff --git a/utils/src/automation.rs b/utils/src/automation.rs index 15ccc521..4baa0c6d 100644 --- a/utils/src/automation.rs +++ b/utils/src/automation.rs @@ -54,10 +54,10 @@ impl TryFrom> for ClockData { } } -/// The triggering conditions of a thread. +/// The triggering conditions of a automation. #[derive(AnchorDeserialize, AnchorSerialize, Debug, Clone, PartialEq)] pub enum Trigger { - /// Allows a thread to be kicked off whenever the data of an account changes. + /// Allows a automation to be kicked off whenever the data of an account changes. Account { /// The address of the account to monitor. address: Pubkey, @@ -67,7 +67,7 @@ pub enum Trigger { size: u64, }, - /// Allows a thread to be kicked off according to a one-time or recurring schedule. + /// Allows a automation to be kicked off according to a one-time or recurring schedule. Cron { /// The schedule in cron syntax. Value must be parsable by the `clockwork_cron` package. schedule: String, @@ -77,20 +77,20 @@ pub enum Trigger { skippable: bool, }, - /// Allows a thread to be kicked off as soon as it's created. + /// Allows a automation to be kicked off as soon as it's created. Immediate, } -/// A response value target programs can return to update the thread. +/// A response value target programs can return to update the automation. #[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)] -pub struct ThreadResponse { +pub struct AutomationResponse { /// A dynamic instruction to execute next. pub next_instruction: Option, - /// Value to update the thread trigger to. + /// Value to update the automation trigger to. pub trigger: Option, } -impl Default for ThreadResponse { +impl Default for AutomationResponse { fn default() -> Self { return Self { next_instruction: None, diff --git a/utils/src/explorer.rs b/utils/src/explorer.rs index 8cb1e690..674e0e8b 100644 --- a/utils/src/explorer.rs +++ b/utils/src/explorer.rs @@ -62,12 +62,12 @@ impl Explorer { } } - /// Ex: https://explorer.clockwork.xyz/thread/{thread} + /// Ex: https://explorer.clockwork.xyz/automation/{automation} /// ?network=custom /// &customRPC=http://localhost:8899 - pub fn thread_url(&self, thread: T, program_id: U) -> String { + pub fn automation_url(&self, automation: T, program_id: U) -> String { let url = format!("{}/address/{}?programID={}&network={}", CK_EXPLORER_URL, - thread, program_id, self + automation, program_id, self .cluster); if self.cluster == "custom" { url + "&customRPC=" + self.custom_rpc.as_ref().unwrap()