Skip to content

Commit

Permalink
Merge branch 'main' into erc3643
Browse files Browse the repository at this point in the history
  • Loading branch information
dorin-iancu authored Feb 21, 2024
2 parents 05e08d9 + 4fc5c31 commit ec6bdd8
Show file tree
Hide file tree
Showing 15 changed files with 888 additions and 0 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ members = [
"contracts/crypto-kitties/kitty-auction/meta",
"contracts/digital-cash",
"contracts/digital-cash/meta",
"contracts/dn404",
"contracts/dn404/meta",
"contracts/empty",
"contracts/empty/meta",
"contracts/erc3643",
Expand Down
24 changes: 24 additions & 0 deletions contracts/dn404/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "dn404"
version = "0.0.0"
authors = ["you"]
edition = "2021"
publish = false

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

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

[dependencies.multiversx-sc-modules]
version = "=0.47.2"

[dev-dependencies]
num-bigint = "0.4.2"
num-traits = "0.2"
hex = "0.4"
hex-literal = "0.4.1"

[dev-dependencies.multiversx-sc-scenario]
version = "=0.47.2"
12 changes: 12 additions & 0 deletions contracts/dn404/meta/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "dn404-meta"
version = "0.0.0"
edition = "2021"
publish = false

[dependencies.dn404]
path = ".."

[dependencies.multiversx-sc-meta]
version = "=0.47.2"
default-features = false
3 changes: 3 additions & 0 deletions contracts/dn404/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::<dn404::AbiProvider>();
}
3 changes: 3 additions & 0 deletions contracts/dn404/multiversx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"language": "rust"
}
152 changes: 152 additions & 0 deletions contracts/dn404/src/available_tokens.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use crate::{fee::FeeType, Nonce, MAX_PERCENTAGE, NFT_AMOUNT};

multiversx_sc::imports!();
multiversx_sc::derive_imports!();

#[derive(TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode)]
pub struct TokenIdNoncePair<M: ManagedTypeApi> {
pub token_id: TokenIdentifier<M>,
pub nonce: Nonce,
}

