1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use crate::{Config, Error, Pallet};
use frame_support::ensure;
use frame_support::traits::Get;
use hydradx_traits::router::{ExecutorError, PoolType, TradeExecution};
use hydradx_traits::AMM;
use orml_traits::MultiCurrency;
use primitives::asset::AssetPair;
use primitives::{AssetId, Balance};
use sp_runtime::DispatchError;
impl<T: Config> TradeExecution<T::Origin, T::AccountId, AssetId, Balance> for Pallet<T> {
type Error = DispatchError;
fn calculate_sell(
pool_type: PoolType<AssetId>,
asset_in: AssetId,
asset_out: AssetId,
amount_in: Balance,
) -> Result<Balance, ExecutorError<Self::Error>> {
if pool_type != PoolType::XYK {
return Err(ExecutorError::NotSupported);
}
let assets = AssetPair { asset_in, asset_out };
if !Self::exists(assets) {
return Err(ExecutorError::Error(Error::<T>::TokenPoolNotFound.into()));
}
let pair_account = Self::get_pair_id(assets);
let asset_in_reserve = T::Currency::free_balance(assets.asset_in, &pair_account);
let asset_out_reserve = T::Currency::free_balance(assets.asset_out, &pair_account);
let amount_out = hydra_dx_math::xyk::calculate_out_given_in(asset_in_reserve, asset_out_reserve, amount_in)
.map_err(|_| ExecutorError::Error(Error::<T>::SellAssetAmountInvalid.into()))?;
ensure!(
asset_out_reserve > amount_out,
ExecutorError::Error(Error::<T>::InsufficientPoolAssetBalance.into())
);
let transfer_fee = Self::calculate_fee(amount_out).map_err(ExecutorError::Error)?;
let amount_out_without_fee = amount_out
.checked_sub(transfer_fee)
.ok_or_else(|| ExecutorError::Error(Error::<T>::SellAssetAmountInvalid.into()))?;
Ok(amount_out_without_fee)
}
fn calculate_buy(
pool_type: PoolType<AssetId>,
asset_in: AssetId,
asset_out: AssetId,
amount_out: Balance,
) -> Result<Balance, ExecutorError<Self::Error>> {
if pool_type != PoolType::XYK {
return Err(ExecutorError::NotSupported);
}
let assets = AssetPair { asset_in, asset_out };
ensure!(
Self::exists(assets),
ExecutorError::Error(Error::<T>::TokenPoolNotFound.into())
);
let pair_account = Self::get_pair_id(assets);
let asset_out_reserve = T::Currency::free_balance(assets.asset_out, &pair_account);
let asset_in_reserve = T::Currency::free_balance(assets.asset_in, &pair_account);
ensure!(
asset_out_reserve > amount_out,
ExecutorError::Error(Error::<T>::InsufficientPoolAssetBalance.into())
);
ensure!(
amount_out >= T::MinTradingLimit::get(),
ExecutorError::Error(Error::<T>::InsufficientTradingAmount.into())
);
let amount_in = hydra_dx_math::xyk::calculate_in_given_out(asset_out_reserve, asset_in_reserve, amount_out)
.map_err(|_| ExecutorError::Error(Error::<T>::BuyAssetAmountInvalid.into()))?;
let transfer_fee = Self::calculate_fee(amount_in).map_err(ExecutorError::Error)?;
let amount_in_with_fee = amount_in
.checked_add(transfer_fee)
.ok_or_else(|| ExecutorError::Error(Error::<T>::BuyAssetAmountInvalid.into()))?;
Ok(amount_in_with_fee)
}
fn execute_sell(
who: T::Origin,
pool_type: PoolType<AssetId>,
asset_in: AssetId,
asset_out: AssetId,
amount_in: Balance,
min_limit: Balance,
) -> Result<(), ExecutorError<Self::Error>> {
if pool_type != PoolType::XYK {
return Err(ExecutorError::NotSupported);
}
Self::sell(who, asset_in, asset_out, amount_in, min_limit, false).map_err(ExecutorError::Error)
}
fn execute_buy(
who: T::Origin,
pool_type: PoolType<AssetId>,
asset_in: AssetId,
asset_out: AssetId,
amount_out: Balance,
max_limit: Balance,
) -> Result<(), ExecutorError<Self::Error>> {
if pool_type != PoolType::XYK {
return Err(ExecutorError::NotSupported);
}
Self::buy(who, asset_out, asset_in, amount_out, max_limit, false).map_err(ExecutorError::Error)
}
}