Skip to content

Commit 1959f08

Browse files
committed
create crosschain api
1 parent f52cf52 commit 1959f08

File tree

18 files changed

+504
-154
lines changed

18 files changed

+504
-154
lines changed
Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from abc import ABC, abstractmethod
21
from typing import Optional
32

43
from pydantic import BaseModel, conint, constr
@@ -33,50 +32,3 @@ class ProvidersConfigModel(BaseModel):
3332
class AllProvidersConfigModel(ProvidersConfigModel, BaseModel):
3433
chain_id: Optional[int] = None
3534

36-
37-
class ChainSwapInfo(ABC):
38-
39-
def __init__(self, chain_id: int):
40-
self._chain_id = chain_id
41-
42-
@abstractmethod
43-
async def get_type(self) -> str:
44-
"""
45-
Args:
46-
self: Access the class attributes
47-
48-
Returns:
49-
A cross_chain or single_chain
50-
"""
51-
52-
@property
53-
def chain_id(self):
54-
return self._chain_id
55-
56-
57-
class SingleChainSwapInfo(ChainSwapInfo):
58-
59-
def __init__(self, chain_id: int):
60-
super().__init__(chain_id)
61-
62-
def get_type(self) -> str:
63-
return 'single_chain'
64-
65-
66-
class CrossChainSwapInfo(ChainSwapInfo):
67-
def __init__(self, give_chain_id: int, take_chain_id: int):
68-
super().__init__(give_chain_id)
69-
self._give_chain_id = give_chain_id
70-
self._take_chain_id = take_chain_id
71-
72-
def get_type(self) -> str:
73-
return 'cross_chain'
74-
75-
@property
76-
def give_chain_id(self) -> int:
77-
return self._give_chain_id
78-
79-
@property
80-
def take_chain_id(self) -> int:
81-
return self._take_chain_id
82-
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
from typing import TypeVar
22

3+
from meta_aggregation_api.providers.base_crosschain_provider import CrossChainProvider
34
from meta_aggregation_api.providers.base_provider import BaseProvider
45

56
T = TypeVar("T")
67

78

89
class ProviderRegistry:
9-
def __init__(self, *providers: BaseProvider):
10+
def __init__(self, *providers: BaseProvider | CrossChainProvider):
1011
self.provider_by_name = {
1112
provider.PROVIDER_NAME: provider for provider in providers
1213
}
1314

14-
def __getitem__(self, provider_name: str) -> BaseProvider:
15+
def __getitem__(self, provider_name: str) -> BaseProvider | CrossChainProvider:
1516
return self.provider_by_name[provider_name]
1617

