multiversx_sc_modules/bonding_curve/utils/
user_endpoints.rs

1multiversx_sc::imports!();
2multiversx_sc::derive_imports!();
3
4use multiversx_sc::contract_base::ManagedSerializer;
5
6use crate::bonding_curve::{
7    curves::curve_function::CurveFunction,
8    utils::{events, storage, structs::BondingCurve},
9};
10
11#[multiversx_sc::module]
12pub trait UserEndpointsModule: storage::StorageModule + events::EventsModule {
13    fn sell_token<T>(&self)
14    where
15        T: CurveFunction<Self::Api>
16            + TopEncode
17            + TopDecode
18            + NestedEncode
19            + NestedDecode
20            + TypeAbi
21            + PartialEq
22            + Default,
23    {
24        let esdt_payment = self.call_value().single_esdt();
25        let offered_token = &esdt_payment.token_identifier;
26        let nonce = esdt_payment.token_nonce;
27        let sell_amount = &esdt_payment.amount;
28        let _ = self.check_owned_return_payment_token::<T>(offered_token, sell_amount);
29
30        let (calculated_price, payment_token) =
31            self.bonding_curve(offered_token).update(|buffer| {
32                let serializer = ManagedSerializer::new();
33
34                let mut bonding_curve: BondingCurve<Self::Api, T> =
35                    serializer.top_decode_from_managed_buffer(buffer);
36
37                require!(
38                    bonding_curve.sell_availability,
39                    "Selling is not available on this token"
40                );
41                let price = self.compute_sell_price::<T>(offered_token, sell_amount);
42                bonding_curve.payment.amount -= &price;
43                bonding_curve.arguments.balance += sell_amount;
44                let payment_token = bonding_curve.payment_token();
45                *buffer = serializer.top_encode_to_managed_buffer(&bonding_curve);
46                (price, payment_token)
47            });
48
49        let caller = self.blockchain().get_caller();
50
51        self.nonce_amount(offered_token, nonce)
52            .update(|val| *val += sell_amount);
53
54        self.tx()
55            .to(&caller)
56            .egld_or_single_esdt(&payment_token, 0u64, &calculated_price)
57            .transfer();
58
59        self.token_details(offered_token)
60            .update(|details| details.add_nonce(nonce));
61
62        self.sell_token_event(&caller, &calculated_price);
63    }
64
65    fn buy_token<T>(
66        &self,
67        requested_amount: BigUint,
68        requested_token: TokenIdentifier,
69        requested_nonce: OptionalValue<u64>,
70    ) where
71        T: CurveFunction<Self::Api>
72            + TopEncode
73            + TopDecode
74            + NestedEncode
75            + NestedDecode
76            + TypeAbi
77            + PartialEq
78            + Default,
79    {
80        let (offered_token, payment) = self.call_value().egld_or_single_fungible_esdt();
81        let payment_token =
82            self.check_owned_return_payment_token::<T>(&requested_token, &requested_amount);
83        self.check_given_token(&payment_token, &offered_token);
84
85        let calculated_price = self.bonding_curve(&requested_token).update(|buffer| {
86            let serializer = ManagedSerializer::new();
87
88            let mut bonding_curve: BondingCurve<Self::Api, T> =
89                serializer.top_decode_from_managed_buffer(buffer);
90
91            let price = self.compute_buy_price::<T>(&requested_token, &requested_amount);
92            require!(
93                price <= payment,
94                "The payment provided is not enough for the transaction"
95            );
96            bonding_curve.payment.amount += &price;
97            bonding_curve.arguments.balance -= &requested_amount;
98            *buffer = serializer.top_encode_to_managed_buffer(&bonding_curve);
99
100            price
101        });
102
103        let caller = self.blockchain().get_caller();
104
105        match requested_nonce {
106            OptionalValue::Some(nonce) => {
107                self.tx()
108                    .to(&caller)
109                    .single_esdt(&requested_token, nonce, &requested_amount)
110                    .transfer();
111                if self.nonce_amount(&requested_token, nonce).get() - requested_amount.clone() > 0 {
112                    self.nonce_amount(&requested_token, nonce)
113                        .update(|val| *val -= requested_amount.clone());
114                } else {
115                    self.nonce_amount(&requested_token, nonce).clear();
116                    self.token_details(&requested_token)
117                        .update(|details| details.remove_nonce(nonce));
118                }
119            }
120            OptionalValue::None => {
121                self.send_next_available_tokens(&caller, requested_token, requested_amount);
122            }
123        };
124
125        self.tx()
126            .to(&caller)
127            .egld_or_single_esdt(&offered_token, 0u64, &(&payment - &calculated_price))
128            .transfer();
129
130        self.buy_token_event(&caller, &calculated_price);
131    }
132
133    fn send_next_available_tokens(
134        &self,
135        caller: &ManagedAddress,
136        token: TokenIdentifier,
137        amount: BigUint,
138    ) {
139        let mut nonces = self.token_details(&token).get().token_nonces;
140        let mut total_amount = amount;
141        let mut tokens_to_send = ManagedVec::<Self::Api, EsdtTokenPayment<Self::Api>>::new();
142        loop {
143            require!(!nonces.is_empty(), "Insufficient balance");
144            let nonce = nonces.get(0);
145            let available_amount = self.nonce_amount(&token, nonce).get();
146
147            let amount_to_send: BigUint;
148            if available_amount <= total_amount {
149                amount_to_send = available_amount.clone();
150                total_amount -= amount_to_send.clone();
151                self.nonce_amount(&token, nonce).clear();
152                nonces.remove(0);
153            } else {
154                self.nonce_amount(&token, nonce)
155                    .update(|val| *val -= total_amount.clone());
156                amount_to_send = total_amount.clone();
157                total_amount = BigUint::zero();
158            }
159            tokens_to_send.push(EsdtTokenPayment::new(token.clone(), nonce, amount_to_send));
160            if total_amount == BigUint::zero() {
161                break;
162            }
163        }
164
165        self.tx().to(caller).multi_esdt(tokens_to_send).transfer();
166
167        self.token_details(&token)
168            .update(|token_ownership| token_ownership.token_nonces = nonces);
169    }
170
171    fn get_buy_price<T>(&self, amount: BigUint, identifier: TokenIdentifier) -> BigUint
172    where
173        T: CurveFunction<Self::Api>
174            + TopEncode
175            + TopDecode
176            + NestedEncode
177            + NestedDecode
178            + TypeAbi
179            + PartialEq
180            + Default,
181    {
182        self.check_token_exists(&identifier);
183        self.compute_buy_price::<T>(&identifier, &amount)
184    }
185
186    fn get_sell_price<T>(&self, amount: BigUint, identifier: TokenIdentifier) -> BigUint
187    where
188        T: CurveFunction<Self::Api>
189            + TopEncode
190            + TopDecode
191            + NestedEncode
192            + NestedDecode
193            + TypeAbi
194            + PartialEq
195            + Default,
196    {
197        self.check_token_exists(&identifier);
198        self.compute_sell_price::<T>(&identifier, &amount)
199    }
200
201    fn check_token_exists(&self, issued_token: &TokenIdentifier) {
202        require!(
203            !self.bonding_curve(issued_token).is_empty(),
204            "Token is not issued yet!"
205        );
206    }
207
208    #[view(getTokenAvailability)]
209    fn get_token_availability(
210        &self,
211        identifier: TokenIdentifier,
212    ) -> MultiValueEncoded<MultiValue2<u64, BigUint>> {
213        let token_nonces = self.token_details(&identifier).get().token_nonces;
214        let mut availability = MultiValueEncoded::new();
215
216        for current_check_nonce in &token_nonces {
217            availability.push(MultiValue2((
218                current_check_nonce,
219                self.nonce_amount(&identifier, current_check_nonce).get(),
220            )));
221        }
222        availability
223    }
224
225    fn check_owned_return_payment_token<T>(
226        &self,
227        issued_token: &TokenIdentifier,
228        amount: &BigUint,
229    ) -> EgldOrEsdtTokenIdentifier
230    where
231        T: CurveFunction<Self::Api>
232            + TopEncode
233            + TopDecode
234            + NestedEncode
235            + NestedDecode
236            + TypeAbi
237            + PartialEq
238            + Default,
239    {
240        self.check_token_exists(issued_token);
241
242        let serializer = ManagedSerializer::new();
243        let bonding_curve: BondingCurve<Self::Api, T> =
244            serializer.top_decode_from_managed_buffer(&self.bonding_curve(issued_token).get());
245
246        require!(
247            bonding_curve.curve != T::default(),
248            "The token price was not set yet!"
249        );
250        require!(amount > &BigUint::zero(), "Must pay more than 0 tokens!");
251        bonding_curve.payment_token()
252    }
253
254    fn check_given_token(
255        &self,
256        accepted_token: &EgldOrEsdtTokenIdentifier,
257        given_token: &EgldOrEsdtTokenIdentifier,
258    ) {
259        require!(
260            given_token == accepted_token,
261            "Only {} tokens accepted",
262            accepted_token
263        );
264    }
265
266    fn compute_buy_price<T>(&self, identifier: &TokenIdentifier, amount: &BigUint) -> BigUint
267    where
268        T: CurveFunction<Self::Api>
269            + TopEncode
270            + TopDecode
271            + NestedEncode
272            + NestedDecode
273            + TypeAbi
274            + PartialEq
275            + Default,
276    {
277        let serializer = ManagedSerializer::new();
278        let bonding_curve: BondingCurve<Self::Api, T> =
279            serializer.top_decode_from_managed_buffer(&self.bonding_curve(identifier).get());
280
281        let arguments = &bonding_curve.arguments;
282        let function_selector = &bonding_curve.curve;
283
284        let token_start = &arguments.first_token_available();
285        function_selector.calculate_price(token_start, amount, arguments)
286    }
287
288    fn compute_sell_price<T>(&self, identifier: &TokenIdentifier, amount: &BigUint) -> BigUint
289    where
290        T: CurveFunction<Self::Api>
291            + TopEncode
292            + TopDecode
293            + NestedEncode
294            + NestedDecode
295            + TypeAbi
296            + PartialEq
297            + Default,
298    {
299        let serializer = ManagedSerializer::new();
300        let bonding_curve: BondingCurve<Self::Api, T> =
301            serializer.top_decode_from_managed_buffer(&self.bonding_curve(identifier).get());
302
303        let arguments = &bonding_curve.arguments;
304        let function_selector = &bonding_curve.curve;
305
306        let token_start = arguments.first_token_available() - amount;
307        function_selector.calculate_price(&token_start, amount, arguments)
308    }
309}