2525use frame_support:: { pallet_prelude:: * , transactional} ;
2626use frame_system:: pallet_prelude:: * ;
2727use nutsfinance_stable_asset:: { traits:: StableAsset as StableAssetT , PoolTokenIndex , StableAssetPoolId } ;
28+ use orml_tokens:: ConvertBalance ;
2829use primitives:: { Balance , CurrencyId } ;
2930#[ cfg( feature = "std" ) ]
3031use serde:: { Deserialize , Serialize } ;
@@ -75,6 +76,8 @@ pub mod module {
7576 #[ pallet:: constant]
7677 type SwapPathLimit : Get < u32 > ;
7778
79+ type RebaseTokenAmountConvertor : ConvertBalance < Balance , Balance , AssetId = CurrencyId > ;
80+
7881 type WeightInfo : WeightInfo ;
7982 }
8083
@@ -124,8 +127,8 @@ pub mod module {
124127 pub fn swap_with_exact_supply (
125128 origin : OriginFor < T > ,
126129 paths : Vec < SwapPath > ,
127- supply_amount : Balance ,
128- min_target_amount : Balance ,
130+ # [ pallet :: compact ] supply_amount : Balance ,
131+ # [ pallet :: compact ] min_target_amount : Balance ,
129132 ) -> DispatchResult {
130133 let who = ensure_signed ( origin) ?;
131134 let paths: BoundedVec < SwapPath , T :: SwapPathLimit > =
@@ -134,6 +137,26 @@ pub mod module {
134137 Ok ( ( ) )
135138 }
136139
140+ #[ pallet:: weight( <T as Config >:: WeightInfo :: swap_with_exact_supply(
141+ paths. iter( ) . fold( 0 , |u, swap_path| match swap_path {
142+ SwapPath :: Dex ( v) => u + ( v. len( ) as u32 ) ,
143+ SwapPath :: Taiga ( _, _, _) => u + 1
144+ } )
145+ ) ) ]
146+ #[ transactional]
147+ pub fn swap_with_exact_target (
148+ origin : OriginFor < T > ,
149+ paths : Vec < SwapPath > ,
150+ #[ pallet:: compact] target_amount : Balance ,
151+ #[ pallet:: compact] max_supply_amount : Balance ,
152+ ) -> DispatchResult {
153+ let who = ensure_signed ( origin) ?;
154+ let paths: BoundedVec < SwapPath , T :: SwapPathLimit > =
155+ paths. try_into ( ) . map_err ( |_| Error :: < T > :: InvalidSwapPath ) ?;
156+ let _ = Self :: do_aggregated_swap ( & who, & paths, SwapLimit :: ExactTarget ( max_supply_amount, target_amount) ) ?;
157+ Ok ( ( ) )
158+ }
159+
137160 /// Update the aggregated swap paths for AggregatedSwap to swap TokenA to TokenB.
138161 ///
139162 /// Requires `GovernanceOrigin`
@@ -169,6 +192,110 @@ pub mod module {
169192}
170193
171194impl < T : Config > Pallet < T > {
195+ fn taiga_get_best_route (
196+ supply_currency_id : CurrencyId ,
197+ target_currency_id : CurrencyId ,
198+ supply_amount : Balance ,
199+ ) -> Option < ( StableAssetPoolId , PoolTokenIndex , PoolTokenIndex , Balance ) > {
200+ T :: StableAsset :: get_best_route (
201+ supply_currency_id,
202+ target_currency_id,
203+ T :: RebaseTokenAmountConvertor :: convert_balance ( supply_amount, supply_currency_id) ,
204+ )
205+ . map ( |( pool_id, input_index, output_index, output_amount) | {
206+ (
207+ pool_id,
208+ input_index,
209+ output_index,
210+ T :: RebaseTokenAmountConvertor :: convert_balance_back ( output_amount, target_currency_id) ,
211+ )
212+ } )
213+ }
214+
215+ fn taiga_get_swap_input_amount (
216+ pool_id : StableAssetPoolId ,
217+ input_asset_index : PoolTokenIndex ,
218+ output_asset_index : PoolTokenIndex ,
219+ output_amount : Balance ,
220+ ) -> Option < ( Balance , Balance ) > {
221+ let pool_info = T :: StableAsset :: pool ( pool_id) ?;
222+ let input_currency_id = pool_info. assets . get ( input_asset_index as usize ) ?;
223+ let output_currency_id = pool_info. assets . get ( output_asset_index as usize ) ?;
224+
225+ T :: StableAsset :: get_swap_input_amount (
226+ pool_id,
227+ input_asset_index,
228+ output_asset_index,
229+ T :: RebaseTokenAmountConvertor :: convert_balance ( output_amount, * output_currency_id) ,
230+ )
231+ . map ( |swap_result| {
232+ (
233+ T :: RebaseTokenAmountConvertor :: convert_balance_back ( swap_result. dx , * input_currency_id) ,
234+ T :: RebaseTokenAmountConvertor :: convert_balance_back ( swap_result. dy , * output_currency_id) ,
235+ )
236+ } )
237+ }
238+
239+ fn taiga_get_swap_output_amount (
240+ pool_id : StableAssetPoolId ,
241+ input_asset_index : PoolTokenIndex ,
242+ output_asset_index : PoolTokenIndex ,
243+ input_amount : Balance ,
244+ ) -> Option < ( Balance , Balance ) > {
245+ let pool_info = T :: StableAsset :: pool ( pool_id) ?;
246+ let input_currency_id = pool_info. assets . get ( input_asset_index as usize ) ?;
247+ let output_currency_id = pool_info. assets . get ( output_asset_index as usize ) ?;
248+
249+ T :: StableAsset :: get_swap_output_amount (
250+ pool_id,
251+ input_asset_index,
252+ output_asset_index,
253+ T :: RebaseTokenAmountConvertor :: convert_balance ( input_amount, * input_currency_id) ,
254+ )
255+ . map ( |swap_result| {
256+ (
257+ T :: RebaseTokenAmountConvertor :: convert_balance_back ( swap_result. dx , * input_currency_id) ,
258+ T :: RebaseTokenAmountConvertor :: convert_balance_back ( swap_result. dy , * output_currency_id) ,
259+ )
260+ } )
261+ }
262+
263+ fn taiga_swap (
264+ who : & T :: AccountId ,
265+ pool_id : StableAssetPoolId ,
266+ input_asset_index : PoolTokenIndex ,
267+ output_asset_index : PoolTokenIndex ,
268+ input_amount : Balance ,
269+ min_output_amount : Balance ,
270+ ) -> sp_std:: result:: Result < ( Balance , Balance ) , DispatchError > {
271+ let pool_info = T :: StableAsset :: pool ( pool_id) . ok_or ( Error :: < T > :: InvalidPoolId ) ?;
272+ let asset_length = pool_info. assets . len ( ) as u32 ;
273+ let input_currency_id = pool_info
274+ . assets
275+ . get ( input_asset_index as usize )
276+ . ok_or ( Error :: < T > :: InvalidTokenIndex ) ?;
277+ let output_currency_id = pool_info
278+ . assets
279+ . get ( output_asset_index as usize )
280+ . ok_or ( Error :: < T > :: InvalidTokenIndex ) ?;
281+
282+ T :: StableAsset :: swap (
283+ who,
284+ pool_id,
285+ input_asset_index,
286+ output_asset_index,
287+ T :: RebaseTokenAmountConvertor :: convert_balance ( input_amount, * input_currency_id) ,
288+ T :: RebaseTokenAmountConvertor :: convert_balance ( min_output_amount, * output_currency_id) ,
289+ asset_length,
290+ )
291+ . map ( |( dx, dy) | {
292+ (
293+ T :: RebaseTokenAmountConvertor :: convert_balance_back ( dx, * input_currency_id) ,
294+ T :: RebaseTokenAmountConvertor :: convert_balance_back ( dy, * output_currency_id) ,
295+ )
296+ } )
297+ }
298+
172299 fn check_swap_paths ( paths : & [ SwapPath ] ) -> sp_std:: result:: Result < ( CurrencyId , CurrencyId ) , DispatchError > {
173300 ensure ! ( !paths. is_empty( ) , Error :: <T >:: InvalidSwapPath ) ;
174301 let mut supply_currency_id: Option < CurrencyId > = None ;
@@ -247,13 +374,14 @@ impl<T: Config> Pallet<T> {
247374 }
248375 SwapPath :: Taiga ( pool_id, supply_asset_index, target_asset_index) => {
249376 // use the output of the previous swap as input.
250- output_amount = T :: StableAsset :: get_swap_output_amount (
377+ let ( _ , actual_output_amount ) = Self :: taiga_get_swap_output_amount (
251378 * pool_id,
252379 * supply_asset_index,
253380 * target_asset_index,
254381 output_amount,
255- )
256- . map ( |swap_result| swap_result. dy ) ?;
382+ ) ?;
383+
384+ output_amount = actual_output_amount;
257385 }
258386 }
259387 }
@@ -277,13 +405,15 @@ impl<T: Config> Pallet<T> {
277405 input_amount = supply_amount;
278406 }
279407 SwapPath :: Taiga ( pool_id, supply_asset_index, target_asset_index) => {
280- input_amount = T :: StableAsset :: get_swap_input_amount (
408+ // calculate the input amount
409+ let ( actual_input_amount, _) = Self :: taiga_get_swap_input_amount (
281410 * pool_id,
282411 * supply_asset_index,
283412 * target_asset_index,
284413 input_amount,
285- )
286- . map ( |swap_result| swap_result. dx ) ?;
414+ ) ?;
415+
416+ input_amount = actual_input_amount;
287417 }
288418 }
289419 }
@@ -328,18 +458,14 @@ impl<T: Config> Pallet<T> {
328458 output_amount = actual_target;
329459 }
330460 SwapPath :: Taiga ( pool_id, supply_asset_index, target_asset_index) => {
331- let pool_info = T :: StableAsset :: pool ( * pool_id) . ok_or ( Error :: < T > :: InvalidPoolId ) ?;
332- let asset_length = pool_info. assets . len ( ) as u32 ;
333-
334461 // use the output of the previous swap as input.
335- let ( _, actual_target) = T :: StableAsset :: swap (
462+ let ( _, actual_target) = Self :: taiga_swap (
336463 who,
337464 * pool_id,
338465 * supply_asset_index,
339466 * target_asset_index,
340467 output_amount,
341468 Zero :: zero ( ) ,
342- asset_length,
343469 ) ?;
344470
345471 output_amount = actual_target;
@@ -353,43 +479,12 @@ impl<T: Config> Pallet<T> {
353479 Ok ( ( exact_supply_amount, output_amount) )
354480 }
355481 // Calculate the supply amount first, then execute swap with ExactSupply
356- SwapLimit :: ExactTarget ( max_supply_amount, exact_target_amount) => {
357- let mut input_amount: Balance = exact_target_amount;
358-
359- for path in paths. iter ( ) . rev ( ) {
360- match path {
361- SwapPath :: Dex ( dex_path) => {
362- // calculate the supply amount
363- let ( supply_amount, _) = T :: DEX :: get_swap_amount (
364- dex_path,
365- SwapLimit :: ExactTarget ( Balance :: max_value ( ) , input_amount) ,
366- )
367- . ok_or ( Error :: < T > :: CannotSwap ) ?;
368-
369- input_amount = supply_amount;
370- }
371- SwapPath :: Taiga ( pool_id, supply_asset_index, target_asset_index) => {
372- let swap_result = T :: StableAsset :: get_swap_input_amount (
373- * pool_id,
374- * supply_asset_index,
375- * target_asset_index,
376- input_amount,
377- )
378- . ok_or ( Error :: < T > :: CannotSwap ) ?;
379-
380- input_amount = swap_result. dx ;
381- }
382- }
383- }
384-
385- // the result must meet the SwapLimit.
386- ensure ! (
387- !input_amount. is_zero( ) && input_amount <= max_supply_amount,
388- Error :: <T >:: CannotSwap
389- ) ;
482+ SwapLimit :: ExactTarget ( _max_supply_amount, exact_target_amount) => {
483+ let ( supply_amount, _) =
484+ Self :: get_aggregated_swap_amount ( paths, swap_limit) . ok_or ( Error :: < T > :: CannotSwap ) ?;
390485
391486 // actually swap by `ExactSupply` limit
392- Self :: do_aggregated_swap ( who, paths, SwapLimit :: ExactSupply ( input_amount , exact_target_amount) )
487+ Self :: do_aggregated_swap ( who, paths, SwapLimit :: ExactSupply ( supply_amount , exact_target_amount) )
393488 }
394489 }
395490 }
@@ -434,6 +529,10 @@ impl<T: Config> Swap<T::AccountId, Balance, CurrencyId> for DexSwap<T> {
434529/// Swap by Taiga pool.
435530pub struct TaigaSwap < T > ( PhantomData < T > ) ;
436531impl < T : Config > Swap < T :: AccountId , Balance , CurrencyId > for TaigaSwap < T > {
532+ // !!! Note: if ths limit is `ExactTarget` and the `max_supply_amount` will cause overflow in
533+ // StableAsset, will return `None`. Because the `get_best_route` of StableAsset treats it as the
534+ // actual input amount. However, it will fail so will not cause loss. Maybe need to modiry
535+ // StableAsset impl to avoid this risk.
437536 fn get_swap_amount (
438537 supply_currency_id : CurrencyId ,
439538 target_currency_id : CurrencyId ,
@@ -442,25 +541,30 @@ impl<T: Config> Swap<T::AccountId, Balance, CurrencyId> for TaigaSwap<T> {
442541 match limit {
443542 SwapLimit :: ExactSupply ( supply_amount, min_target_amount) => {
444543 let ( pool_id, input_index, output_index, _) =
445- T :: StableAsset :: get_best_route ( supply_currency_id, target_currency_id, supply_amount) ?;
544+ Pallet :: < T > :: taiga_get_best_route ( supply_currency_id, target_currency_id, supply_amount) ?;
446545
447- if let Some ( swap_result ) =
448- T :: StableAsset :: get_swap_output_amount ( pool_id, input_index, output_index, supply_amount)
546+ if let Some ( ( input_amount , output_amount ) ) =
547+ Pallet :: < T > :: taiga_get_swap_output_amount ( pool_id, input_index, output_index, supply_amount)
449548 {
450- if swap_result . dy >= min_target_amount {
451- return Some ( ( swap_result . dx , swap_result . dy ) ) ;
549+ if output_amount >= min_target_amount {
550+ return Some ( ( input_amount , output_amount ) ) ;
452551 }
453552 }
454553 }
455554 SwapLimit :: ExactTarget ( max_supply_amount, target_amount) => {
456555 let ( pool_id, input_index, output_index, _) =
457- T :: StableAsset :: get_best_route ( supply_currency_id, target_currency_id, max_supply_amount) ?;
556+ Pallet :: < T > :: taiga_get_best_route ( supply_currency_id, target_currency_id, max_supply_amount) ?;
458557
459- if let Some ( swap_result ) =
460- T :: StableAsset :: get_swap_input_amount ( pool_id, input_index, output_index, target_amount)
558+ if let Some ( ( input_amount , _ ) ) =
559+ Pallet :: < T > :: taiga_get_swap_input_amount ( pool_id, input_index, output_index, target_amount)
461560 {
462- if !swap_result. dx . is_zero ( ) && swap_result. dx <= max_supply_amount {
463- return Some ( ( swap_result. dx , swap_result. dy ) ) ;
561+ if !input_amount. is_zero ( ) && input_amount <= max_supply_amount {
562+ // actually swap by `ExactSupply` limit
563+ return Self :: get_swap_amount (
564+ supply_currency_id,
565+ target_currency_id,
566+ SwapLimit :: ExactSupply ( input_amount, target_amount) ,
567+ ) ;
464568 }
465569 }
466570 }
@@ -486,22 +590,18 @@ impl<T: Config> Swap<T::AccountId, Balance, CurrencyId> for TaigaSwap<T> {
486590 } ;
487591
488592 let ( pool_id, input_index, output_index, _) =
489- T :: StableAsset :: get_best_route ( supply_currency_id, target_currency_id, min_target_amount )
593+ Pallet :: < T > :: taiga_get_best_route ( supply_currency_id, target_currency_id, supply_amount )
490594 . ok_or ( Error :: < T > :: CannotSwap ) ?;
491- let pool_info = T :: StableAsset :: pool ( pool_id) . ok_or ( Error :: < T > :: InvalidPoolId ) ?;
492- let asset_length = pool_info. assets . len ( ) as u32 ;
493-
494- let ( actual_supply, actual_target) = T :: StableAsset :: swap (
595+ let ( actual_supply, actual_target) = Pallet :: < T > :: taiga_swap (
495596 who,
496597 pool_id,
497598 input_index,
498599 output_index,
499600 supply_amount,
500601 min_target_amount,
501- asset_length,
502602 ) ?;
503- ensure ! ( actual_target >= min_target_amount, Error :: <T >:: CannotSwap ) ;
504603
604+ ensure ! ( actual_target >= min_target_amount, Error :: <T >:: CannotSwap ) ;
505605 Ok ( ( actual_supply, actual_target) )
506606 }
507607}
0 commit comments