Skip to content

Commit d2677e9

Browse files
authored
Merge pull request #56 from multiversx/erc3643
erc3643
2 parents 4fc5c31 + ec6bdd8 commit d2677e9

File tree

18 files changed

+803
-0
lines changed

18 files changed

+803
-0
lines changed

Cargo.lock

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ members = [
3434
"contracts/dn404/meta",
3535
"contracts/empty",
3636
"contracts/empty/meta",
37+
"contracts/erc3643",
38+
"contracts/erc3643/meta",
3739
"contracts/factorial",
3840
"contracts/factorial/meta",
3941
"contracts/fair-launch",

contracts/erc3643/Cargo.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "erc3643"
3+
version = "0.0.0"
4+
authors = ["you"]
5+
edition = "2021"
6+
publish = false
7+
8+
[lib]
9+
path = "src/lib.rs"
10+
11+
[dependencies.multiversx-sc]
12+
version = "=0.47.2"
13+
14+
[dependencies.multiversx-sc-modules]
15+
version = "=0.47.2"
16+
17+
[dev-dependencies]
18+
num-bigint = "0.4.2"
19+
20+
[dev-dependencies.multiversx-sc-scenario]
21+
version = "=0.47.2"

contracts/erc3643/meta/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "erc3643-meta"
3+
version = "0.0.0"
4+
edition = "2021"
5+
publish = false
6+
7+
[dependencies.erc3643]
8+
path = ".."
9+
10+
[dependencies.multiversx-sc-meta]
11+
version = "=0.47.2"
12+
default-features = false

contracts/erc3643/meta/src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
multiversx_sc_meta::cli_main::<erc3643::AbiProvider>();
3+
}

