charli3_dendrite 1.5.8.dev1__tar.gz → 1.5.9.dev0__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 (118) hide show
  1. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/PKG-INFO +1 -1
  2. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/pyproject.toml +2 -2
  3. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/amm/dano.py +138 -44
  4. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/LICENSE +0 -0
  5. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/README.md +0 -0
  6. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/__init__.py +0 -0
  7. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/backend/__init__.py +0 -0
  8. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/backend/backend_base.py +0 -0
  9. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/backend/blockfrost/__init__.py +0 -0
  10. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/backend/blockfrost/models.py +0 -0
  11. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/backend/dbsync/__init__.py +0 -0
  12. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/backend/dbsync/models.py +0 -0
  13. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/backend/ogmios_kupo/__init__.py +0 -0
  14. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/backend/ogmios_kupo/models.py +0 -0
  15. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/backend/utils.py +0 -0
  16. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dataclasses/__init__.py +0 -0
  17. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dataclasses/datums.py +0 -0
  18. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dataclasses/models.py +0 -0
  19. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/__init__.py +0 -0
  20. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/amm/__init__.py +0 -0
  21. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/amm/amm_base.py +0 -0
  22. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/amm/amm_types.py +0 -0
  23. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/amm/cswap.py +0 -0
  24. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/amm/minswap.py +0 -0
  25. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/amm/muesli.py +0 -0
  26. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/amm/spectrum.py +0 -0
  27. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/amm/splash.py +0 -0
  28. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/amm/sundae.py +0 -0
  29. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/amm/vyfi.py +0 -0
  30. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/amm/wingriders.py +0 -0
  31. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/core/__init__.py +0 -0
  32. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/core/base.py +0 -0
  33. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/core/errors.py +0 -0
  34. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/ob/__init__.py +0 -0
  35. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/ob/axo.py +0 -0
  36. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/ob/cardanoswaps.py +0 -0
  37. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/ob/chadswap.py +0 -0
  38. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/ob/djed.py +0 -0
  39. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/ob/geniusyield.py +0 -0
  40. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/ob/ob_base.py +0 -0
  41. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/dexs/ob/saturnswap.py +0 -0
  42. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/__init__.py +0 -0
  43. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/base.py +0 -0
  44. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/__init__.py +0 -0
  45. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/constants.py +0 -0
  46. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/datums.py +0 -0
  47. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/loader.py +0 -0
  48. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/market.py +0 -0
  49. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/math.py +0 -0
  50. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/oracles/__init__.py +0 -0
  51. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/oracles/aggregator.py +0 -0
  52. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/oracles/aggregator_datums.py +0 -0
  53. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/oracles/deployments.py +0 -0
  54. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/oracles/forward.py +0 -0
  55. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/oracles/leaves.py +0 -0
  56. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/oracles/locator.py +0 -0
  57. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/oracles/lp_pricing.py +0 -0
  58. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/oracles/recipes.json +0 -0
  59. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/oracles/redeemer.py +0 -0
  60. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/oracles/reproduce.py +0 -0
  61. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/oracles/structured_datums.py +0 -0
  62. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/state.py +0 -0
  63. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/__init__.py +0 -0
  64. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/_common.py +0 -0
  65. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/build.py +0 -0
  66. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/builder.py +0 -0
  67. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/context.py +0 -0
  68. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/create_loan.py +0 -0
  69. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/datum_synth.py +0 -0
  70. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/increase_loan.py +0 -0
  71. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/loan_ids.py +0 -0
  72. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/modify_collateral.py +0 -0
  73. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/oracle_redeemer.py +0 -0
  74. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/oracle_synth.py +0 -0
  75. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/redeemers.py +0 -0
  76. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/repay.py +0 -0
  77. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/topup_withdraw.py +0 -0
  78. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/danogo/transactions/verify.py +0 -0
  79. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/__init__.py +0 -0
  80. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/constants.py +0 -0
  81. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/datums.py +0 -0
  82. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/loader.py +0 -0
  83. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/market.py +0 -0
  84. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/math.py +0 -0
  85. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/__init__.py +0 -0
  86. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/feeds.py +0 -0
  87. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/fluid_api.py +0 -0
  88. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/witness.py +0 -0
  89. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/state.py +0 -0
  90. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/__init__.py +0 -0
  91. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/_common.py +0 -0
  92. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/borrow.py +0 -0
  93. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/builder.py +0 -0
  94. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/change_collateral.py +0 -0
  95. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/context.py +0 -0
  96. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/datum_synth.py +0 -0
  97. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/lend.py +0 -0
  98. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/pool.py +0 -0
  99. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/recast.py +0 -0
  100. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/redeemers.py +0 -0
  101. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/repay.py +0 -0
  102. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/request.py +0 -0
  103. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/resolve.py +0 -0
  104. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/math.py +0 -0
  105. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/oracles/__init__.py +0 -0
  106. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/oracles/charli3.py +0 -0
  107. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/oracles/dex_pooled.py +0 -0
  108. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/oracles/fluid.py +0 -0
  109. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/oracles/models.py +0 -0
  110. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/oracles/orcfax.py +0 -0
  111. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/oracles/qtoken.py +0 -0
  112. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/registry.py +0 -0
  113. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/transactions/__init__.py +0 -0
  114. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/transactions/base.py +0 -0
  115. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/transactions/infra.py +0 -0
  116. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/transactions/snapshot.py +0 -0
  117. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/lending/units.py +0 -0
  118. {charli3_dendrite-1.5.8.dev1 → charli3_dendrite-1.5.9.dev0}/src/charli3_dendrite/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: charli3_dendrite
