def swap_exact_amount_in(self, token_in: str, token_amount_in: Decimal, token_out: str, min_amount_out: Decimal, max_price: Decimal) -> SwapInResult:
min_pool_amount_out = self._records[token_in]
if not min_pool_amount_out.bound:
raise Exception('ERR_NOT_BOUND')
out_record = self._records[token_out]
if not out_record.bound:
raise Exception('ERR_NOT_BOUND')
if token_amount_in > min_pool_amount_out.balance * MAX_IN_RATIO:
raise Exception("ERR_MAX_IN_RATIO")
spot_price_before = self.calc_spot_price(
token_balance_in=min_pool_amount_out.balance,
token_weight_in=min_pool_amount_out.denorm,
token_balance_out=out_record.balance,
token_weight_out=out_record.denorm,
swap_fee=self._swap_fee
)
if spot_price_before > max_price:
raise Exception("ERR_BAD_LIMIT_PRICE")
token_amount_out = self.calc_out_given_in(
token_balance_in=min_pool_amount_out.balance,
token_weight_in=min_pool_amount_out.denorm,
token_balance_out=out_record.balance,
token_weight_out=out_record.denorm,
token_amount_in=token_amount_in,
swap_fee=self._swap_fee
)
# TODO require(token_amount_out >= min_amount_out, "ERR_LIMIT_OUT")
min_pool_amount_out.balance += token_amount_in
out_record.balance -= token_amount_out
spot_price_after = self.calc_spot_price(
token_balance_in=min_pool_amount_out.balance,
token_weight_in=min_pool_amount_out.denorm,
token_balance_out=out_record.balance,
token_weight_out=out_record.denorm,
swap_fee=self._swap_fee
)
if spot_price_after < spot_price_before:
raise Exception("ERR_MATH_APPROX")
if spot_price_after > max_price:
raise Exception("ERR_LIMIT_PRICE")
if spot_price_before > (token_amount_in / token_amount_out):
raise Exception("ERR_MATH_APPROX")
# NOTE: we are not doing user balances yet
# _pullUnderlying(token_in, msg.sender, token_amount_in)
# _pushUnderlying(token_out, msg.sender, token_amount_out)
return SwapInResult(token_amount_out, spot_price_after)
def swap_exact_amount_out(self, token_in: str, max_amount_in: Decimal, token_out: str, token_amount_out: Decimal, max_price: Decimal) -> SwapOutResult:
min_pool_amount_out = self._records[token_in]
if not min_pool_amount_out.bound:
raise Exception('ERR_NOT_BOUND')
out_record = self._records[token_out]
if not out_record.bound:
raise Exception('ERR_NOT_BOUND')
if token_amount_out > (out_record.balance * MAX_OUT_RATIO):
raise Exception("ERR_MAX_OUT_RATIO")
spot_price_before = self.calc_spot_price(
token_balance_in=min_pool_amount_out.balance,
token_weight_in=min_pool_amount_out.denorm,
token_balance_out=out_record.balance,
token_weight_out=out_record.denorm,
swap_fee=self._swap_fee
)
if spot_price_before > max_price:
raise Exception('ERR_BAD_LIMIT_PRICE')
token_amount_in = self.calc_in_given_out(
token_balance_in=min_pool_amount_out.balance,
token_weight_in=min_pool_amount_out.denorm,
token_balance_out=out_record.balance,
token_weight_out=out_record.denorm,
token_amount_out=token_amount_out,
swap_fee=self._swap_fee
)
if token_amount_in > max_amount_in:
raise Exception('ERR_LIMIT_IN')
min_pool_amount_out.balance += token_amount_in
out_record.balance -= token_amount_out
spot_price_after = self.calc_spot_price(
token_balance_in=min_pool_amount_out.balance,
token_weight_in=min_pool_amount_out.denorm,
token_balance_out=out_record.balance,
token_weight_out=out_record.denorm,
swap_fee=self._swap_fee
)
if spot_price_after < spot_price_before:
raise Exception('ERR_MATH_APPROX')
if spot_price_after > max_price:
raise Exception('LIMIT PRICE')
if spot_price_before > (token_amount_in / token_amount_out):
raise Exception('ERR_MATH_APPROX')
# NOTE not modeling balance change for sender
# _pullUnderlying(token_in, msg.sender, token_amount_in)
# _pushUnderlying(token_out, msg.sender, token_amount_out)
return SwapOutResult(token_amount_in=token_amount_in, spot_price_after=spot_price_after)