From 6e3071195fc2c20c19421dfe3b149d5249de47b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 27 Sep 2023 16:07:57 +0300 Subject: [PATCH 01/12] Paymaster SC --- Cargo.toml | 2 + contracts/paymaster/.gitignore | 7 + contracts/paymaster/Cargo.toml | 18 ++ contracts/paymaster/meta/Cargo.toml | 14 ++ contracts/paymaster/meta/src/main.rs | 3 + contracts/paymaster/multiversx.json | 3 + contracts/paymaster/mxsc-template.toml | 18 ++ contracts/paymaster/scenarios/empty.scen.json | 39 ++++ contracts/paymaster/src/fees.rs | 79 +++++++ contracts/paymaster/src/forward_call.rs | 61 +++++ contracts/paymaster/src/paymaster.rs | 32 +++ .../paymaster/tests/empty_scenario_go_test.rs | 10 + .../paymaster/tests/empty_scenario_rs_test.rs | 14 ++ contracts/paymaster/wasm/Cargo.lock | 209 ++++++++++++++++++ contracts/paymaster/wasm/Cargo.toml | 27 +++ contracts/paymaster/wasm/src/lib.rs | 26 +++ 16 files changed, 562 insertions(+) create mode 100644 contracts/paymaster/.gitignore create mode 100644 contracts/paymaster/Cargo.toml create mode 100644 contracts/paymaster/meta/Cargo.toml create mode 100644 contracts/paymaster/meta/src/main.rs create mode 100644 contracts/paymaster/multiversx.json create mode 100644 contracts/paymaster/mxsc-template.toml create mode 100644 contracts/paymaster/scenarios/empty.scen.json create mode 100644 contracts/paymaster/src/fees.rs create mode 100644 contracts/paymaster/src/forward_call.rs create mode 100644 contracts/paymaster/src/paymaster.rs create mode 100644 contracts/paymaster/tests/empty_scenario_go_test.rs create mode 100644 contracts/paymaster/tests/empty_scenario_rs_test.rs create mode 100644 contracts/paymaster/wasm/Cargo.lock create mode 100644 contracts/paymaster/wasm/Cargo.toml create mode 100644 contracts/paymaster/wasm/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 7d807b5b..937ffe66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,8 @@ members = [ "contracts/order-book/factory/meta", "contracts/order-book/pair", "contracts/order-book/pair/meta", + "contracts/paymaster", + "contracts/paymaster/meta", "contracts/ping-pong-egld", "contracts/ping-pong-egld/meta", "contracts/proxy-pause", diff --git a/contracts/paymaster/.gitignore b/contracts/paymaster/.gitignore new file mode 100644 index 00000000..2c76bc98 --- /dev/null +++ b/contracts/paymaster/.gitignore @@ -0,0 +1,7 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +*/target/ + +# The mxpy output +/output*/ diff --git a/contracts/paymaster/Cargo.toml b/contracts/paymaster/Cargo.toml new file mode 100644 index 00000000..5ec2cc83 --- /dev/null +++ b/contracts/paymaster/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "paymaster" +version = "0.0.0" +authors = [ "you",] +edition = "2018" +publish = false + +[lib] +path = "src/paymaster.rs" + +[dev-dependencies] +num-bigint = "0.4.2" + +[dependencies.multiversx-sc] +version = "0.43.4" + +[dev-dependencies.multiversx-sc-scenario] +version = "0.41.1" diff --git a/contracts/paymaster/meta/Cargo.toml b/contracts/paymaster/meta/Cargo.toml new file mode 100644 index 00000000..20157c88 --- /dev/null +++ b/contracts/paymaster/meta/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "paymaster-meta" +version = "0.0.0" +edition = "2018" +publish = false +authors = [ "you",] + +[dev-dependencies] + +[dependencies.paymaster] +path = ".." + +[dependencies.multiversx-sc-meta] +version = "0.43.4" diff --git a/contracts/paymaster/meta/src/main.rs b/contracts/paymaster/meta/src/main.rs new file mode 100644 index 00000000..275bd693 --- /dev/null +++ b/contracts/paymaster/meta/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + multiversx_sc_meta::cli_main::(); +} diff --git a/contracts/paymaster/multiversx.json b/contracts/paymaster/multiversx.json new file mode 100644 index 00000000..73655396 --- /dev/null +++ b/contracts/paymaster/multiversx.json @@ -0,0 +1,3 @@ +{ + "language": "rust" +} \ No newline at end of file diff --git a/contracts/paymaster/mxsc-template.toml b/contracts/paymaster/mxsc-template.toml new file mode 100644 index 00000000..441addd7 --- /dev/null +++ b/contracts/paymaster/mxsc-template.toml @@ -0,0 +1,18 @@ +name = "empty" +contract_trait = "EmptyContract" +src_file = "empty.rs" +rename_pairs = [ + [ + "blockchain.set_current_dir_from_workspace(\"contracts/examples/empty\");", + "// blockchain.set_current_dir_from_workspace(\"relative path to your workspace, if applicable\");", + ], +] +files_include = [ + "meta", + "scenarios", + "src", + "tests", + "wasm/Cargo.toml", + "Cargo.toml", + "multiversx.json", +] diff --git a/contracts/paymaster/scenarios/empty.scen.json b/contracts/paymaster/scenarios/empty.scen.json new file mode 100644 index 00000000..b44fde46 --- /dev/null +++ b/contracts/paymaster/scenarios/empty.scen.json @@ -0,0 +1,39 @@ +{ + "name": "paymaster", + "steps": [ + { + "step": "setState", + "accounts": { + "address:owner": { + "nonce": "1", + "balance": "0" + } + }, + "newAddresses": [ + { + "creatorAddress": "address:owner", + "creatorNonce": "1", + "newAddress": "sc:empty" + } + ] + }, + { + "step": "scDeploy", + "id": "deploy", + "tx": { + "from": "address:owner", + "contractCode": "file:../output/paymaster.wasm", + "arguments": [], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "", + "logs": [], + "gas": "*", + "refund": "*" + } + } + ] +} \ No newline at end of file diff --git a/contracts/paymaster/src/fees.rs b/contracts/paymaster/src/fees.rs new file mode 100644 index 00000000..7781aacc --- /dev/null +++ b/contracts/paymaster/src/fees.rs @@ -0,0 +1,79 @@ +use crate::forward_call::PaymentsVec; + +multiversx_sc::imports!(); + +pub mod pair_proxy { + #[multiversx_sc::proxy] + pub trait PairProxy { + #[view(getSafePriceByDefaultOffset)] + fn get_safe_price_by_default_offset( + &self, + pair_address: ManagedAddress, + input_payment: EsdtTokenPayment, + ) -> EsdtTokenPayment; + } +} + +#[multiversx_sc::module] +pub trait FeesModule { + #[only_owner] + #[endpoint(addAcceptedFeesTokens)] + fn add_accepted_fees_tokens( + &self, + accepted_tokens: MultiValueEncoded>, + ) { + for pair in accepted_tokens { + let (token_id, pair_address) = pair.into_tuple(); + require!(token_id.is_valid_esdt_identifier(), "Invalid token"); + + self.pair_address_for_token(&token_id).set(pair_address); + } + } + + fn get_price(&self, token_id: TokenIdentifier, amount: BigUint) -> BigUint { + let mapper = self.pair_address_for_token(&token_id); + require!( + !mapper.is_empty(), + "There is no pair addres for the token provided!" + ); + + let pair_address = mapper.get(); + let price_query_address = self.price_query_address().get(); + let price: EsdtTokenPayment = self + .pair_proxy(price_query_address) + .get_safe_price_by_default_offset( + pair_address, + EsdtTokenPayment::new(token_id, 0, amount), + ) + .execute_on_dest_context(); + + price.amount + } + + fn pay_fee_to_relayer(&self, relayer_addr: ManagedAddress, payments: PaymentsVec) { + let mut payments_iter = payments.iter(); + let fee_payment = match payments_iter.next() { + Some(fee) => fee, + None => sc_panic!("Fee payment is missing!"), + }; + + self.send().direct_esdt( + &relayer_addr, + &fee_payment.token_identifier, + 0, + &fee_payment.amount, + ); + } + + #[proxy] + fn pair_proxy(&self, sc_address: ManagedAddress) -> pair_proxy::Proxy; + + #[storage_mapper("pairAddressForToken")] + fn pair_address_for_token( + &self, + token_id: &TokenIdentifier, + ) -> SingleValueMapper; + + #[storage_mapper("priceQueryAddress")] + fn price_query_address(&self) -> SingleValueMapper; +} diff --git a/contracts/paymaster/src/forward_call.rs b/contracts/paymaster/src/forward_call.rs new file mode 100644 index 00000000..6f1f8a52 --- /dev/null +++ b/contracts/paymaster/src/forward_call.rs @@ -0,0 +1,61 @@ +multiversx_sc::imports!(); + +pub type PaymentsVec = ManagedVec>; + +static ERR_CALLBACK_MSG: &[u8] = b"Error received in callback:"; + +#[multiversx_sc::module] +pub trait ForwardCall { + fn forward_call( + &self, + dest: ManagedAddress, + endpoint_name: ManagedBuffer, + endpoint_args: MultiValueEncoded, + payments: PaymentsVec, + ) { + let original_caller = self.blockchain().get_caller(); + + if !self.blockchain().is_smart_contract(&dest) { + self.send().direct_multi(&dest, &payments); + } else { + let mut args_buffer = ManagedArgBuffer::new(); + for arg in endpoint_args { + args_buffer.push_arg(arg); + } + + ContractCallWithMultiEsdt::::new(dest, endpoint_name, payments.clone()) + .with_raw_arguments(args_buffer) + .async_call() + .with_callback( + self.callbacks() + .transfer_callback(original_caller, payments), + ) + .call_and_exit(); + } + } + #[callback] + fn transfer_callback( + &self, + original_caller: ManagedAddress, + initial_payments: ManagedVec>, + #[call_result] result: ManagedAsyncCallResult>, + ) -> MultiValueEncoded { + match result { + ManagedAsyncCallResult::Ok(return_values) => return_values, + ManagedAsyncCallResult::Err(err) => { + if !initial_payments.is_empty() { + self.send() + .direct_multi(&original_caller, &initial_payments); + } + + let mut err_result = MultiValueEncoded::new(); + err_result.push(ManagedBuffer::new_from_bytes(ERR_CALLBACK_MSG)); + err_result.push(err.err_msg.clone()); + + sc_print!("{}", err.err_msg); + + err_result + } + } + } +} diff --git a/contracts/paymaster/src/paymaster.rs b/contracts/paymaster/src/paymaster.rs new file mode 100644 index 00000000..b5f84b0d --- /dev/null +++ b/contracts/paymaster/src/paymaster.rs @@ -0,0 +1,32 @@ +#![no_std] + +multiversx_sc::imports!(); + +pub mod fees; +pub mod forward_call; + +/// An empty contract. To be used as a template when starting a new contract from scratch. +#[multiversx_sc::contract] +pub trait PaymasterContract: fees::FeesModule + forward_call::ForwardCall { + #[init] + fn init(&self, price_query_address: ManagedAddress) { + self.price_query_address().set(price_query_address); + } + + #[endpoint(forwardExecution)] + #[payable("*")] + fn forward_execution( + &self, + relayer_addr: ManagedAddress, + dest: ManagedAddress, + endpoint_name: ManagedBuffer, + endpoint_args: MultiValueEncoded, + ) { + let payments = self.call_value().all_esdt_transfers(); + + self.pay_fee_to_relayer(relayer_addr, payments.clone_value()); + let mut payments_without_fee = payments.clone_value(); + payments_without_fee.remove(0); + self.forward_call(dest, endpoint_name, endpoint_args, payments_without_fee); + } +} diff --git a/contracts/paymaster/tests/empty_scenario_go_test.rs b/contracts/paymaster/tests/empty_scenario_go_test.rs new file mode 100644 index 00000000..ef0a7f21 --- /dev/null +++ b/contracts/paymaster/tests/empty_scenario_go_test.rs @@ -0,0 +1,10 @@ +use multiversx_sc_scenario::*; + +fn world() -> ScenarioWorld { + ScenarioWorld::vm_go() +} + +#[test] +fn empty_go() { + world().run("scenarios/empty.scen.json"); +} diff --git a/contracts/paymaster/tests/empty_scenario_rs_test.rs b/contracts/paymaster/tests/empty_scenario_rs_test.rs new file mode 100644 index 00000000..e7cd14ce --- /dev/null +++ b/contracts/paymaster/tests/empty_scenario_rs_test.rs @@ -0,0 +1,14 @@ +use multiversx_sc_scenario::*; + +fn world() -> ScenarioWorld { + let mut blockchain = ScenarioWorld::new(); + blockchain.set_current_dir_from_workspace("contracts/examples/empty"); + + blockchain.register_contract("file:output/paymaster.wasm", paymaster::ContractBuilder); + blockchain +} + +#[test] +fn empty_rs() { + world().run("scenarios/empty.scen.json"); +} diff --git a/contracts/paymaster/wasm/Cargo.lock b/contracts/paymaster/wasm/Cargo.lock new file mode 100644 index 00000000..5a9b6622 --- /dev/null +++ b/contracts/paymaster/wasm/Cargo.lock @@ -0,0 +1,209 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "empty" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "empty-wasm" +version = "0.0.0" +dependencies = [ + "empty", + "multiversx-sc-wasm-adapter", +] + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "multiversx-sc" +version = "0.43.4" +dependencies = [ + "bitflags", + "hashbrown", + "hex-literal", + "multiversx-sc-codec", + "multiversx-sc-derive", + "num-traits", +] + +[[package]] +name = "multiversx-sc-codec" +version = "0.18.1" +dependencies = [ + "arrayvec", + "multiversx-sc-codec-derive", +] + +[[package]] +name = "multiversx-sc-codec-derive" +version = "0.18.1" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multiversx-sc-derive" +version = "0.43.4" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "radix_trie", + "syn", +] + +[[package]] +name = "multiversx-sc-wasm-adapter" +version = "0.43.4" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/contracts/paymaster/wasm/Cargo.toml b/contracts/paymaster/wasm/Cargo.toml new file mode 100644 index 00000000..065dc288 --- /dev/null +++ b/contracts/paymaster/wasm/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "paymaster-wasm" +version = "0.0.0" +edition = "2018" +publish = false +authors = [ "you",] + +[lib] +crate-type = [ "cdylib",] + +[workspace] +members = [ ".",] + +[dev-dependencies] + +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = true +debug = false +panic = "abort" + +[dependencies.paymaster] +path = ".." + +[dependencies.multiversx-sc-wasm-adapter] +version = "0.43.4" diff --git a/contracts/paymaster/wasm/src/lib.rs b/contracts/paymaster/wasm/src/lib.rs new file mode 100644 index 00000000..8ea5e5e0 --- /dev/null +++ b/contracts/paymaster/wasm/src/lib.rs @@ -0,0 +1,26 @@ +// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Endpoints: 0 +// Async Callback (empty): 1 +// Total number of exported functions: 2 + +#![no_std] +#![allow(internal_features)] +#![feature(lang_items)] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + empty + ( + init => init + ) +} + +multiversx_sc_wasm_adapter::async_callback_empty! {} From 71b89a7cf2b1672ca974104a01852099bc4dad87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 27 Sep 2023 17:25:46 +0300 Subject: [PATCH 02/12] Remove Pair proxy code --- contracts/paymaster/src/fees.rs | 79 ---------------------------- contracts/paymaster/src/paymaster.rs | 29 +++++++--- 2 files changed, 23 insertions(+), 85 deletions(-) delete mode 100644 contracts/paymaster/src/fees.rs diff --git a/contracts/paymaster/src/fees.rs b/contracts/paymaster/src/fees.rs deleted file mode 100644 index 7781aacc..00000000 --- a/contracts/paymaster/src/fees.rs +++ /dev/null @@ -1,79 +0,0 @@ -use crate::forward_call::PaymentsVec; - -multiversx_sc::imports!(); - -pub mod pair_proxy { - #[multiversx_sc::proxy] - pub trait PairProxy { - #[view(getSafePriceByDefaultOffset)] - fn get_safe_price_by_default_offset( - &self, - pair_address: ManagedAddress, - input_payment: EsdtTokenPayment, - ) -> EsdtTokenPayment; - } -} - -#[multiversx_sc::module] -pub trait FeesModule { - #[only_owner] - #[endpoint(addAcceptedFeesTokens)] - fn add_accepted_fees_tokens( - &self, - accepted_tokens: MultiValueEncoded>, - ) { - for pair in accepted_tokens { - let (token_id, pair_address) = pair.into_tuple(); - require!(token_id.is_valid_esdt_identifier(), "Invalid token"); - - self.pair_address_for_token(&token_id).set(pair_address); - } - } - - fn get_price(&self, token_id: TokenIdentifier, amount: BigUint) -> BigUint { - let mapper = self.pair_address_for_token(&token_id); - require!( - !mapper.is_empty(), - "There is no pair addres for the token provided!" - ); - - let pair_address = mapper.get(); - let price_query_address = self.price_query_address().get(); - let price: EsdtTokenPayment = self - .pair_proxy(price_query_address) - .get_safe_price_by_default_offset( - pair_address, - EsdtTokenPayment::new(token_id, 0, amount), - ) - .execute_on_dest_context(); - - price.amount - } - - fn pay_fee_to_relayer(&self, relayer_addr: ManagedAddress, payments: PaymentsVec) { - let mut payments_iter = payments.iter(); - let fee_payment = match payments_iter.next() { - Some(fee) => fee, - None => sc_panic!("Fee payment is missing!"), - }; - - self.send().direct_esdt( - &relayer_addr, - &fee_payment.token_identifier, - 0, - &fee_payment.amount, - ); - } - - #[proxy] - fn pair_proxy(&self, sc_address: ManagedAddress) -> pair_proxy::Proxy; - - #[storage_mapper("pairAddressForToken")] - fn pair_address_for_token( - &self, - token_id: &TokenIdentifier, - ) -> SingleValueMapper; - - #[storage_mapper("priceQueryAddress")] - fn price_query_address(&self) -> SingleValueMapper; -} diff --git a/contracts/paymaster/src/paymaster.rs b/contracts/paymaster/src/paymaster.rs index b5f84b0d..33ea6a9f 100644 --- a/contracts/paymaster/src/paymaster.rs +++ b/contracts/paymaster/src/paymaster.rs @@ -1,17 +1,17 @@ #![no_std] +use forward_call::PaymentsVec; + multiversx_sc::imports!(); -pub mod fees; pub mod forward_call; +const FEE_PAYMENT: usize = 0; /// An empty contract. To be used as a template when starting a new contract from scratch. #[multiversx_sc::contract] -pub trait PaymasterContract: fees::FeesModule + forward_call::ForwardCall { +pub trait PaymasterContract: forward_call::ForwardCall { #[init] - fn init(&self, price_query_address: ManagedAddress) { - self.price_query_address().set(price_query_address); - } + fn init(&self) {} #[endpoint(forwardExecution)] #[payable("*")] @@ -25,8 +25,25 @@ pub trait PaymasterContract: fees::FeesModule + forward_call::ForwardCall { let payments = self.call_value().all_esdt_transfers(); self.pay_fee_to_relayer(relayer_addr, payments.clone_value()); + let mut payments_without_fee = payments.clone_value(); - payments_without_fee.remove(0); + payments_without_fee.remove(FEE_PAYMENT); + self.forward_call(dest, endpoint_name, endpoint_args, payments_without_fee); } + + fn pay_fee_to_relayer(&self, relayer_addr: ManagedAddress, payments: PaymentsVec) { + let mut payments_iter = payments.iter(); + let fee_payment = match payments_iter.next() { + Some(fee) => fee, + None => sc_panic!("Fee payment is missing!"), + }; + + self.send().direct_esdt( + &relayer_addr, + &fee_payment.token_identifier, + 0, + &fee_payment.amount, + ); + } } From 6e7ccabbcefaec76489bc5ab72d729e2937bb3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 28 Sep 2023 17:30:12 +0300 Subject: [PATCH 03/12] Fixes after review --- contracts/paymaster/src/forward_call.rs | 35 ++++++++++++++----------- contracts/paymaster/src/paymaster.rs | 24 +++++------------ 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/contracts/paymaster/src/forward_call.rs b/contracts/paymaster/src/forward_call.rs index 6f1f8a52..3c65824c 100644 --- a/contracts/paymaster/src/forward_call.rs +++ b/contracts/paymaster/src/forward_call.rs @@ -3,7 +3,7 @@ multiversx_sc::imports!(); pub type PaymentsVec = ManagedVec>; static ERR_CALLBACK_MSG: &[u8] = b"Error received in callback:"; - +pub const ESDT_TRANSFER_FUNC_NAME: &str = "ESDTTransfer"; #[multiversx_sc::module] pub trait ForwardCall { fn forward_call( @@ -15,31 +15,34 @@ pub trait ForwardCall { ) { let original_caller = self.blockchain().get_caller(); - if !self.blockchain().is_smart_contract(&dest) { - self.send().direct_multi(&dest, &payments); - } else { + let contract_call = if !self.blockchain().is_smart_contract(&dest) { let mut args_buffer = ManagedArgBuffer::new(); - for arg in endpoint_args { - args_buffer.push_arg(arg); - } + args_buffer.push_arg(endpoint_name); - ContractCallWithMultiEsdt::::new(dest, endpoint_name, payments.clone()) + self.send() + .contract_call::<()>(dest, ESDT_TRANSFER_FUNC_NAME.into()) .with_raw_arguments(args_buffer) - .async_call() - .with_callback( - self.callbacks() - .transfer_callback(original_caller, payments), - ) - .call_and_exit(); - } + } else { + self.send() + .contract_call::<()>(dest, endpoint_name) + .with_raw_arguments(endpoint_args.to_arg_buffer()) + }; + + contract_call + .with_multi_token_transfer(payments) + .async_call() + .with_callback(self.callbacks().transfer_callback(original_caller)) + .call_and_exit() } #[callback] fn transfer_callback( &self, original_caller: ManagedAddress, - initial_payments: ManagedVec>, + // initial_payments: ManagedVec>, #[call_result] result: ManagedAsyncCallResult>, ) -> MultiValueEncoded { + let initial_payments = self.call_value().all_esdt_transfers(); + match result { ManagedAsyncCallResult::Ok(return_values) => return_values, ManagedAsyncCallResult::Err(err) => { diff --git a/contracts/paymaster/src/paymaster.rs b/contracts/paymaster/src/paymaster.rs index 33ea6a9f..345295ac 100644 --- a/contracts/paymaster/src/paymaster.rs +++ b/contracts/paymaster/src/paymaster.rs @@ -1,7 +1,5 @@ #![no_std] -use forward_call::PaymentsVec; - multiversx_sc::imports!(); pub mod forward_call; @@ -23,27 +21,19 @@ pub trait PaymasterContract: forward_call::ForwardCall { endpoint_args: MultiValueEncoded, ) { let payments = self.call_value().all_esdt_transfers(); - - self.pay_fee_to_relayer(relayer_addr, payments.clone_value()); - - let mut payments_without_fee = payments.clone_value(); - payments_without_fee.remove(FEE_PAYMENT); + require!(!payments.is_empty(), "There is no fee for payment!"); - self.forward_call(dest, endpoint_name, endpoint_args, payments_without_fee); - } - - fn pay_fee_to_relayer(&self, relayer_addr: ManagedAddress, payments: PaymentsVec) { - let mut payments_iter = payments.iter(); - let fee_payment = match payments_iter.next() { - Some(fee) => fee, - None => sc_panic!("Fee payment is missing!"), - }; - + let fee_payment = payments.get(0).clone(); self.send().direct_esdt( &relayer_addr, &fee_payment.token_identifier, 0, &fee_payment.amount, ); + + let mut payments_without_fee = payments.clone_value(); + payments_without_fee.remove(FEE_PAYMENT); + + self.forward_call(dest, endpoint_name, endpoint_args, payments_without_fee); } } From b345a368b703a61afb2849e7a295e09cf35e8f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 28 Sep 2023 17:34:54 +0300 Subject: [PATCH 04/12] Refactor forward_call --- contracts/paymaster/src/forward_call.rs | 29 ++++++++++++------------- contracts/paymaster/src/paymaster.rs | 6 ++--- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/contracts/paymaster/src/forward_call.rs b/contracts/paymaster/src/forward_call.rs index 3c65824c..8a721324 100644 --- a/contracts/paymaster/src/forward_call.rs +++ b/contracts/paymaster/src/forward_call.rs @@ -15,24 +15,23 @@ pub trait ForwardCall { ) { let original_caller = self.blockchain().get_caller(); - let contract_call = if !self.blockchain().is_smart_contract(&dest) { - let mut args_buffer = ManagedArgBuffer::new(); - args_buffer.push_arg(endpoint_name); - - self.send() - .contract_call::<()>(dest, ESDT_TRANSFER_FUNC_NAME.into()) - .with_raw_arguments(args_buffer) - } else { - self.send() - .contract_call::<()>(dest, endpoint_name) - .with_raw_arguments(endpoint_args.to_arg_buffer()) - }; - - contract_call + let (contract_call_endpoint, contract_call_args) = + if !self.blockchain().is_smart_contract(&dest) { + let mut args_buffer = ManagedArgBuffer::new(); + args_buffer.push_arg(endpoint_name); + + (ESDT_TRANSFER_FUNC_NAME.into(), args_buffer) + } else { + (endpoint_name, endpoint_args.to_arg_buffer()) + }; + + self.send() + .contract_call::<()>(dest, contract_call_endpoint) + .with_raw_arguments(contract_call_args) .with_multi_token_transfer(payments) .async_call() .with_callback(self.callbacks().transfer_callback(original_caller)) - .call_and_exit() + .call_and_exit(); } #[callback] fn transfer_callback( diff --git a/contracts/paymaster/src/paymaster.rs b/contracts/paymaster/src/paymaster.rs index 345295ac..e989f204 100644 --- a/contracts/paymaster/src/paymaster.rs +++ b/contracts/paymaster/src/paymaster.rs @@ -22,8 +22,8 @@ pub trait PaymasterContract: forward_call::ForwardCall { ) { let payments = self.call_value().all_esdt_transfers(); require!(!payments.is_empty(), "There is no fee for payment!"); - - let fee_payment = payments.get(0).clone(); + + let fee_payment = payments.get(FEE_PAYMENT); self.send().direct_esdt( &relayer_addr, &fee_payment.token_identifier, @@ -33,7 +33,7 @@ pub trait PaymasterContract: forward_call::ForwardCall { let mut payments_without_fee = payments.clone_value(); payments_without_fee.remove(FEE_PAYMENT); - + self.forward_call(dest, endpoint_name, endpoint_args, payments_without_fee); } } From 79e9192dd74c0fb0262479b33d634119948b2e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 28 Sep 2023 18:48:12 +0300 Subject: [PATCH 05/12] Add blackbox test setup --- contracts/paymaster/Cargo.toml | 2 +- .../paymaster/tests/empty_scenario_go_test.rs | 10 --- .../paymaster/tests/empty_scenario_rs_test.rs | 14 ---- .../tests/paymaster_blackbox_test.rs | 84 +++++++++++++++++++ contracts/paymaster/wasm/Cargo.lock | 44 ++++++---- contracts/paymaster/wasm/src/lib.rs | 15 ++-- 6 files changed, 121 insertions(+), 48 deletions(-) delete mode 100644 contracts/paymaster/tests/empty_scenario_go_test.rs delete mode 100644 contracts/paymaster/tests/empty_scenario_rs_test.rs create mode 100644 contracts/paymaster/tests/paymaster_blackbox_test.rs diff --git a/contracts/paymaster/Cargo.toml b/contracts/paymaster/Cargo.toml index 5ec2cc83..dcf17029 100644 --- a/contracts/paymaster/Cargo.toml +++ b/contracts/paymaster/Cargo.toml @@ -15,4 +15,4 @@ num-bigint = "0.4.2" version = "0.43.4" [dev-dependencies.multiversx-sc-scenario] -version = "0.41.1" +version = "0.43.4" diff --git a/contracts/paymaster/tests/empty_scenario_go_test.rs b/contracts/paymaster/tests/empty_scenario_go_test.rs deleted file mode 100644 index ef0a7f21..00000000 --- a/contracts/paymaster/tests/empty_scenario_go_test.rs +++ /dev/null @@ -1,10 +0,0 @@ -use multiversx_sc_scenario::*; - -fn world() -> ScenarioWorld { - ScenarioWorld::vm_go() -} - -#[test] -fn empty_go() { - world().run("scenarios/empty.scen.json"); -} diff --git a/contracts/paymaster/tests/empty_scenario_rs_test.rs b/contracts/paymaster/tests/empty_scenario_rs_test.rs deleted file mode 100644 index e7cd14ce..00000000 --- a/contracts/paymaster/tests/empty_scenario_rs_test.rs +++ /dev/null @@ -1,14 +0,0 @@ -use multiversx_sc_scenario::*; - -fn world() -> ScenarioWorld { - let mut blockchain = ScenarioWorld::new(); - blockchain.set_current_dir_from_workspace("contracts/examples/empty"); - - blockchain.register_contract("file:output/paymaster.wasm", paymaster::ContractBuilder); - blockchain -} - -#[test] -fn empty_rs() { - world().run("scenarios/empty.scen.json"); -} diff --git a/contracts/paymaster/tests/paymaster_blackbox_test.rs b/contracts/paymaster/tests/paymaster_blackbox_test.rs new file mode 100644 index 00000000..53a12b98 --- /dev/null +++ b/contracts/paymaster/tests/paymaster_blackbox_test.rs @@ -0,0 +1,84 @@ +use multiversx_sc::types::Address; +use multiversx_sc_scenario::{api::StaticApi, *, scenario_model::{ScDeployStep, AddressValue, Account, SetStateStep}}; +use paymaster::ProxyTrait; + +const PAYMASTER_ADDRESS_EXPR: &str = "sc:paymaster"; +const RELAYER_ADDRESS_EXPR: &str = "sc:relayer"; +const CALLEE_SC_ADDRESS_EXPR: &str = "sc:callee_sc"; +const PAYMASTER_PATH_EXPR: &str = "file:output/paymaster.wasm"; +const CALLER_ADDRESS_EXPR: &str = "address:caller"; +const CALLEE_USER_ADDRESS_EXPR: &str = "address:callee_user"; +const OWNER_ADDRESS_EXPR: &str = "address:owner"; +const BALANCE: &str = "100,000,000"; + +type PaymasterContract = ContractInfo>; + +fn world() -> ScenarioWorld { + let mut blockchain = ScenarioWorld::new(); + blockchain.set_current_dir_from_workspace("contracts/examples/empty"); + + blockchain.register_contract("file:output/paymaster.wasm", paymaster::ContractBuilder); + blockchain +} + +struct PaymasterTestState { + world: ScenarioWorld, + caller_address: Address, + callee_user_address: Address, + paymaster_contract: PaymasterContract, + relayer_address: Address, + callee_sc_address: Address, +} + +impl PaymasterTestState { + fn new() -> Self { + let mut world = world(); + world.set_state_step( + SetStateStep::new() + .put_account(OWNER_ADDRESS_EXPR, Account::new().nonce(1)) + .put_account( + CALLER_ADDRESS_EXPR, + Account::new().nonce(1).balance(BALANCE), + ) + .put_account(CALLEE_USER_ADDRESS_EXPR, Account::new().nonce(1).balance(BALANCE)) + .new_address(OWNER_ADDRESS_EXPR, 1, PAYMASTER_ADDRESS_EXPR) + .new_address(OWNER_ADDRESS_EXPR, 1, RELAYER_ADDRESS_EXPR) + .new_address(OWNER_ADDRESS_EXPR, 1, CALLEE_SC_ADDRESS_EXPR), + ); + + let paymaster_contract = PaymasterContract::new(PAYMASTER_ADDRESS_EXPR); + + let caller_address = AddressValue::from(CALLER_ADDRESS_EXPR).to_address(); + let callee_user_address = AddressValue::from(CALLEE_USER_ADDRESS_EXPR).to_address(); + + let relayer_address = AddressValue::from(RELAYER_ADDRESS_EXPR).to_address(); + let callee_sc_address = AddressValue::from(CALLEE_SC_ADDRESS_EXPR).to_address(); + + Self { + world, + caller_address, + callee_user_address, + paymaster_contract, + relayer_address, + callee_sc_address, + } + } + + fn deploy_paymaster_contract(&mut self) -> &mut Self { + let paymaster_code = self.world.code_expression(PAYMASTER_PATH_EXPR); + + self.world.sc_deploy( + ScDeployStep::new() + .from(OWNER_ADDRESS_EXPR) + .code(paymaster_code) + .call(self.paymaster_contract.init()), + ); + self + } +} + +#[test] +fn test_deploy_paymaster() { + let mut state = PaymasterTestState::new(); + state.deploy_paymaster_contract(); +} diff --git a/contracts/paymaster/wasm/Cargo.lock b/contracts/paymaster/wasm/Cargo.lock index 5a9b6622..7f67dc92 100644 --- a/contracts/paymaster/wasm/Cargo.lock +++ b/contracts/paymaster/wasm/Cargo.lock @@ -37,21 +37,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "empty" -version = "0.0.0" -dependencies = [ - "multiversx-sc", -] - -[[package]] -name = "empty-wasm" -version = "0.0.0" -dependencies = [ - "empty", - "multiversx-sc-wasm-adapter", -] - [[package]] name = "endian-type" version = "0.1.2" @@ -82,6 +67,8 @@ checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" [[package]] name = "multiversx-sc" version = "0.43.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406939660d0c79dd191c6677f4b048df873a95f4531d8abafc9cdbe282bf1725" dependencies = [ "bitflags", "hashbrown", @@ -94,6 +81,8 @@ dependencies = [ [[package]] name = "multiversx-sc-codec" version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1e15b46c17b87c0c7cdd79b041a4abd7f3a2b45f3c993f6ce38c0f233e82b6" dependencies = [ "arrayvec", "multiversx-sc-codec-derive", @@ -102,6 +91,8 @@ dependencies = [ [[package]] name = "multiversx-sc-codec-derive" version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a7bc0762cd6d88f8bc54805bc652b042a61cd7fbc2d0a325010f088b78fb2ac" dependencies = [ "hex", "proc-macro2", @@ -112,6 +103,8 @@ dependencies = [ [[package]] name = "multiversx-sc-derive" version = "0.43.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e006240993963b482fe0682ae49b2d07255495e3c86706925d119137376cdfc" dependencies = [ "hex", "proc-macro2", @@ -123,6 +116,8 @@ dependencies = [ [[package]] name = "multiversx-sc-wasm-adapter" version = "0.43.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40e721d1bc80de2ede4099a9040519486c3c1139cb0287d8fc4f9fc3e8a3f19e" dependencies = [ "multiversx-sc", ] @@ -151,6 +146,21 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "paymaster" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "paymaster-wasm" +version = "0.0.0" +dependencies = [ + "multiversx-sc-wasm-adapter", + "paymaster", +] + [[package]] name = "proc-macro2" version = "1.0.67" @@ -181,9 +191,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "syn" diff --git a/contracts/paymaster/wasm/src/lib.rs b/contracts/paymaster/wasm/src/lib.rs index 8ea5e5e0..dc78bc29 100644 --- a/contracts/paymaster/wasm/src/lib.rs +++ b/contracts/paymaster/wasm/src/lib.rs @@ -5,22 +5,25 @@ //////////////////////////////////////////////////// // Init: 1 -// Endpoints: 0 -// Async Callback (empty): 1 -// Total number of exported functions: 2 +// Endpoints: 1 +// Async Callback: 1 +// Total number of exported functions: 3 #![no_std] -#![allow(internal_features)] + +// Configuration that works with rustc < 1.73.0. +// TODO: Recommended rustc version: 1.73.0 or newer. #![feature(lang_items)] multiversx_sc_wasm_adapter::allocator!(); multiversx_sc_wasm_adapter::panic_handler!(); multiversx_sc_wasm_adapter::endpoints! { - empty + paymaster ( init => init + forwardExecution => forward_execution ) } -multiversx_sc_wasm_adapter::async_callback_empty! {} +multiversx_sc_wasm_adapter::async_callback! { paymaster } From ef0289ec58605b7e9eaf90dd92120dc0f1f2bdcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 28 Sep 2023 18:52:09 +0300 Subject: [PATCH 06/12] Fix compilation --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 937ffe66..ee172748 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,6 @@ members = [ "contracts/liquid-locking/meta", "contracts/multisig", "contracts/multisig/meta", - "contracts/multisig/interact-rs", "contracts/mystery-box", "contracts/mystery-box/meta", "contracts/nft-minter", From 7dd9a998514f56bc567137eee4c0161409cab6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 29 Sep 2023 19:53:04 +0300 Subject: [PATCH 07/12] Paymaster tests --- contracts/paymaster/Cargo.toml | 3 + contracts/paymaster/src/forward_call.rs | 1 - .../tests/paymaster_blackbox_test.rs | 202 +++++++++++++++--- .../paymaster/tests/test-contracts/adder.wasm | Bin 0 -> 685 bytes 4 files changed, 178 insertions(+), 28 deletions(-) create mode 100755 contracts/paymaster/tests/test-contracts/adder.wasm diff --git a/contracts/paymaster/Cargo.toml b/contracts/paymaster/Cargo.toml index dcf17029..1f21e569 100644 --- a/contracts/paymaster/Cargo.toml +++ b/contracts/paymaster/Cargo.toml @@ -16,3 +16,6 @@ version = "0.43.4" [dev-dependencies.multiversx-sc-scenario] version = "0.43.4" + +[dev-dependencies.adder] +path = "../adder" diff --git a/contracts/paymaster/src/forward_call.rs b/contracts/paymaster/src/forward_call.rs index 8a721324..d230012a 100644 --- a/contracts/paymaster/src/forward_call.rs +++ b/contracts/paymaster/src/forward_call.rs @@ -37,7 +37,6 @@ pub trait ForwardCall { fn transfer_callback( &self, original_caller: ManagedAddress, - // initial_payments: ManagedVec>, #[call_result] result: ManagedAsyncCallResult>, ) -> MultiValueEncoded { let initial_payments = self.call_value().all_esdt_transfers(); diff --git a/contracts/paymaster/tests/paymaster_blackbox_test.rs b/contracts/paymaster/tests/paymaster_blackbox_test.rs index 53a12b98..8d3bf45d 100644 --- a/contracts/paymaster/tests/paymaster_blackbox_test.rs +++ b/contracts/paymaster/tests/paymaster_blackbox_test.rs @@ -1,84 +1,232 @@ -use multiversx_sc::types::Address; -use multiversx_sc_scenario::{api::StaticApi, *, scenario_model::{ScDeployStep, AddressValue, Account, SetStateStep}}; -use paymaster::ProxyTrait; +use multiversx_sc::{ + codec::{multi_types::MultiValueVec, top_encode_to_vec_u8_or_panic}, + storage::mappers::SingleValue, + types::{Address, BigUint}, +}; +use multiversx_sc_scenario::{ + api::StaticApi, + scenario_model::{ + Account, AddressValue, CheckAccount, CheckStateStep, ScCallStep, ScDeployStep, ScQueryStep, + SetStateStep, TxExpect, + }, + *, +}; + +use adder::ProxyTrait as _; +use paymaster::ProxyTrait as _; const PAYMASTER_ADDRESS_EXPR: &str = "sc:paymaster"; -const RELAYER_ADDRESS_EXPR: &str = "sc:relayer"; -const CALLEE_SC_ADDRESS_EXPR: &str = "sc:callee_sc"; +const RELAYER_ADDRESS_EXPR: &str = "address:relayer"; +const CALLEE_SC_ADDER_ADDRESS_EXPR: &str = "sc:adder"; const PAYMASTER_PATH_EXPR: &str = "file:output/paymaster.wasm"; +const ADDER_PATH_EXPR: &str = "file:tests/test-contracts/adder.wasm"; const CALLER_ADDRESS_EXPR: &str = "address:caller"; const CALLEE_USER_ADDRESS_EXPR: &str = "address:callee_user"; const OWNER_ADDRESS_EXPR: &str = "address:owner"; const BALANCE: &str = "100,000,000"; +const PAYMASTER_TOKEN_ID_EXPR: &str = "str:PAYMSTR-123456"; +const FEE_TOKEN_ID_EXPR: &str = "str:FEE-123456"; +const FEE_AMOUNT: &str = "20,000"; +const INITIAL_ADD_VALUE: u64 = 5; +const ADDITIONAL_ADD_VALUE: u64 = 5; + + type PaymasterContract = ContractInfo>; +type AdderContract = ContractInfo>; fn world() -> ScenarioWorld { let mut blockchain = ScenarioWorld::new(); - blockchain.set_current_dir_from_workspace("contracts/examples/empty"); + blockchain.set_current_dir_from_workspace("contracts/paymaster"); + + blockchain.register_contract(PAYMASTER_PATH_EXPR, paymaster::ContractBuilder); + blockchain.register_contract(ADDER_PATH_EXPR, adder::ContractBuilder); - blockchain.register_contract("file:output/paymaster.wasm", paymaster::ContractBuilder); blockchain } struct PaymasterTestState { world: ScenarioWorld, - caller_address: Address, callee_user_address: Address, paymaster_contract: PaymasterContract, relayer_address: Address, - callee_sc_address: Address, + callee_sc_adder_contract: AdderContract, } impl PaymasterTestState { fn new() -> Self { let mut world = world(); - world.set_state_step( + world.start_trace().set_state_step( SetStateStep::new() .put_account(OWNER_ADDRESS_EXPR, Account::new().nonce(1)) .put_account( CALLER_ADDRESS_EXPR, + Account::new() + .nonce(1) + .balance(BALANCE) + .esdt_balance(PAYMASTER_TOKEN_ID_EXPR, BALANCE) + .esdt_balance(FEE_TOKEN_ID_EXPR, BALANCE), + ) + .put_account( + CALLEE_USER_ADDRESS_EXPR, Account::new().nonce(1).balance(BALANCE), ) - .put_account(CALLEE_USER_ADDRESS_EXPR, Account::new().nonce(1).balance(BALANCE)) - .new_address(OWNER_ADDRESS_EXPR, 1, PAYMASTER_ADDRESS_EXPR) - .new_address(OWNER_ADDRESS_EXPR, 1, RELAYER_ADDRESS_EXPR) - .new_address(OWNER_ADDRESS_EXPR, 1, CALLEE_SC_ADDRESS_EXPR), + .put_account(RELAYER_ADDRESS_EXPR, Account::new().nonce(1).balance(0u32)), ); - let paymaster_contract = PaymasterContract::new(PAYMASTER_ADDRESS_EXPR); - - let caller_address = AddressValue::from(CALLER_ADDRESS_EXPR).to_address(); let callee_user_address = AddressValue::from(CALLEE_USER_ADDRESS_EXPR).to_address(); let relayer_address = AddressValue::from(RELAYER_ADDRESS_EXPR).to_address(); - let callee_sc_address = AddressValue::from(CALLEE_SC_ADDRESS_EXPR).to_address(); + let paymaster_contract = PaymasterContract::new(PAYMASTER_ADDRESS_EXPR); + let callee_sc_adder_contract = AdderContract::new(CALLEE_SC_ADDER_ADDRESS_EXPR); + // let callee_sc_adder_address = AddressValue::from(CALLEE_SC_ADDER_ADDRESS_EXPR).to_address(); Self { world, - caller_address, callee_user_address, paymaster_contract, relayer_address, - callee_sc_address, + callee_sc_adder_contract, } } fn deploy_paymaster_contract(&mut self) -> &mut Self { let paymaster_code = self.world.code_expression(PAYMASTER_PATH_EXPR); - self.world.sc_deploy( - ScDeployStep::new() - .from(OWNER_ADDRESS_EXPR) - .code(paymaster_code) - .call(self.paymaster_contract.init()), - ); + self.world + .set_state_step(SetStateStep::new().new_address( + OWNER_ADDRESS_EXPR, + 1, + PAYMASTER_ADDRESS_EXPR, + )) + .sc_deploy( + ScDeployStep::new() + .from(OWNER_ADDRESS_EXPR) + .code(paymaster_code) + .call(self.paymaster_contract.init()), + ); + + self + } + + fn deploy_adder_contract(&mut self) -> &mut Self { + let adder_code = self.world.code_expression(ADDER_PATH_EXPR); + + self.world + .set_state_step(SetStateStep::new().new_address( + OWNER_ADDRESS_EXPR, + 2, + CALLEE_SC_ADDER_ADDRESS_EXPR, + )) + .sc_deploy( + ScDeployStep::new() + .from(OWNER_ADDRESS_EXPR) + .code(adder_code) + .call(self.callee_sc_adder_contract.init(INITIAL_ADD_VALUE)), + ); + + self + } + + fn check_esdt_balance( + &mut self, + address_expr: &str, + token_id_expr: &str, + balance_expr: &str, + ) -> &mut Self { + self.world + .check_state_step(CheckStateStep::new().put_account( + address_expr, + CheckAccount::new().esdt_balance(token_id_expr, balance_expr), + )); + self } } #[test] -fn test_deploy_paymaster() { +fn test_deploy_paymasters() { let mut state = PaymasterTestState::new(); state.deploy_paymaster_contract(); + state.deploy_adder_contract(); +} + +#[test] +fn test_forward_call_no_fee_payment() { + let mut state = PaymasterTestState::new(); + state.deploy_paymaster_contract(); + + state.world.sc_call( + ScCallStep::new() + .from(CALLER_ADDRESS_EXPR) + .call(state.paymaster_contract.forward_execution( + state.relayer_address.clone(), + state.callee_user_address.clone(), + managed_buffer!(b"add"), + MultiValueVec::>::new(), + )) + .expect(TxExpect::user_error("str:There is no fee for payment!")), + ); +} + +#[test] +fn test_forward_call_user() { + let mut state = PaymasterTestState::new(); + state.deploy_paymaster_contract(); + + state + .world + .sc_call( + ScCallStep::new() + .from(CALLER_ADDRESS_EXPR) + .call(state.paymaster_contract.forward_execution( + state.relayer_address.clone(), + state.callee_user_address.clone(), + managed_buffer!(b"add"), + MultiValueVec::>::new(), + )) + .esdt_transfer(FEE_TOKEN_ID_EXPR, 0, FEE_AMOUNT) + .expect(TxExpect::ok()), + ) + .check_state_step(CheckStateStep::new().put_account( + RELAYER_ADDRESS_EXPR, + CheckAccount::new().esdt_balance(FEE_TOKEN_ID_EXPR, FEE_AMOUNT), + )); +} + +#[test] +fn test_forward_call_sc_adder() { + let mut state = PaymasterTestState::new(); + state.deploy_paymaster_contract(); + state.deploy_adder_contract(); + + state.check_esdt_balance(CALLER_ADDRESS_EXPR, FEE_TOKEN_ID_EXPR, BALANCE); + state.check_esdt_balance(CALLER_ADDRESS_EXPR, PAYMASTER_TOKEN_ID_EXPR, BALANCE); + + state.world.sc_call( + ScCallStep::new() + .from(CALLER_ADDRESS_EXPR) + .esdt_transfer(FEE_TOKEN_ID_EXPR, 0, FEE_AMOUNT) + .esdt_transfer(PAYMASTER_TOKEN_ID_EXPR, 0, FEE_AMOUNT) + .call(state.paymaster_contract.forward_execution( + state.relayer_address.clone(), + state.callee_sc_adder_contract.to_address(), + managed_buffer!(b"add"), + MultiValueVec::from([top_encode_to_vec_u8_or_panic(&ADDITIONAL_ADD_VALUE)]), + )) + .expect(TxExpect::ok()), + ); + + let expected_adder_sum = INITIAL_ADD_VALUE + ADDITIONAL_ADD_VALUE; + state.world.sc_query( + ScQueryStep::new() + .call(state.callee_sc_adder_contract.sum()) + .expect_value(SingleValue::from(BigUint::from(expected_adder_sum))), + ); + state.check_esdt_balance(RELAYER_ADDRESS_EXPR, FEE_TOKEN_ID_EXPR, FEE_AMOUNT); + state.check_esdt_balance( + CALLEE_SC_ADDER_ADDRESS_EXPR, + PAYMASTER_TOKEN_ID_EXPR, + FEE_AMOUNT, + ); } diff --git a/contracts/paymaster/tests/test-contracts/adder.wasm b/contracts/paymaster/tests/test-contracts/adder.wasm new file mode 100755 index 0000000000000000000000000000000000000000..6e7ea0236e3b763a917d119dd2725703e4cb316f GIT binary patch literal 685 zcmZuvO^?$s5S_8JX*OmPv#mf~aR@!Zi5urlC5n*H3Tfe-xQ&}uZDQmkpq|p7uver# z@N@WA#ZJt8HG&EY^8b$g&1%`7oFDT~nOF3ecj=O2z(z)=Kxf>tf!l*HYitN{d{6 zRw4spr7s8KtD1L}&SiRbI@&kNBkTF0mK9vsYQbtFSGzm)MI5>r zz=D&0#>KYWR-32#j-@VpD$X)+Df1DX5w)8%ZV1#3Nel7sqsNA8v>3UCER;SFaEx6I z&t0>i`=)?vDvZh4TZ#RCE>=63_(_t9S|qZ}D6k~iNbx;cib_)S|DYcrwlVN$GvE*b zo7(X4=-KZLff#o)cuy=d0ge$YoZFDaMK7Sky=M$_>rp0tjCVUoX6AY^cSE_s22bL literal 0 HcmV?d00001 From 41570b9a10f3254a80850aef07f44ed46c4b5552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 29 Sep 2023 19:58:38 +0300 Subject: [PATCH 08/12] Paymaster: Add adder dependecies --- contracts/adder/Cargo.toml | 4 ++-- contracts/adder/meta/Cargo.toml | 2 +- contracts/adder/src/adder.rs | 1 + contracts/adder/wasm/Cargo.lock | 28 ++++++++++++++-------------- contracts/adder/wasm/Cargo.toml | 2 +- contracts/adder/wasm/src/lib.rs | 10 +++++++--- contracts/paymaster/Cargo.toml | 4 +++- contracts/paymaster/README.md | 0 8 files changed, 29 insertions(+), 22 deletions(-) create mode 100644 contracts/paymaster/README.md diff --git a/contracts/adder/Cargo.toml b/contracts/adder/Cargo.toml index c852d498..eaae18d9 100644 --- a/contracts/adder/Cargo.toml +++ b/contracts/adder/Cargo.toml @@ -8,6 +8,6 @@ publish = false [lib] path = "src/adder.rs" [dependencies.multiversx-sc] -version = "0.41.3" +version = "0.43.4" [dev-dependencies.multiversx-sc-scenario] -version = "0.41.3" +version = "0.43.4" diff --git a/contracts/adder/meta/Cargo.toml b/contracts/adder/meta/Cargo.toml index 6ed8ddd5..657af6ce 100644 --- a/contracts/adder/meta/Cargo.toml +++ b/contracts/adder/meta/Cargo.toml @@ -7,4 +7,4 @@ publish = false path = ".." [dependencies.multiversx-sc-meta] -version = "0.41.3" +version = "0.43.4" diff --git a/contracts/adder/src/adder.rs b/contracts/adder/src/adder.rs index f7c47ab0..7357ddb1 100644 --- a/contracts/adder/src/adder.rs +++ b/contracts/adder/src/adder.rs @@ -16,6 +16,7 @@ pub trait Adder { } /// Add desired amount to the storage variable. + #[payable("*")] #[endpoint] fn add(&self, value: BigUint) { self.sum().update(|sum| *sum += value); diff --git a/contracts/adder/wasm/Cargo.lock b/contracts/adder/wasm/Cargo.lock index ecfb40e6..77c5b7bf 100755 --- a/contracts/adder/wasm/Cargo.lock +++ b/contracts/adder/wasm/Cargo.lock @@ -81,9 +81,9 @@ checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" [[package]] name = "multiversx-sc" -version = "0.41.3" +version = "0.43.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfbe80ec68fedf299dd65469f8999cf3c0f884ffa497122428f08303bcb8b884" +checksum = "406939660d0c79dd191c6677f4b048df873a95f4531d8abafc9cdbe282bf1725" dependencies = [ "bitflags", "hashbrown", @@ -95,9 +95,9 @@ dependencies = [ [[package]] name = "multiversx-sc-codec" -version = "0.17.2" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d70ea458247d263b7e9fdfb207530b6a530546247139c162450e515c013a18" +checksum = "0f1e15b46c17b87c0c7cdd79b041a4abd7f3a2b45f3c993f6ce38c0f233e82b6" dependencies = [ "arrayvec", "multiversx-sc-codec-derive", @@ -105,9 +105,9 @@ dependencies = [ [[package]] name = "multiversx-sc-codec-derive" -version = "0.17.2" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad6920f80fda67fc60fd51aaa9f83ec7e069470f60a068c81205f9da5d05a30" +checksum = "9a7bc0762cd6d88f8bc54805bc652b042a61cd7fbc2d0a325010f088b78fb2ac" dependencies = [ "hex", "proc-macro2", @@ -117,9 +117,9 @@ dependencies = [ [[package]] name = "multiversx-sc-derive" -version = "0.41.3" +version = "0.43.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c2b87d817f2176bf1830bef938884f24267516c334a377e71a96bc18ebb09c1" +checksum = "9e006240993963b482fe0682ae49b2d07255495e3c86706925d119137376cdfc" dependencies = [ "hex", "proc-macro2", @@ -130,9 +130,9 @@ dependencies = [ [[package]] name = "multiversx-sc-wasm-adapter" -version = "0.41.3" +version = "0.43.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "932327626de71eeb1dbe86b863cc70cc532f8ceba8e73a059410f6aa56b22a77" +checksum = "40e721d1bc80de2ede4099a9040519486c3c1139cb0287d8fc4f9fc3e8a3f19e" dependencies = [ "multiversx-sc", ] @@ -163,18 +163,18 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.28" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] diff --git a/contracts/adder/wasm/Cargo.toml b/contracts/adder/wasm/Cargo.toml index ffbc5210..b760d3d1 100644 --- a/contracts/adder/wasm/Cargo.toml +++ b/contracts/adder/wasm/Cargo.toml @@ -17,7 +17,7 @@ panic = "abort" path = ".." [dependencies.multiversx-sc-wasm-adapter] -version = "0.41.3" +version = "0.43.4" [workspace] members = ["."] diff --git a/contracts/adder/wasm/src/lib.rs b/contracts/adder/wasm/src/lib.rs index d14d54c5..9fef0547 100644 --- a/contracts/adder/wasm/src/lib.rs +++ b/contracts/adder/wasm/src/lib.rs @@ -10,6 +10,9 @@ // Total number of exported functions: 4 #![no_std] + +// Configuration that works with rustc < 1.73.0. +// TODO: Recommended rustc version: 1.73.0 or newer. #![feature(lang_items)] multiversx_sc_wasm_adapter::allocator!(); @@ -18,9 +21,10 @@ multiversx_sc_wasm_adapter::panic_handler!(); multiversx_sc_wasm_adapter::endpoints! { adder ( - getSum - add + init => init + getSum => sum + add => add ) } -multiversx_sc_wasm_adapter::empty_callback! {} +multiversx_sc_wasm_adapter::async_callback_empty! {} diff --git a/contracts/paymaster/Cargo.toml b/contracts/paymaster/Cargo.toml index 1f21e569..0db9c936 100644 --- a/contracts/paymaster/Cargo.toml +++ b/contracts/paymaster/Cargo.toml @@ -1,9 +1,11 @@ [package] name = "paymaster" version = "0.0.0" -authors = [ "you",] +authors = [ "MultiversX " ] edition = "2018" publish = false +readme = "README.md" + [lib] path = "src/paymaster.rs" diff --git a/contracts/paymaster/README.md b/contracts/paymaster/README.md new file mode 100644 index 00000000..e69de29b From f52d822fda8b8df5f2df5ace748c7976ab73b1f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 29 Sep 2023 20:28:05 +0300 Subject: [PATCH 09/12] Paymaster: Add README + 1 test --- contracts/paymaster/README.md | 28 +++++++++++ .../tests/paymaster_blackbox_test.rs | 48 ++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/contracts/paymaster/README.md b/contracts/paymaster/README.md index e69de29b..f1b9e07d 100644 --- a/contracts/paymaster/README.md +++ b/contracts/paymaster/README.md @@ -0,0 +1,28 @@ +# Paymaster SC + +## Overview + +Paymaster is a SC that makes relayed transactions sustainable. +This means that a user who doesn't own EGLD (native token) can make transactions by paying a fee in another token. + +The Paymaster's objective is twofold: +- take a fee and send it to the Relayers; +- execute what the user wants to be executed. + +## Implementation + +The contract has only one endpoint: `forward_execution` and can be called by anyone. +The user will use `MultiESDTNFTTransfer` support to send multiple payments: +- first payment is always the fee that will be sent to the Relayer; +- rest of the payments will be what users want to send. + +One example of userTX is: +``` +MultiESDTNFTTransfer@paymasterSCAddr@feeTokenID@nonce@value@listofOther(tokenID,nonce,value)@forwardExecution@relayerAddr@destination@endpoint@extraArguments + +``` + +After sending the Relayer the fee, `forward_execution` endpoint will make an *asynchronous call* to the destination. +The destionation can be a user or a smart contract. + +We register a callback to the *asynchronous call*. In case of failure the paymaster SC sends the tokens back to the user. \ No newline at end of file diff --git a/contracts/paymaster/tests/paymaster_blackbox_test.rs b/contracts/paymaster/tests/paymaster_blackbox_test.rs index 8d3bf45d..ee961976 100644 --- a/contracts/paymaster/tests/paymaster_blackbox_test.rs +++ b/contracts/paymaster/tests/paymaster_blackbox_test.rs @@ -26,6 +26,7 @@ const OWNER_ADDRESS_EXPR: &str = "address:owner"; const BALANCE: &str = "100,000,000"; const PAYMASTER_TOKEN_ID_EXPR: &str = "str:PAYMSTR-123456"; const FEE_TOKEN_ID_EXPR: &str = "str:FEE-123456"; +const ADDITIONAL_TOKEN_ID_EXPR: &str = "str:ADDIT-123456"; const FEE_AMOUNT: &str = "20,000"; const INITIAL_ADD_VALUE: u64 = 5; const ADDITIONAL_ADD_VALUE: u64 = 5; @@ -65,7 +66,8 @@ impl PaymasterTestState { .nonce(1) .balance(BALANCE) .esdt_balance(PAYMASTER_TOKEN_ID_EXPR, BALANCE) - .esdt_balance(FEE_TOKEN_ID_EXPR, BALANCE), + .esdt_balance(FEE_TOKEN_ID_EXPR, BALANCE) + .esdt_balance(ADDITIONAL_TOKEN_ID_EXPR, BALANCE), ) .put_account( CALLEE_USER_ADDRESS_EXPR, @@ -230,3 +232,47 @@ fn test_forward_call_sc_adder() { FEE_AMOUNT, ); } + + +#[test] +fn test_forward_call_sc_adder_multiple_payments() { + let mut state = PaymasterTestState::new(); + state.deploy_paymaster_contract(); + state.deploy_adder_contract(); + + state.check_esdt_balance(CALLER_ADDRESS_EXPR, FEE_TOKEN_ID_EXPR, BALANCE); + state.check_esdt_balance(CALLER_ADDRESS_EXPR, PAYMASTER_TOKEN_ID_EXPR, BALANCE); + + state.world.sc_call( + ScCallStep::new() + .from(CALLER_ADDRESS_EXPR) + .esdt_transfer(FEE_TOKEN_ID_EXPR, 0, FEE_AMOUNT) + .esdt_transfer(PAYMASTER_TOKEN_ID_EXPR, 0, FEE_AMOUNT) + .esdt_transfer(ADDITIONAL_TOKEN_ID_EXPR, 0, FEE_AMOUNT) + .call(state.paymaster_contract.forward_execution( + state.relayer_address.clone(), + state.callee_sc_adder_contract.to_address(), + managed_buffer!(b"add"), + MultiValueVec::from([top_encode_to_vec_u8_or_panic(&ADDITIONAL_ADD_VALUE)]), + )) + .expect(TxExpect::ok()), + ); + + let expected_adder_sum = INITIAL_ADD_VALUE + ADDITIONAL_ADD_VALUE; + state.world.sc_query( + ScQueryStep::new() + .call(state.callee_sc_adder_contract.sum()) + .expect_value(SingleValue::from(BigUint::from(expected_adder_sum))), + ); + state.check_esdt_balance(RELAYER_ADDRESS_EXPR, FEE_TOKEN_ID_EXPR, FEE_AMOUNT); + state.check_esdt_balance( + CALLEE_SC_ADDER_ADDRESS_EXPR, + PAYMASTER_TOKEN_ID_EXPR, + FEE_AMOUNT, + ); + state.check_esdt_balance( + CALLEE_SC_ADDER_ADDRESS_EXPR, + ADDITIONAL_TOKEN_ID_EXPR, + FEE_AMOUNT, + ); +} From a3100e448875e76a374c4d5fe667c3fd1ba3c096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 29 Sep 2023 20:41:18 +0300 Subject: [PATCH 10/12] Fixes after review --- contracts/paymaster/src/forward_call.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/contracts/paymaster/src/forward_call.rs b/contracts/paymaster/src/forward_call.rs index d230012a..5c29b644 100644 --- a/contracts/paymaster/src/forward_call.rs +++ b/contracts/paymaster/src/forward_call.rs @@ -15,24 +15,15 @@ pub trait ForwardCall { ) { let original_caller = self.blockchain().get_caller(); - let (contract_call_endpoint, contract_call_args) = - if !self.blockchain().is_smart_contract(&dest) { - let mut args_buffer = ManagedArgBuffer::new(); - args_buffer.push_arg(endpoint_name); - - (ESDT_TRANSFER_FUNC_NAME.into(), args_buffer) - } else { - (endpoint_name, endpoint_args.to_arg_buffer()) - }; - self.send() - .contract_call::<()>(dest, contract_call_endpoint) - .with_raw_arguments(contract_call_args) + .contract_call::<()>(dest, endpoint_name) + .with_raw_arguments(endpoint_args.to_arg_buffer()) .with_multi_token_transfer(payments) .async_call() .with_callback(self.callbacks().transfer_callback(original_caller)) .call_and_exit(); } + #[callback] fn transfer_callback( &self, From 56675a9458f174165f4d9f93eab6f73199153c19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 29 Sep 2023 21:05:10 +0300 Subject: [PATCH 11/12] Improve README --- contracts/paymaster/README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/contracts/paymaster/README.md b/contracts/paymaster/README.md index f1b9e07d..3b4772bd 100644 --- a/contracts/paymaster/README.md +++ b/contracts/paymaster/README.md @@ -2,6 +2,19 @@ ## Overview +**Definition of problem**: the user needs to have a gas token (eGLD in case of MultiversX) to be able to do anything on the blockchain. +This is a hard concept to explain to non-crypto users. +Also gives some headaches for crypto users as well for the first steps of onboarding. +Imagine those people who bridge their NFTs or those who bridge USDC to a new account. +The new account can’t do anything until it does not have some eGLD to pay for the transactions. + +In order for users to onboard faster to xPortal (even when it was called Maiar) we introduced the concept of relayed transactions (we have v1, v2 and soon v3 of it). +This means a relayer can wrap the user transaction inside a relayedTX and in this case the relayer pays for the gas, but the user’s transaction is getting executed. +Right now, the relayers we use are free of charge, thus they do this service for new users for free, but they actually need eGLD to do the transactions. +When we speak about a few hundred users, this is not an issue, but when you want to scale up relaying transactions to thousands/millions of users, it becomes unsustainable. + +## Implementation + Paymaster is a SC that makes relayed transactions sustainable. This means that a user who doesn't own EGLD (native token) can make transactions by paying a fee in another token. @@ -9,7 +22,6 @@ The Paymaster's objective is twofold: - take a fee and send it to the Relayers; - execute what the user wants to be executed. -## Implementation The contract has only one endpoint: `forward_execution` and can be called by anyone. The user will use `MultiESDTNFTTransfer` support to send multiple payments: From 0c3d5a8564c6b3f5eb9cec474bd2c67a6bb56c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 2 Oct 2023 13:05:48 +0300 Subject: [PATCH 12/12] Fixes after review --- contracts/paymaster/README.md | 2 +- contracts/paymaster/tests/paymaster_blackbox_test.rs | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/contracts/paymaster/README.md b/contracts/paymaster/README.md index 3b4772bd..af4fb248 100644 --- a/contracts/paymaster/README.md +++ b/contracts/paymaster/README.md @@ -8,7 +8,7 @@ Also gives some headaches for crypto users as well for the first steps of onboar Imagine those people who bridge their NFTs or those who bridge USDC to a new account. The new account can’t do anything until it does not have some eGLD to pay for the transactions. -In order for users to onboard faster to xPortal (even when it was called Maiar) we introduced the concept of relayed transactions (we have v1, v2 and soon v3 of it). +In order for users to onboard faster to xPortal we introduced the concept of relayed transactions (we have v1, v2 and soon v3 of it). This means a relayer can wrap the user transaction inside a relayedTX and in this case the relayer pays for the gas, but the user’s transaction is getting executed. Right now, the relayers we use are free of charge, thus they do this service for new users for free, but they actually need eGLD to do the transactions. When we speak about a few hundred users, this is not an issue, but when you want to scale up relaying transactions to thousands/millions of users, it becomes unsustainable. diff --git a/contracts/paymaster/tests/paymaster_blackbox_test.rs b/contracts/paymaster/tests/paymaster_blackbox_test.rs index ee961976..65b3f0b6 100644 --- a/contracts/paymaster/tests/paymaster_blackbox_test.rs +++ b/contracts/paymaster/tests/paymaster_blackbox_test.rs @@ -164,7 +164,7 @@ fn test_forward_call_no_fee_payment() { .call(state.paymaster_contract.forward_execution( state.relayer_address.clone(), state.callee_user_address.clone(), - managed_buffer!(b"add"), + b"add" , MultiValueVec::>::new(), )) .expect(TxExpect::user_error("str:There is no fee for payment!")), @@ -184,11 +184,10 @@ fn test_forward_call_user() { .call(state.paymaster_contract.forward_execution( state.relayer_address.clone(), state.callee_user_address.clone(), - managed_buffer!(b"add"), + b"add", MultiValueVec::>::new(), )) .esdt_transfer(FEE_TOKEN_ID_EXPR, 0, FEE_AMOUNT) - .expect(TxExpect::ok()), ) .check_state_step(CheckStateStep::new().put_account( RELAYER_ADDRESS_EXPR, @@ -213,10 +212,9 @@ fn test_forward_call_sc_adder() { .call(state.paymaster_contract.forward_execution( state.relayer_address.clone(), state.callee_sc_adder_contract.to_address(), - managed_buffer!(b"add"), + b"add", MultiValueVec::from([top_encode_to_vec_u8_or_panic(&ADDITIONAL_ADD_VALUE)]), )) - .expect(TxExpect::ok()), ); let expected_adder_sum = INITIAL_ADD_VALUE + ADDITIONAL_ADD_VALUE; @@ -252,10 +250,9 @@ fn test_forward_call_sc_adder_multiple_payments() { .call(state.paymaster_contract.forward_execution( state.relayer_address.clone(), state.callee_sc_adder_contract.to_address(), - managed_buffer!(b"add"), + b"add", MultiValueVec::from([top_encode_to_vec_u8_or_panic(&ADDITIONAL_ADD_VALUE)]), )) - .expect(TxExpect::ok()), ); let expected_adder_sum = INITIAL_ADD_VALUE + ADDITIONAL_ADD_VALUE;