diff --git a/Cargo.lock b/Cargo.lock index b2f60d3..6810148 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1037,6 +1037,15 @@ dependencies = [ "wasmprinter", ] +[[package]] +name = "multiversx-sc-modules" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f5c29c6044f3dc9e866858feee625d7fae5604a68ac7bd66dec683eee97563" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "multiversx-sc-scenario" version = "0.52.3" @@ -1318,6 +1327,24 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[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-lib", + "potlock", +] + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -1532,6 +1559,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rust-interact" +version = "0.0.0" +dependencies = [ + "clap", + "multiversx-sc", + "multiversx-sc-snippets", + "potlock", + "serde", + "toml", +] + [[package]] name = "rust-interact-lottery-esdt" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index dafe850..3588d5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,7 @@ members = [ "contracts/lottery-esdt", "contracts/lottery-esdt/meta", "contracts/lottery-esdt/interactor", + "contracts/potlock", + "contracts/potlock/meta", + "contracts/potlock/interact-rs", ] \ No newline at end of file diff --git a/contracts/lottery-esdt/scenarios/wrong-start-params.scen.json b/contracts/lottery-esdt/scenarios/wrong-start-params.scen.json deleted file mode 100644 index 1e12a98..0000000 --- a/contracts/lottery-esdt/scenarios/wrong-start-params.scen.json +++ /dev/null @@ -1,351 +0,0 @@ -{ - "steps": [ - { - "step": "setState", - "accounts": { - "address:OWNER_ADDRESS": { - "nonce": "1" - } - } - }, - { - "step": "setState", - "accounts": { - "address:FIRST_ADDRESS": { - "nonce": "1", - "esdt": { - "str:BSK-476470": "1000" - } - } - } - }, - { - "step": "setState", - "accounts": { - "address:SECOND_ADDRESS": { - "nonce": "1", - "esdt": { - "str:BSK-476470": "1000" - } - } - } - }, - { - "step": "setState", - "accounts": { - "address:THIRD_ADDRESS": { - "nonce": "1", - "esdt": { - "str:BSK-476470": "1000", - "str:TEST-123456": "1000" - } - } - } - }, - { - "step": "setState", - "currentBlockInfo": { - "blockTimestamp": "10" - } - }, - { - "step": "setState", - "newAddresses": [ - { - "creatorAddress": "address:OWNER_ADDRESS", - "creatorNonce": "1", - "newAddress": "sc:lottery-esdt" - } - ] - }, - { - "step": "scDeploy", - "id": "", - "tx": { - "from": "address:OWNER_ADDRESS", - "contractCode": "mxsc:../output/lottery-esdt.mxsc.json", - "arguments": [], - "gasLimit": "5,000,000" - }, - "expect": { - "out": [], - "status": "0" - } - }, - { - "step": "scCall", - "id": "", - "tx": { - "from": "address:OWNER_ADDRESS", - "to": "sc:lottery-esdt", - "function": "start", - "arguments": [ - "0x", - "0x42534b2d343736343730", - "0x01", - "0x0100000002", - "0x010000000000000014", - "0x0100000001", - "0x01000000024b19", - "0x010000000246495253545f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5345434f4e445f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f", - "0x0a" - ], - "gasLimit": "5,000,000" - }, - "expect": { - "status": "4", - "message": "str:Name can't be empty!" - } - }, - { - "step": "scCall", - "id": "", - "tx": { - "from": "address:OWNER_ADDRESS", - "to": "sc:lottery-esdt", - "function": "start", - "arguments": [ - "0x74657374", - "0x", - "0x01", - "0x0100000002", - "0x010000000000000014", - "0x0100000001", - "0x01000000024b19", - "0x010000000246495253545f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5345434f4e445f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f", - "0x0a" - ], - "gasLimit": "5,000,000" - }, - "expect": { - "status": "4", - "message": "str:Invalid token name provided!" - } - }, - { - "step": "scCall", - "id": "", - "tx": { - "from": "address:OWNER_ADDRESS", - "to": "sc:lottery-esdt", - "function": "start", - "arguments": [ - "0x74657374", - "0x42534b2d343736343730", - "0x", - "0x0100000002", - "0x010000000000000014", - "0x0100000001", - "0x01000000024b19", - "0x010000000246495253545f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5345434f4e445f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f", - "0x0a" - ], - "gasLimit": "5,000,000" - }, - "expect": { - "status": "4", - "message": "str:Ticket price must be higher than 0!" - } - }, - { - "step": "scCall", - "id": "", - "tx": { - "from": "address:OWNER_ADDRESS", - "to": "sc:lottery-esdt", - "function": "start", - "arguments": [ - "0x74657374", - "0x42534b2d343736343730", - "0x01", - "0x0100000000", - "0x010000000000000014", - "0x0100000001", - "0x01000000024b19", - "0x010000000246495253545f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5345434f4e445f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f", - "0x0a" - ], - "gasLimit": "5,000,000" - }, - "expect": { - "status": "4", - "message": "str:Must have more than 0 tickets available!" - } - }, - { - "step": "scCall", - "id": "", - "tx": { - "from": "address:OWNER_ADDRESS", - "to": "sc:lottery-esdt", - "function": "start", - "arguments": [ - "0x74657374", - "0x42534b2d343736343730", - "0x01", - "0x0100000384", - "0x010000000000000014", - "0x0100000001", - "0x01000000024b19", - "0x010000000246495253545f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5345434f4e445f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f", - "0x0a" - ], - "gasLimit": "5,000,000" - }, - "expect": { - "status": "4", - "message": "str:Only 800 or less total tickets per lottery are allowed!" - } - }, - { - "step": "scCall", - "id": "", - "tx": { - "from": "address:OWNER_ADDRESS", - "to": "sc:lottery-esdt", - "function": "start", - "arguments": [ - "0x74657374", - "0x42534b2d343736343730", - "0x01", - "0x0100000002", - "0x010000000000000000", - "0x0100000001", - "0x01000000024b19", - "0x010000000246495253545f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5345434f4e445f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f", - "0x0a" - ], - "gasLimit": "5,000,000" - }, - "expect": { - "status": "4", - "message": "str:Deadline can't be in the past!" - } - }, - { - "step": "scCall", - "id": "", - "tx": { - "from": "address:OWNER_ADDRESS", - "to": "sc:lottery-esdt", - "function": "start", - "arguments": [ - "0x74657374", - "0x42534b2d343736343730", - "0x01", - "0x0100000002", - "0x010000000005f5e100", - "0x0100000001", - "0x01000000024b19", - "0x010000000246495253545f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5345434f4e445f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f", - "0x0a" - ], - "gasLimit": "5,000,000" - }, - "expect": { - "status": "4", - "message": "str:Deadline can't be later than 30 days from now!" - } - }, - { - "step": "scCall", - "id": "", - "tx": { - "from": "address:OWNER_ADDRESS", - "to": "sc:lottery-esdt", - "function": "start", - "arguments": [ - "0x74657374", - "0x42534b2d343736343730", - "0x01", - "0x0100000002", - "0x010000000000000014", - "0x0100000000", - "0x01000000024b19", - "0x010000000246495253545f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5345434f4e445f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f", - "0x0a" - ], - "gasLimit": "5,000,000" - }, - "expect": { - "status": "4", - "message": "str:Must have more than 0 max entries per user!" - } - }, - { - "step": "scCall", - "id": "", - "tx": { - "from": "address:OWNER_ADDRESS", - "to": "sc:lottery-esdt", - "function": "start", - "arguments": [ - "0x74657374", - "0x42534b2d343736343730", - "0x01", - "0x0100000002", - "0x010000000000000014", - "0x0100000001", - "0x01000000023c3c", - "0x010000000246495253545f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5345434f4e445f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f", - "0x0a" - ], - "gasLimit": "5,000,000" - }, - "expect": { - "status": "4", - "message": "str:Prize distribution must add up to exactly 100(%)!" - } - }, - { - "step": "scCall", - "id": "", - "tx": { - "from": "address:OWNER_ADDRESS", - "to": "sc:lottery-esdt", - "function": "start", - "arguments": [ - "0x74657374", - "0x45474c44", - "0x01", - "0x0100000002", - "0x010000000000000014", - "0x0100000001", - "0x01000000024b19", - "0x010000000246495253545f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5345434f4e445f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f", - "0x65" - ], - "gasLimit": "5,000,000" - }, - "expect": { - "status": "4", - "message": "str:EGLD can't be burned!" - } - }, - { - "step": "scCall", - "id": "", - "tx": { - "from": "address:OWNER_ADDRESS", - "to": "sc:lottery-esdt", - "function": "start", - "arguments": [ - "0x74657374", - "0x42534b2d343736343730", - "0x01", - "0x0100000002", - "0x010000000000000014", - "0x0100000001", - "0x01000000024b19", - "0x010000000246495253545f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5345434f4e445f414444524553535f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f5f", - "0x65" - ], - "gasLimit": "5,000,000" - }, - "expect": { - "status": "4", - "message": "str:The contract can't burn the selected token!" - } - } - ] -} diff --git a/contracts/lottery-esdt/tests/lottery_esdt_blackbox.rs b/contracts/lottery-esdt/tests/lottery_esdt_blackbox.rs index 268adda..6071c57 100644 --- a/contracts/lottery-esdt/tests/lottery_esdt_blackbox.rs +++ b/contracts/lottery-esdt/tests/lottery_esdt_blackbox.rs @@ -26,8 +26,6 @@ impl LotteryESDTTestState{ fn new() -> Self { let mut world = world(); - world.start_trace(); - world.account(OWNER_ADDRESS).nonce(1); world @@ -65,10 +63,6 @@ impl LotteryESDTTestState{ .run(); } - fn write_scenario_trace(&mut self, file_name: &str){ - self.world.write_scenario_trace(file_name); - } - fn start_lottery(&mut self) { let lottery_name = ManagedBuffer::new_from_bytes(&b"test"[..]); @@ -76,7 +70,7 @@ impl LotteryESDTTestState{ let ticket_price = BigUint::::from(1u128); let opt_total_tickets = Option::Some(2u32); let opt_deadline = Option::Some(20u64); - let opt_max_entries_per_user = Option::Some(1u32); + let opt_max_entries_per_user = Option::Some(2u32); let prize_distribution_data: &[u8] = &[75,25]; let opt_prize_distribution = Option::Some(ManagedVec::from_iter(prize_distribution_data.iter().copied())); let mut whitelist = ManagedVec::new(); @@ -143,6 +137,7 @@ impl LotteryESDTTestState{ fn start_lottery_error_params(&mut self, lottery_name_wrong: bool, token_identifier_wrong:u64, ticket_price_wrong:bool, opt_total_tickets_wrong: Option, opt_deadline_wrong: Option, opt_max_entries_per_user_wrong: bool, opt_prize_distribution_wrong: bool, opt_burn_percentage_wrong: bool, error: ExpectError) { + let token_id_burnable : &[u8] = &b"TEST-123456"[..]; let lottery_name = if lottery_name_wrong { ManagedBuffer::new_from_bytes(&b""[..]) } else { @@ -198,6 +193,8 @@ impl LotteryESDTTestState{ // .set_roles(TOKEN_BURNABLE) // .run(); + self.world.set_esdt_local_roles(SC_ADDRESS, token_id_burnable, &[EsdtLocalRole::Burn]); + self.world .tx() .from(OWNER_ADDRESS) @@ -267,6 +264,20 @@ impl LotteryESDTTestState{ .run(); } + fn determine_winner(&mut self) + { + let lottery_name = ManagedBuffer::new_from_bytes(&b"test"[..]); + + self.world + .tx() + .from(OWNER_ADDRESS) + .to(SC_ADDRESS) + .typed(proxy::LotteryProxy) + .determine_winner(&lottery_name) + .returns(ReturnsResult) + .run(); + } + fn determine_winner_error(&mut self, error: ExpectError) { let lottery_name = ManagedBuffer::new_from_bytes(&b"test"[..]); @@ -293,7 +304,6 @@ fn lottery_esdt_blackbox_init(){ world.deploy(); - world.write_scenario_trace("scenarios/init-lottery-esdt.scen.json"); } #[test] @@ -310,8 +320,24 @@ fn lottery_esdt_blackbox_buy_all() world.buy_ticket(SECOND_ADDRESS); world.buy_ticket_error(FIRST_ADDRESS, ExpectError(4,"Lottery entry period has ended! Awaiting winner announcement.")); +} + +#[test] +fn lottery_esdt_blackbox_buy_after_winner_announced() { + let mut world = LotteryESDTTestState::new(); + + world.deploy(); + + world.start_lottery(); - world.write_scenario_trace("scenarios/buy-all-tickets-and-exceed-max-tickets.scen.json"); + world.buy_ticket(FIRST_ADDRESS); + + world.buy_ticket(SECOND_ADDRESS); + + world.determine_winner(); + + world.buy_ticket_error(FIRST_ADDRESS, ExpectError(4,"Lottery is currently inactive.")); + } #[test] @@ -331,8 +357,6 @@ fn lottery_esdt_blackbox_buy_after_deadline() world.buy_ticket_error(FIRST_ADDRESS, ExpectError(4,"Lottery entry period has ended! Awaiting winner announcement.")); - world.write_scenario_trace("scenarios/buy-after-deadline.scen.json"); - } #[test] @@ -350,8 +374,6 @@ fn lottery_esdt_blackbox_buy_after_sold_out() world.buy_ticket_error(FIRST_ADDRESS, ExpectError(4,"Lottery entry period has ended! Awaiting winner announcement.")); - world.write_scenario_trace("scenarios/buy-after-sold-out.scen.json"); - } #[test] @@ -365,8 +387,6 @@ fn lottery_esdt_blackbox_buy_not_whitelisted() world.buy_ticket_error(THIRD_ADDRESS, ExpectError(4, "You are not allowed to participate in this lottery!")); - world.write_scenario_trace("scenarios/buy-not-whitelisted.scen.json"); - } #[test] @@ -379,8 +399,6 @@ fn lottery_esdt_blackbox_buy_wrong_fee() world.start_lottery(); world.buy_ticket_wrong_fee(FIRST_ADDRESS, BigUint::::from(2u128)); - - world.write_scenario_trace("scenarios/buy-wrong-fee.scen.json"); } @@ -397,8 +415,22 @@ fn lottery_esdt_blackbox_determine_winner_early() world.determine_winner_error(ExpectError(4,"Lottery is still running!")); - world.write_scenario_trace("scenarios/determine-winner-early.scen.json"); +} +#[test] +fn lottery_esdt_blackbox_determine_winner_one_participant() { + let mut world = LotteryESDTTestState::new(); + + world.deploy(); + + world.start_lottery(); + + world.buy_ticket(FIRST_ADDRESS); + + world.buy_ticket(FIRST_ADDRESS); + + world.determine_winner(); + } #[test] @@ -412,8 +444,6 @@ fn lottery_esdt_blackbox_start_lottery_twice() world.start_lottery_error(ExpectError(4,"Lottery is already active!")); - world.write_scenario_trace("scenarios/start-lottery-twice.scen.json"); - } #[test] @@ -452,8 +482,6 @@ fn lottery_esdt_blackbox_wrong_start_params() world.start_lottery_error_params(false, 0, false, total_tickets, deadline, false, false, true, ExpectError(4,"The contract can't burn the selected token!")); - //world.start_lottery_error_params(false, 3, false, total_tickets, deadline, false, false, true, ExpectError(4,"Invalid burn percentage!")); - - world.write_scenario_trace("scenarios/wrong-start-params.scen.json"); + world.start_lottery_error_params(false, 3, false, total_tickets, deadline, false, false, true, ExpectError(4,"Invalid burn percentage!")); } diff --git a/contracts/lottery-esdt/tests/lottery_esdt_scenario_go_test.rs b/contracts/lottery-esdt/tests/lottery_esdt_scenario_go_test.rs index c7c12b7..7d19c5e 100644 --- a/contracts/lottery-esdt/tests/lottery_esdt_scenario_go_test.rs +++ b/contracts/lottery-esdt/tests/lottery_esdt_scenario_go_test.rs @@ -188,8 +188,3 @@ fn start_with_all_options_go() { fn start_with_no_options_go() { world().run("scenarios/start-with-no-options.scen.json"); } - -#[test] -fn wrong_start_params_go() { - world().run("scenarios/wrong-start-params.scen.json"); -} diff --git a/contracts/lottery-esdt/tests/lottery_esdt_scenario_rs_test.rs b/contracts/lottery-esdt/tests/lottery_esdt_scenario_rs_test.rs index 07e5a22..6f6d60e 100644 --- a/contracts/lottery-esdt/tests/lottery_esdt_scenario_rs_test.rs +++ b/contracts/lottery-esdt/tests/lottery_esdt_scenario_rs_test.rs @@ -199,8 +199,3 @@ fn start_with_all_options_rs() { fn start_with_no_options_rs() { world().run("scenarios/start-with-no-options.scen.json"); } - -#[test] -fn wrong_start_params_rs() { - world().run("scenarios/wrong-start-params.scen.json"); -} diff --git a/contracts/potlock/Cargo.toml b/contracts/potlock/Cargo.toml new file mode 100644 index 0000000..783b1e4 --- /dev/null +++ b/contracts/potlock/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "potlock" +version = "0.0.0" +authors = ["you"] +edition = "2021" +publish = false +readme = "README.md" + +[lib] +path = "src/potlock.rs" + +[dependencies.multiversx-sc] +version = "0.52.3" + +[dependencies.multiversx-sc-modules] +version = "=0.52.3" + +[dev-dependencies] +num-bigint = "0.4.2" + +[dev-dependencies.multiversx-sc-scenario] +version = "0.52.3" diff --git a/contracts/potlock/README.md b/contracts/potlock/README.md new file mode 100644 index 0000000..f13a60f --- /dev/null +++ b/contracts/potlock/README.md @@ -0,0 +1,27 @@ +# 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. + + + +## 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 diff --git a/contracts/potlock/interact-rs/.gitignore b/contracts/potlock/interact-rs/.gitignore new file mode 100644 index 0000000..5a64d09 --- /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 0000000..e0cb932 --- /dev/null +++ b/contracts/potlock/interact-rs/Cargo.toml @@ -0,0 +1,30 @@ +[[bin]] +name = "rust-interact" +path = "src/potlock_interactor_main.rs" + +[package] +name = "rust-interact" +version = "0.0.0" +authors = ["you"] +edition = "2021" +publish = false + +[dependencies] +toml = "0.8.6" + +[dependencies.potlock] +path = ".." + +[dependencies.multiversx-sc-snippets] +version = "0.52.3" + +[dependencies.multiversx-sc] +version = "0.52.3" + +[dependencies.clap] +version = "4.4.7" +features = ["derive"] + +[dependencies.serde] +version = "1.0" +features = ["derive"] diff --git a/contracts/potlock/interact-rs/config.toml b/contracts/potlock/interact-rs/config.toml new file mode 100644 index 0000000..dbbe25d --- /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 0000000..77a3476 --- /dev/null +++ b/contracts/potlock/interact-rs/src/potlock_interactor_config.rs @@ -0,0 +1,32 @@ +use multiversx_sc_snippets::imports::Bech32Address; +use serde::Deserialize; +use std::io::Read; + +/// Config file +const CONFIG_FILE: &str = "config.toml"; + +/// Multisig Interact configuration +#[derive(Debug, Deserialize)] +pub struct Config { + 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() + } + + // 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 new file mode 100644 index 0000000..85eba28 --- /dev/null +++ b/contracts/potlock/interact-rs/src/potlock_interactor_main.rs @@ -0,0 +1,565 @@ +#![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::gateway::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, + "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), + } +} + +#[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 admins = MultiValueVec::from(vec![bech32::decode("")]); + + let new_address = self + .interactor + .tx() + .from(&self.wallet_address) + .gas(30_000_000u64) + .typed(proxy::PotlockProxy) + .init(admins) + .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 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 potlock_id = 0u32; + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .gas(30_000_000u64) + .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()) + .gas(30_000_000u64) + .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()) + .gas(30_000_000u64) + .typed(proxy::PotlockProxy) + .accept_application(project_id) + .returns(ReturnsResultUnmanaged) + .prepare_async() + .run() + .await; + + 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 potlock_id = 0u32; + let user = bech32::decode(""); + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .gas(30_000_000u64) + .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_percentages = + MultiValueVec::from(vec![MultiValue2::::from((0u32, 0u64))]); + + let response = self + .interactor + .tx() + .from(&self.wallet_address) + .to(self.state.current_address()) + .gas(30_000_000u64) + .typed(proxy::PotlockProxy) + .distribute_pot_to_projects(potlock_id, project_percentages) + .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()) + .gas(30_000_000u64) + .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()) + .gas(30_000_000u64) + .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()) + .gas(30_000_000u64) + .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()) + .gas(30_000_000u64) + .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 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 pot_donations(&mut self) { + let potlock_id = 0u32; + + let result_value = self + .interactor + .query() + .to(self.state.current_address()) + .typed(proxy::PotlockProxy) + .pot_donations(potlock_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()) + .gas(30_000_000u64) + .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()) + .gas(30_000_000u64) + .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 0000000..7abb266 --- /dev/null +++ b/contracts/potlock/interact-rs/src/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, + admins: Arg0, + ) -> TxTypedDeploy { + self.wrapped_tx + .payment(NotPayable) + .raw_deploy() + .argument(&admins) + .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 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, + >( + 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 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>, + >( + 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_percentages: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("distributePotToProjects") + .argument(&potlock_id) + .argument(&project_percentages) + .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 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 pot_donations< + Arg0: ProxyArg, + >( + self, + potlock_id: Arg0, + ) -> TxTypedCall, EsdtTokenPayment>>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("potDonations") + .argument(&potlock_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, Debug)] +pub struct Pot +where + Api: ManagedTypeApi, +{ + pub potlock_id: usize, + pub proposer: ManagedAddress, + pub token_identifier: TokenIdentifier, + pub fee: BigUint, + pub name: ManagedBuffer, + pub description: ManagedBuffer, + pub status: Status, +} + +#[type_abi] +#[derive(TopEncode, TopDecode, NestedDecode, NestedEncode, Debug)] +pub enum Status { + Inactive, + Active, +} + +#[type_abi] +#[derive(TopEncode, TopDecode, Debug)] +pub struct Project +where + Api: ManagedTypeApi, +{ + pub potlock_id: usize, + pub name: ManagedBuffer, + pub description: ManagedBuffer, + pub owner: ManagedAddress, + pub status: Status, +} diff --git a/contracts/potlock/interact-rs/state.toml b/contracts/potlock/interact-rs/state.toml new file mode 100644 index 0000000..6a70e4c --- /dev/null +++ b/contracts/potlock/interact-rs/state.toml @@ -0,0 +1 @@ +contract_address = "erd1qqqqqqqqqqqqqpgq5upecnmqrjd673jhuy36z7ehdgsgkkuvd8ssdxdcaq" diff --git a/contracts/potlock/meta/Cargo.toml b/contracts/potlock/meta/Cargo.toml new file mode 100644 index 0000000..0ac1e44 --- /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-lib] +version = "0.52.3" +default-features = false diff --git a/contracts/potlock/meta/src/main.rs b/contracts/potlock/meta/src/main.rs new file mode 100644 index 0000000..1514f0f --- /dev/null +++ b/contracts/potlock/meta/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + multiversx_sc_meta_lib::cli_main::(); +} diff --git a/contracts/potlock/multiversx.json b/contracts/potlock/multiversx.json new file mode 100644 index 0000000..7365539 --- /dev/null +++ b/contracts/potlock/multiversx.json @@ -0,0 +1,3 @@ +{ + "language": "rust" +} \ No newline at end of file diff --git a/contracts/potlock/output/potlock.abi.json b/contracts/potlock/output/potlock.abi.json new file mode 100644 index 0000000..90b53bf --- /dev/null +++ b/contracts/potlock/output/potlock.abi.json @@ -0,0 +1,419 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.79.0", + "commitHash": "129f3b9964af4d4a709d1383930ade12dfe7c081", + "commitDate": "2024-06-10", + "channel": "Stable", + "short": "rustc 1.79.0 (129f3b996 2024-06-10)" + }, + "contractCrate": { + "name": "potlock", + "version": "0.0.0", + "gitVersion": "v0.45.2.1-reproducible-388-g906e146" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.52.3" + } + }, + "name": "Potlock", + "constructor": { + "inputs": [ + { + "name": "admins", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + "upgradeConstructor": { + "inputs": [], + "outputs": [] + }, + "endpoints": [ + { + "name": "changeFeeForPots", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "fee", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "acceptPot", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "removePot", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "acceptApplication", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "project_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "removeApplication", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "project_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "rejectDonation", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + }, + { + "name": "user", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "distributePotToProjects", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + }, + { + "name": "project_percentages", + "type": "variadic>", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "addPot", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "name", + "type": "bytes" + }, + { + "name": "description", + "type": "bytes" + } + ], + "outputs": [] + }, + { + "name": "applyForPot", + "mutability": "mutable", + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + }, + { + "name": "project_name", + "type": "bytes" + }, + { + "name": "description", + "type": "bytes" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "donateToPot", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "donateToProject", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "project_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "getFeeTokenIdentifier", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "TokenIdentifier" + } + ] + }, + { + "name": "getFeeAmount", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getPotlocks", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + }, + { + "name": "getProjects", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + }, + { + "name": "potDonations", + "mutability": "readonly", + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "variadic>", + "multi_result": true + } + ] + }, + { + "name": "projectDonations", + "mutability": "readonly", + "inputs": [ + { + "name": "project_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "variadic>", + "multi_result": true + } + ] + }, + { + "name": "isAdmin", + "mutability": "readonly", + "inputs": [ + { + "name": "address", + "type": "Address" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "name": "addAdmin", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "removeAdmin", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "getAdmins", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic
", + "multi_result": true + } + ] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "EsdtTokenPayment": { + "type": "struct", + "fields": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "token_nonce", + "type": "u64" + }, + { + "name": "amount", + "type": "BigUint" + } + ] + }, + "Pot": { + "type": "struct", + "fields": [ + { + "name": "potlock_id", + "type": "u32" + }, + { + "name": "proposer", + "type": "Address" + }, + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "fee", + "type": "BigUint" + }, + { + "name": "name", + "type": "bytes" + }, + { + "name": "description", + "type": "bytes" + }, + { + "name": "status", + "type": "Status" + } + ] + }, + "Project": { + "type": "struct", + "fields": [ + { + "name": "potlock_id", + "type": "u32" + }, + { + "name": "name", + "type": "bytes" + }, + { + "name": "description", + "type": "bytes" + }, + { + "name": "owner", + "type": "Address" + }, + { + "name": "status", + "type": "Status" + } + ] + }, + "Status": { + "type": "enum", + "variants": [ + { + "name": "Inactive", + "discriminant": 0 + }, + { + "name": "Active", + "discriminant": 1 + } + ] + } + } +} diff --git a/contracts/potlock/output/potlock.imports.json b/contracts/potlock/output/potlock.imports.json new file mode 100644 index 0000000..015532e --- /dev/null +++ b/contracts/potlock/output/potlock.imports.json @@ -0,0 +1,36 @@ +[ + "bigIntAdd", + "bigIntCmp", + "bigIntFinishUnsigned", + "bigIntGetUnsignedArgument", + "bigIntMul", + "bigIntSetInt64", + "bigIntSign", + "bigIntTDiv", + "checkNoPayment", + "getNumArguments", + "mBufferAppend", + "mBufferAppendBytes", + "mBufferCopyByteSlice", + "mBufferEq", + "mBufferFinish", + "mBufferFromBigIntUnsigned", + "mBufferGetArgument", + "mBufferGetByteSlice", + "mBufferGetLength", + "mBufferNew", + "mBufferSetBytes", + "mBufferStorageLoad", + "mBufferStorageStore", + "mBufferToBigIntUnsigned", + "managedCaller", + "managedGetMultiESDTCallValue", + "managedMultiTransferESDTNFTExecute", + "managedOwnerAddress", + "managedSignalError", + "signalError", + "smallIntFinishSigned", + "smallIntFinishUnsigned", + "smallIntGetUnsignedArgument", + "validateTokenIdentifier" +] \ No newline at end of file diff --git a/contracts/potlock/output/potlock.mxsc.json b/contracts/potlock/output/potlock.mxsc.json new file mode 100644 index 0000000..0318e4a --- /dev/null +++ b/contracts/potlock/output/potlock.mxsc.json @@ -0,0 +1,470 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.79.0", + "commitHash": "129f3b9964af4d4a709d1383930ade12dfe7c081", + "commitDate": "2024-06-10", + "channel": "Stable", + "short": "rustc 1.79.0 (129f3b996 2024-06-10)" + }, + "contractCrate": { + "name": "potlock", + "version": "0.0.0" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.52.3" + } + }, + "abi": { + "name": "Potlock", + "constructor": { + "inputs": [ + { + "name": "admins", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + "upgradeConstructor": { + "inputs": [], + "outputs": [] + }, + "endpoints": [ + { + "name": "changeFeeForPots", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "fee", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "acceptPot", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "removePot", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "acceptApplication", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "project_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "removeApplication", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "project_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "rejectDonation", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + }, + { + "name": "user", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "distributePotToProjects", + "onlyAdmin": true, + "mutability": "mutable", + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + }, + { + "name": "project_percentages", + "type": "variadic>", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "addPot", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "name", + "type": "bytes" + }, + { + "name": "description", + "type": "bytes" + } + ], + "outputs": [] + }, + { + "name": "applyForPot", + "mutability": "mutable", + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + }, + { + "name": "project_name", + "type": "bytes" + }, + { + "name": "description", + "type": "bytes" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "donateToPot", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "donateToProject", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "project_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "getFeeTokenIdentifier", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "TokenIdentifier" + } + ] + }, + { + "name": "getFeeAmount", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getPotlocks", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + }, + { + "name": "getProjects", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + }, + { + "name": "potDonations", + "mutability": "readonly", + "inputs": [ + { + "name": "potlock_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "variadic>", + "multi_result": true + } + ] + }, + { + "name": "projectDonations", + "mutability": "readonly", + "inputs": [ + { + "name": "project_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "variadic>", + "multi_result": true + } + ] + }, + { + "name": "isAdmin", + "mutability": "readonly", + "inputs": [ + { + "name": "address", + "type": "Address" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "name": "addAdmin", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "removeAdmin", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "getAdmins", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic
", + "multi_result": true + } + ] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "EsdtTokenPayment": { + "type": "struct", + "fields": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "token_nonce", + "type": "u64" + }, + { + "name": "amount", + "type": "BigUint" + } + ] + }, + "Pot": { + "type": "struct", + "fields": [ + { + "name": "potlock_id", + "type": "u32" + }, + { + "name": "proposer", + "type": "Address" + }, + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "fee", + "type": "BigUint" + }, + { + "name": "name", + "type": "bytes" + }, + { + "name": "description", + "type": "bytes" + }, + { + "name": "status", + "type": "Status" + } + ] + }, + "Project": { + "type": "struct", + "fields": [ + { + "name": "potlock_id", + "type": "u32" + }, + { + "name": "name", + "type": "bytes" + }, + { + "name": "description", + "type": "bytes" + }, + { + "name": "owner", + "type": "Address" + }, + { + "name": "status", + "type": "Status" + } + ] + }, + "Status": { + "type": "enum", + "variants": [ + { + "name": "Inactive", + "discriminant": 0 + }, + { + "name": "Active", + "discriminant": 1 + } + ] + } + } + }, + "code": "0061736d01000000015f1160027f7f0060000060027f7f017f60017f0060017f017f60037f7f7f0060047f7f7f7f006000017f60037f7f7f017f60047f7f7f7f017f60027f7e0060017f017e60017e0060057f7f7e7f7f017f60027e7f0060017e017f60027f7f017e02b9062203656e760b7369676e616c4572726f72000003656e760a6d4275666665724e6577000703656e760d6d427566666572417070656e64000203656e76096d4275666665724571000203656e760d6d42756666657246696e697368000403656e76106d4275666665724765744c656e677468000403656e760e626967496e74536574496e743634000a03656e7609626967496e74416464000503656e76226d616e616765644d756c74695472616e73666572455344544e465445786563757465000d03656e760d6d616e6167656443616c6c6572000303656e76136d616e616765644f776e657241646472657373000303656e761c6d616e616765644765744d756c74694553445443616c6c56616c7565000303656e76126d427566666572476574417267756d656e74000203656e76126d427566666572417070656e644279746573000803656e76126d616e616765645369676e616c4572726f72000303656e7619626967496e74476574556e7369676e6564417267756d656e74000003656e761b736d616c6c496e74476574556e7369676e6564417267756d656e74000b03656e760f6765744e756d417267756d656e7473000703656e76146d427566666572436f707942797465536c696365000903656e760a626967496e745369676e000403656e76136d42756666657247657442797465536c696365000903656e760f6d4275666665725365744279746573000803656e76196d42756666657246726f6d426967496e74556e7369676e6564000203656e76176d427566666572546f426967496e74556e7369676e6564000203656e76126d42756666657253746f726167654c6f6164000203656e76136d42756666657253746f7261676553746f7265000203656e760e636865636b4e6f5061796d656e74000103656e761776616c6964617465546f6b656e4964656e746966696572000403656e7609626967496e744d756c000503656e760a626967496e7454446976000503656e7609626967496e74436d70000203656e7616736d616c6c496e7446696e697368556e7369676e6564000c03656e7614626967496e7446696e697368556e7369676e6564000303656e7614736d616c6c496e7446696e6973685369676e6564000c0391018f010206050005040200050200060702030004020204000400000004030707000607010309010406040004080303030103000e020800000f04000000000000000305030002060200060900020a050002100006020502020405060604000004000400020502020004000b01040307000007030303030303010101010101010101010101010101010101010101010101010005030100030616037f01418080080b7f004185d5080b7f004190d5080b07a2031b066d656d6f7279020004696e69740097010775706772616465009801106368616e6765466565466f72506f747300990109616363657074506f74009a010972656d6f7665506f74009b01116163636570744170706c69636174696f6e009c011172656d6f76654170706c69636174696f6e009d010e72656a656374446f6e6174696f6e009e011764697374726962757465506f74546f50726f6a65637473009f0106616464506f7400a0010b6170706c79466f72506f7400a1010b646f6e617465546f506f7400a2010f646f6e617465546f50726f6a65637400a30115676574466565546f6b656e4964656e74696669657200a4010c676574466565416d6f756e7400a5010b676574506f746c6f636b7300a6010b67657450726f6a6563747300a7010c706f74446f6e6174696f6e7300a8011070726f6a656374446f6e6174696f6e7300a90107697341646d696e00aa010861646441646d696e00ab010b72656d6f766541646d696e00ac010967657441646d696e7300ad010863616c6c4261636b00ae010a5f5f646174615f656e6403010b5f5f686561705f6261736503020adb4f8f014c01027f230041106b22022400200241003a000f20002002410f6a41012001102302400240024020022d000f0e020201000b2001418e8008410d1024000b410121030b200241106a240020030bf00301077f2000280200210702400240024002400240024020002d001045044020002802082204100522054190ce004b0d0141fcd4082d00000d0141f8d408200536020041fcd40841013a00002004410041e88608200510441a200041013a00100b200220076a220841f8d4082802004d0d010c050b200041003a0010200420072001200210440d04200220076a21080c010b200720084b0d0120084190ce004b0d02200741e886086a210320012104200241104f04402004410020046b41037122016a210620010440200321050340200420052d00003a0000200541016a2105200441016a22042006490d000b0b2006200220016b220a417c7122096a21040240200120036a22014103710440200941004c0d012001410374220241187121072001417c71220541046a2103410020026b4118712102200528020021050340200620052007762003280200220520027472360200200341046a2103200641046a22062004490d000b0c010b200941004c0d0020012103034020062003280200360200200341046a2103200641046a22062004490d000b0b200120096a2103200a41037121020b20020440200220046a21010340200420032d00003a0000200341016a2103200441016a22042001490d000b0b0b200020083602000f0b10af01000b1045000b200341938108410f1024000b2c01017f41e38108411b102f2203200010021a200341d480084103100d1a200320012002100d1a2003100e000b2d01017f230041106b220224002002200041ff01714100473a000f20012002410f6a41011026200241106a24000b0b00200020012002100d1a0b0f01017f10012201200010021a20010b0b0020002001100341004a0b5b01037f230041106b2203240020012802042202047f200341086a200128020022042802002002102a2001200328020c36020420042802002002102b210241010541000b21012000200236020420002001360200200341106a24000b800101027f230041206b220324002003410c6a2204200141c78108410b200210692201103120042001105321022003410c6a2001105321042003280210200328020c46044020032d001c044041f8d408410036020041fcd40841003a00000b2000200436020420002002360200200341206a24000f0b200141808008410e1024000b1000200041d2810841062001106910770bc90102027f027e230041306b220224002002200110290240024020022802004101460440200241186a20012802082201280200200141086a28020020022802042201102d2002290318500d02200241106a200241286a22032903002204370300200220022903202205370308200241246a200437020020002001360208200042013703002002200537021c2000200229021837020c200041146a200241206a2902003702002000411c6a20032802003602000c010b200042003703000b200241306a24000f0b10af01000b2201017e2000200120031066047e200041086a20022003106d42010542000b3703000b080041014100102f0b1101017f103e22022000200110151a20020b0700200010041a0b3101017f20011032220210052101200041003a00102000200136020c2000200236020820002001360204200041003602000b0d002000103e220010181a20000b0a0020002001103410350b0e002000200020011053200110540b0d002000103e220010171a20000b0a0020001037200110380b0f01017f103e2201200010161a20010b4f01027f230041106b22022400200220001005220341187420034180fe03714108747220034108764180fe03712003411876727236020c20012002410c6a41041026200120001055200241106a24000b0900200020011000000b870102067f017e230041206b22022400200128020c2204103b41ff0171044020012903002108200241086a103c200228020c210520022802082106103d2103200128020810272107103e22014200100620012001200410072002200136021c20022008370310200220073602182003200241106a103f200020032006200510400b200241206a24000b1300417f20001013220041004720004100481b0b1601017f103d21012000103d360204200020013602000b1101017f103e22004101410010151a20000b1901017f41e4860841e4860828020041016b220036020020000bd20102027f017e230041106b2203240020032001280208220241187420024180fe03714108747220024108764180fe0371200241187672723602002003200128020c220241187420024180fe03714108747220024108764180fe03712002411876727236020c20032001290300220442388620044280fe0383422886842004428080fc0783421886200442808080f80f834208868484200442088842808080f80f832004421888428080fc07838420044228884280fe03832004423888848484370204200020034110100d1a200341106a24000b0f002000200142002002200310081a0b0c01017f103e2200100920000b1d01017f103e2200100a20001041102804400f0b41a0860841241000000bf002020a7f017e230041106b22022400416b210302404184d5082d000022010440416b41ffffffff0720011b21030c010b4184d50841013a0000416b100b0b024020031005417071411046044041002101200310052106200241086a2107410121040340200141106a220820064b0d022007420037030020024200370300200320012002411010441a200404402002290204220b423886200b4280fe038342288684200b428080fc0783421886200b42808080f80f834208868484200b42088842808080f80f83200b421888428080fc078384200b4228884280fe0383200b423888848484210b2002280200220141187420014180fe03714108747220014108764180fe0371200141187672722109200228020c220141187420014180fe03714108747220014108764180fe037120014118767272210a4101210541002104200821010c010b0b1045000b419b800841221000000b2000200a36020c200020093602082000200b370300200241106a24000b0f00200020012003200210144100470b060010af01000b0d002000103e2200100c1a20000b2e01017f41bd80084117102f220420002001100d1a200441d480084103100d1a200420022003100d1a2004100e000b3b01037f103d21022000280200210103404180d50828020020014a04402000200141016a22033602002002200110461049200321010c010b0b20020b4601017f230041106b220224002002200141187420014180fe03714108747220014108764180fe03712001411876727236020c20002002410c6a4104100d1a200241106a24000b2401017e4100101022014280808080105a04402000410a41808008410e1047000b2001a70b1f0020001046220010054120470440200120024182820841101047000b20000b190020004180d5082802004e04400f0b41e8800841121000000b1400101120004604400f0b41fa800841191000000b190020004180d5082802004c04400f0b41d7800841111000000b0b004180d50810113602000b7b02027f017e230041406a22012400200141086a200041046a1051200129030821032001200036021c200120033702140340200141206a200141146a102c200129032050450440200128023c20012903302001280238200128022810041a102e220010382000105220001036200010300c010b0b200141406b24000b2e01017f230041106b220224002002200128020010672000200228020436020420002001360200200241106a24000b7701017f230041106b220224002002200042388620004280fe0383422886842000428080fc0783421886200042808080f80f834208868484200042088842808080f80f832000421888428080fc07838420004228884280fe038320004238888484843703082001200241086a41081026200241106a24000b5001017f230041106b220224002002410036020c20002002410c6a410420011023200228020c2100200241106a2400200041187420004180fe03714108747220004108764180fe0371200041187672720b3801027f2000280200210310012104200028020820032001200410124504402000200120036a36020020040f0b200241938108410f1024000b09002000200110021a0b0a0020002000200110070b1b002000420053044041a2810841111000000b41722000100641720b0d002000416710181a416710050b080020012000105a0b4601017f230041106b220224002002200041187420004180fe03714108747220004108764180fe03712000411876727236020c20012002410c6a4104100d1a200241106a24000b080020002001105c0b09002000200110191a0b3b01017f102e210220012802002002105e2001280204200210382001280208200210382002200128020c105520012d00102002102520002002105c0b080020002001107f0b4d01017f102e210220012802002002105e200220012802041055200128020820021038200128020c2002103620012802102002103820012802142002103820012d00182002102520002002105c0b0a0020004101410010610b0d00200020012002102f10191a0b1200416c4101410010151a2000416c10191a0b0a0020002001106410620b190020001027220041b381084107100d1a20002001105520000bef0101037f230041206b22042400200020012802002206200128020822002002102d200020021064102e2100200328020820001038200329030020001052200328020c200010362000105c200620021066450440200441106a2001280204220310672004200428021c41016a220036021c02402004280210220545044020042000360214410021010c010b200441086a200320042802182201102a200320012004280208200010680b2003200020014100106820042000360218200341d281084106200010692002105b2004200541016a3602102003200441106a106a20062002106b2000ad106c0b200441206a24000b0b0020002001106f4100470bab0101057f230041206b22022400024002402001107e220110584504400c010b2002410c6a22032001103120032001105321062002410c6a2001105321032002410c6a2001105321042002410c6a2001105321052002280210200228020c470d0120022d001c450d0041f8d408410036020041fcd40841003a00000b2000200536020c200020043602082000200336020420002006360200200241206a24000f0b200141808008410e1024000b2000200041c78108410b200110692002102e2200107f20032000107f2000105c0b170020001027220020012002100d1a20032000105a20000b4601017f2000107e21022001280200220004402000102e2200107f20012802042000107f20012802082000107f200128020c2000107f20022000105c0f0b20024101410010610b190020001027220041ba81084108100d1a20002001105520000b890101047f230041106b22022400200220013c000f20022001421888a722033a000c20022001421088a722043a000d20022001420888a722053a000e200241003602082000200320047241ff017145220041044105200341ff01711b6a41002000200541ff01711b22006a200041002001501b6a2200200241086a6a410820006b1061200241106a24000bec0102027f017e230041206b22032400200341046a2204200120021064220110312004200110342102200342003703182004200341186a4108200110232003290318210520042001103321042003280208200328020446044020032d0014044041f8d408410036020041fcd40841003a00000b2000200436020c200020023602082000200542388620054280fe0383422886842005428080fc0783421886200542808080f80f834208868484200542088842808080f80f832005421888428080fc07838420054228884280fe03832005423888848484370300200341206a24000f0b200141808008410e1024000b0a0020002001106b10600b2a01017e20002001106b22002000107022024280808080105a0440200041808008410e1024000b2002a70ba30102017e027f230041106b22032400200342003703082000103222041005220041094f0440200141808008410e1024000b20044100200320006b41106a200010441a20032903082102200341106a2400200242388620024280fe0383422886842002428080fc0783421886200242808080f80f834208868484200242088842808080f80f832002421888428080fc07838420024228884280fe038320024238888484840b090020002001ad106c0b270002402002450d002001107b2002490d002000200210732003105d0f0b41c4860841121000000b190020001027220041c281084105100d1a20002001105920000ba70101057f230041206b220324002003410c6a220420012002107322011031200420011053210220042001103421052003410c6a200110342106200420011075210720042001102221042003280210200328020c46044020032d001c044041f8d408410036020041fcd40841003a00000b200020043a00102000200736020c200020063602082000200536020420002002360200200341206a24000f0b200141808008410e1024000b0a0020004120200110540b0a0020002001107310770b1f01017f2000103222011005412047044020004182820841101024000b20010bc80101077f230041206b220324002003410c6a22042001200210732201103120042001105321022004200110752105200420011034210620042001103321072003410c6a2001103421082003410c6a20011034210920042001102221042003280210200328020c46044020032d001c044041f8d408410036020041fcd40841003a00000b200020043a001820002009360214200020083602102000200736020c200020063602082000200536020420002002360200200341206a24000f0b200141808008410e1024000b250002402003450d002002107b2003490d0020002001200310780f0b41c4860841121000000b250002402003450d002002107b2003490d0020002001200310740f0b41c4860841121000000b2401017e20002000107022014280808080105a0440200041808008410e1024000b2001a70b1000200041c78108410b2001106910600b1000200041d2810841062001106910600b130020001027220041d881084105100d1a20000b4501017f230041106b220224002002200041187420004180fe03714108747220004108764180fe03712000411876727236020c20012002410c6a41041026200241106a24000b08002000103210350b4201037f20002802082203200110820145044020002802042202107b21042000280200200441016a220010732001105b200220001071200320012002107b1083010b0b0c00200020011085014100470b0e00200020011084012002ad106c0b190020001027220041dd81084106100d1a20002001105520000b0b0020002001108401107b0b2501027f2001102721022001102721032000200136020420002003360200200020023602080b7d01037f230041106b22012400200028020821032001410036020c200028020020034102742001410c6a41041044450440200128020c21022000200341016a360208200241187420024180fe03714108747220024108764180fe0371200241187672721027200141106a24000f0b41928208410841d7800841111047000b5601017e024020002001280208200128020449047e200110870110890122024280808080105a0d0120002001108701108901370310200020023e020842010542000b3703000f0b41928208410841808008410e1047000ba30102017e027f230041106b220224002002420037030820001005220341094f044041928208410841808008410e1047000b20004100200220036b41106a200310441a20022903082101200241106a2400200142388620014280fe0383422886842001428080fc0783421886200142808080f80f834208868484200142088842808080f80f832001421888428080fc07838420014228884280fe038320014238888484840b14001041108b01450440419a820841251039000b0b2601017f230041106b22012400200141046a108c01200128020c2000108201200141106a24000b3601037f418d83084118102f22011027210220011027220341fe81084104100d1a2000200336020420002001360200200020023602080b0a0041c682084109102f0b1901017f41cf8208410c102f220220011059200020021086010b1901017f41db82084110102f220220011059200020021086010b0a0041eb82084112102f0b0b00200041fd820810b0010b0b0020004185830810b0010b6701027f230041106b2201240002402000450d00200141086a109101200128020c107b2000490d002001109101200128020021022001280204107b20004f04402002200010731058450d01200141106a24000f0b41c4860841121000000b41bb840841161039000b6701027f230041106b2201240002402000450d00200141086a109201200128020c107b2000490d002001109201200128020021022001280204107b20004f04402002200010731058450d01200141106a24000f0b41c4860841121000000b41d1840841161039000b3e01017f230041206b2201240020011092012001410c6a200128020020012802042000107a20012d001c45044041f9840841161039000b200141206a24000b4001017f230041306b22012400200141086a109101200141146a2001280208200128020c2000107920012d002c044041af8508410e1039000b200141306a24000bb70101057f230041206b22002400101a104f4100104e20004100360214200041146a2203104821042000280214104c104121012003108c01200320011081012004100521012000410036021020002001410276220136020c2000200436020802400340200120024b0440200041086a1087011027220210054120470d02200041146a2201108c0120012002108101200028020c2101200028021021020c010b0b200041206a24000f0b4192820841084182820841101047000b0800101a4100104d0b6101027f101a108a014102104d4100104621004101103e2201100f02402000101b04402001103b41ff01714101470d01109001200010191a108d012200105845044020002001103710191a0b0f0b41ff850841161039000b41958608410b1039000b810101047f230041306b22002400101a108a014101104d41a58308104a22011093012001109601200041086a109101200041146a22022000280208200028020c20011079200041013a002c200010910102402001450d0020002802002000280204107b2001490d00200110732002105f200041306a24000f0b41c4860841121000000ba10101057f230041306b22002400101a108a014101104d41a58308104a22011093012001109601200041086a109101200041106a22022000280208200028020c200110792000280214109001103221042000108d0110800136021c20004200370310200020043602182002103a200010910102402001450d0020002802002000280204107b2001490d00200110731062200041306a24000f0b41c4860841121000000b8d0101037f230041306b22002400101a108a014101104d41af8308104a2201109401200041106a1092012000411c6a200028021020002802142001107a20002d002c044041bd850841121039000b200041086a1092012000411c6a22022000280208200028020c2001107a200041013a002c200010920120002802002000280204200120021072200041306a24000b6901037f230041306b22002400101a108a014101104d41af8308104a22011094012001109501200041106a1092012000411c6a2202200028021020002802142001107a200041003a002c200041086a1092012000280208200028020c200120021072200041306a24000be70201087f23004180016b22002400101a108a014102104d41a58308104a2101410141cf85084104104b21022001109301200041d8006a22062001108e01200041186a200028025820002802602002102d200029031850450440200041386a200041286a2903003703002000200041206a2903003703302002200041306a103a200041f4006a2001108e01200028027422072002106f22050440200041106a200028027822012005102a2000280214210320002802102104200620011067024020040440200041086a20012004102a200120042000280208200310680c010b2000200336025c0b024020030440200020012003102a200120032004200028020410680c010b200020043602600b20012005107c20012005102b1a20012005107d2000200028025841016b3602582001200041d8006a106a20072002106e200041e0006a200028027c22012002106d2001200210630b20004180016a24000f0b41e6850841191039000bda0602097f027e230041d0016b22002400101a108a01104f4101104e41a58308104a2105200041013602a001200041a0016a1048210220002802a001104c2005109301103d21042002100521060340200141046a220320064b450440200041003602a00120022001200041a0016a410410441a200420002802a001220141187420014180fe03714108747220014108764180fe03712001411876727210271049200321010c010b0b200410052101200041003602702000200141027636026c2000200436026803400240200041a0016a200041e8006a10880120002903a00150044020094291ce005a0d01200041d0006a2005108e01200041c8006a109201200028024c21042000280248210620021005210120004100360264200020014102763602602000200236025c200041d4006a210203400240200041e8006a200041dc006a108801200029036850044020004180016a2005108e01200041286a20004184016a22011051200020002903283702a0010340200041206a200041a0016a102920002802200440200028028801200028022410630c0105200041186a20011051200020002903183702a0010340200041106a200041a0016a1029200028021004402000280280012000280214106e0c0105200041e8006a2000280284011067200028026c210103402001450d06200041086a2000280284012001102a200028020c2000280284012001107c2000280284012001107d21010c000b000b000b000b000b000b2000290378210920004180016a2006200420002802702207107a2000280280012005470d01103d2103200041406b2002105120002000290340370294012000200041d0006a36029c010340200041a0016a220120004194016a102c20002903a001500440200041386a10920120012000280238200028023c2007107a20002802ac01200041306a103c20032000280230200028023410400c030520002802b801210820002903b001210a20002802bc012201200120091057101c200120014290ce001057101d200020013602cc012000200a3703c001200020083602c8012003200041c0016a103f0c010b000b000b0b200041a8016a4200370300200042003703a001200028028401200041a0016a106a200041d0016a24000f0520002903b00120097c21090c020b000b0b418f850841201039000bdc0101077f230041406a220024004102104d410010462101410110462102200041106a104302401090011032200028021810280440108d01108001200028021c101e0d0110412103200041086a109101200028020c107b2104103d2105103e220642001006200041003a003c2000200236023820002001360234200020063602302000200536022c200020033602282000200441016a3602242000109101200028020020002802042201107b41016a22021073200041246a105f20012002ad106c200041406b24000f0b41ee8308412a1039000b4198840841231039000b850102057f017e230041206b22002400101a4103104d41a58308104a210141011046210241021046210310412104200041003a001c2000200436021820002003360214200020023602102000200136020c2000109201200028020020002802042201107b41016a220210732000410c6a105d20012002ad2205106c2005101f200041206a24000bc10101067f230041f0006b220024004101104d41a58308104a22011093012000109101200041d0006a2203200028020020002802042001107920002d00680440200041086a22041043104121022000411c6a2001108e01200041286a200028021c20002802242002102d02402000290328500440200041c4006a22052001108e01200320052002200410650c010b200028023c20002802141056200041d0006a2000411c6a2002200041306a10650b200041f0006a24000f0b41e7840841121039000bbe0101047f230041d0006b220024004101104d41af8308104a220110940120011095012000104310412102200041146a2001108f01024002402000280214220320021066450440200041206a22032001108f01200041386a20032002200010650c010b200041206a2003200028021c2002102d2000290320500d0020002802342000280230200028020810271028450d01200028020c1056200041386a200041146a2002200041286a10650b200041d0006a24000f0b41bf8308412f1039000b1000101a4100104d109001103210041a0b1000101a4100104d108d0110800110200bac01010b7f230041306b22002400101a4100104d200041086a10910120002802082103200028020c107b21044101210203400240200220044b0d00200041146a20032002107820002d002c22054102460d00200028022820002802242000280220200028021c2000280218210a102e210120002802142001105e2001200a10021a2001103820011036200110382001103820052001102520011030200241016a21020c010b0b200041306a24000b970101097f230041206b22002400101a4100104d2000109201200028020021032000280204107b21044101210203400240200220044b0d002000410c6a20032002107420002d001c22054102460d002000280218210620002802142000280210102e2101200028020c2001105e20011038200110382001200610021a20052001102520011030200241016a21020c010b0b200041206a24000b2e01027f230041106b22002400101a4101104d200041046a220141a58308104a108e0120011050200041106a24000b2e01027f230041106b22002400101a4101104d200041046a220141af8308104a108f0120011050200041106a24000b1800101a4101104d410041bf82084107104b108b01ad10210b3901037f230041106b22002400101a10424101104d410041bf82084107104b2101200041046a2202108c0120022001108101200041106a24000bd00101087f230041106b22012400101a10424101104d410041bf82084107104b2104200141046a108c010240200128020c22052004108501220204400240200220012802082203107b22004d044020002002460d012003107b2000490d03200128020422072000107621062003107b2002490d032007200210732006105b0c010b0c020b2003107b20004904400c020b20012802042000107310622003200041016b1071200020024704402005200620021083010b2005200410840110620b200141106a24000f0b41c4860841121000000b5401047f230041106b22002400101a4100104d200041046a108c012000280208107b210220002802042103410121010340200120024b45044020032001107610041a200141016a21010c010b0b200041106a24000b02000b0b0041d68608410e1000000b2701027f20014108102f22021027220341fe81084104100d1a20002003360204200020023602000b0bf8060200418080080be406696e70757420746f6f206c6f6e67696e76616c69642076616c7565696e636f7272656374206e756d626572206f662045534454207472616e7366657273617267756d656e74206465636f6465206572726f722028293a20746f6f2066657720617267756d656e7473746f6f206d616e7920617267756d656e747377726f6e67206e756d626572206f6620617267756d656e7473696e70757420746f6f2073686f72746361737420746f20693634206572726f722e6d61707065642e6e6f64655f69642e6974656d2e6e6f64655f6c696e6b732e76616c75652e696e666f2e696e64657873746f72616765206465636f6465206572726f7220286b65793a202e6c656e626164206172726179206c656e6774687661722061726773456e64706f696e742063616e206f6e6c792062652063616c6c65642062792061646d696e7361646472657373666565416d6f756e74706f74446f6e6174696f6e7370726f6a656374446f6e6174696f6e73666565546f6b656e4964656e746966696572706f746c6f636b7370726f6a656374736f6e6c795f61646d696e5f6d6f64756c653a61646d696e73706f746c6f636b5f696470726f6a6563745f696461646d696e73416c7265616479206d6164652061207061796d656e742077697468206120646966666572656e7420546f6b656e494457726f6e6720746f6b656e206964656e74696669657220666f72206372656174696e67206120706f742157726f6e672066656520616d6f756e7420666f72206372656174696e67206120706f74506f746c6f636b20646f65736e27742065786973742150726f6a65637420646f65736e277420657869737421506f74206973206e6f74206163746976652150726f6a656374206973206e6f742061637469766521546f74616c2070657263656e7461676573206d6f7265207468616e2031303025506f74206973206163746976652150726f6a65637420697320616374697665217573657270726f6a6563745f70657263656e74616765734e6f20646f6e6174696f6e20666f7220746869732075736572496e76616c696420746f6b656e2070726f7669646564416d6f756e742069732030456e64706f696e742063616e206f6e6c792062652063616c6c6564206279206f776e6572696e646578206f7574206f662072616e676570616e6963206f636375727265640041e486080b0438ffffff", + "report": { + "imports": [ + "bigIntAdd", + "bigIntCmp", + "bigIntFinishUnsigned", + "bigIntGetUnsignedArgument", + "bigIntMul", + "bigIntSetInt64", + "bigIntSign", + "bigIntTDiv", + "checkNoPayment", + "getNumArguments", + "mBufferAppend", + "mBufferAppendBytes", + "mBufferCopyByteSlice", + "mBufferEq", + "mBufferFinish", + "mBufferFromBigIntUnsigned", + "mBufferGetArgument", + "mBufferGetByteSlice", + "mBufferGetLength", + "mBufferNew", + "mBufferSetBytes", + "mBufferStorageLoad", + "mBufferStorageStore", + "mBufferToBigIntUnsigned", + "managedCaller", + "managedGetMultiESDTCallValue", + "managedMultiTransferESDTNFTExecute", + "managedOwnerAddress", + "managedSignalError", + "signalError", + "smallIntFinishSigned", + "smallIntFinishUnsigned", + "smallIntGetUnsignedArgument", + "validateTokenIdentifier" + ], + "isMemGrow": false, + "eiCheck": { + "eiVersion": "1.3", + "ok": true + }, + "codeReport": { + "path": "../output/potlock.wasm", + "size": 12628, + "hasAllocator": false, + "hasPanic": "without message" + } + } +} diff --git a/contracts/potlock/output/potlock.wasm b/contracts/potlock/output/potlock.wasm new file mode 100644 index 0000000..e3e11c4 Binary files /dev/null and b/contracts/potlock/output/potlock.wasm differ diff --git a/contracts/potlock/sc-config.toml b/contracts/potlock/sc-config.toml new file mode 100644 index 0000000..15d4cf8 --- /dev/null +++ b/contracts/potlock/sc-config.toml @@ -0,0 +1,4 @@ + +[[proxy]] +path = "interact-rs/src/proxy.rs" + diff --git a/contracts/potlock/src/potlock.rs b/contracts/potlock/src/potlock.rs new file mode 100644 index 0000000..6d0502d --- /dev/null +++ b/contracts/potlock/src/potlock.rs @@ -0,0 +1,29 @@ +#![no_std] + +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 +{ + #[init] + fn init(&self, admins: MultiValueEncoded) { + let caller = self.blockchain().get_caller(); + self.admins().insert(caller); + for admin in admins { + self.admins().insert(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 0000000..bf38a93 --- /dev/null +++ b/contracts/potlock/src/potlock_admin_interactions.rs @@ -0,0 +1,132 @@ +use crate::{ + potlock_requirements::{self, MAX_PERCENTAGE}, + potlock_storage::{self, PotlockId, ProjectId, Status}, +}; + +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +pub type ProjectPercentage = MultiValue2; + +#[multiversx_sc::module] +pub trait PotlockAdminInteractions: + potlock_requirements::PotlockRequirements + + 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!(fee > 0, "Amount is 0"); + self.fee_token_identifier().set_if_empty(&token_identifier); + self.fee_amount().set_if_empty(fee); + } + + #[only_admin] + #[endpoint(acceptPot)] + fn accept_pot(&self, potlock_id: PotlockId) { + self.require_potlock_exists(potlock_id); + self.require_potlock_is_inactive(potlock_id); + + let mut accepted_potlock = self.potlocks().get(potlock_id); + accepted_potlock.status = Status::Active; + self.potlocks().set(potlock_id, &accepted_potlock); + } + + #[only_admin] + #[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; + let fee_pot_payment = EsdtTokenPayment::new( + self.fee_token_identifier().get(), + 0u64, + self.fee_amount().get(), + ); + + self.send() + .direct_non_zero_esdt_payment(&pot_proposer, &fee_pot_payment); + self.potlocks().clear_entry(potlock_id); + } + + #[only_admin] + #[endpoint(acceptApplication)] + fn accept_application(&self, project_id: ProjectId) { + self.require_project_exists(project_id); + 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_payment = self.pot_donations(potlock_id).get(&user); + + 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_payment); + self.pot_donations(potlock_id).remove(&user); + } + + #[only_admin] + #[endpoint(distributePotToProjects)] + fn distribute_pot_to_projects( + &self, + potlock_id: PotlockId, + project_percentages: MultiValueEncoded, + ) { + 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; + 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); + } + + self.pot_donations(potlock_id).clear(); + } +} diff --git a/contracts/potlock/src/potlock_interactions.rs b/contracts/potlock/src/potlock_interactions.rs new file mode 100644 index 0000000..15ebbd0 --- /dev/null +++ b/contracts/potlock/src/potlock_interactions.rs @@ -0,0 +1,106 @@ +use crate::potlock_requirements; +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: + potlock_requirements::PotlockRequirements + + potlock_storage::PotlockStorage + + multiversx_sc_modules::only_admin::OnlyAdminModule +{ + #[payable("*")] + #[endpoint(addPot)] + fn add_pot(&self, name: ManagedBuffer, description: ManagedBuffer) { + let payment_for_adding_pot = self.call_value().single_esdt(); + require!( + self.fee_token_identifier().get() == payment_for_adding_pot.token_identifier, + "Wrong token identifier for creating a pot!" + ); + require!( + self.fee_amount().get() == payment_for_adding_pot.amount, + "Wrong fee amount for creating a pot" + ); + let caller = self.blockchain().get_caller(); + + let potlock_id = self.potlocks().len() + 1; + let potlock = Pot::new(potlock_id, caller, name, description); + self.potlocks().push(&potlock); + } + + #[endpoint(applyForPot)] + fn apply_for_pot( + &self, + potlock_id: PotlockId, + project_name: ManagedBuffer, + description: ManagedBuffer, + ) -> usize { + let owner = self.blockchain().get_caller(); + 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) { + self.require_potlock_exists(potlock_id); + self.require_potlock_is_active(potlock_id); + + let payment = self.call_value().single_esdt(); + let caller = self.blockchain().get_caller(); + let mut donation_mapper = self.pot_donations(potlock_id); + + 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; + 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("*")] + #[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(); + + let mut donation_mapper = self.project_donations(project_id); + 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; + donation_mapper.insert(caller, previous_payment); + } + } else { + donation_mapper.insert(caller, payment); + } + } +} diff --git a/contracts/potlock/src/potlock_requirements.rs b/contracts/potlock/src/potlock_requirements.rs new file mode 100644 index 0000000..f923672 --- /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 new file mode 100644 index 0000000..19b71bd --- /dev/null +++ b/contracts/potlock/src/potlock_storage.rs @@ -0,0 +1,103 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +pub type PotlockId = usize; +pub type ProjectId = usize; + +#[derive(TypeAbi, TopEncode, TopDecode, PartialEq, Eq, Debug, NestedEncode, NestedDecode)] +pub enum Status { + Inactive, + Active, +} + +#[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, + pub description: ManagedBuffer, + pub status: Status, +} + +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, + description, + status: Status::Inactive, + } + } +} + +#[derive(TypeAbi, NestedEncode, NestedDecode, PartialEq, Debug, TopEncode, TopDecode)] +pub struct Project { + pub potlock_id: PotlockId, + pub name: ManagedBuffer, + pub description: ManagedBuffer, + pub owner: ManagedAddress, + pub status: Status, +} + +impl Project { + pub fn new( + potlock_id: PotlockId, + name: ManagedBuffer, + description: ManagedBuffer, + owner: ManagedAddress, + ) -> Self { + Project { + potlock_id, + name, + description, + owner, + status: Status::Inactive, + } + } +} + +#[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)] + #[storage_mapper("feeTokenIdentifier")] + fn fee_token_identifier(&self) -> SingleValueMapper; + + #[view(getFeeAmount)] + #[storage_mapper("feeAmount")] + fn fee_amount(&self) -> SingleValueMapper; + + #[view(getPotlocks)] + #[storage_mapper("potlocks")] + fn potlocks(&self) -> VecMapper>; + + #[view(getProjects)] + #[storage_mapper("projects")] + fn projects(&self) -> VecMapper>; + + #[view(potDonations)] + #[storage_mapper("potDonations")] + fn pot_donations(&self, potlock_id: PotlockId) -> MapMapper; + + #[view(projectDonations)] + #[storage_mapper("projectDonations")] + fn project_donations( + &self, + project_id: ProjectId, + ) -> MapMapper; +} diff --git a/contracts/potlock/tests/potlock_blackbox_tests.rs b/contracts/potlock/tests/potlock_blackbox_tests.rs new file mode 100644 index 0000000..95ca710 --- /dev/null +++ b/contracts/potlock/tests/potlock_blackbox_tests.rs @@ -0,0 +1,979 @@ +use multiversx_sc_scenario::{imports::*, ScenarioWorld}; +use potlock::potlock_storage::{PotlockId, ProjectId}; +mod potlock_proxy; + +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 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 DIFFERENT_POT_FEE_CREATION: u64 = 2_000; +const INITIAL_BALANCE: u64 = 2_000; +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(); + + 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(POT_TOKEN_ID, INITIAL_BALANCE) + .esdt_balance(DIFFERENT_TOKEN_ID, INITIAL_BALANCE) + .account(PROJECT_PROPOSER_ADDRESS) + .nonce(1) + .account(POT_DONOR_ADDRESS) + .nonce(1) + .esdt_balance(POT_TOKEN_ID, INITIAL_BALANCE) + .esdt_balance(DIFFERENT_TOKEN_ID, INITIAL_BALANCE) + .account(PROJECT_DONOR_ADDRESS) + .nonce(1) + .esdt_balance(POT_TOKEN_ID, INITIAL_BALANCE) + .esdt_balance(DIFFERENT_TOKEN_ID, INITIAL_BALANCE); + + Self { world } + } + + 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(admins) + .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(POT_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(POT_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 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() + .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, + ) -> 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, donation_token: TestTokenIdentifier) { + 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(donation_token), + 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(POT_TOKEN_ID), + 0u64, + &multiversx_sc::proxy_imports::BigUint::from(DONATION_AMOUNT), + ) + .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 + .check_account(address) + .esdt_balance(POT_TOKEN_ID, balance); + } + + fn check_sc_esdt_balance(&mut self, address: TestSCAddress, balance: u64) { + self.world + .check_account(address) + .esdt_balance(POT_TOKEN_ID, balance); + } + + fn check_potlock_id_is_last(&mut self, potlock_id: PotlockId) { + let potlocks = self + .world + .query() + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .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_description: ManagedBuffer) { + let projects = self + .world + .query() + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .projects() + .returns(ReturnsResult) + .run(); + + let mut project_found = false; + for project in projects.into_iter() { + if project.description == project_description { + project_found = true; + } + } + assert!(project_found); + } +} + +#[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_change_fee_for_pots_twice() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + state.change_fee_for_pots(POT_FEE_CREATION * 2); + + 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); +} + +#[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"); + + 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); +} + +#[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.check_potlock_id_is_last(potlock_id); + + 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); +} + +#[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, INITIAL_BALANCE); + 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; + + 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(new_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); + + // 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); + + 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); +} + +#[test] +fn test_donate_to_pot_twice(){ + 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.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, 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.donate_to_pot(potlock_id, POT_TOKEN_ID); + + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION + 2 * DONATION_AMOUNT); + state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE - 2 * 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); + + // 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(PROJECT_DONOR_ADDRESS, INITIAL_BALANCE); + + 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); +} + +#[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 new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); + + 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(new_project_id); + state.check_project_is_accepted(ManagedBuffer::from("Project description")); +} + +#[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); + + // Accept Pot + state.accept_pot(potlock_id); + + // Add Project + let new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); + + 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); + + // Donate to Pot + 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); + + // Accept project + 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((new_project_id, MAX_PERCENTAGE).into()); + state.distribute_pot_to_projects(potlock_id, percentages); + + state.check_esdt_balance(PROJECT_PROPOSER_ADDRESS, DONATION_AMOUNT); +} + +#[test] +fn test_distribute_to_project_less_than_max_percent() +{ + 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 new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); + + state.check_project_id_is_last(new_project_id); + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE - POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); + + // Donate to Pot + 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); + + // Accept project + 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((new_project_id, 60).into()); + state.distribute_pot_to_projects(potlock_id, percentages); + + state.check_esdt_balance(PROJECT_PROPOSER_ADDRESS, DONATION_AMOUNT * (60/100)); + //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, INITIAL_BALANCE); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, 0); +} + +#[test] +fn test_fail_add_pot_wrong_payment() { + 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") + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(DIFFERENT_TOKEN_ID), + 0u64, + &multiversx_sc::proxy_imports::BigUint::from(POT_FEE_CREATION), + ) + .with_result(ExpectError(4, "Wrong token identifier for creating a pot!")) + .run(); + + state + .world + .tx() + .from(POT_PROPOSER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .add_pot("name", "description") + .egld_or_single_esdt( + &EgldOrEsdtTokenIdentifier::esdt(POT_TOKEN_ID), + 0u64, + &multiversx_sc::proxy_imports::BigUint::from(DIFFERENT_POT_FEE_CREATION), + ) + .with_result(ExpectError(4, "Wrong fee amount for creating a pot")) + .run(); + + state.check_esdt_balance(POT_PROPOSER_ADDRESS, INITIAL_BALANCE); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, 0); +} + +#[test] +fn test_fail_change_fee_for_pots() +{ + let mut state = PotlockTestState::new(); + + state.deploy_potlock_contract(); + + state + .world + .tx() + .from(POT_PROPOSER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .change_fee_for_pots( + TokenIdentifier::from(POT_TOKEN_ID), + BigUint::from(0u64), + ) + .returns(ExpectError(4, "Endpoint can only be called by admins")) + .run(); +} + +#[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, INITIAL_BALANCE - POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); +} + +#[test] +fn test_fail_accept_pot_non_existent() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + let potlock_id = 0usize; + state + .world + .tx() + .from(OWNER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .accept_pot(potlock_id) + .with_result(ExpectError(4, "Potlock doesn't exist!")) + .run(); +} + +#[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, INITIAL_BALANCE - POT_FEE_CREATION); + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); +} + +#[test] +fn test_fail_remove_pot_non_existent() { + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + let potlock_id = 0usize; + state + .world + .tx() + .from(OWNER_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .remove_pot(potlock_id) + .with_result(ExpectError(4, "Potlock doesn't exist!")) + .run(); +} + +#[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 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(new_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(new_project_id) + .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 new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); + + 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(new_project_id); + state.check_project_is_accepted(ManagedBuffer::from("Project description")); + + let mut percentages = MultiValueVec::new(); + percentages.push((new_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(); +} + +#[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 new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); + + 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); + + // Donate to Pot + 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); + + // Accept project + 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((new_project_id, 3 * 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 more 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); + + 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(PROJECT_DONOR_ADDRESS, INITIAL_BALANCE); + + 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); + + state + .world + .tx() + .from(PROJECT_DONOR_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .donate_to_project(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_inactive_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); + + let new_project_id = state.apply_for_pot(potlock_id, "Project name", "Project description"); + state.check_project_id_is_last(new_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 + .world + .tx() + .from(PROJECT_DONOR_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .donate_to_project(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, + "Project is not active!", + )) + .run(); + + state.check_sc_esdt_balance(POTLOCK_ADDRESS, POT_FEE_CREATION); + state.check_esdt_balance(PROJECT_DONOR_ADDRESS, INITIAL_BALANCE); +} + +#[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(); +} + +#[test] +fn test_fail_donate_to_non_existing_pot(){ + let mut state = PotlockTestState::new(); + state.deploy_potlock_contract(); + state.change_fee_for_pots(POT_FEE_CREATION); + + let potlock_id = 0usize; + state + .world + .tx() + .from(POT_DONOR_ADDRESS) + .to(POTLOCK_ADDRESS) + .typed(potlock_proxy::PotlockProxy) + .donate_to_pot(potlock_id) + .with_result(ExpectError(4, "Potlock doesn't exist!")) + .run(); + + state.check_esdt_balance(POT_DONOR_ADDRESS, INITIAL_BALANCE); +} diff --git a/contracts/potlock/tests/potlock_proxy.rs b/contracts/potlock/tests/potlock_proxy.rs new file mode 100644 index 0000000..687a658 --- /dev/null +++ b/contracts/potlock/tests/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, + admins: Arg0, + ) -> TxTypedDeploy { + self.wrapped_tx + .payment(NotPayable) + .raw_deploy() + .argument(&admins) + .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 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, + >( + 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 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>, + >( + 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_percentages: Arg1, + ) -> TxTypedCall { + self.wrapped_tx + .payment(NotPayable) + .raw_call("distributePotToProjects") + .argument(&potlock_id) + .argument(&project_percentages) + .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 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 pot_donations< + Arg0: ProxyArg, + >( + self, + potlock_id: Arg0, + ) -> TxTypedCall, EsdtTokenPayment>>> { + self.wrapped_tx + .payment(NotPayable) + .raw_call("potDonations") + .argument(&potlock_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 proposer: ManagedAddress, + pub token_identifier: TokenIdentifier, + pub fee: BigUint, + pub name: ManagedBuffer, + pub description: ManagedBuffer, + pub status: Status, +} + +#[type_abi] +#[derive(TopEncode, TopDecode, NestedDecode, NestedEncode)] +pub enum Status { + Inactive, + Active, +} + +#[type_abi] +#[derive(TopEncode, TopDecode)] +pub struct Project +where + Api: ManagedTypeApi, +{ + pub potlock_id: usize, + pub name: ManagedBuffer, + pub description: ManagedBuffer, + pub owner: ManagedAddress, + pub status: Status, +} diff --git a/contracts/potlock/wasm/Cargo.lock b/contracts/potlock/wasm/Cargo.lock new file mode 100644 index 0000000..4732b4c --- /dev/null +++ b/contracts/potlock/wasm/Cargo.lock @@ -0,0 +1,198 @@ +# 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.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[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.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "526760b1d6236c011285b264a70a0a9dd3b3dbc53c3b5f76932f4bcfd3a8910c" +dependencies = [ + "bitflags", + "hex-literal", + "multiversx-sc-codec", + "multiversx-sc-derive", + "num-traits", + "unwrap-infallible", +] + +[[package]] +name = "multiversx-sc-codec" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad4f318427761faecf26c1f3115a3beeb5f61858845a60547d9763aa981ddd2d" +dependencies = [ + "arrayvec", + "multiversx-sc-codec-derive", + "unwrap-infallible", +] + +[[package]] +name = "multiversx-sc-codec-derive" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476501462b0c2654b64f9dec6f2c480e24b4e9b7133ec10b7167e64acda35d04" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multiversx-sc-derive" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3557f2f12640a8a07fa6af66cc2a13b188c5b61bed72db22fe631fb3a60c3e96" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "radix_trie", + "syn", +] + +[[package]] +name = "multiversx-sc-modules" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f5c29c6044f3dc9e866858feee625d7fae5604a68ac7bd66dec683eee97563" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "multiversx-sc-wasm-adapter" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed13aaca9cbdbc6911174cd3029e750a7563d85dd3daaa1107b1fd31c7f17245" +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.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +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.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +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.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +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" + +[[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 new file mode 100644 index 0000000..0ead938 --- /dev/null +++ b/contracts/potlock/wasm/Cargo.toml @@ -0,0 +1,34 @@ +# Code generated by the multiversx-sc build system. DO NOT EDIT. + +# ########################################## +# ############## AUTO-GENERATED ############# +# ########################################## + +[package] +name = "potlock-wasm" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = true +debug = false +panic = "abort" +overflow-checks = false + +[profile.dev] +panic = "abort" + +[dependencies.potlock] +path = ".." + +[dependencies.multiversx-sc-wasm-adapter] +version = "0.52.3" + +[workspace] +members = ["."] diff --git a/contracts/potlock/wasm/src/lib.rs b/contracts/potlock/wasm/src/lib.rs new file mode 100644 index 0000000..8bb6ebd --- /dev/null +++ b/contracts/potlock/wasm/src/lib.rs @@ -0,0 +1,47 @@ +// Code generated by the multiversx-sc build system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Upgrade: 1 +// Endpoints: 21 +// Async Callback (empty): 1 +// Total number of exported functions: 24 + +#![no_std] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + potlock + ( + init => init + upgrade => upgrade + changeFeeForPots => change_fee_for_pots + acceptPot => accept_pot + removePot => remove_pot + acceptApplication => accept_application + removeApplication => remove_application + rejectDonation => reject_donation + distributePotToProjects => distribute_pot_to_projects + addPot => add_pot + applyForPot => apply_for_pot + donateToPot => donate_to_pot + donateToProject => donate_to_project + getFeeTokenIdentifier => fee_token_identifier + getFeeAmount => fee_amount + getPotlocks => potlocks + getProjects => projects + potDonations => pot_donations + projectDonations => project_donations + isAdmin => is_admin + addAdmin => add_admin + removeAdmin => remove_admin + getAdmins => admins + ) +} + +multiversx_sc_wasm_adapter::async_callback_empty! {}