Skip to content

Commit

Permalink
proxy deployer contract
Browse files Browse the repository at this point in the history
  • Loading branch information
psorinionut committed Nov 3, 2023
1 parent de783fc commit 5967c88
Show file tree
Hide file tree
Showing 13 changed files with 668 additions and 0 deletions.
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*
16 changes: 16 additions & 0 deletions contracts/proxy-deployer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[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.43.4"
features = ["esdt-token-payment-legacy-decode"]

[dev-dependencies.multiversx-sc-scenario]
version = "=0.43.4"
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.43.4"
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"
}
192 changes: 192 additions & 0 deletions contracts/proxy-deployer/src/address_to_id_mapper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
use core::marker::PhantomData;

use multiversx_sc::{
api::StorageMapperApi,
storage::{storage_get_from_address, storage_get_len_from_address, StorageKey},
storage_clear, storage_get, storage_get_len, storage_set,
};

multiversx_sc::imports!();

static ID_SUFFIX: &[u8] = b"userId";
static ADDRESS_SUFFIX: &[u8] = b"addr";
static LAST_ID_SUFFIX: &[u8] = b"lastId";

static UNKNOW_ADDR_ERR_MSG: &[u8] = b"Unknown address";

pub type AddressId = u64;
pub const NULL_ID: AddressId = 0;

pub struct AddressToIdMapper<SA>
where
SA: StorageMapperApi,
{
_phantom_api: PhantomData<SA>,
base_key: StorageKey<SA>,
}

impl<SA> StorageMapper<SA> for AddressToIdMapper<SA>
where
SA: StorageMapperApi,
{
fn new(base_key: StorageKey<SA>) -> Self {
AddressToIdMapper {
_phantom_api: PhantomData,
base_key,
}
}
}

impl<SA> AddressToIdMapper<SA>
where
SA: StorageMapperApi,
{
pub fn contains_id(&self, id: AddressId) -> bool {
let key = self.id_to_address_key(id);
storage_get_len(key.as_ref()) != 0
}

pub fn get_id(&self, address: &ManagedAddress<SA>) -> AddressId {
let key = self.address_to_id_key(address);
storage_get(key.as_ref())
}

pub fn get_id_at_address(
&self,
sc_address: &ManagedAddress<SA>,
address_to_find: &ManagedAddress<SA>,
) -> AddressId {
let key = self.address_to_id_key(address_to_find);
storage_get_from_address(sc_address.as_ref(), key.as_ref())
}

pub fn get_id_non_zero(&self, address: &ManagedAddress<SA>) -> AddressId {
let id = self.get_id(address);
if id == NULL_ID {
SA::error_api_impl().signal_error(UNKNOW_ADDR_ERR_MSG);
}

id
}

pub fn get_id_at_address_non_zero(
&self,
sc_address: &ManagedAddress<SA>,
address_to_find: &ManagedAddress<SA>,
) -> AddressId {
let id = self.get_id_at_address(sc_address, address_to_find);
if id == NULL_ID {
SA::error_api_impl().signal_error(UNKNOW_ADDR_ERR_MSG);
}

id
}

pub fn insert_new(&self, address: &ManagedAddress<SA>) -> AddressId {
let existing_id = self.get_id(address);
if existing_id != NULL_ID {
SA::error_api_impl().signal_error(b"Address already registered");
}

self.insert_address(address)
}

pub fn get_address(&self, id: AddressId) -> Option<ManagedAddress<SA>> {
let key = self.id_to_address_key(id);
if storage_get_len(key.as_ref()) == 0 {
return None;
}

let addr = storage_get(key.as_ref());
Some(addr)
}

pub fn get_address_at_address(
&self,
sc_address: &ManagedAddress<SA>,
id: AddressId,
) -> Option<ManagedAddress<SA>> {
let key = self.id_to_address_key(id);
if storage_get_len_from_address(sc_address.as_ref(), key.as_ref()) == 0 {
return None;
}

let addr = storage_get_from_address(sc_address.as_ref(), key.as_ref());
Some(addr)
}

pub fn get_id_or_insert(&self, address: &ManagedAddress<SA>) -> AddressId {
let current_id = storage_get(self.address_to_id_key(address).as_ref());
if current_id != 0 {
return current_id;
}

self.insert_address(address)
}

pub fn remove_by_id(&self, id: AddressId) -> Option<ManagedAddress<SA>> {
let address = self.get_address(id)?;
self.remove_entry(id, &address);

Some(address)
}

pub fn remove_by_address(&self, address: &ManagedAddress<SA>) -> AddressId {
let current_id = self.get_id(address);
if current_id != NULL_ID {
self.remove_entry(current_id, address);
}

current_id
}

fn insert_address(&self, address: &ManagedAddress<SA>) -> AddressId {
let new_id = self.get_last_id() + 1;
storage_set(self.address_to_id_key(address).as_ref(), &new_id);
storage_set(self.id_to_address_key(new_id).as_ref(), address);

self.set_last_id(new_id);

new_id
}

fn remove_entry(&self, id: AddressId, address: &ManagedAddress<SA>) {
storage_clear(self.address_to_id_key(address).as_ref());
storage_clear(self.id_to_address_key(id).as_ref());
}

fn id_to_address_key(&self, id: AddressId) -> StorageKey<SA> {
let mut item_key = self.base_key.clone();
item_key.append_bytes(ID_SUFFIX);
item_key.append_item(&id);

item_key
}

fn address_to_id_key(&self, address: &ManagedAddress<SA>) -> StorageKey<SA> {
let mut item_key = self.base_key.clone();
item_key.append_bytes(ADDRESS_SUFFIX);
item_key.append_item(address);

item_key
}

fn last_id_key(&self) -> StorageKey<SA> {
let mut item_key = self.base_key.clone();
item_key.append_bytes(LAST_ID_SUFFIX);

item_key
}

pub fn get_last_id(&self) -> AddressId {
storage_get(self.last_id_key().as_ref())
}

fn set_last_id(&self, last_id: AddressId) {
if last_id == 0 {
SA::error_api_impl().signal_error(b"ID Overflow");
}

storage_set(self.last_id_key().as_ref(), &last_id);
}
}
42 changes: 42 additions & 0 deletions contracts/proxy-deployer/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
multiversx_sc::imports!();

