Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proxy deployer contract #37

Merged
merged 19 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ members = [
"contracts/paymaster/meta",
"contracts/ping-pong-egld",
"contracts/ping-pong-egld/meta",
"contracts/proxy-deployer",
"contracts/proxy-deployer/meta",
"contracts/proxy-pause",
"contracts/proxy-pause/meta",
"contracts/rewards-distribution",
Expand Down
7 changes: 7 additions & 0 deletions contracts/proxy-deployer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Generated by Cargo
# will have compiled files and executables
/target/
*/target/

# The erdpy output
output*
22 changes: 22 additions & 0 deletions contracts/proxy-deployer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "proxy-deployer"
version = "0.0.0"
authors = ["MultiversX <[email protected]>"]
edition = "2021"
publish = false

[lib]
path = "src/lib.rs"

[dependencies.multiversx-sc]
version = "=0.45.2"
features = ["esdt-token-payment-legacy-decode"]

[dev-dependencies.multiversx-sc-scenario]
version = "=0.45.2"

[dependencies.multiversx-sc-modules]
version = "0.45.2"

[dev-dependencies.adder]
path = "../adder"
13 changes: 13 additions & 0 deletions contracts/proxy-deployer/meta/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "proxy-deployer-meta"
version = "0.0.0"
edition = "2021"
publish = false
authors = ["MultiversX <[email protected]>"]

[dev-dependencies]
[dependencies.proxy-deployer]
path = ".."

[dependencies.multiversx-sc-meta]
version = "0.45.2"
3 changes: 3 additions & 0 deletions contracts/proxy-deployer/meta/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
multiversx_sc_meta::cli_main::<proxy_deployer::AbiProvider>();
}
3 changes: 3 additions & 0 deletions contracts/proxy-deployer/multiversx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"language": "rust"
}
117 changes: 117 additions & 0 deletions contracts/proxy-deployer/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
multiversx_sc::imports!();
multiversx_sc::derive_imports!();

#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, TypeAbi)]
pub struct OngoingUpgradeOperation<M: ManagedTypeApi> {
pub template_address: ManagedAddress<M>,
pub arguments: ManagedArgBuffer<M>,
pub contracts_remaining: ManagedVec<M, ManagedAddress<M>>,
}

impl<M: ManagedTypeApi> OngoingUpgradeOperation<M> {
#[inline]
pub fn new(
template_address: ManagedAddress<M>,
arguments: ManagedArgBuffer<M>,
contracts_remaining: ManagedVec<M, ManagedAddress<M>>,
) -> Self {
OngoingUpgradeOperation {
template_address,
arguments,
contracts_remaining,
}
}
}