#[multiversx_sc::module]
pub trait AvailableTokensModule:
crate::price::PriceModule
+ crate::fee::FeeModule
+ multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule
+ multiversx_sc_modules::pause::PauseModule
+ multiversx_sc_modules::only_admin::OnlyAdminModule
{
#[only_admin]
#[payable("*")]
#[endpoint]
fn deposit(&self) {
let payments = self.call_value().all_esdt_transfers().clone_value();
let mut basket = self.basket_of_goods();
for payment in &payments {
self.add_tokens(&mut basket, payment);
}
}

#[payable("*")]
#[endpoint(depositBasketOfGoods)]
fn deposit_basket_of_goods(&self) {
self.require_not_paused();

let payments = self.call_value().all_esdt_transfers().clone_value();
let token_mapper = self.fractal_token();
let token_id = token_mapper.get_token_id_ref();

let mut basket = self.basket_of_goods();
let mut total_output_payment = BigUint::zero();
for payment in &payments {
let price = self.try_get_price(&payment.token_identifier, payment.token_nonce);
let mut price_as_payment = EsdtTokenPayment::new(token_id.clone(), 0, price);

let fee_for_token = self.get_fee(&payment.token_identifier, payment.token_nonce);
let fee_amount = match fee_for_token {
FeeType::FixedAmount(amt) => amt,
FeeType::Percentage(percentage) => {
&price_as_payment.amount * percentage / MAX_PERCENTAGE
}
};
require!(price_as_payment.amount >= fee_amount, "Invalid state");

price_as_payment.amount -= fee_amount;
total_output_payment += &price_as_payment.amount;
self.add_tokens(&mut basket, payment);
}

if total_output_payment > 0 {
let caller = self.blockchain().get_caller();
let _ = token_mapper.mint_and_send(&caller, total_output_payment);
}
}

#[payable("*")]
#[endpoint(claimBasketOfGoods)]
fn claim_basket_of_goods(
&self,
tokens_to_claim: MultiValueEncoded<EsdtTokenPaymentMultiValue>,
) {
self.require_not_paused();
require!(!tokens_to_claim.is_empty(), "No tokens to claim");

let token_mapper = self.fractal_token();
let (payment_token, payment_amount) = self.call_value().single_fungible_esdt();
token_mapper.require_same_token(&payment_token);

let mut total_cost = BigUint::zero();
let mut tokens_vec = ManagedVec::<Self::Api, _>::new();
let mut basket = self.basket_of_goods();
for token_to_claim in tokens_to_claim {
let token_as_payment = token_to_claim.into_esdt_token_payment();
let price = self.try_get_price(
&token_as_payment.token_identifier,
token_as_payment.token_nonce,
);
total_cost += price;

self.remove_token(
&mut basket,
token_as_payment.token_identifier.clone(),
token_as_payment.token_nonce,
);

tokens_vec.push(token_as_payment);
}

require!(payment_amount >= total_cost, "Not enough tokens");

token_mapper.burn(&total_cost);

let caller = self.blockchain().get_caller();
let remaining_tokens = payment_amount - total_cost;
let remaining_tokens_payment = EsdtTokenPayment::new(payment_token, 0, remaining_tokens);
self.send()
.direct_non_zero_esdt_payment(&caller, &remaining_tokens_payment);
self.send().direct_multi(&caller, &tokens_vec);
}

fn add_tokens(
&self,
mapper: &mut UnorderedSetMapper<TokenIdNoncePair<Self::Api>>,
payment: EsdtTokenPayment,
) {
self.remaining_tokens(&payment.token_identifier, payment.token_nonce)
.update(|amt| *amt += payment.amount);
let _ = mapper.insert(TokenIdNoncePair {
token_id: payment.token_identifier,
nonce: payment.token_nonce,
});
}

fn remove_token(
&self,
mapper: &mut UnorderedSetMapper<TokenIdNoncePair<Self::Api>>,
token_id: TokenIdentifier,
nonce: Nonce,
) {
let remove = self.remaining_tokens(&token_id, nonce).update(|amt| {
require!(*amt >= NFT_AMOUNT, "Not enough tokens to claim");

*amt -= NFT_AMOUNT;
*amt == 0
});
if remove {
let _ = mapper.swap_remove(&TokenIdNoncePair { token_id, nonce });
}
}

#[view(getBasketOfGoods)]
#[storage_mapper("basketOfGoods")]
fn basket_of_goods(&self) -> UnorderedSetMapper<TokenIdNoncePair<Self::Api>>;

#[view(getRemainingTokens)]
#[storage_mapper("remTok")]
fn remaining_tokens(
&self,
token_id: &TokenIdentifier,
nonce: Nonce,
) -> SingleValueMapper<BigUint>;
}
64 changes: 64 additions & 0 deletions contracts/dn404/src/fee.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use crate::{Nonce, Percentage, MAX_PERCENTAGE};

multiversx_sc::imports!();
multiversx_sc::derive_imports!();

#[derive(TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode)]
pub enum FeeType<M: ManagedTypeApi> {
FixedAmount(BigUint<M>),
Percentage(Percentage),
}