3
- Version: 1.5.8.dev1
3
+ Version: 1.5.9.dev0
4
4
  Summary:
5
5
  License-File: LICENSE
6
6
  Author: Elder Millenial
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "charli3_dendrite"
3
- version = "1.5.8-dev1"
3
+ version = "1.5.9-dev0"
4
4
  description = ""
5
5
  authors = ["Elder Millenial <eldermillenial@protonmail.com>"]
6
6
  readme = "README.md"
@@ -50,7 +50,7 @@ module = ["pycardano", "pycardano.*", "dotenv", "nox", "uplc", "uplc.*"]
50
50
  ignore_missing_imports = true
51
51
 
52
52
  [tool.bumpversion]
53
- current_version = "1.5.8-dev1"
53
+ current_version = "1.5.9-dev0"
54
54
  parse = """(?x)
55
55
  (?P<major>\\d+)\\.
56
56
  (?P<minor>\\d+)\\.
@@ -35,8 +35,10 @@ from pycardano import TransactionId
35
35
  from pycardano import TransactionInput
36
36
  from pycardano import TransactionOutput
37
37
  from pycardano import UTxO
38
+ from pycardano import Value
38
39
  from pycardano import Withdrawals
39
40
  from pycardano import plutus_script_hash
41
+ from pycardano.serialization import ByteString
40
42
  from pycardano.serialization import CBORSerializable
41
43
 
42
44
  from charli3_dendrite.backend import get_backend
@@ -204,19 +206,44 @@ def parse_swap_redeemer_bytes(
204
206
  return first_byte, action, entries
205
207
 
206
208
 
209
+ def _value_to_assets(value: Value) -> Assets:
210
+ """Convert a pycardano ``Value`` (a raw on-chain UTxO amount) to ``Assets``.
211
+
212
+ Used to seed the new pool output from the spent pool UTxO's exact value bag
213
+ so the swap only mutates the traded reserves + swap-fee lovelace, leaving the
214
+ pool's min-ADA, dex NFT, and any other assets untouched (the withdraw
215
+ validator requires the continuation to preserve them).
216
+ """
217
+ assets = Assets(root={"lovelace": int(value.coin)})
218
+ multi = getattr(value, "multi_asset", None)
219
+ if multi:
220
+ for policy, names in multi.data.items():
221
+ for asset_name, quantity in names.data.items():
222
+ unit = policy.payload.hex() + asset_name.payload.hex()
223
+ assets.root[unit] = int(quantity)
224
+ return assets
225
+
226
+
207
227
  @dataclass
208
228
  class DanoSwapRedeemer(CBORSerializable):
209
- """Single-pool Dano swap redeemer (Spend or Withdraw).
210
-
211
- The on-chain redeemer Plutus Data is the raw swap payload serialized as a
212
- CBOR *byte string* (``0x58<len><payload>``); :meth:`to_primitive` returns
213
- the payload so it serializes that way. The pool input index and the first
214
- byte (``pool_in_idx`` for Spend, the protocol-config reference index for
215
- Withdraw) depend on the FINAL transaction ordering, so they are resolved
216
- post-build by :meth:`set_idx`, which the tx builder calls after sorting and
217
- indexing inputs (mirrors ``SSPoolRedeemer.set_idx`` and the clmm-sdk's
218
- ``RedeemerArg`` callback). ``pool_out_idx`` is the pool's position in the
219
- swap's batch vector — ``0`` for a single-pool swap — NOT a tx output index.
229
+ """Dano swap redeemer (Spend or Withdraw) for a batch of >= 1 pool spends.
230
+
231
+ A Dano swap tx spends N >= 1 pool UTxOs governed by a single withdraw-zero:
232
+ the tx carries N Spend redeemers + one Withdraw redeemer, and EVERY redeemer
233
+ carries the same batch vector of per-pool ``(pool_in_idx, pool_out_idx,
234
+ delta)`` entries. Only the leading ``first_byte`` differs — a Spend's is its
235
+ own pool input index; the Withdraw's is the protocol-config reference index.
236
+ ``pool_in_idx`` is the pool's transaction input index; ``pool_out_idx`` is the
237
+ pool's position in the batch vector (its append index ``0..N-1``), NOT a tx
238
+ output index. The input index depends on the FINAL sorted inputs, so the
239
+ batch is resolved post-build by :meth:`set_idx`, which the tx builder calls
240
+ after sorting and indexing inputs (mirrors ``SSPoolRedeemer.set_idx`` and the
241
+ clmm-sdk's ``RedeemerArg`` callback).
242
+
243
+ ``swap_utxo`` accumulates the batch on ``tx_builder._dano_swap_batch`` as each
244
+ pool is folded in, so a single-pool call and a composed multi-pool tx share
245
+ one code path. The on-chain redeemer Plutus Data is the raw swap payload
246
+ serialized as a CBOR byte string; :meth:`to_primitive` returns the payload.
220
247
  """
