charli3_dendrite 1.5.15.dev1__tar.gz → 1.5.16.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 (149) hide show
  1. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/PKG-INFO +1 -1
  2. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/pyproject.toml +2 -2
  3. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/constants.py +6 -3
  4. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/indexing.py +2 -2
  5. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/builder.py +57 -1
  6. charli3_dendrite-1.5.16.dev0/src/charli3_dendrite/lending/fluidtokens_v4/transactions/claim.py +447 -0
  7. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/common.py +13 -0
  8. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/pool_action.py +2 -5
  9. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/redeemers.py +38 -1
  10. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/resolve.py +148 -0
  11. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/transactions/base.py +3 -1
  12. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/LICENSE +0 -0
  13. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/README.md +0 -0
  14. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/__init__.py +0 -0
  15. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/backend/__init__.py +0 -0
  16. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/backend/backend_base.py +0 -0
  17. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/backend/blockfrost/__init__.py +0 -0
  18. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/backend/blockfrost/models.py +0 -0
  19. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/backend/dbsync/__init__.py +0 -0
  20. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/backend/dbsync/models.py +0 -0
  21. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/backend/ogmios_kupo/__init__.py +0 -0
  22. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/backend/ogmios_kupo/models.py +0 -0
  23. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/backend/utils.py +0 -0
  24. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dataclasses/__init__.py +0 -0
  25. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dataclasses/datums.py +0 -0
  26. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dataclasses/models.py +0 -0
  27. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/__init__.py +0 -0
  28. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/__init__.py +0 -0
  29. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/amm_base.py +0 -0
  30. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/amm_types.py +0 -0
  31. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/cswap.py +0 -0
  32. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/dano.py +0 -0
  33. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/minswap.py +0 -0
  34. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/muesli.py +0 -0
  35. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/multi_asset.py +0 -0
  36. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/spectrum.py +0 -0
  37. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/splash.py +0 -0
  38. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/sundae.py +0 -0
  39. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/sundae_v4.py +0 -0
  40. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/sundae_v4_deployments.json +0 -0
  41. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/vyfi.py +0 -0
  42. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/amm/wingriders.py +0 -0
  43. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/core/__init__.py +0 -0
  44. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/core/base.py +0 -0
  45. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/core/errors.py +0 -0
  46. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/ob/__init__.py +0 -0
  47. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/ob/axo.py +0 -0
  48. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/ob/cardanoswaps.py +0 -0
  49. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/ob/chadswap.py +0 -0
  50. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/ob/djed.py +0 -0
  51. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/ob/geniusyield.py +0 -0
  52. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/ob/ob_base.py +0 -0
  53. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/dexs/ob/saturnswap.py +0 -0
  54. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/__init__.py +0 -0
  55. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/base.py +0 -0
  56. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/__init__.py +0 -0
  57. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/constants.py +0 -0
  58. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/datums.py +0 -0
  59. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/loader.py +0 -0
  60. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/market.py +0 -0
  61. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/math.py +0 -0
  62. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/oracles/__init__.py +0 -0
  63. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/oracles/aggregator.py +0 -0
  64. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/oracles/aggregator_datums.py +0 -0
  65. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/oracles/deployments.py +0 -0
  66. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/oracles/forward.py +0 -0
  67. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/oracles/leaves.py +0 -0
  68. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/oracles/locator.py +0 -0
  69. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/oracles/lp_pricing.py +0 -0
  70. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/oracles/recipes.json +0 -0
  71. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/oracles/redeemer.py +0 -0
  72. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/oracles/reproduce.py +0 -0
  73. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/oracles/structured_datums.py +0 -0
  74. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/state.py +0 -0
  75. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/__init__.py +0 -0
  76. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/_common.py +0 -0
  77. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/build.py +0 -0
  78. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/builder.py +0 -0
  79. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/context.py +0 -0
  80. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/create_loan.py +0 -0
  81. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/datum_synth.py +0 -0
  82. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/increase_loan.py +0 -0
  83. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/loan_ids.py +0 -0
  84. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/modify_collateral.py +0 -0
  85. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/oracle_redeemer.py +0 -0
  86. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/oracle_synth.py +0 -0
  87. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/redeemers.py +0 -0
  88. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/repay.py +0 -0
  89. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/topup_withdraw.py +0 -0
  90. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/danogo/transactions/verify.py +0 -0
  91. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/__init__.py +0 -0
  92. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/constants.py +0 -0
  93. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/datums.py +0 -0
  94. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/loader.py +0 -0
  95. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/market.py +0 -0
  96. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/math.py +0 -0
  97. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/__init__.py +0 -0
  98. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/feeds.py +0 -0
  99. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/fluid_api.py +0 -0
  100. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/oracles/witness.py +0 -0
  101. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/state.py +0 -0
  102. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/__init__.py +0 -0
  103. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/_common.py +0 -0
  104. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/borrow.py +0 -0
  105. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/borrow_terms.py +0 -0
  106. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/builder.py +0 -0
  107. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/change_collateral.py +0 -0
  108. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/context.py +0 -0
  109. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/datum_synth.py +0 -0
  110. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/lend.py +0 -0
  111. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/pool.py +0 -0
  112. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/recast.py +0 -0
  113. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/redeemers.py +0 -0
  114. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/repay.py +0 -0
  115. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/request.py +0 -0
  116. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/resolve.py +0 -0
  117. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens/transactions/utxos.py +0 -0
  118. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/__init__.py +0 -0
  119. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/datums.py +0 -0
  120. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/loader.py +0 -0
  121. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/state.py +0 -0
  122. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/__init__.py +0 -0
  123. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/borrow.py +0 -0
  124. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/change_collateral.py +0 -0
  125. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/lender_bond.py +0 -0
  126. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/loan_action.py +0 -0
  127. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/loan_terms.py +0 -0
  128. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/oracle.py +0 -0
  129. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/pool_cancel.py +0 -0
  130. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/pool_create.py +0 -0
  131. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/pool_edit.py +0 -0
  132. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/pool_terms.py +0 -0
  133. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/recast.py +0 -0
  134. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/fluidtokens_v4/transactions/repay.py +0 -0
  135. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/math.py +0 -0
  136. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/oracles/__init__.py +0 -0
  137. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/oracles/charli3.py +0 -0
  138. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/oracles/dex_pooled.py +0 -0
  139. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/oracles/fluid.py +0 -0
  140. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/oracles/models.py +0 -0
  141. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/oracles/orcfax.py +0 -0
  142. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/oracles/qtoken.py +0 -0
  143. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/plutus.py +0 -0
  144. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/registry.py +0 -0
  145. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/transactions/__init__.py +0 -0
  146. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/transactions/infra.py +0 -0
  147. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/transactions/snapshot.py +0 -0
  148. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.dev0}/src/charli3_dendrite/lending/units.py +0 -0
  149. {charli3_dendrite-1.5.15.dev1 → charli3_dendrite-1.5.16.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.15.dev1
3
+ Version: 1.5.16.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.15-dev1"
3
+ version = "1.5.16-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.15-dev1"
53
+ current_version = "1.5.16-dev0"
54
54
  parse = """(?x)
55
55
  (?P<major>\\d+)\\.
56
56
  (?P<minor>\\d+)\\.
@@ -8,9 +8,9 @@ policy id and script hash. The constants below are the live mainnet values:
8
8
  follows a config update.
9
9
 
10
10
  The lender-manager spend script is not listed in the config datum: pools opt into it
11
- by pointing ``lender_bond_address`` at it, and it ships as a constant here. Its own
12
- action scripts are listed in the lender-manager config datum, held with a separate
13
- config NFT.
11
+ by pointing ``lender_bond_address`` at it, and it ships as a constant here, as does
12
+ its dispatch withdraw script, a parameter of the spend script. Its own action scripts
13
+ are listed in the lender-manager config datum, held with a separate config NFT.
14
14
 
15
15
  The pool manager's owner-check scripts are not listed either: they are parameters of
16
16
  the pool-manager policy, so they change only with a new pool-manager policy, which
@@ -72,6 +72,9 @@ LOCKED_BORROWER_MANAGER_SPEND_SKH = (
72
72
 
73
73
  # Not carried by the config datum (see the module docstring).
74
74
  LENDER_MANAGER_SPEND_SKH = "6743f4b69446b4e066cfb89daa3d01ef041b6d2646968993be4cec09"
75
+ # The lender-manager dispatch: its withdraw redeemer names the action, and it requires
76
+ # the withdrawal of that action's script from the lender-manager config.
77
+ LENDER_MANAGER_WITHDRAW_SKH = "247065fa7d0f8c5bce2bb379aa76d2cd79ad4e6a7ab207627039a804"
75
78
 
76
79
  # The pool manager's owner checks for a pool edit and a pool cancel: the
77
80
  # ``pm_edit_pool`` and ``pm_cancel_pool_manager`` validators of
@@ -69,7 +69,7 @@ _STATE_CLASS: dict[EntityKind, type[FluidV4State]] = {
69
69
 
70
70
  # Everything a malformed record can raise while its datum is decoded. Truncated CBOR
71
71
  # raises cbor2's end-of-stream error, which is not a ValueError.
72
- _DECODE_ERRORS = (
72
+ DECODE_ERRORS = (
73
73
  cbor2.CBORDecodeError,
74
74
  DeserializeException,
75
75
  ValueError,
@@ -187,5 +187,5 @@ def parse_utxo(
187
187
  return None
188
188
  try:
189
189
  return _STATE_CLASS[selector.kind].from_record(info)
190
- except _DECODE_ERRORS:
190
+ except DECODE_ERRORS:
191
191
  return None
@@ -22,6 +22,8 @@ from charli3_dendrite.lending.fluidtokens_v4.transactions.change_collateral impo
22
22
  from charli3_dendrite.lending.fluidtokens_v4.transactions.change_collateral import (
23
23
  build_change_collateral,
24
24
  )
25
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.claim import ClaimSnapshot
26
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.claim import build_claim
25
27
  from charli3_dendrite.lending.fluidtokens_v4.transactions.pool_cancel import (
26
28
  PoolCancelSnapshot,
27
29
  )
@@ -56,6 +58,19 @@ if TYPE_CHECKING:
56
58
  from charli3_dendrite.lending.transactions.base import ActionParams
57
59
  from charli3_dendrite.lending.transactions.snapshot import PoolActionSnapshot
58
60
 
61
+ # The borrower's actions on an existing loan, and the lender's actions.
62
+ _LOAN_ACTIONS = {
63
+ LendingAction.REPAY,
64
+ LendingAction.MODIFY_COLLATERAL,
65
+ LendingAction.RECAST,
66
+ }
67
+ _LENDER_ACTIONS = {
68
+ LendingAction.POOL_CREATE,
69
+ LendingAction.POOL_EDIT,
70
+ LendingAction.POOL_CANCEL,
71
+ LendingAction.CLAIM_REPAYMENTS,
72
+ }
73
+
59
74
  # Each action's snapshot type and the function that adds it to a builder.
60
75
  _ACTIONS: dict[
61
76
  LendingAction,
@@ -71,6 +86,7 @@ _ACTIONS: dict[
71
86
  LendingAction.POOL_CREATE: (PoolCreateSnapshot, build_pool_create),
72
87
  LendingAction.POOL_EDIT: (PoolEditSnapshot, build_pool_edit),
73
88
  LendingAction.POOL_CANCEL: (PoolCancelSnapshot, build_pool_cancel),
89
+ LendingAction.CLAIM_REPAYMENTS: (ClaimSnapshot, build_claim),
74
90
  }
75
91
 
76
92
 
@@ -99,7 +115,7 @@ class FluidTokensV4TxBuilder(AbstractLendingTxBuilder):
99
115
 
100
116
  @classmethod
101
117
  def supported_actions(cls) -> set[LendingAction]:
102
- """Borrow, repay, change collateral, recast, and pool create, edit, cancel."""
118
+ """The borrower's loan actions, the lender's pool actions and the claim."""
103
119
  return set(_ACTIONS)
104
120
 
105
121
  def resolve_snapshot(
@@ -126,11 +142,27 @@ class FluidTokensV4TxBuilder(AbstractLendingTxBuilder):
126
142
  - POOL_EDIT: ``loan_utxo`` is the pool and ``amount`` the principal it gains
127
143
  (negative withdraws); its terms are kept. ``actor_address`` owns the pool.
128
144
  - POOL_CANCEL: ``loan_utxo`` is the pool; ``actor_address`` owns it.
145
+ - CLAIM_REPAYMENTS: the repayments owed to the lender bonds
146
+ ``actor_address``'s key holds at the lender manager, at most
147
+ ``MAX_REPAYMENTS_PER_CLAIM``; no other field may be set.
129
148
  - POOL_CREATE takes lender terms ``ActionParams`` cannot carry: resolve it
130
149
  with ``PoolCreateSnapshot.from_backend``.
131
150
  """
132
151
  if action == LendingAction.BORROW:
133
152
  return self._resolve_borrow(backend, params)
153
+ if action in _LOAN_ACTIONS:
154
+ return self._resolve_loan_action(backend, action, params)
155
+ if action in _LENDER_ACTIONS:
156
+ return self._resolve_lender_action(backend, action, params)
157
+ raise ValueError(f"{self.protocol()} does not support action {action.value!r}")
158
+
159
+ def _resolve_loan_action(
160
+ self,
161
+ backend: AbstractBackend,
162
+ action: LendingAction,
163
+ params: ActionParams,
164
+ ) -> PoolActionSnapshot:
165
+ """REPAY, MODIFY_COLLATERAL or RECAST of the loan in ``params``."""
134
166
  if action == LendingAction.REPAY:
135
167
  if params.amount:
136
168
  raise ValueError(
@@ -158,6 +190,15 @@ class FluidTokensV4TxBuilder(AbstractLendingTxBuilder):
158
190
  recasts=[(_out_ref(params.loan_utxo, "loan"), params.amount)],
159
191
  borrower_address=params.actor_address,
160
192
  )
193
+ raise ValueError(f"{self.protocol()} does not support action {action.value!r}")
194
+
195
+ def _resolve_lender_action(
196
+ self,
197
+ backend: AbstractBackend,
198
+ action: LendingAction,
199
+ params: ActionParams,
200
+ ) -> PoolActionSnapshot:
201
+ """POOL_EDIT, POOL_CANCEL or CLAIM_REPAYMENTS; refuses POOL_CREATE."""
161
202
  if action == LendingAction.POOL_EDIT:
162
203
  if not params.amount:
163
204
  raise ValueError(
@@ -180,6 +221,21 @@ class FluidTokensV4TxBuilder(AbstractLendingTxBuilder):
180
221
  pools=[_out_ref(params.loan_utxo, "pool")],
181
222
  lender_address=params.actor_address,
182
223
  )
224
+ if action == LendingAction.CLAIM_REPAYMENTS:
225
+ if (
226
+ params.loan_utxo
227
+ or params.amount
228
+ or params.collateral
229
+ or (params.borrow_amount)
230
+ ):
231
+ raise ValueError(
232
+ "CLAIM_REPAYMENTS takes only params.actor_address; name the bonds "
233
+ "to claim with ClaimSnapshot.from_backend",
234
+ )
235
+ return ClaimSnapshot.from_backend(
236
+ backend,
237
+ lender_address=params.actor_address,
238
+ )
183
239
  if action == LendingAction.POOL_CREATE:
184
240
  raise ValueError(
185
241
  "POOL_CREATE needs lender terms ActionParams cannot carry; resolve it "
@@ -0,0 +1,447 @@
1
+ """FluidTokens V4 repayment claim: a lender collects the repayments of their loans.
2
+
3
+ A repayment lands at the asset manager, owned by the loan's lender bond, and the asset
4
+ manager releases it to any transaction that spends that bond. Lender bonds sit at the
5
+ lender manager, which lets them move only under an action named by its dispatch; the
6
+ ``WithdrawBonds`` action requires every spent bond's lender to sign. A claim spends
7
+ the lender's bonds and the repayments they own, returns each bond to the lender
8
+ manager unchanged (so its automation keeps serving later repayments), and leaves the
9
+ repayments, with any repayment receipts they carry, to the caller's change.
10
+
11
+ The asset manager checks each repayment against every input of the transaction, so a
12
+ claim's execution budget grows with repayments times inputs: a claim spends at most
13
+ :data:`MAX_REPAYMENTS_PER_CLAIM` repayments, and more take several claims.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from collections.abc import Collection
19
+ from collections.abc import Sequence
20
+ from dataclasses import dataclass
21
+ from typing import TYPE_CHECKING
22
+
23
+ from pycardano import Address
24
+ from pycardano import Redeemer
25
+ from pycardano import ScriptHash
26
+ from pycardano import TransactionBuilder
27
+ from pycardano import VerificationKeyHash
28
+
29
+ from charli3_dendrite.lending.fluidtokens.transactions._common import ref_index
30
+ from charli3_dendrite.lending.fluidtokens.transactions._common import reward_address
31
+ from charli3_dendrite.lending.fluidtokens.transactions.utxos import Utxo
32
+ from charli3_dendrite.lending.fluidtokens.transactions.utxos import ogmios_entry
33
+ from charli3_dendrite.lending.fluidtokens.transactions.utxos import script_ref_by_hash
34
+ from charli3_dendrite.lending.fluidtokens.transactions.utxos import to_pycardano_utxo
35
+ from charli3_dendrite.lending.fluidtokens.transactions.utxos import utxo_from_dict
36
+ from charli3_dendrite.lending.fluidtokens_v4 import constants as c
37
+ from charli3_dendrite.lending.fluidtokens_v4.datums import AssetManagerDatumWithToken
38
+ from charli3_dendrite.lending.fluidtokens_v4.datums import ConfigDatum
39
+ from charli3_dendrite.lending.fluidtokens_v4.datums import LenderManagerConfigDatum
40
+ from charli3_dendrite.lending.fluidtokens_v4.datums import LenderManagerDatum
41
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.common import (
42
+ add_zero_withdrawals,
43
+ )
44
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.common import ledger_order
45
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.common import min_ada
46
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.common import out_ref_of
47
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.common import signing_key
48
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.redeemers import (
49
+ AssetManagerWithdrawRedeemer,
50
+ )
51
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.redeemers import (
52
+ LenderManagerActionWithdrawBonds,
53
+ )
54
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.redeemers import (
55
+ LenderManagerWithdrawRedeemer,
56
+ )
57
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.redeemers import (
58
+ LoanSpendRedeemer,
59
+ )
60
+ from charli3_dendrite.lending.transactions.snapshot import PoolActionSnapshot
61
+
62
+ if TYPE_CHECKING:
63
+ from pycardano import TransactionOutput
64
+
65
+ from charli3_dendrite.backend.backend_base import AbstractBackend
66
+
67
+ # The most repayments one claim spends: this leaves room for the lender-manager and
68
+ # wallet inputs within the transaction's execution budget.
69
+ MAX_REPAYMENTS_PER_CLAIM = 10
70
+
71
+
72
+ def _payment_script(utxo: Utxo) -> str | None:
73
+ """The script hash of ``utxo``'s payment credential, or None for a key address."""
74
+ payment = Address.decode(utxo.address).payment_part
75
+ return payment.payload.hex() if isinstance(payment, ScriptHash) else None
76
+
77
+
78
+ @dataclass
79
+ class ClaimPosition:
80
+ """A UTxO holding lender bonds at the lender manager and the repayments it owns."""
81
+
82
+ bond: Utxo
83
+ repayments: list[Utxo]
84
+
85
+ @property
86
+ def out_ref(self) -> tuple[str, int]:
87
+ """The bond UTxO's out-ref."""
88
+ return out_ref_of(self.bond)
89
+
90
+ @property
91
+ def lender_datum(self) -> LenderManagerDatum:
92
+ """The bond UTxO's lender-manager datum."""
93
+ if self.bond.datum is None:
94
+ raise ValueError("lender-bond UTxO is missing its datum")
95
+ return LenderManagerDatum.from_cbor(self.bond.datum)
96
+
97
+ @property
98
+ def bond_names(self) -> list[bytes]:
99
+ """The lender bonds the UTxO holds; each is named after its loan."""
100
+ return [
101
+ bytes.fromhex(n)
102
+ for p, n, q in self.bond.assets
103
+ if p == c.LENDER_BOND_POLICY
104
+ ]
105
+
106
+ @property
107
+ def owner_pkh(self) -> bytes:
108
+ """The key hash that must sign to move the bonds."""
109
+ return signing_key(self.lender_datum.lender_auth, "lender bond")
110
+
111
+ def check(self) -> None:
112
+ """Raise unless the bonds sit at the lender manager and own every repayment."""
113
+ if _payment_script(self.bond) != c.LENDER_MANAGER_SPEND_SKH:
114
+ raise ValueError(f"UTxO {self.out_ref} is not at the lender manager")
115
+ if not self.bond_names:
116
+ raise ValueError(f"UTxO {self.out_ref} holds no lender bond")
117
+ if not self.repayments:
118
+ raise ValueError(f"lender bond UTxO {self.out_ref} owns no repayment")
119
+ names = set(self.bond_names)
120
+ for repayment in self.repayments:
121
+ owner = AssetManagerDatumWithToken.from_cbor(repayment.datum or "")
122
+ if (
123
+ bytes(owner.owner_asset.policy_id).hex() != c.LENDER_BOND_POLICY
124
+ or bytes(owner.owner_asset.asset_name) not in names
125
+ ):
126
+ raise ValueError(
127
+ f"lender bond UTxO {self.out_ref} does not own repayment "
128
+ f"{out_ref_of(repayment)}",
129
+ )
130
+
131
+
132
+ @dataclass
133
+ class ClaimSnapshot(PoolActionSnapshot):
134
+ """Everything a claim of one lender's repayments needs, resolved from chain."""
135
+
136
+ positions: Sequence[ClaimPosition]
137
+ funding: list[Utxo]
138
+ config: Utxo
139
+ lender_manager_config: Utxo
140
+ lender_manager_spend_script_ref: Utxo
141
+ lender_manager_withdraw_script_ref: Utxo
142
+ withdraw_bonds_script_ref: Utxo
143
+ asset_manager_spend_script_ref: Utxo
144
+ asset_manager_withdraw_script_ref: Utxo
145
+
146
+ @property
147
+ def ordered_positions(self) -> list[ClaimPosition]:
148
+ """The positions in the order the ledger sorts their bond inputs."""
149
+ return sorted(
150
+ self.positions,
151
+ key=lambda p: (bytes.fromhex(p.out_ref[0]), p.out_ref[1]),
152
+ )
153
+
154
+ @property
155
+ def withdraw_bonds_script_hash(self) -> str:
156
+ """The lender manager's ``WithdrawBonds`` action, named by its config."""
157
+ if self.lender_manager_config.datum is None:
158
+ raise ValueError("lender-manager config UTxO is missing its datum")
159
+ return LenderManagerConfigDatum.from_cbor(
160
+ self.lender_manager_config.datum,
161
+ ).withdraw_bonds_action_script_hash.hex()
162
+
163
+ @classmethod
164
+ def from_capture(cls, fix: dict) -> ClaimSnapshot:
165
+ """Rebuild the snapshot of a captured mainnet claim (for byte-exact replay)."""
166
+ inputs = [utxo_from_dict(u) for u in fix["inputs"]]
167
+ refs = [utxo_from_dict(u) for u in fix["ref_inputs"]]
168
+ config = next(
169
+ u for u in refs if u.holds(c.CONFIG_NFT_POLICY, c.CONFIG_NFT_NAME)
170
+ )
171
+ lender_manager_config = next(
172
+ u
173
+ for u in refs
174
+ if u.holds(
175
+ c.LENDER_MANAGER_CONFIG_NFT_POLICY,
176
+ c.LENDER_MANAGER_CONFIG_NFT_NAME,
177
+ )
178
+ )
179
+ scripts = ConfigDatum.from_cbor(config.datum or "")
180
+ am_spend = scripts.asset_manager_spend_script_hash.hex()
181
+ repayments = [u for u in inputs if _payment_script(u) == am_spend]
182
+ positions = []
183
+ for bond in ledger_order(
184
+ u for u in inputs if _payment_script(u) == c.LENDER_MANAGER_SPEND_SKH
185
+ ):
186
+ names = {n for p, n, _ in bond.assets if p == c.LENDER_BOND_POLICY}
187
+ positions.append(
188
+ ClaimPosition(
189
+ bond=bond,
190
+ repayments=[
191
+ u
192
+ for u in repayments
193
+ if bytes(
194
+ AssetManagerDatumWithToken.from_cbor(
195
+ u.datum or "",
196
+ ).owner_asset.asset_name,
197
+ ).hex()
198
+ in names
199
+ ],
200
+ ),
201
+ )
202
+ used = {out_ref_of(u) for p in positions for u in (p.bond, *p.repayments)}
203
+ withdraw_bonds = LenderManagerConfigDatum.from_cbor(
204
+ lender_manager_config.datum or "",
205
+ ).withdraw_bonds_action_script_hash.hex()
206
+ return cls(
207
+ positions=positions,
208
+ funding=[u for u in inputs if u.out_ref not in used],
209
+ config=config,
210
+ lender_manager_config=lender_manager_config,
211
+ lender_manager_spend_script_ref=script_ref_by_hash(
212
+ refs,
213
+ c.LENDER_MANAGER_SPEND_SKH,
214
+ ),
215
+ lender_manager_withdraw_script_ref=script_ref_by_hash(
216
+ refs,
217
+ c.LENDER_MANAGER_WITHDRAW_SKH,
218
+ ),
219
+ withdraw_bonds_script_ref=script_ref_by_hash(refs, withdraw_bonds),
220
+ asset_manager_spend_script_ref=script_ref_by_hash(refs, am_spend),
221
+ asset_manager_withdraw_script_ref=script_ref_by_hash(
222
+ refs,
223
+ scripts.repayment_policy_id.hex(),
224
+ ),
225
+ )
226
+
227
+ @classmethod
228
+ def from_backend(
229
+ cls,
230
+ backend: AbstractBackend,
231
+ *,
232
+ lender_address: str,
233
+ bonds: Collection[bytes] | None = None,
234
+ funding: Sequence[Utxo] | None = None,
235
+ ) -> ClaimSnapshot:
236
+ """Resolve a claim of the repayments owed to ``lender_address``'s bonds.
237
+
238
+ The bonds are the lender bonds at the lender manager whose lender
239
+ authorisation is the key of ``lender_address``; the repayments are the
240
+ asset-manager UTxOs they own, the largest first, at most
241
+ :data:`MAX_REPAYMENTS_PER_CLAIM` of them. ``bonds`` (loan ids, which name the
242
+ bonds) limits the claim to those bonds. The lender's UTxOs fund the claim
243
+ unless ``funding`` is given. Refuses a lender with nothing to claim, a named
244
+ bond that is not the lender's at the lender manager, and a wallet with no
245
+ UTxO to fund the claim.
246
+ """
247
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.resolve import (
248
+ config_datum,
249
+ )
250
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.resolve import (
251
+ lender_manager_config_datum,
252
+ )
253
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.resolve import (
254
+ resolve_claim_positions,
255
+ )
256
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.resolve import (
257
+ resolve_config_utxo,
258
+ )
259
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.resolve import (
260
+ resolve_lender_manager_config_utxo,
261
+ )
262
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.resolve import (
263
+ resolve_script,
264
+ )
265
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.resolve import (
266
+ resolve_wallet_funding,
267
+ )
268
+
269
+ payment = Address.decode(lender_address).payment_part
270
+ if not isinstance(payment, VerificationKeyHash):
271
+ raise NotImplementedError("only a key address can claim repayments")
272
+ config = resolve_config_utxo(backend)
273
+ scripts = config_datum(config)
274
+ lender_manager_config = resolve_lender_manager_config_utxo(backend)
275
+ positions = resolve_claim_positions(
276
+ backend,
277
+ scripts,
278
+ lender_pkh=payment.payload,
279
+ bonds=bonds,
280
+ )
281
+ if not positions:
282
+ raise ValueError(f"{lender_address} has no repayments to claim")
283
+ spent = [u for p in positions for u in (p.bond, *p.repayments)]
284
+ resolved = (
285
+ list(funding)
286
+ if funding is not None
287
+ else resolve_wallet_funding(backend, lender_address, exclude=spent)
288
+ )
289
+ if funding is None and not resolved:
290
+ raise ValueError(f"no UTxOs at {lender_address} fund the claim")
291
+ return cls(
292
+ positions=positions,
293
+ funding=resolved,
294
+ config=config,
295
+ lender_manager_config=lender_manager_config,
296
+ lender_manager_spend_script_ref=resolve_script(
297
+ backend,
298
+ c.LENDER_MANAGER_SPEND_SKH,
299
+ ),
300
+ lender_manager_withdraw_script_ref=resolve_script(
301
+ backend,
302
+ c.LENDER_MANAGER_WITHDRAW_SKH,
303
+ ),
304
+ withdraw_bonds_script_ref=resolve_script(
305
+ backend,
306
+ lender_manager_config_datum(
307
+ lender_manager_config,
308
+ ).withdraw_bonds_action_script_hash,
309
+ ),
310
+ asset_manager_spend_script_ref=resolve_script(
311
+ backend,
312
+ scripts.asset_manager_spend_script_hash,
313
+ ),
314
+ asset_manager_withdraw_script_ref=resolve_script(
315
+ backend,
316
+ scripts.repayment_policy_id,
317
+ ),
318
+ )
319
+
320
+
321
+ def require_sole_lender_manager_action(tx_builder: TransactionBuilder) -> None:
322
+ """Raise if ``tx_builder`` already spends from or dispatches the lender manager.
323
+
324
+ ``WithdrawBonds`` requires the lender of every spent lender-manager UTxO to sign,
325
+ and one dispatch redeemer names one action for the whole transaction.
326
+ """
327
+ spends = any(
328
+ isinstance(u.output.address.payment_part, ScriptHash)
329
+ and u.output.address.payment_part.payload.hex() == c.LENDER_MANAGER_SPEND_SKH
330
+ for u in tx_builder.inputs
331
+ )
332
+ dispatches = reward_address(c.LENDER_MANAGER_WITHDRAW_SKH) in (
333
+ tx_builder.withdrawals or {}
334
+ )
335
+ if spends or dispatches:
336
+ raise ValueError(
337
+ "a claim must be the only lender-manager action in its transaction",
338
+ )
339
+
340
+
341
+ def build_claim(tx_builder: TransactionBuilder, *, snapshot: ClaimSnapshot) -> None:
342
+ """Add a claim of every repayment of ``snapshot`` to ``tx_builder``.
343
+
344
+ Each bond UTxO returns to the lender manager unchanged; the repayments land in
345
+ the caller's change. Funding inputs are recorded for Ogmios evaluation. The
346
+ caller balances, signs (with every bond's lender key) and submits.
347
+
348
+ Everything else the caller wants among the transaction's reference inputs must
349
+ be added before this call, which fills the config indexes last. Discard the
350
+ builder if this raises.
351
+ """
352
+ require_sole_lender_manager_action(tx_builder)
353
+ positions = snapshot.ordered_positions
354
+ _check_claim(positions)
355
+ _add_spends(tx_builder, snapshot=snapshot, positions=positions)
356
+
357
+ tx_builder.reference_inputs.add(to_pycardano_utxo(snapshot.config))
358
+ tx_builder.reference_inputs.add(to_pycardano_utxo(snapshot.lender_manager_config))
359
+ dispatch = LenderManagerWithdrawRedeemer(
360
+ config_ref_input_index=0,
361
+ action=LenderManagerActionWithdrawBonds(),
362
+ )
363
+ release = AssetManagerWithdrawRedeemer(config_ref_input_index=0)
364
+ for script_ref, redeemer in (
365
+ (snapshot.lender_manager_withdraw_script_ref, dispatch),
366
+ # The action reads no redeemer.
367
+ (snapshot.withdraw_bonds_script_ref, LoanSpendRedeemer()),
368
+ (snapshot.asset_manager_withdraw_script_ref, release),
369
+ ):
370
+ tx_builder.add_withdrawal_script(
371
+ to_pycardano_utxo(script_ref),
372
+ Redeemer(redeemer),
373
+ )
374
+ add_zero_withdrawals(
375
+ tx_builder,
376
+ [
377
+ c.LENDER_MANAGER_WITHDRAW_SKH,
378
+ snapshot.withdraw_bonds_script_hash,
379
+ bytes(
380
+ ConfigDatum.from_cbor(snapshot.config.datum or "").repayment_policy_id,
381
+ ).hex(),
382
+ ],
383
+ )
384
+ signers = list(tx_builder.required_signers or [])
385
+ for signer in dict.fromkeys(VerificationKeyHash(p.owner_pkh) for p in positions):
386
+ if signer not in signers:
387
+ signers.append(signer)
388
+ tx_builder.required_signers = signers
389
+ for position in positions:
390
+ tx_builder.add_output(_returned_bond(position))
391
+
392
+ refs = ref_index(tx_builder)
393
+ dispatch.config_ref_input_index = refs[out_ref_of(snapshot.lender_manager_config)]
394
+ release.config_ref_input_index = refs[out_ref_of(snapshot.config)]
395
+
396
+
397
+ def _check_claim(positions: Sequence[ClaimPosition]) -> None:
398
+ """Raise for a claim the contracts or the transaction budget would refuse."""
399
+ if not positions:
400
+ raise ValueError("a claim needs at least one lender bond")
401
+ if sum(len(p.repayments) for p in positions) > MAX_REPAYMENTS_PER_CLAIM:
402
+ raise ValueError(
403
+ f"a claim spends at most {MAX_REPAYMENTS_PER_CLAIM} repayments; claim the "
404
+ "rest in another transaction",
405
+ )
406
+ for position in positions:
407
+ position.check()
408
+ spent = [out_ref_of(u) for p in positions for u in (p.bond, *p.repayments)]
409
+ if len(set(spent)) != len(spent):
410
+ raise ValueError("a claim names each bond and repayment once")
411
+
412
+
413
+ def _add_spends(
414
+ tx_builder: TransactionBuilder,
415
+ *,
416
+ snapshot: ClaimSnapshot,
417
+ positions: Sequence[ClaimPosition],
418
+ ) -> None:
419
+ """Spend the bonds, their repayments and the funding not among them."""
420
+ for position in positions:
421
+ tx_builder.add_script_input(
422
+ to_pycardano_utxo(position.bond),
423
+ script=to_pycardano_utxo(snapshot.lender_manager_spend_script_ref),
424
+ redeemer=Redeemer(LoanSpendRedeemer()),
425
+ )
426
+ for repayment in position.repayments:
427
+ tx_builder.add_script_input(
428
+ to_pycardano_utxo(repayment),
429
+ script=to_pycardano_utxo(snapshot.asset_manager_spend_script_ref),
430
+ redeemer=Redeemer(LoanSpendRedeemer()),
431
+ )
432
+ spent = {out_ref_of(u) for p in positions for u in (p.bond, *p.repayments)}
433
+ for funding in snapshot.funding:
434
+ if funding.out_ref not in spent:
435
+ tx_builder.add_input(to_pycardano_utxo(funding))
436
+ snapshot.add_actor_additional_utxo(ogmios_entry(funding))
437
+
438
+
439
+ def _returned_bond(position: ClaimPosition) -> TransactionOutput:
440
+ """The bond UTxO back at the lender manager, as spent.
441
+
442
+ ``WithdrawBonds`` checks no output, so a bond whose ADA no longer covers its
443
+ output's minimum is topped up to it.
444
+ """
445
+ output = to_pycardano_utxo(position.bond).output
446
+ output.amount.coin = max(output.amount.coin, min_ada(output))
447
+ return output
@@ -19,6 +19,7 @@ from charli3_dendrite.dataclasses.models import Assets
19
19
  from charli3_dendrite.lending.fluidtokens.transactions._common import reward_address
20
20
  from charli3_dendrite.lending.fluidtokens.transactions.utxos import Utxo
21
21
  from charli3_dendrite.lending.fluidtokens_v4 import constants as c
22
+ from charli3_dendrite.lending.fluidtokens_v4.datums import AuthCardanoSignature
22
23
  from charli3_dendrite.lending.transactions.infra import EvalContext
23
24
  from charli3_dendrite.lending.units import constr
24
25
  from charli3_dendrite.utility import asset_to_value
@@ -72,6 +73,18 @@ def settled_min_ada(address: Address, assets: dict[str, int], datum: Datum) -> i
72
73
  lovelace = needed
73
74
 
74
75
 
76
+ def signing_key(auth: Datum, what: str) -> bytes:
77
+ """The key hash of an ``AuthorizationMethod`` that is a signature.
78
+
79
+ Raises ``NotImplementedError`` for any other authorisation: this library signs
80
+ with wallet keys only. ``what`` names the owned thing in the message.
81
+ """
82
+ alt, fields = constr(auth)
83
+ if alt != AuthCardanoSignature.CONSTR_ID:
84
+ raise NotImplementedError(f"only a {what} owned by a key is supported")
85
+ return bytes(fields[0])
86
+
87
+
75
88
  def sole_nft_name(utxo: Utxo, policy: str, what: str) -> bytes:
76
89
  """The name of the one NFT of ``policy`` a ``what`` UTxO holds."""
77
90
  names = [n for p, n, q in utxo.assets if p == policy and q == 1]
@@ -32,7 +32,6 @@ from charli3_dendrite.lending.fluidtokens.transactions.utxos import Utxo
32
32
  from charli3_dendrite.lending.fluidtokens.transactions.utxos import ogmios_entry
33
33
  from charli3_dendrite.lending.fluidtokens.transactions.utxos import to_pycardano_utxo
34
34
  from charli3_dendrite.lending.fluidtokens_v4 import constants as c
35
- from charli3_dendrite.lending.fluidtokens_v4.datums import AuthCardanoSignature
36
35
  from charli3_dendrite.lending.fluidtokens_v4.datums import AuthCardanoWithdrawScript
37
36
  from charli3_dendrite.lending.fluidtokens_v4.datums import PoolDatum
38
37
  from charli3_dendrite.lending.fluidtokens_v4.datums import PoolManagerDatum
@@ -42,6 +41,7 @@ from charli3_dendrite.lending.fluidtokens_v4.transactions.common import (
42
41
  from charli3_dendrite.lending.fluidtokens_v4.transactions.common import out_ref_of
43
42
  from charli3_dendrite.lending.fluidtokens_v4.transactions.common import pool_nft_name
44
43
  from charli3_dendrite.lending.fluidtokens_v4.transactions.common import script_hash_of
44
+ from charli3_dendrite.lending.fluidtokens_v4.transactions.common import signing_key
45
45
  from charli3_dendrite.lending.fluidtokens_v4.transactions.common import sole_nft_name
46
46
  from charli3_dendrite.lending.fluidtokens_v4.transactions.common import (
47
47
  withdraw_redeemer_position,
@@ -77,10 +77,7 @@ def owner_pkh(manager: PoolManagerDatum) -> bytes:
77
77
  Raises ``NotImplementedError`` for an owner that is not a key: such a pool cannot
78
78
  be edited or cancelled with a wallet signature.
79
79
  """
80
- alt, fields = constr(manager.pool_owner_auth)
81
- if alt != AuthCardanoSignature.CONSTR_ID:
82
- raise NotImplementedError("only a pool manager owned by a key is supported")
83
- return bytes(fields[0])
80
+ return signing_key(manager.pool_owner_auth, "pool manager")
84
81
 
85
82
 
86
83
  @dataclass