Skip to content

Automated Market Marke Types

Module providing types and state classes for AMM pools.

AbstractCommonStableSwapPoolState

Bases: AbstractStableSwapPoolState

The common variant of StableSwap.

This class implements the common variant of the stableswap algorithm. The main difference is the

Source code in src/charli3_dendrite/dexs/amm/amm_types.py
367
368
369
370
371
372
373
374
375
376
377
378
379
class AbstractCommonStableSwapPoolState(AbstractStableSwapPoolState):
    """The common variant of StableSwap.

    This class implements the common variant of the stableswap algorithm. The main
    difference is the
    """

    def _get_ann(self) -> int:
        """The modified amp value.

        This is the ann value in the common stableswap variant.
        """
        return self.amp * N_COINS

AbstractConstantLiquidityPoolState

Bases: AbstractPoolState

Represents the state of a constant liquidity pool automated market maker (AMM).

This class serves as a base for constant liquidity pool implementations, providing methods to calculate the input and output asset amounts for swaps.

Source code in src/charli3_dendrite/dexs/amm/amm_types.py
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
class AbstractConstantLiquidityPoolState(AbstractPoolState):
    """Represents the state of a constant liquidity pool automated market maker (AMM).

    This class serves as a base for constant liquidity pool implementations, providing
    methods to calculate the input and output asset amounts for swaps.
    """

    def get_amount_out(
        self,
        asset: Assets,
        precise: bool = True,
    ) -> tuple[Assets, float]:
        """Calculate the output amount for a given input in a constant liquidity pool.

        Args:
            asset (Assets): The input asset amount for the swap.
            precise (bool): If True: the output rounded to the nearest integer.

        Returns:
            tuple[Assets, float]: Tuple containing the output asset and float value.

        Raises:
            NotImplementedError: This method is not implemented in the base class.
        """
        error_msg = "CLPP amount out is not yet implemented."
        raise NotImplementedError(error_msg)

    def get_amount_in(
        self,
        asset: Assets,
        precise: bool = True,
    ) -> tuple[Assets, float]:
        """Calculate input amount needed for desired output in constant liquidity pool.

        Args:
            asset (Assets): The desired output asset amount for the swap.
            precise (bool): If True: the output rounded to the nearest integer.

        Returns:
            tuple[Assets, float]: Tuple containing required input asset and float value.

        Raises:
            NotImplementedError: This method is not implemented in the base class.
        """
        error_msg = "CLPP amount in is not yet implemented."
        raise NotImplementedError(error_msg)

get_amount_in(asset: Assets, precise: bool = True) -> tuple[Assets, float]

Calculate input amount needed for desired output in constant liquidity pool.

Parameters:

Name Type Description Default
asset Assets

The desired output asset amount for the swap.

required
precise bool

If True: the output rounded to the nearest integer.

True

Returns:

Type Description
tuple[Assets, float]

tuple[Assets, float]: Tuple containing required input asset and float value.

Raises:

Type Description
NotImplementedError

This method is not implemented in the base class.

Source code in src/charli3_dendrite/dexs/amm/amm_types.py
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
def get_amount_in(
    self,
    asset: Assets,
    precise: bool = True,
) -> tuple[Assets, float]:
    """Calculate input amount needed for desired output in constant liquidity pool.

    Args:
        asset (Assets): The desired output asset amount for the swap.
        precise (bool): If True: the output rounded to the nearest integer.

    Returns:
        tuple[Assets, float]: Tuple containing required input asset and float value.

    Raises:
        NotImplementedError: This method is not implemented in the base class.
    """
    error_msg = "CLPP amount in is not yet implemented."
    raise NotImplementedError(error_msg)

get_amount_out(asset: Assets, precise: bool = True) -> tuple[Assets, float]

Calculate the output amount for a given input in a constant liquidity pool.

Parameters:

Name Type Description Default
asset Assets

The input asset amount for the swap.

required
precise bool

If True: the output rounded to the nearest integer.

True

Returns:

Type Description
tuple[Assets, float]

tuple[Assets, float]: Tuple containing the output asset and float value.

Raises:

Type Description
NotImplementedError

This method is not implemented in the base class.

Source code in src/charli3_dendrite/dexs/amm/amm_types.py
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
def get_amount_out(
    self,
    asset: Assets,
    precise: bool = True,
) -> tuple[Assets, float]:
    """Calculate the output amount for a given input in a constant liquidity pool.

    Args:
        asset (Assets): The input asset amount for the swap.
        precise (bool): If True: the output rounded to the nearest integer.

    Returns:
        tuple[Assets, float]: Tuple containing the output asset and float value.

    Raises:
        NotImplementedError: This method is not implemented in the base class.
    """
    error_msg = "CLPP amount out is not yet implemented."
    raise NotImplementedError(error_msg)

AbstractConstantProductPoolState

Bases: AbstractPoolState

Represents the state of a constant product automated market maker (AMM) pool.

Source code in src/charli3_dendrite/dexs/amm/amm_types.py
 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
126
127
128
129
130
class AbstractConstantProductPoolState(AbstractPoolState):
    """Represents the state of a constant product automated market maker (AMM) pool."""

    def get_amount_out(
        self,
        asset: Assets,
        precise: bool = True,
    ) -> tuple[Assets, float]:
        """Get the output asset amount given an input asset amount.

        Args:
            asset (Assets): An asset with a defined quantity.
            precise (bool): Whether to return precise calculations.

        Returns:
            A tuple where the first value is the estimated asset returned from the swap
                and the second value is the price impact ratio.
        """
        if len(asset) != 1:
            error_msg = "Asset should only have one token."
            raise ValueError(error_msg)
        if asset.unit() not in [self.unit_a, self.unit_b]:
            error_msg = (
                f"Asset {asset.unit()} is invalid for pool {self.unit_a}-{self.unit_b}"
            )
            raise ValueError(error_msg)

        if asset.unit() == self.unit_a:
            reserve_in, reserve_out = self.reserve_a, self.reserve_b
            unit_out = self.unit_b
        else:
            reserve_in, reserve_out = self.reserve_b, self.reserve_a
            unit_out = self.unit_a

        volume_fee: int = 0
        if self.volume_fee is not None:
            if isinstance(self.volume_fee, int):
                volume_fee = self.volume_fee
            elif asset.unit() == self.unit_a:
                volume_fee = self.volume_fee[0]
            else:
                volume_fee = self.volume_fee[1]

        # Calculate the amount out
        fee_modifier = 10000 - volume_fee
        numerator: int = asset.quantity() * fee_modifier * reserve_out
        denominator: int = asset.quantity() * fee_modifier + reserve_in * 10000
        amount_out = Assets(**{unit_out: numerator // denominator})
        if not precise:
            amount_out.root[unit_out] = numerator // denominator

        if amount_out.quantity() == 0:
            return amount_out, 0

        # Calculate the price impact
        price_numerator: int = (
            reserve_out * asset.quantity() * denominator * fee_modifier
            - numerator * reserve_in * 10000
        )
        price_denominator: int = reserve_out * asset.quantity() * denominator * 10000
        price_impact: float = price_numerator / price_denominator

        return amount_out, price_impact

    def get_amount_in(
        self,
        asset: Assets,
        precise: bool = True,
    ) -> tuple[Assets, float]:
        """Get the input asset amount given a desired output asset amount.

        Args:
            asset (Assets): An asset with a defined quantity.
            precise (bool): Whether to return precise calculations.

        Returns:
            The estimated asset needed for input in the swap.
        """
        if len(asset) != 1:
            error_msg = "Asset should only have one token."
            raise ValueError(error_msg)
        if asset.unit() not in [self.unit_a, self.unit_b]:
            error_msg = (
                f"Asset {asset.unit()} is invalid for pool {self.unit_a}-{self.unit_b}"
            )
            raise ValueError(error_msg)

        if asset.unit() == self.unit_b:
            reserve_in, reserve_out = self.reserve_a, self.reserve_b
            unit_out = self.unit_a
        else:
            reserve_in, reserve_out = self.reserve_b, self.reserve_a
            unit_out = self.unit_b

        volume_fee: int = 0
        if self.volume_fee is not None:
            if isinstance(self.volume_fee, int):
                volume_fee = self.volume_fee
            elif asset.unit() == self.unit_b:
                volume_fee = self.volume_fee[0]
            else:
                volume_fee = self.volume_fee[1]

        # Estimate the required input
        fee_modifier = 10000 - volume_fee
        numerator: int = asset.quantity() * 10000 * reserve_in
        denominator: int = (reserve_out - asset.quantity()) * fee_modifier
        amount_in = Assets(**{unit_out: numerator // denominator})
        if not precise:
            amount_in.root[unit_out] = numerator // denominator

        # Estimate the price impact
        price_numerator: int = (
            reserve_out * numerator * fee_modifier
            - asset.quantity() * denominator * reserve_in * 10000
        )
        price_denominator: int = reserve_out * numerator * 10000
        price_impact: float = price_numerator / price_denominator

        return amount_in, price_impact

get_amount_in(asset: Assets, precise: bool = True) -> tuple[Assets, float]

Get the input asset amount given a desired output asset amount.

Parameters:

Name Type Description Default
asset Assets

An asset with a defined quantity.

required
precise bool

Whether to return precise calculations.

True

Returns:

Type Description
tuple[Assets, float]

The estimated asset needed for input in the swap.

Source code in src/charli3_dendrite/dexs/amm/amm_types.py
 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
126
127
128
129
130
def get_amount_in(
    self,
    asset: Assets,
    precise: bool = True,
) -> tuple[Assets, float]:
    """Get the input asset amount given a desired output asset amount.

    Args:
        asset (Assets): An asset with a defined quantity.
        precise (bool): Whether to return precise calculations.

    Returns:
        The estimated asset needed for input in the swap.
    """
    if len(asset) != 1:
        error_msg = "Asset should only have one token."
        raise ValueError(error_msg)
    if asset.unit() not in [self.unit_a, self.unit_b]:
        error_msg = (
            f"Asset {asset.unit()} is invalid for pool {self.unit_a}-{self.unit_b}"
        )
        raise ValueError(error_msg)

    if asset.unit() == self.unit_b:
        reserve_in, reserve_out = self.reserve_a, self.reserve_b
        unit_out = self.unit_a
    else:
        reserve_in, reserve_out = self.reserve_b, self.reserve_a
        unit_out = self.unit_b

    volume_fee: int = 0
    if self.volume_fee is not None:
        if isinstance(self.volume_fee, int):
            volume_fee = self.volume_fee
        elif asset.unit() == self.unit_b:
            volume_fee = self.volume_fee[0]
        else:
            volume_fee = self.volume_fee[1]

    # Estimate the required input
    fee_modifier = 10000 - volume_fee
    numerator: int = asset.quantity() * 10000 * reserve_in
    denominator: int = (reserve_out - asset.quantity()) * fee_modifier
    amount_in = Assets(**{unit_out: numerator // denominator})
    if not precise:
        amount_in.root[unit_out] = numerator // denominator

    # Estimate the price impact
    price_numerator: int = (
        reserve_out * numerator * fee_modifier
        - asset.quantity() * denominator * reserve_in * 10000
    )
    price_denominator: int = reserve_out * numerator * 10000
    price_impact: float = price_numerator / price_denominator

    return amount_in, price_impact

get_amount_out(asset: Assets, precise: bool = True) -> tuple[Assets, float]

Get the output asset amount given an input asset amount.

Parameters:

Name Type Description Default
asset Assets

An asset with a defined quantity.

required
precise bool

Whether to return precise calculations.

True

Returns:

Type Description
tuple[Assets, float]

A tuple where the first value is the estimated asset returned from the swap and the second value is the price impact ratio.

Source code in src/charli3_dendrite/dexs/amm/amm_types.py
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
def get_amount_out(
    self,
    asset: Assets,
    precise: bool = True,
) -> tuple[Assets, float]:
    """Get the output asset amount given an input asset amount.

    Args:
        asset (Assets): An asset with a defined quantity.
        precise (bool): Whether to return precise calculations.

    Returns:
        A tuple where the first value is the estimated asset returned from the swap
            and the second value is the price impact ratio.
    """
    if len(asset) != 1:
        error_msg = "Asset should only have one token."
        raise ValueError(error_msg)
    if asset.unit() not in [self.unit_a, self.unit_b]:
        error_msg = (
            f"Asset {asset.unit()} is invalid for pool {self.unit_a}-{self.unit_b}"
        )
        raise ValueError(error_msg)

    if asset.unit() == self.unit_a:
        reserve_in, reserve_out = self.reserve_a, self.reserve_b
        unit_out = self.unit_b
    else:
        reserve_in, reserve_out = self.reserve_b, self.reserve_a
        unit_out = self.unit_a

    volume_fee: int = 0
    if self.volume_fee is not None:
        if isinstance(self.volume_fee, int):
            volume_fee = self.volume_fee
        elif asset.unit() == self.unit_a:
            volume_fee = self.volume_fee[0]
        else:
            volume_fee = self.volume_fee[1]

    # Calculate the amount out
    fee_modifier = 10000 - volume_fee
    numerator: int = asset.quantity() * fee_modifier * reserve_out
    denominator: int = asset.quantity() * fee_modifier + reserve_in * 10000
    amount_out = Assets(**{unit_out: numerator // denominator})
    if not precise:
        amount_out.root[unit_out] = numerator // denominator

    if amount_out.quantity() == 0:
        return amount_out, 0

    # Calculate the price impact
    price_numerator: int = (
        reserve_out * asset.quantity() * denominator * fee_modifier
        - numerator * reserve_in * 10000
    )
    price_denominator: int = reserve_out * asset.quantity() * denominator * 10000
    price_impact: float = price_numerator / price_denominator

    return amount_out, price_impact

AbstractStableSwapPoolState

Bases: AbstractPoolState

Represents the state of a stable swap automated market maker (AMM) pool.

Source code in src/charli3_dendrite/dexs/amm/amm_types.py
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
class AbstractStableSwapPoolState(AbstractPoolState):
    """Represents the state of a stable swap automated market maker (AMM) pool."""

    asset_mulitipliers: ClassVar[list[int]] = [1, 1]

    @property
    def reserve_a(self) -> int:
        """Reserve amount of asset A."""
        return self.assets.quantity(0) * self.asset_mulitipliers[0]

    @property
    def reserve_b(self) -> int:
        """Reserve amount of asset B."""
        return self.assets.quantity(1) * self.asset_mulitipliers[1]

    @property
    def amp(self) -> int:
        """Amplification coefficient used in the stable swap algorithm."""
        return 75

    def _get_ann(self) -> int:
        """The modified amp value.

        This is the derived amp value (ann) from the original stableswap paper. This is
        implemented here as the default, but a common variant of this does not use the
        exponent. The alternative version is provided in the
        AbstractCommonStableSwapPoolState class. WingRiders uses this version.
        """
        return self.amp * N_COINS**N_COINS

    def _get_d(self) -> float:
        """Regression to learn the stability constant."""
        # TODO: Expand this to operate on pools with more than one stable
        ann = self._get_ann()
        s = self.reserve_a + self.reserve_b
        if s == 0:
            return 0

        # Iterate until the change in value is <1 unit.
        d = s
        for _ in range(256):
            d_p = d**3 / (N_COINS**N_COINS * self.reserve_a * self.reserve_b)
            d_prev = d
            d = d * (ann * s + d_p * N_COINS) / ((ann - 1) * d + (N_COINS + 1) * d_p)

            if abs(d - d_prev) < 1:
                break

        return d

    def _get_y(
        self,
        in_assets: Assets,
        out_unit: str,
        precise: bool = True,
        get_input: bool = False,
    ) -> Assets:
        """Calculate the output amount using a regression."""
        ann = self._get_ann()
        d = self._get_d()

        subtract = -1 if get_input else 1

        # Make sure only one input supplied
        if len(in_assets) > 1:
            error_msg = "Only one input asset allowed."
            raise ValueError(error_msg)
        if in_assets.unit() not in [self.unit_a, self.unit_b]:
            error_msg = "Invalid input token."
            raise ValueError(error_msg)
        if out_unit not in [self.unit_a, self.unit_b]:
            error_msg = "Invalid output token."
            raise ValueError(error_msg)

        in_quantity = in_assets.quantity()
        if in_assets.unit() == self.unit_a:
            in_reserve = (
                self.reserve_a + in_quantity * self.asset_mulitipliers[0] * subtract
            )
            out_multiplier = self.asset_mulitipliers[1]
        else:
            in_reserve = (
                self.reserve_b + in_quantity * self.asset_mulitipliers[1] * subtract
            )
            out_multiplier = self.asset_mulitipliers[0]

        s = in_reserve
        c = d**3 / (N_COINS**2 * ann * in_reserve)
        b = s + d / ann
        out_prev = 0
        out = d

        for _ in range(256):
            out_prev = int(out)
            out = (out**2 + c) / (2 * out + b - d)

            if abs(out - out_prev) < 1:
                break

        out /= out_multiplier
        out_assets = Assets(**{out_unit: int(out)})
        if not precise:
            out_assets.root[out_unit] = int(out)

        return out_assets

    def get_amount_out(
        self,
        asset: Assets,
        precise: bool = True,
        fee_on_input: bool = True,
    ) -> tuple[Assets, float]:
        """Calculate the amount of assets received when swapping a given input amount.

        This function computes the output amount for a swap operation in the
        stable swap pool, taking into account the volume fee and precision settings.

        Args:
            asset (Assets): The input asset amount for the swap.
            precise (bool): If True, returns precise integer output. Default True.
            fee_on_input (bool): If True, applies the fee to the input amount.
                                        If False, applies the fee to the output amount.
                                        Defaults to True.

        Returns:
            tuple[Assets, float]: A tuple containing:
                - The output asset amount after the swap.
                - A float value (always 0 in this implementation).

        Raises:
            ValueError: If the input asset is invalid or if multiple input
              assets are provided.
        """
        volume_fee: int = 0
        if self.volume_fee is not None:
            if isinstance(self.volume_fee, (int, float)):
                volume_fee = self.volume_fee
            elif asset.unit() == self.unit_a:
                volume_fee = self.volume_fee[0]
            else:
                volume_fee = self.volume_fee[1]

        if fee_on_input:
            in_asset = Assets(
                **{
                    asset.unit(): int(
                        asset.quantity() * (10000 - volume_fee) / 10000,
                    ),
                },
            )
        else:
            in_asset = asset
        out_unit = self.unit_a if asset.unit() == self.unit_b else self.unit_b
        out_asset = self._get_y(in_asset, out_unit, precise=precise)
        out_reserve = (
            self.reserve_b / self.asset_mulitipliers[1]
            if out_unit == self.unit_b
            else self.reserve_a / self.asset_mulitipliers[0]
        )

        out_asset.root[out_asset.unit()] = int(out_reserve - out_asset.quantity())
        if not fee_on_input:
            out_asset.root[out_asset.unit()] = int(
                out_asset.quantity() * (10000 - volume_fee) / 10000,
            )
        if precise:
            out_asset.root[out_asset.unit()] = int(out_asset.quantity())

        return out_asset, 0

    def get_amount_in(
        self,
        asset: Assets,
        precise: bool = True,
        fee_on_input: bool = True,
    ) -> tuple[Assets, float]:
        """Calculate the amount of assets required as input to receive a given output.

        This function computes the input amount needed for a swap operation in the
        stable swap pool to achieve a desired output, taking into account the
        volume fee and precision settings.

        Args:
            asset (Assets): The desired output asset amount for the swap.
            precise (bool): If True, returns precise integer input. Defaults to True.
            fee_on_input (bool): If True, applies the fee to the calculated input.
                                        If False, applies the fee to the given output.
                                        Defaults to True.

        Returns:
            tuple[Assets, float]: A tuple containing:
                - The input asset amount required for the swap.
                - A float value (always 0 in this implementation).

        Raises:
            ValueError: If the output asset is invalid or if multiple output
            assets are provided.
        """
        volume_fee: int = 0
        if self.volume_fee is not None:
            if isinstance(self.volume_fee, (int, float)):
                volume_fee = self.volume_fee
            elif asset.unit() == self.unit_a:
                volume_fee = self.volume_fee[0]
            else:
                volume_fee = self.volume_fee[1]

        if not fee_on_input:
            out_asset = Assets(
                **{
                    asset.unit(): int(
                        asset.quantity() * 10000 / (10000 - volume_fee),
                    ),
                },
            )
        else:
            out_asset = asset
        in_unit = self.unit_a if asset.unit() == self.unit_b else self.unit_b
        in_asset = self._get_y(out_asset, in_unit, precise=precise, get_input=True)
        in_reserve = (
            (self.reserve_b / self.asset_mulitipliers[1])
            if in_unit == self.unit_b
            else (self.reserve_a / self.asset_mulitipliers[0])
        )
        in_asset.root[in_asset.unit()] = int(in_asset.quantity() - in_reserve)
        if fee_on_input:
            in_asset.root[in_asset.unit()] = int(
                in_asset.quantity() * 10000 / (10000 - volume_fee),
            )
        if precise:
            in_asset.root[in_asset.unit()] = int(in_asset.quantity())
        return in_asset, 0

amp: int property

Amplification coefficient used in the stable swap algorithm.

reserve_a: int property

Reserve amount of asset A.

reserve_b: int property

Reserve amount of asset B.

get_amount_in(asset: Assets, precise: bool = True, fee_on_input: bool = True) -> tuple[Assets, float]

Calculate the amount of assets required as input to receive a given output.

This function computes the input amount needed for a swap operation in the stable swap pool to achieve a desired output, taking into account the volume fee and precision settings.

Parameters:

Name Type Description Default
asset Assets

The desired output asset amount for the swap.

required
precise bool

If True, returns precise integer input. Defaults to True.

True
fee_on_input bool

If True, applies the fee to the calculated input. If False, applies the fee to the given output. Defaults to True.

True

Returns:

Type Description
tuple[Assets, float]

tuple[Assets, float]: A tuple containing: - The input asset amount required for the swap. - A float value (always 0 in this implementation).

Raises:

Type Description
ValueError

If the output asset is invalid or if multiple output

Source code in src/charli3_dendrite/dexs/amm/amm_types.py
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
def get_amount_in(
    self,
    asset: Assets,
    precise: bool = True,
    fee_on_input: bool = True,
) -> tuple[Assets, float]:
    """Calculate the amount of assets required as input to receive a given output.

    This function computes the input amount needed for a swap operation in the
    stable swap pool to achieve a desired output, taking into account the
    volume fee and precision settings.

    Args:
        asset (Assets): The desired output asset amount for the swap.
        precise (bool): If True, returns precise integer input. Defaults to True.
        fee_on_input (bool): If True, applies the fee to the calculated input.
                                    If False, applies the fee to the given output.
                                    Defaults to True.

    Returns:
        tuple[Assets, float]: A tuple containing:
            - The input asset amount required for the swap.
            - A float value (always 0 in this implementation).

    Raises:
        ValueError: If the output asset is invalid or if multiple output
        assets are provided.
    """
    volume_fee: int = 0
    if self.volume_fee is not None:
        if isinstance(self.volume_fee, (int, float)):
            volume_fee = self.volume_fee
        elif asset.unit() == self.unit_a:
            volume_fee = self.volume_fee[0]
        else:
            volume_fee = self.volume_fee[1]

    if not fee_on_input:
        out_asset = Assets(
            **{
                asset.unit(): int(
                    asset.quantity() * 10000 / (10000 - volume_fee),
                ),
            },
        )
    else:
        out_asset = asset
    in_unit = self.unit_a if asset.unit() == self.unit_b else self.unit_b
    in_asset = self._get_y(out_asset, in_unit, precise=precise, get_input=True)
    in_reserve = (
        (self.reserve_b / self.asset_mulitipliers[1])
        if in_unit == self.unit_b
        else (self.reserve_a / self.asset_mulitipliers[0])
    )
    in_asset.root[in_asset.unit()] = int(in_asset.quantity() - in_reserve)
    if fee_on_input:
        in_asset.root[in_asset.unit()] = int(
            in_asset.quantity() * 10000 / (10000 - volume_fee),
        )
    if precise:
        in_asset.root[in_asset.unit()] = int(in_asset.quantity())
    return in_asset, 0

get_amount_out(asset: Assets, precise: bool = True, fee_on_input: bool = True) -> tuple[Assets, float]

Calculate the amount of assets received when swapping a given input amount.

This function computes the output amount for a swap operation in the stable swap pool, taking into account the volume fee and precision settings.

Parameters:

Name Type Description Default
asset Assets

The input asset amount for the swap.

required
precise bool

If True, returns precise integer output. Default True.

True
fee_on_input bool

If True, applies the fee to the input amount. If False, applies the fee to the output amount. Defaults to True.

True

Returns:

Type Description
tuple[Assets, float]

tuple[Assets, float]: A tuple containing: - The output asset amount after the swap. - A float value (always 0 in this implementation).

Raises:

Type Description
ValueError

If the input asset is invalid or if multiple input assets are provided.

Source code in src/charli3_dendrite/dexs/amm/amm_types.py
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
def get_amount_out(
    self,
    asset: Assets,
    precise: bool = True,
    fee_on_input: bool = True,
) -> tuple[Assets, float]:
    """Calculate the amount of assets received when swapping a given input amount.

    This function computes the output amount for a swap operation in the
    stable swap pool, taking into account the volume fee and precision settings.

    Args:
        asset (Assets): The input asset amount for the swap.
        precise (bool): If True, returns precise integer output. Default True.
        fee_on_input (bool): If True, applies the fee to the input amount.
                                    If False, applies the fee to the output amount.
                                    Defaults to True.

    Returns:
        tuple[Assets, float]: A tuple containing:
            - The output asset amount after the swap.
            - A float value (always 0 in this implementation).

    Raises:
        ValueError: If the input asset is invalid or if multiple input
          assets are provided.
    """
    volume_fee: int = 0
    if self.volume_fee is not None:
        if isinstance(self.volume_fee, (int, float)):
            volume_fee = self.volume_fee
        elif asset.unit() == self.unit_a:
            volume_fee = self.volume_fee[0]
        else:
            volume_fee = self.volume_fee[1]

    if fee_on_input:
        in_asset = Assets(
            **{
                asset.unit(): int(
                    asset.quantity() * (10000 - volume_fee) / 10000,
                ),
            },
        )
    else:
        in_asset = asset
    out_unit = self.unit_a if asset.unit() == self.unit_b else self.unit_b
    out_asset = self._get_y(in_asset, out_unit, precise=precise)
    out_reserve = (
        self.reserve_b / self.asset_mulitipliers[1]
        if out_unit == self.unit_b
        else self.reserve_a / self.asset_mulitipliers[0]
    )

    out_asset.root[out_asset.unit()] = int(out_reserve - out_asset.quantity())
    if not fee_on_input:
        out_asset.root[out_asset.unit()] = int(
            out_asset.quantity() * (10000 - volume_fee) / 10000,
        )
    if precise:
        out_asset.root[out_asset.unit()] = int(out_asset.quantity())

    return out_asset, 0