charli3_dendrite 1.5.17.dev0__tar.gz → 1.5.18.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 (154) hide show
  1. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/PKG-INFO +1 -1
  2. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/pyproject.toml +2 -2
  3. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/ob/cardanoswaps.py +388 -56
  4. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/ob/ob_base.py +14 -0
  5. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/ob/saturnswap.py +54 -12
  6. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/LICENSE +0 -0
  7. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/README.md +0 -0
  8. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/__init__.py +0 -0
  9. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/backend/__init__.py +0 -0
  10. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/backend/backend_base.py +0 -0
  11. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/backend/blockfrost/__init__.py +0 -0
  12. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/backend/blockfrost/models.py +0 -0
  13. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/backend/dbsync/__init__.py +0 -0
  14. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/backend/dbsync/models.py +0 -0
  15. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/backend/ogmios_kupo/__init__.py +0 -0
  16. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/backend/ogmios_kupo/models.py +0 -0
  17. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/backend/utils.py +0 -0
  18. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dataclasses/__init__.py +0 -0
  19. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dataclasses/datums.py +0 -0
  20. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dataclasses/models.py +0 -0
  21. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/__init__.py +0 -0
  22. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/__init__.py +0 -0
  23. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/amm_base.py +0 -0
  24. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/amm_types.py +0 -0
  25. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/cswap.py +0 -0
  26. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/dano.py +0 -0
  27. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/minswap.py +0 -0
  28. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/muesli.py +0 -0
  29. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/multi_asset.py +0 -0
  30. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/spectrum.py +0 -0
  31. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/splash.py +0 -0
  32. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/sundae.py +0 -0
  33. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/sundae_v4.py +0 -0
  34. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/sundae_v4_deployments.json +0 -0
  35. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/vyfi.py +0 -0
  36. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/amm/wingriders.py +0 -0
  37. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/core/__init__.py +0 -0
  38. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/core/base.py +0 -0
  39. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/core/errors.py +0 -0
  40. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/ob/__init__.py +0 -0
  41. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/ob/axo.py +0 -0
  42. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/ob/chadswap.py +0 -0
  43. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/ob/djed.py +0 -0
  44. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/dexs/ob/geniusyield.py +0 -0
  45. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/__init__.py +0 -0
  46. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/base.py +0 -0
  47. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/__init__.py +0 -0
  48. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/constants.py +0 -0
  49. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/datums.py +0 -0
  50. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/loader.py +0 -0
  51. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/market.py +0 -0
  52. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/math.py +0 -0
  53. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/oracles/__init__.py +0 -0
  54. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/oracles/aggregator.py +0 -0
  55. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/oracles/aggregator_datums.py +0 -0
  56. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/oracles/deployments.py +0 -0
  57. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/oracles/forward.py +0 -0
  58. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/oracles/leaves.py +0 -0
  59. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/oracles/locator.py +0 -0
  60. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/oracles/lp_pricing.py +0 -0
  61. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/oracles/recipes.json +0 -0
  62. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/oracles/redeemer.py +0 -0
  63. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/oracles/reproduce.py +0 -0
  64. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/oracles/structured_datums.py +0 -0
  65. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/state.py +0 -0
  66. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/__init__.py +0 -0
  67. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/_common.py +0 -0
  68. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/build.py +0 -0
  69. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/builder.py +0 -0
  70. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/context.py +0 -0
  71. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/create_loan.py +0 -0
  72. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/datum_synth.py +0 -0
  73. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/increase_loan.py +0 -0
  74. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/loan_ids.py +0 -0
  75. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/modify_collateral.py +0 -0
  76. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/oracle_redeemer.py +0 -0
  77. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/oracle_synth.py +0 -0
  78. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/redeemers.py +0 -0
  79. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/repay.py +0 -0
  80. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/topup_withdraw.py +0 -0
  81. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/danogo/transactions/verify.py +0 -0
  82. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/__init__.py +0 -0
  83. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/constants.py +0 -0
  84. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/datums.py +0 -0
  85. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/loader.py +0 -0
  86. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/market.py +0 -0
  87. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/math.py +0 -0
  88. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/__init__.py +0 -0
  89. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/feeds.py +0 -0
  90. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/fluid_api.py +0 -0
  91. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/witness.py +0 -0
  92. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/state.py +0 -0
  93. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/__init__.py +0 -0
  94. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/_common.py +0 -0
  95. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/borrow.py +0 -0
  96. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/borrow_terms.py +0 -0
  97. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/builder.py +0 -0
  98. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/change_collateral.py +0 -0
  99. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/context.py +0 -0
  100. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/datum_synth.py +0 -0
  101. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/lend.py +0 -0
  102. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/pool.py +0 -0
  103. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/recast.py +0 -0
  104. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/redeemers.py +0 -0
  105. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/repay.py +0 -0
  106. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/request.py +0 -0
  107. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/resolve.py +0 -0
  108. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/utxos.py +0 -0
  109. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/__init__.py +0 -0
  110. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/constants.py +0 -0
  111. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/datums.py +0 -0
  112. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/indexing.py +0 -0
  113. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/loader.py +0 -0
  114. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/market.py +0 -0
  115. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/rates.py +0 -0
  116. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/state.py +0 -0
  117. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/__init__.py +0 -0
  118. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/borrow.py +0 -0
  119. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/builder.py +0 -0
  120. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/change_collateral.py +0 -0
  121. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/claim.py +0 -0
  122. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/common.py +0 -0
  123. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/lender_bond.py +0 -0
  124. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/loan_action.py +0 -0
  125. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/loan_terms.py +0 -0
  126. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/oracle.py +0 -0
  127. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/pool_action.py +0 -0
  128. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/pool_cancel.py +0 -0
  129. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/pool_create.py +0 -0
  130. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/pool_edit.py +0 -0
  131. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/pool_terms.py +0 -0
  132. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/recast.py +0 -0
  133. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/redeemers.py +0 -0
  134. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/repay.py +0 -0
  135. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/resolve.py +0 -0
  136. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/markets.py +0 -0
  137. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/math.py +0 -0
  138. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/normalized.py +0 -0
  139. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/oracles/__init__.py +0 -0
  140. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/oracles/charli3.py +0 -0
  141. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/oracles/dex_pooled.py +0 -0
  142. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/oracles/fluid.py +0 -0
  143. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/oracles/models.py +0 -0
  144. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/oracles/orcfax.py +0 -0
  145. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/oracles/qtoken.py +0 -0
  146. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/plutus.py +0 -0
  147. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/rates.py +0 -0
  148. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/registry.py +0 -0
  149. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/transactions/__init__.py +0 -0
  150. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/transactions/base.py +0 -0
  151. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/transactions/infra.py +0 -0
  152. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/transactions/snapshot.py +0 -0
  153. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.dev0}/src/charli3_dendrite/lending/units.py +0 -0
  154. {charli3_dendrite-1.5.17.dev0 → charli3_dendrite-1.5.18.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.17.dev0
3
+ Version: 1.5.18.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.17-dev0"
3
+ version = "1.5.18-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.17-dev0"
53
+ current_version = "1.5.18-dev0"
54
54
  parse = """(?x)
55
55
  (?P<major>\\d+)\\.
56
56
  (?P<minor>\\d+)\\.
@@ -11,13 +11,17 @@ on-chain ``SwapDatum`` and exposes it through the standard order-book interface
11
11
  are implemented separately.
12
12
  """
13
13
 
14
+ import dataclasses
14
15
  import hashlib
15
16
  import time
16
17
  from dataclasses import dataclass
17
18
  from decimal import Decimal
19
+ from types import SimpleNamespace
18
20
  from typing import Union
21
+ from typing import cast
19
22
 
20
23
  from pycardano import Address
24
+ from pycardano import ChainContext
21
25
  from pycardano import Network
22
26
  from pycardano import PlutusData
23
27
  from pycardano import PlutusV2Script
@@ -28,8 +32,10 @@ from pycardano import TransactionId
28
32
  from pycardano import TransactionInput
29
33
  from pycardano import TransactionOutput
30
34
  from pycardano import UTxO
35
+ from pycardano import VerificationKeyHash
31
36
  from pycardano import plutus_script_hash
32
37
  from pycardano.utils import min_lovelace
38
+ from pycardano.utils import min_lovelace_post_alonzo
33
39
 
34
40
  from charli3_dendrite.backend import get_backend
35
41
  from charli3_dendrite.dataclasses.datums import OrderDatum
@@ -37,6 +43,7 @@ from charli3_dendrite.dataclasses.datums import PlutusNone
37
43
  from charli3_dendrite.dataclasses.models import Assets
38
44
  from charli3_dendrite.dataclasses.models import OrderType
39
45
  from charli3_dendrite.dataclasses.models import PoolSelector
46
+ from charli3_dendrite.dexs.core.errors import InvalidPoolError
40
47
  from charli3_dendrite.dexs.ob.ob_base import AbstractOrderBookState
41
48
  from charli3_dendrite.dexs.ob.ob_base import AbstractOrderState
42
49
  from charli3_dendrite.dexs.ob.ob_base import BuyOrderBook
@@ -154,6 +161,51 @@ def _unit(policy: bytes, name: bytes) -> str:
154
161
  return policy.hex() + name.hex()
155
162
 
156
163
 
164
+ # A staked swap address used only for its size when sizing an output whose owner
165
+ # is not known: every base address (payment + staking credential) serializes to
166
+ # the same 57 bytes, so the credentials themselves never affect the result.
167
+ _SIZING_ADDRESS = Address(
168
+ payment_part=ScriptHash(bytes.fromhex(SWAP_VALIDATOR_HASH)),
169
+ staking_part=VerificationKeyHash(bytes(28)),
170
+ network=Network.MAINNET,
171
+ )
172
+
173
+ # Lovelace balances in [2**16, 2**32) share one CBOR width (a 4-byte argument).
174
+ # Every realistic min-UTxO falls in that range, so sizing starts from a balance
175
+ # inside it and only re-sizes when the minimum lands outside.
176
+ _SIZING_COIN = 1_000_000
177
+ _SIZING_COIN_WIDTH = range(2**16, 2**32)
178
+
179
+
180
+ def _min_lovelace_at(output: TransactionOutput, coins_per_utxo_byte: int) -> int:
181
+ """The ledger min-UTxO of ``output`` at ``coins_per_utxo_byte``.
182
+
183
+ pycardano's rule reads only ``protocol_param.coins_per_utxo_byte`` from the
184
+ chain context, so callers without a context can size outputs too.
185
+ """
186
+ context = SimpleNamespace(
187
+ protocol_param=SimpleNamespace(coins_per_utxo_byte=coins_per_utxo_byte),
188
+ )
189
+ return min_lovelace_post_alonzo(output, cast(ChainContext, context))
190
+
191
+
192
+ def _self_funded_min_lovelace(
193
+ output: TransactionOutput,
194
+ coins_per_utxo_byte: int,
195
+ ) -> int:
196
+ """The min-UTxO of ``output`` when its lovelace balance is that minimum.
197
+
198
+ The balance's own CBOR width is part of the output size, so a minimum whose
199
+ width differs from the starting balance's is re-sized at the balance it names.
200
+ """
201
+ output.amount.coin = _SIZING_COIN
202
+ needed = _min_lovelace_at(output, coins_per_utxo_byte)
203
+ if needed in _SIZING_COIN_WIDTH:
204
+ return needed
205
+ output.amount.coin = needed
206
+ return max(needed, _min_lovelace_at(output, coins_per_utxo_byte))
207
+
208
+
157
209
  @dataclass
158
210
  class CardanoSwapsRational(PlutusData):
159
211
  """A positive rational price (numerator / denominator)."""
@@ -251,6 +303,218 @@ class CardanoSwapsSwapDatum(OrderDatum):
251
303
  """A resting swap order."""
252
304
  return OrderType.swap
253
305
 
306
+ def price(self) -> tuple[int, int]:
307
+ """The ask-per-offer ``swap_price`` as ``(numerator, denominator)``.
308
+
309
+ The beacon policy only mints for a strictly positive price, but any datum
310
+ can sit in a UTxO, so a zero or negative component is rejected here rather
311
+ than surfacing as a division error in the pricing and fill math.
312
+
313
+ Raises:
314
+ InvalidPoolError: ``swap_price`` is not a positive rational.
315
+ """
316
+ num = self.swap_price.numerator
317
+ den = self.swap_price.denominator
318
+ if num <= 0 or den <= 0:
319
+ raise InvalidPoolError(
320
+ f"Cardano-Swaps swap_price must be positive, got {num}/{den}.",
321
+ )
322
+ return num, den
323
+
324
+ def required_ask(self, offer_taken: int) -> int:
325
+ """The least ask the validator accepts for taking ``offer_taken`` of the offer.
326
+
327
+ The validator checks ``offer_taken * numerator <= ask_given * denominator``,
328
+ so the minimum is ``ceil(offer_taken * numerator / denominator)``.
329
+
330
+ Raises:
331
+ InvalidPoolError: ``swap_price`` is not a positive rational.
332
+ """
333
+ num, den = self.price()
334
+ return -(-offer_taken * num // den)
335
+
336
+ def beacon_assets(self, quantity: int = 1) -> Assets:
337
+ """The pair, offer and ask beacons, each at ``quantity``."""
338
+ return Assets(
339
+ root={
340
+ BEACON_POLICY_ID + self.pair_beacon.hex(): quantity,
341
+ BEACON_POLICY_ID + self.offer_beacon.hex(): quantity,
342
+ BEACON_POLICY_ID + self.ask_beacon.hex(): quantity,
343
+ },
344
+ )
345
+
346
+ def continuation_datum(
347
+ self,
348
+ tx_hash: bytes,
349
+ output_index: int,
350
+ ) -> "CardanoSwapsSwapDatum":
351
+ """This datum with ``prev_input`` pointing at the consumed ``tx_hash#index``.
352
+
353
+ The validator requires a fill's continuing output to carry the spent datum
354
+ unchanged except for ``prev_input``, which must reference the spent UTxO.
355
+ """
356
+ return dataclasses.replace(
357
+ self,
358
+ prev_input=CardanoSwapsSomeOutRef(
359
+ value=CardanoSwapsOutputReference(
360
+ transaction_id=CardanoSwapsTxId(tx_hash=tx_hash),
361
+ output_index=output_index,
362
+ ),
363
+ ),
364
+ )
365
+
366
+ def continuation_min_lovelace(
367
+ self,
368
+ held: Assets,
369
+ coins_per_utxo_byte: int,
370
+ *,
371
+ prev_output_index: int = 0,
372
+ address: Address | None = None,
373
+ ) -> int:
374
+ """The min-UTxO of a continuing output of this swap that holds ``held``.
375
+
376
+ The output carries the three beacons, the native tokens in ``held`` (its
377
+ lovelace entry is ignored) and :meth:`continuation_datum` for a spent
378
+ output at ``prev_output_index``. That ``Some`` reference makes it larger
379
+ than the resting output, whose ``prev_input`` is ``None``, so a resting
380
+ UTxO sized only for itself cannot fund its own continuation.
381
+
382
+ The lovelace balance is sized as the minimum itself: the balance a
383
+ continuation is left with once an ADA offer is fully drawn.
384
+
385
+ Args:
386
+ held: The offer and/or ask quantities the continuation holds.
387
+ coins_per_utxo_byte: The ``coins_per_utxo_byte`` protocol parameter.
388
+ prev_output_index: The output index of the spent UTxO, which the
389
+ continuation's ``prev_input`` records.
390
+ address: The swap address; defaults to a staked address of the same
391
+ size as every base address.
392
+
393
+ Returns:
394
+ The lovelace the continuing output must hold.
395
+ """
396
+ tokens = Assets(
397
+ root={
398
+ unit: quantity
399
+ for unit, quantity in held.items()
400
+ if unit != "lovelace" and quantity > 0
401
+ },
402
+ )
403
+ output = TransactionOutput(
404
+ address=address if address is not None else _SIZING_ADDRESS,
405
+ amount=asset_to_value(self.beacon_assets(1) + tokens),
406
+ datum=self.continuation_datum(bytes(32), prev_output_index),
407
+ )
408
+ return _self_funded_min_lovelace(output, coins_per_utxo_byte)
409
+
410
+ def claimable_offer(
411
+ self,
412
+ gross: int,
413
+ coins_per_utxo_byte: int,
414
+ *,
415
+ held_ask: int = 0,
416
+ prev_output_index: int = 0,
417
+ address: Address | None = None,
418
+ ) -> int:
419
+ """The offer a taker can draw from a swap UTxO holding ``gross`` of it.
420
+
421
+ A token offer's min-ADA is a separate lovelace balance, so all of
422
+ ``gross`` is claimable. An ADA offer's lovelace also funds the UTxO's
423
+ min-UTxO: a fill must leave the continuing output its minimum, which is
424
+ largest when the output holds the most ask. That is sized as the ask
425
+ already held (``held_ask``) plus the ask for drawing all of ``gross``, an
426
+ upper bound on any single fill, so a fill of the returned amount always
427
+ leaves the continuation funded.
428
+
429
+ Args:
430
+ gross: The offer balance held in the swap UTxO.
431
+ coins_per_utxo_byte: The ``coins_per_utxo_byte`` protocol parameter.
432
+ held_ask: The ask already accumulated in the swap UTxO.
433
+ prev_output_index: The swap UTxO's own output index, which the
434
+ continuation's ``prev_input`` records.
435
+ address: The swap address, when known.
436
+
437
+ Returns:
438
+ The claimable offer quantity, floored at 0.
439
+
440
+ Raises:
441
+ InvalidPoolError: ``swap_price`` is not a positive rational.
442
+ """
443
+ self.price()
444
+ if self.offer_unit() != "lovelace":
445
+ return gross
446
+ full_ask = held_ask + self.required_ask(gross)
447
+ floor = self.continuation_min_lovelace(
448
+ Assets(root={self.ask_unit(): full_ask}),
449
+ coins_per_utxo_byte,
450
+ prev_output_index=prev_output_index,
451
+ address=address,
452
+ )
453
+ return max(0, gross - floor)
454
+
455
+ def carrier_lovelace(
456
+ self,
457
+ offer_quantity: int,
458
+ coins_per_utxo_byte: int,
459
+ *,
460
+ prev_output_index: int = 0,
461
+ address: Address | None = None,
462
+ ) -> int:
463
+ """The lovelace a new swap offering ``offer_quantity`` funds for its min-UTxO.
464
+
465
+ It is sized for the largest continuation the swap can reach, so no fill
466
+ has to top the output up and the whole offer stays claimable:
467
+
468
+ * ADA offer: funded on top of the offer. A full fill leaves the output
469
+ holding only this carrier, the beacons and the full ask, and
470
+ :meth:`claimable_offer` sizes that ask on the whole balance, so the
471
+ carrier is the fixed point ``c = min_utxo(required_ask(offer + c))``.
472
+ Then ``claimable_offer(offer + c) == offer``.
473
+ * Token offer: funded as the output's whole lovelace balance. A
474
+ continuation holds at most the full offer and, when the ask is also a
475
+ token, the full ask, so both are sized in.
476
+
477
+ Args:
478
+ offer_quantity: The offered quantity.
479
+ coins_per_utxo_byte: The ``coins_per_utxo_byte`` protocol parameter.
480
+ prev_output_index: The output index the swap UTxO is created at.
481
+ address: The swap address, when known.
482
+
483
+ Returns:
484
+ The carrier lovelace.
485
+
486
+ Raises:
487
+ InvalidPoolError: ``swap_price`` is not a positive rational.
488
+ """
489
+ self.price()
490
+ ask_unit = self.ask_unit()
491
+ if self.offer_unit() == "lovelace":
492
+ # The minimum grows with the ask's CBOR width, which grows with the
493
+ # carrier, so iterate from zero; the sequence never decreases and the
494
+ # width takes only a few values, so it settles within a few steps.
495
+ carrier = 0
496
+ while True:
497
+ needed = self.continuation_min_lovelace(
498
+ Assets(
499
+ root={ask_unit: self.required_ask(offer_quantity + carrier)},
500
+ ),
501
+ coins_per_utxo_byte,
502
+ prev_output_index=prev_output_index,
503
+ address=address,
504
+ )
505
+ if needed <= carrier:
506
+ return carrier
507
+ carrier = needed
508
+ held = {self.offer_unit(): offer_quantity}
509
+ if ask_unit != "lovelace":
510
+ held[ask_unit] = self.required_ask(offer_quantity)
511
+ return self.continuation_min_lovelace(
512
+ Assets(root=held),
513
+ coins_per_utxo_byte,
514
+ prev_output_index=prev_output_index,
515
+ address=address,
516
+ )
517
+
254
518
 
255
519
  class CardanoSwapsOrderState(AbstractOrderState):
256
520
  """A resting Cardano-Swaps v2 one-way swap UTxO.
@@ -336,24 +600,61 @@ class CardanoSwapsOrderState(AbstractOrderState):
336
600
 
337
601
  @property
338
602
  def price(self) -> tuple[int, int]:
339
- """Ask-per-offer price as ``(numerator, denominator)``."""
340
- return (
341
- self.order_datum.swap_price.numerator,
342
- self.order_datum.swap_price.denominator,
343
- )
603
+ """Ask-per-offer price as ``(numerator, denominator)``.
604
+
605
+ A UTxO whose datum has a non-positive ``swap_price`` still parses, so a
606
+ caller can skip it on this error instead of failing a whole book.
607
+
608
+ Raises:
609
+ InvalidPoolError: the datum's ``swap_price`` is not a positive rational.
610
+ """
611
+ return self.order_datum.price()
344
612
 
345
613
  @property
346
614
  def available(self) -> Assets:
347
- """Max offer asset that can be taken (the offer balance in the UTxO).
615
+ """The offer balance held in the UTxO.
348
616
 
349
- When the offer asset is ADA, this includes the UTxO's mandatory min-ADA
350
- (the continuing/owner output must still carry it), so the figure is an
351
- upper bound on the genuinely-takeable amount. The contract has no
352
- separate offered-amount field, so the held balance is the only signal;
353
- the fill builder nets out the min-UTxO.
617
+ The contract has no separate offered-amount field, so the held balance is
618
+ the only signal. For an ADA offer it includes the lovelace the continuing
619
+ output must keep as its min-UTxO, so it is an upper bound on what a fill
620
+ can take; :meth:`claimable_offer` nets that out, and :meth:`swap_utxo`
621
+ rejects a fill that takes more.
354
622
  """
355
623
  return Assets(root={self.out_unit: self.assets[self.out_unit]})
356
624
 
625
+ def required_ask(self, offer_taken: int) -> int:
626
+ """The ask a fill taking ``offer_taken`` of the offer pays into the swap.
627
+
628
+ This is exactly what :meth:`swap_utxo` deposits into the continuing output.
629
+
630
+ Raises:
631
+ InvalidPoolError: the datum's ``swap_price`` is not a positive rational.
632
+ """
633
+ return self.order_datum.required_ask(offer_taken)
634
+
635
+ def claimable_offer(self, coins_per_utxo_byte: int) -> int:
636
+ """The most of the offer one fill of this UTxO can take.
637
+
638
+ :meth:`CardanoSwapsSwapDatum.claimable_offer` over this UTxO's offer
639
+ balance and accumulated ask, sized at its own output index (which the
640
+ continuation's ``prev_input`` records) and at its address when known. A
641
+ :meth:`swap_utxo` fill of this amount always leaves the continuing output
642
+ funded.
643
+
644
+ Args:
645
+ coins_per_utxo_byte: The ``coins_per_utxo_byte`` protocol parameter.
646
+
647
+ Raises:
648
+ InvalidPoolError: the datum's ``swap_price`` is not a positive rational.
649
+ """
650
+ return self.order_datum.claimable_offer(
651
+ self.assets[self.out_unit],
652
+ coins_per_utxo_byte,
653
+ held_ask=self.assets[self.in_unit],
654
+ prev_output_index=self.tx_index,
655
+ address=Address.decode(self.address) if self.address is not None else None,
656
+ )
657
+
357
658
  @property
358
659
  def tvl(self) -> int:
359
660
  """Total value locked in the order (the available offer asset)."""
@@ -508,13 +809,7 @@ class CardanoSwapsOrderState(AbstractOrderState):
508
809
  @staticmethod
509
810
  def _beacon_mint_assets(datum: CardanoSwapsSwapDatum, quantity: int) -> Assets:
510
811
  """The three beacons (pair/offer/ask) at +1 (CREATE) or -1 (CLOSE)."""
511
- return Assets(
512
- root={
513
- BEACON_POLICY_ID + datum.pair_beacon.hex(): quantity,
514
- BEACON_POLICY_ID + datum.offer_beacon.hex(): quantity,
515
- BEACON_POLICY_ID + datum.ask_beacon.hex(): quantity,
516
- },
517
- )
812
+ return datum.beacon_assets(quantity)
518
813
 
519
814
  @staticmethod
520
815
  def _accumulate_mint(tx_builder: TransactionBuilder, mint_assets: Assets) -> None:
@@ -544,6 +839,11 @@ class CardanoSwapsOrderState(AbstractOrderState):
544
839
  beacon-tagged swap address carrying the offer asset, the three beacons
545
840
  and an inline ``SwapDatum``. No swap-UTxO is spent.
546
841
 
842
+ The output's lovelace is funded for the largest continuation the swap
843
+ reaches (see :meth:`CardanoSwapsSwapDatum.carrier_lovelace`), sized at the
844
+ output index this output is added at, so the whole offer can be taken by
845
+ :meth:`swap_utxo` without the taker topping the output up.
846
+
547
847
  Args:
548
848
  owner_address: The owner's address; its staking credential identifies
549
849
  the owner and is carried onto the swap address.
@@ -558,6 +858,9 @@ class CardanoSwapsOrderState(AbstractOrderState):
558
858
 
559
859
  Returns:
560
860
  The swap output and its inline ``SwapDatum``.
861
+
862
+ Raises:
863
+ ValueError: ``price`` is not a positive rational.
561
864
  """
562
865
  offer_unit = offer.unit()
563
866
  ask_unit = ask.unit()
@@ -565,6 +868,8 @@ class CardanoSwapsOrderState(AbstractOrderState):
565
868
  ask_id, ask_name = cls._split_unit(ask_unit)
566
869
 
567
870
  num, den = price
871
+ if num <= 0 or den <= 0:
872
+ raise ValueError(f"price must be a positive rational, got {num}/{den}.")
568
873
  exp = (
569
874
  CardanoSwapsSomeInt(value=expiration)
570
875
  if expiration is not None
@@ -599,31 +904,23 @@ class CardanoSwapsOrderState(AbstractOrderState):
599
904
  amount=asset_to_value(output_assets),
600
905
  datum=datum,
601
906
  )
907
+ # The validator derives an ADA offer's ``offer_taken`` as
908
+ # ``lovelace_in - lovelace_out``, so a fill can never draw the UTxO below
909
+ # the min-UTxO of its continuation, whose ``prev_input`` records the spent
910
+ # UTxO. Fund a carrier for that continuation: on top of an ADA offer, or
911
+ # as the lovelace of a token offer. Its first fill spends this output at
912
+ # the index it is added at. The maker reclaims the carrier on CLOSE.
913
+ carrier = datum.carrier_lovelace(
914
+ offer.quantity(),
915
+ tx_builder.context.protocol_param.coins_per_utxo_byte,
916
+ prev_output_index=len(tx_builder.outputs),
917
+ address=swap_address,
918
+ )
602
919
  if offer_unit == "lovelace":
603
- # ADA offer: the offered asset IS the UTxO's min-ADA, and the
604
- # validator derives ``offer_taken = lovelace_in - lovelace_out``, so a
605
- # taker can never draw the UTxO below its min-ADA floor — the tail of
606
- # the stated offer would be un-fillable. Fund a SEPARATE carrier on
607
- # top of the offer, sized to the FINAL full-fill state (3 beacons +
608
- # the fully-accumulated ask asset + inline datum), so the entire
609
- # offer is drawable while the UTxO stays at/above that floor. The
610
- # maker reclaims the carrier (plus the accumulated ask) on CLOSE.
611
- full_ask = -(-offer.quantity() * num // den) # ceil(offer x price)
612
- final_assets = cls._beacon_mint_assets(datum, 1) + Assets(
613
- root={ask_unit: full_ask},
614
- )
615
- final_txo = TransactionOutput(
616
- address=swap_address,
617
- amount=asset_to_value(final_assets),
618
- datum=datum,
619
- )
620
- carrier = min_lovelace(tx_builder.context, output=final_txo)
621
920
  txo.amount.coin = offer.quantity() + carrier
622
921
  else:
623
- # Token offer: the offered token fully leaves on a fill and the
624
- # min-ADA is a separate lovelace carrier (no phantom tail).
625
922
  txo.amount.coin = max(
626
- txo.amount.coin,
923
+ carrier,
627
924
  min_lovelace(tx_builder.context, output=txo),
628
925
  )
629
926
  tx_builder.add_output(txo)
@@ -685,12 +982,25 @@ class CardanoSwapsOrderState(AbstractOrderState):
685
982
  ``out_assets`` is the offer asset the taker receives; ``in_assets`` is the
686
983
  ask asset they pay. Purity (only offer leaves, only ask is added) and the
687
984
  ``offer_taken * price_num <= ask_given * price_den`` price check are
688
- enforced by the validator; this builder constructs a balanced fill.
985
+ enforced by the validator; this builder constructs a balanced fill. The
986
+ ask deposited is :meth:`required_ask` of the offer taken; ``in_assets``
987
+ only selects the ask asset.
988
+
989
+ A token offer's continuation is topped up to its min-UTxO when needed (the
990
+ validator lets a taker deposit ADA). An ADA offer's cannot be: the lovelace
991
+ left behind is what sets ``offer_taken``, so a fill that would leave the
992
+ continuation below its min-UTxO is rejected; :meth:`claimable_offer` is the
993
+ most a fill can take.
689
994
 
690
995
  Returns:
691
996
  ``(continuing_output, continuing_datum)`` — the validator always
692
997
  requires a continuing beacon output (a full fill simply leaves the
693
998
  offer balance at ~0); only CLOSE removes the beacons.
999
+
1000
+ Raises:
1001
+ ValueError: the assets are not the offer and ask, or the fill takes
1002
+ more of the offer than the UTxO can release.
1003
+ InvalidPoolError: the datum's ``swap_price`` is not a positive rational.
694
1004
  """
695
1005
  owner = owner_address if owner_address is not None else address_source
696
1006
 
@@ -703,11 +1013,14 @@ class CardanoSwapsOrderState(AbstractOrderState):
703
1013
  offer_unit = self.out_unit
704
1014
  ask_unit = self.in_unit
705
1015
 
706
- num, den = self.price
707
1016
  offer_taken = out_assets.quantity()
708
- # Minimum ask the maker must receive for this offer at price-or-better:
709
- # offer_taken*num <= ask_given*den => ask_given >= ceil(offer_taken*num/den).
710
- ask_given = -(-offer_taken * num // den) # ceil division
1017
+ held_offer = self.assets[offer_unit]
1018
+ if offer_taken <= 0 or offer_taken > held_offer:
1019
+ raise ValueError(
1020
+ f"out_assets must take between 1 and {held_offer} of the offer, "
1021
+ f"got {offer_taken}.",
1022
+ )
1023
+ ask_given = self.required_ask(offer_taken)
711
1024
 
712
1025
  input_utxo = self._input_utxo(owner)
713
1026
 
@@ -728,12 +1041,9 @@ class CardanoSwapsOrderState(AbstractOrderState):
728
1041
  # no separate maker payment; the maker collects the accumulated ask on
729
1042
  # CLOSE. The datum is identical to the input EXCEPT prev_input = consumed
730
1043
  # TxOutRef.
731
- cont_datum = CardanoSwapsSwapDatum.from_cbor(self.order_datum.to_cbor())
732
- cont_datum.prev_input = CardanoSwapsSomeOutRef(
733
- value=CardanoSwapsOutputReference(
734
- transaction_id=CardanoSwapsTxId(tx_hash=bytes.fromhex(self.tx_hash)),
735
- output_index=self.tx_index,
736
- ),
1044
+ cont_datum = self.order_datum.continuation_datum(
1045
+ bytes.fromhex(self.tx_hash),
1046
+ self.tx_index,
737
1047
  )
738
1048
 
739
1049
  # Continuing value = consumed value, less the offer taken, plus the ask
@@ -759,10 +1069,15 @@ class CardanoSwapsOrderState(AbstractOrderState):
759
1069
  amount=asset_to_value(cont_assets),
760
1070
  datum=cont_datum,
761
1071
  )
762
- cont_txo.amount.coin = max(
763
- cont_txo.amount.coin,
764
- min_lovelace(tx_builder.context, output=cont_txo),
765
- )
1072
+ coin = cont_txo.amount.coin
1073
+ needed = min_lovelace(tx_builder.context, output=cont_txo)
1074
+ if offer_unit == "lovelace" and coin < needed:
1075
+ raise ValueError(
1076
+ f"Taking {offer_taken} lovelace leaves the continuing output "
1077
+ f"{coin} lovelace, below its min-UTxO of {needed}; "
1078
+ "claimable_offer() is the most a fill can take.",
1079
+ )
1080
+ cont_txo.amount.coin = max(coin, needed)
766
1081
  tx_builder.add_output(cont_txo)
767
1082
 
768
1083
  return cont_txo, cont_datum
@@ -865,7 +1180,11 @@ class CardanoSwapsOrderBook(AbstractOrderBookState):
865
1180
  for order in orders:
866
1181
  if order.inactive:
867
1182
  continue
868
- num, denom = order.price
1183
+ try:
1184
+ num, denom = order.price
1185
+ except InvalidPoolError:
1186
+ # A malformed swap_price cannot be priced or filled.
1187
+ continue
869
1188
  o = OrderBookOrder(
870
1189
  price=num / denom,
871
1190
  quantity=int(order.available.quantity()),
@@ -960,13 +1279,17 @@ class CardanoSwapsOrderBook(AbstractOrderBookState):
960
1279
  fill is threaded the maker's real owner — the resting UTxO's staking
961
1280
  credential, carried on the order's ``address`` — so the continuing output
962
1281
  lands back at the correct beacon-tagged swap address (defaulting the owner
963
- to the taker would reconstruct the wrong input and fail validation).
1282
+ to the taker would reconstruct the wrong input and fail validation). Each
1283
+ fill is capped at the order's claimable offer, so an ADA offer's
1284
+ continuation keeps its min-UTxO; an order with nothing claimable is
1285
+ skipped.
964
1286
  """
965
1287
  if in_assets.unit() == self.assets.unit():
966
1288
  book = self.sell_book_full
967
1289
  else:
968
1290
  book = self.buy_book_full
969
1291
 
1292
+ coins_per_utxo_byte = tx_builder.context.protocol_param.coins_per_utxo_byte
970
1293
  in_remaining = Assets.model_validate(in_assets.model_dump())
971
1294
  txo = None
972
1295
  datum = None
@@ -980,6 +1303,15 @@ class CardanoSwapsOrderBook(AbstractOrderBookState):
980
1303
  if order_out.quantity() <= 0 or order_in.quantity() <= 0:
981
1304
  break
982
1305
 
1306
+ claimable = state.claimable_offer(coins_per_utxo_byte)
1307
+ if claimable <= 0:
1308
+ continue
1309
+ if order_out.quantity() > claimable:
1310
+ order_out = Assets(root={state.out_unit: claimable})
1311
+ order_in = Assets(
1312
+ root={state.in_unit: state.required_ask(claimable)},
1313
+ )
1314
+
983
1315
  if state.address is None:
984
1316
  raise ValueError(
985
1317
  "Cardano-Swaps order-book fill requires each resting order's "
@@ -405,6 +405,20 @@ class AbstractOrderBookState(AbstractPairState):
405
405
 
406
406
  return in_assets, 0
407
407
 
408
+ @property
409
+ def taker_fee_bps(self) -> int:
410
+ """Taker fee, in basis points, that the book's level walk charges a fill.
411
+
412
+ The level walk (``get_amount_out`` / ``get_amount_in`` with ``apply_fee``)
413
+ charges the book's own ``fee``, so that is the fee reported here, or 0
414
+ when the book has none. A book whose walk leaves the fee to each order
415
+ overrides this.
416
+ """
417
+ fee = self.fee
418
+ if isinstance(fee, list):
419
+ return max(fee, default=0)
420
+ return fee or 0
421
+
408
422
  @classmethod
409
423
  def reference_utxo(self) -> UTxO | None:
410
424
  return None