221
248
 
222
249
  pool_input: TransactionInput
@@ -240,8 +267,32 @@ class DanoSwapRedeemer(CBORSerializable):
240
267
  return i
241
268
  raise InvalidPoolError("redeemer UTxO not found in transaction during set_idx")
242
269
 
270
+ def _resolve_entries(
271
+ self,
272
+ tx_builder: TransactionBuilder,
273
+ ) -> list[tuple[int, int, int]]:
274
+ """Resolve the shared batch entry vector in pool-batch order.
275
+
276
+ Every Spend and the single Withdraw redeemer of a Dano tx carry the same
277
+ vector of ``(pool_in_idx, pool_out_idx, delta)`` entries — one per pool
278
+ folded into ``tx_builder._dano_swap_batch`` (append order). Mirrors the
279
+ clmm-sdk ``swapTokensRedeemer``: ``pool_in_idx`` is the pool's transaction
280
+ input index; ``pool_out_idx`` is the pool's position in the batch vector
281
+ (its append index ``0..N-1``), NOT a tx output index; ``delta`` is that
282
+ pool's signed swap amount. Order is the batch append order (no sort).
283
+ """
284
+ batch = getattr(tx_builder, "_dano_swap_batch", None)
285
+ if not batch:
286
+ batch = [(self.pool_input, self.delta_amount)]
287
+ inputs = list(tx_builder.inputs)
288
+ return [
289
+ (self._sorted_index(inputs, pool_input), pool_out_idx, delta)
290
+ for pool_out_idx, (pool_input, delta) in enumerate(batch)
291
+ ]
292
+
243
293
  def set_idx(self, tx_builder: TransactionBuilder) -> None:
244
- """Resolve pool/config indices from the final (sorted) transaction."""
294
+ """Resolve the shared batch + this redeemer's first byte post-build."""
295
+ self._entries = self._resolve_entries(tx_builder)
245
296
  self.pool_in_idx = self._sorted_index(list(tx_builder.inputs), self.pool_input)
246
297
  if self.is_withdraw:
247
298
  if self.protocol_config_input is None:
@@ -255,13 +306,22 @@ class DanoSwapRedeemer(CBORSerializable):
255
306
  else:
256
307
  self.first_byte = self.pool_in_idx
257
308
 
