@@ -27,7 +27,7 @@ template <typename TIn, typename TOut>
2727AMMOffer<TIn, TOut>::AMMOffer(
2828 AMMLiquidity<TIn, TOut> const & ammLiquidity,
2929 TAmounts<TIn, TOut> const & amounts,
30- std::optional< TAmounts<TIn, TOut> > const & balances,
30+ TAmounts<TIn, TOut> const & balances,
3131 Quality const & quality)
3232 : ammLiquidity_(ammLiquidity)
3333 , amounts_(amounts)
@@ -110,7 +110,7 @@ AMMOffer<TIn, TOut>::limitOut(
110110 // Change the offer size according to the conservation function. The offer
111111 // quality is increased in this case, but it doesn't matter since there is
112112 // only one path.
113- return {swapAssetOut (* balances_, limit, ammLiquidity_.tradingFee ()), limit};
113+ return {swapAssetOut (balances_, limit, ammLiquidity_.tradingFee ()), limit};
114114}
115115
116116template <typename TIn, typename TOut>
@@ -122,7 +122,7 @@ AMMOffer<TIn, TOut>::limitIn(
122122 // See the comments above in limitOut().
123123 if (ammLiquidity_.multiPath ())
124124 return quality ().ceil_in (offrAmt, limit);
125- return {limit, swapAssetIn (* balances_, limit, ammLiquidity_.tradingFee ())};
125+ return {limit, swapAssetIn (balances_, limit, ammLiquidity_.tradingFee ())};
126126}
127127
128128template <typename TIn, typename TOut>
@@ -132,7 +132,45 @@ AMMOffer<TIn, TOut>::getQualityFunc() const
132132 if (ammLiquidity_.multiPath ())
133133 return QualityFunction{quality (), QualityFunction::CLOBLikeTag{}};
134134 return QualityFunction{
135- *balances_, ammLiquidity_.tradingFee (), QualityFunction::AMMTag{}};
135+ balances_, ammLiquidity_.tradingFee (), QualityFunction::AMMTag{}};
136+ }
137+
138+ template <typename TIn, typename TOut>
139+ bool
140+ AMMOffer<TIn, TOut>::checkInvariant(
141+ TAmounts<TIn, TOut> const & consumed,
142+ beast::Journal j) const
143+ {
144+ if (consumed.in > amounts_.in || consumed.out > amounts_.out )
145+ {
146+ JLOG (j.error ()) << " AMMOffer::checkInvariant failed: consumed "
147+ << to_string (consumed.in ) << " "
148+ << to_string (consumed.out ) << " amounts "
149+ << to_string (amounts_.in ) << " "
150+ << to_string (amounts_.out );
151+
152+ return false ;
153+ }
154+
155+ Number const product = balances_.in * balances_.out ;
156+ auto const newBalances = TAmounts<TIn, TOut>{
157+ balances_.in + consumed.in , balances_.out - consumed.out };
158+ Number const newProduct = newBalances.in * newBalances.out ;
159+
160+ if (newProduct >= product ||
161+ withinRelativeDistance (product, newProduct, Number{1 , -7 }))
162+ return true ;
163+
164+ JLOG (j.error ()) << " AMMOffer::checkInvariant failed: balances "
165+ << to_string (balances_.in ) << " "
166+ << to_string (balances_.out ) << " new balances "
167+ << to_string (newBalances.in ) << " "
168+ << to_string (newBalances.out ) << " product/newProduct "
169+ << product << " " << newProduct << " diff "
170+ << (product != Number{0 }
171+ ? to_string ((product - newProduct) / product)
172+ : " undefined" );
173+ return false ;
136174}
137175
138176template class AMMOffer <STAmount, STAmount>;
0 commit comments