Skip to content

Commit

Permalink
Paymaster SC
Browse files Browse the repository at this point in the history
  • Loading branch information
CostinCarabas committed Sep 27, 2023
1 parent 3f65acd commit 6e30711
Show file tree
Hide file tree
Showing 16 changed files with 562 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 7 additions & 0 deletions contracts/paymaster/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Generated by Cargo
# will have compiled files and executables
/target/
*/target/

# The mxpy output
/output*/
18 changes: 18 additions & 0 deletions contracts/paymaster/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
14 changes: 14 additions & 0 deletions contracts/paymaster/meta/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
3 changes: 3 additions & 0 deletions contracts/paymaster/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::<paymaster::AbiProvider>();
}
3 changes: 3 additions & 0 deletions contracts/paymaster/multiversx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"language": "rust"
}
18 changes: 18 additions & 0 deletions contracts/paymaster/mxsc-template.toml
Original file line number Diff line number Diff line change
@@ -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",
]
39 changes: 39 additions & 0 deletions contracts/paymaster/scenarios/empty.scen.json
Original file line number Diff line number Diff line change
@@ -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": "*"
}
}
]
}
79 changes: 79 additions & 0 deletions contracts/paymaster/src/fees.rs
Original file line number Diff line number Diff line change
@@ -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<MultiValue2<TokenIdentifier, ManagedAddress>>,
) {
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<Self::Api>) {
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<Self::Api>;

#[storage_mapper("pairAddressForToken")]
fn pair_address_for_token(
&self,
token_id: &TokenIdentifier,
) -> SingleValueMapper<ManagedAddress>;

#[storage_mapper("priceQueryAddress")]
fn price_query_address(&self) -> SingleValueMapper<ManagedAddress>;
}
61 changes: 61 additions & 0 deletions contracts/paymaster/src/forward_call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
multiversx_sc::imports!();

pub type PaymentsVec<M> = ManagedVec<M, EsdtTokenPayment<M>>;

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<ManagedBuffer>,
payments: PaymentsVec<Self::Api>,
) {
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::<Self::Api, ()>::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<EsdtTokenPayment<Self::Api>>,
#[call_result] result: ManagedAsyncCallResult<MultiValueEncoded<ManagedBuffer>>,
) -> MultiValueEncoded<ManagedBuffer> {
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
}
}
}
}
32 changes: 32 additions & 0 deletions contracts/paymaster/src/paymaster.rs
Original file line number Diff line number Diff line change
@@ -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<ManagedBuffer>,
) {
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);
}
}
10 changes: 10 additions & 0 deletions contracts/paymaster/tests/empty_scenario_go_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use multiversx_sc_scenario::*;

fn world() -> ScenarioWorld {
ScenarioWorld::vm_go()

Check failure on line 4 in contracts/paymaster/tests/empty_scenario_go_test.rs

View workflow job for this annotation

GitHub Actions / Contracts / Rust tests

no function or associated item named `vm_go` found for struct `multiversx_sc_scenario::ScenarioWorld` in the current scope

Check failure on line 4 in contracts/paymaster/tests/empty_scenario_go_test.rs

View workflow job for this annotation

GitHub Actions / clippy

[clippy] contracts/paymaster/tests/empty_scenario_go_test.rs#L4

error[E0599]: no function or associated item named `vm_go` found for struct `multiversx_sc_scenario::ScenarioWorld` in the current scope --> contracts/paymaster/tests/empty_scenario_go_test.rs:4:20 | 4 | ScenarioWorld::vm_go() | ^^^^^ function or associated item not found in `ScenarioWorld`
Raw output
contracts/paymaster/tests/empty_scenario_go_test.rs:4:20:e:error[E0599]: no function or associated item named `vm_go` found for struct `multiversx_sc_scenario::ScenarioWorld` in the current scope
 --> contracts/paymaster/tests/empty_scenario_go_test.rs:4:20
  |
4 |     ScenarioWorld::vm_go()
  |                    ^^^^^ function or associated item not found in `ScenarioWorld`


__END__
}

