multiversx_sc_modules/bonding_curve/utils/
user_endpoints.rs1multiversx_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}