diff --git a/checksum b/checksum index 7edbe48d..97137749 100644 --- a/checksum +++ b/checksum @@ -1,2 +1,2 @@ -7e1411d78e92ac4ab2dbda9208b62f0b4cfffc926cfc61de10498fe4e40b7afd cw_croncat.wasm +853004395db1861f1f337925a76e31b9f8ed5fe158b807c647240ea71876e875 cw_croncat.wasm de2d1a0c648e41760020dd261f818da085c358240059acf85128f60eb0e05db2 cw_rules.wasm diff --git a/contracts/cw-croncat/src/tasks.rs b/contracts/cw-croncat/src/tasks.rs index 874cb2fa..2b6dc969 100644 --- a/contracts/cw-croncat/src/tasks.rs +++ b/contracts/cw-croncat/src/tasks.rs @@ -254,9 +254,7 @@ impl<'a> CwCroncat<'a> { val: "Interval invalid".to_string(), }); } - if task.actions.is_empty() { - return Err(ContractError::CoreError(CoreError::InvalidAction {})); - } + let (mut amount_for_one_task, gas_amount) = task.is_valid_msg_calculate_usage( deps.api, &env.contract.address, diff --git a/contracts/cw-croncat/src/tests/manager.rs b/contracts/cw-croncat/src/tests/manager.rs index e454320a..5ea8d706 100644 --- a/contracts/cw-croncat/src/tests/manager.rs +++ b/contracts/cw-croncat/src/tests/manager.rs @@ -3066,563 +3066,35 @@ fn test_error_in_reply() { } #[test] -fn queries_fees() { +fn empty_actions_not_allowed() { let (mut app, cw_template_contract, _) = proper_instantiate(); let contract_addr = cw_template_contract.addr(); - // quick agent register - let msg = ExecuteMsg::RegisterAgent { - payable_account_id: Some(AGENT_BENEFICIARY.to_string()), - }; - app.execute_contract(Addr::unchecked(AGENT0), contract_addr.clone(), &msg, &[]) - .unwrap(); - - app.update_block(add_little_time); - - // Non-wasm query - let query = CroncatQuery::HasBalanceGte(HasBalanceGte { - address: contract_addr.to_string(), - required_balance: coins(1, NATIVE_DENOM).into(), - }); - - let transfer_to_bob = BankMsg::Send { - to_address: "bob".to_string(), - amount: coins(1, NATIVE_DENOM), - }; - - let create_task_msg = ExecuteMsg::CreateTask { - task: TaskRequest { - interval: Interval::Once, - boundary: None, - stop_on_fail: false, - actions: vec![Action { - msg: transfer_to_bob.clone().into(), - gas_limit: None, - }], - queries: Some(vec![query]), - transforms: None, - cw20_coins: vec![], - }, - }; - - // Base + action + calling rules + non-wasm query - let gas_needed = GAS_BASE_FEE + GAS_ACTION_FEE + GAS_WASM_QUERY_FEE + GAS_QUERY_FEE; - let agent_fee = gas_needed * 5 / 100; - let gas_to_amount = (gas_needed + agent_fee) * GAS_ADJUSTMENT_NUMERATOR_DEFAULT - / GAS_DENOMINATOR - * GAS_NUMERATOR_DEFAULT - / GAS_DENOMINATOR; - let attached_balance = (gas_to_amount + 1) as u128; - - let task_hash_binary = app - .execute_contract( - Addr::unchecked(ADMIN), - contract_addr.clone(), - &create_task_msg, - &coins(attached_balance, NATIVE_DENOM), - ) - .unwrap() - .data - .unwrap(); - let task_hash: String = String::from_utf8(task_hash_binary.to_vec()).unwrap(); - app.update_block(add_little_time); - - let task: TaskResponse = app - .wrap() - .query_wasm_smart( - contract_addr.clone(), - &QueryMsg::GetTask { - task_hash: task_hash.clone(), - }, - ) - .unwrap(); - assert_eq!( - task.amount_for_one_task_native, - coins(attached_balance, NATIVE_DENOM) - ); - - // execute proxy_call - let proxy_call_msg = ExecuteMsg::ProxyCall { - task_hash: Some(task_hash), - }; - let res = app - .execute_contract( - Addr::unchecked(AGENT0), - contract_addr.clone(), - &proxy_call_msg, - &vec![], - ) - .unwrap(); - print!("{:#?}", res); - - assert!(res.events.iter().any(|ev| ev - .attributes - .iter() - .any(|attr| attr.key == "method" && attr.value == "remove_task"))); - - // Wasm query - let wasm_query = CroncatQuery::Query { - contract_addr: contract_addr.to_string(), - msg: to_binary(&QueryMsg::GetAgent { - account_id: AGENT0.to_string(), - }) - .unwrap(), - }; - - let transfer_to_bob = BankMsg::Send { - to_address: "bob".to_string(), - amount: coins(1, NATIVE_DENOM), - }; - - let create_task_msg = ExecuteMsg::CreateTask { + let empty_actions = ExecuteMsg::CreateTask { task: TaskRequest { interval: Interval::Once, boundary: None, stop_on_fail: false, - actions: vec![Action { - msg: transfer_to_bob.clone().into(), - gas_limit: None, - }], - queries: Some(vec![wasm_query.clone()]), - transforms: None, - cw20_coins: vec![], - }, - }; - - // Base + action + calling rules + wasm query - let gas_needed = GAS_BASE_FEE + GAS_ACTION_FEE + GAS_WASM_QUERY_FEE + GAS_WASM_QUERY_FEE; - let agent_fee = gas_needed * 5 / 100; - let gas_to_amount = (gas_needed + agent_fee) * GAS_ADJUSTMENT_NUMERATOR_DEFAULT - / GAS_DENOMINATOR - * GAS_NUMERATOR_DEFAULT - / GAS_DENOMINATOR; - let attached_balance = (gas_to_amount + 1) as u128; - - let task_hash_binary = app - .execute_contract( - Addr::unchecked(ADMIN), - contract_addr.clone(), - &create_task_msg, - &coins(attached_balance, NATIVE_DENOM), - ) - .unwrap() - .data - .unwrap(); - let task_hash: String = String::from_utf8(task_hash_binary.to_vec()).unwrap(); - app.update_block(add_little_time); - - let task: TaskResponse = app - .wrap() - .query_wasm_smart( - contract_addr.clone(), - &QueryMsg::GetTask { - task_hash: task_hash.clone(), - }, - ) - .unwrap(); - assert_eq!( - task.amount_for_one_task_native, - coins(attached_balance, NATIVE_DENOM) - ); - - // execute proxy_call - let proxy_call_msg = ExecuteMsg::ProxyCall { - task_hash: Some(task_hash), - }; - let res = app - .execute_contract( - Addr::unchecked(AGENT0), - contract_addr.clone(), - &proxy_call_msg, - &vec![], - ) - .unwrap(); - print!("{:#?}", res); - - assert!(res.events.iter().any(|ev| ev - .attributes - .iter() - .any(|attr| attr.key == "method" && attr.value == "remove_task"))); - - // With reschedule to check balance changes - - let create_task_msg = ExecuteMsg::CreateTask { - task: TaskRequest { - interval: Interval::Immediate, - boundary: None, - stop_on_fail: false, - actions: vec![Action { - msg: transfer_to_bob.clone().into(), - gas_limit: None, - }], - queries: Some(vec![wasm_query]), - transforms: None, - cw20_coins: vec![], - }, - }; - - let gas_needed = GAS_BASE_FEE + GAS_ACTION_FEE + GAS_WASM_QUERY_FEE + GAS_WASM_QUERY_FEE; - let agent_fee = gas_needed * 5 / 100; - let gas_to_amount = (gas_needed + agent_fee) * GAS_ADJUSTMENT_NUMERATOR_DEFAULT - / GAS_DENOMINATOR - * GAS_NUMERATOR_DEFAULT - / GAS_DENOMINATOR; - let one_proxy_call_amount = (gas_to_amount + 1) as u128; - - let task_hash_binary = app - .execute_contract( - Addr::unchecked(ADMIN), - contract_addr.clone(), - &create_task_msg, - &coins(one_proxy_call_amount * 3, NATIVE_DENOM), - ) - .unwrap() - .data - .unwrap(); - let task_hash: String = String::from_utf8(task_hash_binary.to_vec()).unwrap(); - app.update_block(add_little_time); - - // Initial balance for 3 proxy calls - let task: TaskResponse = app - .wrap() - .query_wasm_smart( - contract_addr.clone(), - &QueryMsg::GetTask { - task_hash: task_hash.clone(), - }, - ) - .unwrap(); - assert_eq!( - task.amount_for_one_task_native, - coins(one_proxy_call_amount, NATIVE_DENOM) - ); - assert_eq!( - task.total_deposit, - coins(one_proxy_call_amount * 3, NATIVE_DENOM) - ); - - // execute proxy_call - let proxy_call_msg = ExecuteMsg::ProxyCall { - task_hash: Some(task_hash.clone()), - }; - app.execute_contract( - Addr::unchecked(AGENT0), - contract_addr.clone(), - &proxy_call_msg, - &vec![], - ) - .unwrap(); - - // for 2 proxies should have left - let task: TaskResponse = app - .wrap() - .query_wasm_smart( - contract_addr.clone(), - &QueryMsg::GetTask { - task_hash: task_hash.to_string(), - }, - ) - .unwrap(); - assert_eq!( - task.total_deposit, - coins(one_proxy_call_amount * 2, NATIVE_DENOM) - ); - - // execute proxy_call - let proxy_call_msg = ExecuteMsg::ProxyCall { - task_hash: Some(task_hash.clone()), - }; - app.execute_contract( - Addr::unchecked(AGENT0), - contract_addr.clone(), - &proxy_call_msg, - &vec![], - ) - .unwrap(); - - // for 2 proxies should have left - let task: TaskResponse = app - .wrap() - .query_wasm_smart( - contract_addr.clone(), - &QueryMsg::GetTask { - task_hash: task_hash.to_string(), - }, - ) - .unwrap(); - assert_eq!( - task.total_deposit, - coins(one_proxy_call_amount, NATIVE_DENOM) - ); - - // execute proxy_call - let proxy_call_msg = ExecuteMsg::ProxyCall { - task_hash: Some(task_hash.clone()), - }; - let res = app - .execute_contract( - Addr::unchecked(AGENT0), - contract_addr.clone(), - &proxy_call_msg, - &vec![], - ) - .unwrap(); - - assert!(res.events.iter().any(|ev| ev - .attributes - .iter() - .any(|attr| attr.key == "method" && attr.value == "remove_task"))); -} - -#[test] -fn queries_fees_negative() { - let (mut app, cw_template_contract, _) = proper_instantiate(); - let contract_addr = cw_template_contract.addr(); - - // quick agent register - let msg = ExecuteMsg::RegisterAgent { - payable_account_id: Some(AGENT_BENEFICIARY.to_string()), - }; - app.execute_contract(Addr::unchecked(AGENT0), contract_addr.clone(), &msg, &[]) - .unwrap(); - - app.update_block(add_little_time); - - // Non-wasm query - let query = CroncatQuery::HasBalanceGte(HasBalanceGte { - address: contract_addr.to_string(), - required_balance: coins(1, NATIVE_DENOM).into(), - }); - - let transfer_to_bob = BankMsg::Send { - to_address: "bob".to_string(), - amount: coins(1, NATIVE_DENOM), - }; - - let create_task_msg = ExecuteMsg::CreateTask { - task: TaskRequest { - interval: Interval::Once, - boundary: None, - stop_on_fail: false, - actions: vec![Action { - msg: transfer_to_bob.clone().into(), - gas_limit: None, - }], - queries: Some(vec![query]), - transforms: None, - cw20_coins: vec![], - }, - }; - - // Base + action + calling rules + non-wasm query - let gas_needed = GAS_BASE_FEE + GAS_ACTION_FEE + GAS_WASM_QUERY_FEE + GAS_QUERY_FEE; - let agent_fee = gas_needed * 5 / 100; - let gas_to_amount = (gas_needed + agent_fee) * GAS_ADJUSTMENT_NUMERATOR_DEFAULT - / GAS_DENOMINATOR - * GAS_NUMERATOR_DEFAULT - / GAS_DENOMINATOR; - let attached_balance = (gas_to_amount + 1 - 1) as u128; // missing 1 amount - - let err: ContractError = app - .execute_contract( - Addr::unchecked(ADMIN), - contract_addr.clone(), - &create_task_msg, - &coins(attached_balance, NATIVE_DENOM), - ) - .unwrap_err() - .downcast() - .unwrap(); - - assert_eq!( - err, - ContractError::CoreError(CoreError::NotEnoughNative { - denom: NATIVE_DENOM.to_string(), - lack: Uint128::new(1), - }) - ); - - // Wasm query - let wasm_query = CroncatQuery::Query { - contract_addr: contract_addr.to_string(), - msg: to_binary(&QueryMsg::GetAgent { - account_id: AGENT0.to_string(), - }) - .unwrap(), - }; - - let transfer_to_bob = BankMsg::Send { - to_address: "bob".to_string(), - amount: coins(1, NATIVE_DENOM), - }; - - let create_task_msg = ExecuteMsg::CreateTask { - task: TaskRequest { - interval: Interval::Once, - boundary: None, - stop_on_fail: false, - actions: vec![Action { - msg: transfer_to_bob.clone().into(), - gas_limit: None, - }], - queries: Some(vec![wasm_query]), - transforms: None, - cw20_coins: vec![], - }, - }; - - // Base + action + calling rules + wasm query - let gas_needed = GAS_BASE_FEE + GAS_ACTION_FEE + GAS_WASM_QUERY_FEE + GAS_WASM_QUERY_FEE; - let agent_fee = gas_needed * 5 / 100; - let gas_to_amount = (gas_needed + agent_fee) * GAS_ADJUSTMENT_NUMERATOR_DEFAULT - / GAS_DENOMINATOR - * GAS_NUMERATOR_DEFAULT - / GAS_DENOMINATOR; - let attached_balance = (gas_to_amount + 1 - 1) as u128; - - let err: ContractError = app - .execute_contract( - Addr::unchecked(ADMIN), - contract_addr.clone(), - &create_task_msg, - &coins(attached_balance, NATIVE_DENOM), - ) - .unwrap_err() - .downcast() - .unwrap(); - - assert_eq!( - err, - ContractError::CoreError(CoreError::NotEnoughNative { - denom: NATIVE_DENOM.to_string(), - lack: Uint128::new(1), - }) - ); -} - -#[test] -fn gas_fees_configurable() { - let (mut app, cw_template_contract, _) = proper_instantiate(); - let contract_addr = cw_template_contract.addr(); - - // quick agent register - let msg = ExecuteMsg::RegisterAgent { - payable_account_id: Some(AGENT_BENEFICIARY.to_string()), - }; - app.execute_contract(Addr::unchecked(AGENT0), contract_addr.clone(), &msg, &[]) - .unwrap(); - - let mut initial_config: GetConfigResponse = app - .wrap() - .query_wasm_smart(contract_addr.clone(), &QueryMsg::GetConfig {}) - .unwrap(); - let modified_gas_price = GasPrice { - numerator: 10, - denominator: 1000, - gas_adjustment_numerator: 120, - }; - let update_gas_price_msg = ExecuteMsg::UpdateSettings { - owner_id: None, - slot_granularity_time: None, - paused: None, - agent_fee: None, - gas_base_fee: None, - gas_action_fee: None, - gas_query_fee: None, - gas_wasm_query_fee: None, - gas_price: Some(modified_gas_price.clone()), - proxy_callback_gas: None, - min_tasks_per_agent: None, - agents_eject_threshold: None, - }; - app.execute_contract( - Addr::unchecked(ADMIN), - contract_addr.clone(), - &update_gas_price_msg, - &[], - ) - .unwrap(); - let new_config: GetConfigResponse = app - .wrap() - .query_wasm_smart(contract_addr.clone(), &QueryMsg::GetConfig {}) - .unwrap(); - assert_ne!(initial_config, new_config); - initial_config.gas_price = modified_gas_price.clone(); - assert_eq!(initial_config, new_config); - - app.update_block(add_little_time); - let transfer_to_bob = BankMsg::Send { - to_address: "bob".to_string(), - amount: coins(1, NATIVE_DENOM), - }; - let create_task_msg = ExecuteMsg::CreateTask { - task: TaskRequest { - interval: Interval::Once, - boundary: None, - stop_on_fail: false, - actions: vec![Action { - msg: transfer_to_bob.clone().into(), - gas_limit: None, - }], + actions: vec![], queries: None, transforms: None, cw20_coins: vec![], }, }; - // Base + action - let gas_needed = GAS_BASE_FEE + GAS_ACTION_FEE; - let agent_fee = gas_needed * 5 / 100; - let gas_to_amount = (gas_needed + agent_fee) * modified_gas_price.gas_adjustment_numerator - / modified_gas_price.denominator - * modified_gas_price.numerator - / modified_gas_price.denominator; - let attached_balance = (gas_to_amount + 1) as u128; + let total_gas = GAS_BASE_FEE_JUNO + GAS_ACTION_FEE_JUNO; + let attach_per_action = (total_gas + (total_gas * 5 / 100)) / GAS_DENOMINATOR_DEFAULT_JUNO; + let amount_for_three = (attach_per_action) as u128; - // making sure new config values is applied - // one off coin - let err: ContractError = app + let res: ContractError = app .execute_contract( Addr::unchecked(ADMIN), contract_addr.clone(), - &create_task_msg, - &coins(attached_balance - 1, NATIVE_DENOM), + &empty_actions, + &coins(amount_for_three, NATIVE_DENOM), ) .unwrap_err() .downcast() .unwrap(); - assert_eq!( - err, - ContractError::CoreError(CoreError::NotEnoughNative { - denom: NATIVE_DENOM.to_owned(), - lack: Uint128::new(1) - }) - ); - - // should work - app.execute_contract( - Addr::unchecked(ADMIN), - contract_addr.clone(), - &create_task_msg, - &coins(attached_balance, NATIVE_DENOM), - ) - .unwrap(); - app.update_block(add_little_time); - - // execute proxy_call - let proxy_call_msg = ExecuteMsg::ProxyCall { task_hash: None }; - let res = app - .execute_contract( - Addr::unchecked(AGENT0), - contract_addr.clone(), - &proxy_call_msg, - &vec![], - ) - .unwrap(); - print!("{:#?}", res); - - assert!(res.events.iter().any(|ev| ev - .attributes - .iter() - .any(|attr| attr.key == "method" && attr.value == "remove_task"))); + assert_eq!(res, ContractError::CoreError(CoreError::InvalidAction {})); } diff --git a/packages/cw-croncat-core/src/tests/types.rs b/packages/cw-croncat-core/src/tests/types.rs index 21be73c9..cb13ea35 100644 --- a/packages/cw-croncat-core/src/tests/types.rs +++ b/packages/cw-croncat-core/src/tests/types.rs @@ -334,6 +334,31 @@ fn is_valid_msg_send_should_success() { .is_ok()); } +#[test] +fn is_valid_empty_actions() { + let task = TaskRequest { + interval: Interval::Block(10), + boundary: None, + stop_on_fail: false, + actions: vec![], + queries: None, + transforms: None, + cw20_coins: Default::default(), + }; + assert_eq!( + task.is_valid_msg_calculate_usage( + &mock_dependencies().api, + &Addr::unchecked("alice2"), + &Addr::unchecked("bob"), + &Addr::unchecked("bob"), + 5, + 5, + ) + .unwrap_err(), + CoreError::InvalidAction {} + ); +} + #[test] fn test_add_tokens() { let mut coins: GenericBalance = GenericBalance::default(); diff --git a/packages/cw-croncat-core/src/types.rs b/packages/cw-croncat-core/src/types.rs index 22cb8801..6691a733 100644 --- a/packages/cw-croncat-core/src/types.rs +++ b/packages/cw-croncat-core/src/types.rs @@ -243,6 +243,9 @@ impl TaskRequest { let mut gas_amount: u64 = base_gas; let mut amount_for_one_task = GenericBalance::default(); + if self.actions.is_empty() { + return Err(CoreError::InvalidAction {}); + } for action in self.actions.iter() { // checked for cases, where task creator intentionaly tries to overflow gas_amount = gas_amount