charli3_dendrite 1.2.5.dev5__tar.gz → 1.2.5.dev7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/PKG-INFO +1 -1
  2. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/pyproject.toml +2 -2
  3. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/amm/splash.py +204 -205
  4. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/LICENSE +0 -0
  5. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/README.md +0 -0
  6. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/__init__.py +0 -0
  7. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/backend/__init__.py +0 -0
  8. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/backend/backend_base.py +0 -0
  9. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/backend/blockfrost/__init__.py +0 -0
  10. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/backend/blockfrost/models.py +0 -0
  11. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/backend/dbsync/__init__.py +0 -0
  12. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/backend/dbsync/models.py +0 -0
  13. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/backend/ogmios_kupo/__init__.py +0 -0
  14. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/backend/ogmios_kupo/models.py +0 -0
  15. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/backend/utils.py +0 -0
  16. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dataclasses/__init__.py +0 -0
  17. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dataclasses/datums.py +0 -0
  18. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dataclasses/models.py +0 -0
  19. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/__init__.py +0 -0
  20. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/amm/__init__.py +0 -0
  21. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/amm/amm_base.py +0 -0
  22. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/amm/amm_types.py +0 -0
  23. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/amm/cswap.py +0 -0
  24. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/amm/minswap.py +0 -0
  25. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/amm/muesli.py +0 -0
  26. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/amm/spectrum.py +0 -0
  27. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/amm/sundae.py +0 -0
  28. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/amm/vyfi.py +0 -0
  29. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/amm/wingriders.py +0 -0
  30. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/core/__init__.py +0 -0
  31. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/core/base.py +0 -0
  32. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/core/errors.py +0 -0
  33. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/ob/__init__.py +0 -0
  34. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/ob/axo.py +0 -0
  35. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/ob/djed.py +0 -0
  36. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/ob/geniusyield.py +0 -0
  37. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/dexs/ob/ob_base.py +0 -0
  38. {charli3_dendrite-1.2.5.dev5 → charli3_dendrite-1.2.5.dev7}/src/charli3_dendrite/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: charli3_dendrite
3
- Version: 1.2.5.dev5
3
+ Version: 1.2.5.dev7
4
4
  Summary:
5
5
  Author: Elder Millenial
6
6
  Author-email: eldermillenial@protonmail.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "charli3_dendrite"
3
- version = "1.2.5-dev5"
3
+ version = "1.2.5-dev7"
4
4
  description = ""
5
5
  authors = ["Elder Millenial <eldermillenial@protonmail.com>"]
6
6
  readme = "README.md"
@@ -49,7 +49,7 @@ module = ["pycardano", "pycardano.*", "dotenv", "nox"]
49
49
  ignore_missing_imports = true
50
50
 
51
51
  [tool.bumpversion]
