charli3_dendrite 1.5.12.dev0__tar.gz → 1.5.13.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 (121) hide show
  1. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/PKG-INFO +1 -1
  2. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/pyproject.toml +2 -2
  3. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/__init__.py +2 -0
  4. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/backend/backend_base.py +20 -0
  5. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/backend/blockfrost/__init__.py +26 -0
  6. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/backend/dbsync/__init__.py +27 -0
  7. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/backend/ogmios_kupo/__init__.py +12 -0
  8. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dataclasses/models.py +16 -0
  9. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/amm_types.py +0 -191
  10. charli3_dendrite-1.5.13.dev0/src/charli3_dendrite/dexs/amm/multi_asset.py +170 -0
  11. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/sundae_v4.py +817 -173
  12. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/core/errors.py +8 -0
  13. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/LICENSE +0 -0
  14. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/README.md +0 -0
  15. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/backend/__init__.py +0 -0
  16. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/backend/blockfrost/models.py +0 -0
  17. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/backend/dbsync/models.py +0 -0
  18. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/backend/ogmios_kupo/models.py +0 -0
  19. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/backend/utils.py +0 -0
  20. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dataclasses/__init__.py +0 -0
  21. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dataclasses/datums.py +0 -0
  22. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/__init__.py +0 -0
  23. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/__init__.py +0 -0
  24. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/amm_base.py +0 -0
  25. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/cswap.py +0 -0
  26. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/dano.py +0 -0
  27. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/minswap.py +0 -0
  28. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/muesli.py +0 -0
  29. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/spectrum.py +0 -0
  30. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/splash.py +0 -0
  31. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/sundae.py +0 -0
  32. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/sundae_v4_deployments.json +0 -0
  33. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/vyfi.py +0 -0
  34. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/amm/wingriders.py +0 -0
  35. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/core/__init__.py +0 -0
  36. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/core/base.py +0 -0
  37. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/ob/__init__.py +0 -0
  38. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/ob/axo.py +0 -0
  39. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/ob/cardanoswaps.py +0 -0
  40. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/ob/chadswap.py +0 -0
  41. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/ob/djed.py +0 -0
  42. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/ob/geniusyield.py +0 -0
  43. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/ob/ob_base.py +0 -0
  44. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/dexs/ob/saturnswap.py +0 -0
  45. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/__init__.py +0 -0
  46. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/base.py +0 -0
  47. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/__init__.py +0 -0
  48. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/constants.py +0 -0
  49. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/datums.py +0 -0
  50. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/loader.py +0 -0
  51. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/market.py +0 -0
  52. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/math.py +0 -0
  53. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/oracles/__init__.py +0 -0
  54. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/oracles/aggregator.py +0 -0
  55. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/oracles/aggregator_datums.py +0 -0
  56. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/oracles/deployments.py +0 -0
  57. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/oracles/forward.py +0 -0
  58. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/oracles/leaves.py +0 -0
  59. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/oracles/locator.py +0 -0
  60. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/oracles/lp_pricing.py +0 -0
  61. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/oracles/recipes.json +0 -0
  62. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/oracles/redeemer.py +0 -0
  63. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/oracles/reproduce.py +0 -0
  64. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/oracles/structured_datums.py +0 -0
  65. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/state.py +0 -0
  66. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/__init__.py +0 -0
  67. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/_common.py +0 -0
  68. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/build.py +0 -0
  69. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/builder.py +0 -0
  70. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/context.py +0 -0
  71. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/create_loan.py +0 -0
  72. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/datum_synth.py +0 -0
  73. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/increase_loan.py +0 -0
  74. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/loan_ids.py +0 -0
  75. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/modify_collateral.py +0 -0
  76. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/oracle_redeemer.py +0 -0
  77. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/oracle_synth.py +0 -0
  78. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/redeemers.py +0 -0
  79. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/repay.py +0 -0
  80. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/topup_withdraw.py +0 -0
  81. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/danogo/transactions/verify.py +0 -0
  82. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/__init__.py +0 -0
  83. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/constants.py +0 -0
  84. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/datums.py +0 -0
  85. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/loader.py +0 -0
  86. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/market.py +0 -0
  87. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/math.py +0 -0
  88. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/__init__.py +0 -0
  89. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/feeds.py +0 -0
  90. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/fluid_api.py +0 -0
  91. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/witness.py +0 -0
  92. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/state.py +0 -0
  93. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/__init__.py +0 -0
  94. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/_common.py +0 -0
  95. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/borrow.py +0 -0
  96. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/builder.py +0 -0
  97. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/change_collateral.py +0 -0
  98. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/context.py +0 -0
  99. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/datum_synth.py +0 -0
  100. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/lend.py +0 -0
  101. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/pool.py +0 -0
  102. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/recast.py +0 -0
  103. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/redeemers.py +0 -0
  104. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/repay.py +0 -0
  105. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/request.py +0 -0
  106. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/resolve.py +0 -0
  107. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/math.py +0 -0
  108. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/oracles/__init__.py +0 -0
  109. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/oracles/charli3.py +0 -0
  110. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/oracles/dex_pooled.py +0 -0
  111. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/oracles/fluid.py +0 -0
  112. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/oracles/models.py +0 -0
  113. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/oracles/orcfax.py +0 -0
  114. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/oracles/qtoken.py +0 -0
  115. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/registry.py +0 -0
  116. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/transactions/__init__.py +0 -0
  117. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/transactions/base.py +0 -0
  118. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/transactions/infra.py +0 -0
  119. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/transactions/snapshot.py +0 -0
  120. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.dev0}/src/charli3_dendrite/lending/units.py +0 -0
  121. {charli3_dendrite-1.5.12.dev0 → charli3_dendrite-1.5.13.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.12.dev0
3
+ Version: 1.5.13.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.12-dev0"
3
+ version = "1.5.13-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.12-dev0"
53
+ current_version = "1.5.13-dev0"
54
54
  parse = """(?x)
55
55
  (?P<major>\\d+)\\.
56
56
  (?P<minor>\\d+)\\.
@@ -19,6 +19,8 @@ from charli3_dendrite.dexs.amm.splash import SplashSSPState
19
19
  from charli3_dendrite.dexs.amm.sundae import SundaeSwapCPPState
20
20
  from charli3_dendrite.dexs.amm.sundae import SundaeSwapV3CPPState
21
21
  from charli3_dendrite.dexs.amm.sundae import SundaeSwapV3StableSwap
22
+ from charli3_dendrite.dexs.amm.sundae_v4 import SundaeV4ConstantSumPool
23
+ from charli3_dendrite.dexs.amm.sundae_v4 import SundaeV4Vault
22
24
  from charli3_dendrite.dexs.amm.vyfi import VyFiCPPState
23
25
  from charli3_dendrite.dexs.amm.wingriders import WingRidersCPPState
24
26
  from charli3_dendrite.dexs.amm.wingriders import WingRidersSSPState
@@ -10,6 +10,7 @@ from pycardano import Address # type: ignore
10
10
  from charli3_dendrite.dataclasses.models import Assets
11
11
  from charli3_dendrite.dataclasses.models import BlockList
12
12
  from charli3_dendrite.dataclasses.models import PoolStateList
13
+ from charli3_dendrite.dataclasses.models import RedeemerRecord
13
14
  from charli3_dendrite.dataclasses.models import ScriptReference
14
15
  from charli3_dendrite.dataclasses.models import SwapTransactionList
15
16
 
@@ -205,6 +206,25 @@ class AbstractBackend(ABC):
205
206
  """
206
207
  pass
207
208
 
209
+ def get_redeemers(self, tx_hash: str) -> list[RedeemerRecord]:
210
+ """Every redeemer supplied in transaction ``tx_hash``.
211
+
212
+ Backends that cannot index redeemers leave this unimplemented.
213
+
214
+ Args:
215
+ tx_hash: The transaction hash (hex).
216
+
217
+ Returns:
218
+ The transaction's redeemers, in implementation-defined order
219
+ (db-sync orders by purpose then index). ``script_hash`` is ``""``
220
+ for a redeemer with no script, never ``None``.
221
+
222
+ Raises:
223
+ NotImplementedError: if the backend has no redeemer index.
224
+ """
225
+ msg = f"{type(self).__name__} cannot read transaction redeemers."
226
+ raise NotImplementedError(msg)
227
+
208
228
  @abstractmethod
209
229
  def get_axo_target(
210
230
  self,
@@ -18,6 +18,7 @@ from charli3_dendrite.dataclasses.models import Assets
18
18
  from charli3_dendrite.dataclasses.models import BlockList
19
19
  from charli3_dendrite.dataclasses.models import PoolStateInfo
20
20
  from charli3_dendrite.dataclasses.models import PoolStateList
21
+ from charli3_dendrite.dataclasses.models import RedeemerRecord
21
22
  from charli3_dendrite.dataclasses.models import ScriptReference
22
23
  from charli3_dendrite.dataclasses.models import SwapTransactionList
23
24
 
@@ -283,6 +284,31 @@ class BlockFrostBackend(AbstractBackend):
283
284
  )
284
285
  return None
285
286
 
287
+ def get_redeemers(self, tx_hash: str) -> list[RedeemerRecord]:
288
+ """Every redeemer of ``tx_hash``, resolving each datum by its hash.
289
+
290
+ Args:
291
+ tx_hash: The transaction hash (hex).
292
+
293
+ Returns:
294
+ The redeemers in the order the API lists them. ``script_hash`` is
295
+ ``""`` for a redeemer the API reports with no script, never
296
+ ``None``.
297
+ """
298
+ records = []
299
+ for item in self.api.transaction_redeemers(tx_hash, gather_pages=True):
300
+ cbor = self.api.script_datum_cbor(item.redeemer_data_hash).cbor
301
+ records.append(
302
+ RedeemerRecord(
303
+ tx_hash=tx_hash,
304
+ purpose=item.purpose,
305
+ index=item.tx_index,
306
+ script_hash=item.script_hash or "",
307
+ data_cbor=cbor,
308
+ ),
309
+ )
310
+ return records
311
+
286
312
  def get_axo_target(
287
313
  self,
288
314
  assets: Assets,
@@ -20,6 +20,7 @@ from charli3_dendrite.backend.dbsync.models import UTxOSelector
20
20
  from charli3_dendrite.dataclasses.models import Assets
21
21
  from charli3_dendrite.dataclasses.models import BlockList
22
22
  from charli3_dendrite.dataclasses.models import PoolStateList
23
+ from charli3_dendrite.dataclasses.models import RedeemerRecord
23
24
  from charli3_dendrite.dataclasses.models import ScriptReference
24
25
  from charli3_dendrite.dataclasses.models import SwapTransactionList
25
26
 
@@ -575,6 +576,32 @@ LIMIT 1
575
576
 
576
577
  return UTxOSelector.parse(r[0])
577
578
 
579
+ def get_redeemers(self, tx_hash: str) -> list[RedeemerRecord]:
580
+ """Every redeemer of ``tx_hash`` with its datum bytes.
581
+
582
+ Args:
583
+ tx_hash: The transaction hash (hex).
584
+
585
+ Returns:
586
+ The redeemers in ``(purpose, index)`` order. ``script_hash`` is
587
+ ``""`` for a redeemer with no script (db-sync's
588
+ ``redeemer.script_hash`` is nullable), never ``None``.
589
+ """
590
+ query = """
591
+ SELECT encode(tx.hash, 'hex') AS tx_hash,
592
+ r.purpose::text AS purpose,
593
+ r.index AS index,
594
+ COALESCE(encode(r.script_hash, 'hex'), '') AS script_hash,
595
+ encode(rd.bytes, 'hex') AS data_cbor
596
+ FROM redeemer r
597
+ JOIN tx ON tx.id = r.tx_id
598
+ JOIN redeemer_data rd ON rd.id = r.redeemer_data_id
599
+ WHERE tx.hash = %(tx_hash)b
600
+ ORDER BY r.purpose, r.index
601
+ """
602
+ rows = self.db_query(query, {"tx_hash": bytes.fromhex(tx_hash)})
603
+ return [RedeemerRecord.model_validate(row) for row in rows]
604
+
578
605
  def _get_historical_order_utxos(
579
606
  self,
580
607
  stake_addresses: list[str],
@@ -22,6 +22,7 @@ from charli3_dendrite.dataclasses.models import BlockInfo
22
22
  from charli3_dendrite.dataclasses.models import BlockList
23
23
  from charli3_dendrite.dataclasses.models import PoolStateInfo
24
24
  from charli3_dendrite.dataclasses.models import PoolStateList
25
+ from charli3_dendrite.dataclasses.models import RedeemerRecord
25
26
  from charli3_dendrite.dataclasses.models import ScriptReference
26
27
  from charli3_dendrite.dataclasses.models import SwapTransactionList
27
28
 
@@ -50,6 +51,8 @@ class OgmiosKupoBackend(AbstractBackend):
50
51
  ogmios_url (str): URL for the Ogmios service.
51
52
  kupo_url (str): URL for the Kupo service.
52
53
  network (Network): The Cardano network to use.
54
+ path (str): URL path segment appended after the host:port when
55
+ building the Ogmios websocket connection string.
53
56
  """
54
57
  _, ws_string = ogmios_url.split("ws://")
55
58
  self.ws_url, self.port = ws_string.split(":")
@@ -478,6 +481,15 @@ class OgmiosKupoBackend(AbstractBackend):
478
481
  )
479
482
  return None
480
483
 
484
+ def get_redeemers(self, tx_hash: str) -> list[RedeemerRecord]:
485
+ """Kupo indexes outputs, not redeemers.
486
+
487
+ Raises:
488
+ NotImplementedError: always.
489
+ """
490
+ msg = "The Ogmios/Kupo backend has no redeemer index."
491
+ raise NotImplementedError(msg)
492
+
481
493
  def get_axo_target(
482
494
  self,
483
495
  assets: Assets,
@@ -310,6 +310,22 @@ class ScriptReference(DendriteBaseModel):
310
310
  return None
311
311
 
312
312
 
313
+ class RedeemerRecord(DendriteBaseModel):
314
+ """One redeemer of a transaction.
315
+
316
+ ``purpose`` is the ledger purpose (``spend`` / ``mint`` / ``cert`` /
317
+ ``reward`` / ``vote`` / ``propose``), ``index`` the redeemer's index within
318
+ that purpose, ``script_hash`` the hex script it was supplied for and
319
+ ``data_cbor`` the hex CBOR of the redeemer datum.
320
+ """
321
+
322
+ tx_hash: str
323
+ purpose: str
324
+ index: int
325
+ script_hash: str
326
+ data_cbor: str
327
+
328
+
313
329
  class BlockInfo(DendriteBaseModel):
314
330
  epoch_slot_no: int
315
331
  block_no: int
@@ -136,197 +136,6 @@ class AbstractConstantProductPoolState(AbstractPoolState):
136
136
  return amount_in, price_impact
137
137
 
138
138
 
139
- class AbstractConstantSumPoolState(AbstractPoolState):
140
- """State of a constant-sum AMM pool priced on a fixed integer value vector.
141
-
142
- A constant-sum pool conserves total *value* ``V = Sum(price_i * reserve_i)`` on a
143
- fixed integer price vector (positionally aligned to the pool assets), with NO
144
- amplification — it is the linear-curve sibling of
145
- :class:`AbstractConstantProductPoolState` (``x*y=k``) and
146
- :class:`AbstractStableSwapPoolState` (the amplified Curve invariant), and is NOT a
147
- stable-swap variant. Within a routable 2-asset ``(i, j)`` leg a swap of ``dx`` of
148
- asset ``i`` preserves ``V`` minus the floored LP fee: the pool retains
149
- ``floor(dx * p_i * fee_num / fee_den)`` of value and pays the remainder out in
150
- asset ``j`` at the fixed price ratio ``p_i : p_j``. Output is bounded by the
151
- out-side reserve ``reserve_j`` — a constant-sum leg cannot pay out more of an
152
- asset than it holds.
153
-
154
- The returned output is the unique maximum that satisfies the on-chain constant-sum
155
- swap-step relation (the value increase ``v_increase`` is pinned to exactly
156
- ``floor(input_value * fee_num / fee_den)``): a larger output over-pays (fails the
157
- tightness bound) and a smaller output over-charges (fails the achievable bound).
158
-
159
- Concrete constant-sum DEXs supply only the per-leg price weights and fee via the
160
- two hooks below (:meth:`_cs_price_pair` and :meth:`_cs_fee`), mirroring how
161
- :class:`AbstractStableSwapPoolState` exposes ``amp``/``_get_ann`` and
162
- :class:`AbstractConstantLiquidityPoolState` exposes ``_sqrt_price_bounds`` — the
163
- standard ``reserve_a`` / ``reserve_b`` / ``unit_a`` / ``unit_b`` interface drives
164
- the math.
165
- """
166
-
167
- fee_basis: int = 10000
168
-
169
- # -- per-DEX hooks (the analogue of the stable base's amp/_get_ann) --------
170
- def _cs_price_pair(self) -> tuple[int, int]:
171
- """Integer price weights ``(price_a, price_b)`` aligned to ``(unit_a, unit_b)``.
172
-
173
- The constant-sum value vector restricted to this 2-asset leg. Defaults to an
174
- equal-price ``(1, 1)`` leg (the common stable-pair case); override when a leg
175
- carries non-unit integer price weights.
176
- """
177
- return (1, 1)
178
-
179
- def _cs_fee(self) -> tuple[int, int]:
180
- """LP swap fee as an exact integer ratio ``(fee_num, fee_den)``.
181
-
182
- Defaults to ``volume_fee / fee_basis``; override to supply the on-chain
183
- ``fee_num`` / ``fee_den`` directly (the on-chain check floors
184
- ``input_value * fee_num / fee_den``).
185
- """
186
- fee = self.volume_fee
187
- if fee is None:
188
- return (0, self.fee_basis)
189
- if isinstance(fee, (list, tuple)):
190
- return (int(fee[0]), self.fee_basis)
191
- return (int(fee), self.fee_basis)
192
-
193
- # -- generic constant-sum value-conservation math -------------------------
194
- def get_amount_out(
195
- self,
196
- asset: Assets,
197
- precise: bool = True,
198
- ) -> tuple[Assets, float]:
199
- """Output amount + price impact for an input ``asset`` (capped at the reserve).
200
-
201
- Value-conservation forward quote: the pool retains the floored fee on the input
202
- value and pays out the remaining value at the fixed price ratio, capped at the
203
- out-side reserve. This is the unique maximum output that satisfies the on-chain
204
- constant-sum step relation.
205
-
206
- Args:
207
- asset (Assets): The input asset amount for the swap.
208
- precise (bool): Accepted for interface parity; the output is always the
209
- integer floor.
210
-
211
- Returns:
212
- tuple[Assets, float]: The output asset and the price-impact ratio.
213
- """
214
- if len(asset) != 1 or asset.unit() not in (self.unit_a, self.unit_b):
215
- error_msg = f"Invalid input asset for pool: {asset}"
216
- raise ValueError(error_msg)
217
-
218
- price_a, price_b = self._cs_price_pair()
219
- fee_num, fee_den = self._cs_fee()
220
- if asset.unit() == self.unit_a:
221
- price_in, price_out, out_real, out_unit = (
222
- price_a,
223
- price_b,
224
- self.reserve_b,
225
- self.unit_b,
226
- )
227
- else:
228
- price_in, price_out, out_real, out_unit = (
229
- price_b,
230
- price_a,
231
- self.reserve_a,
232
- self.unit_a,
233
- )
234
-
235
- amount_in = asset.quantity()
236
- input_value = amount_in * price_in
237
- # Floor the fee — the pool wins the dust, exactly as the on-chain check pins
238
- # v_increase = floor(input_value * fee_num / fee_den).
239
- fee_value = input_value * fee_num // fee_den
240
- out_value = input_value - fee_value
241
- expected_out = min(out_value // price_out, out_real)
242
- out_assets = Assets(**{out_unit: expected_out})
243
- if not precise:
244
- out_assets.root[out_unit] = expected_out
245
-
246
- if amount_in == 0 or expected_out == 0:
247
- return out_assets, 0.0
248
- # Spot (out per in) is the fixed ratio price_in/price_out; on an uncapped leg
249
- # the only wedge is the fee, so the impact equals the fee fraction (it grows
250
- # once the output saturates the reserve cap).
251
- effective = (expected_out * price_out) / (amount_in * price_in)
252
- return out_assets, 1.0 - effective
253
-
254
- def get_amount_in(
255
- self,
256
- asset: Assets,
257
- precise: bool = True,
258
- ) -> tuple[Assets, float]:
259
- """Minimum input + price impact to obtain a desired output ``asset``.
260
-
261
- Algebraic inverse of :meth:`get_amount_out`: the least input whose post-fee
262
- value covers the requested output value at the fixed price ratio.
263
-
264
- Args:
265
- asset (Assets): The desired output asset amount for the swap.
266
- precise (bool): Accepted for interface parity; the input is the integer
267
- ceiling (the minimal whole-unit input).
268
-
269
- Returns:
270
- tuple[Assets, float]: The required input asset and the price-impact ratio.
271
-
272
- Raises:
273
- InvalidPoolError: If the desired output exceeds the leg's reserve.
274
- """
275
- if len(asset) != 1 or asset.unit() not in (self.unit_a, self.unit_b):
276
- error_msg = f"Invalid output asset for pool: {asset}"
277
- raise ValueError(error_msg)
278
- desired_out = asset.quantity()
279
- if desired_out <= 0:
280
- error_msg = "desired output must be positive"
281
- raise ValueError(error_msg)
282
-
283
- price_a, price_b = self._cs_price_pair()
284
- fee_num, fee_den = self._cs_fee()
285
- if asset.unit() == self.unit_b:
286
- out_real, price_in, price_out, in_unit = (
287
- self.reserve_b,
288
- price_a,
289
- price_b,
290
- self.unit_a,
291
- )
292
- else:
293
- out_real, price_in, price_out, in_unit = (
294
- self.reserve_a,
295
- price_b,
296
- price_a,
297
- self.unit_b,
298
- )
299
- if desired_out >= out_real:
300
- error_msg = (
301
- f"Desired output {desired_out} exceeds available reserve {out_real}"
302
- )
303
- raise InvalidPoolError(error_msg)
304
-
305
- out_value = desired_out * price_out
306
- net = fee_den - fee_num
307
-
308
- def _produced(amount: int) -> int:
309
- value = amount * price_in
310
- return (value - value * fee_num // fee_den) // price_out
311
-
312
- # Closed-form ceil is a valid upper bound (it ignores the fee-flooring that
313
- # makes small inputs cheaper); flooring can move the true minimum down a few
314
- # units, so search down to the minimum, then back up to guarantee coverage.
315
- amount_in = max(1, -(-(out_value * fee_den) // (price_in * net)))
316
- while amount_in > 1 and _produced(amount_in - 1) >= desired_out:
317
- amount_in -= 1
318
- while _produced(amount_in) < desired_out:
319
- amount_in += 1
320
- in_assets = Assets(**{in_unit: amount_in})
321
- if not precise:
322
- in_assets.root[in_unit] = amount_in
323
-
324
- if amount_in == 0:
325
- return in_assets, 0.0
326
- effective = (desired_out * price_out) / (amount_in * price_in)
327
- return in_assets, 1.0 - effective
328
-
329
-
330
139
  class AbstractStableSwapPoolState(AbstractPoolState):
331
140
  """Represents the state of a stable swap automated market maker (AMM) pool."""
332
141
 
@@ -0,0 +1,170 @@
1
+ """Abstract base for pools whose reserves are an N-asset vector.
2
+
3
+ Every quote names its units explicitly: for ``N > 2`` there is no "other"
4
+ asset, so nothing on this base defaults or enumerates pairs.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from abc import ABC
10
+ from abc import abstractmethod
11
+
12
+ from pycardano import Address
13
+ from pycardano import PlutusData
14
+ from pycardano import TransactionOutput
15
+ from pydantic import ConfigDict
16
+
17
+ from charli3_dendrite.dataclasses.models import Assets
18
+ from charli3_dendrite.dataclasses.models import DendriteBaseModel
19
+ from charli3_dendrite.dataclasses.models import PoolSelector
20
+ from charli3_dendrite.utility import asset_to_value
21
+
22
+
23
+ class AbstractMultiAssetPoolState(DendriteBaseModel, ABC):
24
+ """State of a pool priced over an N-entry reserve vector.
25
+
26
+ ``reserves`` is keyed by dendrite unit in canonical order (lovelace first,
27
+ then policy + name). Subclasses own the curve math and the order builders.
28
+ """
29
+
30
+ model_config = ConfigDict(
31
+ alias_generator=None,
32
+ populate_by_name=True,
33
+ arbitrary_types_allowed=True,
34
+ )
35
+
36
+ reserves: Assets
37
+
38
+ @classmethod
39
+ @abstractmethod
40
+ def dex(cls) -> str:
41
+ """The DEX name."""
42
+
43
+ @classmethod
44
+ @abstractmethod
45
+ def pool_selector(cls) -> PoolSelector:
46
+ """How pool UTxOs are found."""
47
+
48
+ @classmethod
49
+ @abstractmethod
50
+ def order_selector(cls) -> list[str]:
51
+ """The order submission addresses."""
52
+
53
+ @property
54
+ @abstractmethod
55
+ def pool_id(self) -> str:
56
+ """A unique identifier for the pool type."""
57
+
58
+ @property
59
+ @abstractmethod
60
+ def stake_address(self) -> Address:
61
+ """The address an order UTxO is locked at."""
62
+
63
+ @abstractmethod
64
+ def get_amount_out(
65
+ self,
66
+ asset: Assets,
67
+ out_unit: str,
68
+ precise: bool = True,
69
+ ) -> tuple[Assets, float]:
70
+ """Output of ``out_unit`` for offering ``asset`` (one or more reserves)."""
71
+
72
+ @abstractmethod
73
+ def get_amount_in(
74
+ self,
75
+ asset: Assets,
76
+ in_unit: str,
77
+ precise: bool = True,
78
+ ) -> tuple[Assets, float]:
79
+ """Minimum ``in_unit`` to obtain the single-asset ``asset``."""
80
+
81
+ @abstractmethod
82
+ def price(self, unit_in: str, unit_out: str) -> tuple[int, int]:
83
+ """The exact ``(price_in, price_out)`` weights between two reserves."""
84
+
85
+ @abstractmethod
86
+ def apply_swap(self, asset_in: Assets, asset_out: Assets) -> None:
87
+ """Move the reserves as if the swap settled."""
88
+
89
+ @abstractmethod
90
+ def batcher_fee(
91
+ self,
92
+ in_assets: Assets | None = None,
93
+ out_assets: Assets | None = None,
94
+ extra_assets: Assets | None = None,
95
+ ) -> Assets:
96
+ """The lovelace an order locks for its execution fee."""
97
+
98
+ @abstractmethod
99
+ def deposit(
100
+ self,
101
+ in_assets: Assets | None = None,
102
+ out_assets: Assets | None = None,
103
+ ) -> Assets:
104
+ """The lovelace rider an order carries and gets back."""
105
+
106
+ @abstractmethod
107
+ def swap_datum(
108
+ self,
109
+ address_source: Address,
110
+ in_assets: Assets,
111
+ out_assets: Assets,
112
+ extra_assets: Assets | None = None,
113
+ address_target: Address | None = None,
114
+ datum_target: PlutusData | None = None,
115
+ ) -> PlutusData:
116
+ """The order datum for a swap of ``in_assets`` into at least ``out_assets``."""
117
+
118
+ def units(self) -> list[str]:
119
+ """The reserve units in canonical order."""
120
+ return list(self.reserves.root)
121
+
122
+ def reserve(self, unit: str) -> int:
123
+ """The declared reserve of ``unit``.
124
+
125
+ Raises:
126
+ KeyError: ``unit`` is not a reserve of this pool.
127
+ """
128
+ return self.reserves.root[unit]
129
+
130
+ def swap_utxo(
131
+ self,
132
+ address_source: Address,
133
+ in_assets: Assets,
134
+ out_assets: Assets,
135
+ extra_assets: Assets | None = None,
136
+ address_target: Address | None = None,
137
+ datum_target: PlutusData | None = None,
138
+ ) -> tuple[TransactionOutput, PlutusData]:
139
+ """The order output for a swap: value = input + fee + rider, inline datum.
140
+
141
+ Raises:
142
+ ValueError: more than one asset offered or asked.
143
+ """
144
+ if len(in_assets) != 1 or len(out_assets) != 1:
145
+ msg = "Only one asset can be supplied as input, and one asset as output."
146
+ raise ValueError(msg)
147
+ order_datum = self.swap_datum(
148
+ address_source=address_source,
149
+ in_assets=in_assets,
150
+ out_assets=out_assets,
151
+ extra_assets=extra_assets,
152
+ address_target=address_target,
153
+ datum_target=datum_target,
154
+ )
155
+ value = Assets(**dict(in_assets.root))
156
+ value.root["lovelace"] = (
157
+ value.root.get("lovelace", 0)
158
+ + self.batcher_fee(
159
+ in_assets=in_assets,
160
+ out_assets=out_assets,
161
+ extra_assets=extra_assets,
162
+ ).quantity()
163
+ + self.deposit(in_assets=in_assets, out_assets=out_assets).quantity()
164
+ )
165
+ output = TransactionOutput(
166
+ address=self.stake_address,
167
+ amount=asset_to_value(value),
168
+ datum=order_datum,
169
+ )
170
+ return output, order_datum