#[multiversx_sc::module]
pub trait ConfigModule {
#[only_owner]
#[endpoint(addDeployerToBlacklist)]
fn add_deployer_to_blacklist(&self, blacklisted_address: ManagedAddress) {
require!(
self.deployers_list().contains(&blacklisted_address),
psorinionut marked this conversation as resolved.
Show resolved Hide resolved
"The address is not a deployer"
);
require!(
!self
.blacklisted_deployers_list()
.contains(&blacklisted_address),
"Address already blacklisted"
);
self.blacklisted_deployers_list()
.insert(blacklisted_address);
}

#[only_owner]
#[endpoint(removeDeployerFromBlacklist)]
fn remove_deployer_from_blacklist(&self, address: ManagedAddress) {
require!(
self.blacklisted_deployers_list().contains(&address),
"Address is not blacklisted"
);

self.blacklisted_deployers_list().swap_remove(&address);
}

#[only_owner]
#[endpoint(setDefaultGasForSaveOperation)]
fn set_default_gas_for_save_operation(&self, default_gas_for_save_operation: u64) {
self.default_gas_for_save_operation()
.set(default_gas_for_save_operation);
}

#[view(getDeployerContractsByTemplate)]
fn get_deployer_contracts_by_template(
&self,
user: ManagedAddress,
template_address: ManagedAddress,
) -> ManagedVec<ManagedAddress> {
let opt_deployer_template_addresses = self
.deployer_template_addresses(&user)
.get(&template_address);
opt_deployer_template_addresses.unwrap_or_default()
}

#[view(getAllDeployerContracts)]
fn get_all_deployer_contracts(&self, user: ManagedAddress) -> ManagedVec<ManagedAddress> {
let mut deployed_addresses = ManagedVec::new();
for value in self.deployer_template_addresses(&user).values() {
deployed_addresses.append_vec(value)
}

deployed_addresses
}

#[view(getAllDeployedContractsByTemplate)]
#[storage_mapper("deployedContractsByTemplate")]
fn deployed_contracts_list_by_template(
&self,
template_address: &ManagedAddress,
) -> SingleValueMapper<ManagedVec<ManagedAddress>>;

#[view(getOngoingUpgradeOperations)]
#[storage_mapper("ongoingUpgradeOperation")]
fn ongoing_upgrade_operation(&self) -> SingleValueMapper<OngoingUpgradeOperation<Self::Api>>;

#[view(getDefaultGasForSaveOperation)]
#[storage_mapper("defaultGasForSaveOperation")]
fn default_gas_for_save_operation(&self) -> SingleValueMapper<u64>;

#[view(getAllDeployers)]
#[storage_mapper("deployersList")]
fn deployers_list(&self) -> UnorderedSetMapper<ManagedAddress>;

#[storage_mapper("deployerContracts")]
fn deployer_contracts(&self, user: &ManagedAddress) -> WhitelistMapper<ManagedAddress>;

// (K, V) - (TemplateAddress, Vec<DeployedAddress>)
#[storage_mapper("deployerTemplateAddresses")]
fn deployer_template_addresses(
&self,
deployer_address: &ManagedAddress,
) -> MapMapper<ManagedAddress, ManagedVec<ManagedAddress>>;

#[view(getAllBlacklistedDeployers)]
#[storage_mapper("blacklistedDeployersList")]
fn blacklisted_deployers_list(&self) -> UnorderedSetMapper<ManagedAddress>;
}
206 changes: 206 additions & 0 deletions contracts/proxy-deployer/src/contract_interactions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
multiversx_sc::imports!();

use multiversx_sc_modules::pause;

use crate::config::{self, OngoingUpgradeOperation};