258
- def to_primitive(self) -> bytes:
259
- """Serialize the redeemer as the packed swap payload bytestring."""
260
- return build_swap_redeemer_bytes(
261
- delta_amount=self.delta_amount,
262
- pool_in_idx=self.pool_in_idx,
263
- pool_out_idx=self.pool_out_idx,
264
- first_byte=self.first_byte,
309
+ def to_primitive(self) -> ByteString:
310
+ """Serialize the redeemer as the packed batch swap payload bytestring.
311
+
312
+ Wrapped in :class:`ByteString` so a payload longer than 64 bytes (any
313
+ batch of >= 2 pools: 2 + 34*N bytes) serializes as a CBOR
314
+ indefinite-length chunked bytestring, as the on-chain validator requires;
315
+ a single-pool payload (36 bytes) stays a definite bytestring.
316
+ """
317
+ entries = getattr(self, "_entries", None)
318
+ if entries is None:
319
+ entries = [(self.pool_in_idx, self.pool_out_idx, self.delta_amount)]
320
+ return ByteString(
321
+ build_batch_swap_redeemer_bytes(
322
+ first_byte=self.first_byte,
323
+ entries=entries,
324
+ ),
265
325
  )
266
326
 
267
327
  @classmethod
@@ -732,15 +792,20 @@ class DanoCLMMState(AbstractConstantLiquidityPoolState):
732
792
 
733
793
  def _new_pool_assets(
734
794
  self,
795
+ base_assets: Assets,
735
796
  pool_change_x: int,
736
797
  pool_change_y: int,
737
798
  swap_fee: int,
738
799
  staking_reward: int = 0,
739
800
  ) -> Assets:
740
801
  d = self._datum
741
- # Build the new output from the GROSS bag — the on-chain UTxO must keep
742
- # the platform fees + min-ADA that ``assets`` (net) no longer carries.
743
- new = self._gross_assets()
802
+ # Copy the spent pool UTxO's exact value bag and mutate ONLY the traded
803
+ # reserves + swap-fee lovelace. The pool's min-ADA, dex NFT, and any other
804
+ # assets carry over untouched — the withdraw validator requires the
805
+ # continuation to preserve them, and reconstructing from the net reserves
806
+ # (``_gross_assets``) drops a token/token pool's real ADA (its min-ADA is
807
+ # not a reserve, so ``assets`` never carried it).
808
+ new = Assets(root=dict(base_assets.root))
744
809
  if self.dex_nft is not None:
745
810
  new.root[self.dex_nft.unit()] = 1
746
811
  new.root[d.unit_x] = new.root.get(d.unit_x, 0) + pool_change_x
@@ -834,6 +899,22 @@ class DanoCLMMState(AbstractConstantLiquidityPoolState):
834
899
  raise InvalidPoolError("Could not re-fetch pool UTxO address via backend")
835
900
  pool_address = order_info[0].address
836
901
 
902
+ # Seed the input value + new pool output from the pool UTxO's EXACT
903
+ # on-chain value bag (min-ADA, dex NFT, any stray assets included) so the
904
+ # swap mutates only the traded reserves + swap-fee lovelace. Reconstructing
905
+ # from the net reserves (``_gross_assets``) drops a token/token pool's real
906
+ # ADA — its min-ADA is not a reserve, so ``assets`` never carried it — and
907
+ # the withdraw validator then rejects the continuation for not preserving
908
+ # it (mirrors SaturnSwap reading its input's coin). Resolve the raw UTxO by
909
+ # out-ref via the chain context; fall back to the reconstructed gross bag
910
+ # when a context cannot resolve by out-ref.
911
+ pool_bag = pool_in_assets
912
+ resolve_utxo = getattr(tx_builder.context, "utxo_by_tx_id", None)
913
+ if resolve_utxo is not None:
914
+ raw_utxo = resolve_utxo(self.tx_hash, self.tx_index)
915
+ if raw_utxo is not None:
916
+ pool_bag = _value_to_assets(raw_utxo.output.amount)
917
+
837
918
  # Overdue ADA pool: when curEpoch > last_withdraw_epoch the validator
838
919
  # requires the pool's accrued staking rewards to be withdrawn from the
839
920
  # per-pool stake credential in the SAME tx (a second withdrawal) and the
@@ -884,6 +965,7 @@ class DanoCLMMState(AbstractConstantLiquidityPoolState):
884
965
 
885
966
  new_datum = self.compute_new_datum(delta_amount, swap_fee, cur_epoch)
886
967
  new_pool_assets = self._new_pool_assets(
968
+ pool_bag,
887
969
  pool_change_x,
888
970
  pool_change_y,
889
971
  swap_fee,
@@ -897,7 +979,7 @@ class DanoCLMMState(AbstractConstantLiquidityPoolState):
897
979
  ),
898
980
  output=TransactionOutput(
899
981
  address=Address.decode(pool_address),
900
- amount=asset_to_value(pool_in_assets),
982
+ amount=asset_to_value(pool_bag),
901
983
  datum=self._datum,
902
984
  ),
903
985
  )