#[test]
fn empty_go() {
world().run("scenarios/empty.scen.json");

Check failure on line 9 in contracts/paymaster/tests/empty_scenario_go_test.rs

View workflow job for this annotation

GitHub Actions / Contracts / Rust tests

no method named `run` found for struct `multiversx_sc_scenario::ScenarioWorld` in the current scope

Check failure on line 9 in contracts/paymaster/tests/empty_scenario_go_test.rs

View workflow job for this annotation

GitHub Actions / clippy

[clippy] contracts/paymaster/tests/empty_scenario_go_test.rs#L9

error[E0599]: no method named `run` found for struct `multiversx_sc_scenario::ScenarioWorld` in the current scope --> contracts/paymaster/tests/empty_scenario_go_test.rs:9:13 | 9 | world().run("scenarios/empty.scen.json"); | ^^^ method not found in `ScenarioWorld`
Raw output
contracts/paymaster/tests/empty_scenario_go_test.rs:9:13:e:error[E0599]: no method named `run` found for struct `multiversx_sc_scenario::ScenarioWorld` in the current scope
 --> contracts/paymaster/tests/empty_scenario_go_test.rs:9:13
  |
9 |     world().run("scenarios/empty.scen.json");
  |             ^^^ method not found in `ScenarioWorld`


__END__
}
14 changes: 14 additions & 0 deletions contracts/paymaster/tests/empty_scenario_rs_test.rs
Original file line number Diff line number Diff line change
@@ -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);

Check failure on line 7 in contracts/paymaster/tests/empty_scenario_rs_test.rs

View workflow job for this annotation

GitHub Actions / Contracts / Wasm tests

the trait bound `paymaster::ContractBuilder: CallableContractBuilder` is not satisfied

Check failure on line 7 in contracts/paymaster/tests/empty_scenario_rs_test.rs

View workflow job for this annotation

GitHub Actions / clippy

[clippy] contracts/paymaster/tests/empty_scenario_rs_test.rs#L7

error[E0277]: the trait bound `paymaster::ContractBuilder: multiversx_sc_scenario::multiversx_sc::contract_base::CallableContractBuilder` is not satisfied --> contracts/paymaster/tests/empty_scenario_rs_test.rs:7:64 | 7 | blockchain.register_contract("file:output/paymaster.wasm", paymaster::ContractBuilder); | ----------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `multiversx_sc_scenario::multiversx_sc::contract_base::CallableContractBuilder` is not implemented for `paymaster::ContractBuilder` | | | required by a bound introduced by this call | note: required by a bound in `multiversx_sc_scenario::ScenarioWorld::register_contract` --> /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/multiversx-sc-scenario-0.41.3/src/facade/scenario_world.rs:53:33 | 53 | pub fn register_contract<B: CallableContractBuilder>( | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ScenarioWorld::register_contract`
Raw output
contracts/paymaster/tests/empty_scenario_rs_test.rs:7:64:e:error[E0277]: the trait bound `paymaster::ContractBuilder: multiversx_sc_scenario::multiversx_sc::contract_base::CallableContractBuilder` is not satisfied
  --> contracts/paymaster/tests/empty_scenario_rs_test.rs:7:64
   |
7  |     blockchain.register_contract("file:output/paymaster.wasm", paymaster::ContractBuilder);
   |                -----------------                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `multiversx_sc_scenario::multiversx_sc::contract_base::CallableContractBuilder` is not implemented for `paymaster::ContractBuilder`
   |                |
   |                required by a bound introduced by this call
   |
note: required by a bound in `multiversx_sc_scenario::ScenarioWorld::register_contract`
  --> /home/runner/.cargo/registry/src/index.crates.io-6f17d22bba15001f/multiversx-sc-scenario-0.41.3/src/facade/scenario_world.rs:53:33
   |
53 |     pub fn register_contract<B: CallableContractBuilder>(
   |                                 ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ScenarioWorld::register_contract`


__END__
blockchain
}

#[test]
fn empty_rs() {
world().run("scenarios/empty.scen.json");

Check failure on line 13 in contracts/paymaster/tests/empty_scenario_rs_test.rs

View workflow job for this annotation

GitHub Actions / Contracts / Wasm tests

no method named `run` found for struct `multiversx_sc_scenario::ScenarioWorld` in the current scope

Check failure on line 13 in contracts/paymaster/tests/empty_scenario_rs_test.rs

View workflow job for this annotation

GitHub Actions / clippy

[clippy] contracts/paymaster/tests/empty_scenario_rs_test.rs#L13

error[E0599]: no method named `run` found for struct `multiversx_sc_scenario::ScenarioWorld` in the current scope --> contracts/paymaster/tests/empty_scenario_rs_test.rs:13:13 | 13 | world().run("scenarios/empty.scen.json"); | ^^^ method not found in `ScenarioWorld`
Raw output
contracts/paymaster/tests/empty_scenario_rs_test.rs:13:13:e:error[E0599]: no method named `run` found for struct `multiversx_sc_scenario::ScenarioWorld` in the current scope
  --> contracts/paymaster/tests/empty_scenario_rs_test.rs:13:13
   |
13 |     world().run("scenarios/empty.scen.json");
   |             ^^^ method not found in `ScenarioWorld`


__END__
}
Loading

0 comments on commit 6e30711

Please sign in to comment.