52
- current_version = "1.2.5-dev5"
52
+ current_version = "1.2.5-dev7"
53
53
  parse = """(?x)
54
54
  (?P<major>\\d+)\\.
55
55
  (?P<minor>\\d+)\\.
@@ -1,8 +1,7 @@
1
1
  """Minswap DEX Module."""
2
2
 
3
- from dataclasses import dataclass, field
4
- from random import choice
5
- from string import hexdigits
3
+ from dataclasses import dataclass
4
+ from dataclasses import field
6
5
  from typing import Any
7
6
  from typing import List
8
7
  from typing import Union
@@ -36,14 +35,20 @@ from charli3_dendrite.dexs.core.errors import NotAPoolError
36
35
  from charli3_dendrite.utility import Assets
37
36
  from charli3_dendrite.utility import asset_to_value
38
37
 
38
+ LIQUIDITY_THRESHOLD = 100000000
39
+
39
40
 
40
41
  @dataclass
41
42
  class BoolFalse(PlutusData):
43
+ """Plutus data representation of boolean false."""
44
+
42
45
  CONSTR_ID = 0
43
46
 
44
47
 
45
48
  @dataclass
46
49
  class BoolTrue(PlutusData):
50
+ """Plutus data representation of boolean true."""
51
+
47
52
  CONSTR_ID = 1
48
53
 
49
54
 
@@ -75,8 +80,8 @@ class SplashOrderDatum(OrderDatum):
75
80
  cancel_pkh: bytes
76
81
  permitted_executors: List[bytes] = field(
77
82
  default_factory=lambda: [
78
- b"\\\xb2\xc9h\xe5\xd1\xc7\x19zl\xe7aYg1\n7UE\xd9\xbce\x06:\x96C5\xb2"
79
- ]
83
+ b"\\\xb2\xc9h\xe5\xd1\xc7\x19zl\xe7aYg1\n7UE\xd9\xbce\x06:\x96C5\xb2",
84
+ ],
80
85
  )
81
86
 
82
87
  def address_source(self) -> Address:
@@ -94,42 +99,6 @@ class SplashOrderDatum(OrderDatum):
94
99
  """This method should return the type of the order."""
95
100
  return OrderType.swap
96
101
 
97
- @classmethod
98
- def create_datum(
99
- cls,
100
- address_source: Address,
101
- in_assets: Assets,
102
- out_assets: Assets,
103
- batcher_fee: Assets,
104
- deposit: Assets,
105
- address_target: Address | None = None,
106
- datum_target: PlutusData | None = None,
107
- ) -> "CSwapOrderDatum":
108
- """Create a CSwap order datum."""
109
- # Validate ADA-only restriction
110
- merged_assets = in_assets + out_assets
111
-
112
- full_address = PlutusFullAddress.from_address(address_source)
113
-
114
- beacon = bytes.fromhex("".join(choice(hexdigits) for _ in range(56)))
115
-
116
- return cls(
117
- tag=b"\x00",
118
- beacon=beacon,
119
- in_asset=AssetClass.from_assets(in_assets),
120
- tradable_input=in_assets.quantity(),
121
- cost_per_ex_step=batcher_fee.quantity(),
122
- min_marginal_output=out_assets.quantity(),
123
- output=AssetClass.from_assets(out_assets),
124
- base_price=Rationale(
125
- numerator=out_assets.quantity(),
126
- denominator=in_assets.quantity(),
127
- ),
128
- fee=0,
129
- redeemer_address=full_address,
130
- cancel_pkh=address_source.payment_part.payload,
131
- )
132
-
133
102
 
134
103
  @dataclass
135
104
  class SplashSSPPoolDatum(PoolDatum):
@@ -216,7 +185,7 @@ class SplashCPPRoyaltyPoolDatum(PoolDatum):
216
185
  treasury_y: int
217
186
  royalty_x: int
218
187
  royalty_y: int
219
- admin_address: List[PlutusFullAddress]
188
+ admin_address: RawDatum
220
189
  treasury_address: bytes
221
190
  royalty_pub_key: bytes
222
191
  nonce: int
@@ -228,6 +197,8 @@ class SplashCPPRoyaltyPoolDatum(PoolDatum):
228
197
 
229
198
  @dataclass
230
199
  class SwapAction(PlutusData):
200
+ """Plutus data for swap action."""
201
+
231
202
  CONSTR_ID = 0
232
203
 
233
204
  context_values_list: List[int]
@@ -235,25 +206,29 @@ class SwapAction(PlutusData):
235
206
 
236
207
  @dataclass
237
208
  class PDAOAction(PlutusData):
209
+ """Plutus data for PDAO action."""
210
+
238
211
  CONSTR_ID = 1
239
212
 
240
213
 
241
214
  @dataclass
242
215
  class SSPoolRedeemer(PlutusData):
216
+ """Plutus data for stable swap pool."""
217
+
243
218
  CONSTR_ID = 0
244
219
 
245
220
  pool_in_idx: int
246
221
  pool_out_idx: int
247
222
  action: Union[SwapAction, PDAOAction]
248
223
 
249
- def set_idx(self, tx_builder: TransactionBuilder):
224
+ def set_idx(self, tx_builder: TransactionBuilder) -> None:
250
225
  """Set the pool in and out indexes.
251
226
 
252
227
  It is necessary to make sure that redeemer indices are already set before
253
228
  calling this function.
254
229
  """
255
230
  contract_hash = Address.from_primitive(
256
- "addr1w9wnm7vle7al9q4aw63aw63wxz7aytnpc4h3gcjy0yufxwc3mr3e5"
231
+ "addr1w9wnm7vle7al9q4aw63aw63wxz7aytnpc4h3gcjy0yufxwc3mr3e5",
257
232
  ).payment_part.payload
258
233
 
259
234
  # Set the input index
@@ -265,18 +240,21 @@ class SSPoolRedeemer(PlutusData):
265
240
  for i, txo in enumerate(tx_builder.outputs):
266
241
  if txo.address.payment_part.payload == contract_hash:
267
242
  pool_index = i
268
- assert pool_index is not None
243
+ if pool_index is None:
244
+ raise RuntimeError("Pool output not found in transaction outputs")
269
245
  self.pool_out_idx = pool_index
270
246
 
271
247
 
272
248
  @dataclass
273
249
  class CPPoolRedeemer(PlutusData):
250
+ """Plutus data for constant product pool."""
251
+
274
252
  CONSTR_ID = 0
275
253
 
276
254
  action: int
277
255
  self_index: int
278
256
 
279
- def set_idx(self, tx_builder: TransactionBuilder):
257
+ def set_idx(self, tx_builder: TransactionBuilder) -> None:
280
258
  """Set the pool in and out indexes.