use crate::address_to_id_mapper::{AddressId, AddressToIdMapper};

#[multiversx_sc::module]
pub trait ConfigModule {
#[only_owner]
#[endpoint(addContractTemplate)]
fn add_contract_template(&self, template_address: ManagedAddress) -> AddressId {
require!(
self.blockchain().is_smart_contract(&template_address),
"Invalid template address"
);

self.address_ids().insert_new(&template_address)
}

#[only_owner]
#[endpoint(removeContractTemplate)]
fn remove_contract_template(&self, address_id: AddressId) {
require!(
self.address_ids().contains_id(address_id),
"Invalid address id"
);

self.address_ids().remove_by_id(address_id);
}

#[storage_mapper("addressIds")]
fn address_ids(&self) -> AddressToIdMapper<Self::Api>;

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

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

use crate::address_to_id_mapper::AddressId;
use crate::config;

#[multiversx_sc::module]
pub trait ContractInteractionsModule: config::ConfigModule {
#[endpoint(deployContract)]
fn deploy_contract(
&self,
template_address_id: AddressId,
args: MultiValueEncoded<ManagedBuffer>,
) -> ManagedAddress {
let caller = self.blockchain().get_caller();

let mut arguments = ManagedArgBuffer::new();
for arg in args {
arguments.push_arg(arg);
}

let opt_template_address = self.address_ids().get_address(template_address_id);
let template_address = match opt_template_address {
Some(template_address) => template_address,
None => sc_panic!("Template not found"),
};

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

self.deployer_contract_addresses(&caller)
.insert(new_contract_address.clone());
self.deployers_list().insert(caller);

new_contract_address
}

#[endpoint(upgradeContract)]
fn upgrade_contract(
&self,
contract_address: ManagedAddress,
template_address_id: AddressId,
args: MultiValueEncoded<ManagedBuffer>,
) {
let caller = self.blockchain().get_caller();
require!(self.deployer_contract_addresses(&caller).contains(&contract_address), "Caller is not the deployer of the contract");

let mut arguments = ManagedArgBuffer::new();
for arg in args {
arguments.push_arg(arg);
}

let opt_template_address = self.address_ids().get_address(template_address_id);
let template_address = match opt_template_address {
Some(template_address) => template_address,
None => sc_panic!("Template not found"),
};

self.send_raw().upgrade_from_source_contract(
&contract_address,
self.blockchain().get_gas_left(),
&BigUint::zero(),
&template_address,
CodeMetadata::DEFAULT,
&arguments,
);
}

#[endpoint(callContractEndpoint)]
fn call_contract_endpoint(
&self,
contract_address: ManagedAddress,
function_name: ManagedBuffer,
args: MultiValueEncoded<ManagedBuffer>,
) {
let caller = self.blockchain().get_caller();
require!(self.deployer_contract_addresses(&caller).contains(&contract_address), "Caller is not the deployer of the contract");

let gas_left = self.blockchain().get_gas_left();
let mut contract_call = self
.send()
.contract_call::<()>(contract_address, function_name)
.with_gas_limit(gas_left);

for arg in args {
contract_call.push_raw_argument(arg);
}
let _: IgnoreValue = contract_call.execute_on_dest_context();
}
}
Loading

0 comments on commit 5967c88

Please sign in to comment.