Compare commits
1 Commits
compound-v
...
add-1inch-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91cd16533d |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -22,6 +22,3 @@ cache
|
||||
|
||||
# env
|
||||
.envrc
|
||||
|
||||
# pycharm
|
||||
.idea
|
||||
|
||||
57
backfill.py
Normal file
57
backfill.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Iterator, Tuple
|
||||
|
||||
|
||||
def get_block_after_before_chunks(
|
||||
after_block: int,
|
||||
before_block: int,
|
||||
n_workers: int,
|
||||
) -> Iterator[Tuple[int, int]]:
|
||||
n_blocks = before_block - after_block
|
||||
remainder = n_blocks % n_workers
|
||||
floor_chunk_size = n_blocks // n_workers
|
||||
|
||||
last_before_block = None
|
||||
|
||||
for worker_index in range(n_workers):
|
||||
chunk_size = floor_chunk_size
|
||||
|
||||
if worker_index < remainder:
|
||||
chunk_size += 1
|
||||
|
||||
batch_after_block = (
|
||||
last_before_block if last_before_block is not None else after_block
|
||||
)
|
||||
|
||||
batch_before_block = batch_after_block + chunk_size
|
||||
yield batch_after_block, batch_before_block
|
||||
last_before_block = batch_before_block
|
||||
|
||||
|
||||
def backfill(after_block: int, before_block: int, n_workers: int):
|
||||
if n_workers <= 0:
|
||||
raise ValueError("Need at least one worker")
|
||||
|
||||
for batch_after_block, batch_before_block in get_block_after_before_chunks(
|
||||
after_block,
|
||||
before_block,
|
||||
n_workers,
|
||||
):
|
||||
print(f"Backfilling {batch_after_block} to {batch_before_block}")
|
||||
backfill_command = f"sh backfill.sh {batch_after_block} {batch_before_block}"
|
||||
process = subprocess.Popen(backfill_command.split(), stdout=subprocess.PIPE)
|
||||
output, _ = process.communicate()
|
||||
print(output)
|
||||
|
||||
|
||||
def main():
|
||||
after_block = int(sys.argv[1])
|
||||
before_block = int(sys.argv[2])
|
||||
n_workers = int(sys.argv[3])
|
||||
|
||||
backfill(after_block, before_block, n_workers)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
6
backfill.sh
Normal file
6
backfill.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
current_image=$(kubectl get deployment mev-inspect -o=jsonpath='{$.spec.template.spec.containers[:1].image}')
|
||||
|
||||
helm template mev-inspect-backfill ./k8s/mev-inspect-backfill \
|
||||
--set image.repository=$current_image \
|
||||
--set command.startBlockNumber=$1 \
|
||||
--set command.endBlockNumber=$2 | kubectl apply -f -
|
||||
23
k8s/mev-inspect-backfill/.helmignore
Normal file
23
k8s/mev-inspect-backfill/.helmignore
Normal file
@@ -0,0 +1,23 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
24
k8s/mev-inspect-backfill/Chart.yaml
Normal file
24
k8s/mev-inspect-backfill/Chart.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
apiVersion: v2
|
||||
name: mev-inspect-backfill
|
||||
description: A Helm chart for Kubernetes
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "1.16.0"
|
||||
62
k8s/mev-inspect-backfill/templates/_helpers.tpl
Normal file
62
k8s/mev-inspect-backfill/templates/_helpers.tpl
Normal file
@@ -0,0 +1,62 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "mev-inspect-backfill.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "mev-inspect-backfill.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "mev-inspect-backfill.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "mev-inspect-backfill.labels" -}}
|
||||
helm.sh/chart: {{ include "mev-inspect-backfill.chart" . }}
|
||||
{{ include "mev-inspect-backfill.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "mev-inspect-backfill.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "mev-inspect-backfill.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "mev-inspect-backfill.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "mev-inspect-backfill.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
68
k8s/mev-inspect-backfill/templates/job.yaml
Normal file
68
k8s/mev-inspect-backfill/templates/job.yaml
Normal file
@@ -0,0 +1,68 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ include "mev-inspect-backfill.fullname" . }}-{{ randAlphaNum 5 | lower }}
|
||||
labels:
|
||||
{{- include "mev-inspect-backfill.labels" . | nindent 4 }}
|
||||
spec:
|
||||
completions: 1
|
||||
parallelism: 1
|
||||
ttlSecondsAfterFinished: 5
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.image.repository }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
args:
|
||||
- run
|
||||
- inspect-many-blocks
|
||||
- {{ .Values.command.startBlockNumber | quote }}
|
||||
- {{ .Values.command.endBlockNumber | quote }}
|
||||
env:
|
||||
- name: POSTGRES_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mev-inspect-db-credentials
|
||||
key: host
|
||||
- name: POSTGRES_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mev-inspect-db-credentials
|
||||
key: username
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mev-inspect-db-credentials
|
||||
key: password
|
||||
- name: TRACE_DB_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: trace-db-credentials
|
||||
key: host
|
||||
optional: true
|
||||
- name: TRACE_DB_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: trace-db-credentials
|
||||
key: username
|
||||
optional: true
|
||||
- name: TRACE_DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: trace-db-credentials
|
||||
key: password
|
||||
optional: true
|
||||
- name: RPC_URL
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: mev-inspect-rpc
|
||||
key: url
|
||||
restartPolicy: OnFailure
|
||||
42
k8s/mev-inspect-backfill/values.yaml
Normal file
42
k8s/mev-inspect-backfill/values.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
# Default values for mev-inspect.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
image:
|
||||
repository: mev-inspect-py
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
podAnnotations: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
@@ -17,14 +17,13 @@ podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
|
||||
@@ -17,15 +17,13 @@ podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- all
|
||||
#readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
|
||||
@@ -36,19 +36,13 @@ def get_aave_liquidations(
|
||||
trace.transaction_hash, trace.trace_address, traces
|
||||
)
|
||||
(debt_token_address, debt_purchase_amount) = _get_debt_data(
|
||||
trace, child_traces, liquidator
|
||||
child_traces, liquidator
|
||||
)
|
||||
|
||||
if debt_purchase_amount == 0:
|
||||
continue
|
||||
|
||||
(received_token_address, received_amount) = _get_received_data(
|
||||
trace, child_traces, liquidator
|
||||
child_traces, liquidator
|
||||
)
|
||||
|
||||
if received_amount == 0:
|
||||
continue
|
||||
|
||||
liquidations.append(
|
||||
Liquidation(
|
||||
liquidated_user=trace.inputs["_user"],
|
||||
@@ -69,26 +63,26 @@ def get_aave_liquidations(
|
||||
|
||||
|
||||
def _get_received_data(
|
||||
liquidation_trace: DecodedCallTrace,
|
||||
child_traces: List[ClassifiedTrace],
|
||||
liquidator: str,
|
||||
child_traces: List[ClassifiedTrace], liquidator: str
|
||||
) -> Tuple[str, int]:
|
||||
|
||||
"""Look for and return liquidator payback from liquidation"""
|
||||
|
||||
for child in child_traces:
|
||||
|
||||
child_transfer: Optional[Transfer] = get_transfer(child)
|
||||
|
||||
if child_transfer is not None and child_transfer.to_address == liquidator:
|
||||
return child_transfer.token_address, child_transfer.amount
|
||||
if child_transfer is not None:
|
||||
|
||||
return liquidation_trace.inputs["_collateral"], 0
|
||||
if child_transfer.to_address == liquidator:
|
||||
|
||||
return child_transfer.token_address, child_transfer.amount
|
||||
|
||||
raise RuntimeError("Transfer from AAVE to liquidator not found!")
|
||||
|
||||
|
||||
def _get_debt_data(
|
||||
liquidation_trace: DecodedCallTrace,
|
||||
child_traces: List[ClassifiedTrace],
|
||||
liquidator: str,
|
||||
child_traces: List[ClassifiedTrace], liquidator: str
|
||||
) -> Tuple[str, int]:
|
||||
"""Get transfer from liquidator to AAVE"""
|
||||
|
||||
@@ -99,9 +93,7 @@ def _get_debt_data(
|
||||
if child_transfer is not None:
|
||||
|
||||
if child_transfer.from_address == liquidator:
|
||||
|
||||
return child_transfer.token_address, child_transfer.amount
|
||||
|
||||
return (
|
||||
liquidation_trace.inputs["_reserve"],
|
||||
0,
|
||||
)
|
||||
raise RuntimeError("Transfer from liquidator to AAVE not found!")
|
||||
|
||||
1
mev_inspect/abis/1inch/AggregationRouterV3.json
Normal file
1
mev_inspect/abis/1inch/AggregationRouterV3.json
Normal file
@@ -0,0 +1 @@
|
||||
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"Error","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"srcToken","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"dstToken","type":"address"},{"indexed":false,"internalType":"address","name":"dstReceiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"spentAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"returnAmount","type":"uint256"}],"name":"Swapped","type":"event"},{"inputs":[],"name":"destroy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IAggregationExecutor","name":"caller","type":"address"},{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"dstToken","type":"address"},{"internalType":"address","name":"srcReceiver","type":"address"},{"internalType":"address","name":"dstReceiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturnAmount","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"internalType":"struct AggregationRouterV3.SwapDescription","name":"desc","type":"tuple"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"discountedSwap","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256","name":"gasLeft","type":"uint256"},{"internalType":"uint256","name":"chiSpent","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IAggregationExecutor","name":"caller","type":"address"},{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"dstToken","type":"address"},{"internalType":"address","name":"srcReceiver","type":"address"},{"internalType":"address","name":"dstReceiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturnAmount","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"internalType":"struct AggregationRouterV3.SwapDescription","name":"desc","type":"tuple"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256","name":"gasLeft","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"name":"unoswap","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"bytes32[]","name":"pools","type":"bytes32[]"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"unoswapWithPermit","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]
|
||||
@@ -3,7 +3,6 @@ from typing import List, Optional, Tuple
|
||||
|
||||
from mev_inspect.schemas.arbitrages import Arbitrage
|
||||
from mev_inspect.schemas.swaps import Swap
|
||||
from mev_inspect.utils import equal_within_percent
|
||||
|
||||
MAX_TOKEN_AMOUNT_PERCENT_DIFFERENCE = 0.01
|
||||
|
||||
@@ -176,16 +175,16 @@ def _get_all_start_end_swaps(swaps: List[Swap]) -> List[Tuple[Swap, List[Swap]]]
|
||||
|
||||
|
||||
def _swap_outs_match_swap_ins(swap_out, swap_in) -> bool:
|
||||
return (
|
||||
swap_out.token_out_address == swap_in.token_in_address
|
||||
and (
|
||||
swap_out.contract_address == swap_in.from_address
|
||||
or swap_out.to_address == swap_in.contract_address
|
||||
or swap_out.to_address == swap_in.from_address
|
||||
if swap_out.token_out_address == swap_in.token_in_address and (
|
||||
swap_out.contract_address == swap_in.from_address
|
||||
or swap_out.to_address == swap_in.contract_address
|
||||
or swap_out.to_address == swap_in.from_address
|
||||
):
|
||||
amount_percent_difference = abs(
|
||||
(float(swap_out.token_out_amount) / swap_in.token_in_amount) - 1.0
|
||||
)
|
||||
and equal_within_percent(
|
||||
swap_out.token_out_amount,
|
||||
swap_in.token_in_amount,
|
||||
MAX_TOKEN_AMOUNT_PERCENT_DIFFERENCE,
|
||||
)
|
||||
)
|
||||
|
||||
if amount_percent_difference < MAX_TOKEN_AMOUNT_PERCENT_DIFFERENCE:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@@ -10,6 +10,7 @@ from .compound import COMPOUND_CLASSIFIER_SPECS
|
||||
from .cryptopunks import CRYPTOPUNKS_CLASSIFIER_SPECS
|
||||
from .curve import CURVE_CLASSIFIER_SPECS
|
||||
from .erc20 import ERC20_CLASSIFIER_SPECS
|
||||
from .one_inch import ONE_INCH_CLASSIFIER_SPECS
|
||||
from .opensea import OPENSEA_CLASSIFIER_SPECS
|
||||
from .uniswap import UNISWAP_CLASSIFIER_SPECS
|
||||
from .weth import WETH_ADDRESS, WETH_CLASSIFIER_SPECS
|
||||
@@ -27,6 +28,7 @@ ALL_CLASSIFIER_SPECS = (
|
||||
+ CRYPTOPUNKS_CLASSIFIER_SPECS
|
||||
+ OPENSEA_CLASSIFIER_SPECS
|
||||
+ BANCOR_CLASSIFIER_SPECS
|
||||
+ ONE_INCH_CLASSIFIER_SPECS
|
||||
)
|
||||
|
||||
_SPECS_BY_ABI_NAME_AND_PROTOCOL: Dict[
|
||||
|
||||
60
mev_inspect/classifiers/specs/one_inch.py
Normal file
60
mev_inspect/classifiers/specs/one_inch.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from mev_inspect.schemas.classifiers import ClassifierSpec, SwapClassifier
|
||||
from mev_inspect.schemas.swaps import Swap
|
||||
from mev_inspect.schemas.traces import DecodedCallTrace, Protocol
|
||||
from mev_inspect.schemas.transfers import Transfer
|
||||
|
||||
|
||||
class OneInchSwapClassifier(SwapClassifier):
|
||||
@staticmethod
|
||||
def parse_swap(
|
||||
trace: DecodedCallTrace,
|
||||
prior_transfers: List[Transfer],
|
||||
child_transfers: List[Transfer],
|
||||
) -> Optional[Swap]:
|
||||
if trace.error is not None:
|
||||
return None
|
||||
|
||||
desc = trace.inputs["desc"]
|
||||
[srcToken, dstToken, srcReceiver, dstReceiver, amountIn, *_] = desc
|
||||
|
||||
transfer_out_candidates = [
|
||||
transfer
|
||||
for transfer in child_transfers
|
||||
if (
|
||||
transfer.token_address == dstToken
|
||||
and transfer.to_address == dstReceiver
|
||||
)
|
||||
]
|
||||
|
||||
if len(transfer_out_candidates) == 0:
|
||||
raise RuntimeError("1inch expected at least one transfer out")
|
||||
|
||||
return Swap(
|
||||
abi_name=trace.abi_name,
|
||||
transaction_hash=trace.transaction_hash,
|
||||
transaction_position=trace.transaction_position,
|
||||
block_number=trace.block_number,
|
||||
trace_address=trace.trace_address,
|
||||
contract_address=trace.to_address,
|
||||
protocol=trace.protocol,
|
||||
from_address=srcReceiver,
|
||||
to_address=dstReceiver,
|
||||
token_in_address=srcToken,
|
||||
token_in_amount=amountIn,
|
||||
token_out_address=dstToken,
|
||||
token_out_amount=transfer_out_candidates[0].amount,
|
||||
error=trace.error,
|
||||
)
|
||||
|
||||
|
||||
ONE_INCH_ROUTER_SPEC = ClassifierSpec(
|
||||
abi_name="AggregationRouterV3",
|
||||
protocol=Protocol.one_inch,
|
||||
classifiers={
|
||||
"swap(address,(address,address,address,address,uint256,uint256,uint256,bytes),bytes)": OneInchSwapClassifier,
|
||||
},
|
||||
)
|
||||
|
||||
ONE_INCH_CLASSIFIER_SPECS = [ONE_INCH_ROUTER_SPEC]
|
||||
@@ -21,7 +21,7 @@ COINBASE_TOKEN_NAME_BY_ADDRESS = {
|
||||
WETH_ADDRESS: "weth",
|
||||
ETH_TOKEN_ADDRESS: "ethereum",
|
||||
WBTC_TOKEN_ADDRESS: "wrapped-bitcoin",
|
||||
LINK_TOKEN_ADDRESS: "chainlink",
|
||||
LINK_TOKEN_ADDRESS: "link",
|
||||
YEARN_TOKEN_ADDRESS: "yearn-finance",
|
||||
AAVE_TOKEN_ADDRESS: "aave",
|
||||
UNI_TOKEN_ADDRESS: "uniswap",
|
||||
|
||||
@@ -39,12 +39,12 @@ def get_compound_liquidations(
|
||||
liquidations.append(
|
||||
Liquidation(
|
||||
liquidated_user=trace.inputs["borrower"],
|
||||
debt_token_address=trace.to_address,
|
||||
debt_token_address=c_token_collateral,
|
||||
liquidator_user=seize_trace.inputs["liquidator"],
|
||||
debt_purchase_amount=trace.value,
|
||||
protocol=trace.protocol,
|
||||
received_amount=seize_trace.inputs["seizeTokens"],
|
||||
received_token_address=c_token_collateral,
|
||||
received_token_address=trace.to_address,
|
||||
transaction_hash=trace.transaction_hash,
|
||||
trace_address=trace.trace_address,
|
||||
block_number=trace.block_number,
|
||||
@@ -57,12 +57,12 @@ def get_compound_liquidations(
|
||||
liquidations.append(
|
||||
Liquidation(
|
||||
liquidated_user=trace.inputs["borrower"],
|
||||
debt_token_address=trace.to_address,
|
||||
debt_token_address=c_token_collateral,
|
||||
liquidator_user=seize_trace.inputs["liquidator"],
|
||||
debt_purchase_amount=trace.inputs["repayAmount"],
|
||||
protocol=trace.protocol,
|
||||
received_amount=seize_trace.inputs["seizeTokens"],
|
||||
received_token_address=c_token_collateral,
|
||||
received_token_address=trace.to_address,
|
||||
transaction_hash=trace.transaction_hash,
|
||||
trace_address=trace.trace_address,
|
||||
block_number=trace.block_number,
|
||||
|
||||
@@ -4,9 +4,6 @@ from mev_inspect.classifiers.specs.weth import WETH_ADDRESS
|
||||
from mev_inspect.coinbase import fetch_coinbase_prices
|
||||
from mev_inspect.schemas.prices import (
|
||||
AAVE_TOKEN_ADDRESS,
|
||||
CDAI_TOKEN_ADDRESS,
|
||||
CUSDC_TOKEN_ADDRESS,
|
||||
DAI_TOKEN_ADDRESS,
|
||||
LINK_TOKEN_ADDRESS,
|
||||
REN_TOKEN_ADDRESS,
|
||||
UNI_TOKEN_ADDRESS,
|
||||
@@ -18,18 +15,15 @@ from mev_inspect.schemas.prices import (
|
||||
from mev_inspect.schemas.transfers import ETH_TOKEN_ADDRESS
|
||||
|
||||
SUPPORTED_TOKENS = [
|
||||
AAVE_TOKEN_ADDRESS,
|
||||
CDAI_TOKEN_ADDRESS,
|
||||
CUSDC_TOKEN_ADDRESS,
|
||||
DAI_TOKEN_ADDRESS,
|
||||
WETH_ADDRESS,
|
||||
ETH_TOKEN_ADDRESS,
|
||||
LINK_TOKEN_ADDRESS,
|
||||
REN_TOKEN_ADDRESS,
|
||||
UNI_TOKEN_ADDRESS,
|
||||
AAVE_TOKEN_ADDRESS,
|
||||
USDC_TOKEN_ADDRESS,
|
||||
REN_TOKEN_ADDRESS,
|
||||
WBTC_TOKEN_ADDRESS,
|
||||
WETH_ADDRESS,
|
||||
YEARN_TOKEN_ADDRESS,
|
||||
UNI_TOKEN_ADDRESS,
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@ from typing import List, Optional
|
||||
|
||||
from mev_inspect.schemas.sandwiches import Sandwich
|
||||
from mev_inspect.schemas.swaps import Swap
|
||||
from mev_inspect.utils import equal_within_percent
|
||||
|
||||
SANDWICH_IN_OUT_MAX_PERCENT_DIFFERENCE = 0.01
|
||||
|
||||
|
||||
def get_sandwiches(swaps: List[Swap]) -> List[Sandwich]:
|
||||
@@ -31,7 +28,8 @@ def _get_sandwich_starting_with_swap(
|
||||
front_swap: Swap,
|
||||
rest_swaps: List[Swap],
|
||||
) -> Optional[Sandwich]:
|
||||
sandwicher_address = front_swap.to_address
|
||||
|
||||
sandwicher_address = front_swap.from_address
|
||||
sandwiched_swaps = []
|
||||
|
||||
for other_swap in rest_swaps:
|
||||
@@ -48,12 +46,7 @@ def _get_sandwich_starting_with_swap(
|
||||
elif (
|
||||
other_swap.token_out_address == front_swap.token_in_address
|
||||
and other_swap.token_in_address == front_swap.token_out_address
|
||||
and equal_within_percent(
|
||||
other_swap.token_in_amount,
|
||||
front_swap.token_out_amount,
|
||||
SANDWICH_IN_OUT_MAX_PERCENT_DIFFERENCE,
|
||||
)
|
||||
and other_swap.from_address == sandwicher_address
|
||||
and other_swap.to_address == sandwicher_address
|
||||
):
|
||||
if len(sandwiched_swaps) > 0:
|
||||
return Sandwich(
|
||||
|
||||
@@ -50,6 +50,7 @@ class Protocol(Enum):
|
||||
cryptopunks = "cryptopunks"
|
||||
bancor = "bancor"
|
||||
opensea = "opensea"
|
||||
one_inch = "1inch"
|
||||
|
||||
|
||||
class ClassifiedTrace(Trace):
|
||||
|
||||
@@ -3,12 +3,3 @@ from hexbytes._utils import hexstr_to_bytes
|
||||
|
||||
def hex_to_int(value: str) -> int:
|
||||
return int.from_bytes(hexstr_to_bytes(value), byteorder="big")
|
||||
|
||||
|
||||
def equal_within_percent(
|
||||
first_value: int, second_value: int, threshold_percent: float
|
||||
) -> bool:
|
||||
difference = abs(
|
||||
(first_value - second_value) / (0.5 * (first_value + second_value))
|
||||
)
|
||||
return difference < threshold_percent
|
||||
|
||||
@@ -18,10 +18,10 @@ def test_c_ether_liquidations(trace_classifier: TraceClassifier):
|
||||
Liquidation(
|
||||
liquidated_user="0xb5535a3681cf8d5431b8acfd779e2f79677ecce9",
|
||||
liquidator_user="0xe0090ec6895c087a393f0e45f1f85098a6c33bef",
|
||||
debt_token_address="0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5",
|
||||
debt_token_address="0x39aa39c021dfbae8fac545936693ac917d5e7563",
|
||||
debt_purchase_amount=268066492249420078,
|
||||
received_amount=4747650169097,
|
||||
received_token_address="0x39aa39c021dfbae8fac545936693ac917d5e7563",
|
||||
received_token_address="0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5",
|
||||
protocol=Protocol.compound_v2,
|
||||
transaction_hash=transaction_hash,
|
||||
trace_address=[1],
|
||||
@@ -42,10 +42,10 @@ def test_c_ether_liquidations(trace_classifier: TraceClassifier):
|
||||
Liquidation(
|
||||
liquidated_user="0x45df6f00166c3fb77dc16b9e47ff57bc6694e898",
|
||||
liquidator_user="0xe0090ec6895c087a393f0e45f1f85098a6c33bef",
|
||||
debt_token_address="0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5",
|
||||
debt_token_address="0x35a18000230da775cac24873d00ff85bccded550",
|
||||
debt_purchase_amount=414547860568297082,
|
||||
received_amount=321973320649,
|
||||
received_token_address="0x35a18000230da775cac24873d00ff85bccded550",
|
||||
received_token_address="0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5",
|
||||
protocol=Protocol.compound_v2,
|
||||
transaction_hash=transaction_hash,
|
||||
trace_address=[1],
|
||||
@@ -67,10 +67,10 @@ def test_c_ether_liquidations(trace_classifier: TraceClassifier):
|
||||
Liquidation(
|
||||
liquidated_user="0xacbcf5d2970eef25f02a27e9d9cd31027b058b9b",
|
||||
liquidator_user="0xe0090ec6895c087a393f0e45f1f85098a6c33bef",
|
||||
debt_token_address="0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5",
|
||||
debt_token_address="0x35a18000230da775cac24873d00ff85bccded550",
|
||||
debt_purchase_amount=1106497772527562662,
|
||||
received_amount=910895850496,
|
||||
received_token_address="0x35a18000230da775cac24873d00ff85bccded550",
|
||||
received_token_address="0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5",
|
||||
protocol=Protocol.compound_v2,
|
||||
transaction_hash=transaction_hash,
|
||||
trace_address=[1],
|
||||
@@ -93,10 +93,10 @@ def test_c_token_liquidation(trace_classifier: TraceClassifier):
|
||||
Liquidation(
|
||||
liquidated_user="0xacdd5528c1c92b57045041b5278efa06cdade4d8",
|
||||
liquidator_user="0xe0090ec6895c087a393f0e45f1f85098a6c33bef",
|
||||
debt_token_address="0x39aa39c021dfbae8fac545936693ac917d5e7563",
|
||||
debt_token_address="0x70e36f6bf80a52b3b46b3af8e106cc0ed743e8e4",
|
||||
debt_purchase_amount=1207055531,
|
||||
received_amount=21459623305,
|
||||
received_token_address="0x70e36f6bf80a52b3b46b3af8e106cc0ed743e8e4",
|
||||
received_token_address="0x39aa39c021dfbae8fac545936693ac917d5e7563",
|
||||
protocol=Protocol.compound_v2,
|
||||
transaction_hash=transaction_hash,
|
||||
trace_address=[1],
|
||||
@@ -119,10 +119,10 @@ def test_cream_token_liquidation(trace_classifier: TraceClassifier):
|
||||
Liquidation(
|
||||
liquidated_user="0x46bf9479dc569bc796b7050344845f6564d45fba",
|
||||
liquidator_user="0xa2863cad9c318669660eb4eca8b3154b90fb4357",
|
||||
debt_token_address="0x697256caa3ccafd62bb6d3aa1c7c5671786a5fd9",
|
||||
debt_token_address="0x44fbebd2f576670a6c33f6fc0b00aa8c5753b322",
|
||||
debt_purchase_amount=14857434973806369550,
|
||||
received_amount=1547215810826,
|
||||
received_token_address="0x44fbebd2f576670a6c33f6fc0b00aa8c5753b322",
|
||||
received_token_address="0x697256caa3ccafd62bb6d3aa1c7c5671786a5fd9",
|
||||
protocol=Protocol.cream,
|
||||
transaction_hash=transaction_hash,
|
||||
trace_address=[],
|
||||
|
||||
Reference in New Issue
Block a user