diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 3d0ff99..80b4d85 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -13,7 +13,7 @@ permissions: jobs: contracts: name: Contracts - uses: multiversx/mx-sc-actions/.github/workflows/contracts.yml@v4.0.0 + uses: multiversx/mx-sc-actions/.github/workflows/contracts.yml@v4.2.0 with: rust-toolchain: stable mx-scenario-go-version: v3.0.0 diff --git a/ping-pong/interactor/.gitignore b/ping-pong/interactor/.gitignore index ac5c74e..e4741b7 100644 --- a/ping-pong/interactor/.gitignore +++ b/ping-pong/interactor/.gitignore @@ -1,9 +1,5 @@ # Pem files are used for interactions, but shouldn't be committed *.pem -*.json # Temporary storage of deployed contract address, so we can preserve the context between executions. -state.toml - -# Trace file of interactor tooling -interactor_trace.scen.json +state.toml \ No newline at end of file diff --git a/ping-pong/interactor/Cargo.toml b/ping-pong/interactor/Cargo.toml index 66062c1..00a33a0 100644 --- a/ping-pong/interactor/Cargo.toml +++ b/ping-pong/interactor/Cargo.toml @@ -1,36 +1,31 @@ -[[bin]] -name = "ping-pong-interact" -path = "src/interact_main.rs" - [package] name = "ping-pong-interact" version = "0.0.0" -authors = ["MultiversX "] +authors = ["you"] edition = "2021" publish = false +[[bin]] +name = "ping-pong-interact" +path = "src/interact_main.rs" + [lib] path = "src/interact.rs" -[dependencies] -toml = "0.8.6" - -[dependencies.clap] -version = "4.4.7" -features = ["derive"] - -[dependencies.serde] -version = "1.0" -features = ["derive"] - -[dependencies.tokio] -version = "1.24" - [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 index c6f9a97..97acd5a 100644 --- a/ping-pong/interactor/config.toml +++ b/ping-pong/interactor/config.toml @@ -1,5 +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 index 018a7e0..bfe97de 100644 --- a/ping-pong/interactor/src/interact.rs +++ b/ping-pong/interactor/src/interact.rs @@ -1,228 +1,193 @@ mod interact_cli; mod interact_config; mod interact_state; +mod ping_pong_proxy; -use crate::interact_state::State; use clap::Parser; pub use interact_config::Config; -use ping_pong::proxy_ping_pong_egld::{self, ContractState, UserStatus}; - +use interact_state::State; use multiversx_sc_snippets::imports::*; -const INTERACTOR_SCENARIO_TRACE_PATH: &str = "interactor_trace.scen.json"; - -const PING_PONG_CODE: MxscPath = MxscPath::new("../output/ping-pong.mxsc.json"); +const PING_PONG_CODE: MxscPath = MxscPath::new("output/ping-pong.mxsc.json"); +pub const EGLD: &str = "EGLD"; -pub async fn ping_pong_egld_cli() { +pub async fn ping_pong_cli() { env_logger::init(); let config = Config::load_config(); - let mut interact = PingPongEgldInteract::init(config).await; - + 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.opt_activation_timestamp, - OptionalValue::from(args.max_funds.clone()), + args.token_id.clone(), ) .await; } Some(interact_cli::InteractCliCommand::Upgrade(args)) => { interact - .upgrade( - args.ping_amount.clone(), - args.duration_in_seconds, - args.opt_activation_timestamp, - OptionalValue::from(args.max_funds.clone()), - ) - .await + .upgrade(args.ping_amount.clone(), args.duration_in_seconds) + .await; } Some(interact_cli::InteractCliCommand::Ping(args)) => { - let sender = interact.ping_pong_owner_address.clone(); interact - .ping(args.cost.unwrap_or_default(), None, &sender) - .await + .ping( + args.token.clone(), + args.nonce, + args.amount, + &interact.alice_wallet_address.clone(), + None, + ) + .await; } Some(interact_cli::InteractCliCommand::Pong) => { - let sender = interact.ping_pong_owner_address.clone(); - interact.pong(None, &sender).await; - } - Some(interact_cli::InteractCliCommand::PongAll) => { - let sender = interact.ping_pong_owner_address.clone(); - interact.pong_all(None, &sender).await; - } - Some(interact_cli::InteractCliCommand::GetUserAddresses) => { - let user_addresses = interact.get_user_addresses().await; - println!("User addresses: "); - for address in user_addresses { - print!("{address} "); - } + interact + .pong(&interact.alice_wallet_address.clone(), None) + .await; } - Some(interact_cli::InteractCliCommand::GetContractState) => { - let contract_state = interact.get_contract_state().await; - println!("Contract state: ping_amount -> {:#?} | deadline -> {:#?} | activation_timestamp -> {:#?} | max_funds -> {:#?} | pong_all_last_user -> {:#?}", - contract_state.ping_amount, - contract_state.deadline, - contract_state.activation_timestamp, - contract_state.max_funds, - contract_state.pong_all_last_user); + 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::GetPingAmount) => { - let ping_amount = interact.get_ping_amount().await; - println!("Ping amount: {}", ping_amount); + 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::GetDeadline) => { - let deadline = interact.get_deadline().await; - println!("Deadline: {}", deadline); + 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::GetActivationTimestamp) => { - let activation_timestamp = interact.get_activation_timestamp().await; - println!("Activation timestamp: {}", activation_timestamp); + Some(interact_cli::InteractCliCommand::GetAcceptedPaymentToken) => { + interact.accepted_payment_token_id().await; } - Some(interact_cli::InteractCliCommand::GetMaxFunds) => { - let max_funds = interact.get_max_funds().await; - match max_funds { - Some(funds) => println!("Max funds: {}", funds), - None => println!("Max funds: none"), - } + Some(interact_cli::InteractCliCommand::GetPingAmount) => { + interact.ping_amount().await; } - Some(interact_cli::InteractCliCommand::GetUserStatus(args)) => { - let user_status = interact.get_user_status(args.id).await; - match user_status { - UserStatus::New => println!("User status: unknown"), - UserStatus::Registered => println!("User status: `ping`-ed"), - UserStatus::Withdrawn => println!("User status: `pong`-ed"), - } + Some(interact_cli::InteractCliCommand::GetDurationTimestamp) => { + interact.duration_in_seconds().await; } - Some(interact_cli::InteractCliCommand::PongAllLastUser) => { - let pong_all_last_user = interact.pong_all_last_user().await; - println!("Pong all last user: {pong_all_last_user}"); + Some(interact_cli::InteractCliCommand::GetUserPingTimestamp(args)) => { + let address = Bech32Address::from_bech32_string(args.address.clone()); + interact.user_ping_timestamp(address).await; } None => {} } } -pub struct PingPongEgldInteract { +pub struct PingPongInteract { pub interactor: Interactor, - pub ping_pong_owner_address: Bech32Address, - pub wallet_address: Bech32Address, + pub alice_wallet_address: Bech32Address, + pub mike_wallet_address: Bech32Address, pub state: State, } -impl PingPongEgldInteract { - pub async fn init(config: Config) -> Self { +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()) - .with_tracer(INTERACTOR_SCENARIO_TRACE_PATH) - .await; + .use_chain_simulator(config.use_chain_simulator()); - interactor.set_current_dir_from_workspace("ping-pong/interactor"); - let ping_pong_owner_address = interactor.register_wallet(test_wallets::eve()).await; - let wallet_address = interactor.register_wallet(test_wallets::mallory()).await; + 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(); - Self { + PingPongInteract { interactor, - ping_pong_owner_address: ping_pong_owner_address.into(), - wallet_address: wallet_address.into(), + alice_wallet_address: alice_wallet_address.into(), + mike_wallet_address: mike_wallet_address.into(), state: State::load_state(), } } - pub async fn set_state(&mut self) { - println!("wallet address: {}", self.wallet_address); - self.interactor - .retrieve_account(&self.ping_pong_owner_address) - .await; - self.interactor.retrieve_account(&self.wallet_address).await; - } - pub async fn deploy( &mut self, ping_amount: RustBigUint, duration_in_seconds: u64, - opt_activation_timestamp: Option, - max_funds: OptionalValue, - ) -> (u64, String) { - self.set_state().await; - - let (new_address, status, message) = self + token_id: String, + ) { + let new_address = self .interactor .tx() - .from(&self.ping_pong_owner_address) + .from(&self.alice_wallet_address) .gas(30_000_000u64) - .typed(proxy_ping_pong_egld::PingPongProxy) + .typed(ping_pong_proxy::PingPongProxy) .init( ping_amount, duration_in_seconds, - opt_activation_timestamp, - max_funds, + OptionalValue::Some(get_token_identifier(token_id)), ) .code(PING_PONG_CODE) - .returns(ReturnsNewBech32Address) - .returns(ReturnsStatus) - .returns(ReturnsMessage) + .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}"); - self.state.set_ping_pong_egld_address(new_address); - - (status, message) + println!("new address: {new_address_bech32}"); } - pub async fn upgrade( - &mut self, - ping_amount: RustBigUint, - duration_in_seconds: u64, - opt_activation_timestamp: Option, - max_funds: OptionalValue, - ) { - let response = self + pub async fn upgrade(&mut self, ping_amount: RustBigUint, duration_in_seconds: u64) { + let upgrade_address = self .interactor .tx() - .to(self.state.current_ping_pong_egld_address()) - .from(&self.wallet_address) + .from(&self.alice_wallet_address) + .to(self.state.current_ping_pong_address()) .gas(30_000_000u64) - .typed(proxy_ping_pong_egld::PingPongProxy) - .upgrade( - ping_amount, - duration_in_seconds, - opt_activation_timestamp, - max_funds, - ) + .typed(ping_pong_proxy::PingPongProxy) + .upgrade(ping_amount, duration_in_seconds) .code(PING_PONG_CODE) .returns(ReturnsNewAddress) .run() .await; - println!("Result: {response:?}"); + 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, egld_amount: u64, message: Option<&str>, sender: &Bech32Address) { + pub async fn ping( + &mut self, + token_id: String, + nonce: u64, + amount: u64, + sender: &Bech32Address, + message: Option<&str>, + ) { let response = self .interactor .tx() .from(sender) - .to(self.state.current_ping_pong_egld_address()) + .to(self.state.current_ping_pong_address()) .gas(30_000_000u64) - .typed(proxy_ping_pong_egld::PingPongProxy) + .typed(ping_pong_proxy::PingPongProxy) .ping() - .egld(egld_amount) + .payment(EgldOrEsdtTokenPayment::new( + get_token_identifier(token_id), + nonce, + BigUint::from(amount), + )) .returns(ReturnsHandledOrError::new()) .run() .await; match response { - Ok(_) => println!("Ping successful!"), + Ok(_) => println!("Ping successfully executed"), Err(err) => { println!("Ping failed with message: {}", err.message); assert_eq!(message.unwrap_or_default(), err.message); @@ -230,21 +195,21 @@ impl PingPongEgldInteract { } } - pub async fn pong(&mut self, message: Option<&str>, sender: &Bech32Address) { + pub async fn pong(&mut self, sender: &Bech32Address, message: Option<&str>) { let response = self .interactor .tx() .from(sender) - .to(self.state.current_ping_pong_egld_address()) + .to(self.state.current_ping_pong_address()) .gas(30_000_000u64) - .typed(proxy_ping_pong_egld::PingPongProxy) + .typed(ping_pong_proxy::PingPongProxy) .pong() .returns(ReturnsHandledOrError::new()) .run() .await; match response { - Ok(_) => println!("Pong successful!"), + Ok(_) => println!("Pong successfully executed"), Err(err) => { println!("Pong failed with message: {}", err.message); assert_eq!(message.unwrap_or_default(), err.message); @@ -252,121 +217,104 @@ impl PingPongEgldInteract { } } - pub async fn pong_all(&mut self, message: Option, sender: &Bech32Address) { - let response = self - .interactor - .tx() - .from(sender) - .to(self.state.current_ping_pong_egld_address()) - .gas(30_000_000u64) - .typed(proxy_ping_pong_egld::PingPongProxy) - .pong_all() - .returns(ReturnsHandledOrError::new()) - .run() - .await; - - match response { - Ok(_) => println!("Pong All successful!"), - Err(err) => { - println!("Pong All failed with message: {}", err.message); - assert_eq!(message.unwrap_or_default(), err.message); - } - } - } - - pub async fn get_user_addresses(&mut self) -> Vec { - let response = self - .interactor - .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .get_user_addresses() - .returns(ReturnsResult) - .run() - .await; - - let mut response_vec: Vec = Vec::new(); - for r in response.to_vec().into_vec() { - response_vec.push(r.as_managed_buffer().to_string()); - } - - response_vec - } - - pub async fn get_contract_state(&mut self) -> ContractState { + pub async fn did_user_ping(&mut self, address: Bech32Address) -> bool { self.interactor .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .get_contract_state() - .returns(ReturnsResult) + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .did_user_ping(address) + .returns(ReturnsResultUnmanaged) .run() .await } - pub async fn get_ping_amount(&mut self) -> RustBigUint { + pub async fn get_pong_enable_timestamp(&mut self, address: Bech32Address) -> u64 { self.interactor .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .ping_amount() + .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_deadline(&mut self) -> u64 { - self.interactor + pub async fn get_time_to_pong(&mut self, address: Bech32Address) -> Option { + let result_value = self + .interactor .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .deadline() + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .get_time_to_pong(address) .returns(ReturnsResultUnmanaged) .run() - .await + .await; + + match result_value { + OptionalValue::Some(time) => Some(time), + OptionalValue::None => { + println!("Address unavailable"); + None + } + } } - pub async fn get_activation_timestamp(&mut self) -> u64 { - self.interactor + pub async fn accepted_payment_token_id(&mut self) -> String { + let result_value = self + .interactor .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .activation_timestamp() + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .accepted_payment_token_id() .returns(ReturnsResultUnmanaged) .run() - .await + .await; + + if result_value.is_egld() { + return EGLD.to_owned(); + } + + result_value.into_esdt_option().unwrap().to_string() } - pub async fn get_max_funds(&mut self) -> Option { + pub async fn ping_amount(&mut self) -> RustBigUint { self.interactor .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .max_funds() + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .ping_amount() .returns(ReturnsResultUnmanaged) .run() .await } - pub async fn get_user_status(&mut self, user_id: usize) -> UserStatus { + pub async fn duration_in_seconds(&mut self) -> u64 { self.interactor .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .user_status(user_id) + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .duration_in_seconds() .returns(ReturnsResultUnmanaged) .run() .await } - pub async fn pong_all_last_user(&mut self) -> usize { + pub async fn user_ping_timestamp(&mut self, address: Bech32Address) -> u64 { self.interactor .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .pong_all_last_user() + .to(self.state.current_ping_pong_address()) + .typed(ping_pong_proxy::PingPongProxy) + .user_ping_timestamp(address) .returns(ReturnsResultUnmanaged) .run() .await } } + +fn get_token_identifier(token_id: String) -> EgldOrEsdtTokenIdentifier { + if token_id.to_uppercase().eq(EGLD) { + EgldOrEsdtTokenIdentifier::egld() + } else { + EgldOrEsdtTokenIdentifier::esdt(&token_id) + } +} diff --git a/ping-pong/interactor/src/interact_cli.rs b/ping-pong/interactor/src/interact_cli.rs index 700f694..b0ee3da 100644 --- a/ping-pong/interactor/src/interact_cli.rs +++ b/ping-pong/interactor/src/interact_cli.rs @@ -16,7 +16,7 @@ pub enum InteractCliCommand { #[command(name = "deploy", about = "Deploy contract.")] Deploy(DeployArgs), #[command(name = "upgrade", about = "Upgrade contract.")] - Upgrade(DeployArgs), + Upgrade(UpgradeArgs), #[command( name = "ping", about = "User sends some EGLD to be locked in the contract for a period of time." @@ -24,33 +24,26 @@ pub enum InteractCliCommand { Ping(PingArgs), #[command(name = "pong", about = "User can take back funds from the contract.")] Pong, - #[command(name = "pong-all", about = "Send back funds to all users who pinged.")] - PongAll, + #[command(name = "did-user-ping", about = "Returns if a user ping-ed or not")] + DidUserPing(DidUserPingArgs), #[command( - name = "user-addresses", - about = "Lists the addresses of all users that have `ping`-ed in the order they have `ping`-ed." + name = "pong-enable", + about = "Returns the timestamp when pong is enabled." )] - GetUserAddresses, - #[command(name = "contract-state", about = "Returns the current contract state.")] - GetContractState, + 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 = "deadline", about = "Return deadline.")] - GetDeadline, + #[command(name = "duration", about = "Returns the duration in seconds.")] + GetDurationTimestamp, #[command( - name = "activation-timestamp", - about = "Block timestamp of the block where the contract got activated. If not specified in the constructor it is the the deploy block timestamp." + name = "user-ping", + about = "Returns the timestamp at which the user pinged" )] - GetActivationTimestamp, - #[command(name = "max-funds", about = "Optional funding cap.")] - GetMaxFunds, - #[command(name = "user-status", about = "State of user funds.")] - GetUserStatus(UserStatusArgs), - #[command( - name = "pong-all-last-user", - about = "`pongAll` status, the last user to be processed. 0 if never called `pongAll` or `pongAll` completed." - )] - PongAllLastUser, + GetUserPingTimestamp(GetUserPingTimestampArgs), } #[derive(Default, Clone, PartialEq, Eq, Debug, Args)] @@ -61,21 +54,51 @@ pub struct DeployArgs { #[arg(short = 'd', long = "duration-in-seconds")] pub duration_in_seconds: u64, - #[arg(short = 'a', long = "activation-timestamp")] - pub opt_activation_timestamp: Option, + #[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 = 'm', long = "max-funds")] - pub max_funds: Option, + #[arg(short = 'd', long = "duration-in-seconds")] + pub duration_in_seconds: u64, } #[derive(Default, Clone, PartialEq, Eq, Debug, Args)] pub struct PingArgs { - #[arg(short = 'c', long = "cost", default_value = "50000000000000000")] - pub cost: Option, + #[arg(short = 't', long = "token")] + pub token: String, + + #[arg(short = 'n', long = "nonce")] + pub nonce: u64, + + #[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 UserStatusArgs { - #[arg(short = 'i')] - pub id: usize, +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 index 013e167..bc35101 100644 --- a/ping-pong/interactor/src/interact_config.rs +++ b/ping-pong/interactor/src/interact_config.rs @@ -11,7 +11,7 @@ pub enum ChainType { Simulator, } -/// Ping Pong Interact configuration +/// Contract Interact configuration #[derive(Debug, Deserialize)] pub struct Config { pub gateway_uri: String, diff --git a/ping-pong/interactor/src/interact_main.rs b/ping-pong/interactor/src/interact_main.rs index e9af240..5143ded 100644 --- a/ping-pong/interactor/src/interact_main.rs +++ b/ping-pong/interactor/src/interact_main.rs @@ -1,6 +1,9 @@ -extern crate ping_pong_interact; + +use multiversx_sc_snippets::imports::*; +use ping_pong_interact::ping_pong_cli; #[tokio::main] -pub async fn main() { - ping_pong_interact::ping_pong_egld_cli().await; -} +async fn main() { + ping_pong_cli().await; +} + diff --git a/ping-pong/interactor/src/interact_state.rs b/ping-pong/interactor/src/interact_state.rs index 38fb3d3..74694c4 100644 --- a/ping-pong/interactor/src/interact_state.rs +++ b/ping-pong/interactor/src/interact_state.rs @@ -11,7 +11,7 @@ const STATE_FILE: &str = "state.toml"; /// Multisig Interact state #[derive(Debug, Default, Serialize, Deserialize)] pub struct State { - ping_pong_egld_address: Option, + ping_pong_address: Option, } impl State { @@ -28,13 +28,13 @@ impl State { } /// Sets the ping pong address - pub fn set_ping_pong_egld_address(&mut self, address: Bech32Address) { - self.ping_pong_egld_address = Some(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_egld_address(&self) -> &Bech32Address { - self.ping_pong_egld_address + pub fn current_ping_pong_address(&self) -> &Bech32Address { + self.ping_pong_address .as_ref() .expect("no known ping pong contract, deploy first") } diff --git a/ping-pong/src/proxy_ping_pong_egld.rs b/ping-pong/interactor/src/ping_pong_proxy.rs similarity index 53% rename from ping-pong/src/proxy_ping_pong_egld.rs rename to ping-pong/interactor/src/ping_pong_proxy.rs index 803954e..5e67e29 100644 --- a/ping-pong/src/proxy_ping_pong_egld.rs +++ b/ping-pong/interactor/src/ping_pong_proxy.rs @@ -44,29 +44,25 @@ where Gas: TxGas, { /// Necessary configuration when deploying: - /// `ping_amount` - the exact EGLD amount that needs to be sent when `ping`-ing. - /// `duration_in_seconds` - how much time (in seconds) until contract expires. - /// `opt_activation_timestamp` - optionally specify the contract to only activate at a later date. - /// `max_funds` - optional funding cap, no more funds than this can be added to the contract. + /// `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>, - Arg3: ProxyArg>>, + Arg2: ProxyArg>>, >( self, ping_amount: Arg0, duration_in_seconds: Arg1, - opt_activation_timestamp: Arg2, - max_funds: Arg3, + opt_token_id: Arg2, ) -> TxTypedDeploy { self.wrapped_tx .payment(NotPayable) .raw_deploy() .argument(&ping_amount) .argument(&duration_in_seconds) - .argument(&opt_activation_timestamp) - .argument(&max_funds) + .argument(&opt_token_id) .original_result() } } @@ -83,22 +79,16 @@ where pub fn upgrade< Arg0: ProxyArg>, Arg1: ProxyArg, - Arg2: ProxyArg>, - Arg3: ProxyArg>>, >( self, ping_amount: Arg0, duration_in_seconds: Arg1, - opt_activation_timestamp: Arg2, - max_funds: Arg3, ) -> TxTypedUpgrade { self.wrapped_tx .payment(NotPayable) .raw_upgrade() .argument(&ping_amount) .argument(&duration_in_seconds) - .argument(&opt_activation_timestamp) - .argument(&max_funds) .original_result() } } @@ -112,7 +102,7 @@ where To: TxTo, Gas: TxGas, { - /// User sends some EGLD to be locked in the contract for a period of time. + /// User sends some tokens to be locked in the contract for a period of time. pub fn ping( self, ) -> TxTypedCall { @@ -132,127 +122,82 @@ where .original_result() } - /// Send back funds to all users who pinged. - /// Returns - /// - `completed` if everything finished - /// - `interrupted` if run out of gas midway. - /// Can only be called after expiration. - pub fn pong_all( + pub fn did_user_ping< + Arg0: ProxyArg>, + >( self, - ) -> TxTypedCall { + address: Arg0, + ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) - .raw_call("pongAll") + .raw_call("didUserPing") + .argument(&address) .original_result() } - /// Lists the addresses of all users that have `ping`-ed, - /// in the order they have `ping`-ed - pub fn get_user_addresses( + pub fn get_pong_enable_timestamp< + Arg0: ProxyArg>, + >( self, - ) -> TxTypedCall>> { + address: Arg0, + ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) - .raw_call("getUserAddresses") + .raw_call("getPongEnableTimestamp") + .argument(&address) .original_result() } - /// Returns the current contract state as a struct - /// for faster fetching from external parties - pub fn get_contract_state( + pub fn get_time_to_pong< + Arg0: ProxyArg>, + >( self, - ) -> TxTypedCall> { + address: Arg0, + ) -> TxTypedCall> { self.wrapped_tx .payment(NotPayable) - .raw_call("getContractState") + .raw_call("getTimeToPong") + .argument(&address) .original_result() } - pub fn ping_amount( + pub fn accepted_payment_token_id( self, - ) -> TxTypedCall> { + ) -> TxTypedCall> { self.wrapped_tx .payment(NotPayable) - .raw_call("getPingAmount") + .raw_call("getAcceptedPaymentToken") .original_result() } - pub fn deadline( + pub fn ping_amount( self, - ) -> TxTypedCall { + ) -> TxTypedCall> { self.wrapped_tx .payment(NotPayable) - .raw_call("getDeadline") + .raw_call("getPingAmount") .original_result() } - /// Block timestamp of the block where the contract got activated. - /// If not specified in the constructor it is the deploy block timestamp. - pub fn activation_timestamp( + pub fn duration_in_seconds( self, ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) - .raw_call("getActivationTimestamp") + .raw_call("getDurationTimestamp") .original_result() } - /// Optional funding cap. - pub fn max_funds( - self, - ) -> TxTypedCall>> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getMaxFunds") - .original_result() - } - - /// State of user funds. - /// 0 - user unknown, never `ping`-ed - /// 1 - `ping`-ed - /// 2 - `pong`-ed - pub fn user_status< - Arg0: ProxyArg, + pub fn user_ping_timestamp< + Arg0: ProxyArg>, >( self, - user_id: Arg0, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getUserStatus") - .argument(&user_id) - .original_result() - } - - /// Part of the `pongAll` status, the last user to be processed. - /// 0 if never called `pongAll` or `pongAll` completed. - pub fn pong_all_last_user( - self, - ) -> TxTypedCall { + address: Arg0, + ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) - .raw_call("pongAllLastUser") + .raw_call("getUserPingTimestamp") + .argument(&address) .original_result() } } - -#[type_abi] -#[derive(TopEncode, TopDecode, Default)] -pub struct ContractState -where - Api: ManagedTypeApi, -{ - pub ping_amount: BigUint, - pub deadline: u64, - pub activation_timestamp: u64, - pub max_funds: Option>, - pub pong_all_last_user: usize, -} - -#[type_abi] -#[derive(TopEncode, TopDecode, PartialEq, Eq, Clone, Copy)] -pub enum UserStatus { - New, - Registered, - Withdrawn, -} diff --git a/ping-pong/interactor/tests/interact_cs_test.rs b/ping-pong/interactor/tests/interact_cs_test.rs deleted file mode 100644 index 799cbc1..0000000 --- a/ping-pong/interactor/tests/interact_cs_test.rs +++ /dev/null @@ -1,138 +0,0 @@ -use multiversx_sc_snippets::imports::RustBigUint; -use ping_pong_interact::{Config, PingPongEgldInteract}; - -#[tokio::test] -#[cfg_attr(not(feature = "chain-simulator-tests"), ignore)] -async fn test_ping_pong_egld() { - let mut interact = PingPongEgldInteract::init(Config::chain_simulator_config()).await; - let wallet_address = interact.wallet_address.clone(); - let ping_pong_owner_address = interact.ping_pong_owner_address.clone(); - - let ping_amount = 1u64; - - // test_ping_unmatched_amount - let duration_in_seconds = 5u64; - let opt_activation_timestamp = 2u64; - let max_funds = 100_000u64; - - interact - .deploy( - ping_amount.into(), - duration_in_seconds, - Some(opt_activation_timestamp), - multiversx_sc_snippets::imports::OptionalValue::Some(max_funds.into()), - ) - .await; - - interact - .ping( - 0u64, - Some("the payment must match the fixed sum"), - &wallet_address, - ) - .await; - - // test_ping_inactive_contracts - let duration_in_seconds = 5u64; - let opt_activation_timestamp = 2_000_000_000u64; - let max_funds = 100_000u64; - - interact - .deploy( - ping_amount.into(), - duration_in_seconds, - Some(opt_activation_timestamp), - multiversx_sc_snippets::imports::OptionalValue::Some(max_funds.into()), - ) - .await; - - interact - .ping(1u64, Some("smart contract not active yet"), &wallet_address) - .await; - - // test_ping_passed_deadline - let duration_in_seconds = 5u64; - let opt_activation_timestamp = 2u64; - let max_funds = 100_000u64; - - interact - .deploy( - ping_amount.into(), - duration_in_seconds, - Some(opt_activation_timestamp), - multiversx_sc_snippets::imports::OptionalValue::Some(max_funds.into()), - ) - .await; - - interact - .ping(1u64, Some("deadline has passed"), &wallet_address) - .await; - - // test_ping_max_funds - let ping_amount = 10u64; - let duration_in_seconds = 30000u64; - let max_funds = 10u64; - - interact - .deploy( - ping_amount.into(), - duration_in_seconds, - None, - multiversx_sc_snippets::imports::OptionalValue::Some(max_funds.into()), - ) - .await; - - interact - .ping(10u64, Some("smart contract full"), &wallet_address) - .await; - - // test ping - let ping_amount = 1u64; - let duration_in_seconds = 20u64; - let max_funds = 100_000u64; - - interact - .deploy( - ping_amount.into(), - duration_in_seconds, - None, - multiversx_sc_snippets::imports::OptionalValue::Some(max_funds.into()), - ) - .await; - - interact.ping(1u64, None, &wallet_address).await; - interact - .ping(1u64, Some("can only ping once"), &wallet_address) - .await; - - assert_eq!(interact.get_ping_amount().await, RustBigUint::from(1u64)); - - interact - .pong(Some("can't withdraw before deadline"), &wallet_address) - .await; - - interact.pong(None, &wallet_address).await; - - // test_pong_all - let ping_amount = 1u64; - let duration_in_seconds = 18u64; - let max_funds = 100_000u64; - - interact - .deploy( - ping_amount.into(), - duration_in_seconds, - None, - multiversx_sc_snippets::imports::OptionalValue::Some(max_funds.into()), - ) - .await; - - interact.ping(1u64, None, &ping_pong_owner_address).await; - - interact.ping(1u64, None, &wallet_address).await; - - interact.pong_all(None, &ping_pong_owner_address).await; - interact - .pong(Some("already withdrawn"), &wallet_address) - .await; -} 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..7590994 --- /dev/null +++ b/ping-pong/interactor/tests/interact_cs_tests.rs @@ -0,0 +1,42 @@ +use multiversx_sc_snippets::imports::*; +use ping_pong_interact::{Config, PingPongInteract, EGLD}; + +#[tokio::test] +#[cfg_attr(not(feature = "chain-simulator-tests"), ignore)] +async fn 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.to_string(), + 0, + 2u64, + &alice, + Some("The payment must match the fixed ping amount"), + ) + .await; + interactor + .ping(EGLD.to_string(), 0, 1u64, &alice, None) + .await; + assert!(interactor.did_user_ping(alice.clone()).await); + + assert!(!interactor.did_user_ping(mike.clone()).await); + interactor + .ping(EGLD.to_string(), 0, 1u64, &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/meta/Cargo.toml b/ping-pong/meta/Cargo.toml index 5ee62a7..a106cb4 100644 --- a/ping-pong/meta/Cargo.toml +++ b/ping-pong/meta/Cargo.toml @@ -10,4 +10,3 @@ path = ".." [dependencies.multiversx-sc-meta-lib] version = "0.54.6" -default-features = false diff --git a/ping-pong/sc-config.toml b/ping-pong/sc-config.toml index bb96777..109751f 100644 --- a/ping-pong/sc-config.toml +++ b/ping-pong/sc-config.toml @@ -1,2 +1,3 @@ + [[proxy]] -path = "src/proxy_ping_pong_egld.rs" +path = "interactor/src/ping_pong_proxy.rs" diff --git a/ping-pong/scenarios/ping-pong-call-ping-after-deadline.scen.json b/ping-pong/scenarios/ping-pong-call-ping-after-deadline.scen.json deleted file mode 100644 index 6c48054..0000000 --- a/ping-pong/scenarios/ping-pong-call-ping-after-deadline.scen.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "try to ping before the contract has begun", - "gasSchedule": "v3", - "steps": [ - { - "step": "externalSteps", - "path": "ping-pong-init.scen.json" - }, - { - "step": "setState", - "currentBlockInfo": { - "blockTimestamp": "123,781" - } - }, - { - "step": "scCall", - "tx": { - "from": "address:participant1", - "to": "sc:ping-pong", - "egldValue": "500,000,000,000", - "function": "ping", - "arguments": [], - "gasLimit": "10,000,000", - "gasPrice": "0" - }, - "expect": { - "out": [], - "status": "4", - "message": "str:deadline has passed", - "gas": "*", - "refund": "*" - } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "1", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "1", - "balance": "1,800,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "0", - "balance": "2,500,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "0", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } - } - ] -} diff --git a/ping-pong/scenarios/ping-pong-call-ping-before-activation.scen.json b/ping-pong/scenarios/ping-pong-call-ping-before-activation.scen.json deleted file mode 100644 index accfd11..0000000 --- a/ping-pong/scenarios/ping-pong-call-ping-before-activation.scen.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "try to ping before the contract has begun", - "gasSchedule": "v3", - "steps": [ - { - "step": "externalSteps", - "path": "ping-pong-init.scen.json" - }, - { - "step": "setState", - "currentBlockInfo": { - "blockTimestamp": "779" - } - }, - { - "step": "scCall", - "tx": { - "from": "address:participant1", - "to": "sc:ping-pong", - "egldValue": "500,000,000,000", - "function": "ping", - "arguments": [], - "gasLimit": "10,000,000", - "gasPrice": "0" - }, - "expect": { - "out": [], - "status": "4", - "message": "str:smart contract not active yet", - "gas": "*", - "refund": "*" - } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "1", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "1", - "balance": "1,800,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "0", - "balance": "2,500,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "0", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } - } - ] -} diff --git a/ping-pong/scenarios/ping-pong-call-ping-before-beginning.scen.json b/ping-pong/scenarios/ping-pong-call-ping-before-beginning.scen.json deleted file mode 100644 index 64c876e..0000000 --- a/ping-pong/scenarios/ping-pong-call-ping-before-beginning.scen.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "try to ping before the contract has begun", - "gasSchedule": "v3", - "steps": [ - { - "step": "externalSteps", - "path": "ping-pong-init.scen.json" - }, - { - "step": "setState", - "currentBlockInfo": { - "blockTimestamp": "779" - } - }, - { - "step": "scCall", - "tx": { - "from": "address:participant1", - "to": "sc:ping-pong", - "egldValue": "500,000,000,000", - "function": "ping", - "arguments": [], - "gasLimit": "10,000,000", - "gasPrice": "0" - }, - "expect": { - "out": [], - "status": "4", - "message": "str:smart contract not active yet", - "gas": "*", - "refund": "*" - } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "1", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "1", - "balance": "1,800,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "0", - "balance": "2,500,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "0", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } - } - ] -} \ No newline at end of file diff --git a/ping-pong/scenarios/ping-pong-call-ping-second-user.scen.json b/ping-pong/scenarios/ping-pong-call-ping-second-user.scen.json index 20d3d81..978dfe1 100644 --- a/ping-pong/scenarios/ping-pong-call-ping-second-user.scen.json +++ b/ping-pong/scenarios/ping-pong-call-ping-second-user.scen.json @@ -1,6 +1,6 @@ { "name": "call ping for a second user", - "gasSchedule": "v3", + "gasSchedule": "v4", "steps": [ { "step": "externalSteps", @@ -17,14 +17,13 @@ "tx": { "from": "address:participant2", "to": "sc:ping-pong", - "egldValue": "500,000,000,000", + "value": "500,000,000,000", "function": "ping", "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, "expect": { - "out": [], "status": "", "gas": "*", "refund": "*" @@ -34,17 +33,17 @@ "step": "checkState", "accounts": { "address:my_address": { - "nonce": "1", + "nonce": "*", "balance": "1,000,000", "storage": {} }, "address:participant1": { - "nonce": "1", + "nonce": "*", "balance": "1,300,000,000,000", "storage": {} }, "address:participant2": { - "nonce": "1", + "nonce": "*", "balance": "2,000,000,000,000", "storage": {} }, @@ -53,19 +52,14 @@ "balance": "1,000,000,000,000", "storage": { "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780", - "str:user_address_to_id|address:participant1": "1", - "str:user_id_to_address|0x0000001": "address:participant1", - "str:userStatus|0x0000001": "1", - "str:user_address_to_id|address:participant2": "2", - "str:user_id_to_address|0x0000002": "address:participant2", - "str:userStatus|0x0000002": "1", - "str:user_count": "2" + "str:durationInSeconds": "123,000", + "str:acceptedPaymentTokenId": "str:EGLD", + "str:userPingTimestamp|address:participant1": "781", + "str:userPingTimestamp|address:participant2": "781" }, "code": "mxsc:../output/ping-pong.mxsc.json" } } } ] -} +} \ No newline at end of file diff --git a/ping-pong/scenarios/ping-pong-call-ping-twice.scen.json b/ping-pong/scenarios/ping-pong-call-ping-twice.scen.json index 6830f3b..b199133 100644 --- a/ping-pong/scenarios/ping-pong-call-ping-twice.scen.json +++ b/ping-pong/scenarios/ping-pong-call-ping-twice.scen.json @@ -1,6 +1,6 @@ { "name": "call ping a second time", - "gasSchedule": "v3", + "gasSchedule": "v4", "steps": [ { "step": "externalSteps", @@ -17,53 +17,18 @@ "tx": { "from": "address:participant1", "to": "sc:ping-pong", - "egldValue": "500,000,000,000", + "value": "500,000,000,000", "function": "ping", "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, "expect": { - "out": [], "status": "4", - "message": "str:can only ping once", + "message": "str:Already pinged", "gas": "*", "refund": "*" } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "1", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "2", - "balance": "1,300,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "0", - "balance": "2,500,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "500,000,000,000", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780", - "str:user_address_to_id|address:participant1": "1", - "str:user_id_to_address|0x0000001": "address:participant1", - "str:userStatus|0x0000001": "1", - "str:user_count": "1" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } } ] } diff --git a/ping-pong/scenarios/ping-pong-call-ping-wrong-ammount.scen.json b/ping-pong/scenarios/ping-pong-call-ping-wrong-ammount.scen.json deleted file mode 100644 index 98e4f68..0000000 --- a/ping-pong/scenarios/ping-pong-call-ping-wrong-ammount.scen.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "try to ping the wrong ammount of funds", - "gasSchedule": "v3", - "steps": [ - { - "step": "externalSteps", - "path": "ping-pong-init.scen.json" - }, - { - "step": "scCall", - "tx": { - "from": "address:participant1", - "to": "sc:ping-pong", - "egldValue": "450,000,000,000", - "function": "ping", - "arguments": [], - "gasLimit": "10,000,000", - "gasPrice": "0" - }, - "expect": { - "out": [], - "status": "4", - "message": "str:the payment must match the fixed sum", - "gas": "*", - "refund": "*" - } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "1", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "1", - "balance": "1,800,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "0", - "balance": "2,500,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "0", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } - } - ] -} diff --git a/ping-pong/scenarios/ping-pong-call-get-user-addresses.scen.json b/ping-pong/scenarios/ping-pong-call-ping-wrong-amount.scen.json similarity index 53% rename from ping-pong/scenarios/ping-pong-call-get-user-addresses.scen.json rename to ping-pong/scenarios/ping-pong-call-ping-wrong-amount.scen.json index 08185b7..10e303c 100644 --- a/ping-pong/scenarios/ping-pong-call-get-user-addresses.scen.json +++ b/ping-pong/scenarios/ping-pong-call-ping-wrong-amount.scen.json @@ -1,28 +1,25 @@ { - "name": "call getUserAddresses in order to check who registered", - "gasSchedule": "v3", + "name": "try to ping the wrong ammount of funds", + "gasSchedule": "v4", "steps": [ { "step": "externalSteps", - "path": "ping-pong-call-ping-second-user.scen.json" + "path": "ping-pong-init.scen.json" }, { "step": "scCall", "tx": { "from": "address:participant1", "to": "sc:ping-pong", - "function": "getUserAddresses", + "value": "450,000,000,000", + "function": "ping", "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, "expect": { - "out": [ - "address:participant1", - "address:participant2" - ], - "status": "0", - "message": "", + "status": "4", + "message": "str:The payment must match the fixed ping amount", "gas": "*", "refund": "*" } diff --git a/ping-pong/scenarios/ping-pong-call-ping.scen.json b/ping-pong/scenarios/ping-pong-call-ping.scen.json index 826e88b..45fa8fd 100644 --- a/ping-pong/scenarios/ping-pong-call-ping.scen.json +++ b/ping-pong/scenarios/ping-pong-call-ping.scen.json @@ -1,6 +1,6 @@ { "name": "ping in order to deposit funds", - "gasSchedule": "v3", + "gasSchedule": "v4", "steps": [ { "step": "externalSteps", @@ -17,14 +17,13 @@ "tx": { "from": "address:participant1", "to": "sc:ping-pong", - "egldValue": "500,000,000,000", + "value": "500,000,000,000", "function": "ping", "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, "expect": { - "out": [], "status": "", "gas": "*", "refund": "*" @@ -34,17 +33,17 @@ "step": "checkState", "accounts": { "address:my_address": { - "nonce": "1", + "nonce": "*", "balance": "1,000,000", "storage": {} }, "address:participant1": { - "nonce": "1", + "nonce": "*", "balance": "1,300,000,000,000", "storage": {} }, "address:participant2": { - "nonce": "0", + "nonce": "*", "balance": "2,500,000,000,000", "storage": {} }, @@ -53,12 +52,9 @@ "balance": "500,000,000,000", "storage": { "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780", - "str:user_address_to_id|address:participant1": "1", - "str:user_id_to_address|0x0000001": "address:participant1", - "str:userStatus|0x0000001": "1", - "str:user_count": "1" + "str:durationInSeconds": "123,000", + "str:acceptedPaymentTokenId": "str:EGLD", + "str:userPingTimestamp|address:participant1": "781" }, "code": "mxsc:../output/ping-pong.mxsc.json" } diff --git a/ping-pong/scenarios/ping-pong-call-pong-all-after-pong.scen.json b/ping-pong/scenarios/ping-pong-call-pong-all-after-pong.scen.json deleted file mode 100644 index 25c22bb..0000000 --- a/ping-pong/scenarios/ping-pong-call-pong-all-after-pong.scen.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "name": "call pong all in order to send the funds back to everyone after one user already got his funds back by calling pong himself", - "gasSchedule": "v3", - "steps": [ - { - "step": "externalSteps", - "path": "ping-pong-call-pong.scen.json" - }, - { - "step": "setState", - "currentBlockInfo": { - "blockTimestamp": "123,781" - } - }, - { - "step": "scCall", - "tx": { - "from": "address:participant1", - "to": "sc:ping-pong", - "function": "pongAll", - "arguments": [], - "gasLimit": "10,000,000", - "gasPrice": "0" - }, - "expect": { - "out": [ - "str:completed" - ], - "status": "0", - "message": "", - "gas": "*", - "refund": "*" - } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "1", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "3", - "balance": "1,800,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "0", - "balance": "2,500,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "0", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780", - "str:user_address_to_id|address:participant1": "1", - "str:user_id_to_address|0x0000001": "address:participant1", - "str:userStatus|0x0000001": "2", - "str:user_count": "1" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } - } - ] -} diff --git a/ping-pong/scenarios/ping-pong-call-pong-all-interrupted-1.scen.json b/ping-pong/scenarios/ping-pong-call-pong-all-interrupted-1.scen.json deleted file mode 100644 index 59ef0c8..0000000 --- a/ping-pong/scenarios/ping-pong-call-pong-all-interrupted-1.scen.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "comment": "initially, the gas is not enough to even perform a single pong", - "gasSchedule": "v3", - "steps": [ - { - "step": "externalSteps", - "path": "ping-pong-call-ping-second-user.scen.json" - }, - { - "step": "setState", - "currentBlockInfo": { - "blockTimestamp": "123,781" - } - }, - { - "step": "scCall", - "id": "pong-all-interrupted-0/2", - "comment": "initially, the gas is only enough to perform 0 of 2 pongs", - "tx": { - "from": "address:participant1", - "to": "sc:ping-pong", - "function": "pongAll", - "arguments": [], - "gasLimit": "6,000,000", - "gasPrice": "0" - }, - "expect": { - "out": [ - "str:interrupted" - ], - "status": "0", - "message": "", - "gas": "*", - "refund": "*" - } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "1", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "2", - "balance": "1,300,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "1", - "balance": "2,000,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "1,000,000,000,000", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780", - "str:user_address_to_id|address:participant1": "1", - "str:user_id_to_address|0x0000001": "address:participant1", - "str:userStatus|0x0000001": "1", - "str:user_address_to_id|address:participant2": "2", - "str:user_id_to_address|0x0000002": "address:participant2", - "str:userStatus|0x0000002": "1", - "str:user_count": "2", - "str:pongAllLastUser": "0" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } - }, - { - "step": "externalSteps", - "comment": "regular pong all with enough gas should continue with no problem", - "path": "ping-pong-call-pong-all.steps.json" - } - ] -} diff --git a/ping-pong/scenarios/ping-pong-call-pong-all-interrupted-2.scen.json b/ping-pong/scenarios/ping-pong-call-pong-all-interrupted-2.scen.json deleted file mode 100644 index 1504901..0000000 --- a/ping-pong/scenarios/ping-pong-call-pong-all-interrupted-2.scen.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "comment": "initially, the gas is only enough to perform 1 of 2 pongs", - "gasSchedule": "v3", - "steps": [ - { - "step": "externalSteps", - "path": "ping-pong-call-ping-second-user.scen.json" - }, - { - "step": "setState", - "currentBlockInfo": { - "blockTimestamp": "123,781" - } - }, - { - "step": "scCall", - "id": "pong-all-interrupted-1/2", - "comment": "initially, the gas is only enough to perform 1 of 2 pongs", - "tx": { - "from": "address:participant1", - "to": "sc:ping-pong", - "function": "pongAll", - "arguments": [], - "gasLimit": "4,500,000", - "gasPrice": "0" - }, - "expect": { - "out": [ - "str:interrupted" - ], - "status": "0", - "message": "", - "gas": "*", - "refund": "*" - } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "1", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "2", - "balance": "1,300,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "1", - "balance": "2,000,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "1,000,000,000,000", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780", - "str:user_address_to_id|address:participant1": "1", - "str:user_id_to_address|0x0000001": "address:participant1", - "str:userStatus|0x0000001": "1", - "str:user_address_to_id|address:participant2": "2", - "str:user_id_to_address|0x0000002": "address:participant2", - "str:userStatus|0x0000002": "1", - "str:user_count": "2" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } - }, - { - "step": "scCall", - "id": "pong-all-interrupted-2/2", - "comment": "only give it enough gas to perform 1 more pong, but this is also the last pong ", - "tx": { - "from": "address:participant1", - "to": "sc:ping-pong", - "function": "pongAll", - "arguments": [], - "gasLimit": "9,000,000", - "gasPrice": "0" - }, - "expect": { - "out": [ - "str:completed" - ], - "status": "0", - "message": "", - "gas": "*", - "refund": "*" - } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "*", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "*", - "balance": "1,800,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "*", - "balance": "2,500,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "*", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780", - "str:user_address_to_id|address:participant1": "1", - "str:user_id_to_address|0x0000001": "address:participant1", - "str:userStatus|0x0000001": "2", - "str:user_address_to_id|address:participant2": "2", - "str:user_id_to_address|0x0000002": "address:participant2", - "str:userStatus|0x0000002": "2", - "str:user_count": "2" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } - }, - { - "step": "externalSteps", - "comment": "regular pong all with enough gas should continue with no problem", - "path": "ping-pong-call-pong-all.steps.json" - } - ] -} diff --git a/ping-pong/scenarios/ping-pong-call-pong-all.scen.json b/ping-pong/scenarios/ping-pong-call-pong-all.scen.json deleted file mode 100644 index 58cbe3c..0000000 --- a/ping-pong/scenarios/ping-pong-call-pong-all.scen.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "call pong all in order to send the funds back to everyone", - "gasSchedule": "v3", - "steps": [ - { - "step": "externalSteps", - "path": "ping-pong-call-ping-second-user.scen.json" - }, - { - "step": "setState", - "currentBlockInfo": { - "blockTimestamp": "123,781" - } - }, - { - "step": "externalSteps", - "path": "ping-pong-call-pong-all.steps.json" - } - ] -} diff --git a/ping-pong/scenarios/ping-pong-call-pong-all.steps.json b/ping-pong/scenarios/ping-pong-call-pong-all.steps.json deleted file mode 100644 index 772f002..0000000 --- a/ping-pong/scenarios/ping-pong-call-pong-all.steps.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "name": "call pong all in order to send the funds back to everyone", - "gasSchedule": "v3", - "steps": [ - { - "step": "scCall", - "id": "pong-all-complete", - "tx": { - "from": "address:participant1", - "to": "sc:ping-pong", - "function": "pongAll", - "arguments": [], - "gasLimit": "10,000,000", - "gasPrice": "0" - }, - "expect": { - "out": [ - "str:completed" - ], - "status": "0", - "message": "", - "gas": "*", - "refund": "*" - } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "*", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "*", - "balance": "1,800,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "*", - "balance": "2,500,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "*", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780", - "str:user_address_to_id|address:participant1": "1", - "str:user_id_to_address|0x0000001": "address:participant1", - "str:userStatus|0x0000001": "2", - "str:user_address_to_id|address:participant2": "2", - "str:user_id_to_address|0x0000002": "address:participant2", - "str:userStatus|0x0000002": "2", - "str:user_count": "2" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } - } - ] -} diff --git a/ping-pong/scenarios/ping-pong-call-pong-before-deadline.scen.json b/ping-pong/scenarios/ping-pong-call-pong-before-deadline.scen.json index 412cc0e..18474e4 100644 --- a/ping-pong/scenarios/ping-pong-call-pong-before-deadline.scen.json +++ b/ping-pong/scenarios/ping-pong-call-pong-before-deadline.scen.json @@ -1,6 +1,6 @@ { "name": "trying withdraw the funds before the deadline has passed", - "gasSchedule": "v3", + "gasSchedule": "v4", "steps": [ { "step": "externalSteps", @@ -17,52 +17,18 @@ "tx": { "from": "address:participant1", "to": "sc:ping-pong", + "value": "0", "function": "pong", "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, "expect": { - "out": [], "status": "4", - "message": "str:can't withdraw before deadline", + "message": "str:Cannot pong before deadline", "gas": "*", "refund": "*" } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "1", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "2", - "balance": "1,300,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "0", - "balance": "2,500,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "500,000,000,000", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780", - "str:user_address_to_id|address:participant1": "1", - "str:user_id_to_address|0x0000001": "address:participant1", - "str:userStatus|0x0000001": "1", - "str:user_count": "1" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } } ] -} +} \ No newline at end of file diff --git a/ping-pong/scenarios/ping-pong-call-pong-twice.scen.json b/ping-pong/scenarios/ping-pong-call-pong-twice.scen.json index 1907d8c..04cec02 100644 --- a/ping-pong/scenarios/ping-pong-call-pong-twice.scen.json +++ b/ping-pong/scenarios/ping-pong-call-pong-twice.scen.json @@ -1,6 +1,6 @@ { "name": "try withdraw the funds again by calling pong a second time", - "gasSchedule": "v3", + "gasSchedule": "v4", "steps": [ { "step": "externalSteps", @@ -11,52 +11,18 @@ "tx": { "from": "address:participant1", "to": "sc:ping-pong", + "value": "0", "function": "pong", "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, "expect": { - "out": [], "status": "4", - "message": "str:already withdrawn", + "message": "str:Must ping first", "gas": "*", "refund": "*" } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "1", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "3", - "balance": "1,800,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "0", - "balance": "2,500,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "0", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780", - "str:user_address_to_id|address:participant1": "1", - "str:user_id_to_address|0x0000001": "address:participant1", - "str:userStatus|0x0000001": "2", - "str:user_count": "1" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } } ] -} +} \ No newline at end of file diff --git a/ping-pong/scenarios/ping-pong-call-pong-without-ping.scen.json b/ping-pong/scenarios/ping-pong-call-pong-without-ping.scen.json index 6b0c576..84d9c6f 100644 --- a/ping-pong/scenarios/ping-pong-call-pong-without-ping.scen.json +++ b/ping-pong/scenarios/ping-pong-call-pong-without-ping.scen.json @@ -1,6 +1,6 @@ { "name": "extracting the funds by calling pong after the deadline has passed", - "gasSchedule": "v3", + "gasSchedule": "v4", "steps": [ { "step": "externalSteps", @@ -17,48 +17,18 @@ "tx": { "from": "address:participant1", "to": "sc:ping-pong", + "value": "0", "function": "pong", "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, "expect": { - "out": [], "status": "4", - "message": "str:can't pong, never pinged", + "message": "str:Must ping first", "gas": "*", "refund": "*" } - }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "1", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "*", - "balance": "1,800,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "0", - "balance": "2,500,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "0", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } } ] -} +} \ No newline at end of file diff --git a/ping-pong/scenarios/ping-pong-call-pong.scen.json b/ping-pong/scenarios/ping-pong-call-pong.scen.json index 3c05dbe..9224e4d 100644 --- a/ping-pong/scenarios/ping-pong-call-pong.scen.json +++ b/ping-pong/scenarios/ping-pong-call-pong.scen.json @@ -1,6 +1,6 @@ { "name": "extracting the funds by calling pong after the deadline has passed", - "gasSchedule": "v3", + "gasSchedule": "v4", "steps": [ { "step": "externalSteps", @@ -17,13 +17,13 @@ "tx": { "from": "address:participant1", "to": "sc:ping-pong", + "value": "0", "function": "pong", "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, "expect": { - "out": [], "status": "0", "message": "", "gas": "*", @@ -34,17 +34,17 @@ "step": "checkState", "accounts": { "address:my_address": { - "nonce": "1", + "nonce": "*", "balance": "1,000,000", "storage": {} }, "address:participant1": { - "nonce": "2", + "nonce": "*", "balance": "1,800,000,000,000", "storage": {} }, "address:participant2": { - "nonce": "0", + "nonce": "*", "balance": "2,500,000,000,000", "storage": {} }, @@ -53,16 +53,12 @@ "balance": "0", "storage": { "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780", - "str:user_address_to_id|address:participant1": "1", - "str:user_id_to_address|0x0000001": "address:participant1", - "str:userStatus|0x0000001": "2", - "str:user_count": "1" + "str:durationInSeconds": "123,000", + "str:acceptedPaymentTokenId": "str:EGLD" }, "code": "mxsc:../output/ping-pong.mxsc.json" } } } ] -} +} \ No newline at end of file diff --git a/ping-pong/scenarios/ping-pong-init.scen.json b/ping-pong/scenarios/ping-pong-init.scen.json index ea7ce79..34a4fae 100644 --- a/ping-pong/scenarios/ping-pong-init.scen.json +++ b/ping-pong/scenarios/ping-pong-init.scen.json @@ -1,81 +1,83 @@ { "name": "ping-pong deployment test", - "gasSchedule": "v3", + "gasSchedule": "v4", "steps": [ - { - "step": "setState", - "accounts": { - "address:my_address": { - "nonce": "0", - "balance": "1,000,000" - }, - "address:participant1": { - "nonce": "0", - "balance": "1,800,000,000,000" - }, - "address:participant2": { - "nonce": "0", - "balance": "2,500,000,000,000" - } - }, - "newAddresses": [ - { - "creatorAddress": "address:my_address", - "creatorNonce": "0", - "newAddress": "sc:ping-pong" - } - ] + { + "step": "setState", + "accounts": { + "address:my_address": { + "nonce": "0", + "balance": "1,000,000", + "storage": {} + }, + "address:participant1": { + "nonce": "0", + "balance": "1,800,000,000,000", + "storage": {} + }, + "address:participant2": { + "nonce": "0", + "balance": "2,500,000,000,000", + "storage": {} + } }, - { - "step": "scDeploy", - "id": "deploy", - "tx": { - "from": "address:my_address", - "contractCode": "mxsc:../output/ping-pong.mxsc.json", - "arguments": [ - "500,000,000,000", - "123,000", - "1|u64:780" - ], - "gasLimit": "10,000,000", - "gasPrice": "0" - }, - "expect": { - "out": [], - "status": "0", - "gas": "*", - "refund": "*" - } + "newAddresses": [ + { + "creatorAddress": "address:my_address", + "creatorNonce": "0", + "newAddress": "sc:ping-pong" + } + ] + }, + { + "step": "scDeploy", + "txId": "deploy", + "tx": { + "from": "address:my_address", + "contractCode": "mxsc:../output/ping-pong.mxsc.json", + "value": "0", + "arguments": [ + "500,000,000,000", + "123,000" + ], + "gasLimit": "10,000,000", + "gasPrice": "0" }, - { - "step": "checkState", - "accounts": { - "address:my_address": { - "nonce": "1", - "balance": "1,000,000", - "storage": {} - }, - "address:participant1": { - "nonce": "0", - "balance": "1,800,000,000,000", - "storage": {} - }, - "address:participant2": { - "nonce": "0", - "balance": "2,500,000,000,000", - "storage": {} - }, - "sc:ping-pong": { - "nonce": "0", - "balance": "0", - "storage": { - "str:pingAmount": "500,000,000,000", - "str:activationTimestamp": "780", - "str:deadline": "123,780" - }, - "code": "mxsc:../output/ping-pong.mxsc.json" - } - } + "expect": { + "status": "0", + "gas": "*", + "refund": "*" + } + }, + { + "step": "checkState", + "accounts": { + "address:my_address": { + "nonce": "1", + "balance": "1,000,000", + "storage": {} + }, + "address:participant1": { + "nonce": "0", + "balance": "1,800,000,000,000", + "storage": {} + }, + "address:participant2": { + "nonce": "0", + "balance": "2,500,000,000,000", + "storage": {} + }, + "sc:ping-pong": { + "nonce": "0", + "balance": "0", + "storage": { + "str:pingAmount": "500,000,000,000", + "str:durationInSeconds": "123,000", + "str:acceptedPaymentTokenId": "str:EGLD" + }, + "code": "mxsc:../output/ping-pong.mxsc.json" + } } + } ] } diff --git a/ping-pong/src/ping_pong.rs b/ping-pong/src/ping_pong.rs index 09f9b35..fedf3ba 100644 --- a/ping-pong/src/ping_pong.rs +++ b/ping-pong/src/ping_pong.rs @@ -2,270 +2,154 @@ use multiversx_sc::imports::*; -pub mod proxy_ping_pong_egld; -mod types; - -use types::{ContractState, UserStatus}; - -/// Derived empirically. -const PONG_ALL_LOW_GAS_LIMIT: u64 = 3_000_000; - /// A contract that allows anyone to send a fixed sum, locks it for a while and then allows users to take it back. /// Sending funds to the contract is called "ping". /// Taking the same funds back is called "pong". /// /// Restrictions: -/// - `ping` can be called only after the contract is activated. By default the contract is activated on deploy. -/// - Users can only `ping` once, ever. /// - Only the set amount can be `ping`-ed, no more, no less. -/// - The contract can optionally have a maximum cap. No more users can `ping` after the cap has been reached. -/// - The `ping` endpoint optionally accepts -/// - `pong` can only be called after the contract expired (a certain duration has passed since activation). -/// - `pongAll` can be used to send to all users to `ping`-ed. If it runs low on gas, it will interrupt itself. -/// It can be continued anytime. +/// - `pong` can only be called after a certain period after `ping`. #[multiversx_sc::contract] pub trait PingPong { /// Necessary configuration when deploying: - /// `ping_amount` - the exact EGLD amount that needs to be sent when `ping`-ing. - /// `duration_in_seconds` - how much time (in seconds) until contract expires. - /// `opt_activation_timestamp` - optionally specify the contract to only activate at a later date. - /// `max_funds` - optional funding cap, no more funds than this can be added to the contract. - #[allow_multiple_var_args] + /// `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". #[init] fn init( &self, - ping_amount: &BigUint, + ping_amount: BigUint, duration_in_seconds: u64, - opt_activation_timestamp: Option, - max_funds: OptionalValue, + opt_token_id: OptionalValue, ) { - self.ping_amount().set(ping_amount); - let activation_timestamp = - opt_activation_timestamp.unwrap_or_else(|| self.blockchain().get_block_timestamp()); - let deadline = activation_timestamp + duration_in_seconds; - self.deadline().set(deadline); - self.activation_timestamp().set(activation_timestamp); - self.max_funds().set(max_funds.into_option()); + require!(ping_amount > 0, "Ping amount cannot be set to zero"); + self.ping_amount().set(&ping_amount); + + require!( + duration_in_seconds > 0, + "Duration in seconds cannot be set to zero" + ); + self.duration_in_seconds().set(duration_in_seconds); + + let token_id = match opt_token_id { + OptionalValue::Some(t) => t, + OptionalValue::None => EgldOrEsdtTokenIdentifier::egld(), + }; + self.accepted_payment_token_id().set(&token_id); } #[upgrade] - fn upgrade( - &self, - ping_amount: &BigUint, - duration_in_seconds: u64, - opt_activation_timestamp: Option, - max_funds: OptionalValue, - ) { + fn upgrade(&self, ping_amount: BigUint, duration_in_seconds: u64) { self.init( ping_amount, duration_in_seconds, - opt_activation_timestamp, - max_funds, + OptionalValue::Some(self.accepted_payment_token_id().get()), ) } - /// User sends some EGLD to be locked in the contract for a period of time. - #[payable("EGLD")] + // endpoints + + /// User sends some tokens to be locked in the contract for a period of time. + #[payable("*")] #[endpoint] fn ping(&self) { - let payment = self.call_value().egld_value(); - - require!( - *payment == self.ping_amount().get(), - "the payment must match the fixed sum" - ); - - let block_timestamp = self.blockchain().get_block_timestamp(); + let (payment_token, payment_amount) = self.call_value().egld_or_single_fungible_esdt(); require!( - self.activation_timestamp().get() <= block_timestamp, - "smart contract not active yet" + payment_token == self.accepted_payment_token_id().get(), + "Invalid payment token" ); - require!( - block_timestamp < self.deadline().get(), - "deadline has passed" + payment_amount == self.ping_amount().get(), + "The payment must match the fixed ping amount" ); - if let Some(max_funds) = self.max_funds().get() { - require!( - &self - .blockchain() - .get_sc_balance(&EgldOrEsdtTokenIdentifier::egld(), 0) - + &*payment - <= max_funds, - "smart contract full" - ); - } - let caller = self.blockchain().get_caller(); - let user_id = self.user_mapper().get_or_create_user(&caller); - let user_status = self.user_status(user_id).get(); - match user_status { - UserStatus::New => { - self.user_status(user_id).set(UserStatus::Registered); - } - UserStatus::Registered => { - sc_panic!("can only ping once") - } - UserStatus::Withdrawn => { - sc_panic!("already withdrawn") - } - } + require!(!self.did_user_ping(&caller), "Already pinged"); - self.ping_event(&caller, &payment); - } - - fn pong_by_user_id(&self, user_id: usize) -> Result<(), &'static str> { - let user_status = self.user_status(user_id).get(); - match user_status { - UserStatus::New => Result::Err("can't pong, never pinged"), - UserStatus::Registered => { - self.user_status(user_id).set(UserStatus::Withdrawn); - if let Some(user_address) = self.user_mapper().get_user_address(user_id) { - let amount = self.ping_amount().get(); - self.tx().to(&user_address).egld(&amount).transfer(); - self.pong_event(&user_address, &amount); - Result::Ok(()) - } else { - Result::Err("unknown user") - } - } - UserStatus::Withdrawn => Result::Err("already withdrawn"), - } + let current_block_timestamp = self.blockchain().get_block_timestamp(); + self.user_ping_timestamp(&caller) + .set(current_block_timestamp); } /// User can take back funds from the contract. /// Can only be called after expiration. #[endpoint] fn pong(&self) { - require!( - self.blockchain().get_block_timestamp() >= self.deadline().get(), - "can't withdraw before deadline" - ); - let caller = self.blockchain().get_caller(); - let user_id = self.user_mapper().get_user_id(&caller); - let pong_result = self.pong_by_user_id(user_id); - if let Result::Err(message) = pong_result { - sc_panic!(message); - } - } + require!(self.did_user_ping(&caller), "Must ping first"); - /// Send back funds to all users who pinged. - /// Returns - /// - `completed` if everything finished - /// - `interrupted` if run out of gas midway. - /// Can only be called after expiration. - #[endpoint(pongAll)] - fn pong_all(&self) -> OperationCompletionStatus { - let now = self.blockchain().get_block_timestamp(); + let pong_enable_timestamp = self.get_pong_enable_timestamp(&caller); + let current_timestamp = self.blockchain().get_block_timestamp(); require!( - now >= self.deadline().get(), - "can't withdraw before deadline" + current_timestamp >= pong_enable_timestamp, + "Cannot pong before deadline" ); - let num_users = self.user_mapper().get_user_count(); - let mut pong_all_last_user = self.pong_all_last_user().get(); - let mut status = OperationCompletionStatus::InterruptedBeforeOutOfGas; - loop { - if pong_all_last_user >= num_users { - // clear field and reset to 0 - pong_all_last_user = 0; - self.pong_all_last_user().set(pong_all_last_user); - status = OperationCompletionStatus::Completed; - break; - } + self.user_ping_timestamp(&caller).clear(); - if self.blockchain().get_gas_left() < PONG_ALL_LOW_GAS_LIMIT { - self.pong_all_last_user().set(pong_all_last_user); - break; - } + let token_id = self.accepted_payment_token_id().get(); + let amount = self.ping_amount().get(); - pong_all_last_user += 1; + self.send().direct(&caller, &token_id, 0, &amount); + self.pong_event(&caller); + } + + // views - // in case of error just ignore the error and skip - let _ = self.pong_by_user_id(pong_all_last_user); + #[view(didUserPing)] + fn did_user_ping(&self, address: &ManagedAddress) -> bool { + !self.user_ping_timestamp(address).is_empty() + } + + #[view(getPongEnableTimestamp)] + fn get_pong_enable_timestamp(&self, address: &ManagedAddress) -> u64 { + if !self.did_user_ping(address) { + return 0; } - self.pong_all_event(now, &status, pong_all_last_user); + let user_ping_timestamp = self.user_ping_timestamp(address).get(); + let duration_in_seconds = self.duration_in_seconds().get(); - status + user_ping_timestamp + duration_in_seconds } - /// Lists the addresses of all users that have `ping`-ed, - /// in the order they have `ping`-ed - #[view(getUserAddresses)] - fn get_user_addresses(&self) -> MultiValueEncoded { - self.user_mapper().get_all_addresses().into() - } + #[view(getTimeToPong)] + fn get_time_to_pong(&self, address: &ManagedAddress) -> OptionalValue { + if !self.did_user_ping(address) { + return OptionalValue::None; + } + + let pong_enable_timestamp = self.get_pong_enable_timestamp(address); + let current_timestamp = self.blockchain().get_block_timestamp(); - /// Returns the current contract state as a struct - /// for faster fetching from external parties - #[view(getContractState)] - fn get_contract_state(&self) -> ContractState { - ContractState { - ping_amount: self.ping_amount().get(), - deadline: self.deadline().get(), - activation_timestamp: self.activation_timestamp().get(), - max_funds: self.max_funds().get(), - pong_all_last_user: self.pong_all_last_user().get(), + if current_timestamp >= pong_enable_timestamp { + OptionalValue::Some(0) + } else { + let time_left = pong_enable_timestamp - current_timestamp; + OptionalValue::Some(time_left) } } // storage + #[view(getAcceptedPaymentToken)] + #[storage_mapper("acceptedPaymentTokenId")] + fn accepted_payment_token_id(&self) -> SingleValueMapper; + #[view(getPingAmount)] #[storage_mapper("pingAmount")] fn ping_amount(&self) -> SingleValueMapper; - #[view(getDeadline)] - #[storage_mapper("deadline")] - fn deadline(&self) -> SingleValueMapper; - - /// Block timestamp of the block where the contract got activated. - /// If not specified in the constructor it is the deploy block timestamp. - #[view(getActivationTimestamp)] - #[storage_mapper("activationTimestamp")] - fn activation_timestamp(&self) -> SingleValueMapper; - - /// Optional funding cap. - #[view(getMaxFunds)] - #[storage_mapper("maxFunds")] - fn max_funds(&self) -> SingleValueMapper>; + #[view(getDurationTimestamp)] + #[storage_mapper("durationInSeconds")] + fn duration_in_seconds(&self) -> SingleValueMapper; - #[storage_mapper("user")] - fn user_mapper(&self) -> UserMapper; - - /// State of user funds. - /// 0 - user unknown, never `ping`-ed - /// 1 - `ping`-ed - /// 2 - `pong`-ed - #[view(getUserStatus)] - #[storage_mapper("userStatus")] - fn user_status(&self, user_id: usize) -> SingleValueMapper; - - /// Part of the `pongAll` status, the last user to be processed. - /// 0 if never called `pongAll` or `pongAll` completed. - #[view(pongAllLastUser)] - #[storage_mapper("pongAllLastUser")] - fn pong_all_last_user(&self) -> SingleValueMapper; + #[view(getUserPingTimestamp)] + #[storage_mapper("userPingTimestamp")] + fn user_ping_timestamp(&self, address: &ManagedAddress) -> SingleValueMapper; // events - /// Signals a successful ping by user with amount - #[event] - fn ping_event(&self, #[indexed] caller: &ManagedAddress, pinged_amount: &BigUint); - - /// Signals a successful pong by user with amount - #[event] - fn pong_event(&self, #[indexed] caller: &ManagedAddress, ponged_amount: &BigUint); - - /// Signals the beginning of the pong_all operation, status and last user - #[event] - fn pong_all_event( - &self, - #[indexed] timestamp: u64, - #[indexed] status: &OperationCompletionStatus, - #[indexed] pong_all_last_user: usize, - ); + #[event("pongEvent")] + fn pong_event(&self, #[indexed] user: &ManagedAddress); } diff --git a/ping-pong/src/types.rs b/ping-pong/src/types.rs deleted file mode 100644 index e83cc71..0000000 --- a/ping-pong/src/types.rs +++ /dev/null @@ -1,20 +0,0 @@ -use multiversx_sc::derive_imports::*; -use multiversx_sc::imports::*; - -#[type_abi] -#[derive(TopEncode, TopDecode, PartialEq, Eq, Clone, Copy)] -pub enum UserStatus { - New, - Registered, - Withdrawn, -} - -#[type_abi] -#[derive(TopEncode, TopDecode, Default)] -pub struct ContractState { - pub ping_amount: BigUint, - pub deadline: u64, - pub activation_timestamp: u64, - pub max_funds: Option>, - pub pong_all_last_user: usize, -} diff --git a/ping-pong/tests/ping_pong_egld_scenario_go_test.rs b/ping-pong/tests/ping_pong_egld_scenario_go_test.rs index 4f8d99b..facffe1 100644 --- a/ping-pong/tests/ping_pong_egld_scenario_go_test.rs +++ b/ping-pong/tests/ping_pong_egld_scenario_go_test.rs @@ -4,31 +4,11 @@ fn world() -> ScenarioWorld { ScenarioWorld::vm_go() } -#[test] -fn ping_pong_call_get_user_addresses_go() { - world().run("scenarios/ping-pong-call-get-user-addresses.scen.json"); -} - #[test] fn ping_pong_call_ping_go() { world().run("scenarios/ping-pong-call-ping.scen.json"); } -#[test] -fn ping_pong_call_ping_after_deadline_go() { - world().run("scenarios/ping-pong-call-ping-after-deadline.scen.json"); -} - -#[test] -fn ping_pong_call_ping_before_activation_go() { - world().run("scenarios/ping-pong-call-ping-before-activation.scen.json"); -} - -#[test] -fn ping_pong_call_ping_before_beginning_go() { - world().run("scenarios/ping-pong-call-ping-before-beginning.scen.json"); -} - #[test] fn ping_pong_call_ping_second_user_go() { world().run("scenarios/ping-pong-call-ping-second-user.scen.json"); @@ -40,8 +20,8 @@ fn ping_pong_call_ping_twice_go() { } #[test] -fn ping_pong_call_ping_wrong_ammount_go() { - world().run("scenarios/ping-pong-call-ping-wrong-ammount.scen.json"); +fn ping_pong_call_ping_wrong_amount_go() { + world().run("scenarios/ping-pong-call-ping-wrong-amount.scen.json"); } #[test] @@ -49,26 +29,6 @@ fn ping_pong_call_pong_go() { world().run("scenarios/ping-pong-call-pong.scen.json"); } -#[test] -fn ping_pong_call_pong_all_go() { - world().run("scenarios/ping-pong-call-pong-all.scen.json"); -} - -#[test] -fn ping_pong_call_pong_all_after_pong_go() { - world().run("scenarios/ping-pong-call-pong-all-after-pong.scen.json"); -} - -#[test] -fn ping_pong_call_pong_all_interrupted_1_go() { - world().run("scenarios/ping-pong-call-pong-all-interrupted-1.scen.json"); -} - -#[test] -fn ping_pong_call_pong_all_interrupted_2_go() { - world().run("scenarios/ping-pong-call-pong-all-interrupted-2.scen.json"); -} - #[test] fn ping_pong_call_pong_before_deadline_go() { world().run("scenarios/ping-pong-call-pong-before-deadline.scen.json"); diff --git a/ping-pong/tests/ping_pong_egld_scenario_rs_test.rs b/ping-pong/tests/ping_pong_egld_scenario_rs_test.rs index 4f53946..e6656d2 100644 --- a/ping-pong/tests/ping_pong_egld_scenario_rs_test.rs +++ b/ping-pong/tests/ping_pong_egld_scenario_rs_test.rs @@ -10,31 +10,11 @@ fn world() -> ScenarioWorld { blockchain } -#[test] -fn ping_pong_call_get_user_addresses_rs() { - world().run("scenarios/ping-pong-call-get-user-addresses.scen.json"); -} - #[test] fn ping_pong_call_ping_rs() { world().run("scenarios/ping-pong-call-ping.scen.json"); } -#[test] -fn ping_pong_call_ping_after_deadline_rs() { - world().run("scenarios/ping-pong-call-ping-after-deadline.scen.json"); -} - -#[test] -fn ping_pong_call_ping_before_activation_rs() { - world().run("scenarios/ping-pong-call-ping-before-activation.scen.json"); -} - -#[test] -fn ping_pong_call_ping_before_beginning_rs() { - world().run("scenarios/ping-pong-call-ping-before-beginning.scen.json"); -} - #[test] fn ping_pong_call_ping_second_user_rs() { world().run("scenarios/ping-pong-call-ping-second-user.scen.json"); @@ -46,8 +26,8 @@ fn ping_pong_call_ping_twice_rs() { } #[test] -fn ping_pong_call_ping_wrong_ammount_rs() { - world().run("scenarios/ping-pong-call-ping-wrong-ammount.scen.json"); +fn ping_pong_call_ping_wrong_amount_rs() { + world().run("scenarios/ping-pong-call-ping-wrong-amount.scen.json"); } #[test] @@ -55,16 +35,6 @@ fn ping_pong_call_pong_rs() { world().run("scenarios/ping-pong-call-pong.scen.json"); } -#[test] -fn ping_pong_call_pong_all_rs() { - world().run("scenarios/ping-pong-call-pong-all.scen.json"); -} - -#[test] -fn ping_pong_call_pong_all_after_pong_rs() { - world().run("scenarios/ping-pong-call-pong-all-after-pong.scen.json"); -} - #[test] fn ping_pong_call_pong_before_deadline_rs() { world().run("scenarios/ping-pong-call-pong-before-deadline.scen.json"); diff --git a/ping-pong/wasm/Cargo.toml b/ping-pong/wasm/Cargo.toml index 86b7fe2..3f203e8 100644 --- a/ping-pong/wasm/Cargo.toml +++ b/ping-pong/wasm/Cargo.toml @@ -1,3 +1,9 @@ +# Code generated by the multiversx-sc build system. DO NOT EDIT. + +# ########################################## +# ############## AUTO-GENERATED ############# +# ########################################## + [package] name = "ping-pong-wasm" version = "0.0.0" diff --git a/ping-pong/wasm/src/lib.rs b/ping-pong/wasm/src/lib.rs index 5929539..ca90d6b 100644 --- a/ping-pong/wasm/src/lib.rs +++ b/ping-pong/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 11 +// Endpoints: 9 // Async Callback (empty): 1 -// Total number of exported functions: 14 +// Total number of exported functions: 12 #![no_std] @@ -22,15 +22,13 @@ multiversx_sc_wasm_adapter::endpoints! { upgrade => upgrade ping => ping pong => pong - pongAll => pong_all - getUserAddresses => get_user_addresses - getContractState => get_contract_state + didUserPing => did_user_ping + getPongEnableTimestamp => get_pong_enable_timestamp + getTimeToPong => get_time_to_pong + getAcceptedPaymentToken => accepted_payment_token_id getPingAmount => ping_amount - getDeadline => deadline - getActivationTimestamp => activation_timestamp - getMaxFunds => max_funds - getUserStatus => user_status - pongAllLastUser => pong_all_last_user + getDurationTimestamp => duration_in_seconds + getUserPingTimestamp => user_ping_timestamp ) }