Skip to content

Commit 3cc5403

Browse files
committed
Merge branch 'release/0.1.9'
2 parents 02ef863 + 9d909e2 commit 3cc5403

File tree

23 files changed

+980
-414
lines changed

23 files changed

+980
-414
lines changed

app/tg-bot/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "solbot-tgbot"
3-
version = "0.1.8"
3+
version = "0.1.9"
44
description = "Telegram bot for Solana trading"
55
authors = [
66
{ name = "mkdir700", email = "mkdir700@gmail.com" },

app/trading/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "solbot-trading"
3-
version = "0.1.8"
3+
version = "0.1.9"
44
description = "Trading for Solana"
55
authors = [
66
{ name = "mkdir700", email = "mkdir700@gmail.com" },

app/trading/trading/executor.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@
2121
class TradingExecutor:
2222
def __init__(self, client: AsyncClient):
2323
self._rpc_client = client
24-
self._jito_client = JitoClient()
25-
self._raydium_api = RaydiumAPI()
2624
self._launch_cache = LaunchCache()
25+
self._trading_service = TradingService(self._rpc_client)
2726

2827
@provide_session
2928
async def __get_keypair(self, pubkey: str, *, session=NEW_ASYNC_SESSION) -> Keypair:
@@ -91,19 +90,14 @@ async def exec(self, swap_event: SwapEvent):
9190
# NOTE: 测试下来不是很理想,暂时使用备选方案
9291
elif swap_event.program_id == RAY_V4_PROGRAM_ID:
9392
logger.info("Program ID is RayV4")
94-
trade_route = TradingRoute.GMGN
93+
trade_route = TradingRoute.DEX
9594
elif program_id is None or program_id == RAY_V4_PROGRAM_ID:
9695
logger.warning("Program ID is Unknown, So We use thrid party to trade")
97-
trade_route = TradingRoute.GMGN
96+
trade_route = TradingRoute.DEX
9897
else:
9998
raise ValueError(f"Program ID is not supported, {swap_event.program_id}")
10099

101-
sig = await TradingService.create(
102-
trade_route,
103-
self._rpc_client,
104-
use_jito=settings.trading.use_jito,
105-
jito_client=self._jito_client,
106-
).swap(
100+
sig = await self._trading_service.use_route(trade_route).swap(
107101
keypair,
108102
token_address,
109103
swap_event.ui_amount,

app/trading/trading/main.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import asyncio
22
import time
3-
import httpx
43
from typing import Optional
54

65
import backoff
7-
6+
import httpx
87
from common.cp.swap_event import SwapEventConsumer
98
from common.cp.swap_result import SwapResultProducer
109
from common.log import logger
11-
from common.types.swap import SwapEvent, SwapResult
1210
from common.prestart import pre_start
11+
from common.types.swap import SwapEvent, SwapResult
1312
from db.redis import RedisClient
1413
from trading.copytrade import CopyTradeProcessor
1514
from trading.executor import TradingExecutor
@@ -47,8 +46,6 @@ async def _process_single_swap_event(self, swap_event: SwapEvent):
4746
"""处理单个交易事件的核心逻辑"""
4847
async with self.semaphore:
4948
logger.info(f"Processing swap event: {swap_event}")
50-
sig = None
51-
swap_result = None
5249

5350
try:
5451
sig = await self._execute_swap(swap_event)

app/trading/trading/transaction/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
from trading.transaction.base import TransactionSender
22
from trading.transaction.builders.base import TransactionBuilder
3-
from trading.transaction.factory import TransactionFactory, TradingService
3+
from trading.transaction.factory import TradingService
44
from trading.transaction.protocol import TradingRoute
55
from trading.transaction.sender import DefaultTransactionSender, JitoTransactionSender
66

77
__all__ = [
88
"TransactionBuilder",
99
"TransactionSender",
10-
"TransactionFactory",
1110
"TradingService",
1211
"TradingRoute",
1312
"DefaultTransactionSender",

app/trading/trading/transaction/base.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,27 @@
11
from abc import ABC, abstractmethod
2-
from typing import Optional
32

43
from solana.rpc.async_api import AsyncClient
5-
from solana.rpc.types import TxOpts
6-
from solders.signature import Signature
7-
from solders.transaction import VersionedTransaction
8-
9-
from common.utils.jito import JitoClient
4+
from solders.signature import Signature # type: ignore
5+
from solders.transaction import VersionedTransaction # type: ignore
106

117

128
class TransactionSender(ABC):
139
"""交易发送器的抽象基类"""
1410

15-
def __init__(self, client: AsyncClient | JitoClient):
16-
self.client = client
11+
def __init__(self, rpc_client: AsyncClient):
12+
self.rpc_client = rpc_client
1713

1814
@abstractmethod
1915
async def send_transaction(
2016
self,
2117
transaction: VersionedTransaction,
22-
opts: Optional[TxOpts] = None,
18+
**kwargs,
2319
) -> Signature:
2420
"""发送交易
2521
2622
Args:
2723
transaction (VersionedTransaction): 要发送的交易
28-
opts (Optional[TxOpts], optional): 交易选项. Defaults to None.
24+
**kwargs: 可选的关键字参数
2925
3026
Returns:
3127
Signature: 交易签名

app/trading/trading/transaction/builders/gmgn.py

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import httpx
1+
from cache.token_info import TokenInfoCache
2+
from common.config import settings
3+
from common.constants import SOL_DECIMAL, WSOL
4+
from common.utils.gmgn import GmgnAPI
25
from solana.rpc.async_api import AsyncClient
36
from solders.keypair import Keypair # type: ignore
47
from solders.transaction import VersionedTransaction # type: ignore
58

6-
from cache.token_info import TokenInfoCache
7-
from common.config import settings
8-
from common.constants import SOL_DECIMAL, WSOL
9-
from trading.exceptions import NoRouteFound
109
from trading.swap import SwapDirection, SwapInType
1110
from trading.tx import sign_transaction_from_raw
1211

@@ -17,10 +16,9 @@ class GMGNTransactionBuilder(TransactionBuilder):
1716
"""GMGN 交易构建器"""
1817

1918
def __init__(self, rpc_client: AsyncClient) -> None:
20-
self.client = httpx.AsyncClient(base_url="https://gmgn.ai")
21-
self.api_path = "/defi/router/v1/sol/tx/get_swap_route"
22-
self.rpc_client = rpc_client
19+
super().__init__(rpc_client=rpc_client)
2320
self.token_info_cache = TokenInfoCache()
21+
self.gmgn_client = GmgnAPI()
2422

2523
async def build_swap_transaction(
2624
self,
@@ -73,30 +71,14 @@ async def build_swap_transaction(
7371
fee = priority_fee
7472

7573
slippage = slippage_bps / 100
76-
params = {
77-
"token_in_address": token_in,
78-
"token_out_address": token_out,
79-
"in_amount": amount,
80-
"from_address": keypair.pubkey().__str__(),
81-
"slippage": slippage,
82-
"swap_mode": swap_mode,
83-
"fee": fee,
84-
}
85-
86-
# TODO: 获取报价解耦
87-
resp = await self.client.get(self.api_path, params=params)
88-
resp.raise_for_status()
89-
90-
js = resp.json()
91-
if js["code"] != 0:
92-
msg = js["msg"]
93-
if "no route" in msg:
94-
raise NoRouteFound(msg)
95-
raise Exception("Error: {}, Argument: {}".format(js["msg"], params))
96-
97-
data = js["data"]
98-
raw_tx = data["raw_tx"]
99-
swap_tx = raw_tx["swapTransaction"]
100-
74+
swap_tx = await self.gmgn_client.get_swap_transaction(
75+
token_in_address=token_in,
76+
token_out_address=token_out,
77+
in_amount=amount,
78+
from_address=keypair.pubkey().__str__(),
79+
slippage=slippage,
80+
swap_mode=swap_mode,
81+
fee=fee,
82+
)
10183
signed_tx = await sign_transaction_from_raw(swap_tx, keypair)
10284
return signed_tx
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from cache.token_info import TokenInfoCache
2+
from common.constants import SOL_DECIMAL, WSOL
3+
from common.utils.jupiter import JupiterAPI
4+
from solana.rpc.async_api import AsyncClient
5+
from solders.keypair import Keypair # type: ignore
6+
from solders.transaction import VersionedTransaction # type: ignore
7+
8+
from trading.swap import SwapDirection, SwapInType
9+
from trading.tx import sign_transaction_from_raw
10+
11+
from .base import TransactionBuilder
12+
13+
14+
class JupiterTransactionBuilder(TransactionBuilder):
15+
"""Jupiter 交易构建器"""
16+
17+
def __init__(self, rpc_client: AsyncClient) -> None:
18+
super().__init__(rpc_client=rpc_client)
19+
self.token_info_cache = TokenInfoCache()
20+
self.jupiter_client = JupiterAPI()
21+
22+
async def build_swap_transaction(
23+
self,
24+
keypair: Keypair,
25+
token_address: str,
26+
ui_amount: float,
27+
swap_direction: SwapDirection,
28+
slippage_bps: int,
29+
in_type: SwapInType | None = None,
30+
use_jito: bool = False,
31+
priority_fee: float | None = None,
32+
) -> VersionedTransaction:
33+
"""Build swap transaction with GMGN API.
34+
35+
Args:
36+
token_address (str): token address
37+
amount_in (float): amount in
38+
swap_direction (SwapDirection): swap direction
39+
slippage (int): slippage, percentage
40+
in_type (SwapInType | None, optional): in type. Defaults to None.
41+
use_jto (bool, optional): use jto. Defaults to False.
42+
priority_fee (float | None, optional): priority fee. Defaults to None.
43+
44+
Returns:
45+
VersionedTransaction: The built transaction ready to be signed and sent
46+
"""
47+
if swap_direction == "sell" and in_type is None:
48+
raise ValueError("in_type must be specified when selling")
49+
50+
if swap_direction == SwapDirection.Buy:
51+
token_in = str(WSOL)
52+
token_out = token_address
53+
amount = int(ui_amount * SOL_DECIMAL)
54+
elif swap_direction == SwapDirection.Sell:
55+
token_info = await self.token_info_cache.get(token_address)
56+
if token_info is None:
57+
raise ValueError("Token info not found")
58+
decimals = token_info.decimals
59+
token_in = token_address
60+
token_out = str(WSOL)
61+
amount = int(ui_amount * 10**decimals)
62+
else:
63+
raise ValueError("swap_direction must be buy or sell")
64+
65+
if use_jito and priority_fee is None:
66+
raise ValueError("priority_fee must be specified when using jito")
67+
68+
swap_tx_response = await self.jupiter_client.get_swap_transaction(
69+
input_mint=token_in,
70+
output_mint=token_out,
71+
user_publickey=str(keypair.pubkey()),
72+
amount=amount,
73+
slippage_bps=slippage_bps,
74+
use_jito=use_jito,
75+
jito_tip_lamports=int(priority_fee * SOL_DECIMAL) if priority_fee else None,
76+
)
77+
swap_tx = swap_tx_response["swapTransaction"]
78+
signed_tx = await sign_transaction_from_raw(swap_tx, keypair)
79+
return signed_tx

0 commit comments

Comments
 (0)