dpx-sdk 0.1.0__py3-none-any.whl
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.
- dpx_sdk/__init__.py +5 -0
- dpx_sdk/client.py +162 -0
- dpx_sdk/tools/__init__.py +12 -0
- dpx_sdk/tools/langchain.py +239 -0
- dpx_sdk/tools/llamaindex.py +201 -0
- dpx_sdk/types.py +61 -0
- dpx_sdk-0.1.0.dist-info/METADATA +104 -0
- dpx_sdk-0.1.0.dist-info/RECORD +9 -0
- dpx_sdk-0.1.0.dist-info/WHEEL +4 -0
dpx_sdk/__init__.py
ADDED
dpx_sdk/client.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DPX Python SDK — thin async client over the DPX REST + Settlement Agent APIs.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from .types import QuoteResult, SettlementResult, OracleStatus, EsgScore
|
|
11
|
+
|
|
12
|
+
STABILITY_URL = "https://stability.untitledfinancial.com"
|
|
13
|
+
ESG_URL = "https://esg.untitledfinancial.com"
|
|
14
|
+
AGENT_URL = "https://agent.untitledfinancial.com"
|
|
15
|
+
COMPLIANCE_URL = "https://compliance.untitledfinancial.com"
|
|
16
|
+
DEFAULT_TIMEOUT = 20.0
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DPXError(Exception):
|
|
20
|
+
def __init__(self, message: str, status_code: Optional[int] = None):
|
|
21
|
+
super().__init__(message)
|
|
22
|
+
self.status_code = status_code
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DPX:
|
|
26
|
+
"""
|
|
27
|
+
Synchronous DPX client.
|
|
28
|
+
|
|
29
|
+
Usage:
|
|
30
|
+
from dpx_sdk import DPX
|
|
31
|
+
dpx = DPX()
|
|
32
|
+
quote = dpx.quote(amount_usd=500_000, has_fx=True, esg_score=75)
|
|
33
|
+
result = dpx.settle(
|
|
34
|
+
amount=500_000,
|
|
35
|
+
source_currency="USD",
|
|
36
|
+
destination_currency="EUR",
|
|
37
|
+
recipient_address="0x...",
|
|
38
|
+
sandbox=True,
|
|
39
|
+
)
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, timeout: float = DEFAULT_TIMEOUT):
|
|
43
|
+
self._client = httpx.Client(
|
|
44
|
+
timeout=timeout,
|
|
45
|
+
headers={"User-Agent": "dpx-sdk-python/0.1.0"},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def _get(self, url: str, params: Optional[dict] = None) -> dict:
|
|
49
|
+
r = self._client.get(url, params=params)
|
|
50
|
+
if not r.is_success:
|
|
51
|
+
raise DPXError(f"HTTP {r.status_code}: {r.text[:200]}", r.status_code)
|
|
52
|
+
return r.json()
|
|
53
|
+
|
|
54
|
+
def _post(self, url: str, body: dict) -> dict:
|
|
55
|
+
r = self._client.post(url, json=body)
|
|
56
|
+
if not r.is_success:
|
|
57
|
+
raise DPXError(f"HTTP {r.status_code}: {r.text[:200]}", r.status_code)
|
|
58
|
+
return r.json()
|
|
59
|
+
|
|
60
|
+
def quote(
|
|
61
|
+
self,
|
|
62
|
+
amount_usd: float,
|
|
63
|
+
has_fx: bool = True,
|
|
64
|
+
esg_score: float = 75,
|
|
65
|
+
monthly_volume_usd: Optional[float] = None,
|
|
66
|
+
) -> QuoteResult:
|
|
67
|
+
params: dict = {"amountUsd": amount_usd, "hasFx": str(has_fx).lower(), "esgScore": int(esg_score)}
|
|
68
|
+
if monthly_volume_usd is not None:
|
|
69
|
+
params["monthlyVolumeUsd"] = monthly_volume_usd
|
|
70
|
+
d = self._get(f"{AGENT_URL}/quote", params)
|
|
71
|
+
fees = d.get("fees", {})
|
|
72
|
+
total = fees.get("total", {})
|
|
73
|
+
return QuoteResult(
|
|
74
|
+
quote_id=d.get("quoteId", ""),
|
|
75
|
+
amount_usd=d.get("amountUsd", amount_usd),
|
|
76
|
+
net_amount_usd=d.get("netAmountUsd", 0.0),
|
|
77
|
+
total_fee_pct=total.get("pct", 0.0),
|
|
78
|
+
total_fee_usd=total.get("usd", 0.0),
|
|
79
|
+
oracle_status=d.get("oracleStatus", ""),
|
|
80
|
+
oracle_score=d.get("oracleScore", 0.0),
|
|
81
|
+
tier=d.get("tier", ""),
|
|
82
|
+
expires_at=d.get("expiresAt", ""),
|
|
83
|
+
reasoning=d.get("reasoning", ""),
|
|
84
|
+
raw=d,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def oracle_status(self) -> OracleStatus:
|
|
88
|
+
d = self._get(f"{STABILITY_URL}/reliability")
|
|
89
|
+
return OracleStatus(
|
|
90
|
+
status=d.get("status", ""),
|
|
91
|
+
score=float(d.get("stabilityScore", d.get("score", 0))),
|
|
92
|
+
recommendation=d.get("recommendation", ""),
|
|
93
|
+
peg_deviation=float(d.get("pegDeviation", 0)),
|
|
94
|
+
outlook=d.get("outlook", ""),
|
|
95
|
+
reasoning=d.get("reasoning", d.get("briefing", "")),
|
|
96
|
+
timestamp=d.get("timestamp", ""),
|
|
97
|
+
raw=d,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def esg_score(self, address: Optional[str] = None) -> EsgScore:
|
|
101
|
+
path = f"/esg-score/{address}" if address else "/esg-score"
|
|
102
|
+
d = self._get(f"{ESG_URL}{path}")
|
|
103
|
+
return EsgScore(
|
|
104
|
+
address=d.get("address", address or "default"),
|
|
105
|
+
esg_score=float(d.get("esgScore", d.get("score", 0))),
|
|
106
|
+
environmental=float(d.get("environmental", 0)),
|
|
107
|
+
social=float(d.get("social", 0)),
|
|
108
|
+
governance=float(d.get("governance", 0)),
|
|
109
|
+
fee_pct=float(d.get("feePct", 0)),
|
|
110
|
+
tier=d.get("tier", ""),
|
|
111
|
+
updated_at=d.get("updatedAt", ""),
|
|
112
|
+
raw=d,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def settle(
|
|
116
|
+
self,
|
|
117
|
+
amount: float,
|
|
118
|
+
source_currency: str,
|
|
119
|
+
destination_currency: str,
|
|
120
|
+
recipient_address: str,
|
|
121
|
+
purpose: str = "vendor-payment",
|
|
122
|
+
reference_id: Optional[str] = None,
|
|
123
|
+
quote_id: Optional[str] = None,
|
|
124
|
+
sandbox: bool = True,
|
|
125
|
+
) -> SettlementResult:
|
|
126
|
+
body: dict = {
|
|
127
|
+
"amount": amount,
|
|
128
|
+
"sourceCurrency": source_currency,
|
|
129
|
+
"destinationCurrency": destination_currency,
|
|
130
|
+
"recipientAddress": recipient_address,
|
|
131
|
+
"purpose": purpose,
|
|
132
|
+
"sandbox": sandbox,
|
|
133
|
+
}
|
|
134
|
+
if reference_id:
|
|
135
|
+
body["referenceId"] = reference_id
|
|
136
|
+
if quote_id:
|
|
137
|
+
body["quoteId"] = quote_id
|
|
138
|
+
d = self._post(f"{AGENT_URL}/settle", body)
|
|
139
|
+
result = d.get("result", d)
|
|
140
|
+
return SettlementResult(
|
|
141
|
+
settlement_id=result.get("settlementId", ""),
|
|
142
|
+
status=result.get("status", ""),
|
|
143
|
+
tx_hash=result.get("txHash"),
|
|
144
|
+
net_amount=float(result.get("netAmount", 0)),
|
|
145
|
+
fees_total=float(result.get("feesTotal", 0)),
|
|
146
|
+
oracle_status=result.get("oracleStatus", ""),
|
|
147
|
+
reasoning=result.get("reasoning", ""),
|
|
148
|
+
timestamp=result.get("timestamp", ""),
|
|
149
|
+
raw=d,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def screen(self, address: str) -> dict:
|
|
153
|
+
return self._get(f"{COMPLIANCE_URL}/screen", {"address": address})
|
|
154
|
+
|
|
155
|
+
def close(self):
|
|
156
|
+
self._client.close()
|
|
157
|
+
|
|
158
|
+
def __enter__(self):
|
|
159
|
+
return self
|
|
160
|
+
|
|
161
|
+
def __exit__(self, *_):
|
|
162
|
+
self.close()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .langchain import DPXToolkit, DPXQuoteTool, DPXSettleTool, DPXOracleTool, DPXEsgTool, DPXComplianceTool
|
|
2
|
+
from .llamaindex import DPXToolSpec
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"DPXToolkit",
|
|
6
|
+
"DPXQuoteTool",
|
|
7
|
+
"DPXSettleTool",
|
|
8
|
+
"DPXOracleTool",
|
|
9
|
+
"DPXEsgTool",
|
|
10
|
+
"DPXComplianceTool",
|
|
11
|
+
"DPXToolSpec",
|
|
12
|
+
]
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DPX LangChain Toolkit
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
from dpx_sdk.tools import DPXToolkit
|
|
6
|
+
from langchain.agents import initialize_agent, AgentType
|
|
7
|
+
from langchain_openai import ChatOpenAI # or any LLM
|
|
8
|
+
|
|
9
|
+
tools = DPXToolkit().get_tools()
|
|
10
|
+
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)
|
|
11
|
+
agent.run("Get a quote for sending $500K USD to EUR and check oracle stability")
|
|
12
|
+
|
|
13
|
+
Published to LangChain Hub: untitledfinancial/dpx-toolkit
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
from typing import Optional, Type, List
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
from langchain.tools import BaseTool
|
|
23
|
+
from langchain.tools import BaseToolkit
|
|
24
|
+
from langchain_core.tools import BaseTool as CoreBaseTool
|
|
25
|
+
from pydantic import BaseModel, Field
|
|
26
|
+
_LANGCHAIN_AVAILABLE = True
|
|
27
|
+
except ImportError:
|
|
28
|
+
_LANGCHAIN_AVAILABLE = False
|
|
29
|
+
BaseTool = object # type: ignore
|
|
30
|
+
BaseToolkit = object # type: ignore
|
|
31
|
+
BaseModel = object # type: ignore
|
|
32
|
+
Field = lambda *a, **kw: None # type: ignore
|
|
33
|
+
|
|
34
|
+
from ..client import DPX
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _require_langchain():
|
|
38
|
+
if not _LANGCHAIN_AVAILABLE:
|
|
39
|
+
raise ImportError("Install langchain: pip install langchain langchain-core")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ── Input schemas ──────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
if _LANGCHAIN_AVAILABLE:
|
|
45
|
+
class QuoteInput(BaseModel):
|
|
46
|
+
amount_usd: float = Field(description="Settlement amount in USD")
|
|
47
|
+
has_fx: bool = Field(default=True, description="True if cross-currency (adds FX fee)")
|
|
48
|
+
esg_score: float = Field(default=75, description="Counterparty ESG score 0–100")
|
|
49
|
+
|
|
50
|
+
class SettleInput(BaseModel):
|
|
51
|
+
amount: float = Field(description="Settlement amount")
|
|
52
|
+
source_currency: str = Field(description="Source currency: USD, EUR, GBP, USDC, EURC")
|
|
53
|
+
destination_currency: str = Field(description="Destination currency: USD, EUR, GBP, USDC, EURC")
|
|
54
|
+
recipient_address: str = Field(description="On-chain recipient wallet address (0x...)")
|
|
55
|
+
purpose: str = Field(default="vendor-payment", description="Payment purpose")
|
|
56
|
+
sandbox: bool = Field(default=True, description="Sandbox mode — set False for live execution")
|
|
57
|
+
|
|
58
|
+
class EsgInput(BaseModel):
|
|
59
|
+
address: Optional[str] = Field(default=None, description="Wallet address (0x...). Omit for protocol default.")
|
|
60
|
+
|
|
61
|
+
class ComplianceInput(BaseModel):
|
|
62
|
+
address: str = Field(description="Wallet address or entity name to screen")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ── Tools ──────────────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
class DPXQuoteTool(BaseTool):
|
|
68
|
+
"""Get a binding settlement fee quote from DPX."""
|
|
69
|
+
name: str = "dpx_quote"
|
|
70
|
+
description: str = (
|
|
71
|
+
"Get a binding fee quote for a DPX cross-border settlement. "
|
|
72
|
+
"Returns all-in rate, fee breakdown, net amount, oracle status, and AI reasoning. "
|
|
73
|
+
"Quote is valid for 300 seconds. Always call before dpx_settle."
|
|
74
|
+
)
|
|
75
|
+
args_schema: Type[BaseModel] = QuoteInput if _LANGCHAIN_AVAILABLE else object # type: ignore
|
|
76
|
+
|
|
77
|
+
def _run(self, amount_usd: float, has_fx: bool = True, esg_score: float = 75) -> str:
|
|
78
|
+
_require_langchain()
|
|
79
|
+
with DPX() as dpx:
|
|
80
|
+
q = dpx.quote(amount_usd=amount_usd, has_fx=has_fx, esg_score=esg_score)
|
|
81
|
+
return json.dumps({
|
|
82
|
+
"quote_id": q.quote_id,
|
|
83
|
+
"amount_usd": q.amount_usd,
|
|
84
|
+
"net_amount_usd": q.net_amount_usd,
|
|
85
|
+
"total_fee_pct": q.total_fee_pct,
|
|
86
|
+
"total_fee_usd": q.total_fee_usd,
|
|
87
|
+
"oracle_status": q.oracle_status,
|
|
88
|
+
"tier": q.tier,
|
|
89
|
+
"expires_at": q.expires_at,
|
|
90
|
+
"reasoning": q.reasoning,
|
|
91
|
+
}, indent=2)
|
|
92
|
+
|
|
93
|
+
async def _arun(self, *args, **kwargs) -> str:
|
|
94
|
+
return self._run(*args, **kwargs)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class DPXSettleTool(BaseTool):
|
|
98
|
+
"""Execute a cross-border settlement through DPX."""
|
|
99
|
+
name: str = "dpx_settle"
|
|
100
|
+
description: str = (
|
|
101
|
+
"Execute a DPX cross-border or domestic settlement. "
|
|
102
|
+
"Checks oracle conditions, runs ESG scoring and compliance checks, "
|
|
103
|
+
"then executes on-chain (or returns sandbox result if sandbox=True). "
|
|
104
|
+
"Returns settlement ID, tx hash, net amount, fees, and AI reasoning. "
|
|
105
|
+
"Default sandbox=True — set False only for live execution with explicit user confirmation."
|
|
106
|
+
)
|
|
107
|
+
args_schema: Type[BaseModel] = SettleInput if _LANGCHAIN_AVAILABLE else object # type: ignore
|
|
108
|
+
|
|
109
|
+
def _run(
|
|
110
|
+
self,
|
|
111
|
+
amount: float,
|
|
112
|
+
source_currency: str,
|
|
113
|
+
destination_currency: str,
|
|
114
|
+
recipient_address: str,
|
|
115
|
+
purpose: str = "vendor-payment",
|
|
116
|
+
sandbox: bool = True,
|
|
117
|
+
) -> str:
|
|
118
|
+
_require_langchain()
|
|
119
|
+
with DPX() as dpx:
|
|
120
|
+
r = dpx.settle(
|
|
121
|
+
amount=amount,
|
|
122
|
+
source_currency=source_currency,
|
|
123
|
+
destination_currency=destination_currency,
|
|
124
|
+
recipient_address=recipient_address,
|
|
125
|
+
purpose=purpose,
|
|
126
|
+
sandbox=sandbox,
|
|
127
|
+
)
|
|
128
|
+
return json.dumps({
|
|
129
|
+
"settlement_id": r.settlement_id,
|
|
130
|
+
"status": r.status,
|
|
131
|
+
"tx_hash": r.tx_hash,
|
|
132
|
+
"net_amount": r.net_amount,
|
|
133
|
+
"fees_total": r.fees_total,
|
|
134
|
+
"oracle_status": r.oracle_status,
|
|
135
|
+
"reasoning": r.reasoning,
|
|
136
|
+
"timestamp": r.timestamp,
|
|
137
|
+
}, indent=2)
|
|
138
|
+
|
|
139
|
+
async def _arun(self, *args, **kwargs) -> str:
|
|
140
|
+
return self._run(*args, **kwargs)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class DPXOracleTool(BaseTool):
|
|
144
|
+
"""Check live macro stability before a settlement."""
|
|
145
|
+
name: str = "dpx_oracle_status"
|
|
146
|
+
description: str = (
|
|
147
|
+
"Get live macro stability assessment from the DPX Stability Oracle. "
|
|
148
|
+
"Returns stability score (0–100), status (STABLE/CAUTION/UNSTABLE), "
|
|
149
|
+
"recommendation (PROCEED/CAUTION/HOLD), peg deviation in bps, and AI reasoning. "
|
|
150
|
+
"Call before large settlements — UNSTABLE status means hold."
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def _run(self) -> str:
|
|
154
|
+
_require_langchain()
|
|
155
|
+
with DPX() as dpx:
|
|
156
|
+
o = dpx.oracle_status()
|
|
157
|
+
return json.dumps({
|
|
158
|
+
"status": o.status,
|
|
159
|
+
"score": o.score,
|
|
160
|
+
"recommendation": o.recommendation,
|
|
161
|
+
"peg_deviation_bps": o.peg_deviation,
|
|
162
|
+
"outlook": o.outlook,
|
|
163
|
+
"reasoning": o.reasoning,
|
|
164
|
+
}, indent=2)
|
|
165
|
+
|
|
166
|
+
async def _arun(self) -> str:
|
|
167
|
+
return self._run()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class DPXEsgTool(BaseTool):
|
|
171
|
+
"""Score a counterparty wallet for ESG risk."""
|
|
172
|
+
name: str = "dpx_esg_score"
|
|
173
|
+
description: str = (
|
|
174
|
+
"Get the ESG counterparty risk score for a wallet address. "
|
|
175
|
+
"Required for EU SFDR Principal Adverse Impact reporting and CSRD disclosure. "
|
|
176
|
+
"Returns E/S/G sub-scores, composite score (0–100), and the fee adjustment "
|
|
177
|
+
"this score produces at settlement. Updated hourly."
|
|
178
|
+
)
|
|
179
|
+
args_schema: Type[BaseModel] = EsgInput if _LANGCHAIN_AVAILABLE else object # type: ignore
|
|
180
|
+
|
|
181
|
+
def _run(self, address: Optional[str] = None) -> str:
|
|
182
|
+
_require_langchain()
|
|
183
|
+
with DPX() as dpx:
|
|
184
|
+
e = dpx.esg_score(address=address)
|
|
185
|
+
return json.dumps({
|
|
186
|
+
"address": e.address,
|
|
187
|
+
"esg_score": e.esg_score,
|
|
188
|
+
"environmental": e.environmental,
|
|
189
|
+
"social": e.social,
|
|
190
|
+
"governance": e.governance,
|
|
191
|
+
"fee_pct": e.fee_pct,
|
|
192
|
+
"tier": e.tier,
|
|
193
|
+
}, indent=2)
|
|
194
|
+
|
|
195
|
+
async def _arun(self, *args, **kwargs) -> str:
|
|
196
|
+
return self._run(*args, **kwargs)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class DPXComplianceTool(BaseTool):
|
|
200
|
+
"""Screen a counterparty through DPX AML/FATF compliance oracle."""
|
|
201
|
+
name: str = "dpx_compliance_screen"
|
|
202
|
+
description: str = (
|
|
203
|
+
"Screen a wallet address or entity through the DPX Compliance Oracle. "
|
|
204
|
+
"Runs FATF R16 Travel Rule, AML behavioural analysis (z-score anomaly + "
|
|
205
|
+
"graph risk), sanctions screening (OFAC SDN), MiCA, and GENIUS Act checks. "
|
|
206
|
+
"Returns APPROVED / FLAGGED / BLOCKED with full reasoning. "
|
|
207
|
+
"Call before every settlement for FATF Travel Rule compliance."
|
|
208
|
+
)
|
|
209
|
+
args_schema: Type[BaseModel] = ComplianceInput if _LANGCHAIN_AVAILABLE else object # type: ignore
|
|
210
|
+
|
|
211
|
+
def _run(self, address: str) -> str:
|
|
212
|
+
_require_langchain()
|
|
213
|
+
with DPX() as dpx:
|
|
214
|
+
return json.dumps(dpx.screen(address), indent=2)
|
|
215
|
+
|
|
216
|
+
async def _arun(self, *args, **kwargs) -> str:
|
|
217
|
+
return self._run(*args, **kwargs)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
# ── Toolkit ────────────────────────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
class DPXToolkit(BaseToolkit):
|
|
223
|
+
"""
|
|
224
|
+
DPX LangChain Toolkit — full settlement lifecycle as LangChain tools.
|
|
225
|
+
|
|
226
|
+
Provides: dpx_quote, dpx_settle, dpx_oracle_status, dpx_esg_score, dpx_compliance_screen
|
|
227
|
+
|
|
228
|
+
LangChain Hub: untitledfinancial/dpx-toolkit
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
def get_tools(self) -> List[BaseTool]:
|
|
232
|
+
_require_langchain()
|
|
233
|
+
return [
|
|
234
|
+
DPXOracleTool(),
|
|
235
|
+
DPXEsgTool(),
|
|
236
|
+
DPXComplianceTool(),
|
|
237
|
+
DPXQuoteTool(),
|
|
238
|
+
DPXSettleTool(),
|
|
239
|
+
]
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DPX LlamaIndex ToolSpec
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
from dpx_sdk.tools import DPXToolSpec
|
|
6
|
+
from llama_index.agent.openai import OpenAIAgent # or any agent
|
|
7
|
+
|
|
8
|
+
dpx_spec = DPXToolSpec()
|
|
9
|
+
tools = dpx_spec.to_tool_list()
|
|
10
|
+
agent = OpenAIAgent.from_tools(tools, verbose=True)
|
|
11
|
+
agent.chat("Get a quote for $1M USD to EUR and check oracle stability")
|
|
12
|
+
|
|
13
|
+
LlamaIndex Hub: untitledfinancial/dpx-tool-spec
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
from llama_index.core.tools import FunctionTool
|
|
23
|
+
from llama_index.core.tools.tool_spec.base import BaseToolSpec
|
|
24
|
+
_LLAMA_AVAILABLE = True
|
|
25
|
+
except ImportError:
|
|
26
|
+
try:
|
|
27
|
+
from llama_index.tools import FunctionTool
|
|
28
|
+
from llama_index.tools.tool_spec.base import BaseToolSpec
|
|
29
|
+
_LLAMA_AVAILABLE = True
|
|
30
|
+
except ImportError:
|
|
31
|
+
_LLAMA_AVAILABLE = False
|
|
32
|
+
BaseToolSpec = object # type: ignore
|
|
33
|
+
|
|
34
|
+
from ..client import DPX
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _require_llama():
|
|
38
|
+
if not _LLAMA_AVAILABLE:
|
|
39
|
+
raise ImportError("Install llama-index: pip install llama-index-core")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class DPXToolSpec(BaseToolSpec):
|
|
43
|
+
"""
|
|
44
|
+
DPX ToolSpec for LlamaIndex agents.
|
|
45
|
+
|
|
46
|
+
Exposes the full DPX settlement lifecycle:
|
|
47
|
+
- get_settlement_quote — Binding fee quote
|
|
48
|
+
- execute_settlement — Cross-border settlement execution
|
|
49
|
+
- get_oracle_status — Macro stability assessment
|
|
50
|
+
- get_esg_score — ESG counterparty scoring
|
|
51
|
+
- screen_compliance — FATF/AML/sanctions screening
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
spec_functions = [
|
|
55
|
+
"get_settlement_quote",
|
|
56
|
+
"execute_settlement",
|
|
57
|
+
"get_oracle_status",
|
|
58
|
+
"get_esg_score",
|
|
59
|
+
"screen_compliance",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
def get_settlement_quote(
|
|
63
|
+
self,
|
|
64
|
+
amount_usd: float,
|
|
65
|
+
has_fx: bool = True,
|
|
66
|
+
esg_score: float = 75,
|
|
67
|
+
) -> str:
|
|
68
|
+
"""
|
|
69
|
+
Get a binding fee quote for a DPX settlement.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
amount_usd: Settlement amount in USD.
|
|
73
|
+
has_fx: True if cross-currency (adds FX fee). Default True.
|
|
74
|
+
esg_score: Counterparty ESG score 0–100. Default 75.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
JSON string with quote_id, net_amount_usd, fee breakdown,
|
|
78
|
+
oracle_status, tier, and AI reasoning. Quote valid 300 seconds.
|
|
79
|
+
"""
|
|
80
|
+
_require_llama()
|
|
81
|
+
with DPX() as dpx:
|
|
82
|
+
q = dpx.quote(amount_usd=amount_usd, has_fx=has_fx, esg_score=esg_score)
|
|
83
|
+
return json.dumps({
|
|
84
|
+
"quote_id": q.quote_id,
|
|
85
|
+
"amount_usd": q.amount_usd,
|
|
86
|
+
"net_amount_usd": q.net_amount_usd,
|
|
87
|
+
"total_fee_pct": q.total_fee_pct,
|
|
88
|
+
"total_fee_usd": q.total_fee_usd,
|
|
89
|
+
"oracle_status": q.oracle_status,
|
|
90
|
+
"tier": q.tier,
|
|
91
|
+
"expires_at": q.expires_at,
|
|
92
|
+
"reasoning": q.reasoning,
|
|
93
|
+
}, indent=2)
|
|
94
|
+
|
|
95
|
+
def execute_settlement(
|
|
96
|
+
self,
|
|
97
|
+
amount: float,
|
|
98
|
+
source_currency: str,
|
|
99
|
+
destination_currency: str,
|
|
100
|
+
recipient_address: str,
|
|
101
|
+
purpose: str = "vendor-payment",
|
|
102
|
+
sandbox: bool = True,
|
|
103
|
+
) -> str:
|
|
104
|
+
"""
|
|
105
|
+
Execute a DPX cross-border or domestic settlement.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
amount: Settlement amount.
|
|
109
|
+
source_currency: USD, EUR, GBP, USDC, or EURC.
|
|
110
|
+
destination_currency: USD, EUR, GBP, USDC, or EURC.
|
|
111
|
+
recipient_address: On-chain recipient wallet address (0x...).
|
|
112
|
+
purpose: vendor-payment, intercompany, payroll, or treasury.
|
|
113
|
+
sandbox: If True (default), returns calculations without on-chain execution.
|
|
114
|
+
Set False only for live execution with explicit user confirmation.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
JSON string with settlement_id, status, tx_hash, net_amount, fees, and reasoning.
|
|
118
|
+
"""
|
|
119
|
+
_require_llama()
|
|
120
|
+
with DPX() as dpx:
|
|
121
|
+
r = dpx.settle(
|
|
122
|
+
amount=amount,
|
|
123
|
+
source_currency=source_currency,
|
|
124
|
+
destination_currency=destination_currency,
|
|
125
|
+
recipient_address=recipient_address,
|
|
126
|
+
purpose=purpose,
|
|
127
|
+
sandbox=sandbox,
|
|
128
|
+
)
|
|
129
|
+
return json.dumps({
|
|
130
|
+
"settlement_id": r.settlement_id,
|
|
131
|
+
"status": r.status,
|
|
132
|
+
"tx_hash": r.tx_hash,
|
|
133
|
+
"net_amount": r.net_amount,
|
|
134
|
+
"fees_total": r.fees_total,
|
|
135
|
+
"oracle_status": r.oracle_status,
|
|
136
|
+
"reasoning": r.reasoning,
|
|
137
|
+
"timestamp": r.timestamp,
|
|
138
|
+
}, indent=2)
|
|
139
|
+
|
|
140
|
+
def get_oracle_status(self) -> str:
|
|
141
|
+
"""
|
|
142
|
+
Get live macro stability assessment from the DPX Stability Oracle.
|
|
143
|
+
|
|
144
|
+
Returns stability score (0–100), status (STABLE/CAUTION/UNSTABLE),
|
|
145
|
+
recommendation (PROCEED/CAUTION/HOLD), peg deviation in bps, outlook,
|
|
146
|
+
and AI reasoning. UNSTABLE means hold large settlements.
|
|
147
|
+
"""
|
|
148
|
+
_require_llama()
|
|
149
|
+
with DPX() as dpx:
|
|
150
|
+
o = dpx.oracle_status()
|
|
151
|
+
return json.dumps({
|
|
152
|
+
"status": o.status,
|
|
153
|
+
"score": o.score,
|
|
154
|
+
"recommendation": o.recommendation,
|
|
155
|
+
"peg_deviation_bps": o.peg_deviation,
|
|
156
|
+
"outlook": o.outlook,
|
|
157
|
+
"reasoning": o.reasoning,
|
|
158
|
+
}, indent=2)
|
|
159
|
+
|
|
160
|
+
def get_esg_score(self, address: Optional[str] = None) -> str:
|
|
161
|
+
"""
|
|
162
|
+
Get the ESG counterparty risk score for a wallet address.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
address: Wallet address (0x...). Omit for protocol default score.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
JSON string with composite ESG score (0–100), E/S/G sub-scores,
|
|
169
|
+
settlement fee adjustment, and tier. Updated hourly from 6 institutional
|
|
170
|
+
sources: World Bank, IMF, OECD, UN, SEC, ClimateMonitor.
|
|
171
|
+
Required for EU SFDR PAI reporting and CSRD disclosure.
|
|
172
|
+
"""
|
|
173
|
+
_require_llama()
|
|
174
|
+
with DPX() as dpx:
|
|
175
|
+
e = dpx.esg_score(address=address)
|
|
176
|
+
return json.dumps({
|
|
177
|
+
"address": e.address,
|
|
178
|
+
"esg_score": e.esg_score,
|
|
179
|
+
"environmental": e.environmental,
|
|
180
|
+
"social": e.social,
|
|
181
|
+
"governance": e.governance,
|
|
182
|
+
"fee_pct": e.fee_pct,
|
|
183
|
+
"tier": e.tier,
|
|
184
|
+
}, indent=2)
|
|
185
|
+
|
|
186
|
+
def screen_compliance(self, address: str) -> str:
|
|
187
|
+
"""
|
|
188
|
+
Screen a counterparty through the DPX Compliance Oracle.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
address: Wallet address or entity name to screen.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
JSON string with compliance decision (APPROVED/FLAGGED/BLOCKED),
|
|
195
|
+
FATF R16 Travel Rule status, AML signals, sanctions check,
|
|
196
|
+
MiCA and GENIUS Act attestations, and full reasoning.
|
|
197
|
+
Call before every settlement for FATF Travel Rule compliance.
|
|
198
|
+
"""
|
|
199
|
+
_require_llama()
|
|
200
|
+
with DPX() as dpx:
|
|
201
|
+
return json.dumps(dpx.screen(address), indent=2)
|
dpx_sdk/types.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class FeeComponent:
|
|
7
|
+
pct: float
|
|
8
|
+
usd: float
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class QuoteResult:
|
|
13
|
+
quote_id: str
|
|
14
|
+
amount_usd: float
|
|
15
|
+
net_amount_usd: float
|
|
16
|
+
total_fee_pct: float
|
|
17
|
+
total_fee_usd: float
|
|
18
|
+
oracle_status: str
|
|
19
|
+
oracle_score: float
|
|
20
|
+
tier: str
|
|
21
|
+
expires_at: str
|
|
22
|
+
reasoning: str
|
|
23
|
+
raw: dict = field(default_factory=dict)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class SettlementResult:
|
|
28
|
+
settlement_id: str
|
|
29
|
+
status: str # executed | sandbox | held | failed
|
|
30
|
+
tx_hash: Optional[str]
|
|
31
|
+
net_amount: float
|
|
32
|
+
fees_total: float
|
|
33
|
+
oracle_status: str
|
|
34
|
+
reasoning: str
|
|
35
|
+
timestamp: str
|
|
36
|
+
raw: dict = field(default_factory=dict)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class OracleStatus:
|
|
41
|
+
status: str # STABLE | CAUTION | UNSTABLE
|
|
42
|
+
score: float
|
|
43
|
+
recommendation: str # PROCEED | CAUTION | HOLD
|
|
44
|
+
peg_deviation: float
|
|
45
|
+
outlook: str
|
|
46
|
+
reasoning: str
|
|
47
|
+
timestamp: str
|
|
48
|
+
raw: dict = field(default_factory=dict)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class EsgScore:
|
|
53
|
+
address: str
|
|
54
|
+
esg_score: float
|
|
55
|
+
environmental: float
|
|
56
|
+
social: float
|
|
57
|
+
governance: float
|
|
58
|
+
fee_pct: float
|
|
59
|
+
tier: str
|
|
60
|
+
updated_at: str
|
|
61
|
+
raw: dict = field(default_factory=dict)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dpx-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the DPX Protocol — compliance-grade stablecoin settlement rail with LangChain and LlamaIndex toolkits
|
|
5
|
+
Project-URL: Homepage, https://untitledfinancial.com
|
|
6
|
+
Project-URL: Documentation, https://docs.untitledfinancial.com
|
|
7
|
+
Project-URL: Repository, https://github.com/untitledfinancial/dpx-mcp
|
|
8
|
+
License: BUSL-1.1
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Requires-Dist: httpx>=0.27.0
|
|
11
|
+
Provides-Extra: all
|
|
12
|
+
Requires-Dist: langchain-core>=0.2.0; extra == 'all'
|
|
13
|
+
Requires-Dist: langchain>=0.2.0; extra == 'all'
|
|
14
|
+
Requires-Dist: llama-index-core>=0.10.0; extra == 'all'
|
|
15
|
+
Requires-Dist: pydantic>=2.0; extra == 'all'
|
|
16
|
+
Provides-Extra: langchain
|
|
17
|
+
Requires-Dist: langchain-core>=0.2.0; extra == 'langchain'
|
|
18
|
+
Requires-Dist: langchain>=0.2.0; extra == 'langchain'
|
|
19
|
+
Requires-Dist: pydantic>=2.0; extra == 'langchain'
|
|
20
|
+
Provides-Extra: llamaindex
|
|
21
|
+
Requires-Dist: llama-index-core>=0.10.0; extra == 'llamaindex'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# dpx-sdk
|
|
25
|
+
|
|
26
|
+
Python SDK for [DPX Protocol](https://untitledfinancial.com) — compliance-grade stablecoin settlement rail with native LangChain and LlamaIndex toolkits.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install dpx-sdk # core client
|
|
30
|
+
pip install "dpx-sdk[langchain]" # + LangChain toolkit
|
|
31
|
+
pip install "dpx-sdk[llamaindex]" # + LlamaIndex ToolSpec
|
|
32
|
+
pip install "dpx-sdk[all]" # everything
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from dpx_sdk import DPX
|
|
39
|
+
|
|
40
|
+
with DPX() as dpx:
|
|
41
|
+
# Check oracle before settling
|
|
42
|
+
oracle = dpx.oracle_status()
|
|
43
|
+
print(oracle.status, oracle.recommendation) # STABLE PROCEED
|
|
44
|
+
|
|
45
|
+
# Get a binding quote
|
|
46
|
+
quote = dpx.quote(amount_usd=500_000, has_fx=True, esg_score=75)
|
|
47
|
+
print(f"Net: ${quote.net_amount_usd:,.2f} Fee: {quote.total_fee_pct:.3f}%")
|
|
48
|
+
|
|
49
|
+
# Execute in sandbox (safe default)
|
|
50
|
+
result = dpx.settle(
|
|
51
|
+
amount=500_000,
|
|
52
|
+
source_currency="USD",
|
|
53
|
+
destination_currency="EUR",
|
|
54
|
+
recipient_address="0xYourRecipientAddress",
|
|
55
|
+
sandbox=True, # set False for live execution
|
|
56
|
+
)
|
|
57
|
+
print(result.status, result.settlement_id)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## LangChain
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from dpx_sdk.tools import DPXToolkit
|
|
64
|
+
from langchain.agents import initialize_agent, AgentType
|
|
65
|
+
from langchain_openai import ChatOpenAI
|
|
66
|
+
|
|
67
|
+
tools = DPXToolkit().get_tools()
|
|
68
|
+
agent = initialize_agent(tools, ChatOpenAI(), agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)
|
|
69
|
+
agent.run("Check oracle stability, get a quote for $1M USD→EUR, and screen the counterparty 0x123...")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Available tools: `dpx_oracle_status` · `dpx_esg_score` · `dpx_compliance_screen` · `dpx_quote` · `dpx_settle`
|
|
73
|
+
|
|
74
|
+
**LangChain Hub:** `untitledfinancial/dpx-toolkit`
|
|
75
|
+
|
|
76
|
+
## LlamaIndex
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from dpx_sdk.tools import DPXToolSpec
|
|
80
|
+
from llama_index.agent.openai import OpenAIAgent
|
|
81
|
+
|
|
82
|
+
tools = DPXToolSpec().to_tool_list()
|
|
83
|
+
agent = OpenAIAgent.from_tools(tools, verbose=True)
|
|
84
|
+
agent.chat("Get a quote for $500K USD to EUR and execute in sandbox mode")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Available functions: `get_settlement_quote` · `execute_settlement` · `get_oracle_status` · `get_esg_score` · `screen_compliance`
|
|
88
|
+
|
|
89
|
+
**LlamaIndex Hub:** `untitledfinancial/dpx-tool-spec`
|
|
90
|
+
|
|
91
|
+
## MCP (Claude / Cursor)
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npx @untitledfinancial/dpx-mcp
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
32 MCP tools for Claude Desktop and Cursor. [Setup →](https://docs.untitledfinancial.com/integrations/mcp)
|
|
98
|
+
|
|
99
|
+
## Links
|
|
100
|
+
|
|
101
|
+
- [Documentation](https://docs.untitledfinancial.com)
|
|
102
|
+
- [MCP on Smithery](https://smithery.ai/server/@untitledfinancial/dpx-mcp)
|
|
103
|
+
- [npm package](https://www.npmjs.com/package/@untitledfinancial/dpx-mcp)
|
|
104
|
+
- [Contracts on Base mainnet](https://base.blockscout.com/address/0xe333551E18ef0471A71d7e8e761212766aa5AD4f)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
dpx_sdk/__init__.py,sha256=x7jKK0dYRs24Fzr_xIDqHGejafqsYXt9L0BfjqBNVtc,223
|
|
2
|
+
dpx_sdk/client.py,sha256=Lq7qQW6OkooAlS5DmldCLEzAbeo_Ribgj9ipGJXbTCM,5587
|
|
3
|
+
dpx_sdk/types.py,sha256=5JNanNtglsUnSKiy0-wQQBeA56nrfHK-7Kv1F1zqlNA,1232
|
|
4
|
+
dpx_sdk/tools/__init__.py,sha256=oIUGq4HTGjm0ortRNu18AaFs7PZwX_Z90lOQwc39ZnM,302
|
|
5
|
+
dpx_sdk/tools/langchain.py,sha256=TwYdDZ6QroJWbBvO8_LwgKKCYI1-WFuKMLBZ0FJZzoA,9441
|
|
6
|
+
dpx_sdk/tools/llamaindex.py,sha256=Tzu4Dj6g54eM-45DjGBdilcRHQswBxLtVaC9DI5lpnA,6839
|
|
7
|
+
dpx_sdk-0.1.0.dist-info/METADATA,sha256=Eh4MtKn6JlDngMIInPiWyD77vhjS5-wCh5ZJTemYrDM,3632
|
|
8
|
+
dpx_sdk-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
9
|
+
dpx_sdk-0.1.0.dist-info/RECORD,,
|