contracts/erc3643/multiversx.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"language": "rust"
3+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
use multiversx_sc_modules::transfer_role_proxy::PaymentsVec;
2+
3+
use crate::hooks::hook_type::ErcHookType;
4+
5+
multiversx_sc::imports!();
6+
7+
pub type EndpointName<M> = ManagedBuffer<M>;
8+
9+
#[multiversx_sc::module]
10+
pub trait ExchangeActionsModule:
11+
crate::users::UsersModule
12+
+ crate::hooks::call_hook::CallHookModule
13+
+ multiversx_sc_modules::pause::PauseModule
14+
{
15+
#[only_owner]
16+
#[endpoint(addExchangeEndpoint)]
17+
fn add_exchange_endpoint(
18+
&self,
19+
sc_addr: ManagedAddress,
20+
endpoint_names: MultiValueEncoded<EndpointName<Self::Api>>,
21+
) {
22+
let mut mapper = self.known_contracts(&sc_addr);
23+
for new_endpoint in endpoint_names {
24+
let _ = mapper.insert(new_endpoint);
25+
}
26+
}
27+
28+
#[only_owner]
29+
#[endpoint(removeExchangeEndpoint)]
30+
fn remove_exchange_endpoint(
31+
&self,
32+
sc_addr: ManagedAddress,
33+
endpoint_names: MultiValueEncoded<ManagedBuffer>,
34+
) {
35+
let mut mapper = self.known_contracts(&sc_addr);
36+
for endpoint_to_remove in endpoint_names {
37+
let is_removed = mapper.swap_remove(&endpoint_to_remove);
38+
39+
require!(is_removed, "Unknown endpoint name");
40+
}
41+
}
42+
43+
/// forward an execute on dest context call on an exchange SC
44+
#[payable("*")]
45+
#[endpoint(forwardExecuteOnDest)]
46+
fn forward_execute_on_dest(
47+
&self,
48+
dest: ManagedAddress,
49+
endpoint_name: ManagedBuffer,
50+
extra_args: MultiValueEncoded<ManagedBuffer>,
51+
) -> PaymentsVec<Self::Api> {
52+
self.require_not_paused();
53+
self.require_known_endpoint(&dest, &endpoint_name);
54+
55+
let egld_value = self.call_value().egld_value().clone_value();
56+
require!(egld_value == 0, "Invalid payment");
57+
58+
let caller = self.blockchain().get_caller();
59+
self.require_whitelisted(&caller);
60+
61+
let payments = self.call_value().all_esdt_transfers().clone_value();
62+
let payments_after_hook = self.call_hook(
63+
ErcHookType::BeforeExchangeAction,
64+
caller.clone(),
65+
payments,
66+
extra_args.to_vec(),
67+
);
68+
69+
let (_, back_transfers) =
70+
ContractCallNoPayment::<_, MultiValueEncoded<ManagedBuffer>>::new(dest, endpoint_name)
71+
.with_multi_token_transfer(payments_after_hook)
72+
.with_raw_arguments(ManagedArgBuffer::from(extra_args.into_vec_of_buffers()))
73+
.execute_on_dest_context_with_back_transfers::<MultiValueEncoded<ManagedBuffer>>();
74+
75+
let output_payments = self.call_hook(
76+
ErcHookType::AfterExchangeAction,
77+
caller.clone(),
78+
back_transfers.esdt_payments,
79+
ManagedVec::new(),
80+
);
81+
82+
if !output_payments.is_empty() {
83+
self.send().direct_multi(&caller, &output_payments);
84+
}
85+
86+
output_payments
87+
}
88+
89+
fn require_known_endpoint(&self, dest: &ManagedAddress, endpoint_name: &ManagedBuffer) {
90+
let known_sc_mapper = self.known_contracts(dest);
91+
require!(
92+
!known_sc_mapper.is_empty(),
93+
"Unknown SC, use forwardTransfer endpoint"
94+
);
95+
96+
require!(known_sc_mapper.contains(endpoint_name), "Unknown endpoint");
97+
}
98+
99+
#[storage_mapper("knownContracts")]
100+
fn known_contracts(
101+
&self,
102+
sc_addr: &ManagedAddress,
103+
) -> UnorderedSetMapper<EndpointName<Self::Api>>;
104+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use multiversx_sc_modules::transfer_role_proxy::PaymentsVec;
2+
3+
use super::hook_type::{Hook, ErcHookType};
4+
5+
multiversx_sc::imports!();
6+
7+
#[multiversx_sc::module]
8+
pub trait CallHookModule {
9+
fn call_hook(
10+
&self,
11+
hook_type: ErcHookType,
12+
original_caller: ManagedAddress,
13+
input_payments: PaymentsVec<Self::Api>,
14+
args: ManagedVec<ManagedBuffer>,
15+
) -> PaymentsVec<Self::Api> {
16+
let hooks = self.hooks(hook_type).get();
17+
if hooks.is_empty() {
18+
return input_payments;
19+
}
20+
21+
let payments_len = input_payments.len();
22+
let mut call_args = ManagedArgBuffer::new();
23+
call_args.push_arg(original_caller);
24+
25+
for arg in &args {
26+
call_args.push_arg(arg);
27+
}
28+
29+
let mut output_payments = input_payments;
30+
for hook in &hooks {
31+
let (_, back_transfers) =
32+
ContractCallNoPayment::<_, MultiValueEncoded<ManagedBuffer>>::new(
33+
hook.dest_address,
34+
hook.endpoint_name,
35+
)
36+
.with_raw_arguments(call_args.clone())
37+
.with_multi_token_transfer(output_payments.clone())
38+
.execute_on_dest_context_with_back_transfers::<MultiValueEncoded<ManagedBuffer>>();
39+
40+
require!(
41+
back_transfers.esdt_payments.len() == payments_len,
42+
"Wrong payments received from SC"
43+
);
44+
45+
for (payment_before, payment_after) in output_payments
46+
.iter()
47+
.zip(back_transfers.esdt_payments.iter())
48+
{
49+
require!(
50+
payment_before.token_identifier == payment_after.token_identifier
51+
&& payment_before.token_nonce == payment_after.token_nonce,
52+
"Invalid payment received from SC"
53+
);
54+
}
55+
56+
output_payments = back_transfers.esdt_payments;
57+
}
58+
59+
output_payments
60+
}
61+
62+
fn encode_arg_to_vec<T: TopEncode>(&self, arg: &T, vec: &mut ManagedVec<ManagedBuffer>) {
63+
let mut encoded_value = ManagedBuffer::new();
64+
let _ = arg.top_encode(&mut encoded_value);
65+
vec.push(encoded_value);
66+
}
67+
68+
#[storage_mapper("hooks")]
69+
fn hooks(&self, hook_type: ErcHookType) -> SingleValueMapper<ManagedVec<Hook<Self::Api>>>;
70+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use super::hook_type::{Hook, ErcHookType};
2+
3+
multiversx_sc::imports!();
4+
5+
#[multiversx_sc::module]
6+
pub trait ChangeHooksModule: super::call_hook::CallHookModule {
7+
#[only_owner]
8+
#[endpoint(addHook)]
9+
fn add_hook(&self, hook_type: ErcHookType, to: ManagedAddress, endpoint_name: ManagedBuffer) {
10+
self.require_sc_address(&to);
11+
self.require_not_empty_buffer(&endpoint_name);
12+
13+
self.hooks(hook_type).update(|hooks| {
14+
hooks.push(Hook {
15+
dest_address: to,
16+
endpoint_name,
17+
})
18+
});
19+
}
20+
21+
#[only_owner]
22+
#[endpoint(removeHook)]
23+
fn remove_hook(
24+
&self,
25+
hook_type: ErcHookType,
26+
to: ManagedAddress,
27+
endpoint_name: ManagedBuffer,
28+
) {
29+
self.hooks(hook_type).update(|hooks| {
30+
let opt_index = hooks.find(&Hook {
31+
dest_address: to,
32+
endpoint_name,
33+
});
34+
35+
require!(opt_index.is_some(), "Item not found");
36+
37+
let index = unsafe { opt_index.unwrap_unchecked() };
38+
hooks.remove(index);
39+
});
40+
}
41+
42+
fn require_sc_address(&self, address: &ManagedAddress) {
43+
require!(
44+
!address.is_zero() && self.blockchain().is_smart_contract(address),
45+
"Invalid SC address"
46+
);
47+
}
48+
49+
fn require_not_empty_buffer(&self, buffer: &ManagedBuffer) {
50+
require!(!buffer.is_empty(), "Empty buffer");
51+
}
52+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use multiversx_sc_modules::transfer_role_proxy::PaymentsVec;
2+
3+
multiversx_sc::imports!();
4+
multiversx_sc::derive_imports!();
5+
6+
#[derive(TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode, Clone, Copy)]
7+
pub enum ErcHookType {
8+
// can't be done, execute_on_dest does not work on init
9+
_BeforeInitialize,
10+
_AfterInitialize,
11+
BeforeTransfer,
12+
BeforeExchangeAction,
13+
AfterExchangeAction,
14+
}
15+
16+
#[derive(TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode, ManagedVecItem, PartialEq)]
17+
pub struct Hook<M: ManagedTypeApi> {
18+
pub dest_address: ManagedAddress<M>,
19+
pub endpoint_name: ManagedBuffer<M>,
20+
}
21+
22+
pub trait ErcHook {
23+
type Sc: ContractBase;
24+
25+
fn before_transfer(
26+
sc: &Self::Sc,
27+
tokens: PaymentsVec<<Self::Sc as ContractBase>::Api>,
28+
original_caller: ManagedAddress<<Self::Sc as ContractBase>::Api>,
29+
dest: ManagedAddress<<Self::Sc as ContractBase>::Api>,
30+
);
31+
32+
fn before_exchange_action(
33+
sc: &Self::Sc,
34+
tokens: PaymentsVec<<Self::Sc as ContractBase>::Api>,
35+
original_caller: ManagedAddress<<Self::Sc as ContractBase>::Api>,
36+
dest: ManagedAddress<<Self::Sc as ContractBase>::Api>,
37+
args: MultiValueEncoded<
38+
<Self::Sc as ContractBase>::Api,
39+
ManagedBuffer<<Self::Sc as ContractBase>::Api>,
40+
>,
41+
);
42+
43+
fn after_exchange_action(
44+
sc: &Self::Sc,
45+
tokens: PaymentsVec<<Self::Sc as ContractBase>::Api>,
46+
original_caller: ManagedAddress<<Self::Sc as ContractBase>::Api>,
47+
);
48+
}

0 commit comments

Comments
 (0)