olas-operate-middleware 0.6.3__py3-none-any.whl → 0.7.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.
@@ -17,26 +17,24 @@
17
17
  # limitations under the License.
18
18
  #
19
19
  # ------------------------------------------------------------------------------
20
- """LI.FI Bridge provider."""
20
+ """LI.FI provider."""
21
21
 
22
22
 
23
23
  import enum
24
24
  import time
25
25
  import typing as t
26
26
  from http import HTTPStatus
27
- from math import ceil
28
27
  from urllib.parse import urlencode
29
28
 
30
29
  import requests
31
30
  from autonomy.chain.base import registry_contracts
32
31
 
33
- from operate.bridge.providers.bridge_provider import (
34
- BridgeProvider,
35
- BridgeRequest,
36
- BridgeRequestStatus,
32
+ from operate.bridge.providers.provider import (
37
33
  DEFAULT_MAX_QUOTE_RETRIES,
38
- GAS_ESTIMATE_BUFFER,
39
34
  MESSAGE_QUOTE_ZERO,
35
+ Provider,
36
+ ProviderRequest,
37
+ ProviderRequestStatus,
40
38
  QuoteData,
41
39
  )
42
40
  from operate.constants import ZERO_ADDRESS
@@ -61,50 +59,50 @@ class LiFiTransactionStatus(str, enum.Enum):
61
59
  return self.value
62
60
 
63
61
 
64
- class LiFiBridgeProvider(BridgeProvider):
65
- """LI.FI Bridge provider."""
62
+ class LiFiProvider(Provider):
63
+ """LI.FI provider."""
66
64
 
67
65
  def description(self) -> str:
68
- """Get a human-readable description of the bridge provider."""
66
+ """Get a human-readable description of the provider."""
69
67
  return "LI.FI Bridge & DEX Aggregation Protocol https://li.fi/"
70
68
 
71
- def quote(self, bridge_request: BridgeRequest) -> None:
69
+ def quote(self, provider_request: ProviderRequest) -> None:
72
70
  """Update the request with the quote."""
73
- self._validate(bridge_request)
71
+ self._validate(provider_request)
74
72
 
75
- if bridge_request.status not in (
76
- BridgeRequestStatus.CREATED,
77
- BridgeRequestStatus.QUOTE_DONE,
78
- BridgeRequestStatus.QUOTE_FAILED,
73
+ if provider_request.status not in (
74
+ ProviderRequestStatus.CREATED,
75
+ ProviderRequestStatus.QUOTE_DONE,
76
+ ProviderRequestStatus.QUOTE_FAILED,
79
77
  ):
80
78
  raise RuntimeError(
81
- f"Cannot quote bridge request {bridge_request.id} with status {bridge_request.status}."
79
+ f"Cannot quote request {provider_request.id} with status {provider_request.status}."
82
80
  )
83
81
 
84
- if bridge_request.execution_data:
82
+ if provider_request.execution_data:
85
83
  raise RuntimeError(
86
- f"Cannot quote bridge request {bridge_request.id}: execution already present."
84
+ f"Cannot quote request {provider_request.id}: execution already present."
87
85
  )
88
86
 
89
- from_chain = bridge_request.params["from"]["chain"]
90
- from_address = bridge_request.params["from"]["address"]
91
- from_token = bridge_request.params["from"]["token"]
92
- to_chain = bridge_request.params["to"]["chain"]
93
- to_address = bridge_request.params["to"]["address"]
94
- to_token = bridge_request.params["to"]["token"]
95
- to_amount = bridge_request.params["to"]["amount"]
87
+ from_chain = provider_request.params["from"]["chain"]
88
+ from_address = provider_request.params["from"]["address"]
89
+ from_token = provider_request.params["from"]["token"]
90
+ to_chain = provider_request.params["to"]["chain"]
91
+ to_address = provider_request.params["to"]["address"]
92
+ to_token = provider_request.params["to"]["token"]
93
+ to_amount = provider_request.params["to"]["amount"]
96
94
 
97
95
  if to_amount == 0:
98
- self.logger.info(f"[LI.FI BRIDGE] {MESSAGE_QUOTE_ZERO}")
96
+ self.logger.info(f"[LI.FI PROVIDER] {MESSAGE_QUOTE_ZERO}")
99
97
  quote_data = QuoteData(
100
- bridge_eta=0,
98
+ eta=0,
101
99
  elapsed_time=0,
102
100
  message=MESSAGE_QUOTE_ZERO,
103
101
  provider_data=None,
104
102
  timestamp=int(time.time()),
105
103
  )
106
- bridge_request.quote_data = quote_data
107
- bridge_request.status = BridgeRequestStatus.QUOTE_DONE
104
+ provider_request.quote_data = quote_data
105
+ provider_request.status = ProviderRequestStatus.QUOTE_DONE
108
106
  return
109
107
 
110
108
  url = "https://li.quest/v1/quote/toAmount"
@@ -122,14 +120,14 @@ class LiFiBridgeProvider(BridgeProvider):
122
120
  for attempt in range(1, DEFAULT_MAX_QUOTE_RETRIES + 1):
123
121
  start = time.time()
124
122
  try:
125
- self.logger.info(f"[LI.FI BRIDGE] GET {url}?{urlencode(params)}")
123
+ self.logger.info(f"[LI.FI PROVIDER] GET {url}?{urlencode(params)}")
126
124
  response = requests.get(
127
125
  url=url, headers=headers, params=params, timeout=30
128
126
  )
129
127
  response.raise_for_status()
130
128
  response_json = response.json()
131
129
  quote_data = QuoteData(
132
- bridge_eta=LIFI_DEFAULT_ETA,
130
+ eta=LIFI_DEFAULT_ETA,
133
131
  elapsed_time=time.time() - start,
134
132
  message=None,
135
133
  provider_data={
@@ -139,15 +137,15 @@ class LiFiBridgeProvider(BridgeProvider):
139
137
  },
140
138
  timestamp=int(time.time()),
141
139
  )
142
- bridge_request.quote_data = quote_data
143
- bridge_request.status = BridgeRequestStatus.QUOTE_DONE
140
+ provider_request.quote_data = quote_data
141
+ provider_request.status = ProviderRequestStatus.QUOTE_DONE
144
142
  return
145
143
  except requests.Timeout as e:
146
144
  self.logger.warning(
147
- f"[LI.FI BRIDGE] Timeout request on attempt {attempt}/{DEFAULT_MAX_QUOTE_RETRIES}: {e}."
145
+ f"[LI.FI PROVIDER] Timeout request on attempt {attempt}/{DEFAULT_MAX_QUOTE_RETRIES}: {e}."
148
146
  )
149
147
  quote_data = QuoteData(
150
- bridge_eta=None,
148
+ eta=None,
151
149
  elapsed_time=time.time() - start,
152
150
  message=str(e),
153
151
  provider_data={
@@ -159,11 +157,11 @@ class LiFiBridgeProvider(BridgeProvider):
159
157
  )
160
158
  except requests.RequestException as e:
161
159
  self.logger.warning(
162
- f"[LI.FI BRIDGE] Request failed on attempt {attempt}/{DEFAULT_MAX_QUOTE_RETRIES}: {e}."
160
+ f"[LI.FI PROVIDER] Request failed on attempt {attempt}/{DEFAULT_MAX_QUOTE_RETRIES}: {e}."
163
161
  )
164
162
  response_json = response.json()
165
163
  quote_data = QuoteData(
166
- bridge_eta=None,
164
+ eta=None,
167
165
  elapsed_time=time.time() - start,
168
166
  message=response_json.get("message") or str(e),
169
167
  provider_data={
@@ -177,10 +175,10 @@ class LiFiBridgeProvider(BridgeProvider):
177
175
  )
178
176
  except Exception as e: # pylint:disable=broad-except
179
177
  self.logger.warning(
180
- f"[LI.FI BRIDGE] Request failed on attempt {attempt}/{DEFAULT_MAX_QUOTE_RETRIES}: {e}."
178
+ f"[LI.FI PROVIDER] Request failed on attempt {attempt}/{DEFAULT_MAX_QUOTE_RETRIES}: {e}."
181
179
  )
182
180
  quote_data = QuoteData(
183
- bridge_eta=None,
181
+ eta=None,
184
182
  elapsed_time=time.time() - start,
185
183
  message=str(e),
186
184
  provider_data={
@@ -192,24 +190,24 @@ class LiFiBridgeProvider(BridgeProvider):
192
190
  )
193
191
  if attempt >= DEFAULT_MAX_QUOTE_RETRIES:
194
192
  self.logger.error(
195
- f"[LI.FI BRIDGE] Request failed after {DEFAULT_MAX_QUOTE_RETRIES} attempts."
193
+ f"[LI.FI PROVIDER] Request failed after {DEFAULT_MAX_QUOTE_RETRIES} attempts."
196
194
  )
197
- bridge_request.quote_data = quote_data
198
- bridge_request.status = BridgeRequestStatus.QUOTE_FAILED
195
+ provider_request.quote_data = quote_data
196
+ provider_request.status = ProviderRequestStatus.QUOTE_FAILED
199
197
  return
200
198
 
201
199
  time.sleep(2)
202
200
 
203
- def _get_approve_tx(self, bridge_request: BridgeRequest) -> t.Optional[t.Dict]:
201
+ def _get_approve_tx(self, provider_request: ProviderRequest) -> t.Optional[t.Dict]:
204
202
  """Get the approve transaction."""
205
203
  self.logger.info(
206
- f"[LI.FI BRIDGE] Get appprove transaction for bridge request {bridge_request.id}."
204
+ f"[LI.FI PROVIDER] Get appprove transaction for request {provider_request.id}."
207
205
  )
208
206
 
209
- if bridge_request.params["to"]["amount"] == 0:
207
+ if provider_request.params["to"]["amount"] == 0:
210
208
  return None
211
209
 
212
- quote_data = bridge_request.quote_data
210
+ quote_data = provider_request.quote_data
213
211
  if not quote_data:
214
212
  return None
215
213
 
@@ -232,7 +230,7 @@ class LiFiBridgeProvider(BridgeProvider):
232
230
  return None
233
231
 
234
232
  from_amount = int(quote["action"]["fromAmount"])
235
- from_ledger_api = self._from_ledger_api(bridge_request)
233
+ from_ledger_api = self._from_ledger_api(provider_request)
236
234
 
237
235
  approve_tx = registry_contracts.erc20.get_approve_tx(
238
236
  ledger_api=from_ledger_api,
@@ -242,21 +240,20 @@ class LiFiBridgeProvider(BridgeProvider):
242
240
  amount=from_amount,
243
241
  )
244
242
  approve_tx["gas"] = 200_000 # TODO backport to ERC20 contract as default
245
- BridgeProvider._update_with_gas_pricing(approve_tx, from_ledger_api)
246
- BridgeProvider._update_with_gas_estimate(approve_tx, from_ledger_api)
247
- approve_tx["gas"] = ceil(approve_tx["gas"] * GAS_ESTIMATE_BUFFER)
243
+ Provider._update_with_gas_pricing(approve_tx, from_ledger_api)
244
+ Provider._update_with_gas_estimate(approve_tx, from_ledger_api)
248
245
  return approve_tx
249
246
 
250
- def _get_bridge_tx(self, bridge_request: BridgeRequest) -> t.Optional[t.Dict]:
247
+ def _get_bridge_tx(self, provider_request: ProviderRequest) -> t.Optional[t.Dict]:
251
248
  """Get the bridge transaction."""
252
249
  self.logger.info(
253
- f"[LI.FI BRIDGE] Get bridge transaction for bridge request {bridge_request.id}."
250
+ f"[LI.FI PROVIDER] Get bridge transaction for request {provider_request.id}."
254
251
  )
255
252
 
256
- if bridge_request.params["to"]["amount"] == 0:
253
+ if provider_request.params["to"]["amount"] == 0:
257
254
  return None
258
255
 
259
- quote_data = bridge_request.quote_data
256
+ quote_data = provider_request.quote_data
260
257
  if not quote_data:
261
258
  return None
262
259
 
@@ -274,7 +271,7 @@ class LiFiBridgeProvider(BridgeProvider):
274
271
  if not transaction_request:
275
272
  return None
276
273
 
277
- from_ledger_api = self._from_ledger_api(bridge_request)
274
+ from_ledger_api = self._from_ledger_api(provider_request)
278
275
 
279
276
  bridge_tx = {
280
277
  "value": int(transaction_request["value"], 16),
@@ -288,36 +285,51 @@ class LiFiBridgeProvider(BridgeProvider):
288
285
  transaction_request["from"]
289
286
  ),
290
287
  }
291
- BridgeProvider._update_with_gas_pricing(bridge_tx, from_ledger_api)
292
- BridgeProvider._update_with_gas_estimate(bridge_tx, from_ledger_api)
288
+ Provider._update_with_gas_pricing(bridge_tx, from_ledger_api)
289
+ Provider._update_with_gas_estimate(bridge_tx, from_ledger_api)
293
290
  return bridge_tx
294
291
 
295
- def _update_execution_status(self, bridge_request: BridgeRequest) -> None:
292
+ def _get_txs(
293
+ self, provider_request: ProviderRequest, *args: t.Any, **kwargs: t.Any
294
+ ) -> t.List[t.Tuple[str, t.Dict]]:
295
+ """Get the sorted list of transactions to execute the quote."""
296
+ txs = []
297
+ approve_tx = self._get_approve_tx(provider_request)
298
+ if approve_tx:
299
+ txs.append(("approve_tx", approve_tx))
300
+ bridge_tx = self._get_bridge_tx(provider_request)
301
+ if bridge_tx:
302
+ txs.append(("bridge_tx", bridge_tx))
303
+ return txs
304
+
305
+ def _update_execution_status(self, provider_request: ProviderRequest) -> None:
296
306
  """Update the execution status."""
297
307
 
298
- if bridge_request.status not in (
299
- BridgeRequestStatus.EXECUTION_PENDING,
300
- BridgeRequestStatus.EXECUTION_UNKNOWN,
308
+ if provider_request.status not in (
309
+ ProviderRequestStatus.EXECUTION_PENDING,
310
+ ProviderRequestStatus.EXECUTION_UNKNOWN,
301
311
  ):
302
312
  return
303
313
 
304
- execution_data = bridge_request.execution_data
314
+ execution_data = provider_request.execution_data
305
315
  if not execution_data:
306
316
  raise RuntimeError(
307
- f"Cannot update bridge request {bridge_request.id}: execution data not present."
317
+ f"Cannot update request {provider_request.id}: execution data not present."
308
318
  )
309
319
 
310
- if not execution_data.from_tx_hash:
320
+ from_tx_hash = execution_data.from_tx_hash
321
+ if not from_tx_hash:
311
322
  return
312
323
 
324
+ lifi_status = LiFiTransactionStatus.UNKNOWN
313
325
  url = "https://li.quest/v1/status"
314
326
  headers = {"accept": "application/json"}
315
327
  params = {
316
- "txHash": execution_data.from_tx_hash,
328
+ "txHash": from_tx_hash,
317
329
  }
318
330
 
319
331
  try:
320
- self.logger.info(f"[LI.FI BRIDGE] GET {url}?{urlencode(params)}")
332
+ self.logger.info(f"[LI.FI PROVIDER] GET {url}?{urlencode(params)}")
321
333
  response = requests.get(url=url, headers=headers, params=params, timeout=30)
322
334
  response_json = response.json()
323
335
  lifi_status = response_json.get(
@@ -329,34 +341,35 @@ class LiFiBridgeProvider(BridgeProvider):
329
341
  response.raise_for_status()
330
342
  except Exception as e:
331
343
  self.logger.error(
332
- f"[LI.FI BRIDGE] Failed to update bridge status for {bridge_request.id}: {e}"
344
+ f"[LI.FI PROVIDER] Failed to update status for request {provider_request.id}: {e}"
333
345
  )
334
346
 
335
347
  if lifi_status == LiFiTransactionStatus.DONE:
336
- self.logger.info(f"[LI.FI BRIDGE] Execution done for {bridge_request.id}.")
337
- from_ledger_api = self._from_ledger_api(bridge_request)
338
- from_tx_hash = execution_data.from_tx_hash
339
- to_ledger_api = self._to_ledger_api(bridge_request)
348
+ self.logger.info(
349
+ f"[LI.FI PROVIDER] Execution done for {provider_request.id}."
350
+ )
351
+ from_ledger_api = self._from_ledger_api(provider_request)
352
+ to_ledger_api = self._to_ledger_api(provider_request)
340
353
  to_tx_hash = response_json.get("receiving", {}).get("txHash")
341
354
  execution_data.message = None
342
355
  execution_data.to_tx_hash = to_tx_hash
343
- execution_data.elapsed_time = BridgeProvider._tx_timestamp(
356
+ execution_data.elapsed_time = Provider._tx_timestamp(
344
357
  to_tx_hash, to_ledger_api
345
- ) - BridgeProvider._tx_timestamp(from_tx_hash, from_ledger_api)
346
- bridge_request.status = BridgeRequestStatus.EXECUTION_DONE
358
+ ) - Provider._tx_timestamp(from_tx_hash, from_ledger_api)
359
+ provider_request.status = ProviderRequestStatus.EXECUTION_DONE
347
360
  elif lifi_status == LiFiTransactionStatus.FAILED:
348
- bridge_request.status = BridgeRequestStatus.EXECUTION_FAILED
361
+ provider_request.status = ProviderRequestStatus.EXECUTION_FAILED
349
362
  elif lifi_status == LiFiTransactionStatus.PENDING:
350
- bridge_request.status = BridgeRequestStatus.EXECUTION_PENDING
363
+ provider_request.status = ProviderRequestStatus.EXECUTION_PENDING
351
364
  else:
352
- bridge_request.status = BridgeRequestStatus.EXECUTION_UNKNOWN
365
+ provider_request.status = ProviderRequestStatus.EXECUTION_UNKNOWN
353
366
 
354
- def _get_explorer_link(self, bridge_request: BridgeRequest) -> t.Optional[str]:
367
+ def _get_explorer_link(self, provider_request: ProviderRequest) -> t.Optional[str]:
355
368
  """Get the explorer link for a transaction."""
356
- if not bridge_request.execution_data:
369
+ if not provider_request.execution_data:
357
370
  return None
358
371
 
359
- tx_hash = bridge_request.execution_data.from_tx_hash
372
+ tx_hash = provider_request.execution_data.from_tx_hash
360
373
  if not tx_hash:
361
374
  return None
362
375