Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Potlock contract #76

Open
wants to merge 52 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
9832d6e
Add Potlock contract
CostinCarabas Jun 17, 2024
e641553
Refactor donations
CostinCarabas Jun 18, 2024
12572ae
Fix building issues
CostinCarabas Jun 18, 2024
622069f
Merge branch 'fix-unit-tests' into add-potlock-contract
CostinCarabas Jun 20, 2024
cc7cea6
Framework upgrade potlock
CostinCarabas Jun 20, 2024
3e52a79
Add blackbox tests
CostinCarabas Jun 20, 2024
849325b
Added more unit tests
CostinCarabas Jun 21, 2024
89ed86f
Add several more tests
CostinCarabas Jun 21, 2024
6fc6db6
Add interactor
CostinCarabas Jun 21, 2024
4103456
Adjust interactors functions
CostinCarabas Jun 22, 2024
214e117
Add diferent types of actors from different wallets
CostinCarabas Jun 26, 2024
a8bb80a
Merge branch 'main' into add-potlock-contract
CostinCarabas Jul 3, 2024
ae32eff
Merge pull request #84 from multiversx/potlock-framework-upgrade
CostinCarabas Jul 3, 2024
a4ecfb3
Merge branch 'main' into add-potlock-contract
CostinCarabas Jul 11, 2024
d0555ce
Merge remote-tracking branch 'origin/main' into add-potlock-contract
CostinCarabas Jul 16, 2024
e35a545
Merge pull request #85 from multiversx/potlock-unit-tests
CostinCarabas Jul 16, 2024
d6fa92b
Merge branch 'add-potlock-contract' into potlock-system-tests
CostinCarabas Jul 16, 2024
b479a1b
Merge pull request #86 from multiversx/potlock-system-tests
CostinCarabas Jul 16, 2024
3af9363
Framework upgrade 0.51.1
CostinCarabas Jul 19, 2024
8b4903e
Fix tests
CostinCarabas Jul 19, 2024
2da0c7a
Fix clippy
CostinCarabas Jul 19, 2024
a5894c5
Fixes after review
CostinCarabas Jul 19, 2024
68b4660
Fixes after review
CostinCarabas Jul 31, 2024
2bbbe84
Fix tests
CostinCarabas Jul 31, 2024
2abeab0
Refuse multiple donations with different tokens
CostinCarabas Jul 31, 2024
324f598
Add tests for multiple donations with different tokens
CostinCarabas Jul 31, 2024
c01041d
Fix donate_to_project
CostinCarabas Jul 31, 2024
dbe90fb
Merge branch 'main' into add-potlock-contract
CostinCarabas Aug 1, 2024
a1e9c73
Framework upgrade 0.52.1
CostinCarabas Aug 1, 2024
b6bdb74
potlock: distributePotToProjects: percentage req
CostinCarabas Aug 5, 2024
a9187f1
Merge branch 'main' into add-potlock-contract
CostinCarabas Aug 6, 2024
56fc27b
Merge branch 'main' into add-potlock-contract
dorin-iancu Aug 7, 2024
f074305
Merge branch 'main' into add-potlock-contract
CostinCarabas Aug 12, 2024
3bc8bf5
potlock: Framework upgrade
CostinCarabas Aug 14, 2024
f406d80
Fix README
CostinCarabas Aug 16, 2024
d7c4857
Fixes after review
CostinCarabas Aug 19, 2024
906e146
Fixes after review
CostinCarabas Aug 19, 2024
c8032b0
Fixes after review
CostinCarabas Aug 19, 2024
e8cd551
Fix tests
CostinCarabas Aug 20, 2024
75f9f47
Fix compiling issues
CostinCarabas Aug 21, 2024
8b51d5f
potlock: more unit tests
CostinCarabas Aug 21, 2024
7e28fa2
Add interactor tests
vladiouz Aug 21, 2024
eadba85
Add interactor tests
vladiouz Sep 6, 2024
f804a7d
Solve warnings
vladiouz Sep 6, 2024
28addac
Execute interactor tests only on demand
vladiouz Sep 6, 2024
e416e52
Cleanse tests
vladiouz Sep 6, 2024
8223852
Cleanse tests
vladiouz Sep 6, 2024
c39f4d5
Merge pull request #105 from multiversx/add-potlock-contract-interact…
vladiouz Sep 6, 2024
091289e
Merge branch 'main' into add-potlock-contract
CostinCarabas Oct 7, 2024
6c7a767
Framework upgrade 0.53.2
CostinCarabas Oct 9, 2024
0d838e9
potlock: clippy fix
CostinCarabas Oct 9, 2024
b7c0cd8
Merge branch 'main' into add-potlock-contract
CostinCarabas Oct 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ members = [
"contracts/paymaster/meta",
"contracts/ping-pong-egld",
"contracts/ping-pong-egld/meta",
"contracts/potlock",
"contracts/potlock/meta",
"contracts/proxy-deployer",
"contracts/proxy-deployer/meta",
"contracts/proxy-pause",
Expand Down
24 changes: 24 additions & 0 deletions contracts/potlock/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "potlock"
version = "0.0.0"
authors = ["you"]
edition = "2021"
publish = false

[lib]
path = "src/potlock.rs"

[dependencies.multiversx-sc]
version = "0.50.4"

[dev-dependencies]
num-bigint = "0.4"

[dev-dependencies.multiversx-sc-scenario]
version = "0.50.4"

[workspace]
members = [
".",
"meta",
]
7 changes: 7 additions & 0 deletions contracts/potlock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Paymaster SC

## Overview



## Implementation
12 changes: 12 additions & 0 deletions contracts/potlock/meta/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "potlock-meta"
version = "0.0.0"
edition = "2021"
publish = false

[dependencies.potlock]
path = ".."

[dependencies.multiversx-sc-meta]
version = "0.50.4"
default-features = false
3 changes: 3 additions & 0 deletions contracts/potlock/meta/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
multiversx_sc_meta::cli_main::<potlock::AbiProvider>();
}
3 changes: 3 additions & 0 deletions contracts/potlock/multiversx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"language": "rust"
}
39 changes: 39 additions & 0 deletions contracts/potlock/scenarios/potlock.scen.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "empty",
"steps": [
{
"step": "setState",
"accounts": {
"address:owner": {
"nonce": "1",
"balance": "0"
}
},
"newAddresses": [
{
"creatorAddress": "address:owner",
"creatorNonce": "1",
"newAddress": "sc:empty"
}
]
},
{
"step": "scDeploy",
"id": "deploy",
"tx": {
"from": "address:owner",
"contractCode": "mxsc:../output/potlock.mxsc.json",
"arguments": [],
"gasLimit": "5,000,000",
"gasPrice": "0"
},
"expect": {
"out": [],
"status": "",
"logs": [],
"gas": "*",
"refund": "*"
}
}
]
}
19 changes: 19 additions & 0 deletions contracts/potlock/src/potlock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#![no_std]

