Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .coverage
Binary file not shown.
24 changes: 8 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Solana Agent Kit provides a growing library of plugins that enhance your Solana

* Solana Transfer - Transfer Solana tokens between the agent's wallet and the destination wallet
* Solana Ultra - Swap Solana tokens using Jupiter Ultra API with automatic slippage, priority fees, and transaction landing
* Solana DFlow Swap - Fast token swaps using DFlow API with platform fees
* Solana DFlow Swap - Fast token swaps using DFlow API
* Jupiter Trigger - Create, cancel, and manage limit orders using Jupiter Trigger API
* Jupiter Recurring - Create, cancel, and manage DCA orders using Jupiter Recurring API
* Jupiter Holdings - Get token holdings with USD values for any wallet
Expand All @@ -26,7 +26,7 @@ Solana Agent Kit provides a growing library of plugins that enhance your Solana
* Privy Ultra - Swap tokens using Jupiter Ultra with Privy delegated wallets
* Privy Trigger - Create and manage limit orders with Privy delegated wallets
* Privy Recurring - Create and manage DCA orders with Privy delegated wallets
* Privy DFlow Swap - Fast token swaps using DFlow API with Privy delegated wallets and platform fees
* Privy DFlow Swap - Fast token swaps using DFlow API with Privy delegated wallets
* Privy Wallet Address - Get the wallet address of a Privy delegated wallet
* Privy Create User - Create a new Privy user with a linked Telegram account (for bot-first flows)
* Privy Create Wallet - Create a Solana wallet for a Privy user with optional bot delegation
Expand Down Expand Up @@ -102,15 +102,15 @@ By default, Jupiter Ultra provides gasless swaps when the user has < 0.01 SOL an

### Solana DFlow Swap

This plugin enables Solana Agent to swap tokens using DFlow's Swap API with a Solana keypair. DFlow offers faster swaps compared to Jupiter Ultra with competitive rates and supports platform fees for monetization.
This plugin enables Solana Agent to swap tokens using DFlow's Swap API with a Solana keypair. DFlow offers faster swaps compared to Jupiter Ultra with competitive rates.

**Note:** Platform fees are not supported with DFlow. Use Solana Ultra (Jupiter) if you need to collect fees on swaps.

```python
config = {
"tools": {
"solana_dflow_swap": {
"private_key": "my-private-key", # Required - base58 string
"platform_fee_bps": 50, # Optional - platform fee in basis points (e.g., 50 = 0.5%)
"fee_account": "your-fee-token-account", # Optional - token account to receive platform fees
"payer_private_key": "payer-private-key", # Optional - for gasless/sponsored transactions
"rpc_url": "https://api.mainnet-beta.solana.com", # Optional - RPC URL (defaults to mainnet)
},
Expand All @@ -120,12 +120,8 @@ config = {

**Features:**
- **Fast Swaps**: DFlow typically executes faster than Jupiter Ultra
- **Platform Fees**: Collect fees on swaps (in basis points) paid to your fee account
- **Gasless Transactions**: Optionally sponsor gas fees for users via `payer_private_key`

**Platform Fee Setup:**
To collect platform fees, you need to create a token account for the output token and provide it as `fee_account`. The `platform_fee_bps` specifies the fee amount (e.g., 50 = 0.5%).

### Jupiter Trigger

This plugin enables Solana Agent to create, cancel, and manage limit orders using Jupiter's Trigger API. It's a smart tool that handles the full lifecycle of limit orders with a single action parameter.
Expand Down Expand Up @@ -363,7 +359,9 @@ config = {

### Privy DFlow Swap

This plugin enables Solana Agent to swap tokens using DFlow's Swap API with Privy delegated wallets. DFlow offers faster swaps compared to Jupiter Ultra with competitive rates and supports platform fees for monetization.
This plugin enables Solana Agent to swap tokens using DFlow's Swap API with Privy delegated wallets. DFlow offers faster swaps compared to Jupiter Ultra with competitive rates.

**Note:** Platform fees are not supported with DFlow. Use Privy Ultra (Jupiter) if you need to collect fees on swaps.

Transactions are signed via Privy and sent via your configured RPC (Helius recommended) for reliable blockhash handling and priority fees.

Expand All @@ -375,8 +373,6 @@ config = {
"app_secret": "your-privy-app-secret", # Required - your Privy application secret
"signing_key": "wallet-auth:your-signing-key", # Required - your Privy wallet authorization signing key
"rpc_url": "https://mainnet.helius-rpc.com/?api-key=YOUR_KEY", # Required - Helius recommended for priority fees
"platform_fee_bps": 50, # Optional - platform fee in basis points (e.g., 50 = 0.5%)
"fee_account": "your-fee-token-account", # Optional - token account to receive platform fees
"payer_private_key": "payer-private-key", # Optional - for gasless/sponsored transactions
},
},
Expand All @@ -385,17 +381,13 @@ config = {

**Features:**
- **Fast Swaps**: DFlow typically executes faster than Jupiter Ultra
- **Platform Fees**: Collect fees on swaps (in basis points) paid to your fee account
- **Privy Delegated Wallets**: Seamless user experience with embedded wallets
- **Helius Priority Fees**: Uses Helius priority fee estimation for reliable transaction landing
- **Gasless Transactions**: Optionally sponsor gas fees for users via `payer_private_key`

**RPC URL (Required):**
Helius RPC is strongly recommended (`https://mainnet.helius-rpc.com/?api-key=YOUR_KEY`). Helius provides priority fee estimation and better blockhash handling, which significantly improves transaction success rates. Get a free API key at [helius.dev](https://helius.dev).

