diff --git a/src/lib.rs b/src/lib.rs index 426097b..83f9454 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,8 +7,8 @@ use near_sdk::serde::{Deserialize, Serialize}; use near_sdk::serde_json::{json, Value}; use near_sdk::store::IterableMap; use near_sdk::{ - env, ext_contract, near, require, AccountId, Gas, NearToken, PanicOnDefault, Promise, - PromiseResult, PublicKey, + assert_one_yocto, env, ext_contract, near, require, AccountId, Gas, NearToken, PanicOnDefault, + Promise, PromiseResult, PublicKey, }; use crate::event::Event; @@ -116,7 +116,9 @@ impl AuroraControllerFactory { /// Attaches new full access key to the controller contract. #[access_control_any(roles(Role::DAO))] + #[payable] pub fn attach_full_access_key(&mut self, public_key: PublicKey) -> Promise { + assert_one_yocto(); event::emit( Event::AttachFullAccessKey, &json!({"public_key": &public_key}), @@ -126,11 +128,16 @@ impl AuroraControllerFactory { /// Delegates an execution of actions to the specified receiver. #[access_control_any(roles(Role::DAO))] + #[payable] pub fn delegate_execution( - &self, + &mut self, receiver_id: AccountId, actions: Vec, ) -> Promise { + require!( + !env::attached_deposit().is_zero(), + "required at least 1 yoctonear", + ); event::emit( Event::DelegatedExecution, &json!({ @@ -138,9 +145,13 @@ impl AuroraControllerFactory { "actions": &actions }), ); + let mut total = env::attached_deposit(); actions .into_iter() .fold(Promise::new(receiver_id), |promise, action| { + total = total + .checked_sub(action.amount) + .unwrap_or_else(|| env::panic_str("not enough deposit attached")); promise.function_call( action.function_name, action.arguments.into(), @@ -152,11 +163,13 @@ impl AuroraControllerFactory { /// Pauses the contract with provided account id. #[access_control_any(roles(Role::DAO, Role::Pauser))] + #[payable] pub fn delegate_pause( - &self, + &mut self, receiver_id: AccountId, pause_method_name: Option, ) -> Promise { + assert_one_yocto(); let function_name = match pause_method_name { Some(method) if ALLOWED_PAUSE_METHODS.contains(&method.as_str()) => method, Some(method) => panic!("pause method: {method} is not allowed"), @@ -177,6 +190,7 @@ impl AuroraControllerFactory { /// Adds new contract release info. #[access_control_any(roles(Role::DAO))] + #[payable] pub fn add_release_info( &mut self, hash: String, @@ -185,6 +199,7 @@ impl AuroraControllerFactory { downgrade_hash: Option, description: Option, ) { + assert_one_yocto(); require!( self.releases.get(&hash).is_none(), "release info for the hash is already exist" @@ -207,7 +222,9 @@ impl AuroraControllerFactory { } /// Adds bytes of the contract smart contract to the corresponding release info. + #[payable] pub fn add_release_blob(&mut self) { + assert_one_yocto(); let blob = env::input().unwrap_or_else(|| panic!("no blob's bytes were provided")); let hash = utils::hash_256(&blob); let release_info = self.releases.get_mut(&hash).unwrap_or_else(|| { @@ -222,7 +239,9 @@ impl AuroraControllerFactory { /// Marks the release with the hash: `hash` as latest. #[access_control_any(roles(Role::DAO, Role::Releaser))] + #[payable] pub fn set_latest_release(&mut self, hash: &String) { + assert_one_yocto(); let new_latest = self.releases.get(hash).unwrap_or_else(|| { panic!("release info doesn't exist for hash: {hash}"); }); @@ -240,7 +259,9 @@ impl AuroraControllerFactory { /// Removes the release info for hash: `hash`. #[access_control_any(roles(Role::DAO))] + #[payable] pub fn remove_release(&mut self, hash: &String) { + assert_one_yocto(); let release_info = self.releases.remove(hash).unwrap_or_else(|| { panic!("release info doesn't exist for hash: {hash}"); }); @@ -281,13 +302,18 @@ impl AuroraControllerFactory { /// Deploys a new contract on the release info that corresponds to the provided hash. #[access_control_any(roles(Role::DAO, Role::Deployer))] + #[payable] pub fn deploy( - &self, + &mut self, new_contract_id: AccountId, init_method: String, init_args: Value, blob_hash: Option, ) -> Promise { + require!( + !env::attached_deposit().is_zero(), + "required at least 1 yoctonear" + ); // Check that the `new_contract_id` wasn't used for another contract before. require!( self.deployments.get(&new_contract_id).is_none(), @@ -342,7 +368,9 @@ impl AuroraControllerFactory { /// Adds new deployment info of previously deployed contract. /// E.g. the contract which has been deployed by not this controller contract. #[access_control_any(roles(Role::DAO))] + #[payable] pub fn add_deployment_info(&mut self, contract_id: AccountId, deployment_info: DeploymentInfo) { + assert_one_yocto(); event::emit( Event::AddDeploymentInfo, &json!({"contract_id": contract_id, "deployment_info": deployment_info}), @@ -376,12 +404,15 @@ impl AuroraControllerFactory { /// Upgrades a contract with account id and provided or the latest hash. #[access_control_any(roles(Role::DAO, Role::Updater))] + #[payable] pub fn upgrade( &mut self, contract_id: AccountId, hash: Option, state_migration_gas: Option, ) -> Promise { + assert_one_yocto(); + self.upgrade_internal( contract_id, hash, @@ -393,12 +424,14 @@ impl AuroraControllerFactory { /// Upgrades a contract with account id and provided hash without checking version. #[access_control_any(roles(Role::DAO))] + #[payable] pub fn unrestricted_upgrade( &mut self, contract_id: AccountId, hash: String, state_migration_gas: Option, ) -> Promise { + assert_one_yocto(); self.upgrade_internal( contract_id, Some(hash), @@ -410,7 +443,9 @@ impl AuroraControllerFactory { /// Downgrades the contract with account id. #[access_control_any(roles(Role::DAO))] + #[payable] pub fn downgrade(&mut self, contract_id: AccountId) -> Promise { + assert_one_yocto(); let mut deployment_info = self.deployments .get(&contract_id) @@ -517,6 +552,7 @@ impl AuroraControllerFactory { UPGRADE_GAS.saturating_add(Gas::from_gas(gas)) }), ) + .with_attached_deposit(NearToken::from_yoctonear(1)) .upgrade(args.code, args.state_migration_gas) .then( Self::ext(env::current_account_id()) @@ -528,6 +564,7 @@ impl AuroraControllerFactory { #[ext_contract(ext_aurora)] pub trait ExtAurora { + /// Requires 1yN attached for security purposes fn upgrade( &mut self, #[serializer(borsh)] code: Vec, diff --git a/src/tests/sdk/mod.rs b/src/tests/sdk/mod.rs index 993201b..321f188 100644 --- a/src/tests/sdk/mod.rs +++ b/src/tests/sdk/mod.rs @@ -1,4 +1,4 @@ -use near_sdk::AccountId; +use near_sdk::{AccountId, NearToken}; use crate::types::ReleaseInfo; use crate::AuroraControllerFactory; @@ -18,6 +18,7 @@ fn test_adding_blob() { set_env!( predecessor_account_id: predecessor_account_id(), input: vec![1; 256], + attached_deposit: NearToken::from_yoctonear(1), ); let mut contract = AuroraControllerFactory::new(dao()); @@ -45,6 +46,7 @@ fn test_adding_blob() { set_env!( predecessor_account_id: predecessor_account_id(), input: vec![2; 256], + attached_deposit: NearToken::from_yoctonear(1), ); contract.add_release_info( @@ -87,6 +89,7 @@ fn test_adding_blob_without_adding_hash() { set_env!( predecessor_account_id: predecessor_account_id(), input: vec![1; 256], + attached_deposit: NearToken::from_yoctonear(1), ); let mut contract = AuroraControllerFactory::new(dao()); @@ -98,6 +101,7 @@ fn test_check_latest_release() { set_env!( predecessor_account_id: predecessor_account_id(), input: vec![1; 256], + attached_deposit: NearToken::from_yoctonear(1), ); let mut contract = AuroraControllerFactory::new(dao()); @@ -121,6 +125,7 @@ fn test_check_latest_release() { set_env!( predecessor_account_id: predecessor_account_id(), input: vec![2; 256], + attached_deposit: NearToken::from_yoctonear(1), ); contract.add_release_info( @@ -162,6 +167,7 @@ fn test_check_latest_release_blob_without_adding() { fn test_set_latest_with_lower_version() { set_env!( predecessor_account_id: predecessor_account_id(), + attached_deposit: NearToken::from_yoctonear(1), ); let mut contract = AuroraControllerFactory::new(dao()); @@ -190,6 +196,7 @@ fn test_set_latest_with_lower_version() { fn test_add_latest_with_lower_version() { set_env!( predecessor_account_id: predecessor_account_id(), + attached_deposit: NearToken::from_yoctonear(1), ); let mut contract = AuroraControllerFactory::new(dao()); @@ -216,9 +223,10 @@ fn test_add_latest_with_lower_version() { fn test_use_not_allowed_pause_method() { set_env!( predecessor_account_id: predecessor_account_id(), + attached_deposit: NearToken::from_yoctonear(1), ); - let contract = AuroraControllerFactory::new(dao()); + let mut contract = AuroraControllerFactory::new(dao()); contract.delegate_pause(new_engine(), Some("some_pause_method".to_string())); } diff --git a/src/tests/workspace/delegate.rs b/src/tests/workspace/delegate.rs index e700152..e814847 100644 --- a/src/tests/workspace/delegate.rs +++ b/src/tests/workspace/delegate.rs @@ -15,6 +15,7 @@ async fn test_delegate_execution() { let result = factory_owner .call(factory.id(), "delegate_execution") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "receiver_id": &contract_id, "actions": vec![FunctionCallArgs { @@ -51,6 +52,7 @@ async fn test_delegate_pause() { let result = factory_owner .call(factory.id(), "delegate_pause") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "receiver_id": &contract_id, "pause_method_name": "pause_contract" @@ -80,6 +82,7 @@ async fn create_factory() -> (Account, Contract, AccountId) { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_6_4, "version": "3.6.4", @@ -93,6 +96,7 @@ async fn create_factory() -> (Account, Contract, AccountId) { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_6_4.to_vec()) .max_gas() .transact() diff --git a/src/tests/workspace/deploy.rs b/src/tests/workspace/deploy.rs index d4782f4..f5b6167 100644 --- a/src/tests/workspace/deploy.rs +++ b/src/tests/workspace/deploy.rs @@ -20,6 +20,7 @@ async fn test_deploy_contract() { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_6_4, "version": "3.6.4", @@ -33,6 +34,7 @@ async fn test_deploy_contract() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_6_4.to_vec()) .max_gas() .transact() @@ -89,6 +91,7 @@ async fn test_deploy_more_than_one_contract() { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_6_4, "version": "3.6.4", @@ -102,6 +105,7 @@ async fn test_deploy_more_than_one_contract() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_6_4.to_vec()) .max_gas() .transact() @@ -139,6 +143,7 @@ async fn test_deploy_more_than_one_contract() { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_7_0, "version": "3.7.0", @@ -152,6 +157,7 @@ async fn test_deploy_more_than_one_contract() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_7_0.to_vec()) .max_gas() .transact() @@ -265,6 +271,7 @@ async fn test_add_deployment_info_for_existed_contract() { let result = factory_owner .call(factory.id(), "add_deployment_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "contract_id": silo_contract.id(), "deployment_info": &deployment_info @@ -277,6 +284,7 @@ async fn test_add_deployment_info_for_existed_contract() { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_7_0, "version": "3.7.0", @@ -290,6 +298,7 @@ async fn test_add_deployment_info_for_existed_contract() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_7_0.to_vec()) .max_gas() .transact() @@ -299,6 +308,7 @@ async fn test_add_deployment_info_for_existed_contract() { let result = factory_owner .call(factory.id(), "upgrade") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "contract_id": silo_contract.id(), "state_migration_gas": MIGRATION_GAS diff --git a/src/tests/workspace/downgrade.rs b/src/tests/workspace/downgrade.rs index d3f2eaf..0404883 100644 --- a/src/tests/workspace/downgrade.rs +++ b/src/tests/workspace/downgrade.rs @@ -12,6 +12,7 @@ async fn test_downgrade_contract() { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_6_4, "version": "3.6.4", @@ -25,6 +26,7 @@ async fn test_downgrade_contract() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_6_4.to_vec()) .max_gas() .transact() @@ -34,6 +36,7 @@ async fn test_downgrade_contract() { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_7_0, "version": "3.7.0", @@ -47,6 +50,7 @@ async fn test_downgrade_contract() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_7_0.to_vec()) .max_gas() .transact() @@ -87,6 +91,7 @@ async fn test_downgrade_contract() { let result = factory_owner .call(factory.id(), "downgrade") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "contract_id": new_contract_id.clone(), })) diff --git a/src/tests/workspace/release.rs b/src/tests/workspace/release.rs index 022dd74..21174b7 100644 --- a/src/tests/workspace/release.rs +++ b/src/tests/workspace/release.rs @@ -1,4 +1,4 @@ -use near_sdk::serde_json::json; +use near_sdk::{serde_json::json, NearToken}; use super::utils; use crate::types::ReleaseInfo; @@ -9,6 +9,7 @@ async fn test_add_new_release() { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": "f5c22e35d04167e37913e7963ce033b1f3d17a924a4e6fe5fc95af1224051921", "version": "1.0.1", @@ -22,6 +23,7 @@ async fn test_add_new_release() { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": "2661920f2409dd6c8adeb0c44972959f232b6429afa913845d0fd95e7e768234", "version": "1.0.0", diff --git a/src/tests/workspace/upgrade.rs b/src/tests/workspace/upgrade.rs index bbd0515..bccb45e 100644 --- a/src/tests/workspace/upgrade.rs +++ b/src/tests/workspace/upgrade.rs @@ -11,6 +11,7 @@ async fn test_upgrade_contract() { let (factory_owner, factory, _) = utils::crate_factory().await.unwrap(); let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_6_4, "version": "3.6.4", @@ -24,6 +25,7 @@ async fn test_upgrade_contract() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_6_4.to_vec()) .max_gas() .transact() @@ -54,6 +56,7 @@ async fn test_upgrade_contract() { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_7_0, "version": "3.7.0", @@ -67,6 +70,7 @@ async fn test_upgrade_contract() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_7_0.to_vec()) .max_gas() .transact() @@ -76,6 +80,7 @@ async fn test_upgrade_contract() { let result = factory_owner .call(factory.id(), "upgrade") + .deposit(NearToken::from_yoctonear(1)) .args_json((&new_contract_id, HASH_3_7_0, MIGRATION_GAS)) .max_gas() .transact() @@ -93,6 +98,7 @@ async fn test_upgrade_contract_to_previous_version() { let (factory_owner, factory, _) = utils::crate_factory().await.unwrap(); let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_7_0, "version": "3.7.0", @@ -106,6 +112,7 @@ async fn test_upgrade_contract_to_previous_version() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_7_0.to_vec()) .max_gas() .transact() @@ -136,6 +143,7 @@ async fn test_upgrade_contract_to_previous_version() { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_6_4, "version": "3.6.4", @@ -149,6 +157,7 @@ async fn test_upgrade_contract_to_previous_version() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_6_4.to_vec()) .max_gas() .transact() @@ -158,6 +167,7 @@ async fn test_upgrade_contract_to_previous_version() { let result = factory_owner .call(factory.id(), "upgrade") + .deposit(NearToken::from_yoctonear(1)) .args_json((&new_contract_id, HASH_3_6_4, MIGRATION_GAS)) .max_gas() .transact() @@ -176,6 +186,7 @@ async fn test_unrestricted_upgrade_contract() { let (factory_owner, factory, _) = utils::crate_factory().await.unwrap(); let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_7_0, "version": "3.7.0", @@ -189,6 +200,7 @@ async fn test_unrestricted_upgrade_contract() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_7_0.to_vec()) .max_gas() .transact() @@ -219,6 +231,7 @@ async fn test_unrestricted_upgrade_contract() { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_6_4, "version": "3.6.4", @@ -232,6 +245,7 @@ async fn test_unrestricted_upgrade_contract() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_6_4.to_vec()) .max_gas() .transact() @@ -241,6 +255,7 @@ async fn test_unrestricted_upgrade_contract() { let result = factory_owner .call(factory.id(), "unrestricted_upgrade") + .deposit(NearToken::from_yoctonear(1)) .args_json((&new_contract_id, HASH_3_6_4, MIGRATION_GAS)) .max_gas() .transact() @@ -258,6 +273,7 @@ async fn test_upgrade_contract_with_small_gas_for_migration() { let (factory_owner, factory, _) = utils::crate_factory().await.unwrap(); let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_6_4, "version": "3.6.4", @@ -271,6 +287,7 @@ async fn test_upgrade_contract_with_small_gas_for_migration() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_6_4.to_vec()) .max_gas() .transact() @@ -308,6 +325,7 @@ async fn test_upgrade_contract_with_small_gas_for_migration() { let result = factory_owner .call(factory.id(), "add_release_info") + .deposit(NearToken::from_yoctonear(1)) .args_json(json!({ "hash": HASH_3_7_0, "version": "3.7.0", @@ -321,6 +339,7 @@ async fn test_upgrade_contract_with_small_gas_for_migration() { let result = factory_owner .call(factory.id(), "add_release_blob") + .deposit(NearToken::from_yoctonear(1)) .args(BLOB_3_7_0.to_vec()) .max_gas() .transact() @@ -330,6 +349,7 @@ async fn test_upgrade_contract_with_small_gas_for_migration() { let result = factory_owner .call(factory.id(), "upgrade") + .deposit(NearToken::from_yoctonear(1)) .args_json(( &new_contract_id, HASH_3_7_0,