From 9832d6ee00d4314a8f832d01bbb1387f299c2881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 17 Jun 2024 11:39:15 +0300 Subject: [PATCH 01/38] Add Potlock contract --- Cargo.toml | 2 + contracts/potlock/Cargo.toml | 24 +++++ contracts/potlock/README.md | 7 ++ contracts/potlock/meta/Cargo.toml | 12 +++ contracts/potlock/meta/src/main.rs | 3 + contracts/potlock/multiversx.json | 3 + contracts/potlock/scenarios/potlock.scen.json | 39 ++++++++ contracts/potlock/src/potlock.rs | 19 ++++ .../potlock/src/potlock_admin_interactions.rs | 71 ++++++++++++++ contracts/potlock/src/potlock_interactions.rs | 50 ++++++++++ contracts/potlock/src/potlock_setup.rs | 30 ++++++ contracts/potlock/src/potlock_storage.rs | 93 +++++++++++++++++++ .../potlock/tests/potlock_scenario_go_test.rs | 10 ++ .../potlock/tests/potlock_scenario_rs_test.rs | 13 +++ 14 files changed, 376 insertions(+) create mode 100644 contracts/potlock/Cargo.toml create mode 100644 contracts/potlock/README.md create mode 100644 contracts/potlock/meta/Cargo.toml create mode 100644 contracts/potlock/meta/src/main.rs create mode 100644 contracts/potlock/multiversx.json create mode 100644 contracts/potlock/scenarios/potlock.scen.json create mode 100644 contracts/potlock/src/potlock.rs create mode 100644 contracts/potlock/src/potlock_admin_interactions.rs create mode 100644 contracts/potlock/src/potlock_interactions.rs create mode 100644 contracts/potlock/src/potlock_setup.rs create mode 100644 contracts/potlock/src/potlock_storage.rs create mode 100644 contracts/potlock/tests/potlock_scenario_go_test.rs create mode 100644 contracts/potlock/tests/potlock_scenario_rs_test.rs diff --git a/Cargo.toml b/Cargo.toml index 8fac2496..513fff4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,8 @@ members = [ "contracts/paymaster/meta", "contracts/ping-pong-egld", "contracts/ping-pong-egld/meta", + "contracts/potlock", + "contracts/potlock/meta", "contracts/proxy-deployer", "contracts/proxy-deployer/meta", "contracts/proxy-pause", diff --git a/contracts/potlock/Cargo.toml b/contracts/potlock/Cargo.toml new file mode 100644 index 00000000..0d2bed35 --- /dev/null +++ b/contracts/potlock/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "potlock" +version = "0.0.0" +authors = ["you"] +edition = "2021" +publish = false + +[lib] +path = "src/potlock.rs" + +[dependencies.multiversx-sc] +version = "0.50.4" + +[dev-dependencies] +num-bigint = "0.4" + +[dev-dependencies.multiversx-sc-scenario] +version = "0.50.4" + +[workspace] +members = [ + ".", + "meta", +] diff --git a/contracts/potlock/README.md b/contracts/potlock/README.md new file mode 100644 index 00000000..aada8d69 --- /dev/null +++ b/contracts/potlock/README.md @@ -0,0 +1,7 @@ +# Paymaster SC + +## Overview + + + +## Implementation diff --git a/contracts/potlock/meta/Cargo.toml b/contracts/potlock/meta/Cargo.toml new file mode 100644 index 00000000..4f254f86 --- /dev/null +++ b/contracts/potlock/meta/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "potlock-meta" +version = "0.0.0" +edition = "2021" +publish = false + +[dependencies.potlock] +path = ".." + +[dependencies.multiversx-sc-meta] +version = "0.50.4" +default-features = false diff --git a/contracts/potlock/meta/src/main.rs b/contracts/potlock/meta/src/main.rs new file mode 100644 index 00000000..7a7e92da --- /dev/null +++ b/contracts/potlock/meta/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + multiversx_sc_meta::cli_main::(); +} diff --git a/contracts/potlock/multiversx.json b/contracts/potlock/multiversx.json new file mode 100644 index 00000000..73655396 --- /dev/null +++ b/contracts/potlock/multiversx.json @@ -0,0 +1,3 @@ +{ + "language": "rust" +} \ No newline at end of file diff --git a/contracts/potlock/scenarios/potlock.scen.json b/contracts/potlock/scenarios/potlock.scen.json new file mode 100644 index 00000000..d2e265a5 --- /dev/null +++ b/contracts/potlock/scenarios/potlock.scen.json @@ -0,0 +1,39 @@ +{ + "name": "empty", + "steps": [ + { + "step": "setState", + "accounts": { + "address:owner": { + "nonce": "1", + "balance": "0" + } + }, + "newAddresses": [ + { + "creatorAddress": "address:owner", + "creatorNonce": "1", + "newAddress": "sc:empty" + } + ] + }, + { + "step": "scDeploy", + "id": "deploy", + "tx": { + "from": "address:owner", + "contractCode": "mxsc:../output/potlock.mxsc.json", + "arguments": [], + "gasLimit": "5,000,000", + "gasPrice": "0" + }, + "expect": { + "out": [], + "status": "", + "logs": [], + "gas": "*", + "refund": "*" + } + } + ] +} diff --git a/contracts/potlock/src/potlock.rs b/contracts/potlock/src/potlock.rs new file mode 100644 index 00000000..810c42c9 --- /dev/null +++ b/contracts/potlock/src/potlock.rs @@ -0,0 +1,19 @@ +#![no_std] + +use multiversx_sc::imports::*; +mod potlock_setup; +mod potlock_storage; +mod potlock_interactions; +mod potlock_admin_interactions; + +/// An empty contract. To be used as a template when starting a new contract from scratch. +#[multiversx_sc::contract] +pub trait Potlock: potlock_admin_interactions: PotlockAdminInteractions + potlock_interactions: PotlockInteractions + potlock_setup: PotlockSetup + potlock_storage: PotlockStorage { + #[init] + fn init(&self, admin: ManagedAddress) { + self.admins().add(admin); + } + + #[upgrade] + fn upgrade(&self) {} +} diff --git a/contracts/potlock/src/potlock_admin_interactions.rs b/contracts/potlock/src/potlock_admin_interactions.rs new file mode 100644 index 00000000..0a0e969b --- /dev/null +++ b/contracts/potlock/src/potlock_admin_interactions.rs @@ -0,0 +1,71 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +pub type ProjectPercentage = MultiValue2; + +#[multiversx_sc::module] +pub trait PotlockAdminInteractions: + crate::potlock::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule +{ + #[only_admin] + #[endpoint(acceptPot)] + fn accept_pot(&self, potlock_id: PotlockId) { + require_potlock_exists(potlock_id); + // TODO: Common fund is another contract? + } + + #[only_admin] + #[endpoint(rejectPot)] + fn reject_pot(&self, potlock_id: PotlockId) { + require_potlock_exists(potlock_id); + + //TODO: Common fund is another contract? + //TODO: "will return the fee back to the user" + + //TODO: Should we remove the potlock? + self.potlocks().clear_entry(proposal_id); + } + + #[only_admin] + #[endpoint(removePot)] + fn remove_pot(&self, potlock_id: PotlockId) { + let caller = self.blockchain().get_caller(); + let payment = self.fee_pot_payments(potlock_id, caller).get(); + + self.send().direct_non_zero_esdt_payment(&caller, &payment); + self.potlocks().clear_entry(proposal_id); + } + + #[only_admin] + #[endpoint(acceptApplication)] + fn accept_application(&self, project: Projectid) { + require_potlock_exists(potlock_id); + // TODO: How should we KYC verification in the SC? + } + rejectDonation@userID@listOfTokens - returns tokens to the users. + + #[only_admin] + #[endpoint(rejectDonation)] + fn reject_donation(&self, potlock: PotlockId, user: ManagedAddress) { + require_potlock_exists(potlock_id); + let fee_pot_payments = self.fee_pot_payments(potlock_id, user); + self.send().direct_non_zero_esdt_payment(&user, &fee_pot_payments); + } + + + #[only_admin] + #[endpoint(distributePotToProjects)] + fn distribute_pot_to_projects(&self, potlock: PotlockId, project_percentage: MultiValueEncoded) { + require_potlock_exists(potlock_id); + let potlock = self.potlocks().get(); + for pp in project_percentage { + let (project_id, percentage) = pp.into_tuple(); + + } + // TODO: How should we KYC verification in the SC? + } + + fn get_total_payments_for_pot(&self) { + let fee_pot_payments = self.fee_pot_payments().get(); + } +} diff --git a/contracts/potlock/src/potlock_interactions.rs b/contracts/potlock/src/potlock_interactions.rs new file mode 100644 index 00000000..c109c286 --- /dev/null +++ b/contracts/potlock/src/potlock_interactions.rs @@ -0,0 +1,50 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +#[multiversx_sc::module] +pub trait PotlockInteractions: crate::potlock::PotlockStorage { + #[payable("*")] + #[endpoint(addPot)] + fn add_pot(&self, name: ManagedBuffer, description: ManagedBuffer) { + let payment = self.call_value().egld_or_single_esdt().into_tuple(); + require!( + self.fee_token_identifier().get() == payment.token_identifier, + "Wrong token identifier for creating a pot!" + ); + require!( + self.fee_amount().get() == payment.amount, + "Wrong fee amount for creating a pot" + ); + let caller = self.blockchain().get_caller(); + + let potlock = Potlock::new(name, description); + let potlock_id = self.potlocks().push(&potlock); + let fee_pot_payments = self.fee_pot_payments(potlock_id, caller).insert(payment); + } + + #[endpoint(applyForPot)] + fn apply_for_pot(&self, project_name: ManagedBuffer, description: ManagedBuffer) { + // TODO: should we set a SC address as a parameter or assume (and verifiy) that the SC will call this endpoint + // TODO: This address will receive the funds + let caller = self.blockchain().get_caller(); + let project = Project::new(name, description); + let project_id = self.projects().push(&project); + } + + #[payable("*")] + #[endpoint(donateToPot)] + fn donate_to_pot(&self, potlock_id: PotlockId) { + let payment = self.call_value().egld_or_single_esdt().into_tuple(); + let caller = self.blockchain().get_caller(); + self.fee_pot_payments(potlock_id).insert(payment); + } + + #[payable("*")] + #[endpoint(donateToProject)] + fn donate_to_project(&self, project_id: ProjectId) { + let payment = self.call_value().egld_or_single_esdt().into_tuple(); + let caller = self.blockchain().get_caller(); + self.fee_project_payments(project_id, caller) + .insert(payment); + } +} diff --git a/contracts/potlock/src/potlock_setup.rs b/contracts/potlock/src/potlock_setup.rs new file mode 100644 index 00000000..f3ed6d0f --- /dev/null +++ b/contracts/potlock/src/potlock_setup.rs @@ -0,0 +1,30 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +#[multiversx_sc::module] +pub trait PotlockSetup: + crate::potlock::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule +{ + #[only_admin] + #[endpoint(changeFeeForPots)] + fn change_fee_for_pots(&self, token_identifier: TokenIdentifier, fee: BigUint) { + require!( + token_identifier.is_valid_esdt_identifier(), + "Invalid token provided" + ); + self.fee_token_identifier().set(&token_identifier); + self.fee_amount().set(fee); + } + + //// internal functions + fn is_valid_potlock_id(&self, potlock_id: PotlockId) -> bool { + potlock_id >= 1 && potlock_id <= self.potlocks().len() + } + + fn require_potlock_exists(&self, potlock_id: PotlockId) { + require( + self.is_valid_potlock_id(potlock_id) && !self.potlock().item_is_empty(potlock_id), + "Potlock doesn't exist!", + ) + } +} diff --git a/contracts/potlock/src/potlock_storage.rs b/contracts/potlock/src/potlock_storage.rs new file mode 100644 index 00000000..5c3b0fd6 --- /dev/null +++ b/contracts/potlock/src/potlock_storage.rs @@ -0,0 +1,93 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +pub type PotlockId = usize; +pub type ProjectId = usize; + +#[derive(TypeAbi, TopEncode, TopDecode, PartialEq, Eq)] +pub enum PotlockStatus { + None, + Active, + Inactive, +} + +#[derive( + TypeAbi, NestedEncode, NestedDecode, PartialEq, Debug, TopEncodeOrDefault, TopDecodeOrDefault, +)] +pub struct Potlock { + pub potlock_id: PotlocklId, + pub token_identifier: TokenIdentifier, + pub fee: BigUint, + pub name: ManagedBuffer, + pub description: ManagedBuffer, + pub status: PotlockStatus, + // pub payment: EsdtTokenPayment, +} + +impl Default for Potlock { + fn default() -> Self { + Self::new() + } +} + +impl Potlock { + pub fn new(name: ManagedBuffer, description: ManagedBuffer) -> Self { + Potlock{ + potlock_id: self.potlocks().len() + 1, + token_identifier: TokenIdentifier::from(ManagedBuffer::default()). + fee: BigUint::default(), + name, + description, + } + } +} + +pub struct Project { + pub project_id: PotlocklId, + pub name: ManagedBuffer, + pub description: ManagedBuffer, + pub address: ManagedAddress, +} + +impl Default for Project { + fn default() -> Self { + Self::new() + } +} + +impl Project { + pub fn new(name: ManagedBuffer, description: ManagedBuffer) -> Self { + Project{ + project_id: self.proposals().len() + 1, + name, + description, + } + } +} + +#[multiversx_sc::module] +pub trait PotlockStorage { + #[view(getFeeTokenIdentifier)] + #[storage_mapper("fee_token_identifier")] + fn fee_token_identifier(&self) -> SingleValueMapper; + + #[view(getFeeAmount)] + #[storage_mapper("fee_amount")] + fn fee_amount(&self) -> SingleValueMapper; + + #[view(getPotlocks)] + #[storage_mapper("potlocks")] + fn potlocks(&self) -> VecMapper>; + + #[view(getProjects)] + #[storage_mapper("projects")] + fn projects(&self) -> VecMapper>; + + #[view(feePotPayments)] + #[storage_mapper("fee_pot_payments")] + fn fee_pot_payments(&self, potlock_id: PotlockId, address: &ManagedAddress) -> UnorderedSetMapper; + + #[view(feeProjectPayments)] + #[storage_mapper("fee_project_payments")] + fn fee_project_payments(&self, project_id: ProjectId, address: &ManagedAddress) -> UnorderedSetMapper; +} diff --git a/contracts/potlock/tests/potlock_scenario_go_test.rs b/contracts/potlock/tests/potlock_scenario_go_test.rs new file mode 100644 index 00000000..5ecddde2 --- /dev/null +++ b/contracts/potlock/tests/potlock_scenario_go_test.rs @@ -0,0 +1,10 @@ +use multiversx_sc_scenario::*; + +fn world() -> ScenarioWorld { + ScenarioWorld::vm_go() +} + +#[test] +fn empty_go() { + world().run("scenarios/potlock.scen.json"); +} diff --git a/contracts/potlock/tests/potlock_scenario_rs_test.rs b/contracts/potlock/tests/potlock_scenario_rs_test.rs new file mode 100644 index 00000000..b417ee4c --- /dev/null +++ b/contracts/potlock/tests/potlock_scenario_rs_test.rs @@ -0,0 +1,13 @@ +use multiversx_sc_scenario::*; + +fn world() -> ScenarioWorld { + let mut blockchain = ScenarioWorld::new(); + + blockchain.register_contract("mxsc:output/potlock.mxsc.json", potlock::ContractBuilder); + blockchain +} + +#[test] +fn empty_rs() { + world().run("scenarios/potlock.scen.json"); +} From e641553efdb9726a969296fb4b4d6f1f52acc16e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 18 Jun 2024 12:41:01 +0300 Subject: [PATCH 02/38] Refactor donations --- Cargo.lock | 391 +++++++++++++++--- contracts/potlock/Cargo.toml | 16 +- contracts/potlock/meta/Cargo.toml | 2 +- contracts/potlock/src/potlock.rs | 23 +- .../potlock/src/potlock_admin_interactions.rs | 96 +++-- contracts/potlock/src/potlock_interactions.rs | 50 ++- contracts/potlock/src/potlock_setup.rs | 20 +- contracts/potlock/src/potlock_storage.rs | 85 ++-- contracts/potlock/wasm/Cargo.toml | 31 ++ contracts/potlock/wasm/src/libs.rs | 0 10 files changed, 543 insertions(+), 171 deletions(-) create mode 100644 contracts/potlock/wasm/Cargo.toml create mode 100644 contracts/potlock/wasm/src/libs.rs diff --git a/Cargo.lock b/Cargo.lock index 5db6b23d..a0d798f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,9 +41,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -95,9 +95,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] @@ -132,9 +132,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -256,9 +256,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" [[package]] name = "cfg-if" @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -296,9 +296,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -308,9 +308,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck", "proc-macro2", @@ -320,9 +320,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" @@ -555,6 +555,17 @@ dependencies = [ "multiversx-sc-meta", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dn404" version = "0.0.0" @@ -938,9 +949,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "globset" @@ -1037,9 +1048,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" [[package]] name = "httpdate" @@ -1055,9 +1066,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", @@ -1090,14 +1101,134 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "idna" -version = "0.5.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -1269,6 +1400,12 @@ dependencies = [ "multiversx-sc-meta", ] +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "lock_api" version = "0.4.12" @@ -1303,9 +1440,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -1358,7 +1495,7 @@ dependencies = [ "multiversx-sc-scenario", "multiversx-sc-snippets", "serde", - "toml 0.8.13", + "toml 0.8.14", ] [[package]] @@ -1500,7 +1637,7 @@ dependencies = [ "semver", "serde", "serde_json", - "toml 0.8.13", + "toml 0.8.14", "wasmparser 0.120.0", "wasmprinter", "zip", @@ -1761,9 +1898,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -1999,6 +2136,24 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" +[[package]] +name = "potlock" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "multiversx-sc-modules", + "multiversx-sc-scenario", + "num-bigint", +] + +[[package]] +name = "potlock-meta" +version = "0.0.0" +dependencies = [ + "multiversx-sc-meta", + "potlock", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2116,18 +2271,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.4.2", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -2137,9 +2292,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -2148,9 +2303,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" @@ -2474,6 +2629,12 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.11.1" @@ -2503,6 +2664,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -2536,6 +2708,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -2569,9 +2751,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -2588,9 +2770,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", @@ -2634,15 +2816,15 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.13", + "toml_edit 0.22.14", ] [[package]] @@ -2669,15 +2851,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.8", + "winnow 0.6.13", ] [[package]] @@ -2717,12 +2899,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -2746,20 +2922,32 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "url" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "vcpkg" @@ -3065,9 +3253,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -3082,12 +3270,91 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerovec" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zip" version = "0.6.6" diff --git a/contracts/potlock/Cargo.toml b/contracts/potlock/Cargo.toml index 0d2bed35..55eee8c1 100644 --- a/contracts/potlock/Cargo.toml +++ b/contracts/potlock/Cargo.toml @@ -4,21 +4,19 @@ version = "0.0.0" authors = ["you"] edition = "2021" publish = false +readme = "README.md" [lib] path = "src/potlock.rs" [dependencies.multiversx-sc] -version = "0.50.4" +version = "0.47.2" + +[dependencies.multiversx-sc-modules] +version = "=0.47.2" [dev-dependencies] -num-bigint = "0.4" +num-bigint = "0.4.2" [dev-dependencies.multiversx-sc-scenario] -version = "0.50.4" - -[workspace] -members = [ - ".", - "meta", -] +version = "0.47.2" \ No newline at end of file diff --git a/contracts/potlock/meta/Cargo.toml b/contracts/potlock/meta/Cargo.toml index 4f254f86..a13049a3 100644 --- a/contracts/potlock/meta/Cargo.toml +++ b/contracts/potlock/meta/Cargo.toml @@ -8,5 +8,5 @@ publish = false path = ".." [dependencies.multiversx-sc-meta] -version = "0.50.4" +version = "0.47.2" default-features = false diff --git a/contracts/potlock/src/potlock.rs b/contracts/potlock/src/potlock.rs index 810c42c9..2e119b5c 100644 --- a/contracts/potlock/src/potlock.rs +++ b/contracts/potlock/src/potlock.rs @@ -1,17 +1,26 @@ #![no_std] -use multiversx_sc::imports::*; -mod potlock_setup; -mod potlock_storage; -mod potlock_interactions; -mod potlock_admin_interactions; +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); +pub mod potlock_admin_interactions; +pub mod potlock_interactions; +pub mod potlock_setup; +pub mod potlock_storage; /// An empty contract. To be used as a template when starting a new contract from scratch. #[multiversx_sc::contract] -pub trait Potlock: potlock_admin_interactions: PotlockAdminInteractions + potlock_interactions: PotlockInteractions + potlock_setup: PotlockSetup + potlock_storage: PotlockStorage { +pub trait Potlock: + potlock_admin_interactions::PotlockAdminInteractions + + potlock_interactions::PotlockInteractions + + potlock_setup::PotlockSetup + + potlock_storage::PotlockStorage + + multiversx_sc_modules::only_admin::OnlyAdminModule +{ #[init] fn init(&self, admin: ManagedAddress) { - self.admins().add(admin); + let caller = self.blockchain().get_caller(); + self.admins().insert(caller); + self.admins().insert(admin); } #[upgrade] diff --git a/contracts/potlock/src/potlock_admin_interactions.rs b/contracts/potlock/src/potlock_admin_interactions.rs index 0a0e969b..35bc8585 100644 --- a/contracts/potlock/src/potlock_admin_interactions.rs +++ b/contracts/potlock/src/potlock_admin_interactions.rs @@ -1,71 +1,95 @@ +use crate::{ + potlock_setup, + potlock_storage::{self, PotlockId, ProjectId}, +}; + multiversx_sc::imports!(); multiversx_sc::derive_imports!(); -pub type ProjectPercentage = MultiValue2; +pub type ProjectPercentage = MultiValue2; #[multiversx_sc::module] pub trait PotlockAdminInteractions: - crate::potlock::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule + potlock_storage::PotlockStorage + + multiversx_sc_modules::only_admin::OnlyAdminModule + + potlock_setup::PotlockSetup { #[only_admin] #[endpoint(acceptPot)] fn accept_pot(&self, potlock_id: PotlockId) { - require_potlock_exists(potlock_id); - // TODO: Common fund is another contract? - } - - #[only_admin] - #[endpoint(rejectPot)] - fn reject_pot(&self, potlock_id: PotlockId) { - require_potlock_exists(potlock_id); - - //TODO: Common fund is another contract? - //TODO: "will return the fee back to the user" + self.require_potlock_exists(potlock_id); + let fee_amount = self.fee_amount().get(); - //TODO: Should we remove the potlock? - self.potlocks().clear_entry(proposal_id); + self.fee_amount_accepted_pots() + .update(|amount| *amount += fee_amount); + self.fee_pot_proposer(potlock_id).clear(); } #[only_admin] #[endpoint(removePot)] fn remove_pot(&self, potlock_id: PotlockId) { - let caller = self.blockchain().get_caller(); - let payment = self.fee_pot_payments(potlock_id, caller).get(); + let pot_proposer = self.fee_pot_proposer(potlock_id).get(); + let fee_pot_payment = EsdtTokenPayment::new( + self.fee_token_identifier().get(), + 0u64, + self.fee_amount().get(), + ); - self.send().direct_non_zero_esdt_payment(&caller, &payment); - self.potlocks().clear_entry(proposal_id); + self.send() + .direct_non_zero_esdt_payment(&pot_proposer, &fee_pot_payment); + self.fee_pot_proposer(potlock_id).clear(); + self.potlocks().clear_entry(potlock_id); } #[only_admin] #[endpoint(acceptApplication)] - fn accept_application(&self, project: Projectid) { - require_potlock_exists(potlock_id); - // TODO: How should we KYC verification in the SC? + fn accept_application(&self, project: ProjectId) { + self.require_project_exists(project); + // TODO: Mark project's status as accepted } - rejectDonation@userID@listOfTokens - returns tokens to the users. #[only_admin] #[endpoint(rejectDonation)] - fn reject_donation(&self, potlock: PotlockId, user: ManagedAddress) { - require_potlock_exists(potlock_id); - let fee_pot_payments = self.fee_pot_payments(potlock_id, user); - self.send().direct_non_zero_esdt_payment(&user, &fee_pot_payments); - } + fn reject_donation(&self, potlock_id: PotlockId, user: ManagedAddress) { + self.require_potlock_exists(potlock_id); + let opt_fee_pot_payments = self.pot_donations(potlock_id).get(&user); + require!(opt_fee_pot_payments.is_some(), "No donation for this user"); + let fee_pot_payments = unsafe { opt_fee_pot_payments.unwrap_unchecked() }; + + self.send() + .direct_non_zero_esdt_payment(&user, &fee_pot_payments); + self.pot_donations(potlock_id).remove(&user); + } #[only_admin] #[endpoint(distributePotToProjects)] - fn distribute_pot_to_projects(&self, potlock: PotlockId, project_percentage: MultiValueEncoded) { - require_potlock_exists(potlock_id); - let potlock = self.potlocks().get(); + fn distribute_pot_to_projects( + &self, + potlock_id: PotlockId, + project_percentage: MultiValueEncoded, + ) { + self.require_potlock_exists(potlock_id); + let pot_donations = self.pot_donations(potlock_id); + for pp in project_percentage { let (project_id, percentage) = pp.into_tuple(); - + let mut output_payments = ManagedVec::new(); + for (_, donation) in pot_donations.iter() { + let project_share_amount = donation.amount * percentage; + let project_share = EsdtTokenPayment::new( + donation.token_identifier, + donation.token_nonce, + project_share_amount, + ); + output_payments.push(project_share); + } + let project_owner = self.projects().get(project_id).owner; + self.send().direct_multi(&project_owner, &output_payments); } - // TODO: How should we KYC verification in the SC? - } - fn get_total_payments_for_pot(&self) { - let fee_pot_payments = self.fee_pot_payments().get(); + self.pot_donations(potlock_id).clear(); + + //TODO: Clear all info regarding the pot? } } diff --git a/contracts/potlock/src/potlock_interactions.rs b/contracts/potlock/src/potlock_interactions.rs index c109c286..4a513eff 100644 --- a/contracts/potlock/src/potlock_interactions.rs +++ b/contracts/potlock/src/potlock_interactions.rs @@ -1,50 +1,64 @@ +use crate::potlock_setup; +use crate::potlock_storage::{self, Pot, Project}; +use crate::potlock_storage::{PotlockId, ProjectId}; + multiversx_sc::imports!(); multiversx_sc::derive_imports!(); #[multiversx_sc::module] -pub trait PotlockInteractions: crate::potlock::PotlockStorage { +pub trait PotlockInteractions: + potlock_storage::PotlockStorage + + potlock_setup::PotlockSetup + + multiversx_sc_modules::only_admin::OnlyAdminModule +{ #[payable("*")] #[endpoint(addPot)] fn add_pot(&self, name: ManagedBuffer, description: ManagedBuffer) { - let payment = self.call_value().egld_or_single_esdt().into_tuple(); + let payment_for_adding_pot = self.call_value().single_esdt(); require!( - self.fee_token_identifier().get() == payment.token_identifier, + self.fee_token_identifier().get() == payment_for_adding_pot.token_identifier, "Wrong token identifier for creating a pot!" ); require!( - self.fee_amount().get() == payment.amount, + self.fee_amount().get() == payment_for_adding_pot.amount, "Wrong fee amount for creating a pot" ); let caller = self.blockchain().get_caller(); - let potlock = Potlock::new(name, description); - let potlock_id = self.potlocks().push(&potlock); - let fee_pot_payments = self.fee_pot_payments(potlock_id, caller).insert(payment); + let potlock_id = self.potlocks().len() + 1; + let potlock = Pot::new(potlock_id, name, description); + self.potlocks().push(&potlock); + + self.fee_pot_proposer(potlock_id).set(caller); } #[endpoint(applyForPot)] - fn apply_for_pot(&self, project_name: ManagedBuffer, description: ManagedBuffer) { - // TODO: should we set a SC address as a parameter or assume (and verifiy) that the SC will call this endpoint - // TODO: This address will receive the funds - let caller = self.blockchain().get_caller(); - let project = Project::new(name, description); - let project_id = self.projects().push(&project); + fn apply_for_pot( + &self, + potlock_id: PotlockId, + project_name: ManagedBuffer, + description: ManagedBuffer, + ) { + let project_id = self.projects().len() + 1; + let owner = self.blockchain().get_caller(); + let project = Project::new(project_id, potlock_id, project_name, description, owner); + self.projects().push(&project); } #[payable("*")] #[endpoint(donateToPot)] fn donate_to_pot(&self, potlock_id: PotlockId) { - let payment = self.call_value().egld_or_single_esdt().into_tuple(); + let payment = self.call_value().single_esdt(); let caller = self.blockchain().get_caller(); - self.fee_pot_payments(potlock_id).insert(payment); + self.pot_donations(potlock_id).insert(caller, payment); } #[payable("*")] #[endpoint(donateToProject)] fn donate_to_project(&self, project_id: ProjectId) { - let payment = self.call_value().egld_or_single_esdt().into_tuple(); + self.require_project_exists(project_id); + let payment = self.call_value().single_esdt(); let caller = self.blockchain().get_caller(); - self.fee_project_payments(project_id, caller) - .insert(payment); + self.project_donations(project_id).insert(caller, payment); } } diff --git a/contracts/potlock/src/potlock_setup.rs b/contracts/potlock/src/potlock_setup.rs index f3ed6d0f..20f4ca01 100644 --- a/contracts/potlock/src/potlock_setup.rs +++ b/contracts/potlock/src/potlock_setup.rs @@ -1,9 +1,10 @@ +use crate::potlock_storage::{self, PotlockId, ProjectId}; + multiversx_sc::imports!(); -multiversx_sc::derive_imports!(); #[multiversx_sc::module] pub trait PotlockSetup: - crate::potlock::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule + potlock_storage::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule { #[only_admin] #[endpoint(changeFeeForPots)] @@ -22,9 +23,20 @@ pub trait PotlockSetup: } fn require_potlock_exists(&self, potlock_id: PotlockId) { - require( - self.is_valid_potlock_id(potlock_id) && !self.potlock().item_is_empty(potlock_id), + require!( + self.is_valid_potlock_id(potlock_id) && !self.potlocks().item_is_empty(potlock_id), "Potlock doesn't exist!", ) } + + fn is_valid_project_id(&self, project_id: ProjectId) -> bool { + project_id >= 1 && project_id <= self.projects().len() + } + + fn require_project_exists(&self, project_id: ProjectId) { + require!( + self.is_valid_project_id(project_id) && !self.projects().item_is_empty(project_id), + "Project doesn't exist!", + ) + } } diff --git a/contracts/potlock/src/potlock_storage.rs b/contracts/potlock/src/potlock_storage.rs index 5c3b0fd6..683f1173 100644 --- a/contracts/potlock/src/potlock_storage.rs +++ b/contracts/potlock/src/potlock_storage.rs @@ -11,30 +11,26 @@ pub enum PotlockStatus { Inactive, } -#[derive( - TypeAbi, NestedEncode, NestedDecode, PartialEq, Debug, TopEncodeOrDefault, TopDecodeOrDefault, -)] -pub struct Potlock { - pub potlock_id: PotlocklId, +#[derive(TypeAbi, NestedEncode, NestedDecode, PartialEq, Debug, TopEncode, TopDecode)] +pub struct Pot { + pub potlock_id: PotlockId, pub token_identifier: TokenIdentifier, pub fee: BigUint, pub name: ManagedBuffer, pub description: ManagedBuffer, - pub status: PotlockStatus, + // pub status: PotlockStatus, // pub payment: EsdtTokenPayment, } -impl Default for Potlock { - fn default() -> Self { - Self::new() - } -} - -impl Potlock { - pub fn new(name: ManagedBuffer, description: ManagedBuffer) -> Self { - Potlock{ - potlock_id: self.potlocks().len() + 1, - token_identifier: TokenIdentifier::from(ManagedBuffer::default()). +impl Pot { + pub fn new( + potlock_id: PotlockId, + name: ManagedBuffer, + description: ManagedBuffer, + ) -> Self { + Pot { + potlock_id, + token_identifier: TokenIdentifier::from(ManagedBuffer::default()), fee: BigUint::default(), name, description, @@ -42,29 +38,39 @@ impl Potlock { } } +#[derive(TypeAbi, NestedEncode, NestedDecode, PartialEq, Debug, TopEncode, TopDecode)] pub struct Project { - pub project_id: PotlocklId, + pub project_id: ProjectId, + pub potlock_id: PotlockId, pub name: ManagedBuffer, pub description: ManagedBuffer, - pub address: ManagedAddress, -} - -impl Default for Project { - fn default() -> Self { - Self::new() - } + pub owner: ManagedAddress, } impl Project { - pub fn new(name: ManagedBuffer, description: ManagedBuffer) -> Self { - Project{ - project_id: self.proposals().len() + 1, + pub fn new( + project_id: ProjectId, + potlock_id: PotlockId, + name: ManagedBuffer, + description: ManagedBuffer, + owner: ManagedAddress, + ) -> Self { + Project { + project_id, + potlock_id, name, description, + owner, } } } +#[derive(TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode, PartialEq, Debug)] +pub struct UserDonations { + pub user: ManagedAddress, + pub donations: EsdtTokenPayment, +} + #[multiversx_sc::module] pub trait PotlockStorage { #[view(getFeeTokenIdentifier)] @@ -77,17 +83,28 @@ pub trait PotlockStorage { #[view(getPotlocks)] #[storage_mapper("potlocks")] - fn potlocks(&self) -> VecMapper>; + fn potlocks(&self) -> VecMapper>; #[view(getProjects)] #[storage_mapper("projects")] fn projects(&self) -> VecMapper>; #[view(feePotPayments)] - #[storage_mapper("fee_pot_payments")] - fn fee_pot_payments(&self, potlock_id: PotlockId, address: &ManagedAddress) -> UnorderedSetMapper; + #[storage_mapper("fee_pot_proposer")] + fn fee_pot_proposer(&self, potlock_id: PotlockId) -> SingleValueMapper; + + #[view(feeAmountAcceptPots)] + #[storage_mapper("fee_amount_accepted_pots")] + fn fee_amount_accepted_pots(&self) -> SingleValueMapper; + + #[view(potDonations)] + #[storage_mapper("pot_donations")] + fn pot_donations(&self, project_id: ProjectId) -> MapMapper; - #[view(feeProjectPayments)] - #[storage_mapper("fee_project_payments")] - fn fee_project_payments(&self, project_id: ProjectId, address: &ManagedAddress) -> UnorderedSetMapper; + #[view(projectDonations)] + #[storage_mapper("project_donations")] + fn project_donations( + &self, + project_id: ProjectId, + ) -> MapMapper; } diff --git a/contracts/potlock/wasm/Cargo.toml b/contracts/potlock/wasm/Cargo.toml new file mode 100644 index 00000000..b2c60bf0 --- /dev/null +++ b/contracts/potlock/wasm/Cargo.toml @@ -0,0 +1,31 @@ +# Code generated by the multiversx-sc build system. DO NOT EDIT. + +# ########################################## +# ############## AUTO-GENERATED ############# +# ########################################## + +[package] +name = "potlock-wasm" +version = "0.0.0" +edition = "2018" +publish = false + +[lib] +crate-type = ["cdylib"] + +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = true +debug = false +panic = "abort" +overflow-checks = false + +[dependencies.potlock] +path = ".." + +[dependencies.multiversx-sc-wasm-adapter] +version = "0.47.2" + +[workspace] +members = ["."] diff --git a/contracts/potlock/wasm/src/libs.rs b/contracts/potlock/wasm/src/libs.rs new file mode 100644 index 00000000..e69de29b From 12572aed618f319a4f18b8c97460ee8eb4aaa37d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 18 Jun 2024 12:50:37 +0300 Subject: [PATCH 03/38] Fix building issues --- contracts/potlock/wasm/Cargo.lock | 190 ++++++++++++++++++++++++++++++ contracts/potlock/wasm/Cargo.toml | 2 +- contracts/potlock/wasm/src/lib.rs | 49 ++++++++ 3 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 contracts/potlock/wasm/Cargo.lock create mode 100644 contracts/potlock/wasm/src/lib.rs diff --git a/contracts/potlock/wasm/Cargo.lock b/contracts/potlock/wasm/Cargo.lock new file mode 100644 index 00000000..11b500a8 --- /dev/null +++ b/contracts/potlock/wasm/Cargo.lock @@ -0,0 +1,190 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "multiversx-sc" +version = "0.47.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34215a81b2c37619a234db4d6facbef5853e4022df3494746b29ada5ff35859f" +dependencies = [ + "bitflags", + "hex-literal", + "multiversx-sc-codec", + "multiversx-sc-derive", + "num-traits", +] + +[[package]] +name = "multiversx-sc-codec" +version = "0.18.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1da6db65170105c9495848c5e4ba388abb1f9201ff2ca362056c9328f36b7760" +dependencies = [ + "arrayvec", + "multiversx-sc-codec-derive", +] + +[[package]] +name = "multiversx-sc-codec-derive" +version = "0.18.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631c4d4b37fc94659c8d6cf559c21b68c68899095201de2e1b779fccad7b0b03" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multiversx-sc-derive" +version = "0.47.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41ce6becb22f45383e9ff60c88fa1f7946d92915c8409e9fbe3c3719325146f" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "radix_trie", + "syn", +] + +[[package]] +name = "multiversx-sc-modules" +version = "0.47.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b5a30a1f32d8097b1382a614ac252e1b8d16e9d7d7a7e00ef75e06697d6a55e" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "multiversx-sc-wasm-adapter" +version = "0.47.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73d1089c0fac8d27347f3f7d61285d304342cb7b57ac1cf1989c7efce6edc17a" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "potlock" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "multiversx-sc-modules", +] + +[[package]] +name = "potlock-wasm" +version = "0.0.0" +dependencies = [ + "multiversx-sc-wasm-adapter", + "potlock", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/contracts/potlock/wasm/Cargo.toml b/contracts/potlock/wasm/Cargo.toml index b2c60bf0..6b05dce2 100644 --- a/contracts/potlock/wasm/Cargo.toml +++ b/contracts/potlock/wasm/Cargo.toml @@ -7,7 +7,7 @@ [package] name = "potlock-wasm" version = "0.0.0" -edition = "2018" +edition = "2021" publish = false [lib] diff --git a/contracts/potlock/wasm/src/lib.rs b/contracts/potlock/wasm/src/lib.rs new file mode 100644 index 00000000..b963798c --- /dev/null +++ b/contracts/potlock/wasm/src/lib.rs @@ -0,0 +1,49 @@ +// Code generated by the multiversx-sc build system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Endpoints: 23 +// Async Callback (empty): 1 +// Total number of exported functions: 25 + +#![no_std] +#![allow(internal_features)] +#![feature(lang_items)] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + potlock + ( + init => init + upgrade => upgrade + acceptPot => accept_pot + removePot => remove_pot + acceptApplication => accept_application + rejectDonation => reject_donation + distributePotToProjects => distribute_pot_to_projects + addPot => add_pot + applyForPot => apply_for_pot + donateToPot => donate_to_pot + donateToProject => donate_to_project + changeFeeForPots => change_fee_for_pots + getFeeTokenIdentifier => fee_token_identifier + getFeeAmount => fee_amount + getPotlocks => potlocks + getProjects => projects + feePotPayments => fee_pot_proposer + feeAmountAcceptPots => fee_amount_accepted_pots + potDonations => pot_donations + projectDonations => project_donations + isAdmin => is_admin + addAdmin => add_admin + removeAdmin => remove_admin + getAdmins => admins + ) +} + +multiversx_sc_wasm_adapter::async_callback_empty! {} From cc7cea6777a5ea66d9b08ba0714a3a26a3c3b9fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 20 Jun 2024 13:23:35 +0300 Subject: [PATCH 04/38] Framework upgrade potlock --- Cargo.lock | 228 ++++-------------------------- contracts/potlock/Cargo.toml | 6 +- contracts/potlock/meta/Cargo.toml | 2 +- contracts/potlock/wasm/Cargo.lock | 52 ++++--- contracts/potlock/wasm/Cargo.toml | 5 +- contracts/potlock/wasm/src/lib.rs | 5 +- 6 files changed, 65 insertions(+), 233 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eedba258..ac3f3ed8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1083,9 +1083,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.3" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "humantime" @@ -1166,134 +1166,14 @@ dependencies = [ "tracing", ] -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "idna" -version = "1.0.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "icu_normalizer", - "icu_properties", - "smallvec", - "utf8_iter", + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -1466,12 +1346,6 @@ dependencies = [ "multiversx-sc-meta", ] -[[package]] -name = "litemap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" - [[package]] name = "lock_api" version = "0.4.12" @@ -1524,9 +1398,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -2223,12 +2097,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" -[[package]] -name = "platforms" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" - [[package]] name = "potlock" version = "0.0.0" @@ -2631,11 +2499,11 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -2825,12 +2693,6 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "strsim" version = "0.11.1" @@ -2839,9 +2701,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "0d0208408ba0c3df17ed26eb06992cb1a1268d41b2c0e12e65203fbe3972cee5" [[package]] name = "syn" @@ -2860,17 +2722,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "system-configuration" version = "0.5.1" @@ -3021,7 +2872,7 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ @@ -3043,9 +2894,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -3112,6 +2963,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -3147,27 +3004,15 @@ checksum = "151ac09978d3c2862c4e39b557f4eceee2cc72150bc4cb4f16abf061b6e381fb" [[package]] name = "url" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "utf8parse" version = "0.2.2" @@ -3467,7 +3312,7 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ @@ -3485,7 +3330,6 @@ dependencies = [ ] [[package]] - name = "zerocopy" version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3525,28 +3369,6 @@ dependencies = [ "syn", ] -[[package]] -name = "zerovec" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zip" version = "2.1.3" diff --git a/contracts/potlock/Cargo.toml b/contracts/potlock/Cargo.toml index 55eee8c1..8971591a 100644 --- a/contracts/potlock/Cargo.toml +++ b/contracts/potlock/Cargo.toml @@ -10,13 +10,13 @@ readme = "README.md" path = "src/potlock.rs" [dependencies.multiversx-sc] -version = "0.47.2" +version = "0.50.4" [dependencies.multiversx-sc-modules] -version = "=0.47.2" +version = "=0.50.4" [dev-dependencies] num-bigint = "0.4.2" [dev-dependencies.multiversx-sc-scenario] -version = "0.47.2" \ No newline at end of file +version = "0.50.4" diff --git a/contracts/potlock/meta/Cargo.toml b/contracts/potlock/meta/Cargo.toml index a13049a3..4f254f86 100644 --- a/contracts/potlock/meta/Cargo.toml +++ b/contracts/potlock/meta/Cargo.toml @@ -8,5 +8,5 @@ publish = false path = ".." [dependencies.multiversx-sc-meta] -version = "0.47.2" +version = "0.50.4" default-features = false diff --git a/contracts/potlock/wasm/Cargo.lock b/contracts/potlock/wasm/Cargo.lock index 11b500a8..ad707b4a 100644 --- a/contracts/potlock/wasm/Cargo.lock +++ b/contracts/potlock/wasm/Cargo.lock @@ -16,9 +16,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "endian-type" @@ -40,32 +40,34 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "multiversx-sc" -version = "0.47.8" +version = "0.50.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34215a81b2c37619a234db4d6facbef5853e4022df3494746b29ada5ff35859f" +checksum = "748a370a86e9a3c51e0609c4a6c0c9a5cceaeb742656ee3284c0620504a3d7a5" dependencies = [ "bitflags", "hex-literal", "multiversx-sc-codec", "multiversx-sc-derive", "num-traits", + "unwrap-infallible", ] [[package]] name = "multiversx-sc-codec" -version = "0.18.6" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1da6db65170105c9495848c5e4ba388abb1f9201ff2ca362056c9328f36b7760" +checksum = "35c94397b2fba14e40edfa55905b3f453ed57aa06c9b1960ad6a0ca6bfb7a236" dependencies = [ "arrayvec", "multiversx-sc-codec-derive", + "unwrap-infallible", ] [[package]] name = "multiversx-sc-codec-derive" -version = "0.18.6" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631c4d4b37fc94659c8d6cf559c21b68c68899095201de2e1b779fccad7b0b03" +checksum = "cf72a8042da0bc19da0b8f0d4f61b4c66ae853560fefc69cd8fea87bf1aa8c14" dependencies = [ "hex", "proc-macro2", @@ -75,9 +77,9 @@ dependencies = [ [[package]] name = "multiversx-sc-derive" -version = "0.47.8" +version = "0.50.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41ce6becb22f45383e9ff60c88fa1f7946d92915c8409e9fbe3c3719325146f" +checksum = "b6ca88f27a90b0a1b17d5b645cc859e0480f4321c551525873a59a954bfbcb53" dependencies = [ "hex", "proc-macro2", @@ -88,18 +90,18 @@ dependencies = [ [[package]] name = "multiversx-sc-modules" -version = "0.47.2" +version = "0.50.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b5a30a1f32d8097b1382a614ac252e1b8d16e9d7d7a7e00ef75e06697d6a55e" +checksum = "e97c7153d996ef412f5fc687a6799fc9173cb221adef283c217d160eebebe7d4" dependencies = [ "multiversx-sc", ] [[package]] name = "multiversx-sc-wasm-adapter" -version = "0.47.8" +version = "0.50.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73d1089c0fac8d27347f3f7d61285d304342cb7b57ac1cf1989c7efce6edc17a" +checksum = "37f083d6844a0919a39d87c2552b8e9ea30e365775265d31b419d24f6aa93eee" dependencies = [ "multiversx-sc", ] @@ -115,9 +117,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -140,18 +142,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -174,9 +176,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" -version = "2.0.48" +version = "2.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" dependencies = [ "proc-macro2", "quote", @@ -188,3 +190,9 @@ name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unwrap-infallible" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151ac09978d3c2862c4e39b557f4eceee2cc72150bc4cb4f16abf061b6e381fb" diff --git a/contracts/potlock/wasm/Cargo.toml b/contracts/potlock/wasm/Cargo.toml index 6b05dce2..21c55c94 100644 --- a/contracts/potlock/wasm/Cargo.toml +++ b/contracts/potlock/wasm/Cargo.toml @@ -21,11 +21,14 @@ debug = false panic = "abort" overflow-checks = false +[profile.dev] +panic = "abort" + [dependencies.potlock] path = ".." [dependencies.multiversx-sc-wasm-adapter] -version = "0.47.2" +version = "0.50.4" [workspace] members = ["."] diff --git a/contracts/potlock/wasm/src/lib.rs b/contracts/potlock/wasm/src/lib.rs index b963798c..7bd5b1a1 100644 --- a/contracts/potlock/wasm/src/lib.rs +++ b/contracts/potlock/wasm/src/lib.rs @@ -5,13 +5,12 @@ //////////////////////////////////////////////////// // Init: 1 -// Endpoints: 23 +// Upgrade: 1 +// Endpoints: 22 // Async Callback (empty): 1 // Total number of exported functions: 25 #![no_std] -#![allow(internal_features)] -#![feature(lang_items)] multiversx_sc_wasm_adapter::allocator!(); multiversx_sc_wasm_adapter::panic_handler!(); From 3e52a79f33c2476d7cd0d741e33b7e715d61db5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 20 Jun 2024 15:35:32 +0300 Subject: [PATCH 05/38] Add blackbox tests --- contracts/potlock/scenarios/potlock.scen.json | 39 -- contracts/potlock/src/potlock.rs | 1 + contracts/potlock/src/potlock_proxy.rs | 390 ++++++++++++++++++ .../potlock/tests/potlock_blackbox_tests.rs | 271 ++++++++++++ .../potlock/tests/potlock_scenario_go_test.rs | 10 - .../potlock/tests/potlock_scenario_rs_test.rs | 13 - 6 files changed, 662 insertions(+), 62 deletions(-) delete mode 100644 contracts/potlock/scenarios/potlock.scen.json create mode 100644 contracts/potlock/src/potlock_proxy.rs create mode 100644 contracts/potlock/tests/potlock_blackbox_tests.rs delete mode 100644 contracts/potlock/tests/potlock_scenario_go_test.rs delete mode 100644 contracts/potlock/tests/potlock_scenario_rs_test.rs diff --git a/contracts/potlock/scenarios/potlock.scen.json b/contracts/potlock/scenarios/potlock.scen.json deleted file mode 100644 index d2e265a5..00000000 --- a/contracts/potlock/scenarios/potlock.scen.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "empty", - "steps": [ - { - "step": "setState", - "accounts": { - "address:owner": { - "nonce": "1", - "balance": "0" - } - }, - "newAddresses": [ - { - "creatorAddress": "address:owner", - "creatorNonce": "1", - "newAddress": "sc:empty" - } - ] - }, - { - "step": "scDeploy", - "id": "deploy", - "tx": { - "from": "address:owner", - "contractCode": "mxsc:../output/potlock.mxsc.json", - "arguments": [], - "gasLimit": "5,000,000", - "gasPrice": "0" - }, - "expect": { - "out": [], - "status": "", - "logs": [], - "gas": "*", - "refund": "*" - } - } - ] -} diff --git a/contracts/potlock/src/potlock.rs b/contracts/potlock/src/potlock.rs index 2e119b5c..a0f5e383 100644 --- a/contracts/potlock/src/potlock.rs +++ b/contracts/potlock/src/potlock.rs @@ -4,6 +4,7 @@ multiversx_sc::imports!(); multiversx_sc::derive_imports!(); pub mod potlock_admin_interactions; pub mod potlock_interactions; +pub mod potlock_proxy; pub mod potlock_setup; pub mod potlock_storage; diff --git a/contracts/potlock/src/potlock_proxy.rs b/contracts/potlock/src/potlock_proxy.rs new file mode 100644 index 00000000..1c0e4336 --- /dev/null +++ b/contracts/potlock/src/potlock_proxy.rs @@ -0,0 +1,390 @@ +// 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 PotlockProxy; + +impl TxProxyTrait for PotlockProxy +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + type TxProxyMethods = PotlockProxyMethods; + + fn proxy_methods(self, tx: Tx) -> Self::TxProxyMethods { + PotlockProxyMethods { wrapped_tx: tx } + } +} + +pub struct PotlockProxyMethods +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + wrapped_tx: Tx, +} + +#[rustfmt::skip] +impl PotlockProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + Gas: TxGas, +{ + pub fn init< + Arg0: ProxyArg>, + >( + self, + admin: Arg0, + ) -> TxTypedDeploy { + self.wrapped_tx + .payment(NotPayable) + .raw_deploy() + .argument(&admin) + .original_result() + } +} + +#[rustfmt::skip] +impl PotlockProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + pub fn upgrade( + self, + ) -> TxTypedUpgrade { + self.wrapped_tx + .payment(NotPayable) + .raw_upgrade() + .original_result() + } +} + +#[rustfmt::skip] +impl PotlockProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + pub fn accept_pot< + Arg0: ProxyArg, + >( + self, + potlock_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("acceptPot") + .argument(&potlock_id) + .original_result() + } + + pub fn remove_pot< + Arg0: ProxyArg, + >( + self, + potlock_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("removePot") + .argument(&potlock_id) + .original_result() + } + + pub fn accept_application< + Arg0: ProxyArg, + >( + self, + project: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("acceptApplication") + .argument(&project) + .original_result() + } + + pub fn reject_donation< + Arg0: ProxyArg, + Arg1: ProxyArg>, + >( + self, + potlock_id: Arg0, + user: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("rejectDonation") + .argument(&potlock_id) + .argument(&user) + .original_result() + } + + pub fn distribute_pot_to_projects< + Arg0: ProxyArg, + Arg1: ProxyArg>>, + >( + self, + potlock_id: Arg0, + project_percentage: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("distributePotToProjects") + .argument(&potlock_id) + .argument(&project_percentage) + .original_result() + } + + pub fn add_pot< + Arg0: ProxyArg>, + Arg1: ProxyArg>, + >( + self, + name: Arg0, + description: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .raw_call("addPot") + .argument(&name) + .argument(&description) + .original_result() + } + + pub fn apply_for_pot< + Arg0: ProxyArg, + Arg1: ProxyArg>, + Arg2: ProxyArg>, + >( + self, + potlock_id: Arg0, + project_name: Arg1, + description: Arg2, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("applyForPot") + .argument(&potlock_id) + .argument(&project_name) + .argument(&description) + .original_result() + } + + pub fn donate_to_pot< + Arg0: ProxyArg, + >( + self, + potlock_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .raw_call("donateToPot") + .argument(&potlock_id) + .original_result() + } + + pub fn donate_to_project< + Arg0: ProxyArg, + >( + self, + project_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .raw_call("donateToProject") + .argument(&project_id) + .original_result() + } + + pub fn change_fee_for_pots< + Arg0: ProxyArg>, + Arg1: ProxyArg>, + >( + self, + token_identifier: Arg0, + fee: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("changeFeeForPots") + .argument(&token_identifier) + .argument(&fee) + .original_result() + } + + pub fn fee_token_identifier( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getFeeTokenIdentifier") + .original_result() + } + + pub fn fee_amount( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getFeeAmount") + .original_result() + } + + pub fn potlocks( + self, + ) -> TxTypedCall>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getPotlocks") + .original_result() + } + + pub fn projects( + self, + ) -> TxTypedCall>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getProjects") + .original_result() + } + + pub fn fee_pot_proposer< + Arg0: ProxyArg, + >( + self, + potlock_id: Arg0, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("feePotPayments") + .argument(&potlock_id) + .original_result() + } + + pub fn fee_amount_accepted_pots( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("feeAmountAcceptPots") + .original_result() + } + + pub fn pot_donations< + Arg0: ProxyArg, + >( + self, + project_id: Arg0, + ) -> TxTypedCall, EsdtTokenPayment>>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("potDonations") + .argument(&project_id) + .original_result() + } + + pub fn project_donations< + Arg0: ProxyArg, + >( + self, + project_id: Arg0, + ) -> TxTypedCall, EsdtTokenPayment>>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("projectDonations") + .argument(&project_id) + .original_result() + } + + pub fn is_admin< + Arg0: ProxyArg>, + >( + self, + address: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("isAdmin") + .argument(&address) + .original_result() + } + + pub fn add_admin< + Arg0: ProxyArg>, + >( + self, + address: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("addAdmin") + .argument(&address) + .original_result() + } + + pub fn remove_admin< + Arg0: ProxyArg>, + >( + self, + address: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("removeAdmin") + .argument(&address) + .original_result() + } + + pub fn admins( + self, + ) -> TxTypedCall>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getAdmins") + .original_result() + } +} + +#[type_abi] +#[derive(TopEncode, TopDecode)] +pub struct Pot +where + Api: ManagedTypeApi, +{ + pub potlock_id: usize, + pub token_identifier: TokenIdentifier, + pub fee: BigUint, + pub name: ManagedBuffer, + pub description: ManagedBuffer, +} + +#[type_abi] +#[derive(TopEncode, TopDecode)] +pub struct Project +where + Api: ManagedTypeApi, +{ + pub project_id: usize, + pub potlock_id: usize, + pub name: ManagedBuffer, + pub description: ManagedBuffer, + pub owner: ManagedAddress, +} diff --git a/contracts/potlock/tests/potlock_blackbox_tests.rs b/contracts/potlock/tests/potlock_blackbox_tests.rs new file mode 100644 index 00000000..854f1974 --- /dev/null +++ b/contracts/potlock/tests/potlock_blackbox_tests.rs @@ -0,0 +1,271 @@ +use std::fmt::Result; + +use multiversx_sc_scenario::{imports::*, ScenarioWorld}; +use potlock::{potlock_proxy, potlock_storage::PotlockId}; + +const POTLOCK_ADDRESS: TestSCAddress = TestSCAddress::new("potlock"); +const POTLOCK_CODE_PATH: MxscPath = MxscPath::new("output/potlock.mxsc.json"); +const OWNER_ADDRESS: TestAddress = TestAddress::new("owner"); +const ADMIN_ADDRESS: TestAddress = TestAddress::new("admin"); +const POT_PROPOSER_ADDRESS: TestAddress = TestAddress::new("pot_proposer"); +const PROJECT_PROPOSER_ADDRESS: TestAddress = TestAddress::new("project_proposer"); +const POT_DONOR_ADDRESS: TestAddress = TestAddress::new("pot_donor"); +const PROJECT_DONOR_ADDRESS: TestAddress = TestAddress::new("project_donor"); +const TOKEN_ID: TestTokenIdentifier = TestTokenIdentifier::new("POT-123456"); +const POT_FEE_CREATION: u64 = 1_000; // 1 week in seconds + +fn world() -> ScenarioWorld { + let mut blockchain = ScenarioWorld::new(); + + blockchain.register_contract(POTLOCK_CODE_PATH, potlock::ContractBuilder); + blockchain +} + +struct PotlockTestState { + world: ScenarioWorld, +} + +impl PotlockTestState { + fn new() -> Self { + let mut world = world(); + + world + .account(OWNER_ADDRESS) + .nonce(1) + .account(ADMIN_ADDRESS) + .nonce(1) + .account(POT_PROPOSER_ADDRESS) + .nonce(1) + .esdt_balance(TOKEN_ID, 1_000) + .account(PROJECT_PROPOSER_ADDRESS) + .nonce(1) + .account(POT_DONOR_ADDRESS) + .nonce(1); + + Self { world } + } + + fn deploy_potlock_contract(&mut self) -> &mut Self { + self.world + .tx() + .from(OWNER_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .init(ADMIN_ADDRESS) + .code(POTLOCK_CODE_PATH) + .new_address(POTLOCK_ADDRESS) + .run(); + self + } + + fn change_fee_for_pots(&mut self, fee_amount: u64) { + self.world + .tx() + .from(ADMIN_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .change_fee_for_pots(TokenIdentifier::from(TOKEN_ID), BigUint::from(fee_amount)) + .run(); + } + + fn add_pot(&mut self, name: &str, description: &str) { + self.world + .tx() + .from(POT_PROPOSER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .add_pot(name, description) + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(TOKEN_ID), + 0u64, + &multiversx_sc::proxy_imports::BigUint::from(POT_FEE_CREATION), + ) + .run(); + } + + fn accept_pot(&mut self, potlock_id: PotlockId) { + self.world + .tx() + .from(OWNER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .accept_pot(potlock_id) + .run(); + } + + fn remove_pot(&mut self, potlock_id: PotlockId) { + self.world + .tx() + .from(OWNER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .remove_pot(potlock_id) + .run(); + } + + fn apply_for_pot(&mut self, potlock_id: PotlockId, project_name: &str, description: &str) { + self.world + .tx() + .from(PROJECT_PROPOSER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .apply_for_pot(potlock_id, project_name, description) + .run(); + } + + ////////// Checks ////////// + fn check_esdt_balance(&mut self, address: TestAddress, balance: u64) { + self.world + .check_account(address) + .esdt_balance(TOKEN_ID, balance); + } + + fn check_sc_esdt_balance(&mut self, address: TestSCAddress, balance: u64) { + self.world + .check_account(address) + .esdt_balance(TOKEN_ID, balance); + } + + fn check_potlock_id(&mut self, potlock_id: PotlockId) { + let potlocks = self + .world + .query() + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .potlocks() + .returns(ReturnsResult) + .run(); + } +} + +#[test] +fn test_deploy_and_config() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); +} + +#[test] +fn test_add_pot() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + + state.check_esdt_balance(POT_PROPOSER_ADDRESS, 0); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); +} + +#[test] +fn test_accept_pot() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + let potlock_id = 1usize; + + state.accept_pot(potlock_id); + state.check_esdt_balance(POT_PROPOSER_ADDRESS, 0); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); +} + +#[test] +fn test_remove_pot() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + let potlock_id = 1usize; + + state.remove_pot(potlock_id); + + // Funds were returned to user + state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, 0); +} + +#[test] +fn test_apply_for_pot() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + let potlock_id = 1usize; + + state.apply_for_pot(potlock_id, "Project name", "Project description"); + + // Funds were returned to user + state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, 0); +} + +///////////// Negative tests ////////////// + +#[test] +fn test_fail_add_pot() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state + .world + .tx() + .from(POT_PROPOSER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .add_pot("name", "description") + .with_result(ExpectError(4, "incorrect number of ESDT transfers")) + .run(); + + state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, 0); +} + +#[test] +fn test_fail_accept_pot() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + + let potlock_id = 1usize; + state + .world + .tx() + .from(POT_PROPOSER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .accept_pot(potlock_id) + .with_result(ExpectError(4, "Endpoint can only be called by admins")) + .run(); + + state.check_esdt_balance(POT_PROPOSER_ADDRESS, 0); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); +} + +#[test] +fn test_fail_remove_pot() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + + let potlock_id = 1usize; + state + .world + .tx() + .from(POT_PROPOSER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .remove_pot(potlock_id) + .with_result(ExpectError(4, "Endpoint can only be called by admins")) + .run(); + + state.check_esdt_balance(POT_PROPOSER_ADDRESS, 0); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); +} diff --git a/contracts/potlock/tests/potlock_scenario_go_test.rs b/contracts/potlock/tests/potlock_scenario_go_test.rs deleted file mode 100644 index 5ecddde2..00000000 --- a/contracts/potlock/tests/potlock_scenario_go_test.rs +++ /dev/null @@ -1,10 +0,0 @@ -use multiversx_sc_scenario::*; - -fn world() -> ScenarioWorld { - ScenarioWorld::vm_go() -} - -#[test] -fn empty_go() { - world().run("scenarios/potlock.scen.json"); -} diff --git a/contracts/potlock/tests/potlock_scenario_rs_test.rs b/contracts/potlock/tests/potlock_scenario_rs_test.rs deleted file mode 100644 index b417ee4c..00000000 --- a/contracts/potlock/tests/potlock_scenario_rs_test.rs +++ /dev/null @@ -1,13 +0,0 @@ -use multiversx_sc_scenario::*; - -fn world() -> ScenarioWorld { - let mut blockchain = ScenarioWorld::new(); - - blockchain.register_contract("mxsc:output/potlock.mxsc.json", potlock::ContractBuilder); - blockchain -} - -#[test] -fn empty_rs() { - world().run("scenarios/potlock.scen.json"); -} From 849325b76d0e350aba67e0c77f62a9e363d2e860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 21 Jun 2024 14:33:28 +0300 Subject: [PATCH 06/38] Added more unit tests --- contracts/potlock/src/potlock.rs | 1 - .../potlock/src/potlock_admin_interactions.rs | 13 +- contracts/potlock/src/potlock_storage.rs | 13 +- .../potlock/tests/potlock_blackbox_tests.rs | 208 ++++++++++++++++-- .../potlock/{src => tests}/potlock_proxy.rs | 13 +- 5 files changed, 221 insertions(+), 27 deletions(-) rename contracts/potlock/{src => tests}/potlock_proxy.rs (97%) diff --git a/contracts/potlock/src/potlock.rs b/contracts/potlock/src/potlock.rs index a0f5e383..2e119b5c 100644 --- a/contracts/potlock/src/potlock.rs +++ b/contracts/potlock/src/potlock.rs @@ -4,7 +4,6 @@ multiversx_sc::imports!(); multiversx_sc::derive_imports!(); pub mod potlock_admin_interactions; pub mod potlock_interactions; -pub mod potlock_proxy; pub mod potlock_setup; pub mod potlock_storage; diff --git a/contracts/potlock/src/potlock_admin_interactions.rs b/contracts/potlock/src/potlock_admin_interactions.rs index 35bc8585..8ef3e51b 100644 --- a/contracts/potlock/src/potlock_admin_interactions.rs +++ b/contracts/potlock/src/potlock_admin_interactions.rs @@ -1,6 +1,6 @@ use crate::{ potlock_setup, - potlock_storage::{self, PotlockId, ProjectId}, + potlock_storage::{self, PotlockId, ProjectId, Status}, }; multiversx_sc::imports!(); @@ -22,6 +22,9 @@ pub trait PotlockAdminInteractions: self.fee_amount_accepted_pots() .update(|amount| *amount += fee_amount); + let mut accepted_potlock = self.potlocks().get(potlock_id); + accepted_potlock.status = Status::Active; + self.potlocks().set(potlock_id, &accepted_potlock); self.fee_pot_proposer(potlock_id).clear(); } @@ -43,9 +46,11 @@ pub trait PotlockAdminInteractions: #[only_admin] #[endpoint(acceptApplication)] - fn accept_application(&self, project: ProjectId) { - self.require_project_exists(project); - // TODO: Mark project's status as accepted + fn accept_application(&self, project_id: ProjectId) { + self.require_project_exists(project_id); + let mut accepted_projects = self.projects().get(project_id); + accepted_projects.status = Status::Active; + self.projects().set(project_id, &accepted_projects); } #[only_admin] diff --git a/contracts/potlock/src/potlock_storage.rs b/contracts/potlock/src/potlock_storage.rs index 683f1173..cb17f6df 100644 --- a/contracts/potlock/src/potlock_storage.rs +++ b/contracts/potlock/src/potlock_storage.rs @@ -4,11 +4,10 @@ multiversx_sc::derive_imports!(); pub type PotlockId = usize; pub type ProjectId = usize; -#[derive(TypeAbi, TopEncode, TopDecode, PartialEq, Eq)] -pub enum PotlockStatus { - None, - Active, +#[derive(TypeAbi, TopEncode, TopDecode, PartialEq, Eq, Debug, NestedEncode, NestedDecode)] +pub enum Status { Inactive, + Active, } #[derive(TypeAbi, NestedEncode, NestedDecode, PartialEq, Debug, TopEncode, TopDecode)] @@ -18,8 +17,7 @@ pub struct Pot { pub fee: BigUint, pub name: ManagedBuffer, pub description: ManagedBuffer, - // pub status: PotlockStatus, - // pub payment: EsdtTokenPayment, + pub status: Status, } impl Pot { @@ -34,6 +32,7 @@ impl Pot { fee: BigUint::default(), name, description, + status: Status::Inactive, } } } @@ -45,6 +44,7 @@ pub struct Project { pub name: ManagedBuffer, pub description: ManagedBuffer, pub owner: ManagedAddress, + pub status: Status, } impl Project { @@ -61,6 +61,7 @@ impl Project { name, description, owner, + status: Status::Inactive, } } } diff --git a/contracts/potlock/tests/potlock_blackbox_tests.rs b/contracts/potlock/tests/potlock_blackbox_tests.rs index 854f1974..a11e0163 100644 --- a/contracts/potlock/tests/potlock_blackbox_tests.rs +++ b/contracts/potlock/tests/potlock_blackbox_tests.rs @@ -1,7 +1,7 @@ -use std::fmt::Result; - use multiversx_sc_scenario::{imports::*, ScenarioWorld}; -use potlock::{potlock_proxy, potlock_storage::PotlockId}; +use potlock::potlock_storage::{PotlockId, ProjectId}; +use potlock_proxy::Status; +mod potlock_proxy; const POTLOCK_ADDRESS: TestSCAddress = TestSCAddress::new("potlock"); const POTLOCK_CODE_PATH: MxscPath = MxscPath::new("output/potlock.mxsc.json"); @@ -12,7 +12,9 @@ const PROJECT_PROPOSER_ADDRESS: TestAddress = TestAddress::new("project_proposer const POT_DONOR_ADDRESS: TestAddress = TestAddress::new("pot_donor"); const PROJECT_DONOR_ADDRESS: TestAddress = TestAddress::new("project_donor"); const TOKEN_ID: TestTokenIdentifier = TestTokenIdentifier::new("POT-123456"); -const POT_FEE_CREATION: u64 = 1_000; // 1 week in seconds +const POT_FEE_CREATION: u64 = 1_000; +const INITIAL_BALANCE: u64 = 2_000; +const DONATION_AMOUNT: u64 = 100; // 1 week in seconds fn world() -> ScenarioWorld { let mut blockchain = ScenarioWorld::new(); @@ -36,11 +38,15 @@ impl PotlockTestState { .nonce(1) .account(POT_PROPOSER_ADDRESS) .nonce(1) - .esdt_balance(TOKEN_ID, 1_000) + .esdt_balance(TOKEN_ID, INITIAL_BALANCE) .account(PROJECT_PROPOSER_ADDRESS) .nonce(1) .account(POT_DONOR_ADDRESS) - .nonce(1); + .nonce(1) + .esdt_balance(TOKEN_ID, INITIAL_BALANCE) + .account(PROJECT_DONOR_ADDRESS) + .nonce(1) + .esdt_balance(TOKEN_ID, INITIAL_BALANCE); Self { world } } @@ -92,6 +98,16 @@ impl PotlockTestState { .run(); } + fn accept_application(&mut self, project_id: ProjectId) { + self.world + .tx() + .from(OWNER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .accept_application(project_id) + .run(); + } + fn remove_pot(&mut self, potlock_id: PotlockId) { self.world .tx() @@ -112,6 +128,36 @@ impl PotlockTestState { .run(); } + fn donate_to_pot(&mut self, potlock_id: PotlockId) { + self.world + .tx() + .from(POT_DONOR_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .donate_to_pot(potlock_id) + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(TOKEN_ID), + 0u64, + &multiversx_sc::proxy_imports::BigUint::from(DONATION_AMOUNT), + ) + .run(); + } + + fn donate_to_project(&mut self, project_id: ProjectId) { + self.world + .tx() + .from(PROJECT_DONOR_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .donate_to_project(project_id) + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(TOKEN_ID), + 0u64, + &multiversx_sc::proxy_imports::BigUint::from(DONATION_AMOUNT), + ) + .run(); + } + ////////// Checks ////////// fn check_esdt_balance(&mut self, address: TestAddress, balance: u64) { self.world @@ -125,7 +171,7 @@ impl PotlockTestState { .esdt_balance(TOKEN_ID, balance); } - fn check_potlock_id(&mut self, potlock_id: PotlockId) { + fn check_potlock_id_is_last(&mut self, potlock_id: PotlockId) { let potlocks = self .world .query() @@ -134,6 +180,35 @@ impl PotlockTestState { .potlocks() .returns(ReturnsResult) .run(); + assert_eq!(potlocks.len(), potlock_id); + } + + fn check_project_id_is_last(&mut self, project_id: PotlockId) { + let projects = self + .world + .query() + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .projects() + .returns(ReturnsResult) + .run(); + assert_eq!(projects.len(), project_id); + } + + fn check_project_is_accepted(&mut self, project_id: PotlockId) { + let projects = self + .world + .query() + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .projects() + .returns(ReturnsResult) + .run(); + for project in projects.into_iter() { + if project.project_id == project_id { + assert_eq!(project.status, Status::Active); + } + } } } @@ -152,7 +227,10 @@ fn test_add_pot() { state.add_pot("Pot", "Pot Description"); - state.check_esdt_balance(POT_PROPOSER_ADDRESS, 0); + let potlock_id = 1usize; + state.check_potlock_id_is_last(potlock_id); + + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); } @@ -163,10 +241,12 @@ fn test_accept_pot() { state.change_fee_for_pots(POT_FEE_CREATION); state.add_pot("Pot", "Pot Description"); + let potlock_id = 1usize; + state.check_potlock_id_is_last(potlock_id); state.accept_pot(potlock_id); - state.check_esdt_balance(POT_PROPOSER_ADDRESS, 0); + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); } @@ -182,7 +262,7 @@ fn test_remove_pot() { state.remove_pot(potlock_id); // Funds were returned to user - state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE); state.check_sc_esdt_balance(POTLOCK_ADDRESS, 0); } @@ -194,12 +274,82 @@ fn test_apply_for_pot() { state.add_pot("Pot", "Pot Description"); let potlock_id = 1usize; + let project_id = 1usize; state.apply_for_pot(potlock_id, "Project name", "Project description"); + state.check_potlock_id_is_last(potlock_id); + state.check_project_id_is_last(project_id); + // Funds were returned to user + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); +} + +#[test] +fn test_donate_to_pot() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + let potlock_id = 1usize; + state.check_potlock_id_is_last(potlock_id); + + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); + state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE); + + state.donate_to_pot(potlock_id); + + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION + DONATION_AMOUNT); + state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); +} + +#[test] +fn test_donate_to_project() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + let potlock_id = 1usize; + state.check_potlock_id_is_last(potlock_id); + + state.apply_for_pot(potlock_id, "Project name", "Project description"); + let project_id = 1usize; + state.check_project_id_is_last(project_id); + + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); + state.check_esdt_balance(PROJECT_DONOR_ADDRESS, INITIAL_BALANCE); + + state.donate_to_project(project_id); + + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION + DONATION_AMOUNT); + state.check_esdt_balance(PROJECT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); +} + +#[test] +fn test_accept_application() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + let potlock_id: usize = 1usize; + state.check_potlock_id_is_last(potlock_id); + + let project_id = 1usize; + + state.apply_for_pot(potlock_id, "Project name", "Project description"); + + state.check_project_id_is_last(project_id); state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); - state.check_sc_esdt_balance(POTLOCK_ADDRESS, 0); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); + + state.accept_application(project_id); + state.check_project_is_accepted(project_id); } ///////////// Negative tests ////////////// @@ -220,7 +370,7 @@ fn test_fail_add_pot() { .with_result(ExpectError(4, "incorrect number of ESDT transfers")) .run(); - state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE); state.check_sc_esdt_balance(POTLOCK_ADDRESS, 0); } @@ -243,7 +393,7 @@ fn test_fail_accept_pot() { .with_result(ExpectError(4, "Endpoint can only be called by admins")) .run(); - state.check_esdt_balance(POT_PROPOSER_ADDRESS, 0); + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); } @@ -266,6 +416,36 @@ fn test_fail_remove_pot() { .with_result(ExpectError(4, "Endpoint can only be called by admins")) .run(); - state.check_esdt_balance(POT_PROPOSER_ADDRESS, 0); + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); +} + +#[test] +fn test_fail_accept_application() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + let potlock_id = 1usize; + let project_id = 1usize; + + state.apply_for_pot(potlock_id, "Project name", "Project description"); + + state.check_potlock_id_is_last(potlock_id); + state.check_project_id_is_last(project_id); + + // Funds were returned to user + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); + + state + .world + .tx() + .from(POT_PROPOSER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .accept_application(project_id) + .with_result(ExpectError(4, "Endpoint can only be called by admins")) + .run(); } diff --git a/contracts/potlock/src/potlock_proxy.rs b/contracts/potlock/tests/potlock_proxy.rs similarity index 97% rename from contracts/potlock/src/potlock_proxy.rs rename to contracts/potlock/tests/potlock_proxy.rs index 1c0e4336..df8acf2a 100644 --- a/contracts/potlock/src/potlock_proxy.rs +++ b/contracts/potlock/tests/potlock_proxy.rs @@ -115,12 +115,12 @@ where Arg0: ProxyArg, >( self, - project: Arg0, + project_id: Arg0, ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) .raw_call("acceptApplication") - .argument(&project) + .argument(&project_id) .original_result() } @@ -374,6 +374,14 @@ where pub fee: BigUint, pub name: ManagedBuffer, pub description: ManagedBuffer, + pub status: Status, +} + +#[type_abi] +#[derive(TopEncode, TopDecode, PartialEq, Eq, Debug, NestedEncode, NestedDecode)] +pub enum Status { + Inactive, + Active, } #[type_abi] @@ -387,4 +395,5 @@ where pub name: ManagedBuffer, pub description: ManagedBuffer, pub owner: ManagedAddress, + pub status: Status, } From 89ed86f781dbf72cacbc4933082e6aed586b3d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 21 Jun 2024 15:11:10 +0300 Subject: [PATCH 07/38] Add several more tests --- .../potlock/src/potlock_admin_interactions.rs | 3 +- .../potlock/tests/potlock_blackbox_tests.rs | 91 ++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/contracts/potlock/src/potlock_admin_interactions.rs b/contracts/potlock/src/potlock_admin_interactions.rs index 8ef3e51b..3664f768 100644 --- a/contracts/potlock/src/potlock_admin_interactions.rs +++ b/contracts/potlock/src/potlock_admin_interactions.rs @@ -7,6 +7,7 @@ multiversx_sc::imports!(); multiversx_sc::derive_imports!(); pub type ProjectPercentage = MultiValue2; +const MAX_PERCENTAGE: u64 = 10_000; // 100% #[multiversx_sc::module] pub trait PotlockAdminInteractions: @@ -81,7 +82,7 @@ pub trait PotlockAdminInteractions: let (project_id, percentage) = pp.into_tuple(); let mut output_payments = ManagedVec::new(); for (_, donation) in pot_donations.iter() { - let project_share_amount = donation.amount * percentage; + let project_share_amount = donation.amount * percentage / MAX_PERCENTAGE; let project_share = EsdtTokenPayment::new( donation.token_identifier, donation.token_nonce, diff --git a/contracts/potlock/tests/potlock_blackbox_tests.rs b/contracts/potlock/tests/potlock_blackbox_tests.rs index a11e0163..7f7082a5 100644 --- a/contracts/potlock/tests/potlock_blackbox_tests.rs +++ b/contracts/potlock/tests/potlock_blackbox_tests.rs @@ -14,7 +14,9 @@ const PROJECT_DONOR_ADDRESS: TestAddress = TestAddress::new("project_donor"); const TOKEN_ID: TestTokenIdentifier = TestTokenIdentifier::new("POT-123456"); const POT_FEE_CREATION: u64 = 1_000; const INITIAL_BALANCE: u64 = 2_000; -const DONATION_AMOUNT: u64 = 100; // 1 week in seconds +const DONATION_AMOUNT: u64 = 100; +const HALF_PERCENTAGE: u64 = 5_000; // 50% +const MAX_PERCENTAGE: u64 = 10_000; // 100% fn world() -> ScenarioWorld { let mut blockchain = ScenarioWorld::new(); @@ -158,6 +160,20 @@ impl PotlockTestState { .run(); } + fn distribute_pot_to_projects( + &mut self, + potlock_id: PotlockId, + percentages: MultiValueVec>, + ) { + self.world + .tx() + .from(ADMIN_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .distribute_pot_to_projects(potlock_id, percentages) + .run(); + } + ////////// Checks ////////// fn check_esdt_balance(&mut self, address: TestAddress, balance: u64) { self.world @@ -352,6 +368,45 @@ fn test_accept_application() { state.check_project_is_accepted(project_id); } +#[test] +fn test_distribute_pot_to_projects() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + // Add pot + state.add_pot("Pot", "Pot Description"); + let potlock_id: usize = 1usize; + state.check_potlock_id_is_last(potlock_id); + + // Add project + let project_id = 1usize; + state.apply_for_pot(potlock_id, "Project name", "Project description"); + + state.check_project_id_is_last(project_id); + state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); + + // Donate to Pot + state.donate_to_pot(potlock_id); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION + DONATION_AMOUNT); + state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); + + // Accept project + state.accept_application(project_id); + state.check_project_is_accepted(project_id); + + // Distribute Pot donations to projects + let mut percentages = MultiValueVec::new(); + percentages.push((project_id, HALF_PERCENTAGE).into()); + state.distribute_pot_to_projects(potlock_id, percentages); + + state.check_esdt_balance( + PROJECT_PROPOSER_ADDRESS, + HALF_PERCENTAGE * DONATION_AMOUNT / MAX_PERCENTAGE, + ); +} + ///////////// Negative tests ////////////// #[test] @@ -449,3 +504,37 @@ fn test_fail_accept_application() { .with_result(ExpectError(4, "Endpoint can only be called by admins")) .run(); } + +#[test] +fn test_fail_distribute_pot_to_projects() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + let potlock_id: usize = 1usize; + state.check_potlock_id_is_last(potlock_id); + + let project_id = 1usize; + + state.apply_for_pot(potlock_id, "Project name", "Project description"); + + state.check_project_id_is_last(project_id); + state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); + + state.accept_application(project_id); + state.check_project_is_accepted(project_id); + + let mut percentages = MultiValueVec::new(); + percentages.push((project_id, HALF_PERCENTAGE).into()); + state + .world + .tx() + .from(POT_PROPOSER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .distribute_pot_to_projects(potlock_id, percentages) + .with_result(ExpectError(4, "Endpoint can only be called by admins")) + .run(); +} From 6fc6db6218dccb4efc6a980cdfe078e2af95208c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 21 Jun 2024 15:23:25 +0300 Subject: [PATCH 08/38] Add interactor --- contracts/potlock/interact-rs/.gitignore | 2 + contracts/potlock/interact-rs/Cargo.toml | 27 + .../interact-rs/src/interactor_main.rs | 536 ++++++++++++++++++ contracts/potlock/interact-rs/src/proxy.rs | 399 +++++++++++++ contracts/potlock/sc-config.toml | 4 + 5 files changed, 968 insertions(+) create mode 100644 contracts/potlock/interact-rs/.gitignore create mode 100644 contracts/potlock/interact-rs/Cargo.toml create mode 100644 contracts/potlock/interact-rs/src/interactor_main.rs create mode 100644 contracts/potlock/interact-rs/src/proxy.rs create mode 100644 contracts/potlock/sc-config.toml diff --git a/contracts/potlock/interact-rs/.gitignore b/contracts/potlock/interact-rs/.gitignore new file mode 100644 index 00000000..5a64d09a --- /dev/null +++ b/contracts/potlock/interact-rs/.gitignore @@ -0,0 +1,2 @@ +# Pem files are used for interactions, but shouldn't be committed +*.pem diff --git a/contracts/potlock/interact-rs/Cargo.toml b/contracts/potlock/interact-rs/Cargo.toml new file mode 100644 index 00000000..09a37f7a --- /dev/null +++ b/contracts/potlock/interact-rs/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "rust-interact" +version = "0.0.0" +authors = ["you"] +edition = "2021" +publish = false + +[[bin]] +name = "rust-interact" +path = "src/interactor_main.rs" + +[dependencies.potlock] +path = ".." + +[dependencies.multiversx-sc-snippets] +version = "0.50.4" + +[dependencies.multiversx-sc] +version = "0.50.4" + +[dependencies] +clap = { version = "4.4.7", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } +toml = "0.8.6" + +# [workspace] + diff --git a/contracts/potlock/interact-rs/src/interactor_main.rs b/contracts/potlock/interact-rs/src/interactor_main.rs new file mode 100644 index 00000000..53370a46 --- /dev/null +++ b/contracts/potlock/interact-rs/src/interactor_main.rs @@ -0,0 +1,536 @@ +#![allow(non_snake_case)] + +mod proxy; + +use multiversx_sc_snippets::imports::*; +use multiversx_sc_snippets::sdk; +use serde::{Deserialize, Serialize}; +use std::{ + io::{Read, Write}, + path::Path, +}; + + +const GATEWAY: &str = sdk::blockchain::DEVNET_GATEWAY; +const STATE_FILE: &str = "state.toml"; + + +#[tokio::main] +async fn main() { + env_logger::init(); + + let mut args = std::env::args(); + let _ = args.next(); + let cmd = args.next().expect("at least one argument required"); + let mut interact = ContractInteract::new().await; + match cmd.as_str() { + "deploy" => interact.deploy().await, + "acceptPot" => interact.accept_pot().await, + "removePot" => interact.remove_pot().await, + "acceptApplication" => interact.accept_application().await, + "rejectDonation" => interact.reject_donation().await, + "distributePotToProjects" => interact.distribute_pot_to_projects().await, + "addPot" => interact.add_pot().await, + "applyForPot" => interact.apply_for_pot().await, + "donateToPot" => interact.donate_to_pot().await, + "donateToProject" => interact.donate_to_project().await, + "changeFeeForPots" => interact.change_fee_for_pots().await, + "getFeeTokenIdentifier" => interact.fee_token_identifier().await, + "getFeeAmount" => interact.fee_amount().await, + "getPotlocks" => interact.potlocks().await, + "getProjects" => interact.projects().await, + "feePotPayments" => interact.fee_pot_proposer().await, + "feeAmountAcceptPots" => interact.fee_amount_accepted_pots().await, + "potDonations" => interact.pot_donations().await, + "projectDonations" => interact.project_donations().await, + "isAdmin" => interact.is_admin().await, + "addAdmin" => interact.add_admin().await, + "removeAdmin" => interact.remove_admin().await, + "getAdmins" => interact.admins().await, + _ => panic!("unknown command: {}", &cmd), + } +} + + +#[derive(Debug, Default, Serialize, Deserialize)] +struct State { + contract_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 contract address + pub fn set_address(&mut self, address: Bech32Address) { + self.contract_address = Some(address); + } + + /// Returns the contract address + pub fn current_address(&self) -> &Bech32Address { + self.contract_address + .as_ref() + .expect("no known contract, deploy first") + } + } + + impl Drop for State { + // Serializes state to file + fn drop(&mut self) { + let mut file = std::fs::File::create(STATE_FILE).unwrap(); + file.write_all(toml::to_string(self).unwrap().as_bytes()) + .unwrap(); + } + } + +struct ContractInteract { + interactor: Interactor, + wallet_address: Address, + contract_code: BytesValue, + state: State +} + +impl ContractInteract { + async fn new() -> Self { + let mut interactor = Interactor::new(GATEWAY).await; + let wallet_address = interactor.register_wallet(test_wallets::alice()); + + let contract_code = BytesValue::interpret_from( + "mxsc:../output/potlock.mxsc.json", + &InterpreterContext::default(), + ); + + ContractInteract { + interactor, + wallet_address, + contract_code, + state: State::load_state() + } + } + + async fn deploy(&mut self) { + let admin = bech32::decode(""); + + let new_address = self + .interactor + .tx() + .from(&self.wallet_address) + .typed(proxy::PotlockProxy) + .init(admin) + .code(&self.contract_code) + .returns(ReturnsNewAddress) + .prepare_async() + .run() + .await; + let new_address_bech32 = bech32::encode(&new_address); + self.state + .set_address(Bech32Address::from_bech32_string(new_address_bech32.clone())); + + println!("new address: {new_address_bech32}"); + } + + async fn accept_pot(&mut self) { + let potlock_id = 0u32; + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .accept_pot(potlock_id) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn remove_pot(&mut self) { + let potlock_id = 0u32; + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .remove_pot(potlock_id) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn accept_application(&mut self) { + let project_id = 0u32; + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .accept_application(project_id) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn reject_donation(&mut self) { + let potlock_id = 0u32; + let user = bech32::decode(""); + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .reject_donation(potlock_id, user) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn distribute_pot_to_projects(&mut self) { + let potlock_id = 0u32; + let project_percentage = MultiValueVec::from(vec![MultiValue2::from((0u32, 0u64))]); + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .distribute_pot_to_projects(potlock_id, project_percentage) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn add_pot(&mut self) { + let token_id = String::new(); + let token_nonce = 0u64; + let token_amount = BigUint::::from(0u128); + + let name = ManagedBuffer::new_from_bytes(&b""[..]); + let description = ManagedBuffer::new_from_bytes(&b""[..]); + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .add_pot(name, description) + .payment((TokenIdentifier::from(token_id.as_str()), token_nonce, token_amount)) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn apply_for_pot(&mut self) { + let potlock_id = 0u32; + let project_name = ManagedBuffer::new_from_bytes(&b""[..]); + let description = ManagedBuffer::new_from_bytes(&b""[..]); + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .apply_for_pot(potlock_id, project_name, description) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn donate_to_pot(&mut self) { + let token_id = String::new(); + let token_nonce = 0u64; + let token_amount = BigUint::::from(0u128); + + let potlock_id = 0u32; + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .donate_to_pot(potlock_id) + .payment((TokenIdentifier::from(token_id.as_str()), token_nonce, token_amount)) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn donate_to_project(&mut self) { + let token_id = String::new(); + let token_nonce = 0u64; + let token_amount = BigUint::::from(0u128); + + let project_id = 0u32; + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .donate_to_project(project_id) + .payment((TokenIdentifier::from(token_id.as_str()), token_nonce, token_amount)) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn change_fee_for_pots(&mut self) { + let token_identifier = TokenIdentifier::from_esdt_bytes(&b""[..]); + let fee = BigUint::::from(0u128); + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .change_fee_for_pots(token_identifier, fee) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn fee_token_identifier(&mut self) { + let result_value = self + .interactor + .query() + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .fee_token_identifier() + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {result_value:?}"); + } + + async fn fee_amount(&mut self) { + let result_value = self + .interactor + .query() + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .fee_amount() + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {result_value:?}"); + } + + async fn potlocks(&mut self) { + let result_value = self + .interactor + .query() + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .potlocks() + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {result_value:?}"); + } + + async fn projects(&mut self) { + let result_value = self + .interactor + .query() + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .projects() + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {result_value:?}"); + } + + async fn fee_pot_proposer(&mut self) { + let potlock_id = 0u32; + + let result_value = self + .interactor + .query() + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .fee_pot_proposer(potlock_id) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {result_value:?}"); + } + + async fn fee_amount_accepted_pots(&mut self) { + let result_value = self + .interactor + .query() + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .fee_amount_accepted_pots() + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {result_value:?}"); + } + + async fn pot_donations(&mut self) { + let project_id = 0u32; + + let result_value = self + .interactor + .query() + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .pot_donations(project_id) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {result_value:?}"); + } + + async fn project_donations(&mut self) { + let project_id = 0u32; + + let result_value = self + .interactor + .query() + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .project_donations(project_id) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {result_value:?}"); + } + + async fn is_admin(&mut self) { + let address = bech32::decode(""); + + let result_value = self + .interactor + .query() + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .is_admin(address) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {result_value:?}"); + } + + async fn add_admin(&mut self) { + let address = bech32::decode(""); + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .add_admin(address) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn remove_admin(&mut self) { + let address = bech32::decode(""); + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .remove_admin(address) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn admins(&mut self) { + let result_value = self + .interactor + .query() + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .admins() + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {result_value:?}"); + } + +} diff --git a/contracts/potlock/interact-rs/src/proxy.rs b/contracts/potlock/interact-rs/src/proxy.rs new file mode 100644 index 00000000..de6fca5f --- /dev/null +++ b/contracts/potlock/interact-rs/src/proxy.rs @@ -0,0 +1,399 @@ +// 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 PotlockProxy; + +impl TxProxyTrait for PotlockProxy +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + type TxProxyMethods = PotlockProxyMethods; + + fn proxy_methods(self, tx: Tx) -> Self::TxProxyMethods { + PotlockProxyMethods { wrapped_tx: tx } + } +} + +pub struct PotlockProxyMethods +where + Env: TxEnv, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + wrapped_tx: Tx, +} + +#[rustfmt::skip] +impl PotlockProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + Gas: TxGas, +{ + pub fn init< + Arg0: ProxyArg>, + >( + self, + admin: Arg0, + ) -> TxTypedDeploy { + self.wrapped_tx + .payment(NotPayable) + .raw_deploy() + .argument(&admin) + .original_result() + } +} + +#[rustfmt::skip] +impl PotlockProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + pub fn upgrade( + self, + ) -> TxTypedUpgrade { + self.wrapped_tx + .payment(NotPayable) + .raw_upgrade() + .original_result() + } +} + +#[rustfmt::skip] +impl PotlockProxyMethods +where + Env: TxEnv, + Env::Api: VMApi, + From: TxFrom, + To: TxTo, + Gas: TxGas, +{ + pub fn accept_pot< + Arg0: ProxyArg, + >( + self, + potlock_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("acceptPot") + .argument(&potlock_id) + .original_result() + } + + pub fn remove_pot< + Arg0: ProxyArg, + >( + self, + potlock_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("removePot") + .argument(&potlock_id) + .original_result() + } + + pub fn accept_application< + Arg0: ProxyArg, + >( + self, + project_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("acceptApplication") + .argument(&project_id) + .original_result() + } + + pub fn reject_donation< + Arg0: ProxyArg, + Arg1: ProxyArg>, + >( + self, + potlock_id: Arg0, + user: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("rejectDonation") + .argument(&potlock_id) + .argument(&user) + .original_result() + } + + pub fn distribute_pot_to_projects< + Arg0: ProxyArg, + Arg1: ProxyArg>>, + >( + self, + potlock_id: Arg0, + project_percentage: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("distributePotToProjects") + .argument(&potlock_id) + .argument(&project_percentage) + .original_result() + } + + pub fn add_pot< + Arg0: ProxyArg>, + Arg1: ProxyArg>, + >( + self, + name: Arg0, + description: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .raw_call("addPot") + .argument(&name) + .argument(&description) + .original_result() + } + + pub fn apply_for_pot< + Arg0: ProxyArg, + Arg1: ProxyArg>, + Arg2: ProxyArg>, + >( + self, + potlock_id: Arg0, + project_name: Arg1, + description: Arg2, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("applyForPot") + .argument(&potlock_id) + .argument(&project_name) + .argument(&description) + .original_result() + } + + pub fn donate_to_pot< + Arg0: ProxyArg, + >( + self, + potlock_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .raw_call("donateToPot") + .argument(&potlock_id) + .original_result() + } + + pub fn donate_to_project< + Arg0: ProxyArg, + >( + self, + project_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .raw_call("donateToProject") + .argument(&project_id) + .original_result() + } + + pub fn change_fee_for_pots< + Arg0: ProxyArg>, + Arg1: ProxyArg>, + >( + self, + token_identifier: Arg0, + fee: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("changeFeeForPots") + .argument(&token_identifier) + .argument(&fee) + .original_result() + } + + pub fn fee_token_identifier( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getFeeTokenIdentifier") + .original_result() + } + + pub fn fee_amount( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getFeeAmount") + .original_result() + } + + pub fn potlocks( + self, + ) -> TxTypedCall>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getPotlocks") + .original_result() + } + + pub fn projects( + self, + ) -> TxTypedCall>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getProjects") + .original_result() + } + + pub fn fee_pot_proposer< + Arg0: ProxyArg, + >( + self, + potlock_id: Arg0, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("feePotPayments") + .argument(&potlock_id) + .original_result() + } + + pub fn fee_amount_accepted_pots( + self, + ) -> TxTypedCall> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("feeAmountAcceptPots") + .original_result() + } + + pub fn pot_donations< + Arg0: ProxyArg, + >( + self, + project_id: Arg0, + ) -> TxTypedCall, EsdtTokenPayment>>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("potDonations") + .argument(&project_id) + .original_result() + } + + pub fn project_donations< + Arg0: ProxyArg, + >( + self, + project_id: Arg0, + ) -> TxTypedCall, EsdtTokenPayment>>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("projectDonations") + .argument(&project_id) + .original_result() + } + + pub fn is_admin< + Arg0: ProxyArg>, + >( + self, + address: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("isAdmin") + .argument(&address) + .original_result() + } + + pub fn add_admin< + Arg0: ProxyArg>, + >( + self, + address: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("addAdmin") + .argument(&address) + .original_result() + } + + pub fn remove_admin< + Arg0: ProxyArg>, + >( + self, + address: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("removeAdmin") + .argument(&address) + .original_result() + } + + pub fn admins( + self, + ) -> TxTypedCall>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("getAdmins") + .original_result() + } +} + +#[type_abi] +#[derive(TopEncode, TopDecode)] +pub struct Pot +where + Api: ManagedTypeApi, +{ + pub potlock_id: usize, + pub token_identifier: TokenIdentifier, + pub fee: BigUint, + pub name: ManagedBuffer, + pub description: ManagedBuffer, + pub status: Status, +} + +#[type_abi] +#[derive(TopEncode, TopDecode)] +pub enum Status { + Inactive, + Active, +} + +#[type_abi] +#[derive(TopEncode, TopDecode)] +pub struct Project +where + Api: ManagedTypeApi, +{ + pub project_id: usize, + pub potlock_id: usize, + pub name: ManagedBuffer, + pub description: ManagedBuffer, + pub owner: ManagedAddress, + pub status: Status, +} diff --git a/contracts/potlock/sc-config.toml b/contracts/potlock/sc-config.toml new file mode 100644 index 00000000..15d4cf88 --- /dev/null +++ b/contracts/potlock/sc-config.toml @@ -0,0 +1,4 @@ + +[[proxy]] +path = "interact-rs/src/proxy.rs" + From 4103456f2d01d8b52110f9c535af7b0fe2f444e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Sat, 22 Jun 2024 19:49:34 +0300 Subject: [PATCH 09/38] Adjust interactors functions --- Cargo.lock | 12 ++ Cargo.toml | 1 + contracts/potlock/interact-rs/Cargo.toml | 2 +- contracts/potlock/interact-rs/config.toml | 6 + .../src/potlock_interactor_config.rs | 28 +++ ...tor_main.rs => potlock_interactor_main.rs} | 161 ++++++++---------- contracts/potlock/interact-rs/src/proxy.rs | 2 +- contracts/potlock/interact-rs/state.toml | 1 + contracts/potlock/src/potlock_interactions.rs | 4 + contracts/potlock/src/potlock_setup.rs | 12 +- 10 files changed, 132 insertions(+), 97 deletions(-) create mode 100644 contracts/potlock/interact-rs/config.toml create mode 100644 contracts/potlock/interact-rs/src/potlock_interactor_config.rs rename contracts/potlock/interact-rs/src/{interactor_main.rs => potlock_interactor_main.rs} (78%) create mode 100644 contracts/potlock/interact-rs/state.toml diff --git a/Cargo.lock b/Cargo.lock index ac3f3ed8..c62b16df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2399,6 +2399,18 @@ dependencies = [ "regex", ] +[[package]] +name = "rust-interact" +version = "0.0.0" +dependencies = [ + "clap", + "multiversx-sc", + "multiversx-sc-snippets", + "potlock", + "serde", + "toml", +] + [[package]] name = "rustc-demangle" version = "0.1.24" diff --git a/Cargo.toml b/Cargo.toml index 513fff4e..7ad745ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,6 +71,7 @@ members = [ "contracts/ping-pong-egld/meta", "contracts/potlock", "contracts/potlock/meta", + "contracts/potlock/interact-rs", "contracts/proxy-deployer", "contracts/proxy-deployer/meta", "contracts/proxy-pause", diff --git a/contracts/potlock/interact-rs/Cargo.toml b/contracts/potlock/interact-rs/Cargo.toml index 09a37f7a..65a2e31f 100644 --- a/contracts/potlock/interact-rs/Cargo.toml +++ b/contracts/potlock/interact-rs/Cargo.toml @@ -7,7 +7,7 @@ publish = false [[bin]] name = "rust-interact" -path = "src/interactor_main.rs" +path = "src/potlock_interactor_main.rs" [dependencies.potlock] path = ".." diff --git a/contracts/potlock/interact-rs/config.toml b/contracts/potlock/interact-rs/config.toml new file mode 100644 index 00000000..dbbe25d0 --- /dev/null +++ b/contracts/potlock/interact-rs/config.toml @@ -0,0 +1,6 @@ +gateway = 'https://devnet-gateway.multiversx.com' +admin = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" +pot_proposer = "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" +project_proposer = "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8" +pot_donor = "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7" +project_donor = "erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4" \ No newline at end of file diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_config.rs b/contracts/potlock/interact-rs/src/potlock_interactor_config.rs new file mode 100644 index 00000000..7f70137c --- /dev/null +++ b/contracts/potlock/interact-rs/src/potlock_interactor_config.rs @@ -0,0 +1,28 @@ +use multiversx_sc::types::TokenIdentifier; +use multiversx_sc_snippets::imports::{Bech32Address, StaticApi}; +use serde::Deserialize; +use std::io::Read; + +/// Config file +const CONFIG_FILE: &str = "config.toml"; + +/// Multisig Interact configuration +#[derive(Debug, Deserialize)] +pub struct Config { + pub gateway: String, + pub admin: Bech32Address, + pub pot_proposer: Bech32Address, + pub project_proposer: Bech32Address, + pub pot_donor: Bech32Address, + pub project_donor: Bech32Address, +} + +impl Config { + // Deserializes config from file + pub fn load_config() -> Self { + let mut file = std::fs::File::open(CONFIG_FILE).unwrap(); + let mut content = String::new(); + file.read_to_string(&mut content).unwrap(); + toml::from_str(&content).unwrap() + } +} diff --git a/contracts/potlock/interact-rs/src/interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs similarity index 78% rename from contracts/potlock/interact-rs/src/interactor_main.rs rename to contracts/potlock/interact-rs/src/potlock_interactor_main.rs index 53370a46..b53c4e49 100644 --- a/contracts/potlock/interact-rs/src/interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -1,5 +1,6 @@ #![allow(non_snake_case)] +mod potlock_interactor_config; mod proxy; use multiversx_sc_snippets::imports::*; @@ -10,10 +11,12 @@ use std::{ path::Path, }; - const GATEWAY: &str = sdk::blockchain::DEVNET_GATEWAY; const STATE_FILE: &str = "state.toml"; +const TOKEN_ID: &str = "WEGLD-a28c59"; +const FEE_AMOUNT: u64 = 50000000000000000; // 0.5 +use potlock_interactor_config::Config; #[tokio::main] async fn main() { @@ -37,8 +40,6 @@ async fn main() { "changeFeeForPots" => interact.change_fee_for_pots().await, "getFeeTokenIdentifier" => interact.fee_token_identifier().await, "getFeeAmount" => interact.fee_amount().await, - "getPotlocks" => interact.potlocks().await, - "getProjects" => interact.projects().await, "feePotPayments" => interact.fee_pot_proposer().await, "feeAmountAcceptPots" => interact.fee_amount_accepted_pots().await, "potDonations" => interact.pot_donations().await, @@ -51,59 +52,59 @@ async fn main() { } } - #[derive(Debug, Default, Serialize, Deserialize)] struct State { - contract_address: Option + contract_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 contract address - pub fn set_address(&mut self, address: Bech32Address) { - self.contract_address = Some(address); - } - - /// Returns the contract address - pub fn current_address(&self) -> &Bech32Address { - self.contract_address - .as_ref() - .expect("no known contract, deploy first") + // 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() } } - - 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(); - } + + /// Sets the contract address + pub fn set_address(&mut self, address: Bech32Address) { + self.contract_address = Some(address); } + /// Returns the contract address + pub fn current_address(&self) -> &Bech32Address { + self.contract_address + .as_ref() + .expect("no known contract, deploy first") + } +} + +impl Drop for State { + // Serializes state to file + fn drop(&mut self) { + let mut file = std::fs::File::create(STATE_FILE).unwrap(); + file.write_all(toml::to_string(self).unwrap().as_bytes()) + .unwrap(); + } +} + struct ContractInteract { interactor: Interactor, wallet_address: Address, contract_code: BytesValue, - state: State + state: State, + config: Config, } impl ContractInteract { async fn new() -> Self { let mut interactor = Interactor::new(GATEWAY).await; let wallet_address = interactor.register_wallet(test_wallets::alice()); - + let contract_code = BytesValue::interpret_from( "mxsc:../output/potlock.mxsc.json", &InterpreterContext::default(), @@ -113,12 +114,13 @@ impl ContractInteract { interactor, wallet_address, contract_code, - state: State::load_state() + state: State::load_state(), + config: Config::load_config(), } } async fn deploy(&mut self) { - let admin = bech32::decode(""); + let admin = &self.config.admin; let new_address = self .interactor @@ -127,19 +129,21 @@ impl ContractInteract { .typed(proxy::PotlockProxy) .init(admin) .code(&self.contract_code) + .gas(50_000_000) .returns(ReturnsNewAddress) .prepare_async() .run() .await; let new_address_bech32 = bech32::encode(&new_address); - self.state - .set_address(Bech32Address::from_bech32_string(new_address_bech32.clone())); + self.state.set_address(Bech32Address::from_bech32_string( + new_address_bech32.clone(), + )); println!("new address: {new_address_bech32}"); } async fn accept_pot(&mut self) { - let potlock_id = 0u32; + let potlock_id = 1u32; let response = self .interactor @@ -175,7 +179,7 @@ impl ContractInteract { } async fn accept_application(&mut self) { - let project_id = 0u32; + let project_id = 1u32; let response = self .interactor @@ -212,8 +216,8 @@ impl ContractInteract { } async fn distribute_pot_to_projects(&mut self) { - let potlock_id = 0u32; - let project_percentage = MultiValueVec::from(vec![MultiValue2::from((0u32, 0u64))]); + let potlock_id = 1u32; + let project_percentage = MultiValueVec::from(vec![MultiValue2::from((1u32, 10_000u64))]); let response = self .interactor @@ -231,12 +235,12 @@ impl ContractInteract { } async fn add_pot(&mut self) { - let token_id = String::new(); + let token_id = TokenIdentifier::from_esdt_bytes(TOKEN_ID); let token_nonce = 0u64; - let token_amount = BigUint::::from(0u128); + let token_amount = BigUint::::from(FEE_AMOUNT); - let name = ManagedBuffer::new_from_bytes(&b""[..]); - let description = ManagedBuffer::new_from_bytes(&b""[..]); + let description = ManagedBuffer::new_from_bytes(b"Pot used for testing"); + let name = ManagedBuffer::new_from_bytes(b"My Pot"); let response = self .interactor @@ -245,7 +249,7 @@ impl ContractInteract { .to(self.state.current_address()) .typed(proxy::PotlockProxy) .add_pot(name, description) - .payment((TokenIdentifier::from(token_id.as_str()), token_nonce, token_amount)) + .payment((TokenIdentifier::from(token_id), token_nonce, token_amount)) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -255,9 +259,9 @@ impl ContractInteract { } async fn apply_for_pot(&mut self) { - let potlock_id = 0u32; - let project_name = ManagedBuffer::new_from_bytes(&b""[..]); - let description = ManagedBuffer::new_from_bytes(&b""[..]); + let potlock_id = 1u32; + let project_name = ManagedBuffer::new_from_bytes(b"New Testing Project"); + let description = ManagedBuffer::new_from_bytes(b"Project used for testing"); let response = self .interactor @@ -275,11 +279,11 @@ impl ContractInteract { } async fn donate_to_pot(&mut self) { - let token_id = String::new(); + let token_id = TokenIdentifier::from_esdt_bytes(TOKEN_ID); let token_nonce = 0u64; - let token_amount = BigUint::::from(0u128); + let token_amount = BigUint::::from(3 * FEE_AMOUNT); - let potlock_id = 0u32; + let potlock_id = 1u32; let response = self .interactor @@ -288,7 +292,7 @@ impl ContractInteract { .to(self.state.current_address()) .typed(proxy::PotlockProxy) .donate_to_pot(potlock_id) - .payment((TokenIdentifier::from(token_id.as_str()), token_nonce, token_amount)) + .payment((TokenIdentifier::from(token_id), token_nonce, token_amount)) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -298,11 +302,11 @@ impl ContractInteract { } async fn donate_to_project(&mut self) { - let token_id = String::new(); + let token_id = TokenIdentifier::from_esdt_bytes(TOKEN_ID); let token_nonce = 0u64; - let token_amount = BigUint::::from(0u128); + let token_amount = BigUint::::from(3 * FEE_AMOUNT); - let project_id = 0u32; + let project_id = 1u32; let response = self .interactor @@ -311,7 +315,7 @@ impl ContractInteract { .to(self.state.current_address()) .typed(proxy::PotlockProxy) .donate_to_project(project_id) - .payment((TokenIdentifier::from(token_id.as_str()), token_nonce, token_amount)) + .payment((TokenIdentifier::from(token_id), token_nonce, token_amount)) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -321,8 +325,8 @@ impl ContractInteract { } async fn change_fee_for_pots(&mut self) { - let token_identifier = TokenIdentifier::from_esdt_bytes(&b""[..]); - let fee = BigUint::::from(0u128); + let token_identifier = TokenIdentifier::from_esdt_bytes(TOKEN_ID); + let fee = BigUint::::from(FEE_AMOUNT); let response = self .interactor @@ -369,36 +373,6 @@ impl ContractInteract { println!("Result: {result_value:?}"); } - async fn potlocks(&mut self) { - let result_value = self - .interactor - .query() - .to(self.state.current_address()) - .typed(proxy::PotlockProxy) - .potlocks() - .returns(ReturnsResultUnmanaged) - .prepare_async() - .run() - .await; - - println!("Result: {result_value:?}"); - } - - async fn projects(&mut self) { - let result_value = self - .interactor - .query() - .to(self.state.current_address()) - .typed(proxy::PotlockProxy) - .projects() - .returns(ReturnsResultUnmanaged) - .prepare_async() - .run() - .await; - - println!("Result: {result_value:?}"); - } - async fn fee_pot_proposer(&mut self) { let potlock_id = 0u32; @@ -532,5 +506,4 @@ impl ContractInteract { println!("Result: {result_value:?}"); } - } diff --git a/contracts/potlock/interact-rs/src/proxy.rs b/contracts/potlock/interact-rs/src/proxy.rs index de6fca5f..df8acf2a 100644 --- a/contracts/potlock/interact-rs/src/proxy.rs +++ b/contracts/potlock/interact-rs/src/proxy.rs @@ -378,7 +378,7 @@ where } #[type_abi] -#[derive(TopEncode, TopDecode)] +#[derive(TopEncode, TopDecode, PartialEq, Eq, Debug, NestedEncode, NestedDecode)] pub enum Status { Inactive, Active, diff --git a/contracts/potlock/interact-rs/state.toml b/contracts/potlock/interact-rs/state.toml new file mode 100644 index 00000000..6a70e4cf --- /dev/null +++ b/contracts/potlock/interact-rs/state.toml @@ -0,0 +1 @@ +contract_address = "erd1qqqqqqqqqqqqqpgq5upecnmqrjd673jhuy36z7ehdgsgkkuvd8ssdxdcaq" diff --git a/contracts/potlock/src/potlock_interactions.rs b/contracts/potlock/src/potlock_interactions.rs index 4a513eff..5182bdb7 100644 --- a/contracts/potlock/src/potlock_interactions.rs +++ b/contracts/potlock/src/potlock_interactions.rs @@ -50,6 +50,8 @@ pub trait PotlockInteractions: fn donate_to_pot(&self, potlock_id: PotlockId) { let payment = self.call_value().single_esdt(); let caller = self.blockchain().get_caller(); + self.require_potlock_exists(potlock_id); + self.require_potlock_is_active(potlock_id); self.pot_donations(potlock_id).insert(caller, payment); } @@ -59,6 +61,8 @@ pub trait PotlockInteractions: self.require_project_exists(project_id); let payment = self.call_value().single_esdt(); let caller = self.blockchain().get_caller(); + self.require_project_exists(project_id); + self.require_project_is_active(project_id); self.project_donations(project_id).insert(caller, payment); } } diff --git a/contracts/potlock/src/potlock_setup.rs b/contracts/potlock/src/potlock_setup.rs index 20f4ca01..1870ca09 100644 --- a/contracts/potlock/src/potlock_setup.rs +++ b/contracts/potlock/src/potlock_setup.rs @@ -1,4 +1,4 @@ -use crate::potlock_storage::{self, PotlockId, ProjectId}; +use crate::potlock_storage::{self, PotlockId, ProjectId, Status}; multiversx_sc::imports!(); @@ -29,6 +29,11 @@ pub trait PotlockSetup: ) } + fn require_potlock_is_active(&self, potlock_id: PotlockId) { + let potlock = self.potlocks().get(potlock_id); + require!(potlock.status == Status::Active, "Pot is not active!",) + } + fn is_valid_project_id(&self, project_id: ProjectId) -> bool { project_id >= 1 && project_id <= self.projects().len() } @@ -39,4 +44,9 @@ pub trait PotlockSetup: "Project doesn't exist!", ) } + + fn require_project_is_active(&self, project_id: ProjectId) { + let project = self.potlocks().get(project_id); + require!(project.status == Status::Active, "Project is not active!",) + } } From 214e1176a5e45b9449848fba6483df2e444210d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 26 Jun 2024 09:26:27 +0300 Subject: [PATCH 10/38] Add diferent types of actors from different wallets --- .../src/potlock_interactor_main.rs | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index b53c4e49..5f207805 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -143,12 +143,13 @@ impl ContractInteract { } async fn accept_pot(&mut self) { + let admin = &self.config.admin; let potlock_id = 1u32; let response = self .interactor .tx() - .from(&self.wallet_address) + .from(admin) .to(self.state.current_address()) .typed(proxy::PotlockProxy) .accept_pot(potlock_id) @@ -161,12 +162,13 @@ impl ContractInteract { } async fn remove_pot(&mut self) { + let admin = &self.config.admin; let potlock_id = 0u32; let response = self .interactor .tx() - .from(&self.wallet_address) + .from(admin) .to(self.state.current_address()) .typed(proxy::PotlockProxy) .remove_pot(potlock_id) @@ -179,12 +181,13 @@ impl ContractInteract { } async fn accept_application(&mut self) { + let admin: &Bech32Address = &self.config.admin; let project_id = 1u32; let response = self .interactor .tx() - .from(&self.wallet_address) + .from(admin) .to(self.state.current_address()) .typed(proxy::PotlockProxy) .accept_application(project_id) @@ -197,13 +200,14 @@ impl ContractInteract { } async fn reject_donation(&mut self) { + let admin: &Bech32Address = &self.config.admin; + let user = &self.config.pot_donor; let potlock_id = 0u32; - let user = bech32::decode(""); let response = self .interactor .tx() - .from(&self.wallet_address) + .from(admin) .to(self.state.current_address()) .typed(proxy::PotlockProxy) .reject_donation(potlock_id, user) @@ -216,13 +220,14 @@ impl ContractInteract { } async fn distribute_pot_to_projects(&mut self) { + let admin: &Bech32Address = &self.config.admin; let potlock_id = 1u32; let project_percentage = MultiValueVec::from(vec![MultiValue2::from((1u32, 10_000u64))]); let response = self .interactor .tx() - .from(&self.wallet_address) + .from(admin) .to(self.state.current_address()) .typed(proxy::PotlockProxy) .distribute_pot_to_projects(potlock_id, project_percentage) @@ -235,6 +240,7 @@ impl ContractInteract { } async fn add_pot(&mut self) { + let pot_proposer: &Bech32Address = &self.config.pot_proposer; let token_id = TokenIdentifier::from_esdt_bytes(TOKEN_ID); let token_nonce = 0u64; let token_amount = BigUint::::from(FEE_AMOUNT); @@ -245,7 +251,7 @@ impl ContractInteract { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(pot_proposer) .to(self.state.current_address()) .typed(proxy::PotlockProxy) .add_pot(name, description) @@ -259,6 +265,7 @@ impl ContractInteract { } async fn apply_for_pot(&mut self) { + let project_proposer: &Bech32Address = &self.config.project_proposer; let potlock_id = 1u32; let project_name = ManagedBuffer::new_from_bytes(b"New Testing Project"); let description = ManagedBuffer::new_from_bytes(b"Project used for testing"); @@ -266,7 +273,7 @@ impl ContractInteract { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(project_proposer) .to(self.state.current_address()) .typed(proxy::PotlockProxy) .apply_for_pot(potlock_id, project_name, description) @@ -279,6 +286,7 @@ impl ContractInteract { } async fn donate_to_pot(&mut self) { + let pot_donor: &Bech32Address = &self.config.pot_donor; let token_id = TokenIdentifier::from_esdt_bytes(TOKEN_ID); let token_nonce = 0u64; let token_amount = BigUint::::from(3 * FEE_AMOUNT); @@ -288,7 +296,7 @@ impl ContractInteract { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(pot_donor) .to(self.state.current_address()) .typed(proxy::PotlockProxy) .donate_to_pot(potlock_id) @@ -302,6 +310,7 @@ impl ContractInteract { } async fn donate_to_project(&mut self) { + let project_donor: &Bech32Address = &self.config.project_donor; let token_id = TokenIdentifier::from_esdt_bytes(TOKEN_ID); let token_nonce = 0u64; let token_amount = BigUint::::from(3 * FEE_AMOUNT); @@ -311,7 +320,7 @@ impl ContractInteract { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(project_donor) .to(self.state.current_address()) .typed(proxy::PotlockProxy) .donate_to_project(project_id) @@ -325,13 +334,14 @@ impl ContractInteract { } async fn change_fee_for_pots(&mut self) { + let admin: &Bech32Address = &self.config.admin; let token_identifier = TokenIdentifier::from_esdt_bytes(TOKEN_ID); let fee = BigUint::::from(FEE_AMOUNT); let response = self .interactor .tx() - .from(&self.wallet_address) + .from(admin) .to(self.state.current_address()) .typed(proxy::PotlockProxy) .change_fee_for_pots(token_identifier, fee) From 3af93630be47c5649305e9ce44483a24e5a5c705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 19 Jul 2024 10:40:49 +0300 Subject: [PATCH 11/38] Framework upgrade 0.51.1 --- Cargo.lock | 4 ++-- contracts/potlock/Cargo.toml | 6 ++--- contracts/potlock/meta/Cargo.toml | 4 ++-- contracts/potlock/meta/src/main.rs | 2 +- contracts/potlock/wasm/Cargo.lock | 36 +++++++++++++++--------------- contracts/potlock/wasm/Cargo.toml | 2 +- contracts/potlock/wasm/src/libs.rs | 0 7 files changed, 27 insertions(+), 27 deletions(-) delete mode 100644 contracts/potlock/wasm/src/libs.rs diff --git a/Cargo.lock b/Cargo.lock index b313453a..867418c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1978,7 +1978,7 @@ dependencies = [ name = "potlock-meta" version = "0.0.0" dependencies = [ - "multiversx-sc-meta", + "multiversx-sc-meta-lib", "potlock", ] @@ -2373,7 +2373,7 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", diff --git a/contracts/potlock/Cargo.toml b/contracts/potlock/Cargo.toml index 8971591a..ca2eaedc 100644 --- a/contracts/potlock/Cargo.toml +++ b/contracts/potlock/Cargo.toml @@ -10,13 +10,13 @@ readme = "README.md" path = "src/potlock.rs" [dependencies.multiversx-sc] -version = "0.50.4" +version = "0.51.1" [dependencies.multiversx-sc-modules] -version = "=0.50.4" +version = "=0.51.1" [dev-dependencies] num-bigint = "0.4.2" [dev-dependencies.multiversx-sc-scenario] -version = "0.50.4" +version = "0.51.1" diff --git a/contracts/potlock/meta/Cargo.toml b/contracts/potlock/meta/Cargo.toml index 4f254f86..42d67977 100644 --- a/contracts/potlock/meta/Cargo.toml +++ b/contracts/potlock/meta/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies.potlock] path = ".." -[dependencies.multiversx-sc-meta] -version = "0.50.4" +[dependencies.multiversx-sc-meta-lib] +version = "0.51.1" default-features = false diff --git a/contracts/potlock/meta/src/main.rs b/contracts/potlock/meta/src/main.rs index 7a7e92da..1514f0fc 100644 --- a/contracts/potlock/meta/src/main.rs +++ b/contracts/potlock/meta/src/main.rs @@ -1,3 +1,3 @@ fn main() { - multiversx_sc_meta::cli_main::(); + multiversx_sc_meta_lib::cli_main::(); } diff --git a/contracts/potlock/wasm/Cargo.lock b/contracts/potlock/wasm/Cargo.lock index ad707b4a..3e6cce2b 100644 --- a/contracts/potlock/wasm/Cargo.lock +++ b/contracts/potlock/wasm/Cargo.lock @@ -16,9 +16,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "endian-type" @@ -40,9 +40,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "multiversx-sc" -version = "0.50.4" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "748a370a86e9a3c51e0609c4a6c0c9a5cceaeb742656ee3284c0620504a3d7a5" +checksum = "236f7890b2208796df8b5ac73b8572ffaf5e2b1531c7ad549d669328b715b657" dependencies = [ "bitflags", "hex-literal", @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "multiversx-sc-codec" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c94397b2fba14e40edfa55905b3f453ed57aa06c9b1960ad6a0ca6bfb7a236" +checksum = "fcecd449ea708b72f92edaa17158fe4859c1780aed9b52b14de45f26124ccb8b" dependencies = [ "arrayvec", "multiversx-sc-codec-derive", @@ -65,9 +65,9 @@ dependencies = [ [[package]] name = "multiversx-sc-codec-derive" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf72a8042da0bc19da0b8f0d4f61b4c66ae853560fefc69cd8fea87bf1aa8c14" +checksum = "68f7fa25402e5e8054d719951289306fd79e481f7c21b2565b5549b6bc359772" dependencies = [ "hex", "proc-macro2", @@ -77,9 +77,9 @@ dependencies = [ [[package]] name = "multiversx-sc-derive" -version = "0.50.4" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6ca88f27a90b0a1b17d5b645cc859e0480f4321c551525873a59a954bfbcb53" +checksum = "eb683bc78d0e2eb43c16cac790144f53cc2ab27912aeb1484433895742ce698d" dependencies = [ "hex", "proc-macro2", @@ -90,18 +90,18 @@ dependencies = [ [[package]] name = "multiversx-sc-modules" -version = "0.50.4" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97c7153d996ef412f5fc687a6799fc9173cb221adef283c217d160eebebe7d4" +checksum = "16af268784dff8a34cb696605413c325253da793d85f81b00dcb0e66f82963c9" dependencies = [ "multiversx-sc", ] [[package]] name = "multiversx-sc-wasm-adapter" -version = "0.50.4" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37f083d6844a0919a39d87c2552b8e9ea30e365775265d31b419d24f6aa93eee" +checksum = "b2f0d6be22f911ce45427491a9bec94612a1678eab2769dd08c9c9731d13da53" dependencies = [ "multiversx-sc", ] @@ -142,9 +142,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -176,9 +176,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" -version = "2.0.61" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", diff --git a/contracts/potlock/wasm/Cargo.toml b/contracts/potlock/wasm/Cargo.toml index 21c55c94..021aff2c 100644 --- a/contracts/potlock/wasm/Cargo.toml +++ b/contracts/potlock/wasm/Cargo.toml @@ -28,7 +28,7 @@ panic = "abort" path = ".." [dependencies.multiversx-sc-wasm-adapter] -version = "0.50.4" +version = "0.51.1" [workspace] members = ["."] diff --git a/contracts/potlock/wasm/src/libs.rs b/contracts/potlock/wasm/src/libs.rs deleted file mode 100644 index e69de29b..00000000 From 8b4903e336f37b1343245f2b9c8b069a2821f245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 19 Jul 2024 10:54:41 +0300 Subject: [PATCH 12/38] Fix tests --- Cargo.lock | 14 ----------- contracts/potlock/interact-rs/Cargo.toml | 25 +++++++++++-------- .../src/potlock_interactor_config.rs | 10 +++++--- .../src/potlock_interactor_main.rs | 5 ++-- .../potlock/tests/potlock_blackbox_tests.rs | 13 ++++++++-- 5 files changed, 34 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 867418c8..1d0ac906 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2252,20 +2252,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "ruplacer" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58a26a1b15ff113d31d139357f7422708312978ed69cd5dd47e36d1b80b7eaf3" -dependencies = [ - "Inflector", - "anyhow", - "clap", - "colored", - "ignore", - "regex", -] - [[package]] name = "rust-interact" version = "0.0.0" diff --git a/contracts/potlock/interact-rs/Cargo.toml b/contracts/potlock/interact-rs/Cargo.toml index 65a2e31f..4991dd2e 100644 --- a/contracts/potlock/interact-rs/Cargo.toml +++ b/contracts/potlock/interact-rs/Cargo.toml @@ -1,3 +1,7 @@ +[[bin]] +name = "rust-interact" +path = "src/potlock_interactor_main.rs" + [package] name = "rust-interact" version = "0.0.0" @@ -5,23 +9,22 @@ authors = ["you"] edition = "2021" publish = false -[[bin]] -name = "rust-interact" -path = "src/potlock_interactor_main.rs" +[dependencies] +toml = "0.8.6" [dependencies.potlock] path = ".." [dependencies.multiversx-sc-snippets] -version = "0.50.4" +version = "0.51.1" [dependencies.multiversx-sc] -version = "0.50.4" - -[dependencies] -clap = { version = "4.4.7", features = ["derive"] } -serde = { version = "1.0", features = ["derive"] } -toml = "0.8.6" +version = "0.51.1" -# [workspace] +[dependencies.clap] +version = "4.4.7" +features = ["derive"] +[dependencies.serde] +version = "1.0" +features = ["derive"] diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_config.rs b/contracts/potlock/interact-rs/src/potlock_interactor_config.rs index 7f70137c..77a34763 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_config.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_config.rs @@ -1,5 +1,4 @@ -use multiversx_sc::types::TokenIdentifier; -use multiversx_sc_snippets::imports::{Bech32Address, StaticApi}; +use multiversx_sc_snippets::imports::Bech32Address; use serde::Deserialize; use std::io::Read; @@ -9,7 +8,7 @@ const CONFIG_FILE: &str = "config.toml"; /// Multisig Interact configuration #[derive(Debug, Deserialize)] pub struct Config { - pub gateway: String, + gateway: String, pub admin: Bech32Address, pub pot_proposer: Bech32Address, pub project_proposer: Bech32Address, @@ -25,4 +24,9 @@ impl Config { file.read_to_string(&mut content).unwrap(); toml::from_str(&content).unwrap() } + + // Returns the gateway + pub fn gateway(&self) -> &str { + &self.gateway + } } diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index 5f207805..60d02358 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -4,14 +4,12 @@ mod potlock_interactor_config; mod proxy; use multiversx_sc_snippets::imports::*; -use multiversx_sc_snippets::sdk; use serde::{Deserialize, Serialize}; use std::{ io::{Read, Write}, path::Path, }; -const GATEWAY: &str = sdk::blockchain::DEVNET_GATEWAY; const STATE_FILE: &str = "state.toml"; const TOKEN_ID: &str = "WEGLD-a28c59"; const FEE_AMOUNT: u64 = 50000000000000000; // 0.5 @@ -102,7 +100,8 @@ struct ContractInteract { impl ContractInteract { async fn new() -> Self { - let mut interactor = Interactor::new(GATEWAY).await; + let config = Config::load_config(); + let mut interactor = Interactor::new(config.gateway()).await; let wallet_address = interactor.register_wallet(test_wallets::alice()); let contract_code = BytesValue::interpret_from( diff --git a/contracts/potlock/tests/potlock_blackbox_tests.rs b/contracts/potlock/tests/potlock_blackbox_tests.rs index 7f7082a5..d020fe36 100644 --- a/contracts/potlock/tests/potlock_blackbox_tests.rs +++ b/contracts/potlock/tests/potlock_blackbox_tests.rs @@ -312,6 +312,9 @@ fn test_donate_to_pot() { let potlock_id = 1usize; state.check_potlock_id_is_last(potlock_id); + // Accept Pot + state.accept_pot(potlock_id); + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE); @@ -332,6 +335,9 @@ fn test_donate_to_project() { let potlock_id = 1usize; state.check_potlock_id_is_last(potlock_id); + // Accept Pot + state.accept_pot(potlock_id); + state.apply_for_pot(potlock_id, "Project name", "Project description"); let project_id = 1usize; state.check_project_id_is_last(project_id); @@ -374,12 +380,15 @@ fn test_distribute_pot_to_projects() { state.deploy_potlock_contract(); state.change_fee_for_pots(POT_FEE_CREATION); - // Add pot + // Add Pot state.add_pot("Pot", "Pot Description"); let potlock_id: usize = 1usize; state.check_potlock_id_is_last(potlock_id); - // Add project + // Accept Pot + state.accept_pot(potlock_id); + + // Add Project let project_id = 1usize; state.apply_for_pot(potlock_id, "Project name", "Project description"); From 2da0c7aca587b87a85938e2fb320b4c06ff1ea11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 19 Jul 2024 11:54:02 +0300 Subject: [PATCH 13/38] Fix clippy --- .../potlock/interact-rs/src/potlock_interactor_main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index 60d02358..d8d53063 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -254,7 +254,7 @@ impl ContractInteract { .to(self.state.current_address()) .typed(proxy::PotlockProxy) .add_pot(name, description) - .payment((TokenIdentifier::from(token_id), token_nonce, token_amount)) + .payment((token_id, token_nonce, token_amount)) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -299,7 +299,7 @@ impl ContractInteract { .to(self.state.current_address()) .typed(proxy::PotlockProxy) .donate_to_pot(potlock_id) - .payment((TokenIdentifier::from(token_id), token_nonce, token_amount)) + .payment((token_id, token_nonce, token_amount)) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -323,7 +323,7 @@ impl ContractInteract { .to(self.state.current_address()) .typed(proxy::PotlockProxy) .donate_to_project(project_id) - .payment((TokenIdentifier::from(token_id), token_nonce, token_amount)) + .payment((token_id, token_nonce, token_amount)) .returns(ReturnsResultUnmanaged) .prepare_async() .run() From a5894c53c0528ae3cc69fadfbcbe0eb03939d51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 19 Jul 2024 12:12:43 +0300 Subject: [PATCH 14/38] Fixes after review --- contracts/potlock/src/potlock_admin_interactions.rs | 7 +++---- contracts/potlock/src/potlock_storage.rs | 12 ++++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/contracts/potlock/src/potlock_admin_interactions.rs b/contracts/potlock/src/potlock_admin_interactions.rs index 3664f768..dfb82ce6 100644 --- a/contracts/potlock/src/potlock_admin_interactions.rs +++ b/contracts/potlock/src/potlock_admin_interactions.rs @@ -32,7 +32,9 @@ pub trait PotlockAdminInteractions: #[only_admin] #[endpoint(removePot)] fn remove_pot(&self, potlock_id: PotlockId) { - let pot_proposer = self.fee_pot_proposer(potlock_id).get(); + self.require_potlock_exists(potlock_id); + + let pot_proposer = self.fee_pot_proposer(potlock_id).take(); let fee_pot_payment = EsdtTokenPayment::new( self.fee_token_identifier().get(), 0u64, @@ -41,7 +43,6 @@ pub trait PotlockAdminInteractions: self.send() .direct_non_zero_esdt_payment(&pot_proposer, &fee_pot_payment); - self.fee_pot_proposer(potlock_id).clear(); self.potlocks().clear_entry(potlock_id); } @@ -95,7 +96,5 @@ pub trait PotlockAdminInteractions: } self.pot_donations(potlock_id).clear(); - - //TODO: Clear all info regarding the pot? } } diff --git a/contracts/potlock/src/potlock_storage.rs b/contracts/potlock/src/potlock_storage.rs index cb17f6df..24477dfe 100644 --- a/contracts/potlock/src/potlock_storage.rs +++ b/contracts/potlock/src/potlock_storage.rs @@ -75,11 +75,11 @@ pub struct UserDonations { #[multiversx_sc::module] pub trait PotlockStorage { #[view(getFeeTokenIdentifier)] - #[storage_mapper("fee_token_identifier")] + #[storage_mapper("feeTokenIdentifier")] fn fee_token_identifier(&self) -> SingleValueMapper; #[view(getFeeAmount)] - #[storage_mapper("fee_amount")] + #[storage_mapper("feeAmount")] fn fee_amount(&self) -> SingleValueMapper; #[view(getPotlocks)] @@ -91,19 +91,19 @@ pub trait PotlockStorage { fn projects(&self) -> VecMapper>; #[view(feePotPayments)] - #[storage_mapper("fee_pot_proposer")] + #[storage_mapper("feePotProposer")] fn fee_pot_proposer(&self, potlock_id: PotlockId) -> SingleValueMapper; #[view(feeAmountAcceptPots)] - #[storage_mapper("fee_amount_accepted_pots")] + #[storage_mapper("feeAmountAcceptedPots")] fn fee_amount_accepted_pots(&self) -> SingleValueMapper; #[view(potDonations)] - #[storage_mapper("pot_donations")] + #[storage_mapper("potDonations")] fn pot_donations(&self, project_id: ProjectId) -> MapMapper; #[view(projectDonations)] - #[storage_mapper("project_donations")] + #[storage_mapper("projectDonations")] fn project_donations( &self, project_id: ProjectId, From 68b46600a885ec45f109f515391770b45291a4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 31 Jul 2024 19:33:18 +0300 Subject: [PATCH 15/38] Fixes after review --- contracts/potlock/src/potlock.rs | 8 +-- .../potlock/src/potlock_admin_interactions.rs | 35 ++++++++---- contracts/potlock/src/potlock_interactions.rs | 9 +-- contracts/potlock/src/potlock_setup.rs | 52 ----------------- contracts/potlock/src/potlock_storage.rs | 56 +++++++++++++++++-- contracts/potlock/wasm/src/lib.rs | 7 +-- 6 files changed, 84 insertions(+), 83 deletions(-) delete mode 100644 contracts/potlock/src/potlock_setup.rs diff --git a/contracts/potlock/src/potlock.rs b/contracts/potlock/src/potlock.rs index 2e119b5c..ea51a588 100644 --- a/contracts/potlock/src/potlock.rs +++ b/contracts/potlock/src/potlock.rs @@ -4,7 +4,6 @@ multiversx_sc::imports!(); multiversx_sc::derive_imports!(); pub mod potlock_admin_interactions; pub mod potlock_interactions; -pub mod potlock_setup; pub mod potlock_storage; /// An empty contract. To be used as a template when starting a new contract from scratch. @@ -12,15 +11,16 @@ pub mod potlock_storage; pub trait Potlock: potlock_admin_interactions::PotlockAdminInteractions + potlock_interactions::PotlockInteractions - + potlock_setup::PotlockSetup + potlock_storage::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule { #[init] - fn init(&self, admin: ManagedAddress) { + fn init(&self, admins: MultiValueEncoded) { let caller = self.blockchain().get_caller(); self.admins().insert(caller); - self.admins().insert(admin); + for admin in admins { + self.admins().insert(admin); + } } #[upgrade] diff --git a/contracts/potlock/src/potlock_admin_interactions.rs b/contracts/potlock/src/potlock_admin_interactions.rs index dfb82ce6..42a30df3 100644 --- a/contracts/potlock/src/potlock_admin_interactions.rs +++ b/contracts/potlock/src/potlock_admin_interactions.rs @@ -1,20 +1,30 @@ -use crate::{ - potlock_setup, - potlock_storage::{self, PotlockId, ProjectId, Status}, -}; +use crate::potlock_storage::{self, PotlockId, ProjectId, Status}; multiversx_sc::imports!(); multiversx_sc::derive_imports!(); pub type ProjectPercentage = MultiValue2; -const MAX_PERCENTAGE: u64 = 10_000; // 100% +pub const MAX_PERCENTAGE: u64 = 10_000; // 100% #[multiversx_sc::module] pub trait PotlockAdminInteractions: - potlock_storage::PotlockStorage - + multiversx_sc_modules::only_admin::OnlyAdminModule - + potlock_setup::PotlockSetup + potlock_storage::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule { + #[only_admin] + #[endpoint(changeFeeForPots)] + fn change_fee_for_pots(&self, token_identifier: TokenIdentifier, fee: BigUint) { + require!( + token_identifier.is_valid_esdt_identifier(), + "Invalid token provided" + ); + require!( + token_identifier.is_valid_esdt_identifier() && fee.ge(&BigUint::zero()), + "Invalid token identifier or amount is 0" + ); + self.fee_token_identifier().set(&token_identifier); + self.fee_amount().set(fee); + } + #[only_admin] #[endpoint(acceptPot)] fn accept_pot(&self, potlock_id: PotlockId) { @@ -26,7 +36,6 @@ pub trait PotlockAdminInteractions: let mut accepted_potlock = self.potlocks().get(potlock_id); accepted_potlock.status = Status::Active; self.potlocks().set(potlock_id, &accepted_potlock); - self.fee_pot_proposer(potlock_id).clear(); } #[only_admin] @@ -34,7 +43,8 @@ pub trait PotlockAdminInteractions: fn remove_pot(&self, potlock_id: PotlockId) { self.require_potlock_exists(potlock_id); - let pot_proposer = self.fee_pot_proposer(potlock_id).take(); + let potlock_mapper = self.potlocks(); + let pot_proposer = potlock_mapper.get(potlock_id).proposer; let fee_pot_payment = EsdtTokenPayment::new( self.fee_token_identifier().get(), 0u64, @@ -74,12 +84,13 @@ pub trait PotlockAdminInteractions: fn distribute_pot_to_projects( &self, potlock_id: PotlockId, - project_percentage: MultiValueEncoded, + project_percentages: MultiValueEncoded, ) { self.require_potlock_exists(potlock_id); + self.require_correct_percentages(project_percentages.clone()); let pot_donations = self.pot_donations(potlock_id); - for pp in project_percentage { + for pp in project_percentages { let (project_id, percentage) = pp.into_tuple(); let mut output_payments = ManagedVec::new(); for (_, donation) in pot_donations.iter() { diff --git a/contracts/potlock/src/potlock_interactions.rs b/contracts/potlock/src/potlock_interactions.rs index 5182bdb7..641214ce 100644 --- a/contracts/potlock/src/potlock_interactions.rs +++ b/contracts/potlock/src/potlock_interactions.rs @@ -1,4 +1,3 @@ -use crate::potlock_setup; use crate::potlock_storage::{self, Pot, Project}; use crate::potlock_storage::{PotlockId, ProjectId}; @@ -7,9 +6,7 @@ multiversx_sc::derive_imports!(); #[multiversx_sc::module] pub trait PotlockInteractions: - potlock_storage::PotlockStorage - + potlock_setup::PotlockSetup - + multiversx_sc_modules::only_admin::OnlyAdminModule + potlock_storage::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule { #[payable("*")] #[endpoint(addPot)] @@ -26,10 +23,8 @@ pub trait PotlockInteractions: let caller = self.blockchain().get_caller(); let potlock_id = self.potlocks().len() + 1; - let potlock = Pot::new(potlock_id, name, description); + let potlock = Pot::new(potlock_id, caller, name, description); self.potlocks().push(&potlock); - - self.fee_pot_proposer(potlock_id).set(caller); } #[endpoint(applyForPot)] diff --git a/contracts/potlock/src/potlock_setup.rs b/contracts/potlock/src/potlock_setup.rs deleted file mode 100644 index 1870ca09..00000000 --- a/contracts/potlock/src/potlock_setup.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::potlock_storage::{self, PotlockId, ProjectId, Status}; - -multiversx_sc::imports!(); - -#[multiversx_sc::module] -pub trait PotlockSetup: - potlock_storage::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule -{ - #[only_admin] - #[endpoint(changeFeeForPots)] - fn change_fee_for_pots(&self, token_identifier: TokenIdentifier, fee: BigUint) { - require!( - token_identifier.is_valid_esdt_identifier(), - "Invalid token provided" - ); - self.fee_token_identifier().set(&token_identifier); - self.fee_amount().set(fee); - } - - //// internal functions - fn is_valid_potlock_id(&self, potlock_id: PotlockId) -> bool { - potlock_id >= 1 && potlock_id <= self.potlocks().len() - } - - fn require_potlock_exists(&self, potlock_id: PotlockId) { - require!( - self.is_valid_potlock_id(potlock_id) && !self.potlocks().item_is_empty(potlock_id), - "Potlock doesn't exist!", - ) - } - - fn require_potlock_is_active(&self, potlock_id: PotlockId) { - let potlock = self.potlocks().get(potlock_id); - require!(potlock.status == Status::Active, "Pot is not active!",) - } - - fn is_valid_project_id(&self, project_id: ProjectId) -> bool { - project_id >= 1 && project_id <= self.projects().len() - } - - fn require_project_exists(&self, project_id: ProjectId) { - require!( - self.is_valid_project_id(project_id) && !self.projects().item_is_empty(project_id), - "Project doesn't exist!", - ) - } - - fn require_project_is_active(&self, project_id: ProjectId) { - let project = self.potlocks().get(project_id); - require!(project.status == Status::Active, "Project is not active!",) - } -} diff --git a/contracts/potlock/src/potlock_storage.rs b/contracts/potlock/src/potlock_storage.rs index 24477dfe..46f6df63 100644 --- a/contracts/potlock/src/potlock_storage.rs +++ b/contracts/potlock/src/potlock_storage.rs @@ -1,3 +1,5 @@ +use crate::potlock_admin_interactions::{ProjectPercentage, MAX_PERCENTAGE}; + multiversx_sc::imports!(); multiversx_sc::derive_imports!(); @@ -13,6 +15,7 @@ pub enum Status { #[derive(TypeAbi, NestedEncode, NestedDecode, PartialEq, Debug, TopEncode, TopDecode)] pub struct Pot { pub potlock_id: PotlockId, + pub proposer: ManagedAddress, pub token_identifier: TokenIdentifier, pub fee: BigUint, pub name: ManagedBuffer, @@ -23,11 +26,13 @@ pub struct Pot { impl Pot { pub fn new( potlock_id: PotlockId, + proposer: ManagedAddress, name: ManagedBuffer, description: ManagedBuffer, ) -> Self { Pot { potlock_id, + proposer, token_identifier: TokenIdentifier::from(ManagedBuffer::default()), fee: BigUint::default(), name, @@ -74,6 +79,53 @@ pub struct UserDonations { #[multiversx_sc::module] pub trait PotlockStorage { + fn is_valid_potlock_id(&self, potlock_id: PotlockId) -> bool { + potlock_id >= 1 && potlock_id <= self.potlocks().len() + } + + fn require_potlock_exists(&self, potlock_id: PotlockId) { + require!( + self.is_valid_potlock_id(potlock_id) && !self.potlocks().item_is_empty(potlock_id), + "Potlock doesn't exist!", + ) + } + + fn require_potlock_is_active(&self, potlock_id: PotlockId) { + let potlock = self.potlocks().get(potlock_id); + require!(potlock.status == Status::Active, "Pot is not active!",) + } + + fn is_valid_project_id(&self, project_id: ProjectId) -> bool { + project_id >= 1 && project_id <= self.projects().len() + } + + fn require_project_exists(&self, project_id: ProjectId) { + require!( + self.is_valid_project_id(project_id) && !self.projects().item_is_empty(project_id), + "Project doesn't exist!", + ) + } + + fn require_project_is_active(&self, project_id: ProjectId) { + let project = self.projects().get(project_id); + require!(project.status == Status::Active, "Project is not active!",) + } + + fn require_correct_percentages( + &self, + project_percentages: MultiValueEncoded, + ) { + let mut total_perc: u64 = 0; + for pp in project_percentages { + let (_, perc) = pp.into_tuple(); + total_perc += perc; + } + require!( + total_perc == MAX_PERCENTAGE, + "Total percentages different than 100%" + ); + } + #[view(getFeeTokenIdentifier)] #[storage_mapper("feeTokenIdentifier")] fn fee_token_identifier(&self) -> SingleValueMapper; @@ -90,10 +142,6 @@ pub trait PotlockStorage { #[storage_mapper("projects")] fn projects(&self) -> VecMapper>; - #[view(feePotPayments)] - #[storage_mapper("feePotProposer")] - fn fee_pot_proposer(&self, potlock_id: PotlockId) -> SingleValueMapper; - #[view(feeAmountAcceptPots)] #[storage_mapper("feeAmountAcceptedPots")] fn fee_amount_accepted_pots(&self) -> SingleValueMapper; diff --git a/contracts/potlock/wasm/src/lib.rs b/contracts/potlock/wasm/src/lib.rs index 7bd5b1a1..99faaf13 100644 --- a/contracts/potlock/wasm/src/lib.rs +++ b/contracts/potlock/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 22 +// Endpoints: 21 // Async Callback (empty): 1 -// Total number of exported functions: 25 +// Total number of exported functions: 24 #![no_std] @@ -20,6 +20,7 @@ multiversx_sc_wasm_adapter::endpoints! { ( init => init upgrade => upgrade + changeFeeForPots => change_fee_for_pots acceptPot => accept_pot removePot => remove_pot acceptApplication => accept_application @@ -29,12 +30,10 @@ multiversx_sc_wasm_adapter::endpoints! { applyForPot => apply_for_pot donateToPot => donate_to_pot donateToProject => donate_to_project - changeFeeForPots => change_fee_for_pots getFeeTokenIdentifier => fee_token_identifier getFeeAmount => fee_amount getPotlocks => potlocks getProjects => projects - feePotPayments => fee_pot_proposer feeAmountAcceptPots => fee_amount_accepted_pots potDonations => pot_donations projectDonations => project_donations From 2bbbe840fbdf8cc2357433e0faf5b474c9ff46ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 31 Jul 2024 19:37:26 +0300 Subject: [PATCH 16/38] Fix tests --- .../potlock/tests/potlock_blackbox_tests.rs | 55 +++++++++++++++++-- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/contracts/potlock/tests/potlock_blackbox_tests.rs b/contracts/potlock/tests/potlock_blackbox_tests.rs index d020fe36..a04a3b84 100644 --- a/contracts/potlock/tests/potlock_blackbox_tests.rs +++ b/contracts/potlock/tests/potlock_blackbox_tests.rs @@ -342,6 +342,9 @@ fn test_donate_to_project() { let project_id = 1usize; state.check_project_id_is_last(project_id); + state.accept_application(project_id); + state.check_project_is_accepted(project_id); + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); state.check_esdt_balance(PROJECT_DONOR_ADDRESS, INITIAL_BALANCE); @@ -407,13 +410,10 @@ fn test_distribute_pot_to_projects() { // Distribute Pot donations to projects let mut percentages = MultiValueVec::new(); - percentages.push((project_id, HALF_PERCENTAGE).into()); + percentages.push((project_id, MAX_PERCENTAGE).into()); state.distribute_pot_to_projects(potlock_id, percentages); - state.check_esdt_balance( - PROJECT_PROPOSER_ADDRESS, - HALF_PERCENTAGE * DONATION_AMOUNT / MAX_PERCENTAGE, - ); + state.check_esdt_balance(PROJECT_PROPOSER_ADDRESS, DONATION_AMOUNT); } ///////////// Negative tests ////////////// @@ -547,3 +547,48 @@ fn test_fail_distribute_pot_to_projects() { .with_result(ExpectError(4, "Endpoint can only be called by admins")) .run(); } + +#[test] +fn test_fail_distribute_pot_to_projects2() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + // Add Pot + state.add_pot("Pot", "Pot Description"); + let potlock_id: usize = 1usize; + state.check_potlock_id_is_last(potlock_id); + + // Accept Pot + state.accept_pot(potlock_id); + + // Add Project + let project_id = 1usize; + state.apply_for_pot(potlock_id, "Project name", "Project description"); + + state.check_project_id_is_last(project_id); + state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); + + // Donate to Pot + state.donate_to_pot(potlock_id); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION + DONATION_AMOUNT); + state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); + + // Accept project + state.accept_application(project_id); + state.check_project_is_accepted(project_id); + + // Distribute Pot donations to projects + let mut percentages = MultiValueVec::new(); + percentages.push((project_id, HALF_PERCENTAGE).into()); + state + .world + .tx() + .from(OWNER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .distribute_pot_to_projects(potlock_id, percentages) + .with_result(ExpectError(4, "Total percentages different than 100%")) + .run(); +} From 2abeab0c17a608d260a1ff28ec42ede9cb5f6420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 31 Jul 2024 19:54:19 +0300 Subject: [PATCH 17/38] Refuse multiple donations with different tokens --- contracts/potlock/src/potlock_interactions.rs | 4 ++-- contracts/potlock/src/potlock_storage.rs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/contracts/potlock/src/potlock_interactions.rs b/contracts/potlock/src/potlock_interactions.rs index 641214ce..c06891a1 100644 --- a/contracts/potlock/src/potlock_interactions.rs +++ b/contracts/potlock/src/potlock_interactions.rs @@ -54,10 +54,10 @@ pub trait PotlockInteractions: #[endpoint(donateToProject)] fn donate_to_project(&self, project_id: ProjectId) { self.require_project_exists(project_id); + self.require_project_is_active(project_id); let payment = self.call_value().single_esdt(); let caller = self.blockchain().get_caller(); - self.require_project_exists(project_id); - self.require_project_is_active(project_id); + self.require_donation_same_token_id(project_id, &caller, payment.token_identifier.clone()); self.project_donations(project_id).insert(caller, payment); } } diff --git a/contracts/potlock/src/potlock_storage.rs b/contracts/potlock/src/potlock_storage.rs index 46f6df63..c4e8b8b5 100644 --- a/contracts/potlock/src/potlock_storage.rs +++ b/contracts/potlock/src/potlock_storage.rs @@ -126,6 +126,24 @@ pub trait PotlockStorage { ); } + fn require_donation_same_token_id( + &self, + project_id: ProjectId, + user: &ManagedAddress, + token_id: TokenIdentifier, + ) { + let donation_mapper = self.project_donations(project_id); + if donation_mapper.contains_key(user) { + let payment = donation_mapper.get(user); + if payment.is_some() { + require!( + payment.unwrap().token_identifier == token_id, + "Already made a payment with a different TokenID" + ); + } + } + } + #[view(getFeeTokenIdentifier)] #[storage_mapper("feeTokenIdentifier")] fn fee_token_identifier(&self) -> SingleValueMapper; From 324f598164e6abc30a5193f4eddfc08f8a2db2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 31 Jul 2024 19:54:47 +0300 Subject: [PATCH 18/38] Add tests for multiple donations with different tokens --- .../potlock/tests/potlock_blackbox_tests.rs | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/contracts/potlock/tests/potlock_blackbox_tests.rs b/contracts/potlock/tests/potlock_blackbox_tests.rs index a04a3b84..d0e07cd3 100644 --- a/contracts/potlock/tests/potlock_blackbox_tests.rs +++ b/contracts/potlock/tests/potlock_blackbox_tests.rs @@ -12,6 +12,7 @@ const PROJECT_PROPOSER_ADDRESS: TestAddress = TestAddress::new("project_proposer const POT_DONOR_ADDRESS: TestAddress = TestAddress::new("pot_donor"); const PROJECT_DONOR_ADDRESS: TestAddress = TestAddress::new("project_donor"); const TOKEN_ID: TestTokenIdentifier = TestTokenIdentifier::new("POT-123456"); +const DIFFERENT_TOKEN_ID: TestTokenIdentifier = TestTokenIdentifier::new("DIFFPOT-123456"); const POT_FEE_CREATION: u64 = 1_000; const INITIAL_BALANCE: u64 = 2_000; const DONATION_AMOUNT: u64 = 100; @@ -48,7 +49,8 @@ impl PotlockTestState { .esdt_balance(TOKEN_ID, INITIAL_BALANCE) .account(PROJECT_DONOR_ADDRESS) .nonce(1) - .esdt_balance(TOKEN_ID, INITIAL_BALANCE); + .esdt_balance(TOKEN_ID, INITIAL_BALANCE) + .esdt_balance(DIFFERENT_TOKEN_ID, INITIAL_BALANCE); Self { world } } @@ -592,3 +594,51 @@ fn test_fail_distribute_pot_to_projects2() { .with_result(ExpectError(4, "Total percentages different than 100%")) .run(); } + +#[test] +fn test_fail_donate_to_project() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + let potlock_id = 1usize; + state.check_potlock_id_is_last(potlock_id); + + // Accept Pot + state.accept_pot(potlock_id); + + state.apply_for_pot(potlock_id, "Project name", "Project description"); + let project_id = 1usize; + state.check_project_id_is_last(project_id); + + state.accept_application(project_id); + state.check_project_is_accepted(project_id); + + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); + state.check_esdt_balance(PROJECT_DONOR_ADDRESS, INITIAL_BALANCE); + + state.donate_to_project(project_id); + + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION + DONATION_AMOUNT); + state.check_esdt_balance(PROJECT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); + + state + .world + .tx() + .from(PROJECT_DONOR_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .donate_to_project(project_id) + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(DIFFERENT_TOKEN_ID), + 0u64, + &multiversx_sc::proxy_imports::BigUint::from(DONATION_AMOUNT), + ) + .with_result(ExpectError( + 4, + "Already made a payment with a different TokenID", + )) + .run(); +} From c01041daf4303f4d203ed082088d5ac9e3ff97db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 31 Jul 2024 20:03:10 +0300 Subject: [PATCH 19/38] Fix donate_to_project Add amount of current payment to previous payment --- contracts/potlock/src/potlock_interactions.rs | 19 +++++++++++++++++-- contracts/potlock/src/potlock_storage.rs | 18 ------------------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/contracts/potlock/src/potlock_interactions.rs b/contracts/potlock/src/potlock_interactions.rs index c06891a1..3873f007 100644 --- a/contracts/potlock/src/potlock_interactions.rs +++ b/contracts/potlock/src/potlock_interactions.rs @@ -57,7 +57,22 @@ pub trait PotlockInteractions: self.require_project_is_active(project_id); let payment = self.call_value().single_esdt(); let caller = self.blockchain().get_caller(); - self.require_donation_same_token_id(project_id, &caller, payment.token_identifier.clone()); - self.project_donations(project_id).insert(caller, payment); + + let mut donation_mapper = self.project_donations(project_id); + if donation_mapper.contains_key(&caller) { + let opt_payment = donation_mapper.remove(&caller); + if opt_payment.is_some() { + let mut previous_payment = opt_payment.unwrap(); + require!( + previous_payment.token_identifier == payment.token_identifier.clone(), + "Already made a payment with a different TokenID" + ); + previous_payment.amount += payment.amount; + self.project_donations(project_id) + .insert(caller, previous_payment); + } + } else { + self.project_donations(project_id).insert(caller, payment); + } } } diff --git a/contracts/potlock/src/potlock_storage.rs b/contracts/potlock/src/potlock_storage.rs index c4e8b8b5..46f6df63 100644 --- a/contracts/potlock/src/potlock_storage.rs +++ b/contracts/potlock/src/potlock_storage.rs @@ -126,24 +126,6 @@ pub trait PotlockStorage { ); } - fn require_donation_same_token_id( - &self, - project_id: ProjectId, - user: &ManagedAddress, - token_id: TokenIdentifier, - ) { - let donation_mapper = self.project_donations(project_id); - if donation_mapper.contains_key(user) { - let payment = donation_mapper.get(user); - if payment.is_some() { - require!( - payment.unwrap().token_identifier == token_id, - "Already made a payment with a different TokenID" - ); - } - } - } - #[view(getFeeTokenIdentifier)] #[storage_mapper("feeTokenIdentifier")] fn fee_token_identifier(&self) -> SingleValueMapper; From a1e9c73d52b9cbacdcb9e06a89bd2c7066ad4715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 1 Aug 2024 09:57:04 +0300 Subject: [PATCH 20/38] Framework upgrade 0.52.1 --- contracts/potlock/Cargo.toml | 6 ++--- contracts/potlock/interact-rs/Cargo.toml | 4 ++-- contracts/potlock/meta/Cargo.toml | 2 +- contracts/potlock/wasm/Cargo.lock | 28 ++++++++++++------------ contracts/potlock/wasm/Cargo.toml | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/contracts/potlock/Cargo.toml b/contracts/potlock/Cargo.toml index ca2eaedc..21f6208f 100644 --- a/contracts/potlock/Cargo.toml +++ b/contracts/potlock/Cargo.toml @@ -10,13 +10,13 @@ readme = "README.md" path = "src/potlock.rs" [dependencies.multiversx-sc] -version = "0.51.1" +version = "0.52.1" [dependencies.multiversx-sc-modules] -version = "=0.51.1" +version = "=0.52.1" [dev-dependencies] num-bigint = "0.4.2" [dev-dependencies.multiversx-sc-scenario] -version = "0.51.1" +version = "0.52.1" diff --git a/contracts/potlock/interact-rs/Cargo.toml b/contracts/potlock/interact-rs/Cargo.toml index 4991dd2e..ef52901f 100644 --- a/contracts/potlock/interact-rs/Cargo.toml +++ b/contracts/potlock/interact-rs/Cargo.toml @@ -16,10 +16,10 @@ toml = "0.8.6" path = ".." [dependencies.multiversx-sc-snippets] -version = "0.51.1" +version = "0.52.1" [dependencies.multiversx-sc] -version = "0.51.1" +version = "0.52.1" [dependencies.clap] version = "4.4.7" diff --git a/contracts/potlock/meta/Cargo.toml b/contracts/potlock/meta/Cargo.toml index 42d67977..0541c340 100644 --- a/contracts/potlock/meta/Cargo.toml +++ b/contracts/potlock/meta/Cargo.toml @@ -8,5 +8,5 @@ publish = false path = ".." [dependencies.multiversx-sc-meta-lib] -version = "0.51.1" +version = "0.52.1" default-features = false diff --git a/contracts/potlock/wasm/Cargo.lock b/contracts/potlock/wasm/Cargo.lock index 3e6cce2b..e9f3674e 100644 --- a/contracts/potlock/wasm/Cargo.lock +++ b/contracts/potlock/wasm/Cargo.lock @@ -40,9 +40,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "multiversx-sc" -version = "0.51.1" +version = "0.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "236f7890b2208796df8b5ac73b8572ffaf5e2b1531c7ad549d669328b715b657" +checksum = "ad1c90e0079028040f6219494a66a5fec724ab1e4b4b836e06cc899b5fe9f2f9" dependencies = [ "bitflags", "hex-literal", @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "multiversx-sc-codec" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcecd449ea708b72f92edaa17158fe4859c1780aed9b52b14de45f26124ccb8b" +checksum = "ad4f318427761faecf26c1f3115a3beeb5f61858845a60547d9763aa981ddd2d" dependencies = [ "arrayvec", "multiversx-sc-codec-derive", @@ -65,9 +65,9 @@ dependencies = [ [[package]] name = "multiversx-sc-codec-derive" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f7fa25402e5e8054d719951289306fd79e481f7c21b2565b5549b6bc359772" +checksum = "476501462b0c2654b64f9dec6f2c480e24b4e9b7133ec10b7167e64acda35d04" dependencies = [ "hex", "proc-macro2", @@ -77,9 +77,9 @@ dependencies = [ [[package]] name = "multiversx-sc-derive" -version = "0.51.1" +version = "0.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb683bc78d0e2eb43c16cac790144f53cc2ab27912aeb1484433895742ce698d" +checksum = "2da247945d1700c84859f935214e49ec4d781fbebc483fadc715291e49801cae" dependencies = [ "hex", "proc-macro2", @@ -90,18 +90,18 @@ dependencies = [ [[package]] name = "multiversx-sc-modules" -version = "0.51.1" +version = "0.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16af268784dff8a34cb696605413c325253da793d85f81b00dcb0e66f82963c9" +checksum = "6dec3bd3e6ea44263db7cc5bef2d16d0626c5dc1e18a1923843459ba7896ded2" dependencies = [ "multiversx-sc", ] [[package]] name = "multiversx-sc-wasm-adapter" -version = "0.51.1" +version = "0.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f0d6be22f911ce45427491a9bec94612a1678eab2769dd08c9c9731d13da53" +checksum = "b81cacdea364a46a8b617c0917aa33d1b472018144bd04a3bd03e19e23423469" dependencies = [ "multiversx-sc", ] @@ -176,9 +176,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" -version = "2.0.68" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", diff --git a/contracts/potlock/wasm/Cargo.toml b/contracts/potlock/wasm/Cargo.toml index 021aff2c..f5d97930 100644 --- a/contracts/potlock/wasm/Cargo.toml +++ b/contracts/potlock/wasm/Cargo.toml @@ -28,7 +28,7 @@ panic = "abort" path = ".." [dependencies.multiversx-sc-wasm-adapter] -version = "0.51.1" +version = "0.52.1" [workspace] members = ["."] From b6bdb7482bdb10745ba1c02d2f27cb5073cd6808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 5 Aug 2024 16:47:34 +0300 Subject: [PATCH 21/38] potlock: distributePotToProjects: percentage req Modify require_correct_percentages from distributePotToProjects endpoint. The total may be lower than 100%. This enables the caller to distribute part of the pot. --- contracts/potlock/src/potlock_storage.rs | 4 ++-- contracts/potlock/tests/potlock_blackbox_tests.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/potlock/src/potlock_storage.rs b/contracts/potlock/src/potlock_storage.rs index 46f6df63..676bce9f 100644 --- a/contracts/potlock/src/potlock_storage.rs +++ b/contracts/potlock/src/potlock_storage.rs @@ -121,8 +121,8 @@ pub trait PotlockStorage { total_perc += perc; } require!( - total_perc == MAX_PERCENTAGE, - "Total percentages different than 100%" + total_perc <= MAX_PERCENTAGE, + "Total percentages more than 100%" ); } diff --git a/contracts/potlock/tests/potlock_blackbox_tests.rs b/contracts/potlock/tests/potlock_blackbox_tests.rs index d0e07cd3..994b8b53 100644 --- a/contracts/potlock/tests/potlock_blackbox_tests.rs +++ b/contracts/potlock/tests/potlock_blackbox_tests.rs @@ -583,7 +583,7 @@ fn test_fail_distribute_pot_to_projects2() { // Distribute Pot donations to projects let mut percentages = MultiValueVec::new(); - percentages.push((project_id, HALF_PERCENTAGE).into()); + percentages.push((project_id, 3* HALF_PERCENTAGE).into()); state .world .tx() @@ -591,7 +591,7 @@ fn test_fail_distribute_pot_to_projects2() { .to(POTLOCK_ADDRESS) .typed(potlock_proxy::PotlockProxy) .distribute_pot_to_projects(potlock_id, percentages) - .with_result(ExpectError(4, "Total percentages different than 100%")) + .with_result(ExpectError(4, "Total percentages more than 100%")) .run(); } From 3bc8bf5536a4dd40013cfb413aa277266e76c495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 14 Aug 2024 12:56:30 +0300 Subject: [PATCH 22/38] potlock: Framework upgrade --- contracts/potlock/Cargo.toml | 6 +++--- contracts/potlock/interact-rs/Cargo.toml | 4 ++-- contracts/potlock/meta/Cargo.toml | 2 +- contracts/potlock/wasm/Cargo.lock | 16 ++++++++-------- contracts/potlock/wasm/Cargo.toml | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/contracts/potlock/Cargo.toml b/contracts/potlock/Cargo.toml index 21f6208f..783b1e46 100644 --- a/contracts/potlock/Cargo.toml +++ b/contracts/potlock/Cargo.toml @@ -10,13 +10,13 @@ readme = "README.md" path = "src/potlock.rs" [dependencies.multiversx-sc] -version = "0.52.1" +version = "0.52.3" [dependencies.multiversx-sc-modules] -version = "=0.52.1" +version = "=0.52.3" [dev-dependencies] num-bigint = "0.4.2" [dev-dependencies.multiversx-sc-scenario] -version = "0.52.1" +version = "0.52.3" diff --git a/contracts/potlock/interact-rs/Cargo.toml b/contracts/potlock/interact-rs/Cargo.toml index ef52901f..e0cb932b 100644 --- a/contracts/potlock/interact-rs/Cargo.toml +++ b/contracts/potlock/interact-rs/Cargo.toml @@ -16,10 +16,10 @@ toml = "0.8.6" path = ".." [dependencies.multiversx-sc-snippets] -version = "0.52.1" +version = "0.52.3" [dependencies.multiversx-sc] -version = "0.52.1" +version = "0.52.3" [dependencies.clap] version = "4.4.7" diff --git a/contracts/potlock/meta/Cargo.toml b/contracts/potlock/meta/Cargo.toml index 0541c340..0ac1e448 100644 --- a/contracts/potlock/meta/Cargo.toml +++ b/contracts/potlock/meta/Cargo.toml @@ -8,5 +8,5 @@ publish = false path = ".." [dependencies.multiversx-sc-meta-lib] -version = "0.52.1" +version = "0.52.3" default-features = false diff --git a/contracts/potlock/wasm/Cargo.lock b/contracts/potlock/wasm/Cargo.lock index e9f3674e..4732b4c6 100644 --- a/contracts/potlock/wasm/Cargo.lock +++ b/contracts/potlock/wasm/Cargo.lock @@ -40,9 +40,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "multiversx-sc" -version = "0.52.1" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1c90e0079028040f6219494a66a5fec724ab1e4b4b836e06cc899b5fe9f2f9" +checksum = "526760b1d6236c011285b264a70a0a9dd3b3dbc53c3b5f76932f4bcfd3a8910c" dependencies = [ "bitflags", "hex-literal", @@ -77,9 +77,9 @@ dependencies = [ [[package]] name = "multiversx-sc-derive" -version = "0.52.1" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da247945d1700c84859f935214e49ec4d781fbebc483fadc715291e49801cae" +checksum = "3557f2f12640a8a07fa6af66cc2a13b188c5b61bed72db22fe631fb3a60c3e96" dependencies = [ "hex", "proc-macro2", @@ -90,18 +90,18 @@ dependencies = [ [[package]] name = "multiversx-sc-modules" -version = "0.52.1" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dec3bd3e6ea44263db7cc5bef2d16d0626c5dc1e18a1923843459ba7896ded2" +checksum = "61f5c29c6044f3dc9e866858feee625d7fae5604a68ac7bd66dec683eee97563" dependencies = [ "multiversx-sc", ] [[package]] name = "multiversx-sc-wasm-adapter" -version = "0.52.1" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b81cacdea364a46a8b617c0917aa33d1b472018144bd04a3bd03e19e23423469" +checksum = "ed13aaca9cbdbc6911174cd3029e750a7563d85dd3daaa1107b1fd31c7f17245" dependencies = [ "multiversx-sc", ] diff --git a/contracts/potlock/wasm/Cargo.toml b/contracts/potlock/wasm/Cargo.toml index f5d97930..0ead938f 100644 --- a/contracts/potlock/wasm/Cargo.toml +++ b/contracts/potlock/wasm/Cargo.toml @@ -28,7 +28,7 @@ panic = "abort" path = ".." [dependencies.multiversx-sc-wasm-adapter] -version = "0.52.1" +version = "0.52.3" [workspace] members = ["."] From f406d80ab97ee3e197bc18575f3fe3d2948888ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Fri, 16 Aug 2024 14:38:23 +0300 Subject: [PATCH 23/38] Fix README --- contracts/potlock/README.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/contracts/potlock/README.md b/contracts/potlock/README.md index aada8d69..f13a60fb 100644 --- a/contracts/potlock/README.md +++ b/contracts/potlock/README.md @@ -1,7 +1,27 @@ -# Paymaster SC +# Potlock SC ## Overview +Potlock is a smart contract designed to enhance and accelerate Public Goods Funding (PGF) by leveraging the power of blockchain technology. Inspired by the collaboration between Potlock and the NEAR Foundation, this contract aims to provide an efficient and transparent mechanism for funding public goods, encouraging community participation, and maximizing the impact of pooled resources. +## Features +* **Public Goods Funding (PGF)**: Facilitates the pooling of resources for funding projects that benefit the public. +* **Transparent Allocation**: Ensures that funds are distributed transparently according to predefined rules. +* **Decentralized Governance:** Empowers the community to participate in decision-making processes related to fund allocation. +* **Efficient Fund Management**: Optimizes the management and distribution of funds to maximize impact. -## Implementation + +## How It Works + +2. **Activation**: +* A POT is defined, where backers and the foundation contribute tokens. +* Anyone can suggest a new POT by paying a fee, but the proposal must be accepted by the admin for it to be activated. +2. **Application and Review**: +* Projects submit their applications to the POT, aiming to secure funding. +* An authority reviews these submissions, evaluating their eligibility and potential impact. +3. **Donation and Matching**: +* Approved projects receive direct contributions from verified donors. +* These donations are further amplified by the POT's value through the Quadratic Funding (QF) model, maximizing the impact of smaller contributions. +4. **Payout**: +* Funds are distributed to the projects based on the matching model. +* After a cooldown period, the process can start again from step 1, with a new or existing POT. \ No newline at end of file From d7c4857074a85acba190467b71d91691e45dae57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 19 Aug 2024 14:05:49 +0300 Subject: [PATCH 24/38] Fixes after review --- .../interact-rs/src/potlock_interactor_main.rs | 18 ++++++++++++++++++ contracts/potlock/src/potlock.rs | 1 - .../potlock/src/potlock_admin_interactions.rs | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index d8d53063..94b69fb8 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -46,6 +46,7 @@ async fn main() { "addAdmin" => interact.add_admin().await, "removeAdmin" => interact.remove_admin().await, "getAdmins" => interact.admins().await, + "getPotlocks" => interact.get_potlocks().await, _ => panic!("unknown command: {}", &cmd), } } @@ -515,4 +516,21 @@ impl ContractInteract { println!("Result: {result_value:?}"); } + + async fn get_potlocks(&mut self) { + let result_value = self + .interactor + .query() + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .potlocks() + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + for pot in result_value.iter() { + println!("Result: {}", pot.name); + } + } } diff --git a/contracts/potlock/src/potlock.rs b/contracts/potlock/src/potlock.rs index ea51a588..6ea6a936 100644 --- a/contracts/potlock/src/potlock.rs +++ b/contracts/potlock/src/potlock.rs @@ -6,7 +6,6 @@ pub mod potlock_admin_interactions; pub mod potlock_interactions; pub mod potlock_storage; -/// An empty contract. To be used as a template when starting a new contract from scratch. #[multiversx_sc::contract] pub trait Potlock: potlock_admin_interactions::PotlockAdminInteractions diff --git a/contracts/potlock/src/potlock_admin_interactions.rs b/contracts/potlock/src/potlock_admin_interactions.rs index 42a30df3..3c06b251 100644 --- a/contracts/potlock/src/potlock_admin_interactions.rs +++ b/contracts/potlock/src/potlock_admin_interactions.rs @@ -18,7 +18,7 @@ pub trait PotlockAdminInteractions: "Invalid token provided" ); require!( - token_identifier.is_valid_esdt_identifier() && fee.ge(&BigUint::zero()), + token_identifier.is_valid_esdt_identifier() && fee.gt(&BigUint::zero()), "Invalid token identifier or amount is 0" ); self.fee_token_identifier().set(&token_identifier); From 906e146c540b2165ed1ee0118e2f93114213fd96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 19 Aug 2024 15:35:30 +0300 Subject: [PATCH 25/38] Fixes after review --- .../src/potlock_interactor_main.rs | 16 ----- contracts/potlock/interact-rs/src/proxy.rs | 9 --- contracts/potlock/src/potlock.rs | 2 + .../potlock/src/potlock_admin_interactions.rs | 57 ++++++++++----- contracts/potlock/src/potlock_interactions.rs | 35 +++++++--- contracts/potlock/src/potlock_requirements.rs | 69 +++++++++++++++++++ contracts/potlock/src/potlock_storage.rs | 58 +--------------- contracts/potlock/tests/potlock_proxy.rs | 9 --- contracts/potlock/wasm/src/lib.rs | 2 +- 9 files changed, 136 insertions(+), 121 deletions(-) create mode 100644 contracts/potlock/src/potlock_requirements.rs diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index 94b69fb8..69f02b79 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -39,7 +39,6 @@ async fn main() { "getFeeTokenIdentifier" => interact.fee_token_identifier().await, "getFeeAmount" => interact.fee_amount().await, "feePotPayments" => interact.fee_pot_proposer().await, - "feeAmountAcceptPots" => interact.fee_amount_accepted_pots().await, "potDonations" => interact.pot_donations().await, "projectDonations" => interact.project_donations().await, "isAdmin" => interact.is_admin().await, @@ -400,21 +399,6 @@ impl ContractInteract { println!("Result: {result_value:?}"); } - async fn fee_amount_accepted_pots(&mut self) { - let result_value = self - .interactor - .query() - .to(self.state.current_address()) - .typed(proxy::PotlockProxy) - .fee_amount_accepted_pots() - .returns(ReturnsResultUnmanaged) - .prepare_async() - .run() - .await; - - println!("Result: {result_value:?}"); - } - async fn pot_donations(&mut self) { let project_id = 0u32; diff --git a/contracts/potlock/interact-rs/src/proxy.rs b/contracts/potlock/interact-rs/src/proxy.rs index df8acf2a..7f85577e 100644 --- a/contracts/potlock/interact-rs/src/proxy.rs +++ b/contracts/potlock/interact-rs/src/proxy.rs @@ -279,15 +279,6 @@ where .original_result() } - pub fn fee_amount_accepted_pots( - self, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("feeAmountAcceptPots") - .original_result() - } - pub fn pot_donations< Arg0: ProxyArg, >( diff --git a/contracts/potlock/src/potlock.rs b/contracts/potlock/src/potlock.rs index 6ea6a936..6d0502d4 100644 --- a/contracts/potlock/src/potlock.rs +++ b/contracts/potlock/src/potlock.rs @@ -4,12 +4,14 @@ multiversx_sc::imports!(); multiversx_sc::derive_imports!(); pub mod potlock_admin_interactions; pub mod potlock_interactions; +pub mod potlock_requirements; pub mod potlock_storage; #[multiversx_sc::contract] pub trait Potlock: potlock_admin_interactions::PotlockAdminInteractions + potlock_interactions::PotlockInteractions + + potlock_requirements::PotlockRequirements + potlock_storage::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule { diff --git a/contracts/potlock/src/potlock_admin_interactions.rs b/contracts/potlock/src/potlock_admin_interactions.rs index 3c06b251..02fd86a0 100644 --- a/contracts/potlock/src/potlock_admin_interactions.rs +++ b/contracts/potlock/src/potlock_admin_interactions.rs @@ -1,14 +1,18 @@ -use crate::potlock_storage::{self, PotlockId, ProjectId, Status}; +use crate::{ + potlock_requirements::{self, MAX_PERCENTAGE}, + potlock_storage::{self, PotlockId, ProjectId, Status}, +}; multiversx_sc::imports!(); multiversx_sc::derive_imports!(); pub type ProjectPercentage = MultiValue2; -pub const MAX_PERCENTAGE: u64 = 10_000; // 100% #[multiversx_sc::module] pub trait PotlockAdminInteractions: - potlock_storage::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule + potlock_requirements::PotlockRequirements + + potlock_storage::PotlockStorage + + multiversx_sc_modules::only_admin::OnlyAdminModule { #[only_admin] #[endpoint(changeFeeForPots)] @@ -17,22 +21,17 @@ pub trait PotlockAdminInteractions: token_identifier.is_valid_esdt_identifier(), "Invalid token provided" ); - require!( - token_identifier.is_valid_esdt_identifier() && fee.gt(&BigUint::zero()), - "Invalid token identifier or amount is 0" - ); + require!(fee > 0, "Amount is 0"); self.fee_token_identifier().set(&token_identifier); - self.fee_amount().set(fee); + self.fee_amount().set_if_empty(fee); } #[only_admin] #[endpoint(acceptPot)] fn accept_pot(&self, potlock_id: PotlockId) { self.require_potlock_exists(potlock_id); - let fee_amount = self.fee_amount().get(); + self.require_potlock_is_inactive(potlock_id); - self.fee_amount_accepted_pots() - .update(|amount| *amount += fee_amount); let mut accepted_potlock = self.potlocks().get(potlock_id); accepted_potlock.status = Status::Active; self.potlocks().set(potlock_id, &accepted_potlock); @@ -42,6 +41,7 @@ pub trait PotlockAdminInteractions: #[endpoint(removePot)] fn remove_pot(&self, potlock_id: PotlockId) { self.require_potlock_exists(potlock_id); + self.require_potlock_is_inactive(potlock_id); let potlock_mapper = self.potlocks(); let pot_proposer = potlock_mapper.get(potlock_id).proposer; @@ -60,22 +60,35 @@ pub trait PotlockAdminInteractions: #[endpoint(acceptApplication)] fn accept_application(&self, project_id: ProjectId) { self.require_project_exists(project_id); - let mut accepted_projects = self.projects().get(project_id); - accepted_projects.status = Status::Active; - self.projects().set(project_id, &accepted_projects); + self.require_project_is_inactive(project_id); + + let mut accepted_project = self.projects().get(project_id); + accepted_project.status = Status::Active; + self.projects().set(project_id, &accepted_project); + } + + #[only_admin] + #[endpoint(removeApplication)] + fn remove_application(&self, project_id: ProjectId) { + self.require_project_exists(project_id); + self.require_project_is_active(project_id); + + let mut rejected_project = self.projects().get(project_id); + rejected_project.status = Status::Inactive; + self.projects().set(project_id, &rejected_project); } #[only_admin] #[endpoint(rejectDonation)] fn reject_donation(&self, potlock_id: PotlockId, user: ManagedAddress) { self.require_potlock_exists(potlock_id); - let opt_fee_pot_payments = self.pot_donations(potlock_id).get(&user); + let opt_fee_pot_payment = self.pot_donations(potlock_id).get(&user); - require!(opt_fee_pot_payments.is_some(), "No donation for this user"); - let fee_pot_payments = unsafe { opt_fee_pot_payments.unwrap_unchecked() }; + require!(opt_fee_pot_payment.is_some(), "No donation for this user"); + let fee_pot_payment = unsafe { opt_fee_pot_payment.unwrap_unchecked() }; self.send() - .direct_non_zero_esdt_payment(&user, &fee_pot_payments); + .direct_non_zero_esdt_payment(&user, &fee_pot_payment); self.pot_donations(potlock_id).remove(&user); } @@ -89,9 +102,17 @@ pub trait PotlockAdminInteractions: self.require_potlock_exists(potlock_id); self.require_correct_percentages(project_percentages.clone()); let pot_donations = self.pot_donations(potlock_id); + let all_projects = self.projects(); for pp in project_percentages { let (project_id, percentage) = pp.into_tuple(); + let project = all_projects.get(project_id); + + // The project must previously apply to this Pot + if project.potlock_id != potlock_id { + continue; + } + let mut output_payments = ManagedVec::new(); for (_, donation) in pot_donations.iter() { let project_share_amount = donation.amount * percentage / MAX_PERCENTAGE; diff --git a/contracts/potlock/src/potlock_interactions.rs b/contracts/potlock/src/potlock_interactions.rs index 3873f007..9634eb11 100644 --- a/contracts/potlock/src/potlock_interactions.rs +++ b/contracts/potlock/src/potlock_interactions.rs @@ -1,3 +1,4 @@ +use crate::potlock_requirements; use crate::potlock_storage::{self, Pot, Project}; use crate::potlock_storage::{PotlockId, ProjectId}; @@ -6,7 +7,9 @@ multiversx_sc::derive_imports!(); #[multiversx_sc::module] pub trait PotlockInteractions: - potlock_storage::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule + potlock_requirements::PotlockRequirements + + potlock_storage::PotlockStorage + + multiversx_sc_modules::only_admin::OnlyAdminModule { #[payable("*")] #[endpoint(addPot)] @@ -33,21 +36,32 @@ pub trait PotlockInteractions: potlock_id: PotlockId, project_name: ManagedBuffer, description: ManagedBuffer, - ) { - let project_id = self.projects().len() + 1; + ) -> usize { let owner = self.blockchain().get_caller(); - let project = Project::new(project_id, potlock_id, project_name, description, owner); - self.projects().push(&project); + let project = Project::new(potlock_id, project_name, description, owner); + self.projects().push(&project) } #[payable("*")] #[endpoint(donateToPot)] fn donate_to_pot(&self, potlock_id: PotlockId) { - let payment = self.call_value().single_esdt(); - let caller = self.blockchain().get_caller(); self.require_potlock_exists(potlock_id); self.require_potlock_is_active(potlock_id); - self.pot_donations(potlock_id).insert(caller, payment); + + let payment = self.call_value().single_esdt(); + let caller = self.blockchain().get_caller(); + let mut pot_donations = self.pot_donations(potlock_id); + + match pot_donations.get(&caller) { + Some(mut previous_payment) => { + // let a = pot_donations.get(&caller).unwrap(); + previous_payment.amount += payment.amount; + pot_donations.insert(caller, previous_payment); + } + None => { + self.pot_donations(potlock_id).insert(caller, payment); + } + } } #[payable("*")] @@ -60,7 +74,7 @@ pub trait PotlockInteractions: let mut donation_mapper = self.project_donations(project_id); if donation_mapper.contains_key(&caller) { - let opt_payment = donation_mapper.remove(&caller); + let opt_payment = donation_mapper.get(&caller); if opt_payment.is_some() { let mut previous_payment = opt_payment.unwrap(); require!( @@ -68,8 +82,7 @@ pub trait PotlockInteractions: "Already made a payment with a different TokenID" ); previous_payment.amount += payment.amount; - self.project_donations(project_id) - .insert(caller, previous_payment); + donation_mapper.insert(caller, previous_payment); } } else { self.project_donations(project_id).insert(caller, payment); diff --git a/contracts/potlock/src/potlock_requirements.rs b/contracts/potlock/src/potlock_requirements.rs new file mode 100644 index 00000000..f923672a --- /dev/null +++ b/contracts/potlock/src/potlock_requirements.rs @@ -0,0 +1,69 @@ +use crate::{ + potlock_admin_interactions::ProjectPercentage, + potlock_storage::{self, PotlockId, ProjectId, Status}, +}; + +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +pub const MAX_PERCENTAGE: u64 = 10_000; // 100% + +#[multiversx_sc::module] +pub trait PotlockRequirements: potlock_storage::PotlockStorage { + fn is_valid_potlock_id(&self, potlock_id: PotlockId) -> bool { + potlock_id >= 1 && potlock_id <= self.potlocks().len() + } + + fn require_potlock_exists(&self, potlock_id: PotlockId) { + require!( + self.is_valid_potlock_id(potlock_id) && !self.potlocks().item_is_empty(potlock_id), + "Potlock doesn't exist!", + ) + } + + fn require_potlock_is_active(&self, potlock_id: PotlockId) { + let potlock = self.potlocks().get(potlock_id); + require!(potlock.status == Status::Active, "Pot is not active!",) + } + + fn require_potlock_is_inactive(&self, potlock_id: PotlockId) { + let potlock = self.potlocks().get(potlock_id); + require!(potlock.status != Status::Active, "Pot is active!",) + } + + fn is_valid_project_id(&self, project_id: ProjectId) -> bool { + project_id >= 1 && project_id <= self.projects().len() + } + + fn require_project_exists(&self, project_id: ProjectId) { + require!( + self.is_valid_project_id(project_id) && !self.projects().item_is_empty(project_id), + "Project doesn't exist!", + ) + } + + fn require_project_is_active(&self, project_id: ProjectId) { + let project = self.projects().get(project_id); + require!(project.status == Status::Active, "Project is not active!",) + } + + fn require_project_is_inactive(&self, project_id: ProjectId) { + let project = self.projects().get(project_id); + require!(project.status != Status::Active, "Project is active!",) + } + + fn require_correct_percentages( + &self, + project_percentages: MultiValueEncoded, + ) { + let mut total_perc: u64 = 0; + for pp in project_percentages { + let (_, perc) = pp.into_tuple(); + total_perc += perc; + } + require!( + total_perc <= MAX_PERCENTAGE, + "Total percentages more than 100%" + ); + } +} diff --git a/contracts/potlock/src/potlock_storage.rs b/contracts/potlock/src/potlock_storage.rs index 676bce9f..19b71bdf 100644 --- a/contracts/potlock/src/potlock_storage.rs +++ b/contracts/potlock/src/potlock_storage.rs @@ -1,5 +1,3 @@ -use crate::potlock_admin_interactions::{ProjectPercentage, MAX_PERCENTAGE}; - multiversx_sc::imports!(); multiversx_sc::derive_imports!(); @@ -44,7 +42,6 @@ impl Pot { #[derive(TypeAbi, NestedEncode, NestedDecode, PartialEq, Debug, TopEncode, TopDecode)] pub struct Project { - pub project_id: ProjectId, pub potlock_id: PotlockId, pub name: ManagedBuffer, pub description: ManagedBuffer, @@ -54,14 +51,12 @@ pub struct Project { impl Project { pub fn new( - project_id: ProjectId, potlock_id: PotlockId, name: ManagedBuffer, description: ManagedBuffer, owner: ManagedAddress, ) -> Self { Project { - project_id, potlock_id, name, description, @@ -79,53 +74,6 @@ pub struct UserDonations { #[multiversx_sc::module] pub trait PotlockStorage { - fn is_valid_potlock_id(&self, potlock_id: PotlockId) -> bool { - potlock_id >= 1 && potlock_id <= self.potlocks().len() - } - - fn require_potlock_exists(&self, potlock_id: PotlockId) { - require!( - self.is_valid_potlock_id(potlock_id) && !self.potlocks().item_is_empty(potlock_id), - "Potlock doesn't exist!", - ) - } - - fn require_potlock_is_active(&self, potlock_id: PotlockId) { - let potlock = self.potlocks().get(potlock_id); - require!(potlock.status == Status::Active, "Pot is not active!",) - } - - fn is_valid_project_id(&self, project_id: ProjectId) -> bool { - project_id >= 1 && project_id <= self.projects().len() - } - - fn require_project_exists(&self, project_id: ProjectId) { - require!( - self.is_valid_project_id(project_id) && !self.projects().item_is_empty(project_id), - "Project doesn't exist!", - ) - } - - fn require_project_is_active(&self, project_id: ProjectId) { - let project = self.projects().get(project_id); - require!(project.status == Status::Active, "Project is not active!",) - } - - fn require_correct_percentages( - &self, - project_percentages: MultiValueEncoded, - ) { - let mut total_perc: u64 = 0; - for pp in project_percentages { - let (_, perc) = pp.into_tuple(); - total_perc += perc; - } - require!( - total_perc <= MAX_PERCENTAGE, - "Total percentages more than 100%" - ); - } - #[view(getFeeTokenIdentifier)] #[storage_mapper("feeTokenIdentifier")] fn fee_token_identifier(&self) -> SingleValueMapper; @@ -142,13 +90,9 @@ pub trait PotlockStorage { #[storage_mapper("projects")] fn projects(&self) -> VecMapper>; - #[view(feeAmountAcceptPots)] - #[storage_mapper("feeAmountAcceptedPots")] - fn fee_amount_accepted_pots(&self) -> SingleValueMapper; - #[view(potDonations)] #[storage_mapper("potDonations")] - fn pot_donations(&self, project_id: ProjectId) -> MapMapper; + fn pot_donations(&self, potlock_id: PotlockId) -> MapMapper; #[view(projectDonations)] #[storage_mapper("projectDonations")] diff --git a/contracts/potlock/tests/potlock_proxy.rs b/contracts/potlock/tests/potlock_proxy.rs index df8acf2a..7f85577e 100644 --- a/contracts/potlock/tests/potlock_proxy.rs +++ b/contracts/potlock/tests/potlock_proxy.rs @@ -279,15 +279,6 @@ where .original_result() } - pub fn fee_amount_accepted_pots( - self, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("feeAmountAcceptPots") - .original_result() - } - pub fn pot_donations< Arg0: ProxyArg, >( diff --git a/contracts/potlock/wasm/src/lib.rs b/contracts/potlock/wasm/src/lib.rs index 99faaf13..8bb6ebda 100644 --- a/contracts/potlock/wasm/src/lib.rs +++ b/contracts/potlock/wasm/src/lib.rs @@ -24,6 +24,7 @@ multiversx_sc_wasm_adapter::endpoints! { acceptPot => accept_pot removePot => remove_pot acceptApplication => accept_application + removeApplication => remove_application rejectDonation => reject_donation distributePotToProjects => distribute_pot_to_projects addPot => add_pot @@ -34,7 +35,6 @@ multiversx_sc_wasm_adapter::endpoints! { getFeeAmount => fee_amount getPotlocks => potlocks getProjects => projects - feeAmountAcceptPots => fee_amount_accepted_pots potDonations => pot_donations projectDonations => project_donations isAdmin => is_admin From c8032b065e6579b239ca2054c32d017aef44ca3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Mon, 19 Aug 2024 16:58:39 +0300 Subject: [PATCH 26/38] Fixes after review --- contracts/potlock/src/potlock_admin_interactions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/potlock/src/potlock_admin_interactions.rs b/contracts/potlock/src/potlock_admin_interactions.rs index 02fd86a0..bf38a939 100644 --- a/contracts/potlock/src/potlock_admin_interactions.rs +++ b/contracts/potlock/src/potlock_admin_interactions.rs @@ -22,7 +22,7 @@ pub trait PotlockAdminInteractions: "Invalid token provided" ); require!(fee > 0, "Amount is 0"); - self.fee_token_identifier().set(&token_identifier); + self.fee_token_identifier().set_if_empty(&token_identifier); self.fee_amount().set_if_empty(fee); } From e8cd551232fd30dc700af4b5566cb81f3bfc815a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 20 Aug 2024 09:03:55 +0300 Subject: [PATCH 27/38] Fix tests --- contracts/potlock/interact-rs/src/proxy.rs | 78 ++++++------- .../potlock/tests/potlock_blackbox_tests.rs | 109 +++++++++--------- contracts/potlock/tests/potlock_proxy.rs | 78 ++++++------- 3 files changed, 135 insertions(+), 130 deletions(-) diff --git a/contracts/potlock/interact-rs/src/proxy.rs b/contracts/potlock/interact-rs/src/proxy.rs index 7f85577e..687a6581 100644 --- a/contracts/potlock/interact-rs/src/proxy.rs +++ b/contracts/potlock/interact-rs/src/proxy.rs @@ -44,15 +44,15 @@ where Gas: TxGas, { pub fn init< - Arg0: ProxyArg>, + Arg0: ProxyArg>>, >( self, - admin: Arg0, + admins: Arg0, ) -> TxTypedDeploy { self.wrapped_tx .payment(NotPayable) .raw_deploy() - .argument(&admin) + .argument(&admins) .original_result() } } @@ -85,6 +85,22 @@ where To: TxTo, Gas: TxGas, { + pub fn change_fee_for_pots< + Arg0: ProxyArg>, + Arg1: ProxyArg>, + >( + self, + token_identifier: Arg0, + fee: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("changeFeeForPots") + .argument(&token_identifier) + .argument(&fee) + .original_result() + } + pub fn accept_pot< Arg0: ProxyArg, >( @@ -124,6 +140,19 @@ where .original_result() } + pub fn remove_application< + Arg0: ProxyArg, + >( + self, + project_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("removeApplication") + .argument(&project_id) + .original_result() + } + pub fn reject_donation< Arg0: ProxyArg, Arg1: ProxyArg>, @@ -146,13 +175,13 @@ where >( self, potlock_id: Arg0, - project_percentage: Arg1, + project_percentages: Arg1, ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) .raw_call("distributePotToProjects") .argument(&potlock_id) - .argument(&project_percentage) + .argument(&project_percentages) .original_result() } @@ -180,7 +209,7 @@ where potlock_id: Arg0, project_name: Arg1, description: Arg2, - ) -> TxTypedCall { + ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) .raw_call("applyForPot") @@ -214,22 +243,6 @@ where .original_result() } - pub fn change_fee_for_pots< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - >( - self, - token_identifier: Arg0, - fee: Arg1, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("changeFeeForPots") - .argument(&token_identifier) - .argument(&fee) - .original_result() - } - pub fn fee_token_identifier( self, ) -> TxTypedCall> { @@ -266,29 +279,16 @@ where .original_result() } - pub fn fee_pot_proposer< - Arg0: ProxyArg, - >( - self, - potlock_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("feePotPayments") - .argument(&potlock_id) - .original_result() - } - pub fn pot_donations< Arg0: ProxyArg, >( self, - project_id: Arg0, + potlock_id: Arg0, ) -> TxTypedCall, EsdtTokenPayment>>> { self.wrapped_tx .payment(NotPayable) .raw_call("potDonations") - .argument(&project_id) + .argument(&potlock_id) .original_result() } @@ -361,6 +361,7 @@ where Api: ManagedTypeApi, { pub potlock_id: usize, + pub proposer: ManagedAddress, pub token_identifier: TokenIdentifier, pub fee: BigUint, pub name: ManagedBuffer, @@ -369,7 +370,7 @@ where } #[type_abi] -#[derive(TopEncode, TopDecode, PartialEq, Eq, Debug, NestedEncode, NestedDecode)] +#[derive(TopEncode, TopDecode, NestedDecode, NestedEncode)] pub enum Status { Inactive, Active, @@ -381,7 +382,6 @@ pub struct Project where Api: ManagedTypeApi, { - pub project_id: usize, pub potlock_id: usize, pub name: ManagedBuffer, pub description: ManagedBuffer, diff --git a/contracts/potlock/tests/potlock_blackbox_tests.rs b/contracts/potlock/tests/potlock_blackbox_tests.rs index 994b8b53..ecbdf2c3 100644 --- a/contracts/potlock/tests/potlock_blackbox_tests.rs +++ b/contracts/potlock/tests/potlock_blackbox_tests.rs @@ -1,6 +1,5 @@ use multiversx_sc_scenario::{imports::*, ScenarioWorld}; use potlock::potlock_storage::{PotlockId, ProjectId}; -use potlock_proxy::Status; mod potlock_proxy; const POTLOCK_ADDRESS: TestSCAddress = TestSCAddress::new("potlock"); @@ -56,11 +55,15 @@ impl PotlockTestState { } fn deploy_potlock_contract(&mut self) -> &mut Self { + let mut admins: MultiValueEncoded> = + MultiValueEncoded::new(); + admins.push(ADMIN_ADDRESS.to_managed_address()); + self.world .tx() .from(OWNER_ADDRESS) .typed(potlock_proxy::PotlockProxy) - .init(ADMIN_ADDRESS) + .init(admins) .code(POTLOCK_CODE_PATH) .new_address(POTLOCK_ADDRESS) .run(); @@ -122,14 +125,23 @@ impl PotlockTestState { .run(); } - fn apply_for_pot(&mut self, potlock_id: PotlockId, project_name: &str, description: &str) { - self.world + fn apply_for_pot( + &mut self, + potlock_id: PotlockId, + project_name: &str, + description: &str, + ) -> usize { + let new_project_id = self + .world .tx() .from(PROJECT_PROPOSER_ADDRESS) .to(POTLOCK_ADDRESS) .typed(potlock_proxy::PotlockProxy) .apply_for_pot(potlock_id, project_name, description) + .returns(ReturnsResult) .run(); + + new_project_id } fn donate_to_pot(&mut self, potlock_id: PotlockId) { @@ -213,7 +225,7 @@ impl PotlockTestState { assert_eq!(projects.len(), project_id); } - fn check_project_is_accepted(&mut self, project_id: PotlockId) { + fn check_project_is_accepted(&mut self, project_description: ManagedBuffer) { let projects = self .world .query() @@ -222,11 +234,14 @@ impl PotlockTestState { .projects() .returns(ReturnsResult) .run(); + + let mut project_found = false; for project in projects.into_iter() { - if project.project_id == project_id { - assert_eq!(project.status, Status::Active); + if project.description == project_description { + project_found = true; } } + assert!(project_found); } } @@ -292,12 +307,11 @@ fn test_apply_for_pot() { state.add_pot("Pot", "Pot Description"); let potlock_id = 1usize; - let project_id = 1usize; - state.apply_for_pot(potlock_id, "Project name", "Project description"); + let new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); state.check_potlock_id_is_last(potlock_id); - state.check_project_id_is_last(project_id); + state.check_project_id_is_last(new_project_id); // Funds were returned to user state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); @@ -340,18 +354,17 @@ fn test_donate_to_project() { // Accept Pot state.accept_pot(potlock_id); - state.apply_for_pot(potlock_id, "Project name", "Project description"); - let project_id = 1usize; - state.check_project_id_is_last(project_id); + let new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); + state.check_project_id_is_last(new_project_id); - state.accept_application(project_id); - state.check_project_is_accepted(project_id); + state.accept_application(new_project_id); + state.check_project_is_accepted(ManagedBuffer::from("Project description")); state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); state.check_esdt_balance(PROJECT_DONOR_ADDRESS, INITIAL_BALANCE); - state.donate_to_project(project_id); + state.donate_to_project(new_project_id); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION + DONATION_AMOUNT); state.check_esdt_balance(PROJECT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); @@ -367,16 +380,14 @@ fn test_accept_application() { let potlock_id: usize = 1usize; state.check_potlock_id_is_last(potlock_id); - let project_id = 1usize; + let new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); - state.apply_for_pot(potlock_id, "Project name", "Project description"); - - state.check_project_id_is_last(project_id); + state.check_project_id_is_last(new_project_id); state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); - state.accept_application(project_id); - state.check_project_is_accepted(project_id); + state.accept_application(new_project_id); + state.check_project_is_accepted(ManagedBuffer::from("Project description")); } #[test] @@ -394,10 +405,9 @@ fn test_distribute_pot_to_projects() { state.accept_pot(potlock_id); // Add Project - let project_id = 1usize; - state.apply_for_pot(potlock_id, "Project name", "Project description"); + let new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); - state.check_project_id_is_last(project_id); + state.check_project_id_is_last(new_project_id); state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); @@ -407,12 +417,12 @@ fn test_distribute_pot_to_projects() { state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); // Accept project - state.accept_application(project_id); - state.check_project_is_accepted(project_id); + state.accept_application(new_project_id); + state.check_project_is_accepted(ManagedBuffer::from("Project description")); // Distribute Pot donations to projects let mut percentages = MultiValueVec::new(); - percentages.push((project_id, MAX_PERCENTAGE).into()); + percentages.push((new_project_id, MAX_PERCENTAGE).into()); state.distribute_pot_to_projects(potlock_id, percentages); state.check_esdt_balance(PROJECT_PROPOSER_ADDRESS, DONATION_AMOUNT); @@ -494,12 +504,11 @@ fn test_fail_accept_application() { state.add_pot("Pot", "Pot Description"); let potlock_id = 1usize; - let project_id = 1usize; - state.apply_for_pot(potlock_id, "Project name", "Project description"); + let new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); state.check_potlock_id_is_last(potlock_id); - state.check_project_id_is_last(project_id); + state.check_project_id_is_last(new_project_id); // Funds were returned to user state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); @@ -511,7 +520,7 @@ fn test_fail_accept_application() { .from(POT_PROPOSER_ADDRESS) .to(POTLOCK_ADDRESS) .typed(potlock_proxy::PotlockProxy) - .accept_application(project_id) + .accept_application(new_project_id) .with_result(ExpectError(4, "Endpoint can only be called by admins")) .run(); } @@ -526,19 +535,17 @@ fn test_fail_distribute_pot_to_projects() { let potlock_id: usize = 1usize; state.check_potlock_id_is_last(potlock_id); - let project_id = 1usize; - - state.apply_for_pot(potlock_id, "Project name", "Project description"); + let new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); - state.check_project_id_is_last(project_id); + state.check_project_id_is_last(new_project_id); state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); - state.accept_application(project_id); - state.check_project_is_accepted(project_id); + state.accept_application(new_project_id); + state.check_project_is_accepted(ManagedBuffer::from("Project description")); let mut percentages = MultiValueVec::new(); - percentages.push((project_id, HALF_PERCENTAGE).into()); + percentages.push((new_project_id, HALF_PERCENTAGE).into()); state .world .tx() @@ -565,10 +572,9 @@ fn test_fail_distribute_pot_to_projects2() { state.accept_pot(potlock_id); // Add Project - let project_id = 1usize; - state.apply_for_pot(potlock_id, "Project name", "Project description"); + let new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); - state.check_project_id_is_last(project_id); + state.check_project_id_is_last(new_project_id); state.check_esdt_balance(POT_PROPOSER_ADDRESS, POT_FEE_CREATION); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); @@ -578,12 +584,12 @@ fn test_fail_distribute_pot_to_projects2() { state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); // Accept project - state.accept_application(project_id); - state.check_project_is_accepted(project_id); + state.accept_application(new_project_id); + state.check_project_is_accepted(ManagedBuffer::from("Project description")); // Distribute Pot donations to projects let mut percentages = MultiValueVec::new(); - percentages.push((project_id, 3* HALF_PERCENTAGE).into()); + percentages.push((new_project_id, 3 * HALF_PERCENTAGE).into()); state .world .tx() @@ -608,18 +614,17 @@ fn test_fail_donate_to_project() { // Accept Pot state.accept_pot(potlock_id); - state.apply_for_pot(potlock_id, "Project name", "Project description"); - let project_id = 1usize; - state.check_project_id_is_last(project_id); + let new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); + state.check_project_id_is_last(new_project_id); - state.accept_application(project_id); - state.check_project_is_accepted(project_id); + state.accept_application(new_project_id); + state.check_project_is_accepted(ManagedBuffer::from("Project description")); state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); state.check_esdt_balance(PROJECT_DONOR_ADDRESS, INITIAL_BALANCE); - state.donate_to_project(project_id); + state.donate_to_project(new_project_id); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION + DONATION_AMOUNT); state.check_esdt_balance(PROJECT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); @@ -630,7 +635,7 @@ fn test_fail_donate_to_project() { .from(PROJECT_DONOR_ADDRESS) .to(POTLOCK_ADDRESS) .typed(potlock_proxy::PotlockProxy) - .donate_to_project(project_id) + .donate_to_project(new_project_id) .egld_or_single_esdt( &EgldOrEsdtTokenIdentifier::esdt(DIFFERENT_TOKEN_ID), 0u64, diff --git a/contracts/potlock/tests/potlock_proxy.rs b/contracts/potlock/tests/potlock_proxy.rs index 7f85577e..687a6581 100644 --- a/contracts/potlock/tests/potlock_proxy.rs +++ b/contracts/potlock/tests/potlock_proxy.rs @@ -44,15 +44,15 @@ where Gas: TxGas, { pub fn init< - Arg0: ProxyArg>, + Arg0: ProxyArg>>, >( self, - admin: Arg0, + admins: Arg0, ) -> TxTypedDeploy { self.wrapped_tx .payment(NotPayable) .raw_deploy() - .argument(&admin) + .argument(&admins) .original_result() } } @@ -85,6 +85,22 @@ where To: TxTo, Gas: TxGas, { + pub fn change_fee_for_pots< + Arg0: ProxyArg>, + Arg1: ProxyArg>, + >( + self, + token_identifier: Arg0, + fee: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("changeFeeForPots") + .argument(&token_identifier) + .argument(&fee) + .original_result() + } + pub fn accept_pot< Arg0: ProxyArg, >( @@ -124,6 +140,19 @@ where .original_result() } + pub fn remove_application< + Arg0: ProxyArg, + >( + self, + project_id: Arg0, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("removeApplication") + .argument(&project_id) + .original_result() + } + pub fn reject_donation< Arg0: ProxyArg, Arg1: ProxyArg>, @@ -146,13 +175,13 @@ where >( self, potlock_id: Arg0, - project_percentage: Arg1, + project_percentages: Arg1, ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) .raw_call("distributePotToProjects") .argument(&potlock_id) - .argument(&project_percentage) + .argument(&project_percentages) .original_result() } @@ -180,7 +209,7 @@ where potlock_id: Arg0, project_name: Arg1, description: Arg2, - ) -> TxTypedCall { + ) -> TxTypedCall { self.wrapped_tx .payment(NotPayable) .raw_call("applyForPot") @@ -214,22 +243,6 @@ where .original_result() } - pub fn change_fee_for_pots< - Arg0: ProxyArg>, - Arg1: ProxyArg>, - >( - self, - token_identifier: Arg0, - fee: Arg1, - ) -> TxTypedCall { - self.wrapped_tx - .payment(NotPayable) - .raw_call("changeFeeForPots") - .argument(&token_identifier) - .argument(&fee) - .original_result() - } - pub fn fee_token_identifier( self, ) -> TxTypedCall> { @@ -266,29 +279,16 @@ where .original_result() } - pub fn fee_pot_proposer< - Arg0: ProxyArg, - >( - self, - potlock_id: Arg0, - ) -> TxTypedCall> { - self.wrapped_tx - .payment(NotPayable) - .raw_call("feePotPayments") - .argument(&potlock_id) - .original_result() - } - pub fn pot_donations< Arg0: ProxyArg, >( self, - project_id: Arg0, + potlock_id: Arg0, ) -> TxTypedCall, EsdtTokenPayment>>> { self.wrapped_tx .payment(NotPayable) .raw_call("potDonations") - .argument(&project_id) + .argument(&potlock_id) .original_result() } @@ -361,6 +361,7 @@ where Api: ManagedTypeApi, { pub potlock_id: usize, + pub proposer: ManagedAddress, pub token_identifier: TokenIdentifier, pub fee: BigUint, pub name: ManagedBuffer, @@ -369,7 +370,7 @@ where } #[type_abi] -#[derive(TopEncode, TopDecode, PartialEq, Eq, Debug, NestedEncode, NestedDecode)] +#[derive(TopEncode, TopDecode, NestedDecode, NestedEncode)] pub enum Status { Inactive, Active, @@ -381,7 +382,6 @@ pub struct Project where Api: ManagedTypeApi, { - pub project_id: usize, pub potlock_id: usize, pub name: ManagedBuffer, pub description: ManagedBuffer, From 75f9f47a9014745304e28aa275490511bb4e5a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 21 Aug 2024 12:14:11 +0300 Subject: [PATCH 28/38] Fix compiling issues --- .../src/potlock_interactor_main.rs | 229 +++++++++++------- contracts/potlock/interact-rs/src/proxy.rs | 6 +- 2 files changed, 140 insertions(+), 95 deletions(-) diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index 69f02b79..85eba287 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -1,20 +1,17 @@ #![allow(non_snake_case)] -mod potlock_interactor_config; mod proxy; use multiversx_sc_snippets::imports::*; +use multiversx_sc_snippets::sdk; use serde::{Deserialize, Serialize}; use std::{ io::{Read, Write}, path::Path, }; +const GATEWAY: &str = sdk::gateway::DEVNET_GATEWAY; const STATE_FILE: &str = "state.toml"; -const TOKEN_ID: &str = "WEGLD-a28c59"; -const FEE_AMOUNT: u64 = 50000000000000000; // 0.5 - -use potlock_interactor_config::Config; #[tokio::main] async fn main() { @@ -26,26 +23,28 @@ async fn main() { let mut interact = ContractInteract::new().await; match cmd.as_str() { "deploy" => interact.deploy().await, + "upgrade" => interact.upgrade().await, + "changeFeeForPots" => interact.change_fee_for_pots().await, "acceptPot" => interact.accept_pot().await, "removePot" => interact.remove_pot().await, "acceptApplication" => interact.accept_application().await, + "removeApplication" => interact.remove_application().await, "rejectDonation" => interact.reject_donation().await, "distributePotToProjects" => interact.distribute_pot_to_projects().await, "addPot" => interact.add_pot().await, "applyForPot" => interact.apply_for_pot().await, "donateToPot" => interact.donate_to_pot().await, "donateToProject" => interact.donate_to_project().await, - "changeFeeForPots" => interact.change_fee_for_pots().await, "getFeeTokenIdentifier" => interact.fee_token_identifier().await, "getFeeAmount" => interact.fee_amount().await, - "feePotPayments" => interact.fee_pot_proposer().await, + "getPotlocks" => interact.potlocks().await, + "getProjects" => interact.projects().await, "potDonations" => interact.pot_donations().await, "projectDonations" => interact.project_donations().await, "isAdmin" => interact.is_admin().await, "addAdmin" => interact.add_admin().await, "removeAdmin" => interact.remove_admin().await, "getAdmins" => interact.admins().await, - "getPotlocks" => interact.get_potlocks().await, _ => panic!("unknown command: {}", &cmd), } } @@ -95,13 +94,11 @@ struct ContractInteract { wallet_address: Address, contract_code: BytesValue, state: State, - config: Config, } impl ContractInteract { async fn new() -> Self { - let config = Config::load_config(); - let mut interactor = Interactor::new(config.gateway()).await; + let mut interactor = Interactor::new(GATEWAY).await; let wallet_address = interactor.register_wallet(test_wallets::alice()); let contract_code = BytesValue::interpret_from( @@ -114,21 +111,20 @@ impl ContractInteract { wallet_address, contract_code, state: State::load_state(), - config: Config::load_config(), } } async fn deploy(&mut self) { - let admin = &self.config.admin; + let admins = MultiValueVec::from(vec![bech32::decode("")]); let new_address = self .interactor .tx() .from(&self.wallet_address) + .gas(30_000_000u64) .typed(proxy::PotlockProxy) - .init(admin) + .init(admins) .code(&self.contract_code) - .gas(50_000_000) .returns(ReturnsNewAddress) .prepare_async() .run() @@ -141,15 +137,54 @@ impl ContractInteract { println!("new address: {new_address_bech32}"); } + async fn upgrade(&mut self) { + let response = self + .interactor + .tx() + .to(self.state.current_address()) + .from(&self.wallet_address) + .gas(30_000_000u64) + .typed(proxy::PotlockProxy) + .upgrade() + .code(&self.contract_code) + .code_metadata(CodeMetadata::UPGRADEABLE) + .returns(ReturnsNewAddress) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn change_fee_for_pots(&mut self) { + let token_identifier = TokenIdentifier::from_esdt_bytes(&b""[..]); + let fee = BigUint::::from(0u128); + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .gas(30_000_000u64) + .typed(proxy::PotlockProxy) + .change_fee_for_pots(token_identifier, fee) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + async fn accept_pot(&mut self) { - let admin = &self.config.admin; - let potlock_id = 1u32; + let potlock_id = 0u32; let response = self .interactor .tx() - .from(admin) + .from(&self.wallet_address) .to(self.state.current_address()) + .gas(30_000_000u64) .typed(proxy::PotlockProxy) .accept_pot(potlock_id) .returns(ReturnsResultUnmanaged) @@ -161,14 +196,14 @@ impl ContractInteract { } async fn remove_pot(&mut self) { - let admin = &self.config.admin; let potlock_id = 0u32; let response = self .interactor .tx() - .from(admin) + .from(&self.wallet_address) .to(self.state.current_address()) + .gas(30_000_000u64) .typed(proxy::PotlockProxy) .remove_pot(potlock_id) .returns(ReturnsResultUnmanaged) @@ -180,14 +215,14 @@ impl ContractInteract { } async fn accept_application(&mut self) { - let admin: &Bech32Address = &self.config.admin; - let project_id = 1u32; + let project_id = 0u32; let response = self .interactor .tx() - .from(admin) + .from(&self.wallet_address) .to(self.state.current_address()) + .gas(30_000_000u64) .typed(proxy::PotlockProxy) .accept_application(project_id) .returns(ReturnsResultUnmanaged) @@ -198,16 +233,35 @@ impl ContractInteract { println!("Result: {response:?}"); } + async fn remove_application(&mut self) { + let project_id = 0u32; + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .gas(30_000_000u64) + .typed(proxy::PotlockProxy) + .remove_application(project_id) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + async fn reject_donation(&mut self) { - let admin: &Bech32Address = &self.config.admin; - let user = &self.config.pot_donor; let potlock_id = 0u32; + let user = bech32::decode(""); let response = self .interactor .tx() - .from(admin) + .from(&self.wallet_address) .to(self.state.current_address()) + .gas(30_000_000u64) .typed(proxy::PotlockProxy) .reject_donation(potlock_id, user) .returns(ReturnsResultUnmanaged) @@ -219,17 +273,18 @@ impl ContractInteract { } async fn distribute_pot_to_projects(&mut self) { - let admin: &Bech32Address = &self.config.admin; - let potlock_id = 1u32; - let project_percentage = MultiValueVec::from(vec![MultiValue2::from((1u32, 10_000u64))]); + let potlock_id = 0u32; + let project_percentages = + MultiValueVec::from(vec![MultiValue2::::from((0u32, 0u64))]); let response = self .interactor .tx() - .from(admin) + .from(&self.wallet_address) .to(self.state.current_address()) + .gas(30_000_000u64) .typed(proxy::PotlockProxy) - .distribute_pot_to_projects(potlock_id, project_percentage) + .distribute_pot_to_projects(potlock_id, project_percentages) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -239,22 +294,26 @@ impl ContractInteract { } async fn add_pot(&mut self) { - let pot_proposer: &Bech32Address = &self.config.pot_proposer; - let token_id = TokenIdentifier::from_esdt_bytes(TOKEN_ID); + let token_id = String::new(); let token_nonce = 0u64; - let token_amount = BigUint::::from(FEE_AMOUNT); + let token_amount = BigUint::::from(0u128); - let description = ManagedBuffer::new_from_bytes(b"Pot used for testing"); - let name = ManagedBuffer::new_from_bytes(b"My Pot"); + let name = ManagedBuffer::new_from_bytes(&b""[..]); + let description = ManagedBuffer::new_from_bytes(&b""[..]); let response = self .interactor .tx() - .from(pot_proposer) + .from(&self.wallet_address) .to(self.state.current_address()) + .gas(30_000_000u64) .typed(proxy::PotlockProxy) .add_pot(name, description) - .payment((token_id, token_nonce, token_amount)) + .payment(( + TokenIdentifier::from(token_id.as_str()), + token_nonce, + token_amount, + )) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -264,16 +323,16 @@ impl ContractInteract { } async fn apply_for_pot(&mut self) { - let project_proposer: &Bech32Address = &self.config.project_proposer; - let potlock_id = 1u32; - let project_name = ManagedBuffer::new_from_bytes(b"New Testing Project"); - let description = ManagedBuffer::new_from_bytes(b"Project used for testing"); + let potlock_id = 0u32; + let project_name = ManagedBuffer::new_from_bytes(&b""[..]); + let description = ManagedBuffer::new_from_bytes(&b""[..]); let response = self .interactor .tx() - .from(project_proposer) + .from(&self.wallet_address) .to(self.state.current_address()) + .gas(30_000_000u64) .typed(proxy::PotlockProxy) .apply_for_pot(potlock_id, project_name, description) .returns(ReturnsResultUnmanaged) @@ -285,21 +344,25 @@ impl ContractInteract { } async fn donate_to_pot(&mut self) { - let pot_donor: &Bech32Address = &self.config.pot_donor; - let token_id = TokenIdentifier::from_esdt_bytes(TOKEN_ID); + let token_id = String::new(); let token_nonce = 0u64; - let token_amount = BigUint::::from(3 * FEE_AMOUNT); + let token_amount = BigUint::::from(0u128); - let potlock_id = 1u32; + let potlock_id = 0u32; let response = self .interactor .tx() - .from(pot_donor) + .from(&self.wallet_address) .to(self.state.current_address()) + .gas(30_000_000u64) .typed(proxy::PotlockProxy) .donate_to_pot(potlock_id) - .payment((token_id, token_nonce, token_amount)) + .payment(( + TokenIdentifier::from(token_id.as_str()), + token_nonce, + token_amount, + )) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -309,21 +372,25 @@ impl ContractInteract { } async fn donate_to_project(&mut self) { - let project_donor: &Bech32Address = &self.config.project_donor; - let token_id = TokenIdentifier::from_esdt_bytes(TOKEN_ID); + let token_id = String::new(); let token_nonce = 0u64; - let token_amount = BigUint::::from(3 * FEE_AMOUNT); + let token_amount = BigUint::::from(0u128); - let project_id = 1u32; + let project_id = 0u32; let response = self .interactor .tx() - .from(project_donor) + .from(&self.wallet_address) .to(self.state.current_address()) + .gas(30_000_000u64) .typed(proxy::PotlockProxy) .donate_to_project(project_id) - .payment((token_id, token_nonce, token_amount)) + .payment(( + TokenIdentifier::from(token_id.as_str()), + token_nonce, + token_amount, + )) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -332,33 +399,28 @@ impl ContractInteract { println!("Result: {response:?}"); } - async fn change_fee_for_pots(&mut self) { - let admin: &Bech32Address = &self.config.admin; - let token_identifier = TokenIdentifier::from_esdt_bytes(TOKEN_ID); - let fee = BigUint::::from(FEE_AMOUNT); - - let response = self + async fn fee_token_identifier(&mut self) { + let result_value = self .interactor - .tx() - .from(admin) + .query() .to(self.state.current_address()) .typed(proxy::PotlockProxy) - .change_fee_for_pots(token_identifier, fee) + .fee_token_identifier() .returns(ReturnsResultUnmanaged) .prepare_async() .run() .await; - println!("Result: {response:?}"); + println!("Result: {result_value:?}"); } - async fn fee_token_identifier(&mut self) { + async fn fee_amount(&mut self) { let result_value = self .interactor .query() .to(self.state.current_address()) .typed(proxy::PotlockProxy) - .fee_token_identifier() + .fee_amount() .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -367,13 +429,13 @@ impl ContractInteract { println!("Result: {result_value:?}"); } - async fn fee_amount(&mut self) { + async fn potlocks(&mut self) { let result_value = self .interactor .query() .to(self.state.current_address()) .typed(proxy::PotlockProxy) - .fee_amount() + .potlocks() .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -382,15 +444,13 @@ impl ContractInteract { println!("Result: {result_value:?}"); } - async fn fee_pot_proposer(&mut self) { - let potlock_id = 0u32; - + async fn projects(&mut self) { let result_value = self .interactor .query() .to(self.state.current_address()) .typed(proxy::PotlockProxy) - .fee_pot_proposer(potlock_id) + .projects() .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -400,14 +460,14 @@ impl ContractInteract { } async fn pot_donations(&mut self) { - let project_id = 0u32; + let potlock_id = 0u32; let result_value = self .interactor .query() .to(self.state.current_address()) .typed(proxy::PotlockProxy) - .pot_donations(project_id) + .pot_donations(potlock_id) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -458,6 +518,7 @@ impl ContractInteract { .tx() .from(&self.wallet_address) .to(self.state.current_address()) + .gas(30_000_000u64) .typed(proxy::PotlockProxy) .add_admin(address) .returns(ReturnsResultUnmanaged) @@ -476,6 +537,7 @@ impl ContractInteract { .tx() .from(&self.wallet_address) .to(self.state.current_address()) + .gas(30_000_000u64) .typed(proxy::PotlockProxy) .remove_admin(address) .returns(ReturnsResultUnmanaged) @@ -500,21 +562,4 @@ impl ContractInteract { println!("Result: {result_value:?}"); } - - async fn get_potlocks(&mut self) { - let result_value = self - .interactor - .query() - .to(self.state.current_address()) - .typed(proxy::PotlockProxy) - .potlocks() - .returns(ReturnsResultUnmanaged) - .prepare_async() - .run() - .await; - - for pot in result_value.iter() { - println!("Result: {}", pot.name); - } - } } diff --git a/contracts/potlock/interact-rs/src/proxy.rs b/contracts/potlock/interact-rs/src/proxy.rs index 687a6581..7abb2669 100644 --- a/contracts/potlock/interact-rs/src/proxy.rs +++ b/contracts/potlock/interact-rs/src/proxy.rs @@ -355,7 +355,7 @@ where } #[type_abi] -#[derive(TopEncode, TopDecode)] +#[derive(TopEncode, TopDecode, Debug)] pub struct Pot where Api: ManagedTypeApi, @@ -370,14 +370,14 @@ where } #[type_abi] -#[derive(TopEncode, TopDecode, NestedDecode, NestedEncode)] +#[derive(TopEncode, TopDecode, NestedDecode, NestedEncode, Debug)] pub enum Status { Inactive, Active, } #[type_abi] -#[derive(TopEncode, TopDecode)] +#[derive(TopEncode, TopDecode, Debug)] pub struct Project where Api: ManagedTypeApi, From 8b51d5fa6fdce4aa35abb294dd3e3b172db1246d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 21 Aug 2024 12:21:06 +0300 Subject: [PATCH 29/38] potlock: more unit tests --- contracts/potlock/src/potlock_interactions.rs | 33 ++++-- .../potlock/tests/potlock_blackbox_tests.rs | 108 +++++++++++++++--- 2 files changed, 118 insertions(+), 23 deletions(-) diff --git a/contracts/potlock/src/potlock_interactions.rs b/contracts/potlock/src/potlock_interactions.rs index 9634eb11..15ebbd09 100644 --- a/contracts/potlock/src/potlock_interactions.rs +++ b/contracts/potlock/src/potlock_interactions.rs @@ -50,18 +50,33 @@ pub trait PotlockInteractions: let payment = self.call_value().single_esdt(); let caller = self.blockchain().get_caller(); - let mut pot_donations = self.pot_donations(potlock_id); + let mut donation_mapper = self.pot_donations(potlock_id); - match pot_donations.get(&caller) { - Some(mut previous_payment) => { - // let a = pot_donations.get(&caller).unwrap(); + if donation_mapper.contains_key(&caller) { + let opt_payment = donation_mapper.get(&caller); + if opt_payment.is_some() { + let mut previous_payment = opt_payment.unwrap(); + require!( + previous_payment.token_identifier == payment.token_identifier.clone(), + "Already made a payment with a different TokenID" + ); previous_payment.amount += payment.amount; - pot_donations.insert(caller, previous_payment); - } - None => { - self.pot_donations(potlock_id).insert(caller, payment); + donation_mapper.insert(caller, previous_payment); } + } else { + donation_mapper.insert(caller, payment); } + + // match donation_mapper.get(&caller) { + // Some(mut previous_payment) => { + // // let a = pot_donations.get(&caller).unwrap(); + // previous_payment.amount += payment.amount; + // pot_donations.insert(caller, previous_payment); + // } + // None => { + // self.pot_donations(potlock_id).insert(caller, payment); + // } + // } } #[payable("*")] @@ -85,7 +100,7 @@ pub trait PotlockInteractions: donation_mapper.insert(caller, previous_payment); } } else { - self.project_donations(project_id).insert(caller, payment); + donation_mapper.insert(caller, payment); } } } diff --git a/contracts/potlock/tests/potlock_blackbox_tests.rs b/contracts/potlock/tests/potlock_blackbox_tests.rs index ecbdf2c3..725e625b 100644 --- a/contracts/potlock/tests/potlock_blackbox_tests.rs +++ b/contracts/potlock/tests/potlock_blackbox_tests.rs @@ -10,7 +10,7 @@ const POT_PROPOSER_ADDRESS: TestAddress = TestAddress::new("pot_proposer"); const PROJECT_PROPOSER_ADDRESS: TestAddress = TestAddress::new("project_proposer"); const POT_DONOR_ADDRESS: TestAddress = TestAddress::new("pot_donor"); const PROJECT_DONOR_ADDRESS: TestAddress = TestAddress::new("project_donor"); -const TOKEN_ID: TestTokenIdentifier = TestTokenIdentifier::new("POT-123456"); +const POT_TOKEN_ID: TestTokenIdentifier = TestTokenIdentifier::new("POT-123456"); const DIFFERENT_TOKEN_ID: TestTokenIdentifier = TestTokenIdentifier::new("DIFFPOT-123456"); const POT_FEE_CREATION: u64 = 1_000; const INITIAL_BALANCE: u64 = 2_000; @@ -40,15 +40,16 @@ impl PotlockTestState { .nonce(1) .account(POT_PROPOSER_ADDRESS) .nonce(1) - .esdt_balance(TOKEN_ID, INITIAL_BALANCE) + .esdt_balance(POT_TOKEN_ID, INITIAL_BALANCE) .account(PROJECT_PROPOSER_ADDRESS) .nonce(1) .account(POT_DONOR_ADDRESS) .nonce(1) - .esdt_balance(TOKEN_ID, INITIAL_BALANCE) + .esdt_balance(POT_TOKEN_ID, INITIAL_BALANCE) + .esdt_balance(DIFFERENT_TOKEN_ID, INITIAL_BALANCE) .account(PROJECT_DONOR_ADDRESS) .nonce(1) - .esdt_balance(TOKEN_ID, INITIAL_BALANCE) + .esdt_balance(POT_TOKEN_ID, INITIAL_BALANCE) .esdt_balance(DIFFERENT_TOKEN_ID, INITIAL_BALANCE); Self { world } @@ -76,7 +77,10 @@ impl PotlockTestState { .from(ADMIN_ADDRESS) .to(POTLOCK_ADDRESS) .typed(potlock_proxy::PotlockProxy) - .change_fee_for_pots(TokenIdentifier::from(TOKEN_ID), BigUint::from(fee_amount)) + .change_fee_for_pots( + TokenIdentifier::from(POT_TOKEN_ID), + BigUint::from(fee_amount), + ) .run(); } @@ -88,7 +92,7 @@ impl PotlockTestState { .typed(potlock_proxy::PotlockProxy) .add_pot(name, description) .egld_or_single_esdt( - &EgldOrEsdtTokenIdentifier::esdt(TOKEN_ID), + &EgldOrEsdtTokenIdentifier::esdt(POT_TOKEN_ID), 0u64, &multiversx_sc::proxy_imports::BigUint::from(POT_FEE_CREATION), ) @@ -144,7 +148,7 @@ impl PotlockTestState { new_project_id } - fn donate_to_pot(&mut self, potlock_id: PotlockId) { + fn donate_to_pot(&mut self, potlock_id: PotlockId, donation_token: TestTokenIdentifier) { self.world .tx() .from(POT_DONOR_ADDRESS) @@ -152,7 +156,7 @@ impl PotlockTestState { .typed(potlock_proxy::PotlockProxy) .donate_to_pot(potlock_id) .egld_or_single_esdt( - &EgldOrEsdtTokenIdentifier::esdt(TOKEN_ID), + &EgldOrEsdtTokenIdentifier::esdt(donation_token), 0u64, &multiversx_sc::proxy_imports::BigUint::from(DONATION_AMOUNT), ) @@ -167,7 +171,7 @@ impl PotlockTestState { .typed(potlock_proxy::PotlockProxy) .donate_to_project(project_id) .egld_or_single_esdt( - &EgldOrEsdtTokenIdentifier::esdt(TOKEN_ID), + &EgldOrEsdtTokenIdentifier::esdt(POT_TOKEN_ID), 0u64, &multiversx_sc::proxy_imports::BigUint::from(DONATION_AMOUNT), ) @@ -192,13 +196,13 @@ impl PotlockTestState { fn check_esdt_balance(&mut self, address: TestAddress, balance: u64) { self.world .check_account(address) - .esdt_balance(TOKEN_ID, balance); + .esdt_balance(POT_TOKEN_ID, balance); } fn check_sc_esdt_balance(&mut self, address: TestSCAddress, balance: u64) { self.world .check_account(address) - .esdt_balance(TOKEN_ID, balance); + .esdt_balance(POT_TOKEN_ID, balance); } fn check_potlock_id_is_last(&mut self, potlock_id: PotlockId) { @@ -335,7 +339,7 @@ fn test_donate_to_pot() { state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE); - state.donate_to_pot(potlock_id); + state.donate_to_pot(potlock_id, POT_TOKEN_ID); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION + DONATION_AMOUNT); state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); @@ -412,7 +416,7 @@ fn test_distribute_pot_to_projects() { state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); // Donate to Pot - state.donate_to_pot(potlock_id); + state.donate_to_pot(potlock_id, POT_TOKEN_ID); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION + DONATION_AMOUNT); state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); @@ -579,7 +583,7 @@ fn test_fail_distribute_pot_to_projects2() { state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); // Donate to Pot - state.donate_to_pot(potlock_id); + state.donate_to_pot(potlock_id, POT_TOKEN_ID); state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION + DONATION_AMOUNT); state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); @@ -647,3 +651,79 @@ fn test_fail_donate_to_project() { )) .run(); } + +#[test] +fn test_fail_donate_to_pot() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + let potlock_id = 1usize; + state.check_potlock_id_is_last(potlock_id); + + // Accept Pot + state.accept_pot(potlock_id); + + let new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); + state.check_project_id_is_last(new_project_id); + + state.accept_application(new_project_id); + state.check_project_is_accepted(ManagedBuffer::from("Project description")); + + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); + state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE); + + state.donate_to_pot(new_project_id, POT_TOKEN_ID); + + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION + DONATION_AMOUNT); + state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE - DONATION_AMOUNT); + + state + .world + .tx() + .from(POT_DONOR_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .donate_to_pot(new_project_id) + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(DIFFERENT_TOKEN_ID), + 0u64, + &multiversx_sc::proxy_imports::BigUint::from(DONATION_AMOUNT), + ) + .with_result(ExpectError( + 4, + "Already made a payment with a different TokenID", + )) + .run(); +} + +#[test] +fn test_fail_donate_to_non_active_pot() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.add_pot("Pot", "Pot Description"); + let potlock_id = 1usize; + state.check_potlock_id_is_last(potlock_id); + + state + .world + .tx() + .from(PROJECT_DONOR_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .donate_to_pot(potlock_id) + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(DIFFERENT_TOKEN_ID), + 0u64, + &multiversx_sc::proxy_imports::BigUint::from(DONATION_AMOUNT), + ) + .with_result(ExpectError( + 4, + "Pot is not active!", + )) + .run(); +} From 7e28fa20a2c963cf1bc2632f8b1a3d9cda3ee916 Mon Sep 17 00:00:00 2001 From: Petru-Vlad Ionescu Date: Wed, 21 Aug 2024 19:30:01 +0300 Subject: [PATCH 30/38] Add interactor tests --- .../src/potlock_interactor_main.rs | 1124 +++++++++++++++-- 1 file changed, 1035 insertions(+), 89 deletions(-) diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index 85eba287..b63a7b2d 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -5,13 +5,26 @@ mod proxy; use multiversx_sc_snippets::imports::*; use multiversx_sc_snippets::sdk; use serde::{Deserialize, Serialize}; +use std::char::MAX; +use std::result; use std::{ io::{Read, Write}, path::Path, }; -const GATEWAY: &str = sdk::gateway::DEVNET_GATEWAY; +const GATEWAY: &str = sdk::gateway::TESTNET_GATEWAY; const STATE_FILE: &str = "state.toml"; +const TOKEN_ID: &str = "VLD-070dac"; +const SECOND_TOKEN_ID: &str = "SCND-620d29"; +const THIRD_TOKEN_ID: &str = "RAND-e3641c"; +const INVALID_TOKEN_ID: &str = "123"; +const FEE_AMOUNT: u64 = 1; +const DONATION_AMOUNT: u64 = 10; +const OWNER_ADDR: &str = "erd1r6f7nfpyzul2tef7gne5h6nx9xqnyt5gehwltlxymnqkztjjzvuqdhderc"; +const SECOND_USER_ADDR: &str = "erd1tjusdv806tuwzllgesljglm7y9jef38wdylkvp85v7a46z9x23us0z5xtr"; +const THIRD_USER_ADDR: &str = "erd1pu4r9rxgn8f7a7gwjchtxjz6y4u3ha7fy93w6r3fjeq26jaqkqjs4ly8fd"; +const MAX_PERCENTAGE: u64 = 10_000; +const BIG_ID: u32 = 1000u32; #[tokio::main] async fn main() { @@ -23,28 +36,28 @@ async fn main() { let mut interact = ContractInteract::new().await; match cmd.as_str() { "deploy" => interact.deploy().await, - "upgrade" => interact.upgrade().await, - "changeFeeForPots" => interact.change_fee_for_pots().await, - "acceptPot" => interact.accept_pot().await, - "removePot" => interact.remove_pot().await, - "acceptApplication" => interact.accept_application().await, - "removeApplication" => interact.remove_application().await, - "rejectDonation" => interact.reject_donation().await, - "distributePotToProjects" => interact.distribute_pot_to_projects().await, - "addPot" => interact.add_pot().await, - "applyForPot" => interact.apply_for_pot().await, - "donateToPot" => interact.donate_to_pot().await, - "donateToProject" => interact.donate_to_project().await, - "getFeeTokenIdentifier" => interact.fee_token_identifier().await, - "getFeeAmount" => interact.fee_amount().await, - "getPotlocks" => interact.potlocks().await, - "getProjects" => interact.projects().await, - "potDonations" => interact.pot_donations().await, - "projectDonations" => interact.project_donations().await, - "isAdmin" => interact.is_admin().await, - "addAdmin" => interact.add_admin().await, - "removeAdmin" => interact.remove_admin().await, - "getAdmins" => interact.admins().await, + // "upgrade" => interact.upgrade().await, + // "changeFeeForPots" => interact.change_fee_for_pots().await, + // "acceptPot" => interact.accept_pot().await, + // "removePot" => interact.remove_pot().await, + // "acceptApplication" => interact.accept_application().await, + // "removeApplication" => interact.remove_application().await, + // "rejectDonation" => interact.reject_donation().await, + // "distributePotToProjects" => interact.distribute_pot_to_projects().await, + // "addPot" => interact.add_pot().await, + // "applyForPot" => interact.apply_for_pot().await, + // "donateToPot" => interact.donate_to_pot().await, + // "donateToProject" => interact.donate_to_project().await, + // "getFeeTokenIdentifier" => interact.fee_token_identifier().await, + // "getFeeAmount" => interact.fee_amount().await, + // "getPotlocks" => interact.potlocks().await, + // "getProjects" => interact.projects().await, + // "potDonations" => interact.pot_donations().await, + // "projectDonations" => interact.project_donations().await, + // "isAdmin" => interact.is_admin().await, + // "addAdmin" => interact.add_admin().await, + // "removeAdmin" => interact.remove_admin().await, + // "getAdmins" => interact.admins().await, _ => panic!("unknown command: {}", &cmd), } } @@ -91,7 +104,9 @@ impl Drop for State { struct ContractInteract { interactor: Interactor, - wallet_address: Address, + owner_address: Address, + second_address: Address, + third_address: Address, contract_code: BytesValue, state: State, } @@ -99,7 +114,12 @@ struct ContractInteract { impl ContractInteract { async fn new() -> Self { let mut interactor = Interactor::new(GATEWAY).await; - let wallet_address = interactor.register_wallet(test_wallets::alice()); + let owner_address = interactor + .register_wallet(Wallet::from_pem_file("wallet1.pem").expect("wallet 1 not found")); + let second_address = interactor + .register_wallet(Wallet::from_pem_file("wallet2.pem").expect("wallet 2 not found")); + let third_address = interactor + .register_wallet(Wallet::from_pem_file("wallet3.pem").expect("wallet 3 not found")); let contract_code = BytesValue::interpret_from( "mxsc:../output/potlock.mxsc.json", @@ -108,20 +128,22 @@ impl ContractInteract { ContractInteract { interactor, - wallet_address, + owner_address, + second_address, + third_address, contract_code, state: State::load_state(), } } async fn deploy(&mut self) { - let admins = MultiValueVec::from(vec![bech32::decode("")]); + let admins = MultiValueVec::from(vec![bech32::decode(THIRD_USER_ADDR)]); let new_address = self .interactor .tx() - .from(&self.wallet_address) - .gas(30_000_000u64) + .from(&self.owner_address) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .init(admins) .code(&self.contract_code) @@ -142,8 +164,8 @@ impl ContractInteract { .interactor .tx() .to(self.state.current_address()) - .from(&self.wallet_address) - .gas(30_000_000u64) + .from(&self.owner_address) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .upgrade() .code(&self.contract_code) @@ -156,16 +178,16 @@ impl ContractInteract { println!("Result: {response:?}"); } - async fn change_fee_for_pots(&mut self) { - let token_identifier = TokenIdentifier::from_esdt_bytes(&b""[..]); - let fee = BigUint::::from(0u128); + async fn change_fee_for_pots(&mut self, caller: &Bech32Address, token_id: &str, fee: u128) { + let token_identifier = TokenIdentifier::from_esdt_bytes(token_id); + let fee = BigUint::::from(fee); let response = self .interactor .tx() - .from(&self.wallet_address) + .from(caller) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .change_fee_for_pots(token_identifier, fee) .returns(ReturnsResultUnmanaged) @@ -176,15 +198,36 @@ impl ContractInteract { println!("Result: {response:?}"); } - async fn accept_pot(&mut self) { - let potlock_id = 0u32; + async fn change_fee_for_pots_fail( + &mut self, + caller: &Bech32Address, + token_id: &str, + fee: u128, + expected_result: ExpectError<'_>, + ) { + let token_identifier = TokenIdentifier::from_esdt_bytes(token_id); + let fee = BigUint::::from(fee); + + self.interactor + .tx() + .from(caller) + .to(self.state.current_address()) + .gas(70_000_000u64) + .typed(proxy::PotlockProxy) + .change_fee_for_pots(token_identifier, fee) + .returns(expected_result) + .prepare_async() + .run() + .await; + } + async fn accept_pot(&mut self, caller: &Bech32Address, potlock_id: u32) { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(caller) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .accept_pot(potlock_id) .returns(ReturnsResultUnmanaged) @@ -195,15 +238,35 @@ impl ContractInteract { println!("Result: {response:?}"); } - async fn remove_pot(&mut self) { - let potlock_id = 0u32; + async fn accept_pot_fail( + &mut self, + caller: &Bech32Address, + potlock_id: u32, + expected_result: ExpectError<'_>, + ) { + let response = self + .interactor + .tx() + .from(caller) + .to(self.state.current_address()) + .gas(70_000_000u64) + .typed(proxy::PotlockProxy) + .accept_pot(potlock_id) + .returns(expected_result) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + async fn remove_pot(&mut self, caller: &Bech32Address, potlock_id: u32) { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(caller) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .remove_pot(potlock_id) .returns(ReturnsResultUnmanaged) @@ -214,15 +277,35 @@ impl ContractInteract { println!("Result: {response:?}"); } - async fn accept_application(&mut self) { - let project_id = 0u32; + async fn remove_pot_fail( + &mut self, + caller: &Bech32Address, + potlock_id: u32, + expected_result: ExpectError<'_>, + ) { + let response = self + .interactor + .tx() + .from(caller) + .to(self.state.current_address()) + .gas(70_000_000u64) + .typed(proxy::PotlockProxy) + .remove_pot(potlock_id) + .returns(expected_result) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + async fn accept_application(&mut self, caller: &Bech32Address, project_id: u32) { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(caller) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .accept_application(project_id) .returns(ReturnsResultUnmanaged) @@ -233,15 +316,35 @@ impl ContractInteract { println!("Result: {response:?}"); } + async fn accept_application_fail( + &mut self, + caller: &Bech32Address, + project_id: u32, + expected_result: ExpectError<'_>, + ) { + let response = self + .interactor + .tx() + .from(caller) + .to(self.state.current_address()) + .gas(70_000_000u64) + .typed(proxy::PotlockProxy) + .accept_application(project_id) + .returns(expected_result) + .prepare_async() + .run() + .await; + } + async fn remove_application(&mut self) { let project_id = 0u32; let response = self .interactor .tx() - .from(&self.wallet_address) + .from(&self.owner_address) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .remove_application(project_id) .returns(ReturnsResultUnmanaged) @@ -259,9 +362,9 @@ impl ContractInteract { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(&self.owner_address) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .reject_donation(potlock_id, user) .returns(ReturnsResultUnmanaged) @@ -272,17 +375,18 @@ impl ContractInteract { println!("Result: {response:?}"); } - async fn distribute_pot_to_projects(&mut self) { - let potlock_id = 0u32; - let project_percentages = - MultiValueVec::from(vec![MultiValue2::::from((0u32, 0u64))]); - + async fn distribute_pot_to_projects( + &mut self, + caller: &Bech32Address, + potlock_id: u32, + project_percentages: MultiValueVec>, + ) { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(caller) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .distribute_pot_to_projects(potlock_id, project_percentages) .returns(ReturnsResultUnmanaged) @@ -293,10 +397,33 @@ impl ContractInteract { println!("Result: {response:?}"); } - async fn add_pot(&mut self) { - let token_id = String::new(); + async fn distribute_pot_to_projects_fail( + &mut self, + caller: &Bech32Address, + potlock_id: u32, + project_percentages: MultiValueVec>, + expected_result: ExpectError<'_>, + ) { + let response = self + .interactor + .tx() + .from(caller) + .to(self.state.current_address()) + .gas(70_000_000u64) + .typed(proxy::PotlockProxy) + .distribute_pot_to_projects(potlock_id, project_percentages) + .returns(expected_result) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn add_pot(&mut self, caller: &Bech32Address, token_id: &str, fee: u128) { + let token_id = token_id.to_string(); let token_nonce = 0u64; - let token_amount = BigUint::::from(0u128); + let token_amount = BigUint::::from(fee); let name = ManagedBuffer::new_from_bytes(&b""[..]); let description = ManagedBuffer::new_from_bytes(&b""[..]); @@ -304,9 +431,9 @@ impl ContractInteract { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(caller) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .add_pot(name, description) .payment(( @@ -322,17 +449,51 @@ impl ContractInteract { println!("Result: {response:?}"); } - async fn apply_for_pot(&mut self) { - let potlock_id = 0u32; + async fn add_pot_fail( + &mut self, + caller: &Bech32Address, + token_id: &str, + fee: u128, + expected_result: ExpectError<'_>, + ) { + let token_id = token_id.to_string(); + let token_nonce = 0u64; + let token_amount = BigUint::::from(fee); + + let name = ManagedBuffer::new_from_bytes(&b""[..]); + let description = ManagedBuffer::new_from_bytes(&b""[..]); + + let response = self + .interactor + .tx() + .from(caller) + .to(self.state.current_address()) + .gas(70_000_000u64) + .typed(proxy::PotlockProxy) + .add_pot(name, description) + .payment(( + TokenIdentifier::from(token_id.as_str()), + token_nonce, + token_amount, + )) + .returns(expected_result) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn apply_for_pot(&mut self, caller: &Bech32Address, potlock_id: u32) { let project_name = ManagedBuffer::new_from_bytes(&b""[..]); let description = ManagedBuffer::new_from_bytes(&b""[..]); let response = self .interactor .tx() - .from(&self.wallet_address) + .from(caller) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .apply_for_pot(potlock_id, project_name, description) .returns(ReturnsResultUnmanaged) @@ -343,19 +504,23 @@ impl ContractInteract { println!("Result: {response:?}"); } - async fn donate_to_pot(&mut self) { - let token_id = String::new(); + async fn donate_to_pot( + &mut self, + caller: &Bech32Address, + potlock_id: u32, + token_id: &str, + amount: u128, + ) { + let token_id = token_id.to_string(); let token_nonce = 0u64; - let token_amount = BigUint::::from(0u128); - - let potlock_id = 0u32; + let token_amount = BigUint::::from(amount); let response = self .interactor .tx() - .from(&self.wallet_address) + .from(caller) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .donate_to_pot(potlock_id) .payment(( @@ -371,19 +536,56 @@ impl ContractInteract { println!("Result: {response:?}"); } - async fn donate_to_project(&mut self) { - let token_id = String::new(); + async fn donate_to_pot_fail( + &mut self, + caller: &Bech32Address, + potlock_id: u32, + token_id: &str, + amount: u128, + expected_result: ExpectError<'_>, + ) { + let token_id = token_id.to_string(); let token_nonce = 0u64; - let token_amount = BigUint::::from(0u128); + let token_amount = BigUint::::from(amount); - let project_id = 0u32; + let response = self + .interactor + .tx() + .from(caller) + .to(self.state.current_address()) + .gas(70_000_000u64) + .typed(proxy::PotlockProxy) + .donate_to_pot(potlock_id) + .payment(( + TokenIdentifier::from(token_id.as_str()), + token_nonce, + token_amount, + )) + .returns(expected_result) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn donate_to_project( + &mut self, + caller: &Bech32Address, + project_id: u32, + token_id: &str, + amount: u128, + ) { + let token_id = token_id.to_string(); + let token_nonce = 0u64; + let token_amount = BigUint::::from(amount); let response = self .interactor .tx() - .from(&self.wallet_address) + .from(caller) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .donate_to_project(project_id) .payment(( @@ -399,7 +601,40 @@ impl ContractInteract { println!("Result: {response:?}"); } - async fn fee_token_identifier(&mut self) { + async fn donate_to_project_fail( + &mut self, + caller: &Bech32Address, + project_id: u32, + token_id: &str, + amount: u128, + expected_result: ExpectError<'_>, + ) { + let token_id = token_id.to_string(); + let token_nonce = 0u64; + let token_amount = BigUint::::from(amount); + + let response = self + .interactor + .tx() + .from(caller) + .to(self.state.current_address()) + .gas(70_000_000u64) + .typed(proxy::PotlockProxy) + .donate_to_project(project_id) + .payment(( + TokenIdentifier::from(token_id.as_str()), + token_nonce, + token_amount, + )) + .returns(expected_result) + .prepare_async() + .run() + .await; + + println!("Result: {response:?}"); + } + + async fn fee_token_identifier(&mut self) -> String { let result_value = self .interactor .query() @@ -411,10 +646,10 @@ impl ContractInteract { .run() .await; - println!("Result: {result_value:?}"); + result_value.to_string() } - async fn fee_amount(&mut self) { + async fn fee_amount(&mut self) -> RustBigUint { let result_value = self .interactor .query() @@ -426,7 +661,7 @@ impl ContractInteract { .run() .await; - println!("Result: {result_value:?}"); + result_value } async fn potlocks(&mut self) { @@ -516,9 +751,9 @@ impl ContractInteract { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(&self.owner_address) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .add_admin(address) .returns(ReturnsResultUnmanaged) @@ -535,9 +770,9 @@ impl ContractInteract { let response = self .interactor .tx() - .from(&self.wallet_address) + .from(&self.owner_address) .to(self.state.current_address()) - .gas(30_000_000u64) + .gas(70_000_000u64) .typed(proxy::PotlockProxy) .remove_admin(address) .returns(ReturnsResultUnmanaged) @@ -563,3 +798,714 @@ impl ContractInteract { println!("Result: {result_value:?}"); } } + +#[tokio::test] +async fn test_deploy_and_config() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; +} + +#[tokio::test] +async fn test_add_pot() { + let mut interact = ContractInteract::new().await; + + interact + .add_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; +} + +#[tokio::test] +async fn test_accept_pot() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .add_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .accept_pot( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; +} + +#[tokio::test] +async fn test_remove_pot() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .add_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .remove_pot( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; +} + +#[tokio::test] +async fn test_donate_to_pot() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .add_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .accept_pot( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .donate_to_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + TOKEN_ID, + DONATION_AMOUNT.into(), + ) + .await; +} + +#[tokio::test] +async fn test_accept_application() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .add_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .accept_pot( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .apply_for_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .accept_application( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; +} + +#[tokio::test] +async fn test_donate_to_project() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .add_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .accept_pot( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .apply_for_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .accept_application( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .donate_to_project( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + TOKEN_ID, + DONATION_AMOUNT.into(), + ) + .await; +} + +#[tokio::test] +async fn test_distribute_pot_to_projects() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .add_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .accept_pot( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .donate_to_pot( + &Bech32Address::from_bech32_string(THIRD_USER_ADDR.to_string()), + 1u32, + TOKEN_ID, + DONATION_AMOUNT.into(), + ) + .await; + + interact + .apply_for_pot( + &Bech32Address::from_bech32_string(THIRD_USER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .accept_application( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; + + let project_percentages = MultiValueVec::from(vec![MultiValue2::from((1u32, MAX_PERCENTAGE))]); + + interact + .distribute_pot_to_projects( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + project_percentages, + ) + .await; +} + +#[tokio::test] +async fn test_donate_to_pot_twice_with_same_token() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .add_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .accept_pot( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .donate_to_pot( + &Bech32Address::from_bech32_string(THIRD_USER_ADDR.to_string()), + 1u32, + TOKEN_ID, + DONATION_AMOUNT.into(), + ) + .await; + + interact + .donate_to_pot( + &Bech32Address::from_bech32_string(THIRD_USER_ADDR.to_string()), + 1u32, + TOKEN_ID, + (DONATION_AMOUNT + 1).into(), + ) + .await; +} + +#[tokio::test] +async fn test_multiple_change_fee_for_pots() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + let fee = interact.fee_amount().await; + let token_id = interact.fee_token_identifier().await; + + assert_eq!(fee, FEE_AMOUNT.into()); + assert_eq!(token_id, TOKEN_ID.to_string()); + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + SECOND_TOKEN_ID, + (FEE_AMOUNT + 1).into(), + ) + .await; + + let fee = interact.fee_amount().await; + let token_id = interact.fee_token_identifier().await; + + assert_eq!(fee, FEE_AMOUNT.into()); + assert_eq!(token_id, TOKEN_ID.to_string()); +} + +#[tokio::test] +async fn test_change_fee_for_pots_non_admin() { + let mut interact = ContractInteract::new().await; + + interact + .change_fee_for_pots_fail( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + INVALID_TOKEN_ID, + FEE_AMOUNT.into(), + ExpectError(4, "Endpoint can only be called by admins"), + ) + .await; +} + +#[tokio::test] +async fn test_accept_pot_non_admin() { + let mut interact = ContractInteract::new().await; + + interact + .accept_pot_fail( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + ExpectError(4, "Endpoint can only be called by admins"), + ) + .await; +} + +#[tokio::test] +async fn test_remove_pot_non_admin() { + let mut interact = ContractInteract::new().await; + + interact + .remove_pot_fail( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + ExpectError(4, "Endpoint can only be called by admins"), + ) + .await; +} + +#[tokio::test] +async fn test_accept_application_non_admin() { + let mut interact = ContractInteract::new().await; + + interact + .accept_application_fail( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + ExpectError(4, "Endpoint can only be called by admins"), + ) + .await; +} + +#[tokio::test] +async fn test_distribute_pot_to_projects_non_admin() { + let mut interact = ContractInteract::new().await; + + let project_percentages = MultiValueVec::from(vec![MultiValue2::from((1u32, MAX_PERCENTAGE))]); + + interact + .distribute_pot_to_projects_fail( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + project_percentages, + ExpectError(4, "Endpoint can only be called by admins"), + ) + .await; +} + +#[tokio::test] +async fn test_distribute_pot_to_projects_more_than_max_percent() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .add_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .accept_pot( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .donate_to_pot( + &Bech32Address::from_bech32_string(THIRD_USER_ADDR.to_string()), + 1u32, + TOKEN_ID, + DONATION_AMOUNT.into(), + ) + .await; + + interact + .apply_for_pot( + &Bech32Address::from_bech32_string(THIRD_USER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .accept_application( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; + + let project_percentages = + MultiValueVec::from(vec![MultiValue2::from((1u32, MAX_PERCENTAGE + 1))]); + + interact + .distribute_pot_to_projects_fail( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + project_percentages, + ExpectError(4, "Total percentages more than 100%"), + ) + .await; +} + +#[tokio::test] +async fn test_donate_to_project_with_different_token() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .add_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .accept_pot( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .apply_for_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .accept_application( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .donate_to_project( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + SECOND_TOKEN_ID, + DONATION_AMOUNT.into(), + ) + .await; + + interact + .donate_to_project_fail( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + TOKEN_ID, + DONATION_AMOUNT.into(), + ExpectError(4, "Already made a payment with a different TokenID"), + ) + .await; +} + +#[tokio::test] +async fn test_donate_to_project_inactive_project() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .add_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .accept_pot( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .apply_for_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + ) + .await; + + interact + .donate_to_project_fail( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + TOKEN_ID, + DONATION_AMOUNT.into(), + ExpectError(4, "Project is not active!"), + ) + .await; +} + +#[tokio::test] +async fn test_donate_to_pot_inactive_pot() { + let mut interact = ContractInteract::new().await; + + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .add_pot( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + + interact + .donate_to_pot_fail( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + 1u32, + TOKEN_ID, + DONATION_AMOUNT.into(), + ExpectError(4, "Pot is not active!"), + ) + .await; +} + +#[tokio::test] +async fn test_add_pot_wrong_payment() { + let mut interact = ContractInteract::new().await; + + interact + .add_pot_fail( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + SECOND_TOKEN_ID, + FEE_AMOUNT.into(), + ExpectError(4, "Wrong token identifier for creating a pot!"), + ) + .await; + + interact + .add_pot_fail( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + TOKEN_ID, + (FEE_AMOUNT + 1).into(), + ExpectError(4, "Wrong fee amount for creating a pot"), + ) + .await; +} + +#[tokio::test] +async fn test_accept_pot_non_existent() { + let mut interact = ContractInteract::new().await; + + interact + .accept_pot_fail( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + BIG_ID, + ExpectError(4, "Potlock doesn't exist!"), + ) + .await; +} + +#[tokio::test] +async fn test_remove_pot_non_existent() { + let mut interact = ContractInteract::new().await; + + interact + .remove_pot_fail( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + BIG_ID, + ExpectError(4, "Potlock doesn't exist!"), + ) + .await; +} + +#[tokio::test] +async fn test_donate_to_pot_non_existent() { + let mut interact = ContractInteract::new().await; + + interact + .donate_to_pot_fail( + &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), + BIG_ID, + TOKEN_ID, + DONATION_AMOUNT.into(), + ExpectError(4, "Potlock doesn't exist!"), + ) + .await; +} From eadba85c94ddcd21b6c4b60dd7ddfdd6ad0c72b0 Mon Sep 17 00:00:00 2001 From: Petru-Vlad Ionescu Date: Fri, 6 Sep 2024 11:36:34 +0300 Subject: [PATCH 31/38] Add interactor tests --- .../src/potlock_interactor_main.rs | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index b63a7b2d..26dec0d2 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -1,5 +1,7 @@ #![allow(non_snake_case)] - +#[allow(unused_imports)] +#[allow(dead_code)] +#[allow(unused_variables)] mod proxy; use multiversx_sc_snippets::imports::*; @@ -16,13 +18,12 @@ const GATEWAY: &str = sdk::gateway::TESTNET_GATEWAY; const STATE_FILE: &str = "state.toml"; const TOKEN_ID: &str = "VLD-070dac"; const SECOND_TOKEN_ID: &str = "SCND-620d29"; -const THIRD_TOKEN_ID: &str = "RAND-e3641c"; const INVALID_TOKEN_ID: &str = "123"; const FEE_AMOUNT: u64 = 1; const DONATION_AMOUNT: u64 = 10; -const OWNER_ADDR: &str = "erd1r6f7nfpyzul2tef7gne5h6nx9xqnyt5gehwltlxymnqkztjjzvuqdhderc"; -const SECOND_USER_ADDR: &str = "erd1tjusdv806tuwzllgesljglm7y9jef38wdylkvp85v7a46z9x23us0z5xtr"; -const THIRD_USER_ADDR: &str = "erd1pu4r9rxgn8f7a7gwjchtxjz6y4u3ha7fy93w6r3fjeq26jaqkqjs4ly8fd"; +const OWNER_ADDR: &str = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"; +const SECOND_USER_ADDR: &str = "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"; +const THIRD_USER_ADDR: &str = "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"; const MAX_PERCENTAGE: u64 = 10_000; const BIG_ID: u32 = 1000u32; @@ -114,12 +115,9 @@ struct ContractInteract { impl ContractInteract { async fn new() -> Self { let mut interactor = Interactor::new(GATEWAY).await; - let owner_address = interactor - .register_wallet(Wallet::from_pem_file("wallet1.pem").expect("wallet 1 not found")); - let second_address = interactor - .register_wallet(Wallet::from_pem_file("wallet2.pem").expect("wallet 2 not found")); - let third_address = interactor - .register_wallet(Wallet::from_pem_file("wallet3.pem").expect("wallet 3 not found")); + let owner_address = interactor.register_wallet(test_wallets::alice()); + let second_address = interactor.register_wallet(test_wallets::bob()); + let third_address = interactor.register_wallet(test_wallets::carol()); let contract_code = BytesValue::interpret_from( "mxsc:../output/potlock.mxsc.json", @@ -818,6 +816,16 @@ async fn test_deploy_and_config() { async fn test_add_pot() { let mut interact = ContractInteract::new().await; + interact.deploy().await; + + interact + .change_fee_for_pots( + &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), + TOKEN_ID, + FEE_AMOUNT.into(), + ) + .await; + interact .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), From f804a7d085edc62f362ebc12518dac95a623d927 Mon Sep 17 00:00:00 2001 From: Petru-Vlad Ionescu Date: Fri, 6 Sep 2024 11:56:06 +0300 Subject: [PATCH 32/38] Solve warnings --- .../potlock/interact-rs/src/potlock_interactor_main.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index 26dec0d2..81d8faad 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -1,7 +1,8 @@ #![allow(non_snake_case)] -#[allow(unused_imports)] -#[allow(dead_code)] -#[allow(unused_variables)] +#![allow(unused_imports)] +#![allow(dead_code)] +#![allow(unused_variables)] + mod proxy; use multiversx_sc_snippets::imports::*; From 28addacc05ce84cb41129178fa53d27e949ee5c7 Mon Sep 17 00:00:00 2001 From: Petru-Vlad Ionescu Date: Fri, 6 Sep 2024 14:41:15 +0300 Subject: [PATCH 33/38] Execute interactor tests only on demand --- .../src/potlock_interactor_main.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index 81d8faad..7788b245 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -799,6 +799,7 @@ impl ContractInteract { } #[tokio::test] +#[ignore = "run on demand"] async fn test_deploy_and_config() { let mut interact = ContractInteract::new().await; @@ -814,6 +815,7 @@ async fn test_deploy_and_config() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_add_pot() { let mut interact = ContractInteract::new().await; @@ -837,6 +839,7 @@ async fn test_add_pot() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_accept_pot() { let mut interact = ContractInteract::new().await; @@ -867,6 +870,7 @@ async fn test_accept_pot() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_remove_pot() { let mut interact = ContractInteract::new().await; @@ -897,6 +901,7 @@ async fn test_remove_pot() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_donate_to_pot() { let mut interact = ContractInteract::new().await; @@ -936,6 +941,7 @@ async fn test_donate_to_pot() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_accept_application() { let mut interact = ContractInteract::new().await; @@ -980,6 +986,7 @@ async fn test_accept_application() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_donate_to_project() { let mut interact = ContractInteract::new().await; @@ -1033,6 +1040,7 @@ async fn test_donate_to_project() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_distribute_pot_to_projects() { let mut interact = ContractInteract::new().await; @@ -1096,6 +1104,7 @@ async fn test_distribute_pot_to_projects() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_donate_to_pot_twice_with_same_token() { let mut interact = ContractInteract::new().await; @@ -1144,6 +1153,7 @@ async fn test_donate_to_pot_twice_with_same_token() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_multiple_change_fee_for_pots() { let mut interact = ContractInteract::new().await; @@ -1179,6 +1189,7 @@ async fn test_multiple_change_fee_for_pots() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_change_fee_for_pots_non_admin() { let mut interact = ContractInteract::new().await; @@ -1193,6 +1204,7 @@ async fn test_change_fee_for_pots_non_admin() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_accept_pot_non_admin() { let mut interact = ContractInteract::new().await; @@ -1206,6 +1218,7 @@ async fn test_accept_pot_non_admin() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_remove_pot_non_admin() { let mut interact = ContractInteract::new().await; @@ -1219,6 +1232,7 @@ async fn test_remove_pot_non_admin() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_accept_application_non_admin() { let mut interact = ContractInteract::new().await; @@ -1232,6 +1246,7 @@ async fn test_accept_application_non_admin() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_distribute_pot_to_projects_non_admin() { let mut interact = ContractInteract::new().await; @@ -1248,6 +1263,7 @@ async fn test_distribute_pot_to_projects_non_admin() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_distribute_pot_to_projects_more_than_max_percent() { let mut interact = ContractInteract::new().await; @@ -1313,6 +1329,7 @@ async fn test_distribute_pot_to_projects_more_than_max_percent() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_donate_to_project_with_different_token() { let mut interact = ContractInteract::new().await; @@ -1376,6 +1393,7 @@ async fn test_donate_to_project_with_different_token() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_donate_to_project_inactive_project() { let mut interact = ContractInteract::new().await; @@ -1423,6 +1441,7 @@ async fn test_donate_to_project_inactive_project() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_donate_to_pot_inactive_pot() { let mut interact = ContractInteract::new().await; @@ -1456,6 +1475,7 @@ async fn test_donate_to_pot_inactive_pot() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_add_pot_wrong_payment() { let mut interact = ContractInteract::new().await; @@ -1479,6 +1499,7 @@ async fn test_add_pot_wrong_payment() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_accept_pot_non_existent() { let mut interact = ContractInteract::new().await; @@ -1492,6 +1513,7 @@ async fn test_accept_pot_non_existent() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_remove_pot_non_existent() { let mut interact = ContractInteract::new().await; @@ -1505,6 +1527,7 @@ async fn test_remove_pot_non_existent() { } #[tokio::test] +#[ignore = "run on demand"] async fn test_donate_to_pot_non_existent() { let mut interact = ContractInteract::new().await; From e416e524ca5cc9ddbd10fbbaafcf6a6b7003ab24 Mon Sep 17 00:00:00 2001 From: Petru-Vlad Ionescu Date: Fri, 6 Sep 2024 15:07:42 +0300 Subject: [PATCH 34/38] Cleanse tests --- .../src/potlock_interactor_main.rs | 122 +++++------------- 1 file changed, 35 insertions(+), 87 deletions(-) diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index 7788b245..bc453acf 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -20,7 +20,7 @@ const STATE_FILE: &str = "state.toml"; const TOKEN_ID: &str = "VLD-070dac"; const SECOND_TOKEN_ID: &str = "SCND-620d29"; const INVALID_TOKEN_ID: &str = "123"; -const FEE_AMOUNT: u64 = 1; +const FEE_AMOUNT: u128 = 1; const DONATION_AMOUNT: u64 = 10; const OWNER_ADDR: &str = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"; const SECOND_USER_ADDR: &str = "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"; @@ -38,28 +38,6 @@ async fn main() { let mut interact = ContractInteract::new().await; match cmd.as_str() { "deploy" => interact.deploy().await, - // "upgrade" => interact.upgrade().await, - // "changeFeeForPots" => interact.change_fee_for_pots().await, - // "acceptPot" => interact.accept_pot().await, - // "removePot" => interact.remove_pot().await, - // "acceptApplication" => interact.accept_application().await, - // "removeApplication" => interact.remove_application().await, - // "rejectDonation" => interact.reject_donation().await, - // "distributePotToProjects" => interact.distribute_pot_to_projects().await, - // "addPot" => interact.add_pot().await, - // "applyForPot" => interact.apply_for_pot().await, - // "donateToPot" => interact.donate_to_pot().await, - // "donateToProject" => interact.donate_to_project().await, - // "getFeeTokenIdentifier" => interact.fee_token_identifier().await, - // "getFeeAmount" => interact.fee_amount().await, - // "getPotlocks" => interact.potlocks().await, - // "getProjects" => interact.projects().await, - // "potDonations" => interact.pot_donations().await, - // "projectDonations" => interact.project_donations().await, - // "isAdmin" => interact.is_admin().await, - // "addAdmin" => interact.add_admin().await, - // "removeAdmin" => interact.remove_admin().await, - // "getAdmins" => interact.admins().await, _ => panic!("unknown command: {}", &cmd), } } @@ -420,7 +398,6 @@ impl ContractInteract { } async fn add_pot(&mut self, caller: &Bech32Address, token_id: &str, fee: u128) { - let token_id = token_id.to_string(); let token_nonce = 0u64; let token_amount = BigUint::::from(fee); @@ -435,11 +412,7 @@ impl ContractInteract { .gas(70_000_000u64) .typed(proxy::PotlockProxy) .add_pot(name, description) - .payment(( - TokenIdentifier::from(token_id.as_str()), - token_nonce, - token_amount, - )) + .payment((TokenIdentifier::from(token_id), token_nonce, token_amount)) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -455,7 +428,6 @@ impl ContractInteract { fee: u128, expected_result: ExpectError<'_>, ) { - let token_id = token_id.to_string(); let token_nonce = 0u64; let token_amount = BigUint::::from(fee); @@ -470,11 +442,7 @@ impl ContractInteract { .gas(70_000_000u64) .typed(proxy::PotlockProxy) .add_pot(name, description) - .payment(( - TokenIdentifier::from(token_id.as_str()), - token_nonce, - token_amount, - )) + .payment((TokenIdentifier::from(token_id), token_nonce, token_amount)) .returns(expected_result) .prepare_async() .run() @@ -510,7 +478,6 @@ impl ContractInteract { token_id: &str, amount: u128, ) { - let token_id = token_id.to_string(); let token_nonce = 0u64; let token_amount = BigUint::::from(amount); @@ -522,11 +489,7 @@ impl ContractInteract { .gas(70_000_000u64) .typed(proxy::PotlockProxy) .donate_to_pot(potlock_id) - .payment(( - TokenIdentifier::from(token_id.as_str()), - token_nonce, - token_amount, - )) + .payment((TokenIdentifier::from(token_id), token_nonce, token_amount)) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -543,7 +506,6 @@ impl ContractInteract { amount: u128, expected_result: ExpectError<'_>, ) { - let token_id = token_id.to_string(); let token_nonce = 0u64; let token_amount = BigUint::::from(amount); @@ -555,11 +517,7 @@ impl ContractInteract { .gas(70_000_000u64) .typed(proxy::PotlockProxy) .donate_to_pot(potlock_id) - .payment(( - TokenIdentifier::from(token_id.as_str()), - token_nonce, - token_amount, - )) + .payment((TokenIdentifier::from(token_id), token_nonce, token_amount)) .returns(expected_result) .prepare_async() .run() @@ -575,7 +533,6 @@ impl ContractInteract { token_id: &str, amount: u128, ) { - let token_id = token_id.to_string(); let token_nonce = 0u64; let token_amount = BigUint::::from(amount); @@ -587,11 +544,7 @@ impl ContractInteract { .gas(70_000_000u64) .typed(proxy::PotlockProxy) .donate_to_project(project_id) - .payment(( - TokenIdentifier::from(token_id.as_str()), - token_nonce, - token_amount, - )) + .payment((TokenIdentifier::from(token_id), token_nonce, token_amount)) .returns(ReturnsResultUnmanaged) .prepare_async() .run() @@ -608,7 +561,6 @@ impl ContractInteract { amount: u128, expected_result: ExpectError<'_>, ) { - let token_id = token_id.to_string(); let token_nonce = 0u64; let token_amount = BigUint::::from(amount); @@ -620,11 +572,7 @@ impl ContractInteract { .gas(70_000_000u64) .typed(proxy::PotlockProxy) .donate_to_project(project_id) - .payment(( - TokenIdentifier::from(token_id.as_str()), - token_nonce, - token_amount, - )) + .payment((TokenIdentifier::from(token_id), token_nonce, token_amount)) .returns(expected_result) .prepare_async() .run() @@ -809,7 +757,7 @@ async fn test_deploy_and_config() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; } @@ -825,7 +773,7 @@ async fn test_add_pot() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -833,7 +781,7 @@ async fn test_add_pot() { .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; } @@ -849,7 +797,7 @@ async fn test_accept_pot() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -857,7 +805,7 @@ async fn test_accept_pot() { .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -880,7 +828,7 @@ async fn test_remove_pot() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -888,7 +836,7 @@ async fn test_remove_pot() { .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -911,7 +859,7 @@ async fn test_donate_to_pot() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -919,7 +867,7 @@ async fn test_donate_to_pot() { .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -951,7 +899,7 @@ async fn test_accept_application() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -959,7 +907,7 @@ async fn test_accept_application() { .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -996,7 +944,7 @@ async fn test_donate_to_project() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1004,7 +952,7 @@ async fn test_donate_to_project() { .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1050,7 +998,7 @@ async fn test_distribute_pot_to_projects() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1058,7 +1006,7 @@ async fn test_distribute_pot_to_projects() { .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1114,7 +1062,7 @@ async fn test_donate_to_pot_twice_with_same_token() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1122,7 +1070,7 @@ async fn test_donate_to_pot_twice_with_same_token() { .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1163,7 +1111,7 @@ async fn test_multiple_change_fee_for_pots() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1197,7 +1145,7 @@ async fn test_change_fee_for_pots_non_admin() { .change_fee_for_pots_fail( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), INVALID_TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ExpectError(4, "Endpoint can only be called by admins"), ) .await; @@ -1273,7 +1221,7 @@ async fn test_distribute_pot_to_projects_more_than_max_percent() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1281,7 +1229,7 @@ async fn test_distribute_pot_to_projects_more_than_max_percent() { .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1339,7 +1287,7 @@ async fn test_donate_to_project_with_different_token() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1347,7 +1295,7 @@ async fn test_donate_to_project_with_different_token() { .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1403,7 +1351,7 @@ async fn test_donate_to_project_inactive_project() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1411,7 +1359,7 @@ async fn test_donate_to_project_inactive_project() { .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1451,7 +1399,7 @@ async fn test_donate_to_pot_inactive_pot() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1459,7 +1407,7 @@ async fn test_donate_to_pot_inactive_pot() { .add_pot( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ) .await; @@ -1483,7 +1431,7 @@ async fn test_add_pot_wrong_payment() { .add_pot_fail( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), SECOND_TOKEN_ID, - FEE_AMOUNT.into(), + FEE_AMOUNT, ExpectError(4, "Wrong token identifier for creating a pot!"), ) .await; From 82238528e978fc5400b4d9547c60fb85cbc446c8 Mon Sep 17 00:00:00 2001 From: Petru-Vlad Ionescu Date: Fri, 6 Sep 2024 15:09:41 +0300 Subject: [PATCH 35/38] Cleanse tests --- contracts/potlock/interact-rs/src/potlock_interactor_main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index bc453acf..e3a9f14c 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -1125,7 +1125,7 @@ async fn test_multiple_change_fee_for_pots() { .change_fee_for_pots( &Bech32Address::from_bech32_string(OWNER_ADDR.to_string()), SECOND_TOKEN_ID, - (FEE_AMOUNT + 1).into(), + FEE_AMOUNT + 1, ) .await; @@ -1440,7 +1440,7 @@ async fn test_add_pot_wrong_payment() { .add_pot_fail( &Bech32Address::from_bech32_string(SECOND_USER_ADDR.to_string()), TOKEN_ID, - (FEE_AMOUNT + 1).into(), + FEE_AMOUNT + 1, ExpectError(4, "Wrong fee amount for creating a pot"), ) .await; From 6c7a7670253b96fe5c41ff9ad65cb1086156e9aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 9 Oct 2024 09:29:41 +0300 Subject: [PATCH 36/38] Framework upgrade 0.53.2 --- Cargo.lock | 14 +++++++-- contracts/potlock/Cargo.toml | 6 ++-- contracts/potlock/interact-rs/Cargo.toml | 8 ++--- contracts/potlock/meta/Cargo.toml | 2 +- contracts/potlock/wasm/Cargo.lock | 36 +++++++++++----------- contracts/potlock/wasm/Cargo.toml | 2 +- contracts/price-aggregator/wasm/src/lib.rs | 4 ++- 7 files changed, 42 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e1e313f..c72a4fb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,7 +20,6 @@ dependencies = [ [[package]] name = "addr2line" - version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" @@ -2011,6 +2010,18 @@ dependencies = [ "num-bigint", ] +[[package]] +name = "potlock-interact" +version = "0.0.0" +dependencies = [ + "clap", + "multiversx-sc", + "multiversx-sc-snippets", + "potlock", + "serde", + "toml", +] + [[package]] name = "potlock-meta" version = "0.0.0" @@ -2258,7 +2269,6 @@ dependencies = [ "clap", "multiversx-sc", "multiversx-sc-snippets", - "potlock", "paymaster", "serde", "toml", diff --git a/contracts/potlock/Cargo.toml b/contracts/potlock/Cargo.toml index 783b1e46..281683a8 100644 --- a/contracts/potlock/Cargo.toml +++ b/contracts/potlock/Cargo.toml @@ -10,13 +10,13 @@ readme = "README.md" path = "src/potlock.rs" [dependencies.multiversx-sc] -version = "0.52.3" +version = "0.53.2" [dependencies.multiversx-sc-modules] -version = "=0.52.3" +version = "=0.53.2" [dev-dependencies] num-bigint = "0.4.2" [dev-dependencies.multiversx-sc-scenario] -version = "0.52.3" +version = "0.53.2" diff --git a/contracts/potlock/interact-rs/Cargo.toml b/contracts/potlock/interact-rs/Cargo.toml index e0cb932b..4bf66a7b 100644 --- a/contracts/potlock/interact-rs/Cargo.toml +++ b/contracts/potlock/interact-rs/Cargo.toml @@ -1,9 +1,9 @@ [[bin]] -name = "rust-interact" +name = "potlock-interact" path = "src/potlock_interactor_main.rs" [package] -name = "rust-interact" +name = "potlock-interact" version = "0.0.0" authors = ["you"] edition = "2021" @@ -16,10 +16,10 @@ toml = "0.8.6" path = ".." [dependencies.multiversx-sc-snippets] -version = "0.52.3" +version = "0.53.2" [dependencies.multiversx-sc] -version = "0.52.3" +version = "0.53.2" [dependencies.clap] version = "4.4.7" diff --git a/contracts/potlock/meta/Cargo.toml b/contracts/potlock/meta/Cargo.toml index 0ac1e448..af05e594 100644 --- a/contracts/potlock/meta/Cargo.toml +++ b/contracts/potlock/meta/Cargo.toml @@ -8,5 +8,5 @@ publish = false path = ".." [dependencies.multiversx-sc-meta-lib] -version = "0.52.3" +version = "0.53.2" default-features = false diff --git a/contracts/potlock/wasm/Cargo.lock b/contracts/potlock/wasm/Cargo.lock index 4732b4c6..271f9870 100644 --- a/contracts/potlock/wasm/Cargo.lock +++ b/contracts/potlock/wasm/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "autocfg" @@ -40,9 +40,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "multiversx-sc" -version = "0.52.3" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "526760b1d6236c011285b264a70a0a9dd3b3dbc53c3b5f76932f4bcfd3a8910c" +checksum = "75ea89a26f0aacda21437a8ae5ccfbefab99d8191942b3d2eddbcbf84f9866d7" dependencies = [ "bitflags", "hex-literal", @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "multiversx-sc-codec" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad4f318427761faecf26c1f3115a3beeb5f61858845a60547d9763aa981ddd2d" +checksum = "007d7a5a8534e5dc9128cb8f15a65a21dd378e135c6016c7cd1491cd012bc8cb" dependencies = [ "arrayvec", "multiversx-sc-codec-derive", @@ -65,9 +65,9 @@ dependencies = [ [[package]] name = "multiversx-sc-codec-derive" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "476501462b0c2654b64f9dec6f2c480e24b4e9b7133ec10b7167e64acda35d04" +checksum = "dffba1dce273ed5b61ee1b90aeea5c8c744617d0f12624f620768c144d83e753" dependencies = [ "hex", "proc-macro2", @@ -77,9 +77,9 @@ dependencies = [ [[package]] name = "multiversx-sc-derive" -version = "0.52.3" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3557f2f12640a8a07fa6af66cc2a13b188c5b61bed72db22fe631fb3a60c3e96" +checksum = "4c17fdf90fafca2f19085ae67b0502d9f71bf8ab1be3c83808eb88e02a8c18b9" dependencies = [ "hex", "proc-macro2", @@ -90,18 +90,18 @@ dependencies = [ [[package]] name = "multiversx-sc-modules" -version = "0.52.3" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f5c29c6044f3dc9e866858feee625d7fae5604a68ac7bd66dec683eee97563" +checksum = "daeb48acbd39255868a3241798df2f85050f0ae8d82d6417bd2cd0e30a241855" dependencies = [ "multiversx-sc", ] [[package]] name = "multiversx-sc-wasm-adapter" -version = "0.52.3" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed13aaca9cbdbc6911174cd3029e750a7563d85dd3daaa1107b1fd31c7f17245" +checksum = "20659915a4377d375c46d7f237e810053a03f7e084fad6362dd5748a7233defb" dependencies = [ "multiversx-sc", ] @@ -151,9 +151,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -176,9 +176,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" -version = "2.0.72" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", diff --git a/contracts/potlock/wasm/Cargo.toml b/contracts/potlock/wasm/Cargo.toml index 0ead938f..1f151346 100644 --- a/contracts/potlock/wasm/Cargo.toml +++ b/contracts/potlock/wasm/Cargo.toml @@ -28,7 +28,7 @@ panic = "abort" path = ".." [dependencies.multiversx-sc-wasm-adapter] -version = "0.52.3" +version = "0.53.2" [workspace] members = ["."] diff --git a/contracts/price-aggregator/wasm/src/lib.rs b/contracts/price-aggregator/wasm/src/lib.rs index 667c425a..a8e75f49 100644 --- a/contracts/price-aggregator/wasm/src/lib.rs +++ b/contracts/price-aggregator/wasm/src/lib.rs @@ -5,9 +5,10 @@ //////////////////////////////////////////////////// // Init: 1 +// Upgrade: 1 // Endpoints: 21 // Async Callback (empty): 1 -// Total number of exported functions: 23 +// Total number of exported functions: 24 #![no_std] @@ -18,6 +19,7 @@ multiversx_sc_wasm_adapter::endpoints! { multiversx_price_aggregator_sc ( init => init + upgrade => upgrade changeAmounts => change_amounts addOracles => add_oracles removeOracles => remove_oracles From 0d838e939d689b2daba9b5649c8e934a24a69cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 9 Oct 2024 09:48:52 +0300 Subject: [PATCH 37/38] potlock: clippy fix --- contracts/potlock/interact-rs/src/potlock_interactor_main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs index e3a9f14c..36f7b51a 100644 --- a/contracts/potlock/interact-rs/src/potlock_interactor_main.rs +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -608,6 +608,8 @@ impl ContractInteract { .run() .await; + println!("Result: {result_value:?}"); + result_value } From 7e030b5ab2d4eb66e18c22113427c45e14d34361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 24 Oct 2024 17:43:25 +0300 Subject: [PATCH 38/38] Fixes after review --- contracts/potlock/src/potlock_interactions.rs | 31 +++++++++---------- contracts/potlock/src/potlock_storage.rs | 4 +++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/contracts/potlock/src/potlock_interactions.rs b/contracts/potlock/src/potlock_interactions.rs index 15ebbd09..67596617 100644 --- a/contracts/potlock/src/potlock_interactions.rs +++ b/contracts/potlock/src/potlock_interactions.rs @@ -1,3 +1,5 @@ +use __wasm__endpoints__::fee_payment; + use crate::potlock_requirements; use crate::potlock_storage::{self, Pot, Project}; use crate::potlock_storage::{PotlockId, ProjectId}; @@ -14,7 +16,12 @@ pub trait PotlockInteractions: #[payable("*")] #[endpoint(addPot)] fn add_pot(&self, name: ManagedBuffer, description: ManagedBuffer) { - let payment_for_adding_pot = self.call_value().single_esdt(); + let fee_payment = self.call_value().single_esdt(); + + require!( + fee_payment().get() == fee_payment, + "Wrong payment for creating a pot!" + ); require!( self.fee_token_identifier().get() == payment_for_adding_pot.token_identifier, "Wrong token identifier for creating a pot!" @@ -48,7 +55,7 @@ pub trait PotlockInteractions: self.require_potlock_exists(potlock_id); self.require_potlock_is_active(potlock_id); - let payment = self.call_value().single_esdt(); + let (payment_token_id, payment_amount) = self.call_value().single_fungible_esdt(); let caller = self.blockchain().get_caller(); let mut donation_mapper = self.pot_donations(potlock_id); @@ -57,26 +64,18 @@ pub trait PotlockInteractions: if opt_payment.is_some() { let mut previous_payment = opt_payment.unwrap(); require!( - previous_payment.token_identifier == payment.token_identifier.clone(), + previous_payment.token_identifier == payment_token_id.clone(), "Already made a payment with a different TokenID" ); - previous_payment.amount += payment.amount; + previous_payment.amount += payment_amount; donation_mapper.insert(caller, previous_payment); } } else { - donation_mapper.insert(caller, payment); + donation_mapper.insert( + caller, + EsdtTokenPayment::new(payment_token_id, 0, payment_amount), + ); } - - // match donation_mapper.get(&caller) { - // Some(mut previous_payment) => { - // // let a = pot_donations.get(&caller).unwrap(); - // previous_payment.amount += payment.amount; - // pot_donations.insert(caller, previous_payment); - // } - // None => { - // self.pot_donations(potlock_id).insert(caller, payment); - // } - // } } #[payable("*")] diff --git a/contracts/potlock/src/potlock_storage.rs b/contracts/potlock/src/potlock_storage.rs index 19b71bdf..011d95aa 100644 --- a/contracts/potlock/src/potlock_storage.rs +++ b/contracts/potlock/src/potlock_storage.rs @@ -82,6 +82,10 @@ pub trait PotlockStorage { #[storage_mapper("feeAmount")] fn fee_amount(&self) -> SingleValueMapper; + #[view(getFeePayment)] + #[storage_mapper("feePayment")] + fn fee_payment(&self) -> SingleValueMapper; + #[view(getPotlocks)] #[storage_mapper("potlocks")] fn potlocks(&self) -> VecMapper>;