281
259
 
282
260
  It is necessary to make sure that redeemer indices are already set before
@@ -289,24 +267,31 @@ class CPPoolRedeemer(PlutusData):
289
267
 
290
268
 
291
269
  class SplashBaseState(AbstractPairState):
270
+ """Base state class for Splash DEX pools."""
271
+
292
272
  @classmethod
293
273
  def dex(cls) -> str:
274
+ """Return the name of the DEX."""
294
275
  return "Splash"
295
276
 
296
277
  @classmethod
297
- def order_selector(self) -> list[str]:
278
+ def order_selector(cls) -> list[str]:
279
+ """Return the order selector addresses."""
298
280
  return ["addr1w9ryamhgnuz6lau86sqytte2gz5rlktv2yce05e0h3207qssa8euj"]
299
281
 
300
282
  @property
301
283
  def stake_address(self) -> Address | None:
284
+ """Return the stake address for orders."""
302
285
  return Address.decode(self.order_selector()[0])
303
286
 
304
287
  @property
305
288
  def swap_forward(self) -> bool:
289
+ """Return whether this DEX supports swap forwarding."""
306
290
  return False
307
291
 
308
292
  @classmethod
309
- def order_datum_class(cls):
293
+ def order_datum_class(cls) -> type[SplashOrderDatum]:
294
+ """Return the order datum class for this DEX."""
310
295
  return SplashOrderDatum
311
296
 
312
297
  @classmethod
@@ -316,6 +301,7 @@ class SplashBaseState(AbstractPairState):
316
301
 
317
302
  @classmethod
318
303
  def script_class(cls) -> type[PlutusV2Script]:
304
+ """Return the script class for this DEX."""
319
305
  return PlutusV2Script
320
306
 
321
307
  @classmethod
@@ -365,15 +351,16 @@ class SplashBaseState(AbstractPairState):
365
351
  return pool_nft
366
352
 
367
353
  @classmethod
368
- def skip_init(cls, values) -> bool:
354
+ def skip_init(cls, values: dict[str, Any]) -> bool:
355
+ """Check if initialization should be skipped for already parsed values."""
369
356
  if "pool_nft" in values and "lp_tokens" in values:
370
357
  order_class = cls.pool_datum_class()
371
358
  try:
372
359
  datum: SplashSSPPoolDatum | SplashCPPPoolDatum = order_class.from_cbor(
373
360
  values["datum_cbor"],
374
361
  )
375
- except DeserializeException:
376
- raise NotAPoolError("Invalid datum")
362
+ except DeserializeException as err:
363
+ raise NotAPoolError("Invalid datum") from err
377
364
 
378
365
  if datum.pool_nft.assets.unit() not in values["pool_nft"]:
379
366
  raise NotAPoolError("Invalid pool NFT")
@@ -382,8 +369,8 @@ class SplashBaseState(AbstractPairState):
382
369
 
383
370
  values["assets"] = Assets.model_validate(values["assets"])
384
371
  return True
385
- else:
386
- return False
372
+
373
+ return False
387
374
 
388
375
  @property
389
376
  def pool_id(self) -> str:
@@ -416,8 +403,7 @@ class SplashBaseState(AbstractPairState):
416
403
  if lp_token not in assets:
417
404
  raise InvalidLPError("A pool must have pool lp tokens.")
418
405
 
419
- else:
420
- lp_tokens = Assets(**{lp_token: assets.root.pop(lp_token)})
406
+ lp_tokens = Assets(**{lp_token: assets.root.pop(lp_token)})
421
407
 
422
408
  values["lp_tokens"] = lp_tokens
423
409
 
@@ -434,6 +420,7 @@ class SplashSSPState(SplashBaseState, AbstractCommonStableSwapPoolState):
434
420
 
435
421
  @classmethod
436
422
  def pool_selector(cls) -> PoolSelector:
423
+ """Return the pool selector for this DEX."""
437
424
  return PoolSelector(
438
425
  addresses=["addr1w9wnm7vle7al9q4aw63aw63wxz7aytnpc4h3gcjy0yufxwc3mr3e5"],
439
426
  )
@@ -449,11 +436,13 @@ class SplashSSPState(SplashBaseState, AbstractCommonStableSwapPoolState):
449
436
  return self.pool_datum.an2n // 4
450
437
 
451
438
  @classmethod