#[multiversx_sc::module]
pub trait ContractInteractionsModule: config::ConfigModule + pause::PauseModule {
#[endpoint(contractDeploy)]
fn contract_deploy(
&self,
template_address: ManagedAddress,
args: MultiValueEncoded<ManagedBuffer>,
) -> ManagedAddress {
self.can_call_endpoint(None);
require!(
self.blockchain().is_smart_contract(&template_address),
"Template address is not a SC"
);

let (new_contract_address, _) = self.send_raw().deploy_from_source_contract(
self.blockchain().get_gas_left(),
&BigUint::zero(),
&template_address,
CodeMetadata::DEFAULT,
&args.to_arg_buffer(),
);

let caller = self.blockchain().get_caller();
let mut deployed_addresses = match self
.deployer_template_addresses(&caller)
.get(&template_address)
{
Some(addresses) => addresses,
None => ManagedVec::new(),
};
deployed_addresses.push(new_contract_address.clone());

self.deployer_contracts(&caller).add(&new_contract_address);
self.deployed_contracts_list_by_template(&template_address)
.update(|deployed_contracts| {
deployed_contracts.push(new_contract_address.clone());
});
self.deployer_template_addresses(&caller)
.insert(template_address, deployed_addresses);
self.deployers_list().insert(caller);

new_contract_address
}

#[endpoint(contractUpgrade)]
fn contract_upgrade(
&self,
contract_address: ManagedAddress,
template_address: ManagedAddress,
args: MultiValueEncoded<ManagedBuffer>,
) {
self.can_call_endpoint(Some(contract_address.clone()));
require!(
self.blockchain().is_smart_contract(&contract_address),
"Contract address is not a SC"
);
require!(
self.blockchain().is_smart_contract(&template_address),
psorinionut marked this conversation as resolved.
Show resolved Hide resolved
"Template address is not a SC"
);

self.send_raw().upgrade_from_source_contract(
&contract_address,
self.blockchain().get_gas_left(),
&BigUint::zero(),
&template_address,
CodeMetadata::DEFAULT,
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
&args.to_arg_buffer(),
);
}

#[endpoint(contractCallByAddress)]
fn contract_call_by_address(
&self,
contract_address: ManagedAddress,
function_name: ManagedBuffer,
args: MultiValueEncoded<ManagedBuffer>,
) {
self.can_call_endpoint(Some(contract_address.clone()));
require!(
self.blockchain().is_smart_contract(&contract_address),
"Contract address is not a SC"
);

self.send()
.contract_call::<()>(contract_address, function_name)
.with_gas_limit(self.blockchain().get_gas_left())
.with_raw_arguments(args.to_arg_buffer())
.execute_on_dest_context()
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
}

#[only_owner]
psorinionut marked this conversation as resolved.
Show resolved Hide resolved
#[allow_multiple_var_args]
#[endpoint(upgradeContractsByTemplate)]
fn upgrade_contracts_by_template(
&self,
gas_per_action: u64,
opt_template_address: OptionalValue<ManagedAddress>,
opt_args: OptionalValue<MultiValueEncoded<ManagedBuffer>>,
) -> bool {
let mut ongoing_upgrade_operation =
psorinionut marked this conversation as resolved.
Show resolved Hide resolved
self.get_ongoing_operation(opt_template_address, opt_args);

let default_gas_for_save = self.default_gas_for_save_operation().get();
while self.blockchain().get_gas_left() >= gas_per_action + default_gas_for_save
&& !ongoing_upgrade_operation.contracts_remaining.is_empty()
psorinionut marked this conversation as resolved.
Show resolved Hide resolved
{
let contract_address = ongoing_upgrade_operation
.contracts_remaining
.get(0)
.clone_value();
self.send_raw().upgrade_from_source_contract(
&contract_address,
gas_per_action,
&BigUint::zero(),
&ongoing_upgrade_operation.template_address,
CodeMetadata::DEFAULT,
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
&ongoing_upgrade_operation.arguments,
);
ongoing_upgrade_operation.contracts_remaining.remove(0);
}
if !ongoing_upgrade_operation.contracts_remaining.is_empty() {
self.ongoing_upgrade_operation()
.set(ongoing_upgrade_operation);
return false;
}

self.ongoing_upgrade_operation().clear();
true
}

fn get_ongoing_operation(
&self,
opt_template_address: OptionalValue<ManagedAddress>,
opt_args: OptionalValue<MultiValueEncoded<ManagedBuffer>>,
) -> OngoingUpgradeOperation<Self::Api> {
let ongoing_operation_mapper = self.ongoing_upgrade_operation();
if opt_template_address.is_none() {
require!(
!ongoing_operation_mapper.is_empty(),
"There is no operation ongoing"
);
return ongoing_operation_mapper.get();
}

require!(
ongoing_operation_mapper.is_empty(),
"Another operation is currently ongoing"
);
let template_address = opt_template_address
.into_option()
.unwrap_or_else(|| sc_panic!("Error decoding the template address"));
require!(
self.blockchain().is_smart_contract(&template_address),
"Template address is not a SC"
);
let contracts_by_template = self
.deployed_contracts_list_by_template(&template_address)
.get();
require!(
!contracts_by_template.is_empty(),
"No contracts deployed with this template"
);
let args = match opt_args.into_option() {
Some(args) => args,
None => MultiValueEncoded::new(),
};

OngoingUpgradeOperation::new(
template_address,
args.to_arg_buffer(),
contracts_by_template,
)
}

fn can_call_endpoint(&self, opt_contract_address: Option<ManagedAddress>) {
let caller = self.blockchain().get_caller();
let owner = self.blockchain().get_owner_address();

if caller == owner {
return;
}

self.require_not_paused();
require!(
!self.blacklisted_deployers_list().contains(&caller),
"User is blacklisted"
);

if opt_contract_address.is_none() {
return;
}
let contract_address =
opt_contract_address.unwrap_or_else(|| sc_panic!("Cannot unwrap the contract address"));
require!(
self.deployer_contracts(&caller).contains(&contract_address),
"Only the deployer can call this function"
);
}
}
Loading
Loading