use multiversx_sc::imports::*;
mod potlock_setup;
mod potlock_storage;
mod potlock_interactions;
mod potlock_admin_interactions;

/// An empty contract. To be used as a template when starting a new contract from scratch.
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't mark as resolved if you don't fix it :nutu:

#[multiversx_sc::contract]
pub trait Potlock: potlock_admin_interactions: PotlockAdminInteractions + potlock_interactions: PotlockInteractions + potlock_setup: PotlockSetup + potlock_storage: PotlockStorage {
#[init]
fn init(&self, admin: ManagedAddress) {
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
self.admins().add(admin);
}

#[upgrade]
fn upgrade(&self) {}
}
71 changes: 71 additions & 0 deletions contracts/potlock/src/potlock_admin_interactions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
multiversx_sc::imports!();
multiversx_sc::derive_imports!();

pub type ProjectPercentage = MultiValue2<usize, usize>;

#[multiversx_sc::module]
pub trait PotlockAdminInteractions:
crate::potlock::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule
{
#[only_admin]
#[endpoint(acceptPot)]
fn accept_pot(&self, potlock_id: PotlockId) {
require_potlock_exists(potlock_id);
// TODO: Common fund is another contract?
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
}

#[only_admin]
#[endpoint(rejectPot)]
fn reject_pot(&self, potlock_id: PotlockId) {
require_potlock_exists(potlock_id);

//TODO: Common fund is another contract?
//TODO: "will return the fee back to the user"
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved

//TODO: Should we remove the potlock?
self.potlocks().clear_entry(proposal_id);
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
}

#[only_admin]
#[endpoint(removePot)]
fn remove_pot(&self, potlock_id: PotlockId) {
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
let caller = self.blockchain().get_caller();
let payment = self.fee_pot_payments(potlock_id, caller).get();

self.send().direct_non_zero_esdt_payment(&caller, &payment);
self.potlocks().clear_entry(proposal_id);
}

#[only_admin]
#[endpoint(acceptApplication)]
fn accept_application(&self, project: Projectid) {
require_potlock_exists(potlock_id);
// TODO: How should we KYC verification in the SC?
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
}
rejectDonation@userID@listOfTokens - returns tokens to the users.

#[only_admin]
#[endpoint(rejectDonation)]
fn reject_donation(&self, potlock: PotlockId, user: ManagedAddress) {
require_potlock_exists(potlock_id);
let fee_pot_payments = self.fee_pot_payments(potlock_id, user);
self.send().direct_non_zero_esdt_payment(&user, &fee_pot_payments);
}


#[only_admin]
#[endpoint(distributePotToProjects)]
fn distribute_pot_to_projects(&self, potlock: PotlockId, project_percentage: MultiValueEncoded<ProjectPercentage>) {
require_potlock_exists(potlock_id);
let potlock = self.potlocks().get();
for pp in project_percentage {
let (project_id, percentage) = pp.into_tuple();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add an extra check here, if the project_id indeed applied for the given pot? To not give the a part of the share to outside projects.


}
// TODO: How should we KYC verification in the SC?
}

fn get_total_payments_for_pot(&self) {
let fee_pot_payments = self.fee_pot_payments().get();
}
}
50 changes: 50 additions & 0 deletions contracts/potlock/src/potlock_interactions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
multiversx_sc::imports!();
multiversx_sc::derive_imports!();