452
- def pool_datum_class(self) -> type[SplashSSPPoolDatum]:
439
+ def pool_datum_class(cls) -> type[SplashSSPPoolDatum]:
440
+ """Return the pool datum class for stable swap pools."""
453
441
  return SplashSSPPoolDatum
454
442
 
455
443
  @classmethod
456
- def post_init(cls, values):
444
+ def post_init(cls, values: dict[str, Any]) -> None:
445
+ """Post-initialization processing for stable swap pools."""
457
446
  super().post_init(values)
458
447
 
459
448
  datum: SplashSSPPoolDatum = SplashSSPPoolDatum.from_cbor(values["datum_cbor"])
@@ -472,7 +461,7 @@ class SplashSSPState(SplashBaseState, AbstractCommonStableSwapPoolState):
472
461
  # Verify pool is active
473
462
  # TODO: should be updated to match the validator:
474
463
  # https://github.com/splashprotocol/splash-core/blob/main/validators/stable_pool/pool.ak
475
- values["inactive"] = assets.quantity() < 100000000
464
+ values["inactive"] = assets.quantity() < LIQUIDITY_THRESHOLD
476
465
 
477
466
  def get_amount_out(
478
467
  self,
@@ -480,6 +469,7 @@ class SplashSSPState(SplashBaseState, AbstractCommonStableSwapPoolState):
480
469
  precise: bool = False,
481
470
  fee_on_input: bool = False,
482
471
  ) -> tuple[Assets, float]:
472
+ """Calculate output amount for a given input asset amount."""
483
473
  return super().get_amount_out(asset=asset, precise=precise, fee_on_input=False)
484
474
 
485
475
  def get_amount_in(
@@ -488,6 +478,7 @@ class SplashSSPState(SplashBaseState, AbstractCommonStableSwapPoolState):
488
478
  precise: bool = False,
489
479
  fee_on_input: bool = False,
490
480
  ) -> tuple[Assets, float]:
481
+ """Calculate input amount required for a given output asset amount."""
491
482
  return super().get_amount_in(asset=asset, precise=precise, fee_on_input=False)
492
483
 
493
484
  def swap_utxo(
@@ -500,10 +491,15 @@ class SplashSSPState(SplashBaseState, AbstractCommonStableSwapPoolState):
500
491
  address_target: Address | None = None,
501
492
  datum_target: PlutusData | None = None,
502
493
  ) -> tuple[TransactionOutput | None, PlutusData]:
503
- assert self.tx_hash is not None
504
- assert self.pool_nft is not None
505
- assert self.lp_tokens is not None
506
- assert tx_builder is not None
494
+ """Create a swap UTXO for the stable swap pool."""
495
+ if self.tx_hash is None:
496
+ raise ValueError("Transaction hash is required for swap operation")
497
+ if self.pool_nft is None:
498
+ raise ValueError("Pool NFT is required for swap operation")
499
+ if self.lp_tokens is None:
500
+ raise ValueError("LP tokens are required for swap operation")
501
+ if tx_builder is None:
502
+ raise ValueError("Transaction builder is required for swap operation")
507
503
 