17-
def get(self, provider_name: str, default: T = None) -> BaseProvider | T:
18+
def get(self, provider_name: str,
19+
default: T = None) -> BaseProvider | CrossChainProvider | T:
1820
return self.provider_by_name.get(provider_name, default)
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import asyncio
2+
from abc import ABC, abstractmethod
3+
from typing import Optional
4+
5+
import aiohttp
6+
from pydantic import ValidationError
7+
8+
from meta_aggregation_api.clients.apm_client import ApmClient
9+
from meta_aggregation_api.config import Config
10+
from meta_aggregation_api.models.meta_agg_models import (
11+
ProviderPriceResponse,
12+
ProviderQuoteResponse,
13+
)
14+
from meta_aggregation_api.utils.errors import (
15+
BaseAggregationProviderError,
16+
ParseResponseError,
17+
ProviderTimeoutError,
18+
)
19+
from meta_aggregation_api.utils.logger import capture_exception
20+
21+
22+
class CrossChainProvider(ABC):
23+
PROVIDER_NAME = 'base_crosschain_provider'
24+
REQUEST_TIMEOUT = 7
25+
26+
def __init__(
27+
self,
28+
session: aiohttp.ClientSession,
29+
config: Config,
30+
apm_client: ApmClient,
31+
**_,
32+
):
33+
self.apm_client = apm_client
34+
self.aiohttp_session = session
35+
self.config = config
36+
37+
@abstractmethod
38+
async def get_swap_quote(
39+
self,
40+
buy_token: str,
41+
sell_token: str,
42+
sell_amount: int,
43+
chain_id_from: int,
44+
chain_id_to: int,
45+
taker_address: str,
46+
gas_price: Optional[int] = None,
47+
slippage_percentage: Optional[float] = None,
48+
fee_recipient: Optional[str] = None,
49+
buy_token_percentage_fee: Optional[float] = None,
50+
) -> ProviderQuoteResponse:
51+
"""
52+
The get_swap_quote function is used to get the data for a swap from the provider.
53+
Args:
54+
self: Access the class attributes
55+
buy_token:str: Token is being buy
56+
sell_token:str: Token is being sold
57+
sell_amount:int: Amount of sell_token to sell
58+
chain_id_from:int: Specify the chain on which the transaction will be executed, a chain where the cross-chain swap will start
59+
chain_id_to:int: chain where the cross-chain swap will finish
60+
taker_address:str: Address who makes the transaction and will receive tokens
61+
gas_price:Optional[int]=None: Specify the gas price for the transaction
62+
slippage_percentage:Optional[float]=None: Specify the percentage of slippage to apply to the quote
63+
fee_recipient:Optional[str]=None: Address who will receive the fee
64+
buy_token_percentage_fee:Optional[float]=None: Percentage of the buy_token fee that will be paid to the fee_recipient
65+
66+
Returns:
67+
A ProviderQuoteResponse object with the data for the swap. Check return type for more info.
68+
"""
69+
70+
@abstractmethod
71+
async def get_swap_price(
72+
self,
73+
buy_token: str,
74+
sell_token: str,
75+
sell_amount: int,
76+
chain_id_from: int,
77+
chain_id_to: int,
78+
gas_price: Optional[int] = None,
79+
slippage_percentage: Optional[float] = None,
80+
taker_address: Optional[str] = None,
81+
fee_recipient: Optional[str] = None,
82+
buy_token_percentage_fee: Optional[float] = None,
83+
) -> ProviderPriceResponse:
84+
"""
85+
The get_swap_price function is used to find the best price for a swap from the provider.
86+
It doesn't require the taker_address to be specified.
87+
Args:
88+
self: Access the class attributes
89+
buy_token:str: Token is being buy
90+
sell_token:str: Token is being sold
91+
sell_amount:int: Amount of sell_token to sell
92+
chain_id_from:int: Specify the chain on which the transaction will be executed, a chain where the cross-chain swap will start
93+
chain_id_to:int: chain where the cross-chain swap will finish
94+
gas_price:Optional[int]=None: Specify the gas price for the transaction
95+
slippage_percentage:Optional[float]=None: Specify the percentage of slippage to apply to the quote
96+
taker_address:Optional[str]=None: Address who makes the transaction and will receive tokens
97+
fee_recipient:Optional[str]=None: Address who will receive the fee
98+
buy_token_percentage_fee:Optional[float]=None: Percentage of the buy_token fee that will be paid to the fee_recipient
99+
100+
Returns:
101+
A ProviderPriceResponse object with the price for the swap. Check return type for more info.
102+
"""
103+
104+
def handle_exception(
105+
self, exception: Exception, **kwargs
106+
) -> BaseAggregationProviderError:
107+
capture_exception(self.apm_client)
108+
if isinstance(exception, (KeyError, ValidationError)):
109+
exc = ParseResponseError(self.PROVIDER_NAME, str(exception), **kwargs)
110+
return exc
111+
if isinstance(
112+
exception, (aiohttp.ServerDisconnectedError, asyncio.TimeoutError)
113+
):
114+
exc = ProviderTimeoutError(self.PROVIDER_NAME, str(exception), **kwargs)
115+
return exc

