From c8bfb82102ff6c5789b8136463b020a2624c6a49 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Tue, 3 Dec 2024 18:19:12 +0200 Subject: [PATCH 01/11] upgrade contract - sync with mx-sdk-rs version --- Cargo.toml | 5 +- elrond.workspace.json | 1 - ping-pong/Cargo.toml | 4 +- ping-pong/interaction/Ping-pong.erdjs.md | 23 -- ping-pong/interaction/out.json | 0 ping-pong/interaction/snippets.sh | 39 -- ping-pong/interactor/.gitignore | 9 + ping-pong/interactor/Cargo.toml | 28 ++ ping-pong/interactor/config.toml | 5 + ping-pong/interactor/src/interact.rs | 374 ++++++++++++++++++ ping-pong/interactor/src/interact_cli.rs | 81 ++++ ping-pong/interactor/src/interact_config.rs | 49 +++ ping-pong/interactor/src/interact_main.rs | 6 + ping-pong/interactor/src/interact_state.rs | 50 +++ .../interactor/tests/interact_cs_test.rs | 138 +++++++ ping-pong/meta/Cargo.toml | 4 +- ping-pong/mxpy.json | 20 - ping-pong/sc-config.toml | 2 + ...ng-pong-call-get-user-addresses.scen.json} | 17 +- ...ng-pong-call-ping-after-deadline.scen.json | 65 +++ ...pong-call-ping-before-activation.scen.json | 65 +++ ...-pong-call-ping-before-beginning.scen.json | 67 ++++ .../ping-pong-call-ping-second-user.scen.json | 26 +- .../ping-pong-call-ping-twice.scen.json | 41 +- ...ing-pong-call-ping-wrong-ammount.scen.json | 59 +++ .../scenarios/ping-pong-call-ping.scen.json | 26 +- ...ng-pong-call-pong-all-after-pong.scen.json | 70 ++++ ...pong-call-pong-all-interrupted-1.scen.json | 81 ++++ ...pong-call-pong-all-interrupted-2.scen.json | 139 +++++++ .../ping-pong-call-pong-all.scen.json | 20 + .../ping-pong-call-pong-all.steps.json | 64 +++ ...g-pong-call-pong-before-deadline.scen.json | 42 +- .../ping-pong-call-pong-twice.scen.json | 42 +- ...ping-pong-call-pong-without-ping.scen.json | 38 +- .../scenarios/ping-pong-call-pong.scen.json | 22 +- ping-pong/scenarios/ping-pong-init.scen.json | 148 ++++--- ping-pong/src/ping_pong.rs | 292 ++++++++++---- ping-pong/src/proxy_ping_pong_egld.rs | 263 ++++++++++++ ping-pong/src/types.rs | 20 + .../tests/ping_pong_egld_scenario_go_test.rs | 44 ++- .../tests/ping_pong_egld_scenario_rs_test.rs | 39 +- ping-pong/wasm/Cargo.toml | 2 +- ping-pong/wasm/src/lib.rs | 20 +- 43 files changed, 2237 insertions(+), 313 deletions(-) delete mode 100644 elrond.workspace.json delete mode 100644 ping-pong/interaction/Ping-pong.erdjs.md delete mode 100644 ping-pong/interaction/out.json delete mode 100755 ping-pong/interaction/snippets.sh 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_main.rs create mode 100644 ping-pong/interactor/src/interact_state.rs create mode 100644 ping-pong/interactor/tests/interact_cs_test.rs delete mode 100644 ping-pong/mxpy.json create mode 100644 ping-pong/sc-config.toml rename ping-pong/scenarios/{ping-pong-call-ping-wrong-amount.scen.json => ping-pong-call-get-user-addresses.scen.json} (53%) create mode 100644 ping-pong/scenarios/ping-pong-call-ping-after-deadline.scen.json create mode 100644 ping-pong/scenarios/ping-pong-call-ping-before-activation.scen.json create mode 100644 ping-pong/scenarios/ping-pong-call-ping-before-beginning.scen.json create mode 100644 ping-pong/scenarios/ping-pong-call-ping-wrong-ammount.scen.json create mode 100644 ping-pong/scenarios/ping-pong-call-pong-all-after-pong.scen.json create mode 100644 ping-pong/scenarios/ping-pong-call-pong-all-interrupted-1.scen.json create mode 100644 ping-pong/scenarios/ping-pong-call-pong-all-interrupted-2.scen.json create mode 100644 ping-pong/scenarios/ping-pong-call-pong-all.scen.json create mode 100644 ping-pong/scenarios/ping-pong-call-pong-all.steps.json create mode 100644 ping-pong/src/proxy_ping_pong_egld.rs create mode 100644 ping-pong/src/types.rs diff --git a/Cargo.toml b/Cargo.toml index d596a09..ac8f311 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,2 @@ [workspace] -members = [ - "ping-pong", - "ping-pong/meta" -] +members = ["ping-pong", "ping-pong/meta", "ping-pong/interactor"] diff --git a/elrond.workspace.json b/elrond.workspace.json deleted file mode 100644 index 9e26dfe..0000000 --- a/elrond.workspace.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/ping-pong/Cargo.toml b/ping-pong/Cargo.toml index b2f7724..8ec3a9b 100644 --- a/ping-pong/Cargo.toml +++ b/ping-pong/Cargo.toml @@ -9,7 +9,7 @@ publish = false path = "src/ping_pong.rs" [dependencies.multiversx-sc] -version = "0.52.3" +version = "0.54.5" [dev-dependencies.multiversx-sc-scenario] -version = "0.52.3" +version = "0.54.5" diff --git a/ping-pong/interaction/Ping-pong.erdjs.md b/ping-pong/interaction/Ping-pong.erdjs.md deleted file mode 100644 index 0f97357..0000000 --- a/ping-pong/interaction/Ping-pong.erdjs.md +++ /dev/null @@ -1,23 +0,0 @@ -# Ping-pong - -First [set up a node terminal](../../../../tutorial/src/interaction/interaction-basic.md). - -```javascript -let erdjs = await require('@elrondnetwork/erdjs'); -let { erdSys, Egld, wallets: { alice, bob, carol, dan } } = await erdjs.setupInteractive("local-testnet"); - -let pingPong = await erdSys.loadWrapper("contracts/examples/ping-pong"); - -await pingPong.sender(alice).gas(150_000_000).call.deploy(Egld(0.5), 2 * 60, null, Egld(1.5)); - -await pingPong.gas(20_000_000).sender(alice).value(Egld(0.5)).ping("note 1"); - -await pingPong.sender(bob).value(Egld(0.5)).ping(null); -await pingPong.sender(carol).value(Egld(0.5)).ping(null); - -// this fails because of the balance limit of 1.5 egld -await pingPong.sender(dan).value(Egld(0.5).ping(null); - -await pingPong.pongAll(); - -``` diff --git a/ping-pong/interaction/out.json b/ping-pong/interaction/out.json deleted file mode 100644 index e69de29..0000000 diff --git a/ping-pong/interaction/snippets.sh b/ping-pong/interaction/snippets.sh deleted file mode 100755 index 087f23c..0000000 --- a/ping-pong/interaction/snippets.sh +++ /dev/null @@ -1,39 +0,0 @@ -PEM_FILE="./ping-pong.pem" -PING_PONG_CONTRACT="output/ping-pong.wasm" - -PROXY_ARGUMENT="--proxy=https://devnet-api.multiversx.com" -CHAIN_ARGUMENT="--chain=D" - -build_ping_pong() { - (set -x; mxpy --verbose contract build "$PING_PONG_CONTRACT") -} - -deploy_ping_pong() { - # local TOKEN_ID=0x45474c44 # "EGLD" - local PING_AMOUNT=1500000000000000000 # 1.5 EGLD - local DURATION=86400 # 1 day in seconds - # local ACTIVATION_TIMESTAMP= # skipped - # local MAX_FUNDS= #skipped - - local OUTFILE="out.json" - (set -x; mxpy contract deploy --bytecode="$PING_PONG_CONTRACT" \ - --pem="$PEM_FILE" \ - $PROXY_ARGUMENT $CHAIN_ARGUMENT \ - --outfile="$OUTFILE" --recall-nonce --gas-limit=60000000 \ - --arguments ${PING_AMOUNT} ${DURATION} --send \ - || return) - - local RESULT_ADDRESS=$(mxpy data parse --file="$OUTFILE" --expression="data['emitted_tx']['address']") - local RESULT_TRANSACTION=$(mxpy data parse --file="$OUTFILE" --expression="data['emitted_tx']['hash']") - - echo "" - echo "Deployed contract with:" - echo " \$RESULT_ADDRESS == ${RESULT_ADDRESS}" - echo " \$RESULT_TRANSACTION == ${RESULT_TRANSACTION}" - echo "" -} - -number_to_u64() { - local NUMBER=$1 - printf "%016x" $NUMBER -} diff --git a/ping-pong/interactor/.gitignore b/ping-pong/interactor/.gitignore new file mode 100644 index 0000000..ac5c74e --- /dev/null +++ b/ping-pong/interactor/.gitignore @@ -0,0 +1,9 @@ +# 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 diff --git a/ping-pong/interactor/Cargo.toml b/ping-pong/interactor/Cargo.toml new file mode 100644 index 0000000..3361aeb --- /dev/null +++ b/ping-pong/interactor/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "ping-pong-interact" +version = "0.0.0" +authors = ["MultiversX "] +edition = "2021" +publish = false + +[[bin]] +name = "ping-pong-interact" +path = "src/interact_main.rs" + +[lib] +path = "src/interact.rs" + +[dependencies] +clap = { version = "4.4.7", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } +toml = "0.8.6" +tokio = { version = "1.24" } + +[dependencies.ping-pong] +path = ".." + +[dependencies.multiversx-sc-snippets] +version = "0.54.5" + +[features] +chain-simulator-tests = [] diff --git a/ping-pong/interactor/config.toml b/ping-pong/interactor/config.toml new file mode 100644 index 0000000..aca7487 --- /dev/null +++ b/ping-pong/interactor/config.toml @@ -0,0 +1,5 @@ +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..e6160e5 --- /dev/null +++ b/ping-pong/interactor/src/interact.rs @@ -0,0 +1,374 @@ +mod interact_cli; +mod interact_config; +mod interact_state; + +use crate::interact_state::State; +use clap::Parser; +pub use interact_config::Config; +use ping_pong::proxy_ping_pong_egld::{self, ContractState, UserStatus}; + +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-egld.mxsc.json"); + +pub async fn ping_pong_egld_cli() { + env_logger::init(); + + let config = Config::load_config(); + + let mut interact = PingPongEgldInteract::init(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()), + ) + .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 + } + Some(interact_cli::InteractCliCommand::Ping(args)) => { + let sender = interact.ping_pong_owner_address.clone(); + interact + .ping(args.cost.unwrap_or_default(), None, &sender) + .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} "); + } + } + 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::GetPingAmount) => { + let ping_amount = interact.get_ping_amount().await; + println!("Ping amount: {}", ping_amount); + } + Some(interact_cli::InteractCliCommand::GetDeadline) => { + let deadline = interact.get_deadline().await; + println!("Deadline: {}", deadline); + } + Some(interact_cli::InteractCliCommand::GetActivationTimestamp) => { + let activation_timestamp = interact.get_activation_timestamp().await; + println!("Activation timestamp: {}", activation_timestamp); + } + 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::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::PongAllLastUser) => { + let pong_all_last_user = interact.pong_all_last_user().await; + println!("Pong all last user: {pong_all_last_user}"); + } + None => {} + } +} + +pub struct PingPongEgldInteract { + pub interactor: Interactor, + pub ping_pong_owner_address: Bech32Address, + pub wallet_address: Bech32Address, + pub state: State, +} + +impl PingPongEgldInteract { + pub async fn init(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; + + interactor.set_current_dir_from_workspace("contracts/examples/ping-pong-egld/interactor"); + let ping_pong_owner_address = interactor.register_wallet(test_wallets::eve()).await; + let wallet_address = interactor.register_wallet(test_wallets::mallory()).await; + + // generate blocks until ESDTSystemSCAddress is enabled + interactor.generate_blocks_until_epoch(1).await.unwrap(); + + Self { + interactor, + ping_pong_owner_address: ping_pong_owner_address.into(), + wallet_address: 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 + .interactor + .tx() + .from(&self.ping_pong_owner_address) + .gas(30_000_000u64) + .typed(proxy_ping_pong_egld::PingPongProxy) + .init( + ping_amount, + duration_in_seconds, + opt_activation_timestamp, + max_funds, + ) + .code(PING_PONG_CODE) + .returns(ReturnsNewBech32Address) + .returns(ReturnsStatus) + .returns(ReturnsMessage) + .run() + .await; + + println!("new address: {new_address}"); + self.state.set_ping_pong_egld_address(new_address); + + (status, message) + } + + pub async fn upgrade( + &mut self, + ping_amount: RustBigUint, + duration_in_seconds: u64, + opt_activation_timestamp: Option, + max_funds: OptionalValue, + ) { + let response = self + .interactor + .tx() + .to(self.state.current_ping_pong_egld_address()) + .from(&self.wallet_address) + .gas(30_000_000u64) + .typed(proxy_ping_pong_egld::PingPongProxy) + .upgrade( + ping_amount, + duration_in_seconds, + opt_activation_timestamp, + max_funds, + ) + .code(PING_PONG_CODE) + .returns(ReturnsNewAddress) + .run() + .await; + + println!("Result: {response:?}"); + } + + pub async fn ping(&mut self, egld_amount: u64, message: Option<&str>, sender: &Bech32Address) { + let _data: IgnoreValue = IgnoreValue; + + 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) + .ping(_data) + .egld(egld_amount) + .returns(ReturnsHandledOrError::new()) + .run() + .await; + + match response { + Ok(_) => println!("Ping successful!"), + Err(err) => { + println!("Ping failed with message: {}", err.message); + assert_eq!(message.unwrap_or_default(), err.message); + } + } + } + + pub async fn pong(&mut self, message: Option<&str>, 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() + .returns(ReturnsHandledOrError::new()) + .run() + .await; + + match response { + Ok(_) => println!("Pong successful!"), + Err(err) => { + println!("Pong failed with message: {}", err.message); + assert_eq!(message.unwrap_or_default(), err.message); + } + } + } + + 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 { + self.interactor + .query() + .to(self.state.current_ping_pong_egld_address()) + .typed(proxy_ping_pong_egld::PingPongProxy) + .get_contract_state() + .returns(ReturnsResult) + .run() + .await + } + + pub async fn get_ping_amount(&mut self) -> RustBigUint { + self.interactor + .query() + .to(self.state.current_ping_pong_egld_address()) + .typed(proxy_ping_pong_egld::PingPongProxy) + .ping_amount() + .returns(ReturnsResultUnmanaged) + .run() + .await + } + + pub async fn get_deadline(&mut self) -> u64 { + self.interactor + .query() + .to(self.state.current_ping_pong_egld_address()) + .typed(proxy_ping_pong_egld::PingPongProxy) + .deadline() + .returns(ReturnsResultUnmanaged) + .run() + .await + } + + pub async fn get_activation_timestamp(&mut self) -> u64 { + self.interactor + .query() + .to(self.state.current_ping_pong_egld_address()) + .typed(proxy_ping_pong_egld::PingPongProxy) + .activation_timestamp() + .returns(ReturnsResultUnmanaged) + .run() + .await + } + + pub async fn get_max_funds(&mut self) -> Option { + self.interactor + .query() + .to(self.state.current_ping_pong_egld_address()) + .typed(proxy_ping_pong_egld::PingPongProxy) + .max_funds() + .returns(ReturnsResultUnmanaged) + .run() + .await + } + + pub async fn get_user_status(&mut self, user_id: usize) -> UserStatus { + self.interactor + .query() + .to(self.state.current_ping_pong_egld_address()) + .typed(proxy_ping_pong_egld::PingPongProxy) + .user_status(user_id) + .returns(ReturnsResultUnmanaged) + .run() + .await + } + + pub async fn pong_all_last_user(&mut self) -> usize { + self.interactor + .query() + .to(self.state.current_ping_pong_egld_address()) + .typed(proxy_ping_pong_egld::PingPongProxy) + .pong_all_last_user() + .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..700f694 --- /dev/null +++ b/ping-pong/interactor/src/interact_cli.rs @@ -0,0 +1,81 @@ +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(DeployArgs), + #[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 = "pong-all", about = "Send back funds to all users who pinged.")] + PongAll, + #[command( + name = "user-addresses", + about = "Lists the addresses of all users that have `ping`-ed in the order they have `ping`-ed." + )] + GetUserAddresses, + #[command(name = "contract-state", about = "Returns the current contract state.")] + GetContractState, + #[command(name = "ping-amount", about = "Returns the ping amount.")] + GetPingAmount, + #[command(name = "deadline", about = "Return deadline.")] + GetDeadline, + #[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." + )] + 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, +} + +#[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 = 'a', long = "activation-timestamp")] + pub opt_activation_timestamp: Option, + + #[arg(short = 'm', long = "max-funds")] + pub max_funds: Option, +} + +#[derive(Default, Clone, PartialEq, Eq, Debug, Args)] +pub struct PingArgs { + #[arg(short = 'c', long = "cost", default_value = "50000000000000000")] + pub cost: Option, +} + +#[derive(Default, Clone, PartialEq, Eq, Debug, Args)] +pub struct UserStatusArgs { + #[arg(short = 'i')] + pub id: usize, +} diff --git a/ping-pong/interactor/src/interact_config.rs b/ping-pong/interactor/src/interact_config.rs new file mode 100644 index 0000000..013e167 --- /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, +} + +/// Ping Pong 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_main.rs b/ping-pong/interactor/src/interact_main.rs new file mode 100644 index 0000000..e9af240 --- /dev/null +++ b/ping-pong/interactor/src/interact_main.rs @@ -0,0 +1,6 @@ +extern crate ping_pong_interact; + +#[tokio::main] +pub async fn main() { + ping_pong_interact::ping_pong_egld_cli().await; +} diff --git a/ping-pong/interactor/src/interact_state.rs b/ping-pong/interactor/src/interact_state.rs new file mode 100644 index 0000000..38fb3d3 --- /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_egld_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_egld_address(&mut self, address: Bech32Address) { + self.ping_pong_egld_address = Some(address); + } + + /// Returns the ping pong contract + pub fn current_ping_pong_egld_address(&self) -> &Bech32Address { + self.ping_pong_egld_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/tests/interact_cs_test.rs b/ping-pong/interactor/tests/interact_cs_test.rs new file mode 100644 index 0000000..799cbc1 --- /dev/null +++ b/ping-pong/interactor/tests/interact_cs_test.rs @@ -0,0 +1,138 @@ +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/meta/Cargo.toml b/ping-pong/meta/Cargo.toml index dd9e623..a8cab33 100644 --- a/ping-pong/meta/Cargo.toml +++ b/ping-pong/meta/Cargo.toml @@ -4,8 +4,10 @@ version = "0.0.0" authors = ["Bruda Claudiu-Marcel "] edition = "2021" publish = false + [dependencies.ping-pong] path = ".." [dependencies.multiversx-sc-meta-lib] -version = "0.52.3" +version = "0.54.5" +default-features = false diff --git a/ping-pong/mxpy.json b/ping-pong/mxpy.json deleted file mode 100644 index 88346ce..0000000 --- a/ping-pong/mxpy.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "configurations": { - "default": { - "proxy": "https://devnet-api.multiversx.com", - "chainID": "D" - } - }, - "contract":{ - "deploy":{ - "verbose": true, - "bytecode": "output/ping-pong.wasm", - "recall-nonce": true, - "pem": "../../wallet/wallet-owner.pem", - "gas-limit": 59999999, - "arguments": [1000000000000000000, 600], - "send": true, - "outfile": "deploy-testnet.interaction.json" - } - } -} diff --git a/ping-pong/sc-config.toml b/ping-pong/sc-config.toml new file mode 100644 index 0000000..bb96777 --- /dev/null +++ b/ping-pong/sc-config.toml @@ -0,0 +1,2 @@ +[[proxy]] +path = "src/proxy_ping_pong_egld.rs" diff --git a/ping-pong/scenarios/ping-pong-call-ping-wrong-amount.scen.json b/ping-pong/scenarios/ping-pong-call-get-user-addresses.scen.json similarity index 53% rename from ping-pong/scenarios/ping-pong-call-ping-wrong-amount.scen.json rename to ping-pong/scenarios/ping-pong-call-get-user-addresses.scen.json index 10e303c..08185b7 100644 --- a/ping-pong/scenarios/ping-pong-call-ping-wrong-amount.scen.json +++ b/ping-pong/scenarios/ping-pong-call-get-user-addresses.scen.json @@ -1,25 +1,28 @@ { - "name": "try to ping the wrong ammount of funds", - "gasSchedule": "v4", + "name": "call getUserAddresses in order to check who registered", + "gasSchedule": "v3", "steps": [ { "step": "externalSteps", - "path": "ping-pong-init.scen.json" + "path": "ping-pong-call-ping-second-user.scen.json" }, { "step": "scCall", "tx": { "from": "address:participant1", "to": "sc:ping-pong", - "value": "450,000,000,000", - "function": "ping", + "function": "getUserAddresses", "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, "expect": { - "status": "4", - "message": "str:The payment must match the fixed ping amount", + "out": [ + "address:participant1", + "address:participant2" + ], + "status": "0", + "message": "", "gas": "*", "refund": "*" } 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 new file mode 100644 index 0000000..6c48054 --- /dev/null +++ b/ping-pong/scenarios/ping-pong-call-ping-after-deadline.scen.json @@ -0,0 +1,65 @@ +{ + "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 new file mode 100644 index 0000000..accfd11 --- /dev/null +++ b/ping-pong/scenarios/ping-pong-call-ping-before-activation.scen.json @@ -0,0 +1,65 @@ +{ + "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 new file mode 100644 index 0000000..01e3848 --- /dev/null +++ b/ping-pong/scenarios/ping-pong-call-ping-before-beginning.scen.json @@ -0,0 +1,67 @@ +{ + "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-second-user.scen.json b/ping-pong/scenarios/ping-pong-call-ping-second-user.scen.json index 2512614..20d3d81 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": "v4", + "gasSchedule": "v3", "steps": [ { "step": "externalSteps", @@ -17,13 +17,14 @@ "tx": { "from": "address:participant2", "to": "sc:ping-pong", - "value": "500,000,000,000", + "egldValue": "500,000,000,000", "function": "ping", "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, "expect": { + "out": [], "status": "", "gas": "*", "refund": "*" @@ -33,17 +34,17 @@ "step": "checkState", "accounts": { "address:my_address": { - "nonce": "*", + "nonce": "1", "balance": "1,000,000", "storage": {} }, "address:participant1": { - "nonce": "*", + "nonce": "1", "balance": "1,300,000,000,000", "storage": {} }, "address:participant2": { - "nonce": "*", + "nonce": "1", "balance": "2,000,000,000,000", "storage": {} }, @@ -52,12 +53,17 @@ "balance": "1,000,000,000,000", "storage": { "str:pingAmount": "500,000,000,000", - "str:durationInSeconds": "123,000", - "str:acceptedPaymentTokenId": "str:EGLD", - "str:userPingTimestamp|address:participant1": "781", - "str:userPingTimestamp|address:participant2": "781" + "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": "file:../output/ping-pong.wasm" + "code": "mxsc:../output/ping-pong.mxsc.json" } } } 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 b199133..6830f3b 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": "v4", + "gasSchedule": "v3", "steps": [ { "step": "externalSteps", @@ -17,18 +17,53 @@ "tx": { "from": "address:participant1", "to": "sc:ping-pong", - "value": "500,000,000,000", + "egldValue": "500,000,000,000", "function": "ping", "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, "expect": { + "out": [], "status": "4", - "message": "str:Already pinged", + "message": "str:can only ping once", "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 new file mode 100644 index 0000000..98e4f68 --- /dev/null +++ b/ping-pong/scenarios/ping-pong-call-ping-wrong-ammount.scen.json @@ -0,0 +1,59 @@ +{ + "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-ping.scen.json b/ping-pong/scenarios/ping-pong-call-ping.scen.json index 65cf135..331f0f9 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": "v4", + "gasSchedule": "v3", "steps": [ { "step": "externalSteps", @@ -17,13 +17,16 @@ "tx": { "from": "address:participant1", "to": "sc:ping-pong", - "value": "500,000,000,000", + "egldValue": "500,000,000,000", "function": "ping", - "arguments": [], + "arguments": [ + "str:optional data, which we ignore" + ], "gasLimit": "10,000,000", "gasPrice": "0" }, "expect": { + "out": [], "status": "", "gas": "*", "refund": "*" @@ -33,17 +36,17 @@ "step": "checkState", "accounts": { "address:my_address": { - "nonce": "*", + "nonce": "1", "balance": "1,000,000", "storage": {} }, "address:participant1": { - "nonce": "*", + "nonce": "1", "balance": "1,300,000,000,000", "storage": {} }, "address:participant2": { - "nonce": "*", + "nonce": "0", "balance": "2,500,000,000,000", "storage": {} }, @@ -52,11 +55,14 @@ "balance": "500,000,000,000", "storage": { "str:pingAmount": "500,000,000,000", - "str:durationInSeconds": "123,000", - "str:acceptedPaymentTokenId": "str:EGLD", - "str:userPingTimestamp|address:participant1": "781" + "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": "file:../output/ping-pong.wasm" + "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 new file mode 100644 index 0000000..25c22bb --- /dev/null +++ b/ping-pong/scenarios/ping-pong-call-pong-all-after-pong.scen.json @@ -0,0 +1,70 @@ +{ + "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 new file mode 100644 index 0000000..59ef0c8 --- /dev/null +++ b/ping-pong/scenarios/ping-pong-call-pong-all-interrupted-1.scen.json @@ -0,0 +1,81 @@ +{ + "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 new file mode 100644 index 0000000..1504901 --- /dev/null +++ b/ping-pong/scenarios/ping-pong-call-pong-all-interrupted-2.scen.json @@ -0,0 +1,139 @@ +{ + "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 new file mode 100644 index 0000000..58cbe3c --- /dev/null +++ b/ping-pong/scenarios/ping-pong-call-pong-all.scen.json @@ -0,0 +1,20 @@ +{ + "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 new file mode 100644 index 0000000..772f002 --- /dev/null +++ b/ping-pong/scenarios/ping-pong-call-pong-all.steps.json @@ -0,0 +1,64 @@ +{ + "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 18474e4..412cc0e 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": "v4", + "gasSchedule": "v3", "steps": [ { "step": "externalSteps", @@ -17,18 +17,52 @@ "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:Cannot pong before deadline", + "message": "str:can't withdraw 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 04cec02..1907d8c 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": "v4", + "gasSchedule": "v3", "steps": [ { "step": "externalSteps", @@ -11,18 +11,52 @@ "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:Must ping first", + "message": "str:already withdrawn", "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 84d9c6f..6b0c576 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": "v4", + "gasSchedule": "v3", "steps": [ { "step": "externalSteps", @@ -17,18 +17,48 @@ "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:Must ping first", + "message": "str:can't pong, never pinged", "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 91b501b..3c05dbe 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": "v4", + "gasSchedule": "v3", "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": "*", + "nonce": "1", "balance": "1,000,000", "storage": {} }, "address:participant1": { - "nonce": "*", + "nonce": "2", "balance": "1,800,000,000,000", "storage": {} }, "address:participant2": { - "nonce": "*", + "nonce": "0", "balance": "2,500,000,000,000", "storage": {} }, @@ -53,12 +53,16 @@ "balance": "0", "storage": { "str:pingAmount": "500,000,000,000", - "str:durationInSeconds": "123,000", - "str:acceptedPaymentTokenId": "str:EGLD" + "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": "file:../output/ping-pong.wasm" + "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 b6d5fd8..ea7ce79 100644 --- a/ping-pong/scenarios/ping-pong-init.scen.json +++ b/ping-pong/scenarios/ping-pong-init.scen.json @@ -1,83 +1,81 @@ { "name": "ping-pong deployment test", - "gasSchedule": "v4", + "gasSchedule": "v3", "steps": [ - { - "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": {} - } - }, - "newAddresses": [ - { - "creatorAddress": "address:my_address", - "creatorNonce": "0", - "newAddress": "sc:ping-pong" - } - ] - }, - { - "step": "scDeploy", - "txId": "deploy", - "tx": { - "from": "address:my_address", - "contractCode": "file:../output/ping-pong.wasm", - "value": "0", - "arguments": [ - "500,000,000,000", - "123,000" - ], - "gasLimit": "10,000,000", - "gasPrice": "0" + { + "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" + } + ] }, - "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" + { + "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" }, - "code": "file:../output/ping-pong.wasm" - } + "expect": { + "out": [], + "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:activationTimestamp": "780", + "str:deadline": "123,780" + }, + "code": "mxsc:../output/ping-pong.mxsc.json" + } + } } - } ] } diff --git a/ping-pong/src/ping_pong.rs b/ping-pong/src/ping_pong.rs index 686995f..abecb0d 100644 --- a/ping-pong/src/ping_pong.rs +++ b/ping-pong/src/ping_pong.rs @@ -2,145 +2,271 @@ 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. -/// - `pong` can only be called after a certain period after `ping`. +/// - 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. #[multiversx_sc::contract] pub trait PingPong { /// 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". + /// `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] #[init] fn init( &self, - ping_amount: BigUint, + ping_amount: &BigUint, duration_in_seconds: u64, - opt_token_id: OptionalValue, + opt_activation_timestamp: Option, + max_funds: OptionalValue, ) { - 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); + 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()); } - // endpoints + #[upgrade] + fn upgrade( + &self, + ping_amount: &BigUint, + duration_in_seconds: u64, + opt_activation_timestamp: Option, + max_funds: OptionalValue, + ) { + self.init( + ping_amount, + duration_in_seconds, + opt_activation_timestamp, + max_funds, + ) + } - /// User sends some tokens to be locked in the contract for a period of time. - #[payable("*")] + /// User sends some EGLD to be locked in the contract for a period of time. + /// Optional `_data` argument is ignored. + #[payable("EGLD")] #[endpoint] - fn ping(&self) { - let (payment_token, payment_amount) = self.call_value().egld_or_single_fungible_esdt(); + fn ping(&self, _data: IgnoreValue) { + 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(); require!( - payment_token == self.accepted_payment_token_id().get(), - "Invalid payment token" + self.activation_timestamp().get() <= block_timestamp, + "smart contract not active yet" ); + require!( - payment_amount == self.ping_amount().get(), - "The payment must match the fixed ping amount" + block_timestamp < self.deadline().get(), + "deadline has passed" ); + 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(); - require!(!self.did_user_ping(&caller), "Already pinged"); + 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") + }, + } - let current_block_timestamp = self.blockchain().get_block_timestamp(); - self.user_ping_timestamp(&caller) - .set(current_block_timestamp); + 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"), + } } /// User can take back funds from the contract. /// Can only be called after expiration. #[endpoint] fn pong(&self) { - let caller = self.blockchain().get_caller(); - require!(self.did_user_ping(&caller), "Must ping first"); - - let pong_enable_timestamp = self.get_pong_enable_timestamp(&caller); - let current_timestamp = self.blockchain().get_block_timestamp(); require!( - current_timestamp >= pong_enable_timestamp, - "Cannot pong before deadline" + self.blockchain().get_block_timestamp() >= self.deadline().get(), + "can't withdraw before deadline" ); - self.user_ping_timestamp(&caller).clear(); + 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); + } + } - let token_id = self.accepted_payment_token_id().get(); - let amount = self.ping_amount().get(); + /// 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(); + require!( + now >= self.deadline().get(), + "can't withdraw before deadline" + ); - self.send().direct(&caller, &token_id, 0, &amount); - self.pong_event(&caller); - } + 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; + } - // views + if self.blockchain().get_gas_left() < PONG_ALL_LOW_GAS_LIMIT { + self.pong_all_last_user().set(pong_all_last_user); + break; + } - #[view(didUserPing)] - fn did_user_ping(&self, address: &ManagedAddress) -> bool { - !self.user_ping_timestamp(address).is_empty() - } + pong_all_last_user += 1; - #[view(getPongEnableTimestamp)] - fn get_pong_enable_timestamp(&self, address: &ManagedAddress) -> u64 { - if !self.did_user_ping(address) { - return 0; + // in case of error just ignore the error and skip + let _ = self.pong_by_user_id(pong_all_last_user); } - let user_ping_timestamp = self.user_ping_timestamp(address).get(); - let duration_in_seconds = self.duration_in_seconds().get(); + self.pong_all_event(now, &status, pong_all_last_user); - user_ping_timestamp + duration_in_seconds + status } - #[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(); + /// 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() + } - if current_timestamp >= pong_enable_timestamp { - OptionalValue::Some(0) - } else { - let time_left = pong_enable_timestamp - current_timestamp; - OptionalValue::Some(time_left) + /// 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(), } } // storage - #[view(getAcceptedPaymentToken)] - #[storage_mapper("acceptedPaymentTokenId")] - fn accepted_payment_token_id(&self) -> SingleValueMapper; - #[view(getPingAmount)] #[storage_mapper("pingAmount")] fn ping_amount(&self) -> SingleValueMapper; - #[view(getDurationTimestamp)] - #[storage_mapper("durationInSeconds")] - fn duration_in_seconds(&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 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(getUserPingTimestamp)] - #[storage_mapper("userPingTimestamp")] - fn user_ping_timestamp(&self, address: &ManagedAddress) -> 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; // events - #[event("pongEvent")] - fn pong_event(&self, #[indexed] user: &ManagedAddress); + /// 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, + ); } diff --git a/ping-pong/src/proxy_ping_pong_egld.rs b/ping-pong/src/proxy_ping_pong_egld.rs new file mode 100644 index 0000000..ad89444 --- /dev/null +++ b/ping-pong/src/proxy_ping_pong_egld.rs @@ -0,0 +1,263 @@ +// 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 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. + pub fn init< + Arg0: ProxyArg>, + Arg1: ProxyArg, + Arg2: ProxyArg>, + Arg3: ProxyArg>>, + >( + self, + ping_amount: Arg0, + duration_in_seconds: Arg1, + opt_activation_timestamp: Arg2, + max_funds: Arg3, + ) -> TxTypedDeploy { + self.wrapped_tx + .payment(NotPayable) + .raw_deploy() + .argument(&ping_amount) + .argument(&duration_in_seconds) + .argument(&opt_activation_timestamp) + .argument(&max_funds) + .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, + 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() + } +} + +#[rustfmt::skip] +impl PingPongProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + /// User sends some EGLD to be locked in the contract for a period of time. + /// Optional `_data` argument is ignored. + pub fn ping< + Arg0: ProxyArg, + >( + self, + _data: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .raw_call("ping") + .argument(&_data) + .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() + } + + /// 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( + self, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("pongAll") + .original_result() + } + + /// Lists the addresses of all users that have `ping`-ed, + /// in the order they have `ping`-ed + pub fn get_user_addresses( + self, + ) -> TxTypedCall>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getUserAddresses") + .original_result() + } + + /// Returns the current contract state as a struct + /// for faster fetching from external parties + pub fn get_contract_state( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getContractState") + .original_result() + } + + pub fn ping_amount( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getPingAmount") + .original_result() + } + + pub fn deadline( + self, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getDeadline") + .original_result() + } + + /// Block timestamp of the block where the contract got activated. + /// If not specified in the constructor it is the the deploy block timestamp. + pub fn activation_timestamp( + self, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getActivationTimestamp") + .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, + >( + 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 { + self.wrapped_tx + .payment(NotPayable) + .raw_call("pongAllLastUser") + .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/src/types.rs b/ping-pong/src/types.rs new file mode 100644 index 0000000..e83cc71 --- /dev/null +++ b/ping-pong/src/types.rs @@ -0,0 +1,20 @@ +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 facffe1..4f8d99b 100644 --- a/ping-pong/tests/ping_pong_egld_scenario_go_test.rs +++ b/ping-pong/tests/ping_pong_egld_scenario_go_test.rs @@ -4,11 +4,31 @@ 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"); @@ -20,8 +40,8 @@ fn ping_pong_call_ping_twice_go() { } #[test] -fn ping_pong_call_ping_wrong_amount_go() { - world().run("scenarios/ping-pong-call-ping-wrong-amount.scen.json"); +fn ping_pong_call_ping_wrong_ammount_go() { + world().run("scenarios/ping-pong-call-ping-wrong-ammount.scen.json"); } #[test] @@ -29,6 +49,26 @@ 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 99b1f2f..4f53946 100644 --- a/ping-pong/tests/ping_pong_egld_scenario_rs_test.rs +++ b/ping-pong/tests/ping_pong_egld_scenario_rs_test.rs @@ -3,15 +3,38 @@ use multiversx_sc_scenario::*; fn world() -> ScenarioWorld { let mut blockchain = ScenarioWorld::new(); - blockchain.register_contract("file:output/ping-pong.wasm", ping_pong::ContractBuilder); + blockchain.register_contract( + "mxsc:output/ping-pong.mxsc.json", + ping_pong::ContractBuilder, + ); 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"); @@ -23,8 +46,8 @@ fn ping_pong_call_ping_twice_rs() { } #[test] -fn ping_pong_call_ping_wrong_amount_rs() { - world().run("scenarios/ping-pong-call-ping-wrong-amount.scen.json"); +fn ping_pong_call_ping_wrong_ammount_rs() { + world().run("scenarios/ping-pong-call-ping-wrong-ammount.scen.json"); } #[test] @@ -32,6 +55,16 @@ 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 2aea6fc..a2b8716 100644 --- a/ping-pong/wasm/Cargo.toml +++ b/ping-pong/wasm/Cargo.toml @@ -28,7 +28,7 @@ panic = "abort" path = ".." [dependencies.multiversx-sc-wasm-adapter] -version = "0.52.3" +version = "0.54.5" [workspace] members = ["."] diff --git a/ping-pong/wasm/src/lib.rs b/ping-pong/wasm/src/lib.rs index 69a0c04..5929539 100644 --- a/ping-pong/wasm/src/lib.rs +++ b/ping-pong/wasm/src/lib.rs @@ -5,9 +5,10 @@ //////////////////////////////////////////////////// // Init: 1 -// Endpoints: 9 +// Upgrade: 1 +// Endpoints: 11 // Async Callback (empty): 1 -// Total number of exported functions: 11 +// Total number of exported functions: 14 #![no_std] @@ -18,15 +19,18 @@ multiversx_sc_wasm_adapter::endpoints! { ping_pong ( init => init + upgrade => upgrade ping => ping pong => pong - didUserPing => did_user_ping - getPongEnableTimestamp => get_pong_enable_timestamp - getTimeToPong => get_time_to_pong - getAcceptedPaymentToken => accepted_payment_token_id + pongAll => pong_all + getUserAddresses => get_user_addresses + getContractState => get_contract_state getPingAmount => ping_amount - getDurationTimestamp => duration_in_seconds - getUserPingTimestamp => user_ping_timestamp + getDeadline => deadline + getActivationTimestamp => activation_timestamp + getMaxFunds => max_funds + getUserStatus => user_status + pongAllLastUser => pong_all_last_user ) } From bf555815af2cf98eef0f45668a996f6c6e39a347 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Tue, 3 Dec 2024 18:37:25 +0200 Subject: [PATCH 02/11] upgrade - fix path and set workspace resolver 2 --- Cargo.toml | 1 + ping-pong/interactor/src/interact.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ac8f311..fe0ec31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,3 @@ [workspace] +resolver = "2" members = ["ping-pong", "ping-pong/meta", "ping-pong/interactor"] diff --git a/ping-pong/interactor/src/interact.rs b/ping-pong/interactor/src/interact.rs index e6160e5..90edc8e 100644 --- a/ping-pong/interactor/src/interact.rs +++ b/ping-pong/interactor/src/interact.rs @@ -11,7 +11,7 @@ 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-egld.mxsc.json"); +const PING_PONG_CODE: MxscPath = MxscPath::new("../output/ping-pong.mxsc.json"); pub async fn ping_pong_egld_cli() { env_logger::init(); @@ -122,7 +122,7 @@ impl PingPongEgldInteract { .with_tracer(INTERACTOR_SCENARIO_TRACE_PATH) .await; - interactor.set_current_dir_from_workspace("contracts/examples/ping-pong-egld/interactor"); + 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; From 658b1e872a9cd858ab560ae2004d3065c081f13e Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Tue, 3 Dec 2024 18:41:01 +0200 Subject: [PATCH 03/11] interactor - set real chain --- ping-pong/interactor/config.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ping-pong/interactor/config.toml b/ping-pong/interactor/config.toml index aca7487..c6f9a97 100644 --- a/ping-pong/interactor/config.toml +++ b/ping-pong/interactor/config.toml @@ -1,5 +1,5 @@ -chain_type = 'simulator' -gateway_uri = 'http://localhost:8085' +# chain_type = 'simulator' +# gateway_uri = 'http://localhost:8085' -# chain_type = 'real' -# gateway_uri = 'https://devnet-gateway.multiversx.com' +chain_type = 'real' +gateway_uri = 'https://devnet-gateway.multiversx.com' From ef18412a106a3cf78a48492720ccdab6c61ae6a6 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Thu, 5 Dec 2024 13:00:39 +0200 Subject: [PATCH 04/11] remove IgnoreValue from ping endpoint --- .github/workflows/actions.yml | 2 +- ping-pong/interactor/src/interact.rs | 4 +--- ping-pong/src/ping_pong.rs | 13 ++++++------- ping-pong/src/proxy_ping_pong_egld.rs | 9 ++------- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 1f01ab9..dff89da 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@v3.3.1 + uses: multiversx/mx-sc-actions/.github/workflows/contracts.yml@v4.1.0 with: rust-toolchain: stable enable-contracts-size-report: false diff --git a/ping-pong/interactor/src/interact.rs b/ping-pong/interactor/src/interact.rs index 90edc8e..018a7e0 100644 --- a/ping-pong/interactor/src/interact.rs +++ b/ping-pong/interactor/src/interact.rs @@ -208,8 +208,6 @@ impl PingPongEgldInteract { } pub async fn ping(&mut self, egld_amount: u64, message: Option<&str>, sender: &Bech32Address) { - let _data: IgnoreValue = IgnoreValue; - let response = self .interactor .tx() @@ -217,7 +215,7 @@ impl PingPongEgldInteract { .to(self.state.current_ping_pong_egld_address()) .gas(30_000_000u64) .typed(proxy_ping_pong_egld::PingPongProxy) - .ping(_data) + .ping() .egld(egld_amount) .returns(ReturnsHandledOrError::new()) .run() diff --git a/ping-pong/src/ping_pong.rs b/ping-pong/src/ping_pong.rs index abecb0d..09f9b35 100644 --- a/ping-pong/src/ping_pong.rs +++ b/ping-pong/src/ping_pong.rs @@ -65,10 +65,9 @@ pub trait PingPong { } /// User sends some EGLD to be locked in the contract for a period of time. - /// Optional `_data` argument is ignored. #[payable("EGLD")] #[endpoint] - fn ping(&self, _data: IgnoreValue) { + fn ping(&self) { let payment = self.call_value().egld_value(); require!( @@ -104,13 +103,13 @@ pub trait PingPong { 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") - }, + } } self.ping_event(&caller, &payment); @@ -130,7 +129,7 @@ pub trait PingPong { } else { Result::Err("unknown user") } - }, + } UserStatus::Withdrawn => Result::Err("already withdrawn"), } } @@ -224,7 +223,7 @@ pub trait PingPong { fn deadline(&self) -> SingleValueMapper; /// Block timestamp of the block where the contract got activated. - /// If not specified in the constructor it is the the deploy block timestamp. + /// If not specified in the constructor it is the deploy block timestamp. #[view(getActivationTimestamp)] #[storage_mapper("activationTimestamp")] fn activation_timestamp(&self) -> SingleValueMapper; diff --git a/ping-pong/src/proxy_ping_pong_egld.rs b/ping-pong/src/proxy_ping_pong_egld.rs index ad89444..803954e 100644 --- a/ping-pong/src/proxy_ping_pong_egld.rs +++ b/ping-pong/src/proxy_ping_pong_egld.rs @@ -113,16 +113,11 @@ where Gas: TxGas, { /// User sends some EGLD to be locked in the contract for a period of time. - /// Optional `_data` argument is ignored. - pub fn ping< - Arg0: ProxyArg, - >( + pub fn ping( self, - _data: Arg0, ) -> TxTypedCall { self.wrapped_tx .raw_call("ping") - .argument(&_data) .original_result() } @@ -192,7 +187,7 @@ where } /// Block timestamp of the block where the contract got activated. - /// If not specified in the constructor it is the the deploy block timestamp. + /// If not specified in the constructor it is the deploy block timestamp. pub fn activation_timestamp( self, ) -> TxTypedCall { From 50a7ff3a88f8dfbf54cf3a1bc748cd4bceafee9f Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Thu, 5 Dec 2024 13:05:36 +0200 Subject: [PATCH 05/11] upgrade to v0.54.6 --- ping-pong/Cargo.toml | 4 ++-- ping-pong/interactor/Cargo.toml | 24 ++++++++++++++++-------- ping-pong/meta/Cargo.toml | 2 +- ping-pong/wasm/Cargo.toml | 8 +------- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/ping-pong/Cargo.toml b/ping-pong/Cargo.toml index 8ec3a9b..4b7804e 100644 --- a/ping-pong/Cargo.toml +++ b/ping-pong/Cargo.toml @@ -9,7 +9,7 @@ publish = false path = "src/ping_pong.rs" [dependencies.multiversx-sc] -version = "0.54.5" +version = "0.54.6" [dev-dependencies.multiversx-sc-scenario] -version = "0.54.5" +version = "0.54.6" diff --git a/ping-pong/interactor/Cargo.toml b/ping-pong/interactor/Cargo.toml index 3361aeb..66062c1 100644 --- a/ping-pong/interactor/Cargo.toml +++ b/ping-pong/interactor/Cargo.toml @@ -1,3 +1,7 @@ +[[bin]] +name = "ping-pong-interact" +path = "src/interact_main.rs" + [package] name = "ping-pong-interact" version = "0.0.0" @@ -5,24 +9,28 @@ authors = ["MultiversX "] edition = "2021" publish = false -[[bin]] -name = "ping-pong-interact" -path = "src/interact_main.rs" - [lib] path = "src/interact.rs" [dependencies] -clap = { version = "4.4.7", features = ["derive"] } -serde = { version = "1.0", features = ["derive"] } toml = "0.8.6" -tokio = { version = "1.24" } + +[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.5" +version = "0.54.6" [features] chain-simulator-tests = [] diff --git a/ping-pong/meta/Cargo.toml b/ping-pong/meta/Cargo.toml index a8cab33..5ee62a7 100644 --- a/ping-pong/meta/Cargo.toml +++ b/ping-pong/meta/Cargo.toml @@ -9,5 +9,5 @@ publish = false path = ".." [dependencies.multiversx-sc-meta-lib] -version = "0.54.5" +version = "0.54.6" default-features = false diff --git a/ping-pong/wasm/Cargo.toml b/ping-pong/wasm/Cargo.toml index a2b8716..86b7fe2 100644 --- a/ping-pong/wasm/Cargo.toml +++ b/ping-pong/wasm/Cargo.toml @@ -1,9 +1,3 @@ -# Code generated by the multiversx-sc build system. DO NOT EDIT. - -# ########################################## -# ############## AUTO-GENERATED ############# -# ########################################## - [package] name = "ping-pong-wasm" version = "0.0.0" @@ -28,7 +22,7 @@ panic = "abort" path = ".." [dependencies.multiversx-sc-wasm-adapter] -version = "0.54.5" +version = "0.54.6" [workspace] members = ["."] From 5e735c54fba32db0068d724fba47b8ee96ec4a83 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Thu, 5 Dec 2024 13:14:14 +0200 Subject: [PATCH 06/11] fix tests --- .github/workflows/actions.yml | 1 + .../ping-pong-call-ping-before-beginning.scen.json | 6 ++---- ping-pong/scenarios/ping-pong-call-ping.scen.json | 6 ++---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index dff89da..f4d14e1 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -16,6 +16,7 @@ jobs: uses: multiversx/mx-sc-actions/.github/workflows/contracts.yml@v4.1.0 with: rust-toolchain: stable + mx-scenario-go-version: v3.0.0 enable-contracts-size-report: false secrets: token: ${{ secrets.GITHUB_TOKEN }} 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 index 01e3848..64c876e 100644 --- a/ping-pong/scenarios/ping-pong-call-ping-before-beginning.scen.json +++ b/ping-pong/scenarios/ping-pong-call-ping-before-beginning.scen.json @@ -19,9 +19,7 @@ "to": "sc:ping-pong", "egldValue": "500,000,000,000", "function": "ping", - "arguments": [ - "" - ], + "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, @@ -64,4 +62,4 @@ } } ] -} +} \ No newline at end of file diff --git a/ping-pong/scenarios/ping-pong-call-ping.scen.json b/ping-pong/scenarios/ping-pong-call-ping.scen.json index 331f0f9..826e88b 100644 --- a/ping-pong/scenarios/ping-pong-call-ping.scen.json +++ b/ping-pong/scenarios/ping-pong-call-ping.scen.json @@ -19,9 +19,7 @@ "to": "sc:ping-pong", "egldValue": "500,000,000,000", "function": "ping", - "arguments": [ - "str:optional data, which we ignore" - ], + "arguments": [], "gasLimit": "10,000,000", "gasPrice": "0" }, @@ -67,4 +65,4 @@ } } ] -} +} \ No newline at end of file From 0a08e6efb427984844436b98810bda4dfa5b9b48 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Thu, 5 Dec 2024 14:04:31 +0200 Subject: [PATCH 07/11] change mx-sc-action version --- .github/workflows/actions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index f4d14e1..3d0ff99 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.1.0 + uses: multiversx/mx-sc-actions/.github/workflows/contracts.yml@v4.0.0 with: rust-toolchain: stable mx-scenario-go-version: v3.0.0 From 204acc20f24e5268cb2ad8bfd191dd5bc57490bc Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Tue, 10 Dec 2024 12:42:26 +0200 Subject: [PATCH 08/11] revert to ping-pong esdt or egld --- .github/workflows/actions.yml | 3 +- Cargo.toml | 6 +- elrond.workspace.json | 1 + ping-pong/Cargo.toml | 4 +- ping-pong/interaction/Ping-pong.erdjs.md | 23 ++ ping-pong/interaction/out.json | 0 ping-pong/interaction/snippets.sh | 39 ++ ping-pong/interactor/.gitignore | 9 - ping-pong/interactor/Cargo.toml | 36 -- ping-pong/interactor/config.toml | 5 - ping-pong/interactor/src/interact.rs | 372 ------------------ ping-pong/interactor/src/interact_cli.rs | 81 ---- ping-pong/interactor/src/interact_config.rs | 49 --- ping-pong/interactor/src/interact_main.rs | 6 - ping-pong/interactor/src/interact_state.rs | 50 --- .../interactor/tests/interact_cs_test.rs | 138 ------- ping-pong/meta/Cargo.toml | 4 +- ping-pong/mxpy.json | 20 + ping-pong/sc-config.toml | 2 - ...ng-pong-call-ping-after-deadline.scen.json | 65 --- ...pong-call-ping-before-activation.scen.json | 65 --- ...-pong-call-ping-before-beginning.scen.json | 65 --- .../ping-pong-call-ping-second-user.scen.json | 26 +- .../ping-pong-call-ping-twice.scen.json | 41 +- ...ing-pong-call-ping-wrong-ammount.scen.json | 59 --- ...ing-pong-call-ping-wrong-amount.scen.json} | 17 +- .../scenarios/ping-pong-call-ping.scen.json | 24 +- ...ng-pong-call-pong-all-after-pong.scen.json | 70 ---- ...pong-call-pong-all-interrupted-1.scen.json | 81 ---- ...pong-call-pong-all-interrupted-2.scen.json | 139 ------- .../ping-pong-call-pong-all.scen.json | 20 - .../ping-pong-call-pong-all.steps.json | 64 --- ...g-pong-call-pong-before-deadline.scen.json | 42 +- .../ping-pong-call-pong-twice.scen.json | 42 +- ...ping-pong-call-pong-without-ping.scen.json | 38 +- .../scenarios/ping-pong-call-pong.scen.json | 22 +- ping-pong/scenarios/ping-pong-init.scen.json | 148 +++---- ping-pong/src/ping_pong.rs | 289 ++++---------- ping-pong/src/proxy_ping_pong_egld.rs | 258 ------------ ping-pong/src/types.rs | 20 - .../tests/ping_pong_egld_scenario_go_test.rs | 44 +-- .../tests/ping_pong_egld_scenario_rs_test.rs | 39 +- ping-pong/wasm/Cargo.toml | 8 +- ping-pong/wasm/src/lib.rs | 20 +- 44 files changed, 319 insertions(+), 2235 deletions(-) create mode 100644 elrond.workspace.json create mode 100644 ping-pong/interaction/Ping-pong.erdjs.md create mode 100644 ping-pong/interaction/out.json create mode 100755 ping-pong/interaction/snippets.sh delete mode 100644 ping-pong/interactor/.gitignore delete mode 100644 ping-pong/interactor/Cargo.toml delete mode 100644 ping-pong/interactor/config.toml delete mode 100644 ping-pong/interactor/src/interact.rs delete mode 100644 ping-pong/interactor/src/interact_cli.rs delete mode 100644 ping-pong/interactor/src/interact_config.rs delete mode 100644 ping-pong/interactor/src/interact_main.rs delete mode 100644 ping-pong/interactor/src/interact_state.rs delete mode 100644 ping-pong/interactor/tests/interact_cs_test.rs create mode 100644 ping-pong/mxpy.json delete mode 100644 ping-pong/sc-config.toml delete mode 100644 ping-pong/scenarios/ping-pong-call-ping-after-deadline.scen.json delete mode 100644 ping-pong/scenarios/ping-pong-call-ping-before-activation.scen.json delete mode 100644 ping-pong/scenarios/ping-pong-call-ping-before-beginning.scen.json delete mode 100644 ping-pong/scenarios/ping-pong-call-ping-wrong-ammount.scen.json rename ping-pong/scenarios/{ping-pong-call-get-user-addresses.scen.json => ping-pong-call-ping-wrong-amount.scen.json} (53%) delete mode 100644 ping-pong/scenarios/ping-pong-call-pong-all-after-pong.scen.json delete mode 100644 ping-pong/scenarios/ping-pong-call-pong-all-interrupted-1.scen.json delete mode 100644 ping-pong/scenarios/ping-pong-call-pong-all-interrupted-2.scen.json delete mode 100644 ping-pong/scenarios/ping-pong-call-pong-all.scen.json delete mode 100644 ping-pong/scenarios/ping-pong-call-pong-all.steps.json delete mode 100644 ping-pong/src/proxy_ping_pong_egld.rs delete mode 100644 ping-pong/src/types.rs diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 3d0ff99..1f01ab9 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -13,10 +13,9 @@ 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@v3.3.1 with: rust-toolchain: stable - mx-scenario-go-version: v3.0.0 enable-contracts-size-report: false secrets: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/Cargo.toml b/Cargo.toml index fe0ec31..d596a09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,5 @@ [workspace] -resolver = "2" -members = ["ping-pong", "ping-pong/meta", "ping-pong/interactor"] +members = [ + "ping-pong", + "ping-pong/meta" +] diff --git a/elrond.workspace.json b/elrond.workspace.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/elrond.workspace.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/ping-pong/Cargo.toml b/ping-pong/Cargo.toml index 4b7804e..b2f7724 100644 --- a/ping-pong/Cargo.toml +++ b/ping-pong/Cargo.toml @@ -9,7 +9,7 @@ publish = false path = "src/ping_pong.rs" [dependencies.multiversx-sc] -version = "0.54.6" +version = "0.52.3" [dev-dependencies.multiversx-sc-scenario] -version = "0.54.6" +version = "0.52.3" diff --git a/ping-pong/interaction/Ping-pong.erdjs.md b/ping-pong/interaction/Ping-pong.erdjs.md new file mode 100644 index 0000000..0f97357 --- /dev/null +++ b/ping-pong/interaction/Ping-pong.erdjs.md @@ -0,0 +1,23 @@ +# Ping-pong + +First [set up a node terminal](../../../../tutorial/src/interaction/interaction-basic.md). + +```javascript +let erdjs = await require('@elrondnetwork/erdjs'); +let { erdSys, Egld, wallets: { alice, bob, carol, dan } } = await erdjs.setupInteractive("local-testnet"); + +let pingPong = await erdSys.loadWrapper("contracts/examples/ping-pong"); + +await pingPong.sender(alice).gas(150_000_000).call.deploy(Egld(0.5), 2 * 60, null, Egld(1.5)); + +await pingPong.gas(20_000_000).sender(alice).value(Egld(0.5)).ping("note 1"); + +await pingPong.sender(bob).value(Egld(0.5)).ping(null); +await pingPong.sender(carol).value(Egld(0.5)).ping(null); + +// this fails because of the balance limit of 1.5 egld +await pingPong.sender(dan).value(Egld(0.5).ping(null); + +await pingPong.pongAll(); + +``` diff --git a/ping-pong/interaction/out.json b/ping-pong/interaction/out.json new file mode 100644 index 0000000..e69de29 diff --git a/ping-pong/interaction/snippets.sh b/ping-pong/interaction/snippets.sh new file mode 100755 index 0000000..087f23c --- /dev/null +++ b/ping-pong/interaction/snippets.sh @@ -0,0 +1,39 @@ +PEM_FILE="./ping-pong.pem" +PING_PONG_CONTRACT="output/ping-pong.wasm" + +PROXY_ARGUMENT="--proxy=https://devnet-api.multiversx.com" +CHAIN_ARGUMENT="--chain=D" + +build_ping_pong() { + (set -x; mxpy --verbose contract build "$PING_PONG_CONTRACT") +} + +deploy_ping_pong() { + # local TOKEN_ID=0x45474c44 # "EGLD" + local PING_AMOUNT=1500000000000000000 # 1.5 EGLD + local DURATION=86400 # 1 day in seconds + # local ACTIVATION_TIMESTAMP= # skipped + # local MAX_FUNDS= #skipped + + local OUTFILE="out.json" + (set -x; mxpy contract deploy --bytecode="$PING_PONG_CONTRACT" \ + --pem="$PEM_FILE" \ + $PROXY_ARGUMENT $CHAIN_ARGUMENT \ + --outfile="$OUTFILE" --recall-nonce --gas-limit=60000000 \ + --arguments ${PING_AMOUNT} ${DURATION} --send \ + || return) + + local RESULT_ADDRESS=$(mxpy data parse --file="$OUTFILE" --expression="data['emitted_tx']['address']") + local RESULT_TRANSACTION=$(mxpy data parse --file="$OUTFILE" --expression="data['emitted_tx']['hash']") + + echo "" + echo "Deployed contract with:" + echo " \$RESULT_ADDRESS == ${RESULT_ADDRESS}" + echo " \$RESULT_TRANSACTION == ${RESULT_TRANSACTION}" + echo "" +} + +number_to_u64() { + local NUMBER=$1 + printf "%016x" $NUMBER +} diff --git a/ping-pong/interactor/.gitignore b/ping-pong/interactor/.gitignore deleted file mode 100644 index ac5c74e..0000000 --- a/ping-pong/interactor/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# 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 diff --git a/ping-pong/interactor/Cargo.toml b/ping-pong/interactor/Cargo.toml deleted file mode 100644 index 66062c1..0000000 --- a/ping-pong/interactor/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[[bin]] -name = "ping-pong-interact" -path = "src/interact_main.rs" - -[package] -name = "ping-pong-interact" -version = "0.0.0" -authors = ["MultiversX "] -edition = "2021" -publish = false - -[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" - -[features] -chain-simulator-tests = [] diff --git a/ping-pong/interactor/config.toml b/ping-pong/interactor/config.toml deleted file mode 100644 index c6f9a97..0000000 --- a/ping-pong/interactor/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -# 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 deleted file mode 100644 index 018a7e0..0000000 --- a/ping-pong/interactor/src/interact.rs +++ /dev/null @@ -1,372 +0,0 @@ -mod interact_cli; -mod interact_config; -mod interact_state; - -use crate::interact_state::State; -use clap::Parser; -pub use interact_config::Config; -use ping_pong::proxy_ping_pong_egld::{self, ContractState, UserStatus}; - -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"); - -pub async fn ping_pong_egld_cli() { - env_logger::init(); - - let config = Config::load_config(); - - let mut interact = PingPongEgldInteract::init(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()), - ) - .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 - } - Some(interact_cli::InteractCliCommand::Ping(args)) => { - let sender = interact.ping_pong_owner_address.clone(); - interact - .ping(args.cost.unwrap_or_default(), None, &sender) - .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} "); - } - } - 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::GetPingAmount) => { - let ping_amount = interact.get_ping_amount().await; - println!("Ping amount: {}", ping_amount); - } - Some(interact_cli::InteractCliCommand::GetDeadline) => { - let deadline = interact.get_deadline().await; - println!("Deadline: {}", deadline); - } - Some(interact_cli::InteractCliCommand::GetActivationTimestamp) => { - let activation_timestamp = interact.get_activation_timestamp().await; - println!("Activation timestamp: {}", activation_timestamp); - } - 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::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::PongAllLastUser) => { - let pong_all_last_user = interact.pong_all_last_user().await; - println!("Pong all last user: {pong_all_last_user}"); - } - None => {} - } -} - -pub struct PingPongEgldInteract { - pub interactor: Interactor, - pub ping_pong_owner_address: Bech32Address, - pub wallet_address: Bech32Address, - pub state: State, -} - -impl PingPongEgldInteract { - pub async fn init(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; - - 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; - - // generate blocks until ESDTSystemSCAddress is enabled - interactor.generate_blocks_until_epoch(1).await.unwrap(); - - Self { - interactor, - ping_pong_owner_address: ping_pong_owner_address.into(), - wallet_address: 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 - .interactor - .tx() - .from(&self.ping_pong_owner_address) - .gas(30_000_000u64) - .typed(proxy_ping_pong_egld::PingPongProxy) - .init( - ping_amount, - duration_in_seconds, - opt_activation_timestamp, - max_funds, - ) - .code(PING_PONG_CODE) - .returns(ReturnsNewBech32Address) - .returns(ReturnsStatus) - .returns(ReturnsMessage) - .run() - .await; - - println!("new address: {new_address}"); - self.state.set_ping_pong_egld_address(new_address); - - (status, message) - } - - pub async fn upgrade( - &mut self, - ping_amount: RustBigUint, - duration_in_seconds: u64, - opt_activation_timestamp: Option, - max_funds: OptionalValue, - ) { - let response = self - .interactor - .tx() - .to(self.state.current_ping_pong_egld_address()) - .from(&self.wallet_address) - .gas(30_000_000u64) - .typed(proxy_ping_pong_egld::PingPongProxy) - .upgrade( - ping_amount, - duration_in_seconds, - opt_activation_timestamp, - max_funds, - ) - .code(PING_PONG_CODE) - .returns(ReturnsNewAddress) - .run() - .await; - - println!("Result: {response:?}"); - } - - pub async fn ping(&mut self, egld_amount: u64, message: Option<&str>, 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) - .ping() - .egld(egld_amount) - .returns(ReturnsHandledOrError::new()) - .run() - .await; - - match response { - Ok(_) => println!("Ping successful!"), - Err(err) => { - println!("Ping failed with message: {}", err.message); - assert_eq!(message.unwrap_or_default(), err.message); - } - } - } - - pub async fn pong(&mut self, message: Option<&str>, 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() - .returns(ReturnsHandledOrError::new()) - .run() - .await; - - match response { - Ok(_) => println!("Pong successful!"), - Err(err) => { - println!("Pong failed with message: {}", err.message); - assert_eq!(message.unwrap_or_default(), err.message); - } - } - } - - 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 { - self.interactor - .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .get_contract_state() - .returns(ReturnsResult) - .run() - .await - } - - pub async fn get_ping_amount(&mut self) -> RustBigUint { - self.interactor - .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .ping_amount() - .returns(ReturnsResultUnmanaged) - .run() - .await - } - - pub async fn get_deadline(&mut self) -> u64 { - self.interactor - .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .deadline() - .returns(ReturnsResultUnmanaged) - .run() - .await - } - - pub async fn get_activation_timestamp(&mut self) -> u64 { - self.interactor - .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .activation_timestamp() - .returns(ReturnsResultUnmanaged) - .run() - .await - } - - pub async fn get_max_funds(&mut self) -> Option { - self.interactor - .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .max_funds() - .returns(ReturnsResultUnmanaged) - .run() - .await - } - - pub async fn get_user_status(&mut self, user_id: usize) -> UserStatus { - self.interactor - .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .user_status(user_id) - .returns(ReturnsResultUnmanaged) - .run() - .await - } - - pub async fn pong_all_last_user(&mut self) -> usize { - self.interactor - .query() - .to(self.state.current_ping_pong_egld_address()) - .typed(proxy_ping_pong_egld::PingPongProxy) - .pong_all_last_user() - .returns(ReturnsResultUnmanaged) - .run() - .await - } -} diff --git a/ping-pong/interactor/src/interact_cli.rs b/ping-pong/interactor/src/interact_cli.rs deleted file mode 100644 index 700f694..0000000 --- a/ping-pong/interactor/src/interact_cli.rs +++ /dev/null @@ -1,81 +0,0 @@ -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(DeployArgs), - #[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 = "pong-all", about = "Send back funds to all users who pinged.")] - PongAll, - #[command( - name = "user-addresses", - about = "Lists the addresses of all users that have `ping`-ed in the order they have `ping`-ed." - )] - GetUserAddresses, - #[command(name = "contract-state", about = "Returns the current contract state.")] - GetContractState, - #[command(name = "ping-amount", about = "Returns the ping amount.")] - GetPingAmount, - #[command(name = "deadline", about = "Return deadline.")] - GetDeadline, - #[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." - )] - 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, -} - -#[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 = 'a', long = "activation-timestamp")] - pub opt_activation_timestamp: Option, - - #[arg(short = 'm', long = "max-funds")] - pub max_funds: Option, -} - -#[derive(Default, Clone, PartialEq, Eq, Debug, Args)] -pub struct PingArgs { - #[arg(short = 'c', long = "cost", default_value = "50000000000000000")] - pub cost: Option, -} - -#[derive(Default, Clone, PartialEq, Eq, Debug, Args)] -pub struct UserStatusArgs { - #[arg(short = 'i')] - pub id: usize, -} diff --git a/ping-pong/interactor/src/interact_config.rs b/ping-pong/interactor/src/interact_config.rs deleted file mode 100644 index 013e167..0000000 --- a/ping-pong/interactor/src/interact_config.rs +++ /dev/null @@ -1,49 +0,0 @@ -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, -} - -/// Ping Pong 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_main.rs b/ping-pong/interactor/src/interact_main.rs deleted file mode 100644 index e9af240..0000000 --- a/ping-pong/interactor/src/interact_main.rs +++ /dev/null @@ -1,6 +0,0 @@ -extern crate ping_pong_interact; - -#[tokio::main] -pub async fn main() { - ping_pong_interact::ping_pong_egld_cli().await; -} diff --git a/ping-pong/interactor/src/interact_state.rs b/ping-pong/interactor/src/interact_state.rs deleted file mode 100644 index 38fb3d3..0000000 --- a/ping-pong/interactor/src/interact_state.rs +++ /dev/null @@ -1,50 +0,0 @@ -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_egld_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_egld_address(&mut self, address: Bech32Address) { - self.ping_pong_egld_address = Some(address); - } - - /// Returns the ping pong contract - pub fn current_ping_pong_egld_address(&self) -> &Bech32Address { - self.ping_pong_egld_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/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/meta/Cargo.toml b/ping-pong/meta/Cargo.toml index 5ee62a7..dd9e623 100644 --- a/ping-pong/meta/Cargo.toml +++ b/ping-pong/meta/Cargo.toml @@ -4,10 +4,8 @@ version = "0.0.0" authors = ["Bruda Claudiu-Marcel "] edition = "2021" publish = false - [dependencies.ping-pong] path = ".." [dependencies.multiversx-sc-meta-lib] -version = "0.54.6" -default-features = false +version = "0.52.3" diff --git a/ping-pong/mxpy.json b/ping-pong/mxpy.json new file mode 100644 index 0000000..88346ce --- /dev/null +++ b/ping-pong/mxpy.json @@ -0,0 +1,20 @@ +{ + "configurations": { + "default": { + "proxy": "https://devnet-api.multiversx.com", + "chainID": "D" + } + }, + "contract":{ + "deploy":{ + "verbose": true, + "bytecode": "output/ping-pong.wasm", + "recall-nonce": true, + "pem": "../../wallet/wallet-owner.pem", + "gas-limit": 59999999, + "arguments": [1000000000000000000, 600], + "send": true, + "outfile": "deploy-testnet.interaction.json" + } + } +} diff --git a/ping-pong/sc-config.toml b/ping-pong/sc-config.toml deleted file mode 100644 index bb96777..0000000 --- a/ping-pong/sc-config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[[proxy]] -path = "src/proxy_ping_pong_egld.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..2512614 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,17 +52,12 @@ "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" + "code": "file:../output/ping-pong.wasm" } } } 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..65cf135 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,16 +52,13 @@ "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" + "code": "file:../output/ping-pong.wasm" } } } ] -} \ No newline at end of file +} 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..91b501b 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" + "code": "file:../output/ping-pong.wasm" } } } ] -} +} \ 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..b6d5fd8 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": "file:../output/ping-pong.wasm", + "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": "file:../output/ping-pong.wasm" + } } + } ] } diff --git a/ping-pong/src/ping_pong.rs b/ping-pong/src/ping_pong.rs index 09f9b35..686995f 100644 --- a/ping-pong/src/ping_pong.rs +++ b/ping-pong/src/ping_pong.rs @@ -2,270 +2,145 @@ 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); - #[upgrade] - fn upgrade( - &self, - ping_amount: &BigUint, - duration_in_seconds: u64, - opt_activation_timestamp: Option, - max_funds: OptionalValue, - ) { - self.init( - ping_amount, - duration_in_seconds, - opt_activation_timestamp, - max_funds, - ) + 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); } - /// 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/proxy_ping_pong_egld.rs b/ping-pong/src/proxy_ping_pong_egld.rs deleted file mode 100644 index 803954e..0000000 --- a/ping-pong/src/proxy_ping_pong_egld.rs +++ /dev/null @@ -1,258 +0,0 @@ -// 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 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. - pub fn init< - Arg0: ProxyArg>, - Arg1: ProxyArg, - Arg2: ProxyArg>, - Arg3: ProxyArg>>, - >( - self, - ping_amount: Arg0, - duration_in_seconds: Arg1, - opt_activation_timestamp: Arg2, - max_funds: Arg3, - ) -> TxTypedDeploy { - self.wrapped_tx - .payment(NotPayable) - .raw_deploy() - .argument(&ping_amount) - .argument(&duration_in_seconds) - .argument(&opt_activation_timestamp) - .argument(&max_funds) - .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, - 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() - } -} - -#[rustfmt::skip] -impl PingPongProxyMethods -where - Env: TxEnv, - Env::Api: VMApi, - From: TxFrom, - To: TxTo, - Gas: TxGas, -{ - /// User sends some EGLD 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() - } - - /// 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( - self, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("pongAll") - .original_result() - } - - /// Lists the addresses of all users that have `ping`-ed, - /// in the order they have `ping`-ed - pub fn get_user_addresses( - self, - ) -> TxTypedCall>> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getUserAddresses") - .original_result() - } - - /// Returns the current contract state as a struct - /// for faster fetching from external parties - pub fn get_contract_state( - self, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getContractState") - .original_result() - } - - pub fn ping_amount( - self, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getPingAmount") - .original_result() - } - - pub fn deadline( - self, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getDeadline") - .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( - self, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("getActivationTimestamp") - .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, - >( - 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 { - self.wrapped_tx - .payment(NotPayable) - .raw_call("pongAllLastUser") - .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/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..99b1f2f 100644 --- a/ping-pong/tests/ping_pong_egld_scenario_rs_test.rs +++ b/ping-pong/tests/ping_pong_egld_scenario_rs_test.rs @@ -3,38 +3,15 @@ use multiversx_sc_scenario::*; fn world() -> ScenarioWorld { let mut blockchain = ScenarioWorld::new(); - blockchain.register_contract( - "mxsc:output/ping-pong.mxsc.json", - ping_pong::ContractBuilder, - ); + blockchain.register_contract("file:output/ping-pong.wasm", ping_pong::ContractBuilder); 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 +23,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 +32,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..2aea6fc 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" @@ -22,7 +28,7 @@ panic = "abort" path = ".." [dependencies.multiversx-sc-wasm-adapter] -version = "0.54.6" +version = "0.52.3" [workspace] members = ["."] diff --git a/ping-pong/wasm/src/lib.rs b/ping-pong/wasm/src/lib.rs index 5929539..69a0c04 100644 --- a/ping-pong/wasm/src/lib.rs +++ b/ping-pong/wasm/src/lib.rs @@ -5,10 +5,9 @@ //////////////////////////////////////////////////// // Init: 1 -// Upgrade: 1 -// Endpoints: 11 +// Endpoints: 9 // Async Callback (empty): 1 -// Total number of exported functions: 14 +// Total number of exported functions: 11 #![no_std] @@ -19,18 +18,15 @@ multiversx_sc_wasm_adapter::endpoints! { ping_pong ( init => init - 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 ) } From 0c9243fffb3c1c3e955843f1a456ae9242582412 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Tue, 10 Dec 2024 13:15:21 +0200 Subject: [PATCH 09/11] upgrade framework --- Cargo.toml | 6 ++---- elrond.workspace.json | 1 - ping-pong/Cargo.toml | 4 ++-- ping-pong/meta/Cargo.toml | 3 ++- ping-pong/mxpy.json | 20 ------------------- .../ping-pong-call-ping-second-user.scen.json | 4 ++-- .../scenarios/ping-pong-call-ping.scen.json | 4 ++-- .../scenarios/ping-pong-call-pong.scen.json | 2 +- ping-pong/scenarios/ping-pong-init.scen.json | 4 ++-- ping-pong/src/ping_pong.rs | 9 +++++++++ .../tests/ping_pong_egld_scenario_rs_test.rs | 5 ++++- ping-pong/wasm/Cargo.toml | 2 +- ping-pong/wasm/src/lib.rs | 4 +++- 13 files changed, 30 insertions(+), 38 deletions(-) delete mode 100644 elrond.workspace.json delete mode 100644 ping-pong/mxpy.json diff --git a/Cargo.toml b/Cargo.toml index d596a09..81d78ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,3 @@ [workspace] -members = [ - "ping-pong", - "ping-pong/meta" -] +resolver = "2" +members = ["ping-pong", "ping-pong/meta"] diff --git a/elrond.workspace.json b/elrond.workspace.json deleted file mode 100644 index 9e26dfe..0000000 --- a/elrond.workspace.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/ping-pong/Cargo.toml b/ping-pong/Cargo.toml index b2f7724..4b7804e 100644 --- a/ping-pong/Cargo.toml +++ b/ping-pong/Cargo.toml @@ -9,7 +9,7 @@ publish = false path = "src/ping_pong.rs" [dependencies.multiversx-sc] -version = "0.52.3" +version = "0.54.6" [dev-dependencies.multiversx-sc-scenario] -version = "0.52.3" +version = "0.54.6" diff --git a/ping-pong/meta/Cargo.toml b/ping-pong/meta/Cargo.toml index dd9e623..a106cb4 100644 --- a/ping-pong/meta/Cargo.toml +++ b/ping-pong/meta/Cargo.toml @@ -4,8 +4,9 @@ version = "0.0.0" authors = ["Bruda Claudiu-Marcel "] edition = "2021" publish = false + [dependencies.ping-pong] path = ".." [dependencies.multiversx-sc-meta-lib] -version = "0.52.3" +version = "0.54.6" diff --git a/ping-pong/mxpy.json b/ping-pong/mxpy.json deleted file mode 100644 index 88346ce..0000000 --- a/ping-pong/mxpy.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "configurations": { - "default": { - "proxy": "https://devnet-api.multiversx.com", - "chainID": "D" - } - }, - "contract":{ - "deploy":{ - "verbose": true, - "bytecode": "output/ping-pong.wasm", - "recall-nonce": true, - "pem": "../../wallet/wallet-owner.pem", - "gas-limit": 59999999, - "arguments": [1000000000000000000, 600], - "send": true, - "outfile": "deploy-testnet.interaction.json" - } - } -} 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 2512614..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 @@ -57,9 +57,9 @@ "str:userPingTimestamp|address:participant1": "781", "str:userPingTimestamp|address:participant2": "781" }, - "code": "file:../output/ping-pong.wasm" + "code": "mxsc:../output/ping-pong.mxsc.json" } } } ] -} +} \ No newline at end of file diff --git a/ping-pong/scenarios/ping-pong-call-ping.scen.json b/ping-pong/scenarios/ping-pong-call-ping.scen.json index 65cf135..45fa8fd 100644 --- a/ping-pong/scenarios/ping-pong-call-ping.scen.json +++ b/ping-pong/scenarios/ping-pong-call-ping.scen.json @@ -56,9 +56,9 @@ "str:acceptedPaymentTokenId": "str:EGLD", "str:userPingTimestamp|address:participant1": "781" }, - "code": "file:../output/ping-pong.wasm" + "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 91b501b..9224e4d 100644 --- a/ping-pong/scenarios/ping-pong-call-pong.scen.json +++ b/ping-pong/scenarios/ping-pong-call-pong.scen.json @@ -56,7 +56,7 @@ "str:durationInSeconds": "123,000", "str:acceptedPaymentTokenId": "str:EGLD" }, - "code": "file:../output/ping-pong.wasm" + "code": "mxsc:../output/ping-pong.mxsc.json" } } } diff --git a/ping-pong/scenarios/ping-pong-init.scen.json b/ping-pong/scenarios/ping-pong-init.scen.json index b6d5fd8..34a4fae 100644 --- a/ping-pong/scenarios/ping-pong-init.scen.json +++ b/ping-pong/scenarios/ping-pong-init.scen.json @@ -34,7 +34,7 @@ "txId": "deploy", "tx": { "from": "address:my_address", - "contractCode": "file:../output/ping-pong.wasm", + "contractCode": "mxsc:../output/ping-pong.mxsc.json", "value": "0", "arguments": [ "500,000,000,000", @@ -75,7 +75,7 @@ "str:durationInSeconds": "123,000", "str:acceptedPaymentTokenId": "str:EGLD" }, - "code": "file:../output/ping-pong.wasm" + "code": "mxsc:../output/ping-pong.mxsc.json" } } } diff --git a/ping-pong/src/ping_pong.rs b/ping-pong/src/ping_pong.rs index 686995f..fedf3ba 100644 --- a/ping-pong/src/ping_pong.rs +++ b/ping-pong/src/ping_pong.rs @@ -38,6 +38,15 @@ pub trait PingPong { self.accepted_payment_token_id().set(&token_id); } + #[upgrade] + fn upgrade(&self, ping_amount: BigUint, duration_in_seconds: u64) { + self.init( + ping_amount, + duration_in_seconds, + OptionalValue::Some(self.accepted_payment_token_id().get()), + ) + } + // endpoints /// User sends some tokens to be locked in the contract for a period of time. 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 99b1f2f..e6656d2 100644 --- a/ping-pong/tests/ping_pong_egld_scenario_rs_test.rs +++ b/ping-pong/tests/ping_pong_egld_scenario_rs_test.rs @@ -3,7 +3,10 @@ use multiversx_sc_scenario::*; fn world() -> ScenarioWorld { let mut blockchain = ScenarioWorld::new(); - blockchain.register_contract("file:output/ping-pong.wasm", ping_pong::ContractBuilder); + blockchain.register_contract( + "mxsc:output/ping-pong.mxsc.json", + ping_pong::ContractBuilder, + ); blockchain } diff --git a/ping-pong/wasm/Cargo.toml b/ping-pong/wasm/Cargo.toml index 2aea6fc..3f203e8 100644 --- a/ping-pong/wasm/Cargo.toml +++ b/ping-pong/wasm/Cargo.toml @@ -28,7 +28,7 @@ panic = "abort" path = ".." [dependencies.multiversx-sc-wasm-adapter] -version = "0.52.3" +version = "0.54.6" [workspace] members = ["."] diff --git a/ping-pong/wasm/src/lib.rs b/ping-pong/wasm/src/lib.rs index 69a0c04..ca90d6b 100644 --- a/ping-pong/wasm/src/lib.rs +++ b/ping-pong/wasm/src/lib.rs @@ -5,9 +5,10 @@ //////////////////////////////////////////////////// // Init: 1 +// Upgrade: 1 // Endpoints: 9 // Async Callback (empty): 1 -// Total number of exported functions: 11 +// Total number of exported functions: 12 #![no_std] @@ -18,6 +19,7 @@ multiversx_sc_wasm_adapter::endpoints! { ping_pong ( init => init + upgrade => upgrade ping => ping pong => pong didUserPing => did_user_ping From c39322d19cc17d4fdbe56c3d80e92380dd8b8928 Mon Sep 17 00:00:00 2001 From: BiancaIalangi Date: Tue, 10 Dec 2024 13:21:37 +0200 Subject: [PATCH 10/11] update github actions --- .github/workflows/actions.yml | 4 +++- ping-pong/Cargo.toml | 8 ++++++-- ping-pong/meta/Cargo.toml | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 1f01ab9..5abacbf 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -13,9 +13,11 @@ permissions: jobs: contracts: name: Contracts - uses: multiversx/mx-sc-actions/.github/workflows/contracts.yml@v3.3.1 + uses: multiversx/mx-sc-actions/.github/workflows/contracts.yml@72dc7659e6945c8749d01ec28638843bae33437e with: rust-toolchain: stable + sc-meta-hash-git: 51791cb3c23f8084e97c5ef0af82a9d16ab70cb9 + mx-scenario-go-version: v3.0.0 enable-contracts-size-report: false secrets: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/ping-pong/Cargo.toml b/ping-pong/Cargo.toml index 4b7804e..d44e121 100644 --- a/ping-pong/Cargo.toml +++ b/ping-pong/Cargo.toml @@ -9,7 +9,11 @@ publish = false path = "src/ping_pong.rs" [dependencies.multiversx-sc] -version = "0.54.6" +version = "=0.54.6" +git = "https://github.com/multiversx/mx-sdk-rs" +rev = "51791cb3c23f8084e97c5ef0af82a9d16ab70cb9" [dev-dependencies.multiversx-sc-scenario] -version = "0.54.6" +version = "=0.54.6" +git = "https://github.com/multiversx/mx-sdk-rs" +rev = "51791cb3c23f8084e97c5ef0af82a9d16ab70cb9" diff --git a/ping-pong/meta/Cargo.toml b/ping-pong/meta/Cargo.toml index a106cb4..fc4d53a 100644 --- a/ping-pong/meta/Cargo.toml +++ b/ping-pong/meta/Cargo.toml @@ -9,4 +9,6 @@ publish = false path = ".." [dependencies.multiversx-sc-meta-lib] -version = "0.54.6" +version = "=0.54.6" +git = "https://github.com/multiversx/mx-sdk-rs" +rev = "51791cb3c23f8084e97c5ef0af82a9d16ab70cb9" From 4f1fa75eadac1b9e696ec577404e88a1b16e514d Mon Sep 17 00:00:00 2001 From: Bianca Ialangi Date: Fri, 13 Dec 2024 16:28:12 +0200 Subject: [PATCH 11/11] remove git dependency framework version --- .github/workflows/actions.yml | 2 +- ping-pong/Cargo.toml | 8 ++------ ping-pong/meta/Cargo.toml | 4 +--- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 5abacbf..04bb65a 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -16,7 +16,7 @@ jobs: uses: multiversx/mx-sc-actions/.github/workflows/contracts.yml@72dc7659e6945c8749d01ec28638843bae33437e with: rust-toolchain: stable - sc-meta-hash-git: 51791cb3c23f8084e97c5ef0af82a9d16ab70cb9 + sc-meta-hash-git: ca051fc3d0c03ec1ce5624cf81bdada57b5fc3e2 mx-scenario-go-version: v3.0.0 enable-contracts-size-report: false secrets: diff --git a/ping-pong/Cargo.toml b/ping-pong/Cargo.toml index d44e121..4b7804e 100644 --- a/ping-pong/Cargo.toml +++ b/ping-pong/Cargo.toml @@ -9,11 +9,7 @@ publish = false path = "src/ping_pong.rs" [dependencies.multiversx-sc] -version = "=0.54.6" -git = "https://github.com/multiversx/mx-sdk-rs" -rev = "51791cb3c23f8084e97c5ef0af82a9d16ab70cb9" +version = "0.54.6" [dev-dependencies.multiversx-sc-scenario] -version = "=0.54.6" -git = "https://github.com/multiversx/mx-sdk-rs" -rev = "51791cb3c23f8084e97c5ef0af82a9d16ab70cb9" +version = "0.54.6" diff --git a/ping-pong/meta/Cargo.toml b/ping-pong/meta/Cargo.toml index fc4d53a..a106cb4 100644 --- a/ping-pong/meta/Cargo.toml +++ b/ping-pong/meta/Cargo.toml @@ -9,6 +9,4 @@ publish = false path = ".." [dependencies.multiversx-sc-meta-lib] -version = "=0.54.6" -git = "https://github.com/multiversx/mx-sdk-rs" -rev = "51791cb3c23f8084e97c5ef0af82a9d16ab70cb9" +version = "0.54.6"