charli3_dendrite 1.1.3.dev1__tar.gz → 1.1.3.dev2__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.
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/PKG-INFO +3 -3
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/README.md +2 -2
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/pyproject.toml +2 -2
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/cswap.py +2 -2
- charli3_dendrite-1.1.3.dev2/src/charli3_dendrite/dexs/ob/djed.py +946 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/LICENSE +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/__init__.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/backend/__init__.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/backend/backend_base.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/backend/blockfrost/__init__.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/backend/blockfrost/models.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/backend/dbsync/__init__.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/backend/dbsync/models.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/backend/ogmios_kupo/__init__.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/backend/ogmios_kupo/models.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/backend/utils.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dataclasses/__init__.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dataclasses/datums.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dataclasses/models.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/__init__.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/__init__.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/amm_base.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/amm_types.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/minswap.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/muesli.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/spectrum.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/splash.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/sundae.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/vyfi.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/wingriders.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/core/__init__.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/core/base.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/core/errors.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/ob/__init__.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/ob/axo.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/ob/geniusyield.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/ob/ob_base.py +0 -0
- {charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/utility.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: charli3_dendrite
|
|
3
|
-
Version: 1.1.3.
|
|
3
|
+
Version: 1.1.3.dev2
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Elder Millenial
|
|
6
6
|
Author-email: eldermillenial@protonmail.com
|
|
@@ -36,7 +36,7 @@ Charli3 Dendrite is a powerful Python SDK designed for seamless interaction with
|
|
|
36
36
|
|
|
37
37
|
## Key Features
|
|
38
38
|
|
|
39
|
-
- 🔄 Multi-DEX Support: Integrate with Minswap, MuesliSwap, Spectrum, SundaeSwap, VyFi, GeniusYield, Splash, and WingRiders
|
|
39
|
+
- 🔄 Multi-DEX Support: Integrate with CSwap, Minswap, MuesliSwap, Spectrum, SundaeSwap, VyFi, GeniusYield, Splash, and WingRiders
|
|
40
40
|
- 💧 Liquidity Pool Data: Fetch and analyze pool information across different DEXs
|
|
41
41
|
- 💱 Swap Operations: Execute token swaps with ease
|
|
42
42
|
- 🧩 Flexible Asset Handling: Manage various asset types and pool states efficiently
|
|
@@ -65,6 +65,7 @@ Charli3 Dendrite currently supports the following Cardano DEXs:
|
|
|
65
65
|
- WingRiders
|
|
66
66
|
- GeniusYield
|
|
67
67
|
- Splash
|
|
68
|
+
- CSwap
|
|
68
69
|
|
|
69
70
|
### Deprecated DEXs
|
|
70
71
|
|
|
@@ -73,7 +74,6 @@ Charli3 Dendrite currently supports the following Cardano DEXs:
|
|
|
73
74
|
### Not Yet Implemented
|
|
74
75
|
|
|
75
76
|
- CardanoSwaps
|
|
76
|
-
- CSwap
|
|
77
77
|
- Cerra
|
|
78
78
|
- SaturnSwap
|
|
79
79
|
|
|
@@ -15,7 +15,7 @@ Charli3 Dendrite is a powerful Python SDK designed for seamless interaction with
|
|
|
15
15
|
|
|
16
16
|
## Key Features
|
|
17
17
|
|
|
18
|
-
- 🔄 Multi-DEX Support: Integrate with Minswap, MuesliSwap, Spectrum, SundaeSwap, VyFi, GeniusYield, Splash, and WingRiders
|
|
18
|
+
- 🔄 Multi-DEX Support: Integrate with CSwap, Minswap, MuesliSwap, Spectrum, SundaeSwap, VyFi, GeniusYield, Splash, and WingRiders
|
|
19
19
|
- 💧 Liquidity Pool Data: Fetch and analyze pool information across different DEXs
|
|
20
20
|
- 💱 Swap Operations: Execute token swaps with ease
|
|
21
21
|
- 🧩 Flexible Asset Handling: Manage various asset types and pool states efficiently
|
|
@@ -44,6 +44,7 @@ Charli3 Dendrite currently supports the following Cardano DEXs:
|
|
|
44
44
|
- WingRiders
|
|
45
45
|
- GeniusYield
|
|
46
46
|
- Splash
|
|
47
|
+
- CSwap
|
|
47
48
|
|
|
48
49
|
### Deprecated DEXs
|
|
49
50
|
|
|
@@ -52,7 +53,6 @@ Charli3 Dendrite currently supports the following Cardano DEXs:
|
|
|
52
53
|
### Not Yet Implemented
|
|
53
54
|
|
|
54
55
|
- CardanoSwaps
|
|
55
|
-
- CSwap
|
|
56
56
|
- Cerra
|
|
57
57
|
- SaturnSwap
|
|
58
58
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "charli3_dendrite"
|
|
3
|
-
version = "1.1.3-
|
|
3
|
+
version = "1.1.3-dev2"
|
|
4
4
|
description = ""
|
|
5
5
|
authors = ["Elder Millenial <eldermillenial@protonmail.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -49,7 +49,7 @@ module = ["pycardano", "pycardano.*", "dotenv", "nox"]
|
|
|
49
49
|
ignore_missing_imports = true
|
|
50
50
|
|
|
51
51
|
[tool.bumpversion]
|
|
52
|
-
current_version = "1.1.3-
|
|
52
|
+
current_version = "1.1.3-dev2"
|
|
53
53
|
parse = """(?x)
|
|
54
54
|
(?P<major>\\d+)\\.
|
|
55
55
|
(?P<minor>\\d+)\\.
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/cswap.py
RENAMED
|
@@ -53,7 +53,7 @@ class CSwapOrderDatum(OrderDatum):
|
|
|
53
53
|
address: PlutusFullAddress # Field 0: Complex address structure
|
|
54
54
|
target_assets: List[List[Union[bytes, int]]]
|
|
55
55
|
input_assets: List[List[Union[bytes, int]]]
|
|
56
|
-
|
|
56
|
+
otype: Union[CSwapOrderSwapType | CSwapOrderZapInType | CSwapOrderZapOutType]
|
|
57
57
|
slippage: int = 50
|
|
58
58
|
platform_fee: int = 15
|
|
59
59
|
|
|
@@ -104,7 +104,7 @@ class CSwapOrderDatum(OrderDatum):
|
|
|
104
104
|
address=full_address,
|
|
105
105
|
target_assets=target_assets,
|
|
106
106
|
input_assets=input_assets,
|
|
107
|
-
|
|
107
|
+
otype=CSwapOrderSwapType(),
|
|
108
108
|
slippage=50, # 0.5% default slippage
|
|
109
109
|
platform_fee=15, # 0.15% platform fee
|
|
110
110
|
)
|
|
@@ -0,0 +1,946 @@
|
|
|
1
|
+
"""Djed/Shen Stablecoin Order Book Module.
|
|
2
|
+
|
|
3
|
+
This module provides order book functionality for Djed (collateralized stablecoin)
|
|
4
|
+
and Shen (liquidity token) operations, following the exact patterns established
|
|
5
|
+
by the GeniusYield implementation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Union
|
|
11
|
+
|
|
12
|
+
from pycardano import Address
|
|
13
|
+
from pycardano import PlutusData
|
|
14
|
+
from pycardano import PlutusV1Script
|
|
15
|
+
from pycardano import PlutusV2Script
|
|
16
|
+
from pycardano import Redeemer
|
|
17
|
+
from pycardano import TransactionBuilder
|
|
18
|
+
from pycardano import TransactionId
|
|
19
|
+
from pycardano import TransactionInput
|
|
20
|
+
from pycardano import TransactionOutput
|
|
21
|
+
from pycardano import UTxO
|
|
22
|
+
|
|
23
|
+
from charli3_dendrite.backend import get_backend
|
|
24
|
+
from charli3_dendrite.dataclasses.datums import OrderDatum
|
|
25
|
+
from charli3_dendrite.dataclasses.datums import PlutusFullAddress
|
|
26
|
+
from charli3_dendrite.dataclasses.models import Assets
|
|
27
|
+
from charli3_dendrite.dataclasses.models import OrderType
|
|
28
|
+
from charli3_dendrite.dataclasses.models import PoolSelector
|
|
29
|
+
from charli3_dendrite.dexs.ob.ob_base import AbstractOrderBookState
|
|
30
|
+
from charli3_dendrite.dexs.ob.ob_base import AbstractOrderState
|
|
31
|
+
from charli3_dendrite.dexs.ob.ob_base import BuyOrderBook
|
|
32
|
+
from charli3_dendrite.dexs.ob.ob_base import OrderBookOrder
|
|
33
|
+
from charli3_dendrite.dexs.ob.ob_base import SellOrderBook
|
|
34
|
+
from charli3_dendrite.utility import DjedRational
|
|
35
|
+
from charli3_dendrite.utility import asset_to_value
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class DjedRationalDatum(PlutusData):
|
|
40
|
+
"""Plutus-compatible rational number for on-chain data."""
|
|
41
|
+
|
|
42
|
+
CONSTR_ID = 0
|
|
43
|
+
numerator: int
|
|
44
|
+
denominator: int
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class DjedMintAction(PlutusData):
|
|
49
|
+
"""Djed minting action in order datum."""
|
|
50
|
+
|
|
51
|
+
CONSTR_ID = 0
|
|
52
|
+
djed_amount: int
|
|
53
|
+
ada_amount: int
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class DjedBurnAction(PlutusData):
|
|
58
|
+
"""Djed burning action in order datum."""
|
|
59
|
+
|
|
60
|
+
CONSTR_ID = 1
|
|
61
|
+
djed_amount: int
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class ShenMintAction(PlutusData):
|
|
66
|
+
"""Shen minting action in order datum."""
|
|
67
|
+
|
|
68
|
+
CONSTR_ID = 2
|
|
69
|
+
shen_amount: int
|
|
70
|
+
ada_amount: int
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class ShenBurnAction(PlutusData):
|
|
75
|
+
"""Shen burning action in order datum."""
|
|
76
|
+
|
|
77
|
+
CONSTR_ID = 3
|
|
78
|
+
shen_amount: int
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class DjedOrderDatum(OrderDatum):
|
|
83
|
+
"""Djed/Shen order datum structure (following existing OrderDatum pattern)."""
|
|
84
|
+
|
|
85
|
+
CONSTR_ID = 0
|
|
86
|
+
action: Union[DjedMintAction, DjedBurnAction, ShenMintAction, ShenBurnAction]
|
|
87
|
+
owner_address: PlutusFullAddress
|
|
88
|
+
oracle_rate: DjedRationalDatum
|
|
89
|
+
creation_time: int
|
|
90
|
+
order_nft: bytes
|
|
91
|
+
|
|
92
|
+
def pool_pair(self) -> Assets | None:
|
|
93
|
+
"""Return the asset pair for this order (required by OrderDatum interface)."""
|
|
94
|
+
if isinstance(self.action, (DjedMintAction, DjedBurnAction)):
|
|
95
|
+
# Djed <-> ADA pair (placeholder policy IDs)
|
|
96
|
+
return Assets(lovelace=0) + Assets(**{"djed_policy_id.djed_token_name": 0})
|
|
97
|
+
else: # Shen operations
|
|
98
|
+
# Shen <-> ADA pair (placeholder policy IDs)
|
|
99
|
+
return Assets(lovelace=0) + Assets(**{"shen_policy_id.shen_token_name": 0})
|
|
100
|
+
|
|
101
|
+
def address_source(self) -> str | None:
|
|
102
|
+
"""Source address (required by OrderDatum interface)."""
|
|
103
|
+
return self.owner_address.to_address().encode("bech32")
|
|
104
|
+
|
|
105
|
+
def requested_amount(self) -> Assets:
|
|
106
|
+
"""Return the requested amount for this order (required by OrderDatum interface)."""
|
|
107
|
+
if isinstance(self.action, DjedMintAction):
|
|
108
|
+
return Assets(**{"djed_policy_id.djed_token_name": self.action.djed_amount})
|
|
109
|
+
elif isinstance(self.action, DjedBurnAction):
|
|
110
|
+
# For burn, calculate ADA amount based on oracle rate
|
|
111
|
+
oracle_rate = DjedRational(
|
|
112
|
+
self.oracle_rate.numerator, self.oracle_rate.denominator
|
|
113
|
+
)
|
|
114
|
+
ada_amount = (
|
|
115
|
+
oracle_rate.invert().mul(DjedRational(self.action.djed_amount)).to_int()
|
|
116
|
+
)
|
|
117
|
+
return Assets(lovelace=ada_amount)
|
|
118
|
+
elif isinstance(self.action, ShenMintAction):
|
|
119
|
+
return Assets(**{"shen_policy_id.shen_token_name": self.action.shen_amount})
|
|
120
|
+
else: # ShenBurnAction
|
|
121
|
+
# For Shen burn, calculation is more complex and requires pool state
|
|
122
|
+
return Assets(lovelace=self.action.shen_amount) # Placeholder
|
|
123
|
+
|
|
124
|
+
def order_type(self) -> OrderType | None:
|
|
125
|
+
"""Order type classification (required by OrderDatum interface)."""
|
|
126
|
+
if isinstance(self.action, (DjedMintAction, ShenMintAction)):
|
|
127
|
+
return OrderType.deposit # Minting = deposit operation
|
|
128
|
+
else:
|
|
129
|
+
return OrderType.swap # Burning = swap operation
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# === SHARED BASE CLASS FOR COMMON FUNCTIONALITY ===
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class DjedShenOrderStateBase(AbstractOrderState):
|
|
136
|
+
"""Base class for Djed/Shen order states sharing common functionality.
|
|
137
|
+
|
|
138
|
+
Reduces code duplication between Djed and Shen implementations by providing
|
|
139
|
+
shared methods that follow the exact GeniusYield pattern.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
tx_hash: str
|
|
143
|
+
tx_index: int
|
|
144
|
+
datum_cbor: str
|
|
145
|
+
datum_hash: str
|
|
146
|
+
inactive: bool = False
|
|
147
|
+
|
|
148
|
+
_batcher_fee: Assets = Assets(lovelace=2_000_000) # 2 ADA operator fee
|
|
149
|
+
_datum_parsed: PlutusData | None = None
|
|
150
|
+
|
|
151
|
+
@classmethod
|
|
152
|
+
def dex_policy(cls) -> list[str] | None:
|
|
153
|
+
"""Djed/Shen order NFT policy IDs (following GeniusYield pattern)."""
|
|
154
|
+
return [
|
|
155
|
+
"djed_order_policy_mainnet_placeholder", # Replace with actual policy
|
|
156
|
+
"djed_order_policy_preprod_placeholder", # Replace with actual policy
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def volume_fee(self) -> float:
|
|
161
|
+
"""Fee percentage for operations (following GeniusYield pattern)."""
|
|
162
|
+
return 150 # 1.5% in basis points
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def reference_utxo(self) -> UTxO | None:
|
|
166
|
+
"""Get reference UTxO for script validation (following GeniusYield pattern)."""
|
|
167
|
+
order_info = get_backend().get_pool_in_tx(
|
|
168
|
+
self.tx_hash,
|
|
169
|
+
assets=[self.dex_nft.unit()],
|
|
170
|
+
addresses=self.pool_selector().addresses,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
script = get_backend().get_script_from_address(
|
|
174
|
+
Address.decode(order_info[0].address),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
return UTxO(
|
|
178
|
+
input=TransactionInput(
|
|
179
|
+
TransactionId(bytes.fromhex(script.tx_hash)),
|
|
180
|
+
index=script.tx_index,
|
|
181
|
+
),
|
|
182
|
+
output=TransactionOutput(
|
|
183
|
+
address=script.address,
|
|
184
|
+
amount=asset_to_value(script.assets),
|
|
185
|
+
script=PlutusV2Script(bytes.fromhex(script.script)),
|
|
186
|
+
),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def _get_pool_utxo(self) -> UTxO:
|
|
190
|
+
"""Get pool UTxO using backend (shared by both Djed and Shen)."""
|
|
191
|
+
pool_utxos = get_backend().get_pool_utxos(
|
|
192
|
+
addresses=["djed_pool_address_placeholder"], # Replace with actual
|
|
193
|
+
assets=["djed_pool_nft_placeholder"], # Replace with actual
|
|
194
|
+
limit=1,
|
|
195
|
+
historical=False,
|
|
196
|
+
)
|
|
197
|
+
if not pool_utxos:
|
|
198
|
+
raise RuntimeError("Pool UTxO not found")
|
|
199
|
+
|
|
200
|
+
pool_info = pool_utxos[0]
|
|
201
|
+
return UTxO(
|
|
202
|
+
input=TransactionInput(
|
|
203
|
+
TransactionId(bytes.fromhex(pool_info.tx_hash)),
|
|
204
|
+
index=pool_info.tx_index,
|
|
205
|
+
),
|
|
206
|
+
output=TransactionOutput(
|
|
207
|
+
address=Address.decode(pool_info.address),
|
|
208
|
+
amount=asset_to_value(pool_info.assets),
|
|
209
|
+
),
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
def _get_oracle_utxo(self) -> UTxO:
|
|
213
|
+
"""Get oracle UTxO using backend (shared by both Djed and Shen)."""
|
|
214
|
+
oracle_utxos = get_backend().get_pool_utxos(
|
|
215
|
+
addresses=["djed_oracle_address_placeholder"], # Replace with actual
|
|
216
|
+
assets=["djed_oracle_nft_placeholder"], # Replace with actual
|
|
217
|
+
limit=1,
|
|
218
|
+
historical=False,
|
|
219
|
+
)
|
|
220
|
+
if not oracle_utxos:
|
|
221
|
+
raise RuntimeError("Oracle UTxO not found")
|
|
222
|
+
|
|
223
|
+
oracle_info = oracle_utxos[0]
|
|
224
|
+
return UTxO(
|
|
225
|
+
input=TransactionInput(
|
|
226
|
+
TransactionId(bytes.fromhex(oracle_info.tx_hash)),
|
|
227
|
+
index=oracle_info.tx_index,
|
|
228
|
+
),
|
|
229
|
+
output=TransactionOutput(
|
|
230
|
+
address=Address.decode(oracle_info.address),
|
|
231
|
+
amount=asset_to_value(oracle_info.assets),
|
|
232
|
+
),
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
@classmethod
|
|
236
|
+
def post_init(cls, values: dict[str, ...]):
|
|
237
|
+
"""Post initialization validation (shared logic)."""
|
|
238
|
+
super().post_init(values)
|
|
239
|
+
|
|
240
|
+
# Parse and validate order datum
|
|
241
|
+
try:
|
|
242
|
+
datum = cls.order_datum_class().from_cbor(values["datum_cbor"])
|
|
243
|
+
|
|
244
|
+
# Check if order is expired (3-minute TTL)
|
|
245
|
+
current_time = int(time.time())
|
|
246
|
+
if current_time > datum.creation_time + 180: # 3 minutes
|
|
247
|
+
values["inactive"] = True
|
|
248
|
+
|
|
249
|
+
except Exception as e:
|
|
250
|
+
values["inactive"] = True
|
|
251
|
+
|
|
252
|
+
@classmethod
|
|
253
|
+
def order_selector(cls) -> list[str]:
|
|
254
|
+
"""Order contract addresses (shared)."""
|
|
255
|
+
return [
|
|
256
|
+
"addr1_djed_order_mainnet_placeholder", # Replace with actual
|
|
257
|
+
"addr_test1_djed_order_preprod_placeholder", # Replace with actual
|
|
258
|
+
]
|
|
259
|
+
|
|
260
|
+
@classmethod
|
|
261
|
+
def pool_selector(cls) -> PoolSelector:
|
|
262
|
+
"""Pool selection for Djed/Shen orders (shared)."""
|
|
263
|
+
return PoolSelector(
|
|
264
|
+
addresses=[
|
|
265
|
+
"addr1_djed_pool_mainnet_placeholder", # Replace with actual
|
|
266
|
+
"addr_test1_djed_pool_preprod_placeholder", # Replace with actual
|
|
267
|
+
],
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
@property
|
|
271
|
+
def swap_forward(self) -> bool:
|
|
272
|
+
"""Returns if swap forwarding is enabled."""
|
|
273
|
+
return False
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def stake_address(self) -> Address | None:
|
|
277
|
+
"""Return the staking address."""
|
|
278
|
+
return None
|
|
279
|
+
|
|
280
|
+
@classmethod
|
|
281
|
+
def order_datum_class(cls) -> type[PlutusData]:
|
|
282
|
+
"""Returns data class used for handling order datums."""
|
|
283
|
+
return DjedOrderDatum
|
|
284
|
+
|
|
285
|
+
@classmethod
|
|
286
|
+
def default_script_class(cls) -> type[PlutusV1Script] | type[PlutusV2Script]:
|
|
287
|
+
"""Get default script class."""
|
|
288
|
+
return PlutusV2Script
|
|
289
|
+
|
|
290
|
+
@property
|
|
291
|
+
def pool_id(self) -> str:
|
|
292
|
+
"""A unique identifier for the pool or ob."""
|
|
293
|
+
return "Djed"
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
# === DJED-SPECIFIC ORDER STATE ===
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class DjedOrderState(DjedShenOrderStateBase):
|
|
300
|
+
"""Djed order state handling Djed mint/burn operations.
|
|
301
|
+
|
|
302
|
+
Inherits common functionality from DjedShenOrderStateBase and implements
|
|
303
|
+
Djed-specific pricing and transaction logic.
|
|
304
|
+
"""
|
|
305
|
+
|
|
306
|
+
@classmethod
|
|
307
|
+
def dex(cls) -> str:
|
|
308
|
+
"""Official dex name."""
|
|
309
|
+
return "Djed"
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def price(self) -> tuple[int, int]:
|
|
313
|
+
"""Price for Djed operations (ADA per Djed)."""
|
|
314
|
+
oracle_rate_datum = self.order_datum.oracle_rate
|
|
315
|
+
oracle_rate = DjedRational(
|
|
316
|
+
oracle_rate_datum.numerator, oracle_rate_datum.denominator
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
if isinstance(self.order_datum.action, DjedMintAction):
|
|
320
|
+
# Djed mint: ADA per Djed (includes fees)
|
|
321
|
+
base_rate = oracle_rate.invert() # Convert Djed/ADA to ADA/Djed
|
|
322
|
+
fee_multiplier = DjedRational(1015, 1000) # 1.5% fee
|
|
323
|
+
final_rate = base_rate.mul(fee_multiplier)
|
|
324
|
+
return final_rate.to_tuple()
|
|
325
|
+
else: # DjedBurnAction
|
|
326
|
+
# Djed burn: ADA per Djed (after fees)
|
|
327
|
+
base_rate = oracle_rate.invert()
|
|
328
|
+
fee_multiplier = DjedRational(985, 1000) # 1.5% fee deduction
|
|
329
|
+
final_rate = base_rate.mul(fee_multiplier)
|
|
330
|
+
return final_rate.to_tuple()
|
|
331
|
+
|
|
332
|
+
@property
|
|
333
|
+
def available(self) -> Assets:
|
|
334
|
+
"""Available amount for Djed orders."""
|
|
335
|
+
if isinstance(self.order_datum.action, DjedMintAction):
|
|
336
|
+
return Assets(
|
|
337
|
+
**{
|
|
338
|
+
"djed_policy_id.djed_token_name": self.order_datum.action.djed_amount
|
|
339
|
+
}
|
|
340
|
+
)
|
|
341
|
+
else: # DjedBurnAction
|
|
342
|
+
# Calculate ADA to return based on current oracle rate
|
|
343
|
+
ada_amount = self._calculate_ada_return(self.order_datum.action.djed_amount)
|
|
344
|
+
return Assets(lovelace=ada_amount)
|
|
345
|
+
|
|
346
|
+
def _calculate_ada_return(self, djed_amount: int) -> int:
|
|
347
|
+
"""Calculate ADA to return for Djed burning."""
|
|
348
|
+
oracle_rate_datum = self.order_datum.oracle_rate
|
|
349
|
+
oracle_rate = DjedRational(
|
|
350
|
+
oracle_rate_datum.numerator, oracle_rate_datum.denominator
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
base_rate = oracle_rate.invert() # ADA per Djed
|
|
354
|
+
fee_multiplier = DjedRational(985, 1000) # 1.5% fee deduction
|
|
355
|
+
final_rate = base_rate.mul(fee_multiplier)
|
|
356
|
+
|
|
357
|
+
djed_rational = DjedRational(djed_amount, 1)
|
|
358
|
+
return final_rate.mul(djed_rational).to_int("ROUND_DOWN")
|
|
359
|
+
|
|
360
|
+
def swap_utxo(
|
|
361
|
+
self,
|
|
362
|
+
address_source: Address,
|
|
363
|
+
in_assets: Assets,
|
|
364
|
+
out_assets: Assets,
|
|
365
|
+
tx_builder: TransactionBuilder,
|
|
366
|
+
extra_assets: Assets | None = None,
|
|
367
|
+
address_target: Address | None = None,
|
|
368
|
+
datum_target: PlutusData | None = None,
|
|
369
|
+
) -> tuple[TransactionOutput | None, PlutusData]:
|
|
370
|
+
"""Build transaction for Djed order processing."""
|
|
371
|
+
|
|
372
|
+
# Get reference UTxOs (using shared methods)
|
|
373
|
+
pool_utxo = self._get_pool_utxo()
|
|
374
|
+
oracle_utxo = self._get_oracle_utxo()
|
|
375
|
+
|
|
376
|
+
# Add order UTxO as script input (following GeniusYield pattern)
|
|
377
|
+
assets = self.assets + Assets(**{self.dex_nft.unit(): 1})
|
|
378
|
+
order_utxo = UTxO(
|
|
379
|
+
TransactionInput(
|
|
380
|
+
transaction_id=TransactionId(bytes.fromhex(self.tx_hash)),
|
|
381
|
+
index=self.tx_index,
|
|
382
|
+
),
|
|
383
|
+
output=TransactionOutput(
|
|
384
|
+
address=Address.decode(self.address),
|
|
385
|
+
amount=asset_to_value(assets),
|
|
386
|
+
datum_hash=self.order_datum.hash(),
|
|
387
|
+
),
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
# Add script input with redeemer
|
|
391
|
+
if out_assets.quantity() < self.available.quantity():
|
|
392
|
+
redeemer = Redeemer(self._get_partial_redeemer(out_assets))
|
|
393
|
+
else:
|
|
394
|
+
redeemer = Redeemer(self._get_complete_redeemer())
|
|
395
|
+
|
|
396
|
+
tx_builder.add_script_input(
|
|
397
|
+
utxo=order_utxo,
|
|
398
|
+
script=self.reference_utxo,
|
|
399
|
+
redeemer=redeemer,
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
# Add reference inputs
|
|
403
|
+
tx_builder.reference_inputs.add(pool_utxo)
|
|
404
|
+
tx_builder.reference_inputs.add(oracle_utxo)
|
|
405
|
+
|
|
406
|
+
# Process based on Djed operation type
|
|
407
|
+
if isinstance(self.order_datum.action, DjedMintAction):
|
|
408
|
+
return self._process_djed_mint(tx_builder, in_assets, out_assets, pool_utxo)
|
|
409
|
+
else: # DjedBurnAction
|
|
410
|
+
return self._process_djed_burn(tx_builder, in_assets, out_assets, pool_utxo)
|
|
411
|
+
|
|
412
|
+
def _process_djed_mint(
|
|
413
|
+
self,
|
|
414
|
+
tx_builder: TransactionBuilder,
|
|
415
|
+
in_assets: Assets,
|
|
416
|
+
out_assets: Assets,
|
|
417
|
+
pool_utxo: UTxO,
|
|
418
|
+
) -> tuple[TransactionOutput | None, PlutusData]:
|
|
419
|
+
"""Process Djed minting order."""
|
|
420
|
+
# Update order datum if partial fill
|
|
421
|
+
order_datum = self.order_datum_class().from_cbor(self.order_datum.to_cbor())
|
|
422
|
+
order_datum.action.djed_amount -= out_assets.quantity()
|
|
423
|
+
|
|
424
|
+
# Update pool state with new Djed tokens
|
|
425
|
+
updated_assets = self.assets.copy()
|
|
426
|
+
updated_assets.root[in_assets.unit()] += in_assets.quantity()
|
|
427
|
+
updated_assets.root[out_assets.unit()] -= out_assets.quantity()
|
|
428
|
+
updated_assets += self._batcher_fee
|
|
429
|
+
|
|
430
|
+
if out_assets.quantity() < self.available.quantity():
|
|
431
|
+
# Partial fill - return updated order
|
|
432
|
+
txo = TransactionOutput(
|
|
433
|
+
address=Address.decode(self.address),
|
|
434
|
+
amount=asset_to_value(updated_assets),
|
|
435
|
+
datum_hash=order_datum.hash(),
|
|
436
|
+
)
|
|
437
|
+
else:
|
|
438
|
+
# Complete fill - pay user and close order
|
|
439
|
+
# Burn the beacon token
|
|
440
|
+
tx_builder.add_minting_script(
|
|
441
|
+
script=self.reference_utxo,
|
|
442
|
+
redeemer=Redeemer(PlutusData()), # Cancel redeemer placeholder
|
|
443
|
+
)
|
|
444
|
+
if tx_builder.mint is None:
|
|
445
|
+
tx_builder.mint = asset_to_value(
|
|
446
|
+
Assets(**{self.dex_nft.unit(): -1}),
|
|
447
|
+
).multi_asset
|
|
448
|
+
else:
|
|
449
|
+
tx_builder.mint += asset_to_value(
|
|
450
|
+
Assets(**{self.dex_nft.unit(): -1}),
|
|
451
|
+
).multi_asset
|
|
452
|
+
|
|
453
|
+
# Pay Djed tokens to user
|
|
454
|
+
payment_assets = Assets(**{out_assets.unit(): out_assets.quantity()})
|
|
455
|
+
payment_assets += Assets(lovelace=2_000_000) # Min ADA
|
|
456
|
+
|
|
457
|
+
txo = TransactionOutput(
|
|
458
|
+
address=order_datum.owner_address.to_address(),
|
|
459
|
+
amount=asset_to_value(payment_assets),
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
tx_builder.datums.update({order_datum.hash(): order_datum})
|
|
463
|
+
return txo, order_datum
|
|
464
|
+
|
|
465
|
+
def _process_djed_burn(
|
|
466
|
+
self,
|
|
467
|
+
tx_builder: TransactionBuilder,
|
|
468
|
+
in_assets: Assets,
|
|
469
|
+
out_assets: Assets,
|
|
470
|
+
pool_utxo: UTxO,
|
|
471
|
+
) -> tuple[TransactionOutput | None, PlutusData]:
|
|
472
|
+
"""Process Djed burning order."""
|
|
473
|
+
# Similar to mint but burning Djed for ADA
|
|
474
|
+
order_datum = self.order_datum_class().from_cbor(self.order_datum.to_cbor())
|
|
475
|
+
order_datum.action.djed_amount -= in_assets.quantity()
|
|
476
|
+
|
|
477
|
+
# Update pool state
|
|
478
|
+
updated_assets = self.assets.copy()
|
|
479
|
+
updated_assets.root[in_assets.unit()] -= in_assets.quantity()
|
|
480
|
+
updated_assets.root[out_assets.unit()] += out_assets.quantity()
|
|
481
|
+
updated_assets += self._batcher_fee
|
|
482
|
+
|
|
483
|
+
if in_assets.quantity() < self.available.quantity():
|
|
484
|
+
# Partial fill
|
|
485
|
+
txo = TransactionOutput(
|
|
486
|
+
address=Address.decode(self.address),
|
|
487
|
+
amount=asset_to_value(updated_assets),
|
|
488
|
+
datum_hash=order_datum.hash(),
|
|
489
|
+
)
|
|
490
|
+
else:
|
|
491
|
+
# Complete fill - close order and pay ADA
|
|
492
|
+
# Burn the beacon token
|
|
493
|
+
tx_builder.add_minting_script(
|
|
494
|
+
script=self.reference_utxo,
|
|
495
|
+
redeemer=Redeemer(PlutusData()),
|
|
496
|
+
)
|
|
497
|
+
if tx_builder.mint is None:
|
|
498
|
+
tx_builder.mint = asset_to_value(
|
|
499
|
+
Assets(**{self.dex_nft.unit(): -1}),
|
|
500
|
+
).multi_asset
|
|
501
|
+
else:
|
|
502
|
+
tx_builder.mint += asset_to_value(
|
|
503
|
+
Assets(**{self.dex_nft.unit(): -1}),
|
|
504
|
+
).multi_asset
|
|
505
|
+
|
|
506
|
+
# Pay ADA to user
|
|
507
|
+
payment_assets = Assets(lovelace=out_assets.quantity())
|
|
508
|
+
|
|
509
|
+
txo = TransactionOutput(
|
|
510
|
+
address=order_datum.owner_address.to_address(),
|
|
511
|
+
amount=asset_to_value(payment_assets),
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
tx_builder.datums.update({order_datum.hash(): order_datum})
|
|
515
|
+
return txo, order_datum
|
|
516
|
+
|
|
517
|
+
def _get_partial_redeemer(self, out_assets: Assets) -> PlutusData:
|
|
518
|
+
"""Get redeemer for partial order processing."""
|
|
519
|
+
return PlutusData() # Placeholder - implement based on actual contract
|
|
520
|
+
|
|
521
|
+
def _get_complete_redeemer(self) -> PlutusData:
|
|
522
|
+
"""Get redeemer for complete order processing."""
|
|
523
|
+
return PlutusData() # Placeholder - implement based on actual contract
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
# === SHEN-SPECIFIC ORDER STATE ===
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
class ShenOrderState(DjedShenOrderStateBase):
|
|
530
|
+
"""Shen order state handling Shen mint/burn operations.
|
|
531
|
+
|
|
532
|
+
Inherits common functionality from DjedShenOrderStateBase and implements
|
|
533
|
+
Shen-specific pricing and transaction logic. Shen pricing is more complex
|
|
534
|
+
as it depends on pool reserves and collateral ratios.
|
|
535
|
+
"""
|
|
536
|
+
|
|
537
|
+
@classmethod
|
|
538
|
+
def dex(cls) -> str:
|
|
539
|
+
"""Official dex name."""
|
|
540
|
+
return "Shen"
|
|
541
|
+
|
|
542
|
+
@property
|
|
543
|
+
def price(self) -> tuple[int, int]:
|
|
544
|
+
"""Price for Shen operations (more complex - requires pool state)."""
|
|
545
|
+
return self._calculate_shen_price()
|
|
546
|
+
|
|
547
|
+
@property
|
|
548
|
+
def available(self) -> Assets:
|
|
549
|
+
"""Available amount for Shen orders."""
|
|
550
|
+
if isinstance(self.order_datum.action, ShenMintAction):
|
|
551
|
+
return Assets(
|
|
552
|
+
**{
|
|
553
|
+
"shen_policy_id.shen_token_name": self.order_datum.action.shen_amount
|
|
554
|
+
}
|
|
555
|
+
)
|
|
556
|
+
else: # ShenBurnAction
|
|
557
|
+
ada_amount = self._calculate_shen_ada_return(
|
|
558
|
+
self.order_datum.action.shen_amount
|
|
559
|
+
)
|
|
560
|
+
return Assets(lovelace=ada_amount)
|
|
561
|
+
|
|
562
|
+
def _calculate_shen_price(self) -> tuple[int, int]:
|
|
563
|
+
"""Calculate Shen price based on current pool state.
|
|
564
|
+
|
|
565
|
+
Shen price is determined by the excess ADA reserves beyond what's needed
|
|
566
|
+
to back the Djed tokens. This is more complex than Djed pricing.
|
|
567
|
+
"""
|
|
568
|
+
try:
|
|
569
|
+
# Get current pool state
|
|
570
|
+
pool_utxo = self._get_pool_utxo()
|
|
571
|
+
# TODO: Parse pool datum to get reserve amounts and Djed supply
|
|
572
|
+
# For now, return placeholder
|
|
573
|
+
return (1, 1) # ADA per Shen - placeholder
|
|
574
|
+
except Exception:
|
|
575
|
+
return (1, 1) # Fallback price
|
|
576
|
+
|
|
577
|
+
def _calculate_shen_ada_return(self, shen_amount: int) -> int:
|
|
578
|
+
"""Calculate ADA to return for Shen burning.
|
|
579
|
+
|
|
580
|
+
Based on Shen's share of excess reserves beyond Djed backing.
|
|
581
|
+
"""
|
|
582
|
+
try:
|
|
583
|
+
# Get current pool state and calculate Shen's share
|
|
584
|
+
# TODO: Implement complex calculation based on pool reserves
|
|
585
|
+
return shen_amount # Placeholder - 1:1 ratio
|
|
586
|
+
except Exception:
|
|
587
|
+
return shen_amount # Fallback
|
|
588
|
+
|
|
589
|
+
def swap_utxo(
|
|
590
|
+
self,
|
|
591
|
+
address_source: Address,
|
|
592
|
+
in_assets: Assets,
|
|
593
|
+
out_assets: Assets,
|
|
594
|
+
tx_builder: TransactionBuilder,
|
|
595
|
+
extra_assets: Assets | None = None,
|
|
596
|
+
address_target: Address | None = None,
|
|
597
|
+
datum_target: PlutusData | None = None,
|
|
598
|
+
) -> tuple[TransactionOutput | None, PlutusData]:
|
|
599
|
+
"""Build transaction for Shen order processing."""
|
|
600
|
+
|
|
601
|
+
# Get reference UTxOs (using shared methods)
|
|
602
|
+
pool_utxo = self._get_pool_utxo()
|
|
603
|
+
oracle_utxo = self._get_oracle_utxo()
|
|
604
|
+
|
|
605
|
+
# Similar structure to Djed but with Shen-specific logic
|
|
606
|
+
assets = self.assets + Assets(**{self.dex_nft.unit(): 1})
|
|
607
|
+
order_utxo = UTxO(
|
|
608
|
+
TransactionInput(
|
|
609
|
+
transaction_id=TransactionId(bytes.fromhex(self.tx_hash)),
|
|
610
|
+
index=self.tx_index,
|
|
611
|
+
),
|
|
612
|
+
output=TransactionOutput(
|
|
613
|
+
address=Address.decode(self.address),
|
|
614
|
+
amount=asset_to_value(assets),
|
|
615
|
+
datum_hash=self.order_datum.hash(),
|
|
616
|
+
),
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
# Add script input with redeemer
|
|
620
|
+
if out_assets.quantity() < self.available.quantity():
|
|
621
|
+
redeemer = Redeemer(PlutusData()) # Partial redeemer
|
|
622
|
+
else:
|
|
623
|
+
redeemer = Redeemer(PlutusData()) # Complete redeemer
|
|
624
|
+
|
|
625
|
+
tx_builder.add_script_input(
|
|
626
|
+
utxo=order_utxo,
|
|
627
|
+
script=self.reference_utxo,
|
|
628
|
+
redeemer=redeemer,
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
# Add reference inputs
|
|
632
|
+
tx_builder.reference_inputs.add(pool_utxo)
|
|
633
|
+
tx_builder.reference_inputs.add(oracle_utxo)
|
|
634
|
+
|
|
635
|
+
# Process based on Shen operation type
|
|
636
|
+
if isinstance(self.order_datum.action, ShenMintAction):
|
|
637
|
+
return self._process_shen_mint(tx_builder, in_assets, out_assets, pool_utxo)
|
|
638
|
+
else: # ShenBurnAction
|
|
639
|
+
return self._process_shen_burn(tx_builder, in_assets, out_assets, pool_utxo)
|
|
640
|
+
|
|
641
|
+
def _process_shen_mint(
|
|
642
|
+
self,
|
|
643
|
+
tx_builder: TransactionBuilder,
|
|
644
|
+
in_assets: Assets,
|
|
645
|
+
out_assets: Assets,
|
|
646
|
+
pool_utxo: UTxO,
|
|
647
|
+
) -> tuple[TransactionOutput | None, PlutusData]:
|
|
648
|
+
"""Process Shen minting order."""
|
|
649
|
+
# Similar to Djed mint but for Shen tokens
|
|
650
|
+
order_datum = self.order_datum_class().from_cbor(self.order_datum.to_cbor())
|
|
651
|
+
order_datum.action.shen_amount -= out_assets.quantity()
|
|
652
|
+
|
|
653
|
+
updated_assets = self.assets.copy()
|
|
654
|
+
updated_assets.root[in_assets.unit()] += in_assets.quantity()
|
|
655
|
+
updated_assets.root[out_assets.unit()] -= out_assets.quantity()
|
|
656
|
+
updated_assets += self._batcher_fee
|
|
657
|
+
|
|
658
|
+
if out_assets.quantity() < self.available.quantity():
|
|
659
|
+
txo = TransactionOutput(
|
|
660
|
+
address=Address.decode(self.address),
|
|
661
|
+
amount=asset_to_value(updated_assets),
|
|
662
|
+
datum_hash=order_datum.hash(),
|
|
663
|
+
)
|
|
664
|
+
else:
|
|
665
|
+
# Complete fill logic (similar to Djed)
|
|
666
|
+
tx_builder.add_minting_script(
|
|
667
|
+
script=self.reference_utxo,
|
|
668
|
+
redeemer=Redeemer(PlutusData()),
|
|
669
|
+
)
|
|
670
|
+
if tx_builder.mint is None:
|
|
671
|
+
tx_builder.mint = asset_to_value(
|
|
672
|
+
Assets(**{self.dex_nft.unit(): -1}),
|
|
673
|
+
).multi_asset
|
|
674
|
+
else:
|
|
675
|
+
tx_builder.mint += asset_to_value(
|
|
676
|
+
Assets(**{self.dex_nft.unit(): -1}),
|
|
677
|
+
).multi_asset
|
|
678
|
+
|
|
679
|
+
payment_assets = Assets(**{out_assets.unit(): out_assets.quantity()})
|
|
680
|
+
payment_assets += Assets(lovelace=2_000_000)
|
|
681
|
+
|
|
682
|
+
txo = TransactionOutput(
|
|
683
|
+
address=order_datum.owner_address.to_address(),
|
|
684
|
+
amount=asset_to_value(payment_assets),
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
tx_builder.datums.update({order_datum.hash(): order_datum})
|
|
688
|
+
return txo, order_datum
|
|
689
|
+
|
|
690
|
+
def _process_shen_burn(
|
|
691
|
+
self,
|
|
692
|
+
tx_builder: TransactionBuilder,
|
|
693
|
+
in_assets: Assets,
|
|
694
|
+
out_assets: Assets,
|
|
695
|
+
pool_utxo: UTxO,
|
|
696
|
+
) -> tuple[TransactionOutput | None, PlutusData]:
|
|
697
|
+
"""Process Shen burning order."""
|
|
698
|
+
# Similar to Djed burn but for Shen tokens
|
|
699
|
+
order_datum = self.order_datum_class().from_cbor(self.order_datum.to_cbor())
|
|
700
|
+
order_datum.action.shen_amount -= in_assets.quantity()
|
|
701
|
+
|
|
702
|
+
updated_assets = self.assets.copy()
|
|
703
|
+
updated_assets.root[in_assets.unit()] -= in_assets.quantity()
|
|
704
|
+
updated_assets.root[out_assets.unit()] += out_assets.quantity()
|
|
705
|
+
updated_assets += self._batcher_fee
|
|
706
|
+
|
|
707
|
+
if in_assets.quantity() < self.available.quantity():
|
|
708
|
+
txo = TransactionOutput(
|
|
709
|
+
address=Address.decode(self.address),
|
|
710
|
+
amount=asset_to_value(updated_assets),
|
|
711
|
+
datum_hash=order_datum.hash(),
|
|
712
|
+
)
|
|
713
|
+
else:
|
|
714
|
+
# Complete fill logic
|
|
715
|
+
tx_builder.add_minting_script(
|
|
716
|
+
script=self.reference_utxo,
|
|
717
|
+
redeemer=Redeemer(PlutusData()),
|
|
718
|
+
)
|
|
719
|
+
if tx_builder.mint is None:
|
|
720
|
+
tx_builder.mint = asset_to_value(
|
|
721
|
+
Assets(**{self.dex_nft.unit(): -1}),
|
|
722
|
+
).multi_asset
|
|
723
|
+
else:
|
|
724
|
+
tx_builder.mint += asset_to_value(
|
|
725
|
+
Assets(**{self.dex_nft.unit(): -1}),
|
|
726
|
+
).multi_asset
|
|
727
|
+
|
|
728
|
+
payment_assets = Assets(lovelace=out_assets.quantity())
|
|
729
|
+
|
|
730
|
+
txo = TransactionOutput(
|
|
731
|
+
address=order_datum.owner_address.to_address(),
|
|
732
|
+
amount=asset_to_value(payment_assets),
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
tx_builder.datums.update({order_datum.hash(): order_datum})
|
|
736
|
+
return txo, order_datum
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
# === SHARED ORDER BOOK BASE CLASS ===
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
class DjedShenOrderBookBase(AbstractOrderBookState):
|
|
743
|
+
"""Base class for Djed/Shen order books sharing common functionality."""
|
|
744
|
+
|
|
745
|
+
fee: int = 150 # 1.5% fee in basis points
|
|
746
|
+
_deposit: Assets = Assets(lovelace=2_000_000)
|
|
747
|
+
|
|
748
|
+
@classmethod
|
|
749
|
+
def order_selector(cls) -> list[str]:
|
|
750
|
+
"""Order selection information."""
|
|
751
|
+
return DjedShenOrderStateBase.order_selector()
|
|
752
|
+
|
|
753
|
+
@classmethod
|
|
754
|
+
def pool_selector(cls) -> PoolSelector:
|
|
755
|
+
"""Pool selection information."""
|
|
756
|
+
return DjedShenOrderStateBase.pool_selector()
|
|
757
|
+
|
|
758
|
+
@property
|
|
759
|
+
def swap_forward(self) -> bool:
|
|
760
|
+
"""Returns if swap forwarding is enabled."""
|
|
761
|
+
return True
|
|
762
|
+
|
|
763
|
+
@classmethod
|
|
764
|
+
def default_script_class(cls):
|
|
765
|
+
"""Get default script class."""
|
|
766
|
+
return DjedShenOrderStateBase.default_script_class()
|
|
767
|
+
|
|
768
|
+
@classmethod
|
|
769
|
+
def order_datum_class(cls):
|
|
770
|
+
"""Returns data class used for handling order datums."""
|
|
771
|
+
return DjedShenOrderStateBase.order_datum_class()
|
|
772
|
+
|
|
773
|
+
@property
|
|
774
|
+
def stake_address(self) -> Address | None:
|
|
775
|
+
"""Return the staking address."""
|
|
776
|
+
return None
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
# === DJED ORDER BOOK ===
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
class DjedOrderBook(DjedShenOrderBookBase):
|
|
783
|
+
"""Djed order book for Djed mint/burn operations."""
|
|
784
|
+
|
|
785
|
+
@classmethod
|
|
786
|
+
def get_book(
|
|
787
|
+
cls,
|
|
788
|
+
assets: Assets,
|
|
789
|
+
orders: list[DjedOrderState] | None = None,
|
|
790
|
+
) -> "DjedOrderBook":
|
|
791
|
+
"""Create Djed order book."""
|
|
792
|
+
if orders is None:
|
|
793
|
+
selector = DjedOrderState.pool_selector()
|
|
794
|
+
|
|
795
|
+
result = get_backend().get_pool_utxos(
|
|
796
|
+
limit=1000,
|
|
797
|
+
historical=False,
|
|
798
|
+
**selector.model_dump(),
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
# Filter for Djed orders only
|
|
802
|
+
orders = [
|
|
803
|
+
DjedOrderState.model_validate(r.model_dump())
|
|
804
|
+
for r in result
|
|
805
|
+
if cls._is_djed_order(r)
|
|
806
|
+
]
|
|
807
|
+
|
|
808
|
+
# Sort into buy (mint) and sell (burn) orders
|
|
809
|
+
buy_orders = [] # Djed mint orders
|
|
810
|
+
sell_orders = [] # Djed burn orders
|
|
811
|
+
|
|
812
|
+
for order in orders:
|
|
813
|
+
if order.inactive:
|
|
814
|
+
continue
|
|
815
|
+
|
|
816
|
+
price = order.price[0] / order.price[1]
|
|
817
|
+
o = OrderBookOrder(
|
|
818
|
+
price=price,
|
|
819
|
+
quantity=int(order.available.quantity()),
|
|
820
|
+
state=order,
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
if isinstance(order.order_datum.action, DjedMintAction):
|
|
824
|
+
buy_orders.append(o) # Mint = Buy
|
|
825
|
+
else: # DjedBurnAction
|
|
826
|
+
sell_orders.append(o) # Burn = Sell
|
|
827
|
+
|
|
828
|
+
ob = DjedOrderBook(
|
|
829
|
+
assets=assets,
|
|
830
|
+
plutus_v2=True,
|
|
831
|
+
block_time=int(time.time()),
|
|
832
|
+
block_index=0,
|
|
833
|
+
sell_book_full=SellOrderBook(sell_orders),
|
|
834
|
+
buy_book_full=BuyOrderBook(buy_orders),
|
|
835
|
+
)
|
|
836
|
+
|
|
837
|
+
# Limit orders per transaction (following GeniusYield pattern)
|
|
838
|
+
ob.sell_book_full = ob.sell_book_full[:3]
|
|
839
|
+
ob.buy_book_full = ob.buy_book_full[:3]
|
|
840
|
+
|
|
841
|
+
return ob
|
|
842
|
+
|
|
843
|
+
@classmethod
|
|
844
|
+
def dex(cls) -> str:
|
|
845
|
+
"""Official dex name."""
|
|
846
|
+
return "Djed"
|
|
847
|
+
|
|
848
|
+
@property
|
|
849
|
+
def pool_id(self) -> str:
|
|
850
|
+
"""A unique identifier for the pool or ob."""
|
|
851
|
+
return "Djed"
|
|
852
|
+
|
|
853
|
+
@classmethod
|
|
854
|
+
def _is_djed_order(cls, order_data) -> bool:
|
|
855
|
+
"""Check if order is a Djed order (not Shen)."""
|
|
856
|
+
try:
|
|
857
|
+
# Parse datum to check action type
|
|
858
|
+
datum = DjedOrderDatum.from_cbor(order_data.datum_cbor)
|
|
859
|
+
return isinstance(datum.action, (DjedMintAction, DjedBurnAction))
|
|
860
|
+
except Exception:
|
|
861
|
+
return False
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
# === SHEN ORDER BOOK ===
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
class ShenOrderBook(DjedShenOrderBookBase):
|
|
868
|
+
"""Shen order book for Shen mint/burn operations."""
|
|
869
|
+
|
|
870
|
+
@classmethod
|
|
871
|
+
def get_book(
|
|
872
|
+
cls,
|
|
873
|
+
assets: Assets,
|
|
874
|
+
orders: list[ShenOrderState] | None = None,
|
|
875
|
+
) -> "ShenOrderBook":
|
|
876
|
+
"""Create Shen order book."""
|
|
877
|
+
if orders is None:
|
|
878
|
+
selector = ShenOrderState.pool_selector()
|
|
879
|
+
|
|
880
|
+
result = get_backend().get_pool_utxos(
|
|
881
|
+
limit=1000,
|
|
882
|
+
historical=False,
|
|
883
|
+
**selector.model_dump(),
|
|
884
|
+
)
|
|
885
|
+
|
|
886
|
+
# Filter for Shen orders only
|
|
887
|
+
orders = [
|
|
888
|
+
ShenOrderState.model_validate(r.model_dump())
|
|
889
|
+
for r in result
|
|
890
|
+
if cls._is_shen_order(r)
|
|
891
|
+
]
|
|
892
|
+
|
|
893
|
+
# Sort into buy (mint) and sell (burn) orders
|
|
894
|
+
buy_orders = [] # Shen mint orders
|
|
895
|
+
sell_orders = [] # Shen burn orders
|
|
896
|
+
|
|
897
|
+
for order in orders:
|
|
898
|
+
if order.inactive:
|
|
899
|
+
continue
|
|
900
|
+
|
|
901
|
+
price = order.price[0] / order.price[1]
|
|
902
|
+
o = OrderBookOrder(
|
|
903
|
+
price=price,
|
|
904
|
+
quantity=int(order.available.quantity()),
|
|
905
|
+
state=order,
|
|
906
|
+
)
|
|
907
|
+
|
|
908
|
+
if isinstance(order.order_datum.action, ShenMintAction):
|
|
909
|
+
buy_orders.append(o) # Mint = Buy
|
|
910
|
+
else: # ShenBurnAction
|
|
911
|
+
sell_orders.append(o) # Burn = Sell
|
|
912
|
+
|
|
913
|
+
ob = ShenOrderBook(
|
|
914
|
+
assets=assets,
|
|
915
|
+
plutus_v2=True,
|
|
916
|
+
block_time=int(time.time()),
|
|
917
|
+
block_index=0,
|
|
918
|
+
sell_book_full=SellOrderBook(sell_orders),
|
|
919
|
+
buy_book_full=BuyOrderBook(buy_orders),
|
|
920
|
+
)
|
|
921
|
+
|
|
922
|
+
# Limit orders per transaction (following GeniusYield pattern)
|
|
923
|
+
ob.sell_book_full = ob.sell_book_full[:3]
|
|
924
|
+
ob.buy_book_full = ob.buy_book_full[:3]
|
|
925
|
+
|
|
926
|
+
return ob
|
|
927
|
+
|
|
928
|
+
@classmethod
|
|
929
|
+
def dex(cls) -> str:
|
|
930
|
+
"""Official dex name."""
|
|
931
|
+
return "Shen"
|
|
932
|
+
|
|
933
|
+
@property
|
|
934
|
+
def pool_id(self) -> str:
|
|
935
|
+
"""A unique identifier for the pool or ob."""
|
|
936
|
+
return "Shen"
|
|
937
|
+
|
|
938
|
+
@classmethod
|
|
939
|
+
def _is_shen_order(cls, order_data) -> bool:
|
|
940
|
+
"""Check if order is a Shen order (not Djed)."""
|
|
941
|
+
try:
|
|
942
|
+
# Parse datum to check action type
|
|
943
|
+
datum = DjedOrderDatum.from_cbor(order_data.datum_cbor)
|
|
944
|
+
return isinstance(datum.action, (ShenMintAction, ShenBurnAction))
|
|
945
|
+
except Exception:
|
|
946
|
+
return False
|
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/__init__.py
RENAMED
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/backend/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/backend/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/minswap.py
RENAMED
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/muesli.py
RENAMED
|
File without changes
|
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/splash.py
RENAMED
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/sundae.py
RENAMED
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/amm/vyfi.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/core/base.py
RENAMED
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/core/errors.py
RENAMED
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/ob/__init__.py
RENAMED
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/ob/axo.py
RENAMED
|
File without changes
|
|
File without changes
|
{charli3_dendrite-1.1.3.dev1 → charli3_dendrite-1.1.3.dev2}/src/charli3_dendrite/dexs/ob/ob_base.py
RENAMED
|
File without changes
|
|
File without changes
|