#[multiversx_sc::module]
pub trait PotlockInteractions: crate::potlock::PotlockStorage {
#[payable("*")]
#[endpoint(addPot)]
fn add_pot(&self, name: ManagedBuffer, description: ManagedBuffer) {
let payment = self.call_value().egld_or_single_esdt().into_tuple();
require!(
self.fee_token_identifier().get() == payment.token_identifier,
"Wrong token identifier for creating a pot!"
);
require!(
self.fee_amount().get() == payment.amount,
"Wrong fee amount for creating a pot"
);
let caller = self.blockchain().get_caller();

let potlock = Potlock::new(name, description);
let potlock_id = self.potlocks().push(&potlock);
let fee_pot_payments = self.fee_pot_payments(potlock_id, caller).insert(payment);
}

#[endpoint(applyForPot)]
fn apply_for_pot(&self, project_name: ManagedBuffer, description: ManagedBuffer) {
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
// TODO: should we set a SC address as a parameter or assume (and verifiy) that the SC will call this endpoint
// TODO: This address will receive the funds
let caller = self.blockchain().get_caller();
let project = Project::new(name, description);
let project_id = self.projects().push(&project);
}

#[payable("*")]
#[endpoint(donateToPot)]
fn donate_to_pot(&self, potlock_id: PotlockId) {
let payment = self.call_value().egld_or_single_esdt().into_tuple();
let caller = self.blockchain().get_caller();
self.fee_pot_payments(potlock_id).insert(payment);
}

#[payable("*")]
#[endpoint(donateToProject)]
fn donate_to_project(&self, project_id: ProjectId) {
let payment = self.call_value().egld_or_single_esdt().into_tuple();
let caller = self.blockchain().get_caller();
self.fee_project_payments(project_id, caller)
.insert(payment);
}
}
30 changes: 30 additions & 0 deletions contracts/potlock/src/potlock_setup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
multiversx_sc::imports!();
multiversx_sc::derive_imports!();

#[multiversx_sc::module]
pub trait PotlockSetup:
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
crate::potlock::PotlockStorage + multiversx_sc_modules::only_admin::OnlyAdminModule
{
#[only_admin]
#[endpoint(changeFeeForPots)]
fn change_fee_for_pots(&self, token_identifier: TokenIdentifier, fee: BigUint) {
require!(
token_identifier.is_valid_esdt_identifier(),
"Invalid token provided"
);
self.fee_token_identifier().set(&token_identifier);
self.fee_amount().set(fee);
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
}

//// internal functions
fn is_valid_potlock_id(&self, potlock_id: PotlockId) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not work as intended when you delete data from the potlocks storage. See the comment from the addPot endpoint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my reply there.

potlock_id >= 1 && potlock_id <= self.potlocks().len()
}