#[multiversx_sc::module]
pub trait FeeModule {
#[only_owner]
#[endpoint(setFeeForFractionalisingNft)]
fn set_fee_for_fractionalizing_nft(
&self,
token_id: TokenIdentifier,
nonce: Nonce,
fee: BigUint,
) {
self.fee_nft(&token_id, nonce).set(fee);
}

#[only_owner]
#[endpoint(setFeeForFactionalisingCollection)]
fn set_fee_for_fractionalizing_collection(&self, token_id: TokenIdentifier, fee: BigUint) {
self.fee_collection(&token_id).set(fee);
}

#[only_owner]
#[endpoint(setFeeForDepositBaskedOfGoods)]
fn set_fee_for_deposit_basket_of_goods(&self, fee_percentage: Percentage) {
require!(fee_percentage <= MAX_PERCENTAGE, "Invalid fee percentage");

self.fee_basket().set(fee_percentage);
}

#[view(getFee)]
fn get_fee(&self, token_id: &TokenIdentifier, nonce: Nonce) -> FeeType<Self::Api> {
let fee_for_token = self.fee_nft(token_id, nonce).get();
if fee_for_token > 0 {
return FeeType::FixedAmount(fee_for_token);
}

let fee_collection = self.fee_collection(token_id).get();
if fee_collection > 0 {
return FeeType::FixedAmount(fee_collection);
}

let fee_percentage = self.fee_basket().get();
FeeType::Percentage(fee_percentage)
}

#[storage_mapper("feeNft")]
fn fee_nft(&self, token_id: &TokenIdentifier, nonce: Nonce) -> SingleValueMapper<BigUint>;

#[storage_mapper("feeColl")]
fn fee_collection(&self, token_id: &TokenIdentifier) -> SingleValueMapper<BigUint>;

#[view(getFeePercentageForBasketDeposit)]
#[storage_mapper("feeBasket")]
fn fee_basket(&self) -> SingleValueMapper<Percentage>;
}
42 changes: 42 additions & 0 deletions contracts/dn404/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#![no_std]

multiversx_sc::imports!();

pub mod available_tokens;
pub mod fee;
pub mod price;

pub type Nonce = u64;
pub type Percentage = u32;

pub const MAX_PERCENTAGE: Percentage = 10_000;
pub const NFT_AMOUNT: u32 = 1;

#[multiversx_sc::contract]
pub trait Dn404:
available_tokens::AvailableTokensModule
+ price::PriceModule
+ fee::FeeModule
+ multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule
+ multiversx_sc_modules::pause::PauseModule
+ multiversx_sc_modules::only_admin::OnlyAdminModule
{
/// Needs mint and burn roles for fractal_token
#[init]
fn init(&self, fractal_token_id: TokenIdentifier, admins: MultiValueEncoded<ManagedAddress>) {
require!(
fractal_token_id.is_valid_esdt_identifier(),
"Invalid token ID"
);

self.fractal_token().set_token_id(fractal_token_id);

let caller = self.blockchain().get_caller();
let _ = self.admins().insert(caller);
self.admins().extend(admins);
self.set_paused(true);
}

#[endpoint]
fn upgrade(&self) {}
}
50 changes: 50 additions & 0 deletions contracts/dn404/src/price.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use crate::Nonce;

multiversx_sc::imports!();

#[multiversx_sc::module]
pub trait PriceModule {
#[only_owner]
#[endpoint(setInternalPriceForToken)]
fn set_internal_price_for_token(
&self,
token_id: TokenIdentifier,
nonce: Nonce,
price: BigUint,
) {
self.price_for_token(&token_id, nonce).set(price);
}

#[only_owner]
#[endpoint(setInternalPriceForCollection)]
fn set_internal_price_for_collection(&self, token_id: TokenIdentifier, price: BigUint) {
self.price_for_collection(&token_id).set(price);
}

#[view(getPriceForToken)]
fn try_get_price(&self, token_id: &TokenIdentifier, nonce: Nonce) -> BigUint {
let price_for_token = self.price_for_token(token_id, nonce).get();
if price_for_token > 0 {
return price_for_token;
}

let price_for_collection = self.price_for_collection(token_id).get();
require!(price_for_collection > 0, "No price set for token");

price_for_collection
}

#[view(getFractalTokenId)]
#[storage_mapper("fractalToken")]
fn fractal_token(&self) -> FungibleTokenMapper;

#[storage_mapper("priceColl")]
fn price_for_collection(&self, token_id: &TokenIdentifier) -> SingleValueMapper<BigUint>;

#[storage_mapper("priceTok")]
fn price_for_token(
&self,
token_id: &TokenIdentifier,
nonce: Nonce,
) -> SingleValueMapper<BigUint>;
}
Loading

0 comments on commit ec6bdd8

Please sign in to comment.