**Platform Fee Setup:**
To collect platform fees, you need to create a token account for the output token and provide it as `fee_account`. The `platform_fee_bps` specifies the fee amount (e.g., 50 = 0.5%).

### Privy Wallet Address

This plugin enables Solana Agent to get the wallet address of a Privy delegated wallet.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "sakit"
version = "14.1.3"
version = "14.1.4"
description = "Solana Agent Kit"
authors = ["Bevan Hunt <bevan@bevanhunt.com>"]
license = "MIT"
Expand Down
20 changes: 5 additions & 15 deletions sakit/privy_dflow_swap.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
"""
DFlow Swap tool for Privy embedded wallets.
"""DFlow Swap tool for Privy embedded wallets.

Enables fast token swaps using DFlow's Swap API via Privy delegated wallets.
Uses the official Privy Python SDK for wallet operations.

DFlow offers faster swaps compared to Jupiter Ultra with similar liquidity
and supports platform fees for monetization.
DFlow offers faster swaps compared to Jupiter Ultra with similar liquidity.

Transactions are signed via Privy's signTransaction and sent via Helius RPC
for priority fees and reliable blockhash handling.

Note: Platform fees are not supported. Use Jupiter Ultra (privy_ultra) if you
need to collect fees on swaps.
"""

import base64
Expand Down Expand Up @@ -207,9 +208,6 @@ def __init__(self, registry: Optional[ToolRegistry] = None):
self._app_id: Optional[str] = None
self._app_secret: Optional[str] = None
self._signing_key: Optional[str] = None
self._platform_fee_bps: Optional[int] = None
self._fee_account: Optional[str] = None
self._referral_account: Optional[str] = None
self._payer_private_key: Optional[str] = None
self._rpc_url: Optional[str] = None

Expand Down Expand Up @@ -249,9 +247,6 @@ def configure(self, config: Dict[str, Any]) -> None:
self._app_id = tool_cfg.get("app_id")
self._app_secret = tool_cfg.get("app_secret")
self._signing_key = tool_cfg.get("signing_key")
self._platform_fee_bps = tool_cfg.get("platform_fee_bps")
self._fee_account = tool_cfg.get("fee_account")
self._referral_account = tool_cfg.get("referral_account")
self._payer_private_key = tool_cfg.get("payer_private_key")
# RPC URL for sending transactions (Helius recommended for priority fees)
self._rpc_url = tool_cfg.get("rpc_url")
Expand Down Expand Up @@ -306,10 +301,6 @@ async def execute( # pragma: no cover
amount=int(amount),
user_public_key=public_key,
slippage_bps=slippage_bps if slippage_bps > 0 else None,
platform_fee_bps=self._platform_fee_bps,
platform_fee_mode="outputMint",
fee_account=self._fee_account,
referral_account=self._referral_account,
sponsor=sponsor,
)

Expand Down Expand Up @@ -386,7 +377,6 @@ async def execute( # pragma: no cover
"input_mint": order_result.input_mint,
"output_mint": order_result.output_mint,
"price_impact": order_result.price_impact_pct,
"platform_fee": order_result.platform_fee,
"execution_mode": order_result.execution_mode,
"message": f"Swap successful! Signature: {signature}",
}
Expand Down
20 changes: 5 additions & 15 deletions sakit/solana_dflow_swap.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""
DFlow Swap tool for Solana wallets.
"""DFlow Swap tool for Solana wallets.

