Skip to content

Commit

Permalink
Merge pull request #104 from multiversx/paymaster-interactor
Browse files Browse the repository at this point in the history
paymaster: Add interactor
  • Loading branch information
CostinCarabas authored Aug 26, 2024
2 parents 714a45e + d03d431 commit 726ee8f
Show file tree
Hide file tree
Showing 11 changed files with 337 additions and 1 deletion.
12 changes: 12 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ members = [
"contracts/pair-mock/meta",
"contracts/paymaster",
"contracts/paymaster/meta",
"contracts/paymaster/interactor",
"contracts/ping-pong-egld",
"contracts/ping-pong-egld/meta",
"contracts/proxy-deployer",
Expand Down
2 changes: 2 additions & 0 deletions contracts/paymaster/interactor/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Pem files are used for interactions, but shouldn't be committed
*.pem
27 changes: 27 additions & 0 deletions contracts/paymaster/interactor/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "rust-interact"
version = "0.0.0"
authors = ["you"]
edition = "2021"
publish = false

[[bin]]
name = "rust-interact"
path = "src/interactor_main.rs"

[dependencies.paymaster]
path = ".."

[dependencies.multiversx-sc-snippets]
version = "0.52.3"

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

[dependencies]
clap = { version = "4.4.7", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
toml = "0.8.6"

# [workspace]

7 changes: 7 additions & 0 deletions contracts/paymaster/interactor/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
gateway = 'https://devnet-gateway.multiversx.com'

relayer_addr = "erd19azy5vlq343un82cqz03744vmta9ya8l5xwj3ywz7r8m8pkttd0syhkffa"
egld_mex_pair_address = "erd1qqqqqqqqqqqqqpgqzw0d0tj25qme9e4ukverjjjqle6xamay0n4s5r0v9g"
egld_usdc_pair_address = "erd1qqqqqqqqqqqqqpgqtqfhy99su9xzjjrq59kpzpp25udtc9eq0n4sr90ax6"
wegld_address = "erd1qqqqqqqqqqqqqpgqpv09kfzry5y4sj05udcngesat07umyj70n4sa2c0rp"
router_address = "erd1qqqqqqqqqqqqqpgqa7hv0nahgsl8tz0psat46x0tchm0wuyc0n4s6q28ad"
174 changes: 174 additions & 0 deletions contracts/paymaster/interactor/src/interactor_main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#![allow(non_snake_case)]

mod paymaster_config;
mod proxy;

use multiversx_sc_snippets::imports::*;
use multiversx_sc_snippets::sdk;
use paymaster_config::Config;
use serde::{Deserialize, Serialize};
use std::{
io::{Read, Write},
path::Path,
};

const GATEWAY: &str = sdk::gateway::DEVNET_GATEWAY;
const STATE_FILE: &str = "state.toml";

const ONE_UNIT: u64 = 1_000_000_000_000_000_000;
const ONE_MILLION: u64 = 1_000_000;

pub static WEGLD_TOKEN_ID: &[u8] = b"WEGLD-a28c59";
pub static MEX_TOKEN_ID: &[u8] = b"MEX-a659d0";
pub static ONE_TOKEN_ID: &[u8] = b"ONE-83a7c0";
pub static USDC_TOKEN_ID: &[u8] = b"USDC-350c4e";
pub static UTK_TOKEN_ID: &[u8] = b"UTK-14d57d";

pub const SWAP_TOKENS_FIXED_INPUT_FUNC_NAME: &[u8] = b"swapTokensFixedInput";
pub const SWAP_TOKENS_FIXED_OUTPUT_FUNC_NAME: &[u8] = b"swapTokensFixedOutput";

#[tokio::main]
async fn main() {
env_logger::init();

let mut args = std::env::args();
let _ = args.next();
let cmd = args.next().expect("at least one argument required");
let mut interact = ContractInteract::new().await;
match cmd.as_str() {
"deploy" => interact.deploy().await,
"forwardExecution" => interact.forward_execution().await,
_ => panic!("unknown command: {}", &cmd),
}
}

#[derive(Debug, Default, Serialize, Deserialize)]
struct State {
contract_address: Option<Bech32Address>,
}

impl State {
// Deserializes state from file
pub fn load_state() -> Self {
if Path::new(STATE_FILE).exists() {
let mut file = std::fs::File::open(STATE_FILE).unwrap();
let mut content = String::new();
file.read_to_string(&mut content).unwrap();
toml::from_str(&content).unwrap()
} else {
Self::default()
}
}

/// Sets the contract address
pub fn set_address(&mut self, address: Bech32Address) {
self.contract_address = Some(address);
}

/// Returns the contract address
pub fn current_address(&self) -> &Bech32Address {
self.contract_address
.as_ref()
.expect("no known contract, deploy first")
}
}

impl Drop for State {
// Serializes state to file
fn drop(&mut self) {
let mut file = std::fs::File::create(STATE_FILE).unwrap();
file.write_all(toml::to_string(self).unwrap().as_bytes())
.unwrap();
}
}

struct ContractInteract {
interactor: Interactor,
wallet_address: Address,
contract_code: BytesValue,
config: Config,
state: State,
}

impl ContractInteract {
async fn new() -> Self {
let mut interactor = Interactor::new(GATEWAY).await;
let wallet_address = interactor.register_wallet(test_wallets::alice());

let contract_code = BytesValue::interpret_from(
"mxsc:../output/paymaster.mxsc.json",
&InterpreterContext::default(),
);

ContractInteract {
interactor,
wallet_address,
contract_code,
config: Config::load_config(),
state: State::load_state(),
}
}

async fn deploy(&mut self) {
let new_address = self
.interactor
.tx()
.from(&self.wallet_address)
.gas(30_000_000u64)
.typed(proxy::PaymasterContractProxy)
.init()
.code(&self.contract_code)
.returns(ReturnsNewAddress)
.prepare_async()
.run()
.await;
let new_address_bech32 = bech32::encode(&new_address);
self.state.set_address(Bech32Address::from_bech32_string(
new_address_bech32.clone(),
));

println!("new address: {new_address_bech32}");
}

async fn forward_execution(&mut self) {
let token_nonce = 0u64;
let token_amount = BigUint::<StaticApi>::from(ONE_UNIT) * ONE_MILLION;

let relayer_addr = &self.config.relayer_addr;

let dest = &self.config.egld_mex_pair_address;
let endpoint_name = ManagedBuffer::new_from_bytes(SWAP_TOKENS_FIXED_INPUT_FUNC_NAME);
let endpoint_args = MultiValueVec::from(vec![
ManagedBuffer::new_from_bytes(WEGLD_TOKEN_ID),
ManagedBuffer::new_from_bytes(b"1"),
]);

let mut payments = ManagedVec::<StaticApi, EsdtTokenPayment<StaticApi>>::new();
payments.push(EsdtTokenPayment::new(
TokenIdentifier::from(WEGLD_TOKEN_ID),
token_nonce,
BigUint::<StaticApi>::from(ONE_UNIT / 100), // 0.01 WEGLD
));
payments.push(EsdtTokenPayment::new(
TokenIdentifier::from(MEX_TOKEN_ID),
token_nonce,
token_amount, // 1_000_000 MEX
));

let response = self
.interactor
.tx()
.from(&self.wallet_address)
.to(self.state.current_address())
.gas(30_000_000u64)
.typed(proxy::PaymasterContractProxy)
.forward_execution(relayer_addr, dest, endpoint_name, endpoint_args)
.payment(payments)
.returns(ReturnsResultUnmanaged)
.prepare_async()
.run()
.await;

println!("Result: {response:?}");
}
}
23 changes: 23 additions & 0 deletions contracts/paymaster/interactor/src/paymaster_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use multiversx_sc_snippets::imports::Bech32Address;
use serde::Deserialize;
use std::io::Read;

/// Config file
const CONFIG_FILE: &str = "config.toml";

/// Multisig Interact configuration
#[derive(Debug, Deserialize)]
pub struct Config {
pub relayer_addr: Bech32Address,
pub egld_mex_pair_address: Bech32Address,
}

impl Config {
// Deserializes config from file
pub fn load_config() -> Self {
let mut file = std::fs::File::open(CONFIG_FILE).unwrap();
let mut content = String::new();
file.read_to_string(&mut content).unwrap();
toml::from_str(&content).unwrap()
}
}
85 changes: 85 additions & 0 deletions contracts/paymaster/interactor/src/proxy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Code generated by the multiversx-sc proxy generator. DO NOT EDIT.

////////////////////////////////////////////////////
////////////////// AUTO-GENERATED //////////////////
////////////////////////////////////////////////////

#![allow(dead_code)]
#![allow(clippy::all)]

use multiversx_sc::proxy_imports::*;

pub struct PaymasterContractProxy;

impl<Env, From, To, Gas> TxProxyTrait<Env, From, To, Gas> for PaymasterContractProxy
where
Env: TxEnv,
From: TxFrom<Env>,
To: TxTo<Env>,
Gas: TxGas<Env>,
{
type TxProxyMethods = PaymasterContractProxyMethods<Env, From, To, Gas>;

fn proxy_methods(self, tx: Tx<Env, From, To, (), Gas, (), ()>) -> Self::TxProxyMethods {
PaymasterContractProxyMethods { wrapped_tx: tx }
}
}

pub struct PaymasterContractProxyMethods<Env, From, To, Gas>
where
Env: TxEnv,
From: TxFrom<Env>,
To: TxTo<Env>,
Gas: TxGas<Env>,
{
wrapped_tx: Tx<Env, From, To, (), Gas, (), ()>,
}

#[rustfmt::skip]
impl<Env, From, Gas> PaymasterContractProxyMethods<Env, From, (), Gas>
where
Env: TxEnv,
Env::Api: VMApi,
From: TxFrom<Env>,
Gas: TxGas<Env>,
{
pub fn init(
self,
) -> TxTypedDeploy<Env, From, NotPayable, Gas, ()> {
self.wrapped_tx
.payment(NotPayable)
.raw_deploy()
.original_result()
}
}

#[rustfmt::skip]
impl<Env, From, To, Gas> PaymasterContractProxyMethods<Env, From, To, Gas>
where
Env: TxEnv,
Env::Api: VMApi,
From: TxFrom<Env>,
To: TxTo<Env>,
Gas: TxGas<Env>,
{
pub fn forward_execution<
Arg0: ProxyArg<ManagedAddress<Env::Api>>,
Arg1: ProxyArg<ManagedAddress<Env::Api>>,
Arg2: ProxyArg<ManagedBuffer<Env::Api>>,
Arg3: ProxyArg<MultiValueEncoded<Env::Api, ManagedBuffer<Env::Api>>>,
>(
self,
relayer_addr: Arg0,
dest: Arg1,
endpoint_name: Arg2,
endpoint_args: Arg3,
) -> TxTypedCall<Env, From, To, (), Gas, ()> {
self.wrapped_tx
.raw_call("forwardExecution")
.argument(&relayer_addr)
.argument(&dest)
.argument(&endpoint_name)
.argument(&endpoint_args)
.original_result()
}
}
1 change: 1 addition & 0 deletions contracts/paymaster/interactor/state.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
contract_address = "erd1qqqqqqqqqqqqqpgqgr7585knhje7ns7w662pptwtjc03hn34d8sspl0nd4"
4 changes: 4 additions & 0 deletions contracts/paymaster/sc-config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
[[proxy]]
path = "src/paymaster_proxy.rs"

[[proxy]]
path = "interactor/src/proxy.rs"

2 changes: 1 addition & 1 deletion contracts/paymaster/src/forward_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub trait ForwardCall {
.payment(&back_transfers.esdt_payments)
.transfer();
}
if back_transfers.total_egld_amount != BigUint::zero() {
if back_transfers.total_egld_amount > 0 {
self.tx()
.to(&original_caller)
.egld(back_transfers.total_egld_amount)
Expand Down

0 comments on commit 726ee8f

Please sign in to comment.