508
504
  order_info = get_backend().get_pool_in_tx(
509
505
  self.tx_hash,
@@ -564,13 +560,14 @@ class SplashSSPState(SplashBaseState, AbstractCommonStableSwapPoolState):
564
560
  class SplashCPPState(SplashBaseState, AbstractConstantProductPoolState):
565
561
  """Splash StableSwap Pool State."""
566
562
 
567
- fee: int = 30
563
+ fee: Union[int, list[int]] = 30
568
564
  fee_basis: int = 100000
569
565
  _batcher = Assets(lovelace=0)
570
566
  _deposit = Assets(lovelace=0)
571
567
 
572
568
  @classmethod
573
569
  def pool_selector(cls) -> PoolSelector:
570
+ """Return the pool selector for constant product pools."""
574
571
  return PoolSelector(
575
572
  addresses=[
576
573
  "addr1w8cq97k066w4rd37wprvd4qrfxctzlyd6a67us2uv6hnenqrkvy2j",
@@ -579,15 +576,17 @@ class SplashCPPState(SplashBaseState, AbstractConstantProductPoolState):
579
576
  )
580
577
 
581
578
  @classmethod
582
- def pool_datum_class(self) -> type[SplashCPPPoolDatum]:
579
+ def pool_datum_class(cls) -> type[SplashCPPPoolDatum]:
580
+ """Return the pool datum class for constant product pools."""
583
581
  return SplashCPPPoolDatum
584
582
 
585
583
  @classmethod
586
- def post_init(cls, values):
584
+ def post_init(cls, values: dict[str, Any]) -> None:
585
+ """Post-initialization processing for constant product pools."""
587
586
  super().post_init(values)
588
587
 
589
588
  datum: SplashCPPPoolDatum = cls.pool_datum_class().from_cbor(
590
- values["datum_cbor"]
589
+ values["datum_cbor"],
591
590
  )
592
591
 
593
592
  values["fee"] = 100000 - datum.pool_fee + datum.treasury_fee
@@ -599,7 +598,7 @@ class SplashCPPState(SplashBaseState, AbstractConstantProductPoolState):
599
598
  # Verify pool is active
600
599
  # TODO: should be updated to match the validator:
601
600
  # https://github.com/splashprotocol/splash-core/blob/9fd951054ac7143de6acf491f36d1073e729ba90/plutarch-validators/WhalePoolsDex/PContracts/PPool.hs#L367
602
- values["inactive"] = assets.quantity() < 100000000
601
+ values["inactive"] = assets.quantity() < LIQUIDITY_THRESHOLD
603
602
 
604
603
  def _treasury_x(self) -> int:
605
604
  return self.pool_datum.treasury_x
@@ -612,10 +611,11 @@ class SplashCPPState(SplashBaseState, AbstractConstantProductPoolState):
612
611
  asset: Assets,
613
612
  precise: bool = True,
614
613
  ) -> tuple[Assets, float]:
614
+ """Calculate output amount for constant product pool with treasury fees."""
615
615
  amount_out, slippage = super().get_amount_out(asset=asset, precise=precise)
616
616
 
617
- if isinstance(self.fee, (int, float)) or len(self.fee) == 1:
618
- fee = self.fee
617
+ if isinstance(self.fee, (int, float)):
618
+ fee: int = self.fee
619
619
  else:
620
620
  fee = self.fee[0] if asset.unit() == self.assets.unit_a else self.fee[1]
621
621
 
@@ -625,26 +625,34 @@ class SplashCPPState(SplashBaseState, AbstractConstantProductPoolState):
625
625
  rx = self.assets[asset.unit()]
626
626
 
627
627
  if asset.unit() == self.assets.unit():
628
- ry = self.assets.quantity(1) + self._treasury_y()
629
- rx += self._treasury_x()
628
+ ry = self.assets.quantity(1)
630
629
  else:
631
- ry = self.assets.quantity(0) + self._treasury_x()
632
- rx += self._treasury_y()
630
+ ry = self.assets.quantity(0)
633
631
 
634
632
  # Check to make sure the output passes invariant, and decrease until it does
635
633
  dy = -amount_out.quantity()
636
634
  dyf = dy * fee
637
- try:
638
- assert -dy * (rx * 100000 + dxf) <= ry * dxf
639
- assert -dx * (ry * 100000 + dyf) <= rx * dyf
640
- except AssertionError:
635
+
636
+ # First invariant check - if it fails, adjust the output amount
637
+ if not (
638
+ -dy * (rx * 100000 + dxf) <= ry * dxf
639
+ and -dx * (ry * 100000 + dyf) <= rx * dyf
640
+ ):
641
641
  amount_out.root[amount_out.unit()] = int(ry * dxf / (rx * 100000 + dxf))
642
642
 
643
643
  dy = -amount_out.quantity()
644
644
  dyf = dy * fee
645
645
 
646
- assert -dy * (rx * 100000 + dxf) <= ry * dxf
647
- assert -dx * (ry * 100000 + dyf) <= rx * dyf
646
+ # Final invariant check - if this fails, it's a serious error
647
+ if not (
648
+ -dy * (rx * 100000 + dxf) <= ry * dxf
649
+ and -dx * (ry * 100000 + dyf) <= rx * dyf
650
+ ):
651
+ raise ValueError(
652
+ "AMM violation: mathematical constraints cannot be satisfied. "
653
+ f"dy={dy}, dx={dx}, rx={rx}, ry={ry}, "
654
+ f"dxf={dxf}, dyf={dyf}",
655
+ )
648
656
 
649
657
  return amount_out, slippage
650
658
 
@@ -658,10 +666,15 @@ class SplashCPPState(SplashBaseState, AbstractConstantProductPoolState):
658
666
  address_target: Address | None = None,
659
667
  datum_target: PlutusData | None = None,
660
668
  ) -> tuple[TransactionOutput | None, PlutusData]:
661
- assert self.tx_hash is not None
662
- assert self.pool_nft is not None
663
- assert self.lp_tokens is not None
664
- assert tx_builder is not None
669
+ """Create a swap UTXO for the constant product pool."""
670
+ if self.tx_hash is None:
671
+ raise ValueError("Transaction hash is required for swap operation")
672
+ if self.pool_nft is None:
673
+ raise ValueError("Pool NFT is required for swap operation")
674
+ if self.lp_tokens is None:
675
+ raise ValueError("LP tokens are required for swap operation")
676
+ if tx_builder is None:
677
+ raise ValueError("Transaction builder is required for swap operation")
665
678
 
666
679
  order_info = get_backend().get_pool_in_tx(
667
680
  self.tx_hash,
@@ -742,12 +755,12 @@ class SplashCPPState(SplashBaseState, AbstractConstantProductPoolState):
742
755
  class SplashCPPBidirState(SplashCPPState):
743
756
  """Splash StableSwap Pool State."""
744
757
 
745
- fee: int = [30, 30]
746
758
  _batcher = Assets(lovelace=0)
747
759
  _deposit = Assets(lovelace=0)
748
760
 
749
761
  @classmethod
750
762
  def pool_selector(cls) -> PoolSelector:
763
+ """Return the pool selector for bidirectional constant product pools."""
751
764
  return PoolSelector(
752
765
  addresses=[
753
766
  "addr1w95q755yrsr0xt8vmn007tpqee4hps49yjdef5dzknhl99qntsmh0",
@@ -755,11 +768,13 @@ class SplashCPPBidirState(SplashCPPState):
755
768
  )
756
769
 
757
770
  @classmethod
758
- def pool_datum_class(self) -> type[SplashCPPBidirPoolDatum]:
771
+ def pool_datum_class(cls) -> type[SplashCPPBidirPoolDatum]:
772
+ """Return the pool datum class for bidirectional pools."""
759
773
  return SplashCPPBidirPoolDatum
760
774
 
761
775
  @classmethod
762
- def post_init(cls, values):
776
+ def post_init(cls, values: dict[str, Any]) -> None:
777
+ """Post-initialization processing for bidirectional constant product pools."""
763
778
  super().post_init(values)
764
779
 
765
780
  datum: SplashCPPBidirPoolDatum = SplashCPPBidirPoolDatum.from_cbor(
@@ -776,15 +791,14 @@ class SplashCPPBidirState(SplashCPPState):
776
791
  assets.root[assets.unit(1)] -= datum.treasury_y
777
792
 
778
793
  # Verify pool is active
779
- values["inactive"] = assets.quantity() < 100000000
794
+ values["inactive"] = assets.quantity() < LIQUIDITY_THRESHOLD
780
795
 
781
796
 
782
797
  class SplashCPPRoyaltyState(SplashCPPState):
783
798
  """Splash StableSwap Pool State."""
784
799
 
785
- fee: int = 30
786
- _batcher = Assets(lovelace=1000000)
787
- _deposit = Assets(lovelace=1500000)
800
+ _batcher = Assets(lovelace=0)
801
+ _deposit = Assets(lovelace=0)
788
802
 
789
803
  def _treasury_x(self) -> int:
790
804
  return self.pool_datum.treasury_x + self.pool_datum.royalty_x
@@ -794,6 +808,7 @@ class SplashCPPRoyaltyState(SplashCPPState):
794
808
 
795
809
  @classmethod
796
810
  def pool_selector(cls) -> PoolSelector:
811
+ """Return the pool selector for royalty constant product pools."""
797
812
  return PoolSelector(
798
813
  addresses=[
799
814
  "addr1w89ksjnfu7ys02tedvslc9g2wk90tu5qte0dt4dge60hdugfqe64t",
@@ -801,25 +816,24 @@ class SplashCPPRoyaltyState(SplashCPPState):
801
816
  )
802
817
 
803
818
  @classmethod
804
- def pool_datum_class(self) -> type[SplashCPPRoyaltyPoolDatum]:
819
+ def pool_datum_class(cls) -> type[SplashCPPRoyaltyPoolDatum]:
820
+ """Return the pool datum class for royalty pools."""
805
821
  return SplashCPPRoyaltyPoolDatum
806
822
 
807
823
  @classmethod
808
- def post_init(cls, values):
824
+ def post_init(cls, values: dict[str, Any]) -> None:
825
+ """Post-initialization processing for royalty constant product pools."""
809
826
  super().post_init(values)
810
827
 
811
828
  datum: SplashCPPRoyaltyPoolDatum = SplashCPPRoyaltyPoolDatum.from_cbor(
812
829
  values["datum_cbor"],
813
830
  )
814
831
 
815
- values["fee"] = 100000 - datum.pool_fee + datum.treasury_fee + datum.royalty_fee
832
+ values["fee"] += datum.royalty_fee
816
833
 
817
834
  assets = values["assets"]
818
- assets.root[assets.unit(0)] -= datum.treasury_x + datum.royalty_x
819
- assets.root[assets.unit(1)] -= datum.treasury_y + datum.royalty_y
820
-
821
- # Verify pool is active
822
- values["inactive"] = assets.quantity() < 100000000
835
+ assets.root[assets.unit(0)] -= datum.royalty_x
836
+ assets.root[assets.unit(1)] -= datum.royalty_y
823
837
 
824
838
  def swap_utxo(
825
839
  self,
@@ -831,112 +845,97 @@ class SplashCPPRoyaltyState(SplashCPPState):
831
845
  address_target: Address | None = None,
832
846
  datum_target: PlutusData | None = None,
833
847
  ) -> tuple[TransactionOutput | None, PlutusData]:
834
- return super(SplashBaseState, self).swap_utxo(
835
- address_source=address_source,
836
- in_assets=in_assets,
837
- out_assets=out_assets,
838
- tx_builder=tx_builder,
839
- extra_assets=extra_assets,
840
- address_target=address_target,
841
- datum_target=datum_target,
848
+ """Create a swap UTXO for the royalty constant product pool."""
849
+ if self.tx_hash is None:
850
+ raise ValueError("Transaction hash is required for swap operation")
851
+ if self.pool_nft is None:
852
+ raise ValueError("Pool NFT is required for swap operation")
853
+ if self.lp_tokens is None:
854
+ raise ValueError("LP tokens are required for swap operation")
855
+ if tx_builder is None:
856
+ raise ValueError("Transaction builder is required for swap operation")
857
+
858
+ order_info = get_backend().get_pool_in_tx(
859
+ self.tx_hash,
860
+ assets=[self.pool_nft.unit()],
861
+ addresses=self.pool_selector().addresses,
862
+ )
863
+
864
+ # Get the output assets
865
+ out_assets, _ = self.get_amount_out(asset=in_assets)
866
+
867
+ # Create the output redeemer
868
+ redeemer = Redeemer(
869
+ CPPoolRedeemer(
870
+ action=2,
871
+ self_index=-1,
872
+ ),
873
+ )
874
+
875
+ # Create the pool input UTxO
876
+ pool_datum_class = self.pool_datum_class()
877
+ pool_datum = pool_datum_class.from_cbor(
878
+ self.pool_datum.to_cbor(),
879
+ )
880
+ assets = self.assets + self.pool_nft + self.lp_tokens
881
+ assets.root[self.assets.unit()] += (
882
+ pool_datum.treasury_x + self.pool_datum.royalty_x
883
+ )
884
+ assets.root[self.assets.unit(1)] += (
885
+ pool_datum.treasury_y + self.pool_datum.royalty_y
886
+ )
887
+ input_utxo = UTxO(
888
+ TransactionInput(
889
+ transaction_id=TransactionId(bytes.fromhex(self.tx_hash)),
890
+ index=self.tx_index,
891
+ ),
892
+ output=TransactionOutput(
893
+ address=order_info[0].address,
894
+ amount=asset_to_value(assets),
895
+ datum=self.pool_datum,
896
+ ),
842
897
  )
843
898
 
844
- # def swap_utxo(
845
- # self,
846
- # address_source: Address,
847
- # in_assets: Assets,
848
- # out_assets: Assets,
849
- # tx_builder: TransactionBuilder | None = None,
850
- # extra_assets: Assets | None = None,
851
- # address_target: Address | None = None,
852
- # datum_target: PlutusData | None = None,
853
- # ) -> tuple[TransactionOutput | None, PlutusData]:
854
- # assert self.tx_hash is not None
855
- # assert self.pool_nft is not None
856
- # assert self.lp_tokens is not None
857
- # assert tx_builder is not None
858
-
859
- # order_info = get_backend().get_pool_in_tx(
860
- # self.tx_hash,
861
- # assets=[self.pool_nft.unit()],
862
- # addresses=self.pool_selector().addresses,
863
- # )
864
-
865
- # # Get the output assets
866
- # out_assets, _ = self.get_amount_out(asset=in_assets)
867
-
868
- # # Create the output redeemer
869
- # redeemer = Redeemer(
870
- # CPPoolRedeemer(
871
- # action=2,
872
- # self_index=-1,
873
- # ),
874
- # )
875
-
876
- # # Create the pool input UTxO
877
- # pool_datum_class = self.pool_datum_class()
878
- # pool_datum = pool_datum_class.from_cbor(
879
- # self.pool_datum.to_cbor(),
880
- # )
881
- # assets = self.assets + self.pool_nft + self.lp_tokens
882
- # assets.root[self.assets.unit()] += (
883
- # pool_datum.treasury_x + self.pool_datum.royalty_x
884
- # )
885
- # assets.root[self.assets.unit(1)] += (
886
- # pool_datum.treasury_y + self.pool_datum.royalty_y
887
- # )
888
- # input_utxo = UTxO(
889
- # TransactionInput(
890
- # transaction_id=TransactionId(bytes.fromhex(self.tx_hash)),
891
- # index=self.tx_index,
892
- # ),
893
- # output=TransactionOutput(
894
- # address=order_info[0].address,
895
- # amount=asset_to_value(assets),
896
- # datum=self.pool_datum,
897
- # ),
898
- # )
899
-
900
- # # Create the pool output UTxO
901
- # new_assets = Assets.model_validate(assets.model_dump())
902
- # new_assets.root[in_assets.unit()] += in_assets.quantity()
903
- # new_assets.root[out_assets.unit()] += -out_assets.quantity()
904
- # new_pool_datum = pool_datum_class.from_cbor(
905
- # self.pool_datum.to_cbor(),
906
- # )
907
-
908
- # if in_assets.unit() == new_pool_datum.asset_x.assets.unit():
909
- # new_pool_datum.treasury_x += int(
910
- # in_assets.quantity() * new_pool_datum.treasury_fee // 100000,
911
- # )
912
- # new_pool_datum.royalty_x += int(
913
- # in_assets.quantity() * new_pool_datum.royalty_fee // 100000,
914
- # )
915
- # elif in_assets.unit() == new_pool_datum.asset_y.assets.unit():
916
- # new_pool_datum.treasury_y += int(
917
- # in_assets.quantity() * new_pool_datum.treasury_fee // 100000,
918
- # )
919
- # new_pool_datum.royalty_y += int(
920
- # in_assets.quantity() * new_pool_datum.royalty_fee // 100000,
921
- # )
922
- # else:
923
- # raise ValueError("Invalid input asset")
924
-
925
- # txo = TransactionOutput(
926
- # address=order_info[0].address,
927
- # amount=asset_to_value(new_assets),
928
- # datum=new_pool_datum,
929
- # )
930
-
931
- # # Add the script input
932
- # pool_hash = Address.decode(order_info[0].address).payment_part.payload
933
- # script = (
934
- # get_backend()
935
- # .get_script_from_address(
936
- # Address(payment_part=ScriptHash(payload=pool_hash)),
937
- # )
938
- # .to_utxo()
939
- # )
940
- # tx_builder.add_script_input(utxo=input_utxo, script=script, redeemer=redeemer)
941
-
942
- # return txo, self.pool_datum
899
+ # Create the pool output UTxO
900
+ new_assets = Assets.model_validate(assets.model_dump())
901
+ new_assets.root[in_assets.unit()] += in_assets.quantity()
902
+ new_assets.root[out_assets.unit()] += -out_assets.quantity()
903
+ new_pool_datum = pool_datum_class.from_cbor(
904
+ self.pool_datum.to_cbor(),
905
+ )
906
+
907
+ if in_assets.unit() == new_pool_datum.asset_x.assets.unit():
908
+ new_pool_datum.treasury_x += int(
909
+ in_assets.quantity() * new_pool_datum.treasury_fee // 100000,
910
+ )
911
+ new_pool_datum.royalty_x += int(
912
+ in_assets.quantity() * new_pool_datum.royalty_fee // 100000,
913
+ )
914
+ elif in_assets.unit() == new_pool_datum.asset_y.assets.unit():
915
+ new_pool_datum.treasury_y += int(
916
+ in_assets.quantity() * new_pool_datum.treasury_fee // 100000,
917
+ )
918
+ new_pool_datum.royalty_y += int(
919
+ in_assets.quantity() * new_pool_datum.royalty_fee // 100000,
920
+ )
921
+ else:
922
+ raise ValueError("Invalid input asset")
923
+
924
+ txo = TransactionOutput(
925
+ address=order_info[0].address,
926
+ amount=asset_to_value(new_assets),
927
+ datum=new_pool_datum,
928
+ )
929
+
930
+ # Add the script input
931
+ pool_hash = Address.decode(order_info[0].address).payment_part.payload
932
+ script = (
933
+ get_backend()
934
+ .get_script_from_address(
935
+ Address(payment_part=ScriptHash(payload=pool_hash)),
936
+ )
937
+ .to_utxo()
938
+ )
939
+ tx_builder.add_script_input(utxo=input_utxo, script=script, redeemer=redeemer)
940
+
941
+ return txo, self.pool_datum