meta_aggregation_api/providers/base_provider.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
from meta_aggregation_api.clients.apm_client import ApmClient
99
from meta_aggregation_api.config import Config
10-
from meta_aggregation_api.models.chain import ChainSwapInfo
1110
from meta_aggregation_api.models.meta_agg_models import (
1211
ProviderPriceResponse,
1312
ProviderQuoteResponse,
@@ -41,7 +40,7 @@ async def get_swap_quote(
4140
buy_token: str,
4241
sell_token: str,
4342
sell_amount: int,
44-
chain_info: ChainSwapInfo,
43+
chain_id: int,
4544
taker_address: str,
4645
gas_price: Optional[int] = None,
4746
slippage_percentage: Optional[float] = None,
@@ -55,7 +54,7 @@ async def get_swap_quote(
5554
buy_token:str: Token is being buy
5655
sell_token:str: Token is being sold
5756
sell_amount:int: Amount of sell_token to sell
58-
chain_info:ChainSwapInfo: Information about chains to execute transaction
57+
chain_id:int: Specify the chain on which the transaction will be executed
5958
taker_address:str: Address who makes the transaction and will receive tokens
6059
gas_price:Optional[int]=None: Specify the gas price for the transaction
6160
slippage_percentage:Optional[float]=None: Specify the percentage of slippage to apply to the quote
@@ -72,7 +71,7 @@ async def get_swap_price(
7271
buy_token: str,
7372
sell_token: str,
7473
sell_amount: int,
75-
chain_info: ChainSwapInfo,
74+
chain_id: int,
7675
gas_price: Optional[int] = None,
7776
slippage_percentage: Optional[float] = None,
7877
taker_address: Optional[str] = None,
@@ -87,7 +86,7 @@ async def get_swap_price(
8786
buy_token:str: Token is being buy
8887
sell_token:str: Token is being sold
8988
sell_amount:int: Amount of sell_token to sell
90-
chain_info:ChainSwapInfo: Information about chains to execute transaction
89+
chain_id:int: Specify the chain on which the transaction will be executed
9190
gas_price:Optional[int]=None: Specify the gas price for the transaction
9291
slippage_percentage:Optional[float]=None: Specify the percentage of slippage to apply to the quote
9392
taker_address:Optional[str]=None: Address who makes the transaction and will receive tokens

meta_aggregation_api/providers/debridge_dln_v1/debridge_dln_provider_v1.py

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@
1010
from aiohttp import ClientResponse, ClientResponseError, ServerDisconnectedError
1111
from pydantic import ValidationError
1212

13-
from meta_aggregation_api.models.chain import CrossChainSwapInfo
1413
from meta_aggregation_api.models.meta_agg_models import (
1514
ProviderPriceResponse,
1615
ProviderQuoteResponse,
1716
)
1817
from meta_aggregation_api.models.provider_response_models import SwapSources
19-
from meta_aggregation_api.providers.base_provider import BaseProvider
18+
from meta_aggregation_api.providers.base_crosschain_provider import CrossChainProvider
2019
from meta_aggregation_api.utils.errors import (
2120
AggregationProviderError,
2221
BaseAggregationProviderError,
@@ -26,10 +25,10 @@
2625
logger = get_logger(__name__)
2726

2827

29-
class DebridgeDlnProviderV1(BaseProvider):
28+
class DebridgeDlnProviderV1(CrossChainProvider):
3029
"""https://docs.debridge.finance"""
3130

32-
TRADING_API = os.environ.get('DEBRIDGE_TRADING_API', 'https://api.dln.trade/v1.0/dln')
31+
TRADING_API = os.environ.get('DEBRIDGE_TRADING_API', 'https://api.dln.trade/v1.0/dln/order')
3332
ORDER_API = os.environ.get('DEBRIDGE_ORDER_API', 'https://dln-api.debridge.finance/api')
3433

3534
with open(Path(__file__).parent / 'config.json') as f:
@@ -90,15 +89,14 @@ async def get_swap_price(
9089
buy_token: str,
9190
sell_token: str,
9291
sell_amount: int,
93-
chain_info: CrossChainSwapInfo,
92+
chain_id_from: int,
93+
chain_id_to: int,
9494
gas_price: Optional[int] = None,
9595
slippage_percentage: Optional[float] = 1,
9696
taker_address: Optional[str] = None,
9797
fee_recipient: Optional[str] = None,
9898
buy_token_percentage_fee: Optional[float] = None,
9999
):
100-
give_chain_id = chain_info.give_chain_id
101-
take_chain_id = chain_info.take_chain_id
102100
if buy_token.lower() == self.config.NATIVE_TOKEN_ADDRESS:
103101
buy_token = '0x0000000000000000000000000000000000000000'
104102

@@ -107,10 +105,10 @@ async def get_swap_price(
107105

108106
url = '%s/quote' % (self.TRADING_API)
109107
params = {
110-
'srcChainId': give_chain_id,
108+
'srcChainId': chain_id_from,
111109
'srcChainTokenIn': sell_token,
112110
'srcChainTokenInAmount': sell_amount,
113-
'dstChainId': take_chain_id,
111+
'dstChainId': chain_id_to,
114112
'dstChainTokenOut': buy_token,
115113
'affiliateFeePercent': 0,
116114
'prependOperatingExpenses': 'false',
@@ -123,8 +121,8 @@ async def get_swap_price(
123121
ServerDisconnectedError,
124122
) as e:
125123
exc = self.handle_exception(
126-
e, params=params, token_address=sell_token, give_chain_id=give_chain_id,
127-
take_chain_id=take_chain_id
124+
e, params=params, token_address=sell_token, give_chain_id=chain_id_from,
125+
take_chain_id=chain_id_to
128126
)
129127
raise exc
130128
return self._convert_response_from_swap_price(response)
@@ -134,35 +132,32 @@ async def get_swap_quote(
134132
buy_token: str,
135133
sell_token: str,
136134
sell_amount: int,
137-
chain_info: CrossChainSwapInfo,
135+
chain_id_from: int,
136+
chain_id_to: int,
138137
taker_address: str,
139138
gas_price: Optional[int] = None,
140139
slippage_percentage: Optional[float] = None,
141140
fee_recipient: Optional[str] = None,
142141
buy_token_percentage_fee: Optional[float] = None,
143142
) -> ProviderQuoteResponse:
144-
give_chain_id = chain_info.give_chain_id
145-
take_chain_id = chain_info.take_chain_id
146143
if buy_token.lower() == self.config.NATIVE_TOKEN_ADDRESS:
147144
buy_token = '0x0000000000000000000000000000000000000000'
148145

149146
if sell_token.lower() == self.config.NATIVE_TOKEN_ADDRESS:
150147
sell_token = '0x0000000000000000000000000000000000000000'
151148

152-
url = '%s/createOrder' % (self.TRADING_API)
149+
url = '%s/create-tx' % (self.TRADING_API)
153150
params = {
154-
'srcChainId': give_chain_id,
151+
'srcChainId': chain_id_from,
155152
'srcChainTokenIn': sell_token,
156153
'srcChainTokenInAmount': sell_amount,
157-
'dstChainId': take_chain_id,
154+
'dstChainId': chain_id_to,
158155
'dstChainTokenOut': buy_token,
159156
'affiliateFeePercent': 0,
160157
'dstChainTokenOutAmount': 'auto',
158+
'srcChainOrderAuthorityAddress': taker_address,
161159
'dstChainTokenOutRecipient': taker_address,
162-
'srcChainRefundAddress': taker_address,
163160
'dstChainOrderAuthorityAddress': taker_address,
164-
'senderAddress': taker_address,
165-
'enableEstimate': 'true',
166161
}
167162

168163
try:
@@ -173,7 +168,7 @@ async def get_swap_quote(
173168
ServerDisconnectedError,
174169
) as e:
175170
exc = self.handle_exception(
176-
e, params=params, token_address=sell_token, chain_id=chain_info.chain_id
171+
e, params=params, token_address=sell_token, chain_id=chain_id_from
177172
)
178173
raise exc
179174
return self._convert_response_from_swap_quote(response)
@@ -224,7 +219,7 @@ def _convert_response_from_swap_quote(
224219
buy_amount=estimation['dstChainTokenOut']['amount'],
225220
sell_amount=estimation['srcChainTokenIn']['amount'],
226221
gas_price=0,
227-
gas=tx['gasLimit'],
222+
gas=0,
228223
value=tx['value'],
229224
price=str(price),
230225
data=tx['data'],

0 commit comments

Comments
 (0)