Skip to content

Commit 0c67e4f

Browse files
authored
feat: complete FAssets protocol connector implementation with swap functionality (#48)
2 parents fe1aea3 + 2f121e3 commit 0c67e4f

26 files changed

+3124
-346
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ wheels/
88

99
# Virtual environments
1010
.venv
11+
.venv*/
1112
.env
1213
.ruff_cache
1314
.pytest_cache

examples/02_fassets_basic.py

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
import asyncio
2+
import time
3+
from typing import Any
4+
5+
from web3 import Web3
6+
7+
from flare_ai_kit import FlareAIKit
8+
from flare_ai_kit.common import FAssetType
9+
from flare_ai_kit.common.schemas import FAssetInfo
10+
from flare_ai_kit.ecosystem.protocols.fassets import FAssets
11+
12+
13+
async def print_supported_fassets(fassets: FAssets) -> None:
14+
"""Print information about supported FAssets."""
15+
print("=== Supported FAssets ===")
16+
supported_fassets = await fassets.get_supported_fassets()
17+
18+
for symbol, info in supported_fassets.items():
19+
print(f"{symbol}: {info.name}")
20+
print(f" Underlying: {info.underlying_symbol}")
21+
print(f" Decimals: {info.decimals}")
22+
print(f" Active: {info.is_active}")
23+
print(f" Asset Manager: {info.asset_manager_address}")
24+
print(f" FAsset Token: {info.f_asset_address}")
25+
print()
26+
27+
28+
async def check_balance_and_allowance(fassets: FAssets) -> None:
29+
"""Check FXRP balance and SparkDEX allowance."""
30+
print("=== Balance & Allowance Operations ===")
31+
if not fassets.address:
32+
print("No account address configured - skipping balance checks")
33+
return
34+
35+
try:
36+
# Check FXRP balance
37+
balance = await fassets.get_fasset_balance(FAssetType.FXRP, fassets.address)
38+
print(f"FXRP Balance: {balance} wei")
39+
40+
# Check allowance for SparkDEX router (if configured)
41+
if fassets.sparkdex_router:
42+
allowance = await fassets.get_fasset_allowance(
43+
FAssetType.FXRP,
44+
fassets.address,
45+
fassets.sparkdex_router.address,
46+
)
47+
print(f"FXRP Allowance for SparkDEX: {allowance} wei")
48+
else:
49+
print("SparkDEX router not configured - skipping allowance check")
50+
except Exception as e:
51+
print(f"Balance/allowance check failed (expected with placeholders): {e}")
52+
53+
54+
async def perform_swap_operations(
55+
fassets: FAssets, supported_fassets: dict[str, FAssetInfo]
56+
) -> None:
57+
"""Demonstrate various swap operations."""
58+
print("=== Swap Operations (SparkDEX Integration) ===")
59+
try:
60+
# Example swap parameters
61+
swap_amount = 1000000 # 1 FXRP (6 decimals)
62+
min_native_out = 500000000000000000 # 0.5 FLR/SGB
63+
deadline = int(time.time()) + 3600 # 1 hour from now
64+
65+
print("1. Swap FXRP for Native Token (FLR/SGB)")
66+
tx_hash = await fassets.swap_fasset_for_native(
67+
FAssetType.FXRP,
68+
amount_in=swap_amount,
69+
amount_out_min=min_native_out,
70+
deadline=deadline,
71+
)
72+
print(f" Transaction: {tx_hash}")
73+
74+
print("2. Swap Native Token for FXRP")
75+
native_amount = 1000000000000000000 # 1 FLR/SGB
76+
min_fxrp_out = 900000 # 0.9 FXRP
77+
tx_hash = await fassets.swap_native_for_fasset(
78+
FAssetType.FXRP,
79+
amount_out_min=min_fxrp_out,
80+
deadline=deadline,
81+
amount_in=native_amount,
82+
)
83+
print(f" Transaction: {tx_hash}")
84+
85+
# Cross-FAsset swap (if multiple FAssets available)
86+
if len(supported_fassets) > 1:
87+
other_fassets = [k for k in supported_fassets if k != "FXRP"]
88+
if other_fassets:
89+
other_fasset = getattr(FAssetType, other_fassets[0])
90+
print(f"3. Swap FXRP for {other_fassets[0]}")
91+
tx_hash = await fassets.swap_fasset_for_fasset(
92+
FAssetType.FXRP,
93+
other_fasset,
94+
amount_in=swap_amount,
95+
amount_out_min=500000, # Adjust based on decimals
96+
deadline=deadline,
97+
)
98+
print(f" Transaction: {tx_hash}")
99+
100+
except Exception as e:
101+
print(f"Swap operations failed (expected with placeholders): {e}")
102+
103+
104+
async def demonstrate_minting_workflow(fassets: FAssets) -> None:
105+
"""Demonstrate the complete minting workflow."""
106+
print("=== Complete Minting Workflow ===")
107+
try:
108+
# Get all agents
109+
agents = await fassets.get_all_agents(FAssetType.FXRP)
110+
print(f"Available Agents: {len(agents)}")
111+
112+
if not agents:
113+
print("No agents available")
114+
return
115+
116+
agent_address = agents[0]
117+
# Get available lots
118+
available_lots: dict[str, Any] = await fassets.get_available_lots(
119+
FAssetType.FXRP, agent_address
120+
)
121+
print(f"Available lots from {agent_address}: {available_lots}")
122+
123+
# Step 1: Reserve collateral for minting
124+
print("Step 1: Reserve Collateral")
125+
executor = fassets.address or Web3.to_checksum_address(
126+
"0x0000000000000000000000000000000000000000"
127+
)
128+
reservation_id = await fassets.reserve_collateral(
129+
FAssetType.FXRP,
130+
agent_vault=agent_address,
131+
lots=1,
132+
max_minting_fee_bips=100, # 1%
133+
executor=executor,
134+
executor_fee_nat=0,
135+
)
136+
print(f"Collateral Reservation ID: {reservation_id}")
137+
138+
# Step 2: Execute minting (after underlying payment)
139+
print("Step 2: Execute Minting")
140+
payment_reference = (
141+
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
142+
)
143+
minted_amount = await fassets.execute_minting(
144+
FAssetType.FXRP,
145+
collateral_reservation_id=int(reservation_id),
146+
payment_reference=payment_reference,
147+
recipient=executor,
148+
)
149+
print(f"Minted Amount: {minted_amount} wei")
150+
151+
except Exception as e:
152+
print(f"Minting workflow failed (expected with placeholders): {e}")
153+
154+
155+
async def perform_redemption_operations(fassets: FAssets) -> None:
156+
"""Demonstrate redemption operations."""
157+
print("=== Redemption Operations ===")
158+
try:
159+
executor = fassets.address or Web3.to_checksum_address(
160+
"0x0000000000000000000000000000000000000000"
161+
)
162+
# Redeem FAssets back to underlying
163+
redemption_id = await fassets.redeem_from_agent(
164+
FAssetType.FXRP,
165+
lots=1,
166+
max_redemption_fee_bips=100, # 1%
167+
underlying_address="rXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", # XRP address
168+
executor=executor,
169+
executor_fee_nat=0,
170+
)
171+
print(f"Redemption Request ID: {redemption_id}")
172+
173+
# Get redemption request details
174+
request_id = int(redemption_id)
175+
if request_id > 0:
176+
redemption_details: dict[str, Any] = await fassets.get_redemption_request(
177+
FAssetType.FXRP, request_id
178+
)
179+
print("Redemption Details:")
180+
print(f" Agent Vault: {redemption_details['agent_vault']}")
181+
print(f" Value UBA: {redemption_details['value_uba']}")
182+
print(f" Fee UBA: {redemption_details['fee_uba']}")
183+
print(f" Payment Address: {redemption_details['payment_address']}")
184+
185+
except Exception as e:
186+
print(f"Redemption operations failed (expected with placeholders): {e}")
187+
188+
189+
async def check_other_fassets(
190+
fassets: FAssets, supported_fassets: dict[str, FAssetInfo]
191+
) -> None:
192+
"""Check status of other FAssets like FBTC and FDOGE."""
193+
# Check for FBTC on Flare Mainnet
194+
if "FBTC" in supported_fassets:
195+
print("=== FBTC Operations ===")
196+
try:
197+
fbtc_info = await fassets.get_fasset_info(FAssetType.FBTC)
198+
print(f"FBTC Info: {fbtc_info}")
199+
status = "Coming Soon" if not fbtc_info.is_active else "Active"
200+
print(f"Status: {status}")
201+
except Exception as e:
202+
print(f"Error with FBTC operations: {e}")
203+
print()
204+
205+
# Check for FDOGE on Flare Mainnet
206+
if "FDOGE" in supported_fassets:
207+
print("=== FDOGE Operations ===")
208+
try:
209+
fdoge_info = await fassets.get_fasset_info(FAssetType.FDOGE)
210+
print(f"FDOGE Info: {fdoge_info}")
211+
status = "Coming Soon" if not fdoge_info.is_active else "Active"
212+
print(f"Status: {status}")
213+
except Exception as e:
214+
print(f"Error with FDOGE operations: {e}")
215+
print()
216+
217+
218+
async def demonstrate_fxrp_operations(
219+
fassets: FAssets, supported_fassets: dict[str, FAssetInfo]
220+
) -> None:
221+
"""Demonstrate comprehensive FXRP operations."""
222+
print("=== FXRP Operations ===")
223+
try:
224+
# Get FXRP specific information
225+
fxrp_info = await fassets.get_fasset_info(FAssetType.FXRP)
226+
print(f"FXRP Info: {fxrp_info}")
227+
228+
# Get asset manager settings
229+
settings = await fassets.get_asset_manager_settings(FAssetType.FXRP)
230+
print("Asset Manager Settings:")
231+
print(f" Asset Name: {settings.get('asset_name')}")
232+
print(f" Asset Symbol: {settings.get('asset_symbol')}")
233+
print(f" Lot Size: {settings.get('lot_size_amg')}")
234+
cr_value = settings.get("minting_vault_collateral_ratio")
235+
print(f" Minting Vault CR: {cr_value}")
236+
print()
237+
238+
await check_balance_and_allowance(fassets)
239+
print()
240+
241+
await perform_swap_operations(fassets, supported_fassets)
242+
print()
243+
244+
await demonstrate_minting_workflow(fassets)
245+
print()
246+
247+
await perform_redemption_operations(fassets)
248+
print()
249+
250+
except Exception as e:
251+
msg = "Error with FXRP operations (expected with placeholder addresses)"
252+
print(f"{msg}: {e}")
253+
254+
255+
async def main() -> None:
256+
"""
257+
Comprehensive FAssets operations example - including swaps and redemptions.
258+
259+
This example demonstrates:
260+
1. Querying supported assets and agent information
261+
2. Balance and allowance checks
262+
3. FAsset swap operations using SparkDEX
263+
4. Minting and redemption workflows
264+
265+
Note: This example uses placeholder contract addresses. For real usage,
266+
update the contract addresses in the FAssets connector with actual
267+
deployed addresses.
268+
"""
269+
# Initialize the Flare AI Kit
270+
kit = FlareAIKit()
271+
272+
try:
273+
# Get the FAssets connector
274+
fassets = await kit.fassets
275+
276+
# Get and display supported FAssets
277+
supported_fassets = await fassets.get_supported_fassets()
278+
await print_supported_fassets(fassets)
279+
280+
# If FXRP is supported, demonstrate comprehensive operations
281+
if "FXRP" in supported_fassets:
282+
await demonstrate_fxrp_operations(fassets, supported_fassets)
283+
284+
# Check other FAssets
285+
await check_other_fassets(fassets, supported_fassets)
286+
287+
except Exception as e:
288+
print(f"Fatal error: {e}")
289+
290+
291+
if __name__ == "__main__":
292+
asyncio.run(main())

0 commit comments

Comments
 (0)