From 22250c47ff33284d4a477638941e0ca3fe11ec74 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Mon, 9 Dec 2024 19:19:46 +0200 Subject: [PATCH] interactor --- ping-pong/interactor/.gitignore | 2 + ping-pong/interactor/Cargo.toml | 32 ++ ping-pong/interactor/config.toml | 7 + ping-pong/interactor/src/interact.rs | 326 ++++++++++++++++++ ping-pong/interactor/src/interact_cli.rs | 104 ++++++ ping-pong/interactor/src/interact_config.rs | 49 +++ ping-pong/interactor/src/interact_state.rs | 50 +++ ping-pong/interactor/src/interactor_main.rs | 9 + ping-pong/interactor/src/ping_pong_proxy.rs | 203 +++++++++++ ping-pong/interactor/state.toml | 1 + .../interactor/tests/interact_cs_tests.rs | 40 +++ ping-pong/sc-config.toml | 3 + 12 files changed, 826 insertions(+) create mode 100644 ping-pong/interactor/.gitignore create mode 100644 ping-pong/interactor/Cargo.toml create mode 100644 ping-pong/interactor/config.toml create mode 100644 ping-pong/interactor/src/interact.rs create mode 100644 ping-pong/interactor/src/interact_cli.rs create mode 100644 ping-pong/interactor/src/interact_config.rs create mode 100644 ping-pong/interactor/src/interact_state.rs create mode 100644 ping-pong/interactor/src/interactor_main.rs create mode 100644 ping-pong/interactor/src/ping_pong_proxy.rs create mode 100644 ping-pong/interactor/state.toml create mode 100644 ping-pong/interactor/tests/interact_cs_tests.rs create mode 100644 ping-pong/sc-config.toml diff --git a/ping-pong/interactor/.gitignore b/ping-pong/interactor/.gitignore new file mode 100644 index 0000000..5a64d09 --- /dev/null +++ b/ping-pong/interactor/.gitignore @@ -0,0 +1,2 @@ +# Pem files are used for interactions, but shouldn't be committed +*.pem diff --git a/ping-pong/interactor/Cargo.toml b/ping-pong/interactor/Cargo.toml new file mode 100644 index 0000000..f8d7630 --- /dev/null +++ b/ping-pong/interactor/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "ping-pong-interact" +version = "0.0.0" +authors = ["you"] +edition = "2021" +publish = false + +[[bin]] +name = "ping-pong-interact" +path = "src/interactor_main.rs" + +[lib] +path = "src/interact.rs" + +[dependencies.ping-pong] +path = ".." + +[dependencies.multiversx-sc-snippets] +version = "0.54.6" + +[dependencies.multiversx-sc] +version = "0.54.6" + +[dependencies] +clap = { version = "4.4.7", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } +toml = "0.8.6" +tokio = { version = "1.24" } + + +[features] +chain-simulator-tests = [] diff --git a/ping-pong/interactor/config.toml b/ping-pong/interactor/config.toml new file mode 100644 index 0000000..97acd5a --- /dev/null +++ b/ping-pong/interactor/config.toml @@ -0,0 +1,7 @@ + +# chain_type = 'simulator' +# gateway_uri = 'http://localhost:8085' + +chain_type = 'real' +gateway_uri = 'https://devnet-gateway.multiversx.com' + diff --git a/ping-pong/interactor/src/interact.rs b/ping-pong/interactor/src/interact.rs new file mode 100644 index 0000000..8411035 --- /dev/null +++ b/ping-pong/interactor/src/interact.rs @@ -0,0 +1,326 @@ +mod interact_cli; +mod interact_config; +mod interact_state; +mod ping_pong_proxy; + +use clap::Parser; +pub use interact_config::Config; +use interact_state::State; +use multiversx_sc_snippets::imports::*; + +const PING_PONG_CODE: MxscPath = MxscPath::new("output/ping-pong.mxsc.json"); + +pub async fn ping_pong_cli() { + env_logger::init(); + + let config = Config::load_config(); + + let mut interact = PingPongInteract::new(config).await; + let cli = interact_cli::InteractCli::parse(); + + match &cli.command { + Some(interact_cli::InteractCliCommand::Deploy(args)) => { + interact + .deploy( + args.ping_amount.clone(), + args.duration_in_seconds, + args.token_id.clone(), + ) + .await; + } + Some(interact_cli::InteractCliCommand::Upgrade(args)) => { + interact + .upgrade(args.ping_amount.clone(), args.duration_in_seconds) + .await; + } + Some(interact_cli::InteractCliCommand::Ping(args)) => { + interact + .ping( + &args.token, + args.nonce, + args.amount, + &interact.alice_wallet_address.clone(), + None, + ) + .await; + } + Some(interact_cli::InteractCliCommand::Pong) => { + interact + .pong(&interact.alice_wallet_address.clone(), None) + .await; + } + Some(interact_cli::InteractCliCommand::DidUserPing(args)) => { + let address = Bech32Address::from_bech32_string(args.address.clone()); + interact.did_user_ping(address).await; + } + Some(interact_cli::InteractCliCommand::GetPongEnableTimestamp(args)) => { + let address = Bech32Address::from_bech32_string(args.address.clone()); + interact.get_pong_enable_timestamp(address).await; + } + Some(interact_cli::InteractCliCommand::GetTimeToPong(args)) => { + let address = Bech32Address::from_bech32_string(args.address.clone()); + interact.get_time_to_pong(address).await; + } + Some(interact_cli::InteractCliCommand::GetAcceptedPaymentToken) => { + interact.accepted_payment_token_id().await; + } + Some(interact_cli::InteractCliCommand::GetPingAmount) => { + interact.ping_amount().await; + } + Some(interact_cli::InteractCliCommand::GetDurationTimestamp) => { + interact.duration_in_seconds().await; + } + Some(interact_cli::InteractCliCommand::GetUserPingTimestamp(args)) => { + let address = Bech32Address::from_bech32_string(args.address.clone()); + interact.user_ping_timestamp(address).await; + } + None => {} + } +} + +pub struct PingPongInteract { + pub interactor: Interactor, + pub alice_wallet_address: Bech32Address, + pub mike_wallet_address: Bech32Address, + pub state: State, +} + +impl PingPongInteract { + pub async fn new(config: Config) -> Self { + let mut interactor = Interactor::new(config.gateway_uri()) + .await + .use_chain_simulator(config.use_chain_simulator()); + + interactor.set_current_dir_from_workspace("ping-pong"); + let alice_wallet_address = interactor.register_wallet(test_wallets::alice()).await; + let mike_wallet_address = interactor.register_wallet(test_wallets::mike()).await; + + // Useful in the chain simulator setting + // generate blocks until ESDTSystemSCAddress is enabled + interactor.generate_blocks_until_epoch(1).await.unwrap(); + + PingPongInteract { + interactor, + alice_wallet_address: alice_wallet_address.into(), + mike_wallet_address: mike_wallet_address.into(), + state: State::load_state(), + } + } + + pub async fn deploy( + &mut self, + ping_amount: RustBigUint, + duration_in_seconds: u64, + token_id: String, + ) { + let token = if token_id.to_uppercase() == "EGLD" { + EgldOrEsdtTokenIdentifier::egld() + } else { + EgldOrEsdtTokenIdentifier::esdt(&token_id) + }; + + let new_address = self + .interactor + .tx() + .from(&self.alice_wallet_address) + .gas(30_000_000u64) + .typed(ping_pong_proxy::PingPongProxy) + .init(ping_amount, duration_in_seconds, OptionalValue::Some(token)) + .code(PING_PONG_CODE) + .returns(ReturnsNewAddress) + .run() + .await; + let new_address_bech32 = bech32::encode(&new_address); + self.state + .set_ping_pong_address(Bech32Address::from_bech32_string( + new_address_bech32.clone(), + )); + + println!("new address: {new_address_bech32}"); + } + + pub async fn upgrade(&mut self, ping_amount: RustBigUint, duration_in_seconds: u64) { + let upgrade_address = self + .interactor + .tx() + .from(&self.alice_wallet_address) + .to(self.state.current_ping_pong_address()) + .gas(30_000_000u64) + .typed(ping_pong_proxy::PingPongProxy) + .upgrade(ping_amount, duration_in_seconds) + .code(PING_PONG_CODE) + .returns(ReturnsNewAddress) + .run() + .await; + + let upgrade_address_bech32 = bech32::encode(&upgrade_address); + self.state + .set_ping_pong_address(Bech32Address::from_bech32_string( + upgrade_address_bech32.clone(), + )); + + println!("new upgrade address: {upgrade_address_bech32}"); + } + + pub async fn ping( + &mut self, + token_id: &str, + nonce: Option, + amount: u64, + sender: &Bech32Address, + message: Option<&str>, + ) { + let response = if token_id.to_ascii_uppercase() == "EGLD" { + self.interactor + .tx() + .from(sender) + .to(self.state.current_ping_pong_address()) + .gas(30_000_000u64) + .typed(ping_pong_proxy::PingPongProxy) + .ping() + .egld(amount) + .returns(ReturnsHandledOrError::new()) + .run() + .await + } else { + self.interactor + .tx() + .from(sender) + .to(self.state.current_ping_pong_address()) + .gas(30_000_000u64) + .typed(ping_pong_proxy::PingPongProxy) + .ping() + .payment(( + TokenIdentifier::from(&token_id.to_uppercase()), + nonce.unwrap(), + BigUint::from(amount), + )) + .returns(ReturnsHandledOrError::new()) + .run() + .await + }; + + match response { + Ok(_) => println!("Ping successfully executed"), + Err(err) => { + println!("Ping failed with message: {}", err.message); + assert_eq!(message.unwrap_or_default(), err.message); + } + } + } + + pub async fn pong(&mut self, sender: &Bech32Address, message: Option<&str>) { + let response = self + .interactor + .tx() + .from(sender) + .to(self.state.current_ping_pong_address()) + .gas(30_000_000u64) + .typed(ping_pong_proxy::PingPongProxy) + .pong() + .returns(ReturnsHandledOrError::new()) + .run() + .await; + + match response { + Ok(_) => println!("Pong successfully executed"), + Err(err) => { + println!("Pong failed with message: {}", err.message); + assert_eq!(message.unwrap_or_default(), err.message); + } + } + } + + pub async fn did_user_ping(&mut self, address: Bech32Address) -> bool { + self.interactor + .query() + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .did_user_ping(address) + .returns(ReturnsResultUnmanaged) + .run() + .await + } + + pub async fn get_pong_enable_timestamp(&mut self, address: Bech32Address) -> u64 { + self.interactor + .query() + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .get_pong_enable_timestamp(address) + .returns(ReturnsResultUnmanaged) + .run() + .await + } + + pub async fn get_time_to_pong(&mut self, address: Bech32Address) -> Option { + let result_value = self + .interactor + .query() + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .get_time_to_pong(address) + .returns(ReturnsResultUnmanaged) + .run() + .await; + + match result_value { + OptionalValue::Some(time) => Some(time), + OptionalValue::None => { + println!("Address unavailable"); + None + } + } + } + + pub async fn accepted_payment_token_id(&mut self) -> String { + let result_value = self + .interactor + .query() + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .accepted_payment_token_id() + .returns(ReturnsResultUnmanaged) + .run() + .await; + + if result_value.is_egld() { + return "EGLD".to_owned(); + } + + result_value.into_esdt_option().unwrap().to_string() + } + + pub async fn ping_amount(&mut self) -> RustBigUint { + self.interactor + .query() + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .ping_amount() + .returns(ReturnsResultUnmanaged) + .run() + .await + } + + pub async fn duration_in_seconds(&mut self) -> u64 { + self.interactor + .query() + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .duration_in_seconds() + .returns(ReturnsResultUnmanaged) + .run() + .await + } + + pub async fn user_ping_timestamp(&mut self, address: Bech32Address) -> u64 { + self.interactor + .query() + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .user_ping_timestamp(address) + .returns(ReturnsResultUnmanaged) + .run() + .await + } +} diff --git a/ping-pong/interactor/src/interact_cli.rs b/ping-pong/interactor/src/interact_cli.rs new file mode 100644 index 0000000..00adcfd --- /dev/null +++ b/ping-pong/interactor/src/interact_cli.rs @@ -0,0 +1,104 @@ +use clap::{Args, Parser, Subcommand}; +use multiversx_sc_snippets::imports::RustBigUint; + +/// Ping Pong Interact CLI +#[derive(Default, PartialEq, Eq, Debug, Parser)] +#[command(version, about)] +#[command(propagate_version = true)] +pub struct InteractCli { + #[command(subcommand)] + pub command: Option, +} + +/// Ping Pong Interact CLI Commands +#[derive(Clone, PartialEq, Eq, Debug, Subcommand)] +pub enum InteractCliCommand { + #[command(name = "deploy", about = "Deploy contract.")] + Deploy(DeployArgs), + #[command(name = "upgrade", about = "Upgrade contract.")] + Upgrade(UpgradeArgs), + #[command( + name = "ping", + about = "User sends some EGLD to be locked in the contract for a period of time." + )] + Ping(PingArgs), + #[command(name = "pong", about = "User can take back funds from the contract.")] + Pong, + #[command(name = "did-user-ping", about = "Returns if a user ping-ed or not")] + DidUserPing(DidUserPingArgs), + #[command( + name = "pong-enable", + about = "Returns the timestamp when pong is enabled." + )] + GetPongEnableTimestamp(GetPongEnableTimestampArgs), + #[command(name = "time-to-pong", about = "Returns the time left to pong.")] + GetTimeToPong(GetTimeToPongArgs), + #[command(name = "token", about = "Returns accepted token to ping.")] + GetAcceptedPaymentToken, + #[command(name = "ping-amount", about = "Returns the ping amount.")] + GetPingAmount, + #[command(name = "duration", about = "Returns the duration in seconds.")] + GetDurationTimestamp, + #[command( + name = "user-ping", + about = "Returns the timestamp at which the user pinged" + )] + GetUserPingTimestamp(GetUserPingTimestampArgs), +} + +#[derive(Default, Clone, PartialEq, Eq, Debug, Args)] +pub struct DeployArgs { + #[arg(short = 'p', long = "ping-amount")] + pub ping_amount: RustBigUint, + + #[arg(short = 'd', long = "duration-in-seconds")] + pub duration_in_seconds: u64, + + #[arg(short = 't', long = "token-id", default_value = "EGLD")] + pub token_id: String, +} + +#[derive(Default, Clone, PartialEq, Eq, Debug, Args)] +pub struct UpgradeArgs { + #[arg(short = 'p', long = "ping-amount")] + pub ping_amount: RustBigUint, + + #[arg(short = 'd', long = "duration-in-seconds")] + pub duration_in_seconds: u64, +} + +#[derive(Default, Clone, PartialEq, Eq, Debug, Args)] +pub struct PingArgs { + #[arg(short = 't', long = "token")] + pub token: String, + + #[arg(short = 'n', long = "nonce")] + pub nonce: Option, + + #[arg(short = 'a', long = "amount")] + pub amount: u64, +} + +#[derive(Default, Clone, PartialEq, Eq, Debug, Args)] +pub struct DidUserPingArgs { + #[arg(short = 'a', long = "address")] + pub address: String, +} + +#[derive(Default, Clone, PartialEq, Eq, Debug, Args)] +pub struct GetPongEnableTimestampArgs { + #[arg(short = 'a', long = "address")] + pub address: String, +} + +#[derive(Default, Clone, PartialEq, Eq, Debug, Args)] +pub struct GetTimeToPongArgs { + #[arg(short = 'a', long = "address")] + pub address: String, +} + +#[derive(Default, Clone, PartialEq, Eq, Debug, Args)] +pub struct GetUserPingTimestampArgs { + #[arg(short = 'a', long = "address")] + pub address: String, +} diff --git a/ping-pong/interactor/src/interact_config.rs b/ping-pong/interactor/src/interact_config.rs new file mode 100644 index 0000000..bc35101 --- /dev/null +++ b/ping-pong/interactor/src/interact_config.rs @@ -0,0 +1,49 @@ +use serde::Deserialize; +use std::io::Read; + +/// Config file +const CONFIG_FILE: &str = "config.toml"; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum ChainType { + Real, + Simulator, +} + +/// Contract Interact configuration +#[derive(Debug, Deserialize)] +pub struct Config { + pub gateway_uri: String, + pub chain_type: ChainType, +} + +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() + } + + pub fn chain_simulator_config() -> Self { + Config { + gateway_uri: "http://localhost:8085".to_owned(), + chain_type: ChainType::Simulator, + } + } + + // Returns the gateway URI + pub fn gateway_uri(&self) -> &str { + &self.gateway_uri + } + + // Returns if chain type is chain simulator + pub fn use_chain_simulator(&self) -> bool { + match self.chain_type { + ChainType::Real => false, + ChainType::Simulator => true, + } + } +} diff --git a/ping-pong/interactor/src/interact_state.rs b/ping-pong/interactor/src/interact_state.rs new file mode 100644 index 0000000..74694c4 --- /dev/null +++ b/ping-pong/interactor/src/interact_state.rs @@ -0,0 +1,50 @@ +use multiversx_sc_snippets::imports::*; +use serde::{Deserialize, Serialize}; +use std::{ + io::{Read, Write}, + path::Path, +}; + +/// State file +const STATE_FILE: &str = "state.toml"; + +/// Multisig Interact state +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct State { + ping_pong_address: Option, +} + +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 ping pong address + pub fn set_ping_pong_address(&mut self, address: Bech32Address) { + self.ping_pong_address = Some(address); + } + + /// Returns the ping pong contract + pub fn current_ping_pong_address(&self) -> &Bech32Address { + self.ping_pong_address + .as_ref() + .expect("no known ping pong 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(); + } +} diff --git a/ping-pong/interactor/src/interactor_main.rs b/ping-pong/interactor/src/interactor_main.rs new file mode 100644 index 0000000..5143ded --- /dev/null +++ b/ping-pong/interactor/src/interactor_main.rs @@ -0,0 +1,9 @@ + +use multiversx_sc_snippets::imports::*; +use ping_pong_interact::ping_pong_cli; + +#[tokio::main] +async fn main() { + ping_pong_cli().await; +} + diff --git a/ping-pong/interactor/src/ping_pong_proxy.rs b/ping-pong/interactor/src/ping_pong_proxy.rs new file mode 100644 index 0000000..5e67e29 --- /dev/null +++ b/ping-pong/interactor/src/ping_pong_proxy.rs @@ -0,0 +1,203 @@ +// 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 PingPongProxy; + +impl TxProxyTrait for PingPongProxy +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + type TxProxyMethods = PingPongProxyMethods; + + fn proxy_methods(self, tx: Tx) -> Self::TxProxyMethods { + PingPongProxyMethods { wrapped_tx: tx } + } +} + +pub struct PingPongProxyMethods +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + wrapped_tx: Tx, +} + +#[rustfmt::skip] +impl PingPongProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + Gas: TxGas, +{ + /// Necessary configuration when deploying: + /// `ping_amount` - the exact amount that needs to be sent when `ping`-ing. + /// `duration_in_seconds` - how much time (in seconds) until `pong` can be called after the initial `ping` call + /// `token_id` - Optional. The Token Identifier of the token that is going to be used. Default is "EGLD". + pub fn init< + Arg0: ProxyArg>, + Arg1: ProxyArg, + Arg2: ProxyArg>>, + >( + self, + ping_amount: Arg0, + duration_in_seconds: Arg1, + opt_token_id: Arg2, + ) -> TxTypedDeploy { + self.wrapped_tx + .payment(NotPayable) + .raw_deploy() + .argument(&ping_amount) + .argument(&duration_in_seconds) + .argument(&opt_token_id) + .original_result() + } +} + +#[rustfmt::skip] +impl PingPongProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + pub fn upgrade< + Arg0: ProxyArg>, + Arg1: ProxyArg, + >( + self, + ping_amount: Arg0, + duration_in_seconds: Arg1, + ) -> TxTypedUpgrade { + self.wrapped_tx + .payment(NotPayable) + .raw_upgrade() + .argument(&ping_amount) + .argument(&duration_in_seconds) + .original_result() + } +} + +#[rustfmt::skip] +impl PingPongProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + /// User sends some tokens to be locked in the contract for a period of time. + pub fn ping( + self, + ) -> TxTypedCall { + self.wrapped_tx + .raw_call("ping") + .original_result() + } + + /// User can take back funds from the contract. + /// Can only be called after expiration. + pub fn pong( + self, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("pong") + .original_result() + } + + pub fn did_user_ping< + Arg0: ProxyArg>, + >( + self, + address: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("didUserPing") + .argument(&address) + .original_result() + } + + pub fn get_pong_enable_timestamp< + Arg0: ProxyArg>, + >( + self, + address: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getPongEnableTimestamp") + .argument(&address) + .original_result() + } + + pub fn get_time_to_pong< + Arg0: ProxyArg>, + >( + self, + address: Arg0, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getTimeToPong") + .argument(&address) + .original_result() + } + + pub fn accepted_payment_token_id( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getAcceptedPaymentToken") + .original_result() + } + + pub fn ping_amount( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getPingAmount") + .original_result() + } + + pub fn duration_in_seconds( + self, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getDurationTimestamp") + .original_result() + } + + pub fn user_ping_timestamp< + Arg0: ProxyArg>, + >( + self, + address: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getUserPingTimestamp") + .argument(&address) + .original_result() + } +} diff --git a/ping-pong/interactor/state.toml b/ping-pong/interactor/state.toml new file mode 100644 index 0000000..3d00b45 --- /dev/null +++ b/ping-pong/interactor/state.toml @@ -0,0 +1 @@ +ping_pong_address = "erd1qqqqqqqqqqqqqpgqncc4kv32u4ptyu6fpw0qas226ede3w04d8ssmpaq05" diff --git a/ping-pong/interactor/tests/interact_cs_tests.rs b/ping-pong/interactor/tests/interact_cs_tests.rs new file mode 100644 index 0000000..7b8aa71 --- /dev/null +++ b/ping-pong/interactor/tests/interact_cs_tests.rs @@ -0,0 +1,40 @@ +use multiversx_sc_snippets::imports::*; +use ping_pong_interact::{Config, PingPongInteract}; + +const EGLD: &str = "EGLD"; + +#[tokio::test] +#[cfg_attr(not(feature = "chain-simulator-tests"), ignore)] +async fn deploy_test_ping_pong_cs() { + let mut interactor = PingPongInteract::new(Config::chain_simulator_config()).await; + + let alice = interactor.alice_wallet_address.clone(); + let mike = interactor.mike_wallet_address.clone(); + let amount = RustBigUint::from(1u32); + let time = 15u64; + + interactor.deploy(amount, time, EGLD.to_string()).await; + + interactor + .ping( + EGLD, + None, + 2, + &alice, + Some("The payment must match the fixed ping amount"), + ) + .await; + interactor.ping(EGLD, None, 1, &alice, None).await; + assert_eq!(true, interactor.did_user_ping(alice.clone()).await); + + assert_eq!(false, interactor.did_user_ping(mike.clone()).await); + interactor.ping(EGLD, None, 1, &mike, None).await; + + assert_eq!(Some(15), interactor.get_time_to_pong(mike.clone()).await); + assert_eq!(EGLD, interactor.accepted_payment_token_id().await); + assert_eq!(RustBigUint::from(1u64), interactor.ping_amount().await); + assert_eq!(time, interactor.duration_in_seconds().await); + + interactor.pong(&alice, None).await; + interactor.pong(&alice, Some("Must ping first")).await; +} diff --git a/ping-pong/sc-config.toml b/ping-pong/sc-config.toml new file mode 100644 index 0000000..109751f --- /dev/null +++ b/ping-pong/sc-config.toml @@ -0,0 +1,3 @@ + +[[proxy]] +path = "interactor/src/ping_pong_proxy.rs"