multiversx_sc_modules/bonding_curve/utils/
user_endpoints.rsmultiversx_sc::imports!();
multiversx_sc::derive_imports!();
use multiversx_sc::contract_base::ManagedSerializer;
use crate::bonding_curve::{
curves::curve_function::CurveFunction,
utils::{events, storage, structs::BondingCurve},
};
#[multiversx_sc::module]
pub trait UserEndpointsModule: storage::StorageModule + events::EventsModule {
fn sell_token<T>(&self)
where
T: CurveFunction<Self::Api>
+ TopEncode
+ TopDecode
+ NestedEncode
+ NestedDecode
+ TypeAbi
+ PartialEq
+ Default,
{
let esdt_payment = self.call_value().single_esdt();
let offered_token = &esdt_payment.token_identifier;
let nonce = esdt_payment.token_nonce;
let sell_amount = &esdt_payment.amount;
let _ = self.check_owned_return_payment_token::<T>(offered_token, sell_amount);
let (calculated_price, payment_token) =
self.bonding_curve(offered_token).update(|buffer| {
let serializer = ManagedSerializer::new();
let mut bonding_curve: BondingCurve<Self::Api, T> =
serializer.top_decode_from_managed_buffer(buffer);
require!(
bonding_curve.sell_availability,
"Selling is not available on this token"
);
let price = self.compute_sell_price::<T>(offered_token, sell_amount);
bonding_curve.payment.amount -= &price;
bonding_curve.arguments.balance += sell_amount;
let payment_token = bonding_curve.payment_token();
*buffer = serializer.top_encode_to_managed_buffer(&bonding_curve);
(price, payment_token)
});
let caller = self.blockchain().get_caller();
self.nonce_amount(offered_token, nonce)
.update(|val| *val += sell_amount);
self.tx()
.to(&caller)
.egld_or_single_esdt(&payment_token, 0u64, &calculated_price)
.transfer();
self.token_details(offered_token)
.update(|details| details.add_nonce(nonce));
self.sell_token_event(&caller, &calculated_price);
}
fn buy_token<T>(
&self,
requested_amount: BigUint,
requested_token: TokenIdentifier,
requested_nonce: OptionalValue<u64>,
) where
T: CurveFunction<Self::Api>
+ TopEncode
+ TopDecode
+ NestedEncode
+ NestedDecode
+ TypeAbi
+ PartialEq
+ Default,
{
let (offered_token, payment) = self.call_value().egld_or_single_fungible_esdt();
let payment_token =
self.check_owned_return_payment_token::<T>(&requested_token, &requested_amount);
self.check_given_token(&payment_token, &offered_token);
let calculated_price = self.bonding_curve(&requested_token).update(|buffer| {
let serializer = ManagedSerializer::new();
let mut bonding_curve: BondingCurve<Self::Api, T> =
serializer.top_decode_from_managed_buffer(buffer);
let price = self.compute_buy_price::<T>(&requested_token, &requested_amount);
require!(
price <= payment,
"The payment provided is not enough for the transaction"
);
bonding_curve.payment.amount += &price;
bonding_curve.arguments.balance -= &requested_amount;
*buffer = serializer.top_encode_to_managed_buffer(&bonding_curve);
price
});
let caller = self.blockchain().get_caller();
match requested_nonce {
OptionalValue::Some(nonce) => {
self.tx()
.to(&caller)
.single_esdt(&requested_token, nonce, &requested_amount)
.transfer();
if self.nonce_amount(&requested_token, nonce).get() - requested_amount.clone() > 0 {
self.nonce_amount(&requested_token, nonce)
.update(|val| *val -= requested_amount.clone());
} else {
self.nonce_amount(&requested_token, nonce).clear();
self.token_details(&requested_token)
.update(|details| details.remove_nonce(nonce));
}
},
OptionalValue::None => {
self.send_next_available_tokens(&caller, requested_token, requested_amount);
},
};
self.tx()
.to(&caller)
.egld_or_single_esdt(&offered_token, 0u64, &(&payment - &calculated_price))
.transfer();
self.buy_token_event(&caller, &calculated_price);
}
fn send_next_available_tokens(
&self,
caller: &ManagedAddress,
token: TokenIdentifier,
amount: BigUint,
) {
let mut nonces = self.token_details(&token).get().token_nonces;
let mut total_amount = amount;
let mut tokens_to_send = ManagedVec::<Self::Api, EsdtTokenPayment<Self::Api>>::new();
loop {
require!(!nonces.is_empty(), "Insufficient balance");
let nonce = nonces.get(0);
let available_amount = self.nonce_amount(&token, nonce).get();
let amount_to_send: BigUint;
if available_amount <= total_amount {
amount_to_send = available_amount.clone();
total_amount -= amount_to_send.clone();
self.nonce_amount(&token, nonce).clear();
nonces.remove(0);
} else {
self.nonce_amount(&token, nonce)
.update(|val| *val -= total_amount.clone());
amount_to_send = total_amount.clone();
total_amount = BigUint::zero();
}
tokens_to_send.push(EsdtTokenPayment::new(token.clone(), nonce, amount_to_send));
if total_amount == BigUint::zero() {
break;
}
}
self.tx().to(caller).multi_esdt(tokens_to_send).transfer();
self.token_details(&token)
.update(|token_ownership| token_ownership.token_nonces = nonces);
}
fn get_buy_price<T>(&self, amount: BigUint, identifier: TokenIdentifier) -> BigUint
where
T: CurveFunction<Self::Api>
+ TopEncode
+ TopDecode
+ NestedEncode
+ NestedDecode
+ TypeAbi
+ PartialEq
+ Default,
{
self.check_token_exists(&identifier);
self.compute_buy_price::<T>(&identifier, &amount)
}
fn get_sell_price<T>(&self, amount: BigUint, identifier: TokenIdentifier) -> BigUint
where
T: CurveFunction<Self::Api>
+ TopEncode
+ TopDecode
+ NestedEncode
+ NestedDecode
+ TypeAbi
+ PartialEq
+ Default,
{
self.check_token_exists(&identifier);
self.compute_sell_price::<T>(&identifier, &amount)
}
fn check_token_exists(&self, issued_token: &TokenIdentifier) {
require!(
!self.bonding_curve(issued_token).is_empty(),
"Token is not issued yet!"
);
}
#[view(getTokenAvailability)]
fn get_token_availability(
&self,
identifier: TokenIdentifier,
) -> MultiValueEncoded<MultiValue2<u64, BigUint>> {
let token_nonces = self.token_details(&identifier).get().token_nonces;
let mut availability = MultiValueEncoded::new();
for current_check_nonce in &token_nonces {
availability.push(MultiValue2((
current_check_nonce,
self.nonce_amount(&identifier, current_check_nonce).get(),
)));
}
availability
}
fn check_owned_return_payment_token<T>(
&self,
issued_token: &TokenIdentifier,
amount: &BigUint,
) -> EgldOrEsdtTokenIdentifier
where
T: CurveFunction<Self::Api>
+ TopEncode
+ TopDecode
+ NestedEncode
+ NestedDecode
+ TypeAbi
+ PartialEq
+ Default,
{
self.check_token_exists(issued_token);
let serializer = ManagedSerializer::new();
let bonding_curve: BondingCurve<Self::Api, T> =
serializer.top_decode_from_managed_buffer(&self.bonding_curve(issued_token).get());
require!(
bonding_curve.curve != T::default(),
"The token price was not set yet!"
);
require!(amount > &BigUint::zero(), "Must pay more than 0 tokens!");
bonding_curve.payment_token()
}
fn check_given_token(
&self,
accepted_token: &EgldOrEsdtTokenIdentifier,
given_token: &EgldOrEsdtTokenIdentifier,
) {
require!(
given_token == accepted_token,
"Only {} tokens accepted",
accepted_token
);
}
fn compute_buy_price<T>(&self, identifier: &TokenIdentifier, amount: &BigUint) -> BigUint
where
T: CurveFunction<Self::Api>
+ TopEncode
+ TopDecode
+ NestedEncode
+ NestedDecode
+ TypeAbi
+ PartialEq
+ Default,
{
let serializer = ManagedSerializer::new();
let bonding_curve: BondingCurve<Self::Api, T> =
serializer.top_decode_from_managed_buffer(&self.bonding_curve(identifier).get());
let arguments = &bonding_curve.arguments;
let function_selector = &bonding_curve.curve;
let token_start = &arguments.first_token_available();
function_selector.calculate_price(token_start, amount, arguments)
}
fn compute_sell_price<T>(&self, identifier: &TokenIdentifier, amount: &BigUint) -> BigUint
where
T: CurveFunction<Self::Api>
+ TopEncode
+ TopDecode
+ NestedEncode
+ NestedDecode
+ TypeAbi
+ PartialEq
+ Default,
{
let serializer = ManagedSerializer::new();
let bonding_curve: BondingCurve<Self::Api, T> =
serializer.top_decode_from_managed_buffer(&self.bonding_curve(identifier).get());
let arguments = &bonding_curve.arguments;
let function_selector = &bonding_curve.curve;
let token_start = arguments.first_token_available() - amount;
function_selector.calculate_price(&token_start, amount, arguments)
}
}