Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paymaster SC #24

Merged
merged 13 commits into from
Oct 3, 2023
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -52,6 +51,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
4 changes: 2 additions & 2 deletions contracts/adder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
2 changes: 1 addition & 1 deletion contracts/adder/meta/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ publish = false
path = ".."

[dependencies.multiversx-sc-meta]
version = "0.41.3"
version = "0.43.4"
1 change: 1 addition & 0 deletions contracts/adder/src/adder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
28 changes: 14 additions & 14 deletions contracts/adder/wasm/Cargo.lock

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

2 changes: 1 addition & 1 deletion contracts/adder/wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ panic = "abort"
path = ".."

[dependencies.multiversx-sc-wasm-adapter]
version = "0.41.3"
version = "0.43.4"

[workspace]
members = ["."]
10 changes: 7 additions & 3 deletions contracts/adder/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!();
Expand All @@ -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! {}
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*/
23 changes: 23 additions & 0 deletions contracts/paymaster/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "paymaster"
version = "0.0.0"
authors = [ "MultiversX <[email protected]>" ]
edition = "2018"
publish = false
readme = "README.md"


[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.43.4"

[dev-dependencies.adder]
path = "../adder"
28 changes: 28 additions & 0 deletions contracts/paymaster/README.md
Original file line number Diff line number Diff line change
@@ -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.
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": "*"
}
}
]
}
53 changes: 53 additions & 0 deletions contracts/paymaster/src/forward_call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
multiversx_sc::imports!();

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

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(
&self,
dest: ManagedAddress,
endpoint_name: ManagedBuffer,
endpoint_args: MultiValueEncoded<ManagedBuffer>,
payments: PaymentsVec<Self::Api>,
) {
let original_caller = self.blockchain().get_caller();

self.send()
.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,
original_caller: ManagedAddress,
#[call_result] result: ManagedAsyncCallResult<MultiValueEncoded<ManagedBuffer>>,
) -> MultiValueEncoded<ManagedBuffer> {
let initial_payments = self.call_value().all_esdt_transfers();

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
}
}
}
}
39 changes: 39 additions & 0 deletions contracts/paymaster/src/paymaster.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#![no_std]

multiversx_sc::imports!();

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: forward_call::ForwardCall {
#[init]
fn init(&self) {}

#[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();
require!(!payments.is_empty(), "There is no fee for payment!");

let fee_payment = payments.get(FEE_PAYMENT);
self.send().direct_esdt(
&relayer_addr,
&fee_payment.token_identifier,
0,
&fee_payment.amount,
);
Comment on lines +26 to +32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't you check that the fee is equal to some amount?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is checked at the relayer level.


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);
}
}
Loading
Loading