fn require_potlock_exists(&self, potlock_id: PotlockId) {
require(
self.is_valid_potlock_id(potlock_id) && !self.potlock().item_is_empty(potlock_id),
"Potlock doesn't exist!",
)
}
}
93 changes: 93 additions & 0 deletions contracts/potlock/src/potlock_storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
multiversx_sc::imports!();
multiversx_sc::derive_imports!();

pub type PotlockId = usize;
pub type ProjectId = usize;

#[derive(TypeAbi, TopEncode, TopDecode, PartialEq, Eq)]
pub enum PotlockStatus {
None,
Active,
Inactive,
}

#[derive(
TypeAbi, NestedEncode, NestedDecode, PartialEq, Debug, TopEncodeOrDefault, TopDecodeOrDefault,
)]
pub struct Potlock<M: ManagedTypeApi> {
pub potlock_id: PotlocklId,
pub token_identifier: TokenIdentifier<M>,
pub fee: BigUint<M>,
pub name: ManagedBuffer<M>,
pub description: ManagedBuffer<M>,
pub status: PotlockStatus,
// pub payment: EsdtTokenPayment<M>,
}

impl<M: ManagedTypeApi> Default for Potlock<M> {
fn default() -> Self {
Self::new()
}
}

impl<M: ManagedTypeApi> Potlock<M> {
pub fn new(name: ManagedBuffer, description: ManagedBuffer) -> Self {
Potlock{
potlock_id: self.potlocks().len() + 1,
token_identifier: TokenIdentifier::from(ManagedBuffer::default()).
fee: BigUint::default(),
name,
description,
}
}
}

pub struct Project<M: ManagedTypeApi> {
pub project_id: PotlocklId,
pub name: ManagedBuffer<M>,
pub description: ManagedBuffer<M>,
pub address: ManagedAddress<M>,
}

impl<M: ManagedTypeApi> Default for Project<M> {
fn default() -> Self {
Self::new()
}
}

impl<M: ManagedTypeApi> Project<M> {
pub fn new(name: ManagedBuffer, description: ManagedBuffer) -> Self {
Project{
project_id: self.proposals().len() + 1,
name,
description,
}
}
}

#[multiversx_sc::module]
pub trait PotlockStorage {
CostinCarabas marked this conversation as resolved.
Show resolved Hide resolved
#[view(getFeeTokenIdentifier)]
#[storage_mapper("fee_token_identifier")]
fn fee_token_identifier(&self) -> SingleValueMapper<TokenIdentifier>;

#[view(getFeeAmount)]
#[storage_mapper("fee_amount")]
fn fee_amount(&self) -> SingleValueMapper<BigUint>;

#[view(getPotlocks)]
#[storage_mapper("potlocks")]
fn potlocks(&self) -> VecMapper<Potlock<Self::Api>>;

#[view(getProjects)]
#[storage_mapper("projects")]
fn projects(&self) -> VecMapper<Project<Self::Api>>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UnorderedSetMapper here as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would get a different behavior as explained to potlocks.


#[view(feePotPayments)]
#[storage_mapper("fee_pot_payments")]
fn fee_pot_payments(&self, potlock_id: PotlockId, address: &ManagedAddress) -> UnorderedSetMapper<EsdtTokenPayment>;

#[view(feeProjectPayments)]
#[storage_mapper("fee_project_payments")]
fn fee_project_payments(&self, project_id: ProjectId, address: &ManagedAddress) -> UnorderedSetMapper<EsdtTokenPayment>;
}
10 changes: 10 additions & 0 deletions contracts/potlock/tests/potlock_scenario_go_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use multiversx_sc_scenario::*;

fn world() -> ScenarioWorld {
ScenarioWorld::vm_go()
}

#[test]
fn empty_go() {
world().run("scenarios/potlock.scen.json");
}
13 changes: 13 additions & 0 deletions contracts/potlock/tests/potlock_scenario_rs_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use multiversx_sc_scenario::*;

fn world() -> ScenarioWorld {
let mut blockchain = ScenarioWorld::new();

blockchain.register_contract("mxsc:output/potlock.mxsc.json", potlock::ContractBuilder);
blockchain
}

#[test]
fn empty_rs() {
world().run("scenarios/potlock.scen.json");
}
Loading