Skip to content

Commit

Permalink
Merge pull request #56 from multiversx/erc3643
Browse files Browse the repository at this point in the history
erc3643
  • Loading branch information
dorin-iancu authored Feb 21, 2024
2 parents 4fc5c31 + ec6bdd8 commit d2677e9
Show file tree
Hide file tree
Showing 18 changed files with 803 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ members = [
"contracts/dn404/meta",
"contracts/empty",
"contracts/empty/meta",
"contracts/erc3643",
"contracts/erc3643/meta",
"contracts/factorial",
"contracts/factorial/meta",
"contracts/fair-launch",
Expand Down
21 changes: 21 additions & 0 deletions contracts/erc3643/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "erc3643"
version = "0.0.0"
authors = ["you"]
edition = "2021"
publish = false

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

[dependencies.multiversx-sc]
version = "=0.47.2"

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

[dev-dependencies]
num-bigint = "0.4.2"

[dev-dependencies.multiversx-sc-scenario]
version = "=0.47.2"
12 changes: 12 additions & 0 deletions contracts/erc3643/meta/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "erc3643-meta"
version = "0.0.0"
edition = "2021"
publish = false

[dependencies.erc3643]
path = ".."

[dependencies.multiversx-sc-meta]
version = "=0.47.2"
default-features = false
3 changes: 3 additions & 0 deletions contracts/erc3643/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::<erc3643::AbiProvider>();
}
3 changes: 3 additions & 0 deletions contracts/erc3643/multiversx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"language": "rust"
}
104 changes: 104 additions & 0 deletions contracts/erc3643/src/exchange_actions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use multiversx_sc_modules::transfer_role_proxy::PaymentsVec;

use crate::hooks::hook_type::ErcHookType;

multiversx_sc::imports!();

pub type EndpointName<M> = ManagedBuffer<M>;

#[multiversx_sc::module]
pub trait ExchangeActionsModule:
crate::users::UsersModule
+ crate::hooks::call_hook::CallHookModule
+ multiversx_sc_modules::pause::PauseModule
{
#[only_owner]
#[endpoint(addExchangeEndpoint)]
fn add_exchange_endpoint(
&self,
sc_addr: ManagedAddress,
endpoint_names: MultiValueEncoded<EndpointName<Self::Api>>,
) {
let mut mapper = self.known_contracts(&sc_addr);
for new_endpoint in endpoint_names {
let _ = mapper.insert(new_endpoint);
}
}

#[only_owner]
#[endpoint(removeExchangeEndpoint)]
fn remove_exchange_endpoint(
&self,
sc_addr: ManagedAddress,
endpoint_names: MultiValueEncoded<ManagedBuffer>,
) {
let mut mapper = self.known_contracts(&sc_addr);
for endpoint_to_remove in endpoint_names {
let is_removed = mapper.swap_remove(&endpoint_to_remove);

require!(is_removed, "Unknown endpoint name");
}
}

/// forward an execute on dest context call on an exchange SC
#[payable("*")]
#[endpoint(forwardExecuteOnDest)]
fn forward_execute_on_dest(
&self,
dest: ManagedAddress,
endpoint_name: ManagedBuffer,
extra_args: MultiValueEncoded<ManagedBuffer>,
) -> PaymentsVec<Self::Api> {
self.require_not_paused();
self.require_known_endpoint(&dest, &endpoint_name);

let egld_value = self.call_value().egld_value().clone_value();
require!(egld_value == 0, "Invalid payment");

let caller = self.blockchain().get_caller();
self.require_whitelisted(&caller);

let payments = self.call_value().all_esdt_transfers().clone_value();
let payments_after_hook = self.call_hook(
ErcHookType::BeforeExchangeAction,
caller.clone(),
payments,
extra_args.to_vec(),
);

let (_, back_transfers) =
ContractCallNoPayment::<_, MultiValueEncoded<ManagedBuffer>>::new(dest, endpoint_name)
.with_multi_token_transfer(payments_after_hook)
.with_raw_arguments(ManagedArgBuffer::from(extra_args.into_vec_of_buffers()))
.execute_on_dest_context_with_back_transfers::<MultiValueEncoded<ManagedBuffer>>();

let output_payments = self.call_hook(
ErcHookType::AfterExchangeAction,
caller.clone(),
back_transfers.esdt_payments,
ManagedVec::new(),
);

if !output_payments.is_empty() {
self.send().direct_multi(&caller, &output_payments);
}

output_payments
}

fn require_known_endpoint(&self, dest: &ManagedAddress, endpoint_name: &ManagedBuffer) {
let known_sc_mapper = self.known_contracts(dest);
require!(
!known_sc_mapper.is_empty(),
"Unknown SC, use forwardTransfer endpoint"
);

require!(known_sc_mapper.contains(endpoint_name), "Unknown endpoint");
}

#[storage_mapper("knownContracts")]
fn known_contracts(
&self,
sc_addr: &ManagedAddress,
) -> UnorderedSetMapper<EndpointName<Self::Api>>;
}
70 changes: 70 additions & 0 deletions contracts/erc3643/src/hooks/call_hook.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use multiversx_sc_modules::transfer_role_proxy::PaymentsVec;

use super::hook_type::{Hook, ErcHookType};

multiversx_sc::imports!();

