generated from multiversx/mx-template-sc
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
de783fc
commit 5967c88
Showing
13 changed files
with
668 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
fn main() { | ||
multiversx_sc_meta::cli_main::<proxy_deployer::AbiProvider>(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"language": "rust" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
Oops, something went wrong.