@@ -914,42 +996,54 @@ class DanoCLMMState(AbstractConstantLiquidityPoolState):
914
996
  "Pool script reference UTxO unavailable from backend",
915
997
  )
916
998
 
917
- # The spend + withdraw redeemers encode the pool input index and the
918
- # first byte (pool input index for Spend, protocol-config reference
919
- # index for Withdraw), which depend on the FINAL sorted inputs /
920
- # reference inputs. The builder may still trim or reorder inputs after
921
- # this call, so the indices are resolved POST-BUILD by
922
- # ``DanoSwapRedeemer.set_idx`` — the tx builder runs it after assigning
923
- # redeemer indices (mirrors Splash's pool redeemer + the clmm-sdk's
924
- # ``RedeemerArg`` callback). ``pool_out_idx`` is the single-pool batch
925
- # position (0), not a tx output index.
999
+ # Fold this pool into the tx's shared Dano swap batch. A Dano tx spends
1000
+ # N >= 1 pool UTxOs under ONE withdraw-zero: every Spend redeemer and the
1001
+ # single Withdraw redeemer carry the same batch vector, resolved
1002
+ # POST-BUILD in ``DanoSwapRedeemer.set_idx`` (the builder runs it after
1003
+ # assigning redeemer indices). Composing another Dano pool into the same
1004
+ # ``tx_builder`` appends here and reuses the existing withdraw-zero — the
1005
+ # first pool folded in owns it, later pools only extend the batch.
1006
+ batch = getattr(tx_builder, "_dano_swap_batch", None)
1007
+ if batch is None:
1008
+ batch = []
1009
+ tx_builder._dano_swap_batch = batch
1010
+ first_dano = len(batch) == 0
1011
+ batch.append((input_utxo.input, delta_amount))
1012
+
926
1013
  spend_redeemer = DanoSwapRedeemer(
927
1014
  pool_input=input_utxo.input,
928
1015
  delta_amount=delta_amount,
929
1016
  is_withdraw=False,
930
1017
  )
931
- withdraw_redeemer = DanoSwapRedeemer(
932
- pool_input=input_utxo.input,
933
- delta_amount=delta_amount,
934
- is_withdraw=True,
935
- protocol_config_input=pc_utxo.input,
936
- )
937
-
938
1018
  tx_builder.add_script_input(
939
1019
  utxo=input_utxo,
940
1020
  script=script_ref,
941
1021
  redeemer=Redeemer(spend_redeemer),
942
1022
  )
943
1023
 
1024
+ # Add the withdraw-zero (and its batch Withdraw redeemer) exactly once per
1025
+ # tx. Merge into any existing withdrawals rather than overwrite, so a
1026
+ # prior Dano pool's withdraw-zero and any overdue per-pool staking
1027
+ # withdrawal survive.
944
1028
  reward_addr = Address.from_primitive(DANO_POOL_REWARD_ADDRESS_MAINNET)
945
- withdrawals: dict[bytes, int] = {bytes(reward_addr): 0}
1029
+ withdrawals: dict[bytes, int] = (
1030
+ dict(tx_builder.withdrawals) if tx_builder.withdrawals else {}
1031
+ )
1032
+ withdrawals[bytes(reward_addr)] = 0
946
1033
  if is_overdue and staking_reward_addr is not None:
947
1034
  withdrawals[bytes(staking_reward_addr)] = reward
948
1035
  tx_builder.withdrawals = Withdrawals(withdrawals)
949
- tx_builder.add_withdrawal_script(
950
- script_ref,
951
- Redeemer(withdraw_redeemer),
952
- )
1036
+ if first_dano:
1037
+ withdraw_redeemer = DanoSwapRedeemer(
1038
+ pool_input=input_utxo.input,
1039
+ delta_amount=delta_amount,
1040
+ is_withdraw=True,
1041
+ protocol_config_input=pc_utxo.input,
1042
+ )
1043
+ tx_builder.add_withdrawal_script(
1044
+ script_ref,
1045
+ Redeemer(withdraw_redeemer),
1046
+ )
953
1047
  if is_overdue and staking_ref_utxo is not None:
954
1048
  # Per-pool staking-reward withdrawal. Its redeemer is pool-input
955
1049
  # indexed (like Spend, NOT the protocol-config-indexed withdraw-zero).