Enables fast token swaps using DFlow's Swap API with a Solana keypair.
DFlow offers faster swaps compared to Jupiter Ultra with similar liquidity
and supports platform fees for monetization.
DFlow offers faster swaps compared to Jupiter Ultra with similar liquidity.

Note: Platform fees are not supported. Use Jupiter Ultra (solana_ultra) if you
need to collect fees on swaps.
"""

import base64
Expand Down Expand Up @@ -77,9 +78,6 @@ def __init__(self, registry: Optional[ToolRegistry] = None):
registry=registry,
)
self._private_key: Optional[str] = None
self._platform_fee_bps: Optional[int] = None
self._fee_account: Optional[str] = None
self._referral_account: Optional[str] = None
self._payer_private_key: Optional[str] = None
self._rpc_url: Optional[str] = None

Expand Down Expand Up @@ -113,9 +111,6 @@ def configure(self, config: Dict[str, Any]) -> None:
super().configure(config)
tool_cfg = config.get("tools", {}).get("solana_dflow_swap", {})
self._private_key = tool_cfg.get("private_key")
self._platform_fee_bps = tool_cfg.get("platform_fee_bps")
self._fee_account = tool_cfg.get("fee_account")
self._referral_account = tool_cfg.get("referral_account")
self._payer_private_key = tool_cfg.get("payer_private_key")
self._rpc_url = tool_cfg.get("rpc_url") or DEFAULT_RPC_URL

Expand Down Expand Up @@ -150,10 +145,6 @@ async def execute(
amount=amount,
user_public_key=user_pubkey,
slippage_bps=slippage_bps if slippage_bps > 0 else None,
platform_fee_bps=self._platform_fee_bps,
platform_fee_mode="outputMint",
fee_account=self._fee_account,
referral_account=self._referral_account,
sponsor=sponsor,
)

Expand Down Expand Up @@ -191,7 +182,6 @@ async def execute(
"input_mint": order_result.input_mint,
"output_mint": order_result.output_mint,
"price_impact": order_result.price_impact_pct,
"platform_fee": order_result.platform_fee,
"execution_mode": order_result.execution_mode,
"message": f"Swap successful! Signature: {signature}",
}
Expand Down
11 changes: 5 additions & 6 deletions tests/test_privy_dflow_swap_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ def test_configure_sets_credentials(self):
"app_id": "test_app_id",
"app_secret": "test_app_secret",
"signing_key": "test_signing_key",
"platform_fee_bps": 50,
"fee_account": "FeeAccount123",
"payer_private_key": "payer_key",
"rpc_url": "https://custom-rpc.com",
}
}
}
Expand All @@ -76,8 +76,8 @@ def test_configure_sets_credentials(self):
assert tool._app_id == "test_app_id"
assert tool._app_secret == "test_app_secret"
assert tool._signing_key == "test_signing_key"
assert tool._platform_fee_bps == 50
assert tool._fee_account == "FeeAccount123"
assert tool._payer_private_key == "payer_key"
assert tool._rpc_url == "https://custom-rpc.com"


class TestPrivyDFlowSwapToolExecute:
Expand All @@ -96,8 +96,7 @@ def configured_tool(self):
"signing_key": "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg"
+ "A" * 43
+ "=",
"platform_fee_bps": 50,
"fee_account": "FeeAccount123",
"rpc_url": "https://api.mainnet-beta.solana.com",
}
}
}
Expand Down
59 changes: 2 additions & 57 deletions tests/test_solana_dflow_swap_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ def test_configure_sets_credentials(self):
"tools": {
"solana_dflow_swap": {
"private_key": "test_private_key",
"platform_fee_bps": 50,
"fee_account": "FeeAccount123",
"referral_account": "RefAccount123",
"payer_private_key": "payer_key",
"rpc_url": "https://custom-rpc.com",
}
Expand All @@ -81,9 +78,6 @@ def test_configure_sets_credentials(self):
tool.configure(config)

assert tool._private_key == "test_private_key"
assert tool._platform_fee_bps == 50
assert tool._fee_account == "FeeAccount123"
assert tool._referral_account == "RefAccount123"
assert tool._payer_private_key == "payer_key"
assert tool._rpc_url == "https://custom-rpc.com"

Expand Down Expand Up @@ -214,15 +208,15 @@ def test_plugin_configure(self):
"tools": {
"solana_dflow_swap": {
"private_key": "test_private_key",
"platform_fee_bps": 100,
"payer_private_key": "payer_key",
}
}
}
plugin.configure(config)

tool = plugin.get_tools()[0]
assert tool._private_key == "test_private_key"
assert tool._platform_fee_bps == 100
assert tool._payer_private_key == "payer_key"


class TestSignDFlowTransaction:
Expand Down Expand Up @@ -469,55 +463,6 @@ async def test_execute_with_slippage_bps(self):
call_kwargs = mock_dflow_instance.get_order.call_args.kwargs
assert call_kwargs.get("slippage_bps") == 100

@pytest.mark.asyncio
async def test_execute_with_platform_fees(self):
"""Should pass platform fee configuration."""
tool = SolanaDFlowSwapTool()
tool._private_key = "5MaiiCavjCmn9Hs1o3eznqDEhRwxo7pXiAYez7keQUviUkauRiTMD8DrESdrNjN8zd9mTmVhRvBJeg5vhyvgrAhG"
tool._rpc_url = "https://api.mainnet-beta.solana.com"
tool._platform_fee_bps = 50
tool._fee_account = "FeeAccount123"
tool._referral_account = "RefAccount123"

with (
patch("sakit.solana_dflow_swap.DFlowSwap") as MockDFlow,
patch("sakit.solana_dflow_swap._sign_dflow_transaction") as mock_sign,
patch.object(
tool, "_send_transaction", new_callable=AsyncMock
) as mock_send,
):
mock_dflow_instance = MagicMock()
mock_order_result = MagicMock(
success=True,
transaction="dHJhbnNhY3Rpb24=",
in_amount="1000000000",
out_amount="50000000",
min_out_amount="49500000",
input_mint="So11111111111111111111111111111111111111112",
output_mint="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
price_impact_pct="0.1",
platform_fee="100",
execution_mode="direct",
error=None,
)
mock_dflow_instance.get_order = AsyncMock(return_value=mock_order_result)
MockDFlow.return_value = mock_dflow_instance

mock_sign.return_value = "c2lnbmVkX3R4"
mock_send.return_value = "5abc123def456"

await tool.execute(
input_mint="So11111111111111111111111111111111111111112",
output_mint="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
amount=1000000000,
)

# Verify fee config was passed
call_kwargs = mock_dflow_instance.get_order.call_args.kwargs
assert call_kwargs.get("platform_fee_bps") == 50
assert call_kwargs.get("fee_account") == "FeeAccount123"
assert call_kwargs.get("referral_account") == "RefAccount123"

@pytest.mark.asyncio
async def test_execute_exception_handling(self):
"""Should handle exceptions gracefully."""
Expand Down