#[multiversx_sc::module]
pub trait CallHookModule {
fn call_hook(
&self,
hook_type: ErcHookType,
original_caller: ManagedAddress,
input_payments: PaymentsVec<Self::Api>,
args: ManagedVec<ManagedBuffer>,
) -> PaymentsVec<Self::Api> {
let hooks = self.hooks(hook_type).get();
if hooks.is_empty() {
return input_payments;
}

let payments_len = input_payments.len();
let mut call_args = ManagedArgBuffer::new();
call_args.push_arg(original_caller);

for arg in &args {
call_args.push_arg(arg);
}

let mut output_payments = input_payments;
for hook in &hooks {
let (_, back_transfers) =
ContractCallNoPayment::<_, MultiValueEncoded<ManagedBuffer>>::new(
hook.dest_address,
hook.endpoint_name,
)
.with_raw_arguments(call_args.clone())
.with_multi_token_transfer(output_payments.clone())
.execute_on_dest_context_with_back_transfers::<MultiValueEncoded<ManagedBuffer>>();

require!(
back_transfers.esdt_payments.len() == payments_len,
"Wrong payments received from SC"
);

for (payment_before, payment_after) in output_payments
.iter()
.zip(back_transfers.esdt_payments.iter())
{
require!(
payment_before.token_identifier == payment_after.token_identifier
&& payment_before.token_nonce == payment_after.token_nonce,
"Invalid payment received from SC"
);
}

output_payments = back_transfers.esdt_payments;
}

output_payments
}

fn encode_arg_to_vec<T: TopEncode>(&self, arg: &T, vec: &mut ManagedVec<ManagedBuffer>) {
let mut encoded_value = ManagedBuffer::new();
let _ = arg.top_encode(&mut encoded_value);
vec.push(encoded_value);
}

#[storage_mapper("hooks")]
fn hooks(&self, hook_type: ErcHookType) -> SingleValueMapper<ManagedVec<Hook<Self::Api>>>;
}
52 changes: 52 additions & 0 deletions contracts/erc3643/src/hooks/change_hooks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use super::hook_type::{Hook, ErcHookType};

multiversx_sc::imports!();

#[multiversx_sc::module]
pub trait ChangeHooksModule: super::call_hook::CallHookModule {
#[only_owner]
#[endpoint(addHook)]
fn add_hook(&self, hook_type: ErcHookType, to: ManagedAddress, endpoint_name: ManagedBuffer) {
self.require_sc_address(&to);
self.require_not_empty_buffer(&endpoint_name);

self.hooks(hook_type).update(|hooks| {
hooks.push(Hook {
dest_address: to,
endpoint_name,
})
});
}

#[only_owner]
#[endpoint(removeHook)]
fn remove_hook(
&self,
hook_type: ErcHookType,
to: ManagedAddress,
endpoint_name: ManagedBuffer,
) {
self.hooks(hook_type).update(|hooks| {
let opt_index = hooks.find(&Hook {
dest_address: to,
endpoint_name,
});

require!(opt_index.is_some(), "Item not found");

let index = unsafe { opt_index.unwrap_unchecked() };
hooks.remove(index);
});
}

fn require_sc_address(&self, address: &ManagedAddress) {
require!(
!address.is_zero() && self.blockchain().is_smart_contract(address),
"Invalid SC address"
);
}

fn require_not_empty_buffer(&self, buffer: &ManagedBuffer) {
require!(!buffer.is_empty(), "Empty buffer");
}
}
48 changes: 48 additions & 0 deletions contracts/erc3643/src/hooks/hook_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use multiversx_sc_modules::transfer_role_proxy::PaymentsVec;

multiversx_sc::imports!();
multiversx_sc::derive_imports!();

#[derive(TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode, Clone, Copy)]
pub enum ErcHookType {
// can't be done, execute_on_dest does not work on init
_BeforeInitialize,
_AfterInitialize,
BeforeTransfer,
BeforeExchangeAction,
AfterExchangeAction,
}

#[derive(TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode, ManagedVecItem, PartialEq)]
pub struct Hook<M: ManagedTypeApi> {
pub dest_address: ManagedAddress<M>,
pub endpoint_name: ManagedBuffer<M>,
}

pub trait ErcHook {
type Sc: ContractBase;

fn before_transfer(
sc: &Self::Sc,
tokens: PaymentsVec<<Self::Sc as ContractBase>::Api>,
original_caller: ManagedAddress<<Self::Sc as ContractBase>::Api>,
dest: ManagedAddress<<Self::Sc as ContractBase>::Api>,
);

fn before_exchange_action(
sc: &Self::Sc,
tokens: PaymentsVec<<Self::Sc as ContractBase>::Api>,
original_caller: ManagedAddress<<Self::Sc as ContractBase>::Api>,
dest: ManagedAddress<<Self::Sc as ContractBase>::Api>,
args: MultiValueEncoded<
<Self::Sc as ContractBase>::Api,
ManagedBuffer<<Self::Sc as ContractBase>::Api>,
>,
);

fn after_exchange_action(
sc: &Self::Sc,
tokens: PaymentsVec<<Self::Sc as ContractBase>::Api>,
original_caller: ManagedAddress<<Self::Sc as ContractBase>::Api>,
);
}
3 changes: 3 additions & 0 deletions contracts/erc3643/src/hooks/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod call_hook;
pub mod change_hooks;
pub mod hook_type;
27 changes: 27 additions & 0 deletions contracts/erc3643/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#![no_std]

multiversx_sc::imports!();

pub mod exchange_actions;
pub mod hooks;
pub mod token;
pub mod transfer;
pub mod users;

#[multiversx_sc::contract]
pub trait Erc3643:
users::UsersModule
+ token::TokenModule
+ hooks::call_hook::CallHookModule
+ hooks::change_hooks::ChangeHooksModule
+ multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule
+ multiversx_sc_modules::pause::PauseModule
{
#[init]
fn init(&self) {
self.set_paused(true);
}

#[endpoint]
fn upgrade(&self) {}
}
Loading

0 comments on commit d2677e9

Please sign in to comment.