diff --git a/pom.xml b/pom.xml index 6103960a..0315ab49 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ 11 11 4.8.4 + 3.0.0 -Duser.language=en -Duser.region=US pro.belbix.ethparser.Application @@ -41,6 +42,16 @@ org.springframework.boot spring-boot-starter-data-rest + + org.springframework.retry + spring-retry + 1.1.5.RELEASE + + + org.springframework + spring-aspects + 5.2.8.RELEASE + org.springframework.boot spring-boot-starter-test @@ -70,7 +81,14 @@ org.springframework.boot spring-boot-starter-actuator - + + + + org.springframework.cloud + spring-cloud-contract-wiremock + ${spring.wiremock.version} + test + @@ -270,7 +288,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.0.0 + 3.0.2 copy-dependencies diff --git a/scripts/application.example.yml b/scripts/application.example.yml index 9b03a7d9..c29dcde1 100644 --- a/scripts/application.example.yml +++ b/scripts/application.example.yml @@ -18,3 +18,30 @@ spring: server: port: 4142 + +external: + covalenthq: + url: https://api.covalenthq.com/v1/ + key: ckey_91bf3242cc5042599af1094b965 + harvest: + url: https://api-ui.harvest.finance/ + key: 41e90ced-d559-4433-b390-af424fdc76d6 + + +task: + pool: + fixedRate: 3600000 # Each hour + enable: false + vault: + fixedRate: 3600000 # Each hour + enable: false + uni-pair: + fixedRate: 86400000 # Everyday + enable: false + transaction: + max-thread-size: 30 + fixedRate: 86400000 # Everyday + enable: false + info: + fixedRate: 3600000 # Each hour + enable: false \ No newline at end of file diff --git a/scripts/run_app/run.sh b/scripts/run_app/run.sh old mode 100644 new mode 100755 index 852492e2..c98acf9b --- a/scripts/run_app/run.sh +++ b/scripts/run_app/run.sh @@ -1,7 +1,7 @@ rm -rf ./lib rm -rf ./logs rm ethparser.jar -mvn install -Dmaven.test.skip=true -f ./../../pom.xml +mvn install -Dmaven.test.skip=true -Dmaven.version=1.0.1 -f ./../../pom.xml cp -R ./../../dist/lib/. ./lib cp ./../../dist/ethparser.jar ./ethparser.jar -java -Xmx1g -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -Dspring.config.location=./../application.yml,run_config.yml -cp ethparser.jar pro.belbix.ethparser.Application +java -Xmx4g -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -Dspring.config.location=./../application.yml,run_config.yml -cp ethparser.jar pro.belbix.ethparser.Application diff --git a/scripts/sql/schema.sql b/scripts/sql/schema.sql new file mode 100644 index 00000000..84d9259a --- /dev/null +++ b/scripts/sql/schema.sql @@ -0,0 +1,1151 @@ +create user grafana + valid until 'infinity'; +create user backup; +create user hv_dev; + +create table a_eth_address +( + address varchar(255) not null + primary key, + idx bigint + constraint uk_lyyq39oejm6m49nbd9ijhupfg + unique +); +alter table a_eth_address + owner to hv_dev; +grant select on a_eth_address to grafana; +grant select on a_eth_address to backup; + +create table a_eth_hash +( + hash varchar(255) not null + primary key, + idx bigint + constraint uk_eccojqwcjpvg9g6clh6drv36f + unique +); +alter table a_eth_hash + owner to hv_dev; +grant select on a_eth_hash to grafana; +grant select on a_eth_hash to backup; + + +create table a_eth_block +( + number bigint not null + primary key, + author varchar(255), + difficulty varchar(255), + extra_data text, + gas_limit bigint not null, + gas_used bigint not null, + nonce varchar(255), + size bigint not null, + timestamp bigint not null, + total_difficulty varchar(255), + hash bigint + constraint uk_n7r41qxlm5x3rc5vwrpqedhxo + unique + constraint fkffdolbifluijxubt7ff53lf4o + references a_eth_hash (idx), + miner bigint + constraint fk2hme9la57iqv3mj3rgma5d514 + references a_eth_address (idx), + parent_hash bigint + constraint fk5rymjl6kop16ll230q8frrxj9 + references a_eth_hash (idx), + network integer not null +); +alter table a_eth_block + owner to hv_dev; +create index idx_eth_block_hash + on a_eth_block (hash); +create index idx_eth_block_network + on a_eth_block (network); +create index idx_eth_block_timestamp + on a_eth_block (timestamp); +grant select on a_eth_block to grafana; +grant select on a_eth_block to backup; + +create sequence a_eth_tx_id_seq; +create table a_eth_tx +( + id bigserial + primary key, + creates varchar(255), + cumulative_gas_used bigint not null, + gas bigint not null, + gas_price bigint not null, + gas_used bigint not null, + input text, + nonce varchar(255), + public_key varchar(255), + raw varchar(255), + revert_reason varchar(255), + root varchar(255), + status varchar(255), + transaction_index bigint not null, + value varchar(255), + block_number bigint + constraint fkt6utu5q2n3l2cprwxolf4ievh + references a_eth_block + on delete cascade, + contract_address bigint + constraint fkcvpylqyqq9csklhntdsxc3uwf + references a_eth_address (idx), + from_address bigint + constraint fkbwe6xntwov7u176f4ycl6a6gr + references a_eth_address (idx), + hash bigint + constraint idx_eth_txs_hash + unique + constraint fkggohnme0pqf1rbo7ho4bnjj55 + references a_eth_hash (idx), + to_address bigint + constraint fkf7i3tsjk7j6fwjnqq3hh4k8xt + references a_eth_address (idx) +); +alter table a_eth_tx + owner to hv_dev; +grant select on sequence a_eth_tx_id_seq to grafana; +grant select on sequence a_eth_tx_id_seq to backup; +create index idx_eth_txs_block_number + on a_eth_tx (block_number); +create index idx_eth_txs_contract_address + on a_eth_tx (contract_address); +create index idx_eth_txs_from_address + on a_eth_tx (from_address); +create index idx_eth_txs_to_address + on a_eth_tx (to_address); +grant select on a_eth_tx to grafana; +grant select on a_eth_tx to backup; +alter sequence a_eth_tx_id_seq owner to hv_dev; +alter sequence a_eth_tx_id_seq owned by a_eth_tx.id; +grant select on sequence a_eth_tx_id_seq to grafana; +grant select on sequence a_eth_tx_id_seq to backup; + + +create sequence a_eth_log_id_seq; +create table a_eth_log +( + id bigserial + primary key, + data text, + log_id bigint not null, + removed integer not null, + topics text, + transaction_index bigint not null, + type varchar(255), + address bigint + constraint fk7f1b2cyhljaje7ndtadxtdthc + references a_eth_address (idx), + block_number bigint + constraint fkm1bby09fkeo27p1boj9wc1w0r + references a_eth_block, + first_topic bigint + constraint fk515nptp7wer9rq11pi317v0vl + references a_eth_hash (idx), + tx_id bigint + constraint fkdbi2jp2fga70304mo4cjyulc + references a_eth_tx + on delete cascade, + constraint idx_eth_log_tx_id_log_id + unique (tx_id, log_id) +); +alter table a_eth_log + owner to hv_dev; +grant select on sequence a_eth_log_id_seq to grafana; +grant select on sequence a_eth_log_id_seq to backup; +create index idx_eth_log_address + on a_eth_log (address); +create index idx_eth_log_block_number + on a_eth_log (block_number); +create index idx_eth_log_first_topic + on a_eth_log (first_topic); +grant select on a_eth_log to grafana; +grant select on a_eth_log to backup; +alter sequence a_eth_log_id_seq owner to hv_dev; +alter sequence a_eth_log_id_seq owned by a_eth_log.id; +grant select on sequence a_eth_log_id_seq to grafana; +grant select on sequence a_eth_log_id_seq to backup; + + +create sequence b_contract_events_id_seq; +create table b_contract_events +( + id bigserial + primary key, + block bigint + constraint fkta3rg7xqns9jj26rgpyflbxsq + references a_eth_block + on delete cascade, + contract bigint + constraint fkkyifgyelocpd5tnq5bhgra8wk + references a_eth_address (idx), + constraint b_contract_events_block_contract + unique (block, contract) +); +alter table b_contract_events + owner to hv_dev; +grant select on sequence b_contract_events_id_seq to grafana; +grant select on sequence b_contract_events_id_seq to backup; +grant select on b_contract_events to grafana; +grant select on b_contract_events to backup; +alter sequence b_contract_events_id_seq owner to hv_dev; +alter sequence b_contract_events_id_seq owned by b_contract_events.id; +grant select on sequence b_contract_events_id_seq to grafana; +grant select on sequence b_contract_events_id_seq to backup; + +create table b_func_hashes +( + method_id varchar(255) not null + primary key, + name varchar(255) +); +alter table b_func_hashes + owner to hv_dev; +create index b_func_hashes_name + on b_func_hashes (name); +grant select on b_func_hashes to grafana; +grant select on b_func_hashes to backup; + + +create sequence b_contract_txs_id_seq; +create table b_contract_txs +( + id bigserial + primary key, + func_data jsonb, + func_hash varchar(255) + constraint fk15n0nwgc592lqjpue85o8fqqr + references b_func_hashes, + tx_id bigint + constraint b_contract_txs_tx + unique + constraint fk5gv98kcrok11boi2yenwfjkot + references a_eth_tx +); +alter table b_contract_txs + owner to hv_dev; + +grant select on sequence b_contract_txs_id_seq to grafana; +grant select on sequence b_contract_txs_id_seq to backup; +create index b_contract_txs_func_hash + on b_contract_txs (func_hash); +grant select on b_contract_txs to grafana; +grant select on b_contract_txs to backup; +alter sequence b_contract_txs_id_seq owner to hv_dev; +alter sequence b_contract_txs_id_seq owned by b_contract_txs.id; +grant select on sequence b_contract_txs_id_seq to grafana; +grant select on sequence b_contract_txs_id_seq to backup; + + + + +create table b_contract_event_to_tx +( + event_id bigint not null + constraint fkm241gqpq79nknje280fvu30w5 + references b_contract_events, + tx_id bigint not null + constraint fkovb71c3arwj9r2ejp7mmdwblp + references b_contract_txs, + primary key (event_id, tx_id) +); +alter table b_contract_event_to_tx + owner to hv_dev; +grant select on b_contract_event_to_tx to grafana; +grant select on b_contract_event_to_tx to backup; + +create table b_log_hashes +( + method_id varchar(255) not null + primary key, + method_name varchar(255), + topic_hash bigint + constraint fkp9f5yq694sbmt8r1mpb4ha4n0 + references a_eth_hash (idx) +); +alter table b_log_hashes + owner to hv_dev; +create index b_log_hashes_method_name + on b_log_hashes (method_name); +create index b_log_hashes_topic_hash + on b_log_hashes (topic_hash); +grant select on b_log_hashes to grafana; +grant select on b_log_hashes to backup; + +create sequence b_contract_logs_id_seq; +create table b_contract_logs +( + id bigserial + primary key, + log_idx bigint not null, + logs jsonb, + address bigint + constraint fkrkkdnf7mom8emboqbyqfwrdwf + references a_eth_address (idx), + contract_tx_id bigint + constraint fk2wp23byi8261k79hban35a52h + references b_contract_txs + on delete cascade, + topic varchar(255) + constraint fk6aoeyu9exo4d24rrvwqesi3h2 + references b_log_hashes, + constraint b_contract_logs_contract_tx_id_log_idx + unique (contract_tx_id, log_idx) +); +alter table b_contract_logs + owner to hv_dev; +grant select on sequence b_contract_logs_id_seq to grafana; +grant select on sequence b_contract_logs_id_seq to backup; +create index b_contract_logs_address + on b_contract_logs (address); +create index b_contract_logs_topic + on b_contract_logs (topic); +grant select on b_contract_logs to grafana; +grant select on b_contract_logs to backup; +alter sequence b_contract_logs_id_seq owner to hv_dev; +alter sequence b_contract_logs_id_seq owned by b_contract_logs.id; +grant select on sequence b_contract_logs_id_seq to grafana; +grant select on sequence b_contract_logs_id_seq to backup; + +create sequence b_contract_states_id_seq; +create table b_contract_states +( + id bigserial + primary key, + name varchar(255), + value jsonb, + contract_event_id bigint + constraint fknqso7038g1ri26ie22hna1pkm + references b_contract_events + on delete cascade, + constraint uk80s6o9bviindcpv15lpnnyu4x + unique (contract_event_id, name) +); +alter table b_contract_states + owner to hv_dev; +grant select on sequence b_contract_states_id_seq to grafana; +grant select on sequence b_contract_states_id_seq to backup; +grant select on b_contract_states to grafana; +grant select on b_contract_states to backup; +alter sequence b_contract_states_id_seq owner to hv_dev; +alter sequence b_contract_states_id_seq owned by b_contract_states.id; +grant select on sequence b_contract_states_id_seq to grafana; +grant select on sequence b_contract_states_id_seq to backup; + +create table bancor_tx +( + id varchar(255) not null + primary key, + amount double precision, + amount_bnt double precision, + amount_farm double precision, + block bigint, + block_date bigint, + coin varchar(255), + coin_address varchar(255), + farm_as_source boolean, + hash varchar(255), + last_gas double precision, + last_price double precision, + log_id bigint, + other_amount double precision, + other_coin varchar(255), + other_coin_address varchar(255), + owner varchar(255), + price_bnt double precision, + price_farm double precision, + type varchar(255) +); +alter table bancor_tx + owner to hv_dev; + +create table block_cache +( + block bigint not null + primary key, + block_date bigint not null, + network varchar(255) +); + +alter table block_cache + owner to hv_dev; +create index idx_block_cache + on block_cache (block_date); +create index idx_block_cache_net + on block_cache (network); +grant select on block_cache to grafana; +grant select on block_cache to backup; + +create table deployer_tx +( + id varchar(255) not null + primary key, + block bigint not null, + block_date bigint not null, + confirmed integer not null, + from_address varchar(255), + gas_limit numeric(19, 2), + gas_price numeric(19, 2), + gas_used numeric(19, 2), + idx bigint not null, + method_name varchar(255), + to_address varchar(255), + type varchar(255), + value numeric(19, 2), + name varchar(255), + network varchar(255) +); +alter table deployer_tx + owner to hv_dev; +create index idx_deployer_tx + on deployer_tx (block); +create index idx_deployer_tx_network + on deployer_tx (network); +grant select on deployer_tx to grafana; +grant select on deployer_tx to backup; + +create sequence error_parse_id_seq + as integer; +create table error_parse +( + id serial + primary key, + error_class varchar(255), + json text, + network varchar(255), + status integer +); +alter table error_parse + owner to hv_dev; +grant select on sequence error_parse_id_seq to grafana; +grant select on sequence error_parse_id_seq to backup; +grant select on error_parse to grafana; +grant select on error_parse to backup; +alter sequence error_parse_id_seq owner to hv_dev; +alter sequence error_parse_id_seq owned by error_parse.id; +grant select on sequence error_parse_id_seq to grafana; +grant select on sequence error_parse_id_seq to backup; + +create sequence eth_contract_source_codes_id_seq + as integer; +create table eth_contract_source_codes +( + id serial + primary key, + abi text, + address varchar(255) not null, + compiler_version varchar(255), + constructor_arguments text, + contract_name varchar(255) not null, + created_at timestamp, + evmversion varchar(255), + implementation varchar(255), + library varchar(255), + license_type varchar(255), + network varchar(255) not null, + optimization_used boolean, + proxy boolean, + runs varchar(255), + source_code text, + swarm_source varchar(255), + updated_at timestamp, + constraint eth_contract_source_codes_adr_net_pk + unique (address, network) +); +alter table eth_contract_source_codes + owner to hv_dev; +grant select on sequence eth_contract_source_codes_id_seq to grafana; +grant select on sequence eth_contract_source_codes_id_seq to backup; +create index idx_eth_contract_source_codes_address + on eth_contract_source_codes (address); +create index idx_eth_contract_source_codes_name + on eth_contract_source_codes (contract_name); +grant select on eth_contract_source_codes to grafana; +grant select on eth_contract_source_codes to backup; +alter sequence eth_contract_source_codes_id_seq owner to hv_dev; +alter sequence eth_contract_source_codes_id_seq owned by eth_contract_source_codes.id; +grant select on sequence eth_contract_source_codes_id_seq to grafana; +grant select on sequence eth_contract_source_codes_id_seq to backup; + +create sequence eth_contracts_id_seq + as integer; +create table eth_contracts +( + id serial + primary key, + address varchar(255), + created bigint, + name varchar(255), + network varchar(255), + type integer not null, + underlying varchar(255), + updated bigint, + created_date bigint, + updated_date bigint, + constraint eth_c_adr_net_pk + unique (address, network) +); +alter table eth_contracts + owner to hv_dev; +grant select on sequence eth_contracts_id_seq to grafana; +grant select on sequence eth_contracts_id_seq to backup; +create index idx_eth_contracts_address + on eth_contracts (address); +create index idx_eth_contracts_name + on eth_contracts (name); +grant select on eth_contracts to grafana; +grant select on eth_contracts to backup; +alter sequence eth_contracts_id_seq owner to hv_dev; +alter sequence eth_contracts_id_seq owned by eth_contracts.id; +grant select on sequence eth_contracts_id_seq to grafana; +grant select on sequence eth_contracts_id_seq to backup; + +create sequence eth_pools_id_seq + as integer; +create table eth_pools +( + id serial + primary key, + updated_block bigint, + contract integer + constraint uk_5udgya1dmr3v5qtd91fk8wv48 + unique + constraint fk10ejggrjn5iw4rumqvk6mu5yk + references eth_contracts, + controller integer + constraint fkpnih15629ecxme9rgofq22u1q + references eth_contracts, + governance integer + constraint fkik78udlo8lek4dusd872ftko9 + references eth_contracts, + lp_token integer + constraint fk93eitbhw4da8p2a3caom2hck1 + references eth_contracts, + owner integer + constraint fk4su50ff4lc76ng8keety1n2wq + references eth_contracts, + reward_token integer + constraint fk3ldve4891mi7w0hde9kknwalv + references eth_contracts +); +alter table eth_pools + owner to hv_dev; +grant select on sequence eth_pools_id_seq to grafana; +grant select on sequence eth_pools_id_seq to backup; +create index idx_eth_pools + on eth_pools (contract); +grant select on eth_pools to grafana; +grant select on eth_pools to backup; +alter sequence eth_pools_id_seq owner to hv_dev; +alter sequence eth_pools_id_seq owned by eth_pools.id; +grant select on sequence eth_pools_id_seq to grafana; +grant select on sequence eth_pools_id_seq to backup; + +create sequence eth_strategies_id_seq + as integer; +create table eth_strategies +( + id serial + primary key, + updated_block bigint, + contract integer + constraint uk_mm5ag0ivmy35w6u8mwavvk6kh + unique + constraint fkc0kwtg4tlo1el67d0ulxcdkv1 + references eth_contracts +); +alter table eth_strategies + owner to hv_dev; +grant select on sequence eth_strategies_id_seq to grafana; +grant select on sequence eth_strategies_id_seq to backup; +create index idx_eth_strategies_contracts + on eth_strategies (contract); +grant select on eth_strategies to grafana; +grant select on eth_strategies to backup; +alter sequence eth_strategies_id_seq owner to hv_dev; +alter sequence eth_strategies_id_seq owned by eth_strategies.id; +grant select on sequence eth_strategies_id_seq to grafana; +grant select on sequence eth_strategies_id_seq to backup; + +create sequence eth_uni_pairs_id_seq + as integer; +create table eth_uni_pairs +( + id serial + primary key, + decimals bigint, + type integer not null, + updated_block bigint, + contract integer + constraint uk_chgws7al9e6gy2i81674gsmx6 + unique + constraint fkth7ib1cocfiqfp1d0wx5g53ol + references eth_contracts, + token0_id integer + constraint fkt60ljxkxdle0yvh2mnssy5kkr + references eth_contracts, + token1_id integer + constraint fkcacgxuk04t17glca80y7hi38o + references eth_contracts +); +alter table eth_uni_pairs + owner to hv_dev; +grant select on sequence eth_uni_pairs_id_seq to grafana; +grant select on sequence eth_uni_pairs_id_seq to backup; +create index idx_eth_uni_pairs + on eth_uni_pairs (contract); +grant select on eth_uni_pairs to grafana; +grant select on eth_uni_pairs to backup; +alter sequence eth_uni_pairs_id_seq owner to hv_dev; +alter sequence eth_uni_pairs_id_seq owned by eth_uni_pairs.id; +grant select on sequence eth_uni_pairs_id_seq to grafana; +grant select on sequence eth_uni_pairs_id_seq to backup; + +create sequence eth_tokens_id_seq + as integer; +create table eth_tokens +( + id serial + primary key, + decimals bigint, + name varchar(255), + symbol varchar(255), + updated_block bigint, + contract integer + constraint uk_4mjr2gwj0w6ncvr6f0uq04s3q + unique + constraint fkimgaagi5vuont8s0ew6gxqqai + references eth_contracts +); +alter table eth_tokens + owner to hv_dev; +grant select on sequence eth_tokens_id_seq to grafana; +grant select on sequence eth_tokens_id_seq to backup; +create index idx_eth_tokens + on eth_tokens (contract); +grant select on eth_tokens to grafana; +grant select on eth_tokens to backup; +alter sequence eth_tokens_id_seq owner to hv_dev; +alter sequence eth_tokens_id_seq owned by eth_tokens.id; +grant select on sequence eth_tokens_id_seq to grafana; +grant select on sequence eth_tokens_id_seq to backup; + +create sequence eth_token_to_uni_pair_id_seq + as integer; +create table eth_token_to_uni_pair +( + id serial + primary key, + block_start bigint, + token_id integer not null + constraint fki8id42hiwjjwvtifm3ugjon33 + references eth_tokens, + uni_pair_id integer not null + constraint fkrulm04mmvh87yeprexxh1o5xj + references eth_uni_pairs +); +alter table eth_token_to_uni_pair + owner to hv_dev; +grant select on sequence eth_token_to_uni_pair_id_seq to grafana; +grant select on sequence eth_token_to_uni_pair_id_seq to backup; +grant select on eth_token_to_uni_pair to grafana; +grant select on eth_token_to_uni_pair to backup; +alter sequence eth_token_to_uni_pair_id_seq owner to hv_dev; +alter sequence eth_token_to_uni_pair_id_seq owned by eth_token_to_uni_pair.id; +grant select on sequence eth_token_to_uni_pair_id_seq to grafana; +grant select on sequence eth_token_to_uni_pair_id_seq to backup; + +create sequence eth_vaults_id_seq + as integer; +create table eth_vaults +( + id serial + primary key, + decimals bigint, + name varchar(255), + symbol varchar(255), + underlying_unit bigint, + updated_block bigint, + contract integer + constraint uk_jcwcjqrfly836mv8b6i9p6wp6 + unique + constraint fkbj9vb6wndcb0pr44uqwf1rfhw + references eth_contracts, + controller_id integer + constraint fk9unq8mb4dwbt600hj503vr8nj + references eth_contracts, + governance_id integer + constraint fksvfll9hksjiwqodxy2auwdhxv + references eth_contracts, + strategy_id integer + constraint fkohdsk6q4bk3dk52hvykj3533 + references eth_contracts, + underlying_id integer + constraint fkh4gl719e2x448rqt9grxsdx34 + references eth_contracts +); +alter table eth_vaults + owner to hv_dev; +grant select on sequence eth_vaults_id_seq to grafana; +grant select on sequence eth_vaults_id_seq to backup; +create index idx_eth_vaults + on eth_vaults (contract); +grant select on eth_vaults to grafana; +grant select on eth_vaults to backup; +alter sequence eth_vaults_id_seq owner to hv_dev; +alter sequence eth_vaults_id_seq owned by eth_vaults.id; +grant select on sequence eth_vaults_id_seq to grafana; +grant select on sequence eth_vaults_id_seq to backup; + +create table events_tx +( + id varchar(255) not null + primary key, + block bigint, + block_date bigint, + event varchar(255), + hash varchar(255), + info text, + mint_amount double precision, + new_strategy varchar(255), + old_strategy varchar(255), + vault varchar(255), + network varchar(255), + vault_address varchar(255) +); +alter table events_tx + owner to hv_dev; +create index idx_events_network + on events_tx (network); +create index idx_events_tx + on events_tx (block_date); +grant select on events_tx to grafana; +grant select on events_tx to backup; + +create table hard_work +( + id varchar(255) not null + primary key, + all_profit double precision not null, + apr double precision not null, + block bigint not null, + block_date bigint not null, + calls_quantity integer not null, + eth_price double precision not null, + farm_buyback double precision not null, + farm_buyback_eth double precision not null, + farm_buyback_sum double precision not null, + farm_price double precision not null, + fee double precision not null, + fee_eth double precision not null, + full_reward_usd double precision not null, + full_reward_usd_total double precision not null, + gas_used double precision not null, + idle_time bigint not null, + invested double precision not null, + investment_target double precision not null, + perc double precision not null, + period_of_work bigint, + pool_users integer not null, + ps_apr double precision not null, + ps_period_of_work bigint, + ps_tvl_usd double precision not null, + saved_gas_fees double precision not null, + saved_gas_fees_sum double precision not null, + share_change double precision not null, + tvl double precision, + vault varchar(255), + weekly_all_profit double precision not null, + weekly_average_tvl double precision, + weekly_profit double precision not null, + buy_back_rate double precision, + profit_sharing_rate double precision, + auto_stake integer, + network varchar(255), + vault_address varchar(255) +); +alter table hard_work + owner to hv_dev; +create index idx_hard_work + on hard_work (block_date); +create index idx_hard_work_2 + on hard_work (full_reward_usd); +create index idx_hard_work_network + on hard_work (network); +create index idx_hard_work_vault + on hard_work (vault); +create index idx_hard_work_vault_address + on hard_work (vault_address); +grant select on hard_work to grafana; +grant select on hard_work to backup; + +create table harvest_tvl +( + calculate_hash varchar(255) not null + primary key, + calculate_time bigint, + last_all_owners_count integer not null, + last_owners_count integer not null, + last_price double precision, + last_tvl double precision, + network varchar(255) +); +alter table harvest_tvl + owner to hv_dev; +create index idx_harvest_tvl + on harvest_tvl (calculate_time); +grant select on harvest_tvl to grafana; +grant select on harvest_tvl to backup; + +create table harvest_tx +( + id varchar(255) not null + primary key, + all_owners_count integer, + all_pools_owners_count integer, + amount double precision, + amount_in double precision, + block bigint, + block_date bigint, + confirmed integer not null, + hash varchar(255), + last_all_usd_tvl double precision, + last_gas double precision, + last_tvl double precision, + last_usd_tvl double precision, + lp_stat text, + method_name varchar(255), + migrated boolean not null, + owner varchar(255), + owner_balance double precision, + owner_balance_usd double precision, + owner_count integer, + prices text, + profit double precision, + profit_usd double precision, + share_price double precision, + total_amount double precision, + underlying_price double precision, + usd_amount bigint, + vault varchar(255), + network varchar(255), + vault_address varchar(255), + underlying_address varchar(255) +); +alter table harvest_tx + owner to hv_dev; +create index idx_harvest_block_date + on harvest_tx (block_date); +create index idx_harvest_method_name + on harvest_tx (method_name); +create index idx_harvest_network + on harvest_tx (network); +create index idx_harvest_tx + on harvest_tx (block_date); +create index idx_harvest_tx2 + on harvest_tx (method_name, vault); +create index idx_harvest_vault + on harvest_tx (vault); +create index idx_harvest_vault_address + on harvest_tx (vault_address); +grant select on harvest_tx to grafana; +grant select on harvest_tx to backup; + +create table income +( + id varchar(255) not null + primary key, + amount double precision not null, + amount_sum double precision not null, + amount_sum_usd double precision not null, + amount_usd double precision not null, + perc double precision not null, + ps_tvl double precision not null, + ps_tvl_usd double precision not null, + timestamp bigint not null, + week_perc double precision not null +); +alter table income + owner to hv_dev; +create index idx_income + on income (timestamp); +grant select on income to grafana; +grant select on income to backup; + +create table layer_seq +( + seq bigint not null + primary key +); +alter table layer_seq + owner to hv_dev; +grant select on layer_seq to grafana; +grant select on layer_seq to backup; + +create table log_last +( + network varchar(255) not null + primary key, + block bigint +); +alter table log_last + owner to hv_dev; +grant select on log_last to grafana; +grant select on log_last to backup; + + +create table prices +( + id varchar(255) not null + primary key, + block bigint, + block_date bigint, + buy integer, + lp_token0pooled double precision, + lp_token1pooled double precision, + lp_total_supply double precision, + other_token varchar(255), + other_token_amount double precision, + price double precision, + source varchar(255), + token varchar(255), + token_amount double precision, + network varchar(255), + other_token_address varchar(255), + source_address varchar(255), + token_address varchar(255), + owner varchar(255), + recipient varchar(255) +); +alter table prices + owner to hv_dev; +create index idx_prices + on prices (block); +create index idx_prices_network + on prices (network); +create index idx_prices_source + on prices (source); +create index idx_prices_source_address + on prices (source_address); +create index prices_block_date_index + on prices (block_date); +create index prices_network_index + on prices (network); +create index prices_source_index + on prices (source); +grant select on prices to grafana; +grant select on prices to backup; + +create table rewards +( + id varchar(255) not null + primary key, + apy double precision not null, + block bigint not null, + block_date bigint not null, + farm_balance double precision not null, + period_finish bigint not null, + reward double precision not null, + tvl double precision not null, + vault varchar(255), + weekly_apy double precision not null, + network varchar(255), + is_weekly_reward integer, + vault_address varchar(255), + pool_address varchar(255) +); +alter table rewards + owner to hv_dev; +create index idx_rewards + on rewards (block_date); +create index idx_rewards_network + on rewards (network); +create index idx_rewards_vault_address + on rewards (vault_address); +grant select on rewards to grafana; +grant select on rewards to backup; + + +create table strat_info +( + id varchar(255) not null + primary key, + apr double precision, + apy double precision, + block bigint not null, + block_date bigint, + network varchar(255), + percent_of_invested double precision, + percent_of_pool double precision, + platform varchar(255), + pool_address varchar(255), + pool_balance double precision, + pool_extra_info1 varchar(255), + pool_extra_info2 varchar(255), + pool_extra_info3 varchar(255), + pool_specific_underlying varchar(255), + pool_total_supply double precision, + reward_period bigint, + reward_tokens_raw text, + strategy_address varchar(255), + strategy_balance double precision, + strategy_balance_usd double precision, + strategy_created bigint, + strategy_created_date bigint, + strategy_name varchar(255), + strategy_underlying_address varchar(255), + strategy_underlying_name varchar(255), + strategy_underlying_price double precision, + vault_address varchar(255) +); +alter table strat_info + owner to hv_dev; +create index idx_strat_info + on strat_info (block); +create index idx_strat_info_network + on strat_info (network); +create index idx_strat_info_source_vadr + on strat_info (vault_address); +create index idx_strat_info_stadr + on strat_info (strategy_address); +grant select on strat_info to grafana; +grant select on strat_info to backup; + +create table transaction_last +( + network varchar(255) not null + primary key, + block bigint +); +alter table transaction_last + owner to hv_dev; +grant select on transaction_last to grafana; +grant select on transaction_last to backup; + + +create table transfers +( + id varchar(255) not null + primary key, + balance_owner double precision not null, + balance_recipient double precision not null, + block bigint not null, + block_date bigint not null, + method_name varchar(255), + name varchar(255), + owner varchar(255), + price double precision not null, + profit double precision, + profit_usd double precision, + recipient varchar(255), + type varchar(255), + value double precision not null, + network varchar(255), + token_address varchar(255) +); +alter table transfers + owner to hv_dev; +create index idx_transfers_date + on transfers (block_date); +create index idx_transfers_method_name + on transfers (method_name); +create index idx_transfers_name + on transfers (name); +create index idx_transfers_network + on transfers (network); +create index idx_transfers_owner + on transfers (owner); +create index idx_transfers_type + on transfers (type); +grant select on transfers to grafana; +grant select on transfers to backup; + +create table uni_tx +( + id varchar(255) not null + primary key, + amount double precision not null, + block numeric(19, 2), + block_date bigint, + coin varchar(255), + confirmed boolean not null, + hash varchar(255), + last_gas double precision, + last_price double precision, + lp varchar(255), + method_name varchar(255), + other_amount double precision not null, + other_coin varchar(255), + owner varchar(255), + owner_balance double precision, + owner_balance_usd double precision, + owner_count integer, + ps_income_usd double precision, + ps_week_apy double precision, + type varchar(255), + coin_address varchar(255), + lp_address varchar(255), + other_coin_address varchar(255) +); +alter table uni_tx + owner to hv_dev; +create index idx_uni_block_date + on uni_tx (block_date); +create index idx_uni_coin_address + on uni_tx (coin_address); +create index idx_uni_owner + on uni_tx (owner); +create index idx_uni_owner_balance_usd + on uni_tx (owner_balance_usd); +create index idx_uni_tx + on uni_tx (block_date); +grant select on uni_tx to grafana; +grant select on uni_tx to backup; + + +create table covalenthq_vault_tx +( + id bigserial + primary key, + network varchar(255), + block numeric(19, 0), + transaction_hash varchar(255), + contract_decimal numeric(19, 0), + contract_address varchar(255), + owner_address varchar(255), + share_price numeric(60, 0), + token_price numeric(60, 6), + value numeric(60, 0), + signed_at timestamp, + type varchar(255) +); + +create table share_price +( + id varchar(255) primary key, + value numeric(60, 0) +); + +create table token_price +( + id varchar(255) primary key, + value numeric(60, 6) +); + +create table harvest_vault_data +( + id varchar(255) not null + constraint harvest_vault_data_pk + primary key, + vault_address varchar(255) not null, + reward_pool varchar(255), + display_name varchar(255), + network varchar(99), + apy double precision not null, + tvl double precision not null, + total_supply double precision not null +); + diff --git a/src/main/java/pro/belbix/ethparser/AppConfig.java b/src/main/java/pro/belbix/ethparser/AppConfig.java index e268fbb0..06287a08 100644 --- a/src/main/java/pro/belbix/ethparser/AppConfig.java +++ b/src/main/java/pro/belbix/ethparser/AppConfig.java @@ -1,26 +1,31 @@ package pro.belbix.ethparser; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.retry.annotation.EnableRetry; import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.client.RestTemplate; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; import pro.belbix.ethparser.properties.AppProperties; import pro.belbix.ethparser.properties.BscAppProperties; import pro.belbix.ethparser.properties.EthAppProperties; +import pro.belbix.ethparser.properties.ExternalProperties; import pro.belbix.ethparser.properties.MaticAppProperties; -import pro.belbix.ethparser.properties.NetworkPropertiesI; @Configuration @EnableConfigurationProperties({ AppProperties.class, EthAppProperties.class, BscAppProperties.class, - MaticAppProperties.class + MaticAppProperties.class, + ExternalProperties.class }) @EnableScheduling +@EnableRetry public class AppConfig { @Configuration @@ -41,4 +46,9 @@ public void registerStompEndpoints(StompEndpointRegistry registry) { } } + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + } diff --git a/src/main/java/pro/belbix/ethparser/controllers/HardWorkController.java b/src/main/java/pro/belbix/ethparser/controllers/HardWorkController.java index 4d43265f..f08510c0 100644 --- a/src/main/java/pro/belbix/ethparser/controllers/HardWorkController.java +++ b/src/main/java/pro/belbix/ethparser/controllers/HardWorkController.java @@ -48,7 +48,8 @@ public class HardWorkController { public HardWorkController(HardWorkRepository hardWorkRepository, HardWorkCalculator hardWorkCalculator, - ContractDbService contractDbService, DtoCache dtoCache) { + ContractDbService contractDbService, DtoCache dtoCache + ) { this.hardWorkRepository = hardWorkRepository; this.hardWorkCalculator = hardWorkCalculator; this.contractDbService = contractDbService; @@ -291,5 +292,4 @@ public RestResponse hardworkPages( } } - } diff --git a/src/main/java/pro/belbix/ethparser/controllers/HarvestController.java b/src/main/java/pro/belbix/ethparser/controllers/HarvestController.java index 2fb923a4..651c535f 100644 --- a/src/main/java/pro/belbix/ethparser/controllers/HarvestController.java +++ b/src/main/java/pro/belbix/ethparser/controllers/HarvestController.java @@ -4,6 +4,7 @@ import static pro.belbix.ethparser.utils.CommonUtils.parseLong; import static pro.belbix.ethparser.utils.CommonUtils.reduceListElements; +import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ArraySchema; @@ -46,6 +47,7 @@ public class HarvestController { private final VaultActionsDBService vaultActionsDBService; private final ContractDbService contractDbService; private final DtoCache dtoCache; + private final ObjectMapper objectMapper = new ObjectMapper(); public HarvestController(HarvestRepository harvestRepository, VaultActionsDBService vaultActionsDBService, @@ -286,4 +288,15 @@ public RestResponse vaultPeriodOfWork( return RestResponse.ok(periods.get(0).toString()); } + @Operation(summary = "Returns unique owner addresses by network", description = "") + @GetMapping("api/transactions/history/harvest/addresses") + public RestResponse fetchUniqueAddressByNetwork(@RequestParam String network) { + try { + var result = harvestRepository.fetchUniqueAddressByNetwork(network); + return RestResponse.ok(objectMapper.writeValueAsString(result)); + } catch (Exception e) { + return RestResponse.error("Server error"); + } + } + } diff --git a/src/main/java/pro/belbix/ethparser/controllers/HarvestVaultDataController.java b/src/main/java/pro/belbix/ethparser/controllers/HarvestVaultDataController.java new file mode 100644 index 00000000..58b85dea --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/controllers/HarvestVaultDataController.java @@ -0,0 +1,26 @@ +package pro.belbix.ethparser.controllers; + +import java.util.List; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import pro.belbix.ethparser.entity.HarvestVaultData; +import pro.belbix.ethparser.service.HarvestVaultDataService; + +@RestController +@RequiredArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) +public class HarvestVaultDataController { + HarvestVaultDataService harvestVaultDataService; + + @GetMapping("/harvest/vaults") + @ResponseStatus(HttpStatus.OK) + public List getAll() { + return harvestVaultDataService.getAllVaultInfo(); + } + +} diff --git a/src/main/java/pro/belbix/ethparser/controllers/ProfitController.java b/src/main/java/pro/belbix/ethparser/controllers/ProfitController.java new file mode 100644 index 00000000..62f436ef --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/controllers/ProfitController.java @@ -0,0 +1,73 @@ +package pro.belbix.ethparser.controllers; + +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; + +import io.swagger.v3.oas.annotations.Parameter; +import lombok.extern.log4j.Log4j2; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import pro.belbix.ethparser.model.ProfitListResult; +import pro.belbix.ethparser.model.ProfitResult; +import pro.belbix.ethparser.service.ProfitService; + +@RestController +@Log4j2 +public class ProfitController { + + private final ProfitService profitService; + + public ProfitController(ProfitService profitService){ + + this.profitService = profitService; + } + + @RequestMapping(value = "api/profit/total", method = RequestMethod.GET) + public Double fetchProfit( + @RequestParam("address") @Parameter(description = "Owner address") String address, + @RequestParam(value = "start", required = false, defaultValue = "0") + @Parameter(description = "Block creation time from") String start, + @RequestParam(value = "end", required = false, defaultValue = Long.MAX_VALUE + "") + @Parameter(description = "Block creation time to") String end + ) { + + return profitService.calculationProfitForPeriod(address, start, end); + } + + @RequestMapping(value = "api/profit/vaults", method = RequestMethod.GET) + public Double fetchProfitByVault( + @RequestParam("address") @Parameter(description = "Vault address") String address, + @RequestParam(value = "network", required = false, defaultValue = ETH_NETWORK) String network, + @RequestParam(value = "start", required = false, defaultValue = "0") + @Parameter(description = "Block creation time from") String start, + @RequestParam(value = "end", required = false, defaultValue = Long.MAX_VALUE + "") + @Parameter(description = "Block creation time to") String end + ) { + + return profitService.calculationProfitByVaultForPeriod(address, network, start, end); + } + + @GetMapping("api/profit") + public ProfitResult calculateProfit( + @RequestParam String address, + @RequestParam(required = false, defaultValue = "eth") String network, + @RequestParam(required = false, defaultValue = "") String vaultAddress, + @RequestParam(required = false, defaultValue = "0") Long blockFrom, + @RequestParam(required = false, defaultValue = "0") Long blockTo) { + return new ProfitResult(profitService.calculateProfit(address, network, vaultAddress, blockFrom, blockTo)); + } + + @GetMapping("api/profit/{address}") + public ProfitListResult calculateProfit(@PathVariable String address, @RequestParam(required = false, defaultValue = "eth") String network) { + return profitService.calculateProfit(address, network); + } + + @GetMapping("api/profit/vault") + public ProfitResult calculateVaultProfit(@RequestParam String address, @RequestParam String network, + @RequestParam long blockFrom, @RequestParam long blockTo) { + return new ProfitResult(profitService.calculateVaultProfit(address, network, blockFrom, blockTo)); + } +} diff --git a/src/main/java/pro/belbix/ethparser/dto/v0/HardWorkHarvestDTO.java b/src/main/java/pro/belbix/ethparser/dto/v0/HardWorkHarvestDTO.java new file mode 100644 index 00000000..8d43f8e7 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/dto/v0/HardWorkHarvestDTO.java @@ -0,0 +1,53 @@ +package pro.belbix.ethparser.dto.v0; + +import java.math.BigDecimal; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class HardWorkHarvestDTO extends HardWorkDTO { + BigDecimal apy; + BigDecimal tvl; + + public HardWorkHarvestDTO(HardWorkDTO hardWorkDTO) { + + id = hardWorkDTO.id; + vault = hardWorkDTO.vault; + vaultAddress = hardWorkDTO.vaultAddress; + block = hardWorkDTO.block; + blockDate = hardWorkDTO.blockDate; + network = hardWorkDTO.network; + shareChange = hardWorkDTO.shareChange; + fullRewardUsd = hardWorkDTO.fullRewardUsd; + farmBuyback = hardWorkDTO.farmBuyback; + fee = hardWorkDTO.fee; + farmBuybackEth = hardWorkDTO.farmBuybackEth; + gasUsed = hardWorkDTO.gasUsed; + invested = hardWorkDTO.invested; + investmentTarget = hardWorkDTO.investmentTarget; + farmPrice = hardWorkDTO.farmPrice; + ethPrice = hardWorkDTO.ethPrice; + profitSharingRate = hardWorkDTO.profitSharingRate; + buyBackRate = hardWorkDTO.buyBackRate; + autoStake = hardWorkDTO.autoStake; + + idleTime = hardWorkDTO.idleTime; + feeEth = hardWorkDTO.feeEth; + savedGasFeesSum = hardWorkDTO.savedGasFeesSum; + savedGasFees = hardWorkDTO.savedGasFees; + poolUsers = hardWorkDTO.poolUsers; + callsQuantity = hardWorkDTO.callsQuantity; + farmBuybackSum = hardWorkDTO.farmBuybackSum; + psApr = hardWorkDTO.psApr; + psTvlUsd = hardWorkDTO.psTvlUsd; + weeklyProfit = hardWorkDTO.weeklyProfit; + + weeklyAllProfit = hardWorkDTO.weeklyAllProfit; + apr = hardWorkDTO.apr; + perc = hardWorkDTO.perc; + fullRewardUsdTotal = hardWorkDTO.fullRewardUsdTotal; + allProfit = hardWorkDTO.allProfit; + + } +} diff --git a/src/main/java/pro/belbix/ethparser/entity/ErrorEntity.java b/src/main/java/pro/belbix/ethparser/entity/ErrorEntity.java index 91928856..86a9a1e4 100644 --- a/src/main/java/pro/belbix/ethparser/entity/ErrorEntity.java +++ b/src/main/java/pro/belbix/ethparser/entity/ErrorEntity.java @@ -25,4 +25,5 @@ public class ErrorEntity { private String json; private String errorClass; private String network; + private Integer status; } diff --git a/src/main/java/pro/belbix/ethparser/entity/HarvestVaultData.java b/src/main/java/pro/belbix/ethparser/entity/HarvestVaultData.java new file mode 100644 index 00000000..4aa891bb --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/entity/HarvestVaultData.java @@ -0,0 +1,31 @@ +package pro.belbix.ethparser.entity; + +import java.math.BigDecimal; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldDefaults; + +@Entity +@Table(name = "harvest_vault_data") +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class HarvestVaultData { + @Id + String id; + String vaultAddress; + String rewardPool; + String displayName; + BigDecimal apy; + BigDecimal tvl; + BigDecimal totalSupply; + String network; +} diff --git a/src/main/java/pro/belbix/ethparser/entity/contracts/ContractEntity.java b/src/main/java/pro/belbix/ethparser/entity/contracts/ContractEntity.java index ce105f64..1f9fd928 100644 --- a/src/main/java/pro/belbix/ethparser/entity/contracts/ContractEntity.java +++ b/src/main/java/pro/belbix/ethparser/entity/contracts/ContractEntity.java @@ -1,8 +1,6 @@ package pro.belbix.ethparser.entity.contracts; import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Index; import javax.persistence.Table; @@ -26,7 +24,8 @@ public class ContractEntity { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) +// TODO Can not save for HarvestVaultInfoTask, after return GeneratedValue annotation +// @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String address; private String name; diff --git a/src/main/java/pro/belbix/ethparser/entity/contracts/VaultEntity.java b/src/main/java/pro/belbix/ethparser/entity/contracts/VaultEntity.java index f3cecb59..9865a342 100644 --- a/src/main/java/pro/belbix/ethparser/entity/contracts/VaultEntity.java +++ b/src/main/java/pro/belbix/ethparser/entity/contracts/VaultEntity.java @@ -2,8 +2,6 @@ import javax.persistence.Entity; import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Index; import javax.persistence.JoinColumn; @@ -22,7 +20,8 @@ public class VaultEntity { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) +// TODO Can not save for HarvestVaultInfoTask, after return GeneratedValue annotation +// @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @OneToOne(fetch = FetchType.EAGER) @JoinColumn(name = "contract", unique = true) diff --git a/src/main/java/pro/belbix/ethparser/entity/profit/CovalenthqVaultTransaction.java b/src/main/java/pro/belbix/ethparser/entity/profit/CovalenthqVaultTransaction.java new file mode 100644 index 00000000..016e8019 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/entity/profit/CovalenthqVaultTransaction.java @@ -0,0 +1,36 @@ +package pro.belbix.ethparser.entity.profit; + + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDateTime; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import lombok.AccessLevel; +import lombok.Data; +import lombok.experimental.FieldDefaults; + +@Entity +@Table(name = "covalenthq_vault_tx") +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class CovalenthqVaultTransaction { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + Integer id; + String network; + long block; + String transactionHash; + int contractDecimal; + String contractAddress; + String ownerAddress; + BigDecimal value; + BigInteger sharePrice; + Double tokenPrice; + LocalDateTime signedAt; + String type; +} diff --git a/src/main/java/pro/belbix/ethparser/entity/profit/CovalenthqVaultTransactionType.java b/src/main/java/pro/belbix/ethparser/entity/profit/CovalenthqVaultTransactionType.java new file mode 100644 index 00000000..dcaa49b4 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/entity/profit/CovalenthqVaultTransactionType.java @@ -0,0 +1,35 @@ +package pro.belbix.ethparser.entity.profit; + +import lombok.AccessLevel; +import lombok.experimental.FieldDefaults; + +@FieldDefaults(makeFinal = true, level = AccessLevel.PUBLIC) +public enum CovalenthqVaultTransactionType { + WITHDRAW_UNI("account", "writeAmount", "Withdraw", 3), + WITHDRAW("provider", "value", "Withdraw", 2), + DEPOSIT_UNI("user", "amount", "Deposit", 3), + DEPOSIT("dst", "wad", "Deposit", 2), + TRANSFER("from", "to", "valueTransfer", "Transfer", 3); + + CovalenthqVaultTransactionType(String address, String value, String type, int paramSize) { + this.address = address; + this.value = value; + this.type = type; + this.paramSize = paramSize; + this.toAddress = ""; + } + + CovalenthqVaultTransactionType(String address, String toAddress, String value, String type, int paramSize) { + this.address = address; + this.value = value; + this.type = type; + this.paramSize = paramSize; + this.toAddress = toAddress; + } + + String address; + String toAddress; + String value; + String type; + int paramSize; +} diff --git a/src/main/java/pro/belbix/ethparser/entity/profit/SharePrice.java b/src/main/java/pro/belbix/ethparser/entity/profit/SharePrice.java new file mode 100644 index 00000000..8a18b7a3 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/entity/profit/SharePrice.java @@ -0,0 +1,23 @@ +package pro.belbix.ethparser.entity.profit; + +import java.math.BigInteger; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldDefaults; + +@Entity +@Table(name = "share_price") +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +@NoArgsConstructor +@AllArgsConstructor +public class SharePrice { + @Id + String id; + BigInteger value; +} diff --git a/src/main/java/pro/belbix/ethparser/entity/profit/TokenPrice.java b/src/main/java/pro/belbix/ethparser/entity/profit/TokenPrice.java new file mode 100644 index 00000000..d6b58f71 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/entity/profit/TokenPrice.java @@ -0,0 +1,22 @@ +package pro.belbix.ethparser.entity.profit; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldDefaults; + +@Entity +@Table(name = "token_price") +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +@NoArgsConstructor +@AllArgsConstructor +public class TokenPrice { + @Id + String id; + Double value; +} diff --git a/src/main/java/pro/belbix/ethparser/error/GlobalControllerAdvice.java b/src/main/java/pro/belbix/ethparser/error/GlobalControllerAdvice.java new file mode 100644 index 00000000..37dc9b80 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/error/GlobalControllerAdvice.java @@ -0,0 +1,22 @@ +package pro.belbix.ethparser.error; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import pro.belbix.ethparser.error.exceptions.CanNotCalculateProfitException; +import pro.belbix.ethparser.model.ErrorResponse; + +@RestControllerAdvice +public class GlobalControllerAdvice { + + @ExceptionHandler(CanNotCalculateProfitException.class) + public ResponseEntity canNotCalculateProfitException() { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body( + ErrorResponse.builder() + .message("Can not calculate profit, try later again") + .code("500") + .build() + ); + } +} diff --git a/src/main/java/pro/belbix/ethparser/error/exceptions/CanNotCalculateProfitException.java b/src/main/java/pro/belbix/ethparser/error/exceptions/CanNotCalculateProfitException.java new file mode 100644 index 00000000..efa15088 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/error/exceptions/CanNotCalculateProfitException.java @@ -0,0 +1,5 @@ +package pro.belbix.ethparser.error.exceptions; + +public class CanNotCalculateProfitException extends RuntimeException { + +} diff --git a/src/main/java/pro/belbix/ethparser/error/exceptions/CanNotFetchPriceException.java b/src/main/java/pro/belbix/ethparser/error/exceptions/CanNotFetchPriceException.java new file mode 100644 index 00000000..aee689ad --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/error/exceptions/CanNotFetchPriceException.java @@ -0,0 +1,4 @@ +package pro.belbix.ethparser.error.exceptions; + +public class CanNotFetchPriceException extends RuntimeException{ +} diff --git a/src/main/java/pro/belbix/ethparser/model/AbiProviderResponse.java b/src/main/java/pro/belbix/ethparser/model/AbiProviderResponse.java new file mode 100644 index 00000000..fba799c4 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/model/AbiProviderResponse.java @@ -0,0 +1,13 @@ +package pro.belbix.ethparser.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.AccessLevel; +import lombok.Data; +import lombok.experimental.FieldDefaults; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +@JsonIgnoreProperties(ignoreUnknown = true) +public class AbiProviderResponse { + long result; +} diff --git a/src/main/java/pro/belbix/ethparser/model/CovalenthqHistoricalPrice.java b/src/main/java/pro/belbix/ethparser/model/CovalenthqHistoricalPrice.java new file mode 100644 index 00000000..18a41619 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/model/CovalenthqHistoricalPrice.java @@ -0,0 +1,19 @@ +package pro.belbix.ethparser.model; + +import java.util.List; +import lombok.Data; + +@Data +public class CovalenthqHistoricalPrice { + private CovalenthqHistoricalPriceData data; + + @Data + public static class CovalenthqHistoricalPriceData { + private List prices; + + @Data + public static class CovalenthqHistoricalPriceDataPrice { + private Double price; + } + } +} diff --git a/src/main/java/pro/belbix/ethparser/model/CovalenthqTransactionByContractAddress.java b/src/main/java/pro/belbix/ethparser/model/CovalenthqTransactionByContractAddress.java new file mode 100644 index 00000000..901e62d9 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/model/CovalenthqTransactionByContractAddress.java @@ -0,0 +1,26 @@ +package pro.belbix.ethparser.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class CovalenthqTransactionByContractAddress { + private CovalenthqTransactionByContractAddressData data; + + @Data + public static class CovalenthqTransactionByContractAddressData { + private CovalenthqTransactionByContractAddressDataItems items; + + @Data + public static class CovalenthqTransactionByContractAddressDataItems { + @JsonProperty("tx_hash") + private String hash; + private boolean successful; + @JsonProperty("from_address") + private String fromAddress; + @JsonProperty("to_address") + private String toAddress; + + } + } +} diff --git a/src/main/java/pro/belbix/ethparser/model/CovalenthqTransactionHistory.java b/src/main/java/pro/belbix/ethparser/model/CovalenthqTransactionHistory.java new file mode 100644 index 00000000..cf654571 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/model/CovalenthqTransactionHistory.java @@ -0,0 +1,68 @@ +package pro.belbix.ethparser.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import java.time.LocalDateTime; +import java.util.List; +import lombok.Data; + +@Data +@JsonIgnoreProperties +public class CovalenthqTransactionHistory { + private CovalenthqTransactionHistoryItems data; + + @Data + @JsonIgnoreProperties + public static class CovalenthqTransactionHistoryItems { + private List items; + private CovalenthqTransactionHistoryPagination pagination; + + @Data + @JsonIgnoreProperties + public static class CovalenthqTransactionHistoryItem { + @JsonProperty("block_height") + private long blockHeight; + @JsonProperty("tx_hash") + private String transactionHash; + @JsonProperty("from_address") + private String fromAddress; + @JsonProperty("to_address") + private String toAddress; + @JsonProperty("log_events") + private List logs; + + @Data + @JsonIgnoreProperties + public static class CovalenthqTransactionHistoryItemLog { + @JsonProperty("block_signed_at") + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'") + private LocalDateTime signedAt; + @JsonProperty("block_height") + private long blockHeight; + @JsonProperty("tx_hash") + private String transactionHash; + @JsonProperty("sender_contract_decimals") + private int contractDecimal; + @JsonProperty(value = "raw_log_topics") + private List topics; + @JsonProperty("raw_log_data") + private String data; + @JsonProperty("sender_address") + private String senderAddress; + } + } + + @Data + public static class CovalenthqTransactionHistoryPagination { + @JsonProperty("has_more") + private boolean hasMore; + } + } +} diff --git a/src/main/java/pro/belbix/ethparser/model/ErrorResponse.java b/src/main/java/pro/belbix/ethparser/model/ErrorResponse.java new file mode 100644 index 00000000..c1038cf4 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/model/ErrorResponse.java @@ -0,0 +1,14 @@ +package pro.belbix.ethparser.model; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Data; +import lombok.experimental.FieldDefaults; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +@Builder +public class ErrorResponse { + String message; + String code; +} diff --git a/src/main/java/pro/belbix/ethparser/model/HarvestPoolInfo.java b/src/main/java/pro/belbix/ethparser/model/HarvestPoolInfo.java new file mode 100644 index 00000000..026b25bf --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/model/HarvestPoolInfo.java @@ -0,0 +1,25 @@ +package pro.belbix.ethparser.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import lombok.Data; +import lombok.ToString; + +@Data +@ToString +public class HarvestPoolInfo { + @JsonProperty("eth") + private List ethereumNetwork; + @JsonProperty("matic") + private List maticNetwork; + @JsonProperty("bsc") + private List bscNetwork; + + @Data + @ToString + public static class HarvestPoolItemInfo { + private String contractAddress; + private String type; + private String id; + } +} diff --git a/src/main/java/pro/belbix/ethparser/model/HarvestVaultInfo.java b/src/main/java/pro/belbix/ethparser/model/HarvestVaultInfo.java new file mode 100644 index 00000000..7b0d9d88 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/model/HarvestVaultInfo.java @@ -0,0 +1,29 @@ +package pro.belbix.ethparser.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Map; +import lombok.Data; + +@Data +public class HarvestVaultInfo { + @JsonProperty("eth") + private Map ethereumNetwork; + @JsonProperty("matic") + private Map maticNetwork; + @JsonProperty("bsc") + private Map bscNetwork; + + + @Data + public static class HarvestVaultItemInfo { + private int chain; + private String vaultAddress; + private String id; + private String rewardPool; + private String displayName; + private BigDecimal estimatedApy; + private BigDecimal totalValueLocked; + private BigDecimal totalSupply; + } +} diff --git a/src/main/java/pro/belbix/ethparser/model/ProfitListResult.java b/src/main/java/pro/belbix/ethparser/model/ProfitListResult.java new file mode 100644 index 00000000..09aa4cba --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/model/ProfitListResult.java @@ -0,0 +1,31 @@ +package pro.belbix.ethparser.model; + +import java.math.BigDecimal; +import java.util.List; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldDefaults; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ProfitListResult { + BigDecimal totalProfit; + List items; + + @Data + @FieldDefaults(level = AccessLevel.PRIVATE) + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class ProfitListResultItem { + String name; + String contractAddress; + BigDecimal profit; + } +} diff --git a/src/main/java/pro/belbix/ethparser/model/ProfitResult.java b/src/main/java/pro/belbix/ethparser/model/ProfitResult.java new file mode 100644 index 00000000..3e3dba3a --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/model/ProfitResult.java @@ -0,0 +1,11 @@ +package pro.belbix.ethparser.model; + +import java.math.BigDecimal; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class ProfitResult { + private BigDecimal result; +} diff --git a/src/main/java/pro/belbix/ethparser/model/TokenInfo.java b/src/main/java/pro/belbix/ethparser/model/TokenInfo.java new file mode 100644 index 00000000..06b32523 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/model/TokenInfo.java @@ -0,0 +1,20 @@ +package pro.belbix.ethparser.model; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldDefaults; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@FieldDefaults(level = AccessLevel.PRIVATE) +@EqualsAndHashCode +public class TokenInfo { + String address; + String network; +} diff --git a/src/main/java/pro/belbix/ethparser/properties/ExternalProperties.java b/src/main/java/pro/belbix/ethparser/properties/ExternalProperties.java new file mode 100644 index 00000000..8cdb431a --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/properties/ExternalProperties.java @@ -0,0 +1,27 @@ +package pro.belbix.ethparser.properties; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.FieldDefaults; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +@Validated +@ConfigurationProperties(prefix = "external") +@FieldDefaults(level = AccessLevel.PRIVATE) +@Getter +@Setter +public class ExternalProperties { + ExternalApi covalenthq; + ExternalApi harvest; + + @FieldDefaults(level = AccessLevel.PRIVATE) + @Getter + @Setter + public static class ExternalApi { + String url; + String key; + } + +} diff --git a/src/main/java/pro/belbix/ethparser/repositories/HarvestVaultDataRepository.java b/src/main/java/pro/belbix/ethparser/repositories/HarvestVaultDataRepository.java new file mode 100644 index 00000000..3de2f258 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/repositories/HarvestVaultDataRepository.java @@ -0,0 +1,8 @@ +package pro.belbix.ethparser.repositories; + +import org.springframework.data.jpa.repository.JpaRepository; +import pro.belbix.ethparser.entity.HarvestVaultData; + +public interface HarvestVaultDataRepository extends JpaRepository { + +} diff --git a/src/main/java/pro/belbix/ethparser/repositories/SharePriceRepository.java b/src/main/java/pro/belbix/ethparser/repositories/SharePriceRepository.java new file mode 100644 index 00000000..e13fa1c0 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/repositories/SharePriceRepository.java @@ -0,0 +1,8 @@ +package pro.belbix.ethparser.repositories; + +import org.springframework.data.jpa.repository.JpaRepository; +import pro.belbix.ethparser.entity.profit.SharePrice; + +public interface SharePriceRepository extends JpaRepository { + +} diff --git a/src/main/java/pro/belbix/ethparser/repositories/TokenPriceRepository.java b/src/main/java/pro/belbix/ethparser/repositories/TokenPriceRepository.java new file mode 100644 index 00000000..59a7ecfc --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/repositories/TokenPriceRepository.java @@ -0,0 +1,8 @@ +package pro.belbix.ethparser.repositories; + +import org.springframework.data.jpa.repository.JpaRepository; +import pro.belbix.ethparser.entity.profit.TokenPrice; + +public interface TokenPriceRepository extends JpaRepository { + +} diff --git a/src/main/java/pro/belbix/ethparser/repositories/covalenthq/CovalenthqVaultTransactionRepository.java b/src/main/java/pro/belbix/ethparser/repositories/covalenthq/CovalenthqVaultTransactionRepository.java new file mode 100644 index 00000000..d27ac4e5 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/repositories/covalenthq/CovalenthqVaultTransactionRepository.java @@ -0,0 +1,26 @@ +package pro.belbix.ethparser.repositories.covalenthq; + +import java.util.List; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import pro.belbix.ethparser.entity.profit.CovalenthqVaultTransaction; + +public interface CovalenthqVaultTransactionRepository extends JpaRepository { + List findAllByNetworkAndContractAddress(String network, String contractAddress, Pageable pageable); + List findAllByTransactionHashIn(List transactionHashes); + @Query("select c from CovalenthqVaultTransaction c " + + "where c.network = :network AND " + + "c.contractAddress like %:address% AND " + + "c.block BETWEEN :blockFrom AND :blockTo AND " + + "c.ownerAddress = :ownerAddress") + List findAllByOwnerAddressAndNetwork(String ownerAddress, String network, String address, Long blockFrom, Long blockTo); + + @Query("select c from CovalenthqVaultTransaction c " + + "where c.network = :network AND c.contractAddress = :address AND c.block BETWEEN :blockFrom AND :blockTo") + List findAllByContractAddressAndBlockBetween(String address, String network, long blockFrom, long blockTo); + + @Query("select c from CovalenthqVaultTransaction c " + + "where c.ownerAddress = :address and c.network = :network") + List findAllByOwnerAndNetwork(String address, String network); +} diff --git a/src/main/java/pro/belbix/ethparser/repositories/eth/ContractRepository.java b/src/main/java/pro/belbix/ethparser/repositories/eth/ContractRepository.java index 45d654ca..64403d99 100644 --- a/src/main/java/pro/belbix/ethparser/repositories/eth/ContractRepository.java +++ b/src/main/java/pro/belbix/ethparser/repositories/eth/ContractRepository.java @@ -16,6 +16,14 @@ ContractEntity findFirstByAddress( @Param("network") String network ); + @Query("select t from ContractEntity t " + + "where lower(t.address) = lower(:address) and t.network = :network") + List findFirstByAddress( + @Param("address") String address, + @Param("network") String network, + Pageable pageable + ); + @Query("select t from ContractEntity t " + "where lower(t.address) = lower(:address) and t.type = :type and t.network = :network") ContractEntity findFirstByAddressAndType( @@ -46,4 +54,22 @@ List findPoolsByVaultAddress( @Param("network") String network ); + @Query("select c from ContractEntity c " + + "where c.network = :network " + + "and lower(c.address) in(:addresses) " + + "and c.type = :type") + List findAllByNetworkAndInAddressAndType(String network, List addresses, int type); + + @Query("select c from ContractEntity c " + + "left join UniPairEntity u on u.contract.id = c.id " + + "left join TokenToUniPairEntity t on t.uniPair.id = u.id " + + "where c.type = 2 and t.id is null") + List findAllUniPairContractWithoutData(); + + @Query("select c from ContractEntity c " + + "where c.type = 0 and c.network = :network") + List findAllVaultsByNetwork(String network); + + @Query("select max(t.id) from ContractEntity t") + int findMaxId(); } diff --git a/src/main/java/pro/belbix/ethparser/repositories/eth/TokenRepository.java b/src/main/java/pro/belbix/ethparser/repositories/eth/TokenRepository.java index c420b50b..d0230abf 100644 --- a/src/main/java/pro/belbix/ethparser/repositories/eth/TokenRepository.java +++ b/src/main/java/pro/belbix/ethparser/repositories/eth/TokenRepository.java @@ -4,7 +4,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import pro.belbix.ethparser.entity.contracts.ContractEntity; import pro.belbix.ethparser.entity.contracts.TokenEntity; public interface TokenRepository extends JpaRepository { @@ -29,4 +28,7 @@ TokenEntity findFirstByName( + "left join fetch t.contract f1 " + "where f1.network = :network") List fetchAllByNetwork(@Param("network") String network); + + @Query("select max(t.id) from TokenEntity t") + int findMaxId(); } diff --git a/src/main/java/pro/belbix/ethparser/repositories/eth/TokenToUniPairRepository.java b/src/main/java/pro/belbix/ethparser/repositories/eth/TokenToUniPairRepository.java index 4db8eb7b..c3e0d77a 100644 --- a/src/main/java/pro/belbix/ethparser/repositories/eth/TokenToUniPairRepository.java +++ b/src/main/java/pro/belbix/ethparser/repositories/eth/TokenToUniPairRepository.java @@ -4,9 +4,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import pro.belbix.ethparser.entity.contracts.TokenEntity; import pro.belbix.ethparser.entity.contracts.TokenToUniPairEntity; -import pro.belbix.ethparser.entity.contracts.UniPairEntity; public interface TokenToUniPairRepository extends JpaRepository { @@ -58,4 +56,15 @@ List findByUniPair( @Param("network") String network ); + @Query("select t from TokenToUniPairEntity t " + + "join fetch t.uniPair f1 " + + "join fetch f1.contract c " + + "where lower(c.address) = :uniPairAdr and c.network = :network") + List findByUniPairAddress( + @Param("uniPairAdr") String uniPairAdr, + @Param("network") String network + ); + + @Query("select max(t.id) from TokenToUniPairEntity t") + int findMaxId(); } diff --git a/src/main/java/pro/belbix/ethparser/repositories/eth/VaultRepository.java b/src/main/java/pro/belbix/ethparser/repositories/eth/VaultRepository.java index dd4f8ac2..61f8a4b3 100644 --- a/src/main/java/pro/belbix/ethparser/repositories/eth/VaultRepository.java +++ b/src/main/java/pro/belbix/ethparser/repositories/eth/VaultRepository.java @@ -4,7 +4,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import pro.belbix.ethparser.entity.contracts.ContractEntity; import pro.belbix.ethparser.entity.contracts.VaultEntity; public interface VaultRepository extends JpaRepository { @@ -29,4 +28,17 @@ VaultEntity findFirstByContract( + "left join fetch t.underlying f5 " + "where f1.network = :network") List fetchAllByNetwork(@Param("network") String network); + + @Query("select t from VaultEntity t " + + "left join fetch t.contract f1 " + + "where f1.network = :network") + List findAllByNetwork(String network); + + @Query("select t from VaultEntity t " + + "left join fetch t.contract f1 " + + "where f1.network = :network and f1.address in :address") + List findAllInContractAddress(List address, String network); + + @Query("select max(t.id) from VaultEntity t") + int findMaxId(); } diff --git a/src/main/java/pro/belbix/ethparser/repositories/v0/HarvestRepository.java b/src/main/java/pro/belbix/ethparser/repositories/v0/HarvestRepository.java index 2e818e8f..a4f1d9c0 100644 --- a/src/main/java/pro/belbix/ethparser/repositories/v0/HarvestRepository.java +++ b/src/main/java/pro/belbix/ethparser/repositories/v0/HarvestRepository.java @@ -100,6 +100,32 @@ Integer fetchAllPoolsUsersQuantity( @Param("network") String network ); + @Query(nativeQuery = true, value = "" + + " select coalesce(sum(t.deposit), 0) as difference " + + " from ( " + + " select - sum(harvest_tx.owner_balance) as deposit " + + " from harvest_tx " + + " where harvest_tx.owner = :owner " + + " and harvest_tx.vault = :vault " + + " and harvest_tx.network = :network " + + " and harvest_tx.method_name = 'Deposit' " + + " and harvest_tx.block_date <= :block_date " + + " union all " + + " select sum(harvest_tx.owner_balance) as withdrow " + + " from harvest_tx " + + " where harvest_tx.owner = :owner " + + " and harvest_tx.vault = :vault " + + " and harvest_tx.network = :network " + + " and harvest_tx.method_name = 'Withdraw' " + + " and harvest_tx.block_date <= :block_date " + + " ) t ") + Double fetchTotalDifferenceDepositAndWithdraw( + @Param("vault") String vault, + @Param("owner") String owner, + @Param("block_date") long blockDate, + @Param("network") String network + ); + HarvestDTO findFirstByNetworkOrderByBlockDesc(String network); @Query("select max(t.blockDate) - min(t.blockDate) as period from HarvestDTO t where " @@ -134,6 +160,28 @@ List fetchAllByOwner( @Param("network") String network ); + @Query("select t from HarvestDTO t where " + + "t.owner = :owner " + + "and t.blockDate between :from and :to " + + "order by t.blockDate asc") + List fetchAllByOwnerWithoutNetwork( + @Param("owner") String owner, + @Param("from") long from, + @Param("to") long to + ); + + @Query("select t from HarvestDTO t where " + + "lower(t.vaultAddress) = lower(:vault) " + + "and t.blockDate between :from and :to " + + "and t.network = :network " + + "order by t.blockDate asc") + List fetchAllByVaultAddressAndNetwork( + @Param("vault") String vaultAddress, + @Param("from") long from, + @Param("to") long to, + @Param("network") String network + ); + @Query("select t from HarvestDTO t where " + "(t.ownerBalance is null " + "or t.ownerBalanceUsd is null) " @@ -259,6 +307,13 @@ Page fetchPagesByVault( + "order by balance desc") List fetchOwnerBalances(@Param("network") String network); + @Query("select h.owner from HarvestDTO h " + + "where h.network = :network " + + "group by h.owner") + List fetchUniqueAddressByNetwork(@Param("network") String network); + + List findAllByOwner(String owner); + interface UserBalance { String getOwner(); diff --git a/src/main/java/pro/belbix/ethparser/repositories/v0/PriceRepository.java b/src/main/java/pro/belbix/ethparser/repositories/v0/PriceRepository.java index e4402b6c..5427ea21 100644 --- a/src/main/java/pro/belbix/ethparser/repositories/v0/PriceRepository.java +++ b/src/main/java/pro/belbix/ethparser/repositories/v0/PriceRepository.java @@ -3,8 +3,10 @@ import java.util.List; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; import pro.belbix.ethparser.dto.v0.PriceDTO; public interface PriceRepository extends JpaRepository { @@ -47,6 +49,8 @@ List fetchLastPrices( + "group by source_address") List fetchAllSourceAddresses(@Param("network") String network); + @Modifying + @Transactional @Query(nativeQuery = true, value = "" + "delete from prices " + "where block_date < :delete_all_before") diff --git a/src/main/java/pro/belbix/ethparser/service/AbiProviderService.java b/src/main/java/pro/belbix/ethparser/service/AbiProviderService.java index e8f058a0..62eb4dd9 100644 --- a/src/main/java/pro/belbix/ethparser/service/AbiProviderService.java +++ b/src/main/java/pro/belbix/ethparser/service/AbiProviderService.java @@ -11,13 +11,17 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import lombok.Data; import lombok.extern.log4j.Log4j2; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; +import pro.belbix.ethparser.model.AbiProviderResponse; @Log4j2 +@Service public class AbiProviderService { public final static int RATE_TIMEOUT = 1000; @@ -29,6 +33,7 @@ public class AbiProviderService { private final static String BSC_URL = "https://api.bscscan.com/api"; private final static String MATIC_URL = "https://api.polygonscan.com/api"; private final static String PARAMS = "?module={module}&action={action}&address={address}&apikey={apikey}"; + private final static String PARAMS_FOR_BLOCK = "?module={module}&action={action}&apikey={apikey}&closest={closest}×tamp={timestamp}"; private final RestTemplate restTemplate = new RestTemplate(); private Instant lastRequest = Instant.now(); @@ -92,6 +97,27 @@ public SourceCodeResult contractSourceCode(String address, String apiKey, String return result; } + public long getBlockByTimestamp(String timestamp, String network, String apiKey) { + var params = Map.of( + "module", "block", + "action", "getblocknobytime", + "apikey", apiKey, + "closest", "after", + "timestamp", timestamp + ); + + var result = restTemplate.getForObject( + getUrl(network) + PARAMS_FOR_BLOCK, + AbiProviderResponse.class, + params + ); + + return Optional.ofNullable(result) + .orElse(new AbiProviderResponse()) + .getResult(); + + } + private ResponseEntity sendRequest(Map vars, String network) { int count = 0; while (true) { @@ -134,7 +160,7 @@ private String getUrl(String network) { return ETHERSCAN_URL; } else if (BSC_NETWORK.equals(network)) { return BSC_URL; - }else if (MATIC_NETWORK.equals(network)) { + } else if (MATIC_NETWORK.equals(network)) { return MATIC_URL; } else { throw new IllegalStateException("Unknown network " + network); diff --git a/src/main/java/pro/belbix/ethparser/service/ErrorService.java b/src/main/java/pro/belbix/ethparser/service/ErrorService.java index 5a0a8a53..13b885c2 100644 --- a/src/main/java/pro/belbix/ethparser/service/ErrorService.java +++ b/src/main/java/pro/belbix/ethparser/service/ErrorService.java @@ -68,6 +68,9 @@ public void startFixErrorService() { log.info("Load errors from db: " + listErrors.size()); for (ErrorEntity errorEntity : listErrors) { try { + if (errorEntity.getStatus() != null && errorEntity.getStatus() == 1) { + continue; + } parseObject(errorEntity); errorDbService.delete(errorEntity); } catch (Exception e) { @@ -80,7 +83,7 @@ public void startFixErrorService() { public void parseObject(ErrorEntity errorEntity) { if (errorEntity == null || errorEntity.getErrorClass() == null) { - throw new IllegalStateException("Detected unknown errorClass: " + errorEntity.toString()); + throw new IllegalStateException("Detected unknown errorClass: " + errorEntity); } switch (errorEntity.getErrorClass()) { case "DeployerTransactionsParser": diff --git a/src/main/java/pro/belbix/ethparser/service/HarvestVaultDataService.java b/src/main/java/pro/belbix/ethparser/service/HarvestVaultDataService.java new file mode 100644 index 00000000..6168017d --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/service/HarvestVaultDataService.java @@ -0,0 +1,61 @@ +package pro.belbix.ethparser.service; + + +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import pro.belbix.ethparser.entity.HarvestVaultData; +import pro.belbix.ethparser.model.HarvestVaultInfo.HarvestVaultItemInfo; +import pro.belbix.ethparser.repositories.HarvestVaultDataRepository; + +@Service +@RequiredArgsConstructor +@Slf4j +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class HarvestVaultDataService { + private final static Map CHAIN_BY_NETWORK = Map.of( + 137, MATIC_NETWORK, + 1, ETH_NETWORK, + 56, BSC_NETWORK + ); + + HarvestVaultDataRepository harvestVaultDataRepository; + + public List saveAll(List items) { + return harvestVaultDataRepository.saveAll( + items.stream() + .map(this::toHarvestVaultData) + .collect(Collectors.toList()) + ); + } + + public HarvestVaultData save(HarvestVaultItemInfo harvestVaultInfo) { + return harvestVaultDataRepository.save(toHarvestVaultData(harvestVaultInfo)); + } + + public List getAllVaultInfo() { + return harvestVaultDataRepository.findAll(); + } + + private HarvestVaultData toHarvestVaultData(HarvestVaultItemInfo harvestVaultInfo) { + return HarvestVaultData.builder() + .id(harvestVaultInfo.getId()) + .vaultAddress(harvestVaultInfo.getVaultAddress()) + .displayName(harvestVaultInfo.getDisplayName()) + .rewardPool(harvestVaultInfo.getRewardPool()) + .apy(harvestVaultInfo.getEstimatedApy()) + .totalSupply(harvestVaultInfo.getTotalSupply()) + .tvl(harvestVaultInfo.getTotalValueLocked()) + .network(CHAIN_BY_NETWORK.get(harvestVaultInfo.getChain())) + .build(); + } +} diff --git a/src/main/java/pro/belbix/ethparser/service/ProfitService.java b/src/main/java/pro/belbix/ethparser/service/ProfitService.java new file mode 100644 index 00000000..e93936ec --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/service/ProfitService.java @@ -0,0 +1,276 @@ +package pro.belbix.ethparser.service; + +import static pro.belbix.ethparser.utils.CommonUtils.parseLong; +import static pro.belbix.ethparser.web3.abi.FunctionsNames.BALANCE_OF; +import static pro.belbix.ethparser.web3.abi.FunctionsNames.GET_PRICE_PER_FULL_SHARE; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; +import pro.belbix.ethparser.dto.v0.HarvestDTO; +import pro.belbix.ethparser.entity.contracts.ContractEntity; +import pro.belbix.ethparser.entity.profit.CovalenthqVaultTransaction; +import pro.belbix.ethparser.error.exceptions.CanNotCalculateProfitException; +import pro.belbix.ethparser.model.ProfitListResult; +import pro.belbix.ethparser.model.ProfitListResult.ProfitListResultItem; +import pro.belbix.ethparser.repositories.covalenthq.CovalenthqVaultTransactionRepository; +import pro.belbix.ethparser.repositories.v0.HarvestRepository; +import pro.belbix.ethparser.web3.EthBlockService; +import pro.belbix.ethparser.web3.Web3Functions; +import pro.belbix.ethparser.web3.abi.FunctionsUtils; +import pro.belbix.ethparser.web3.contracts.db.ContractDbService; + +@Service +@Log4j2 +@RequiredArgsConstructor +public class ProfitService { + + private final static BigInteger DEFAULT_POW = new BigInteger("10"); + private final static String WITHDRAW_NAME = "Withdraw"; + + private final HarvestRepository harvestRepository; + private final ContractDbService contractDbService; + private final EthBlockService ethBlockService; + private final FunctionsUtils functionsUtils; + private final CovalenthqVaultTransactionRepository covalenthqVaultTransactionRepository; + private final Web3Functions web3Functions; + + + public ProfitListResult calculateProfit(String address, String network) { + var transactions = covalenthqVaultTransactionRepository.findAllByOwnerAndNetwork(address, network); + var contracts = contractDbService.findAllVaultsByNetwork(network); + + return ProfitListResult.builder() + .totalProfit(calculateTxProfit(transactions)) + .items(calculateProfitByVaults(transactions, contracts)) + .build(); + } + + public BigDecimal calculateProfit(String address, String network, String vault, Long blockFrom, Long blockTo) { + try { + if (blockTo == 0) { + blockTo = web3Functions.fetchCurrentBlock(network).longValue(); + } + var transactions = covalenthqVaultTransactionRepository.findAllByOwnerAddressAndNetwork(address, network, vault, blockFrom, blockTo); + + return calculateTxProfit(transactions); + } catch (CanNotCalculateProfitException e) { + throw e; + } catch (Exception e) { + log.error("Error during calculate profit - ", e); + throw new CanNotCalculateProfitException(); + } + } + + public BigDecimal calculateVaultProfit(String address, String network, long blockFrom, long blockTo) { + try { + var transactions = + covalenthqVaultTransactionRepository.findAllByContractAddressAndBlockBetween(address, network, blockFrom, blockTo); + + return calculateTxProfit(transactions); + } catch (CanNotCalculateProfitException e) { + throw e; + } catch (Exception e) { + log.error("Error during calculate profit - ", e); + throw new CanNotCalculateProfitException(); + } + } + + public Double calculationProfitForPeriod(String address, String start, String end) { + + List harvestDTOS = harvestRepository.fetchAllByOwnerWithoutNetwork( + address.toLowerCase(), + parseLong(start, 0), + parseLong(end, Long.MAX_VALUE)); + + if (harvestDTOS == null || harvestDTOS.isEmpty()) { + return 0.0; + } + + return calculationTotalYield(harvestDTOS.stream() + .collect(Collectors.groupingBy(HarvestDTO::getVault, Collectors.toList())), + parseLong(start, 0), parseLong(end, Long.MAX_VALUE)); + } + + private Double calculationTotalYield(Map> maps, + Long start, Long end) { + + return maps.values().stream().map(e -> calcYieldByVault(e, start, end)) + .reduce(0.0, Double::sum); + } + + + private Double calcYieldByVault(List v, Long start, Long end) { + long startBlockNumber = (v.get(0).getBlock() * start) / v.get(0).getBlockDate(); + long endBlockNumber = getEndBlockNumber(v, end); + + String vaultAddress = v.get(0).getVaultAddress(); + String network = v.get(0).getNetwork(); + String owner = v.get(0).getOwner(); + String vault = v.get(0).getVault(); + + String poolAddress = contractDbService.getPoolContractByVaultAddress( + vaultAddress, startBlockNumber, network) + .orElseThrow().getAddress(); + + return getYieldValue(owner, vault, vaultAddress, network, startBlockNumber, endBlockNumber, + poolAddress, start, end); + } + + private double getYieldValue(String owner, String vault, String vaultAddress, String network, + long startBlockNumber, Long endBlockNumber, String poolAddress, Long start, Long end) { + + double underlyingBalanceStart = getUnderlyingBalance(owner, startBlockNumber, vaultAddress, + network, poolAddress); + + double underlyingBalanceEnd = getUnderlyingBalance(owner, endBlockNumber, vaultAddress, + network, poolAddress); + + double totalDifferenceDepositAndWithdrawUnderlyingStart = + harvestRepository.fetchTotalDifferenceDepositAndWithdraw( + vault, owner, start, network); + + double totalDifferenceDepositAndWithdrawUnderlyingEnd = + harvestRepository.fetchTotalDifferenceDepositAndWithdraw( + vault, owner, end, network); + + double yieldEnd = underlyingBalanceEnd + totalDifferenceDepositAndWithdrawUnderlyingEnd; + double yieldStart = underlyingBalanceStart + totalDifferenceDepositAndWithdrawUnderlyingStart; + + return yieldEnd - yieldStart; + } + + private double getUnderlyingBalance(String owner, long block, + String vaultAddress, String network, String poolAddress) { + return functionsUtils. + parseAmount(getCoinBalance(block, owner, vaultAddress, network) + .add(getCoinBalance(block, owner, poolAddress, network)), + vaultAddress, network) + * pricePerFullShare(vaultAddress, block, network); + } + + private double pricePerFullShare(String vaultAddress, Long block, String network) { + var sharePriceInt = functionsUtils.callIntByName( + GET_PRICE_PER_FULL_SHARE, vaultAddress, block, network) + .orElse(BigInteger.ZERO); + + if (BigInteger.ONE.equals(sharePriceInt)) { + return 0.0; + } + return functionsUtils + .parseAmount(sharePriceInt, vaultAddress, network); + + } + + private BigInteger getCoinBalance(long block, String owner, String address, String network) { + return functionsUtils.callIntByNameWithAddressArg(BALANCE_OF, owner, address, block, + network) + .orElseThrow(() -> new IllegalStateException("Error get amount from " + address)); + } + + public Double calculationProfitByVaultForPeriod(String address, String network, String start, + String end) { + List harvestDTOS = harvestRepository.fetchAllByVaultAddressAndNetwork( + address.toLowerCase(), + parseLong(start, 0), + parseLong(end, Long.MAX_VALUE), + network); + + if (harvestDTOS == null || harvestDTOS.isEmpty()) { + return 0.0; + } + + return calculationTotalYield(harvestDTOS.stream() + .collect(Collectors.groupingBy(HarvestDTO::getVault, Collectors.toList())), + parseLong(start, 0), parseLong(end, Long.MAX_VALUE)); + } + + private long getEndBlockNumber(List v, Long end) { + if (end == Long.MAX_VALUE) { + return ethBlockService.getLastBlock(v.get(0).getNetwork()); + } + + return (v.get(v.size() - 1).getBlock() * end) / v.get(v.size() - 1).getBlockDate(); + } + + private BigDecimal calculateTxProfit(List transactions) { + BigDecimal totalProfit = BigDecimal.ZERO; + + for (CovalenthqVaultTransaction i: transactions) { + if (i.getContractDecimal() == 0 || i.getSharePrice().equals(BigInteger.ZERO) || i.getTokenPrice() == 0) { + log.error("Can not calculate profit, incorrect transaction : {}", i); + throw new CanNotCalculateProfitException(); + } + var decimal = new BigDecimal(DEFAULT_POW.pow(i.getContractDecimal()).toString()); + var value = i.getValue() + .divide(decimal) + .multiply( + new BigDecimal(i.getSharePrice().toString()).divide(decimal) + ) + .multiply(BigDecimal.valueOf(i.getTokenPrice())); + + if (i.getType().equals(WITHDRAW_NAME)) { + totalProfit = totalProfit.add(value); + } else { + totalProfit = totalProfit.subtract(value); + } + } + return totalProfit; + } + + private List calculateProfitByVaults(List transactions, List contracts) { + return joinAnyVersionVaults( + contracts.stream() + .map(i -> { + var txByContract = transactions.stream() + .filter(tx -> tx.getContractAddress().equalsIgnoreCase(i.getAddress())) + .collect(Collectors.toList()); + + return ProfitListResultItem.builder() + .contractAddress(i.getAddress()) + .name(i.getName()) + .profit(calculateTxProfit(txByContract)) + .build(); + }) + .filter(i -> i.getProfit().compareTo(BigDecimal.ZERO) != 0) + .collect(Collectors.toList()) + ); + } + + private List joinAnyVersionVaults(List result) { + result = result.stream() + .sorted((i1, i2) -> i1.getName().compareToIgnoreCase(i2.getName())) + .collect(Collectors.toList()); + + var checkedResult = new ArrayList(); + var returnResult = new ArrayList(); + for (ProfitListResultItem item : result) { + if (checkedResult.stream().anyMatch(i -> item.equals(i))) { + continue; + } + + var values = result.stream() + .filter(i -> i.getName().startsWith(item.getName())) + .collect(Collectors.toList()); + + checkedResult.addAll(values); + returnResult.add(ProfitListResultItem.builder() + .profit( + values.stream() + .map(i -> i.getProfit()) + .reduce(BigDecimal.ZERO, BigDecimal::add) + ) + .name(item.getName()) + .contractAddress(item.getContractAddress()) + .build()); + } + + return returnResult; + } +} diff --git a/src/main/java/pro/belbix/ethparser/service/SharePriceService.java b/src/main/java/pro/belbix/ethparser/service/SharePriceService.java new file mode 100644 index 00000000..08db3d2e --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/service/SharePriceService.java @@ -0,0 +1,52 @@ +package pro.belbix.ethparser.service; + +import static pro.belbix.ethparser.web3.abi.FunctionsNames.GET_PRICE_PER_FULL_SHARE; + +import java.math.BigInteger; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import pro.belbix.ethparser.entity.profit.SharePrice; +import pro.belbix.ethparser.repositories.SharePriceRepository; +import pro.belbix.ethparser.utils.ProfitUtils; +import pro.belbix.ethparser.web3.abi.FunctionsUtils; +import pro.belbix.ethparser.web3.contracts.ContractConstantsV2; + +@Service +@RequiredArgsConstructor +@Slf4j +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class SharePriceService { + SharePriceRepository sharePriceRepository; + FunctionsUtils functionsUtils; + + @Cacheable("share_price") + public BigInteger getSharePrice(String vaultAddress, long block, String network) { + var id = ProfitUtils.toId(vaultAddress, String.valueOf(block), network); + var sharePrice = sharePriceRepository.findById(id); + + if (sharePrice.isPresent()) { + return sharePrice.get().getValue(); + } + + if (ContractConstantsV2.EXCLUDE_ADDRESSES_FOR_PRICE_SHARE_BY_NETWORK.get(network).contains(vaultAddress.toLowerCase())) { + sharePriceRepository.save(new SharePrice(id, BigInteger.ONE)); + return BigInteger.ONE; + } + + var sharePriceInt = functionsUtils.callIntByName(GET_PRICE_PER_FULL_SHARE, vaultAddress, block, network) + .orElse(BigInteger.ZERO); + + if (sharePriceInt.compareTo(BigInteger.ZERO) == 0) { + log.info("Share price is zero {}", id); + sharePriceInt = BigInteger.ONE; + } + sharePriceRepository.save(new SharePrice(id, sharePriceInt)); + + return sharePriceInt; + } + +} diff --git a/src/main/java/pro/belbix/ethparser/service/TokenPriceService.java b/src/main/java/pro/belbix/ethparser/service/TokenPriceService.java new file mode 100644 index 00000000..7d2db1a3 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/service/TokenPriceService.java @@ -0,0 +1,93 @@ +package pro.belbix.ethparser.service; + +import static pro.belbix.ethparser.web3.abi.FunctionsNames.NAME; +import static pro.belbix.ethparser.web3.abi.FunctionsNames.UNDERLYING; + +import java.math.BigDecimal; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import pro.belbix.ethparser.entity.profit.TokenPrice; +import pro.belbix.ethparser.error.exceptions.CanNotFetchPriceException; +import pro.belbix.ethparser.repositories.TokenPriceRepository; +import pro.belbix.ethparser.utils.ProfitUtils; +import pro.belbix.ethparser.web3.abi.FunctionsUtils; +import pro.belbix.ethparser.web3.contracts.ContractUtils; +import pro.belbix.ethparser.web3.contracts.UniPairType; +import pro.belbix.ethparser.web3.contracts.UniswapV3Pools; +import pro.belbix.ethparser.web3.prices.PriceProvider; + +@Service +@RequiredArgsConstructor +@Slf4j +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class TokenPriceService { + private final static Double DEFAULT_RETURN_VALUE = 0.0; + + TokenPriceRepository tokenPriceRepository; + PriceProvider priceProvider; + FunctionsUtils functionsUtils; + + + @Cacheable("token_price") + public Double getTokenPrice(String vaultAddress, BigDecimal amount, long block, String network) { + try { + + var id = ProfitUtils.toId(vaultAddress, String.valueOf(block), network); + var tokenPrice = tokenPriceRepository.findById(id); + var price = DEFAULT_RETURN_VALUE; + + if (tokenPrice.isPresent()) { + return tokenPrice.get().getValue(); + } + + if (ContractUtils.isPsAddress(vaultAddress, network)) { + return priceProvider.getPriceForCoin(ContractUtils.getFarmAddress(network), block, network); + } + + // UniSwapV3 + if (UniswapV3Pools.VAULTS_TO_POOLS.get(network).containsKey(vaultAddress.toLowerCase())) { + price = priceProvider.getUniswapV3Price(vaultAddress, block, network); + tokenPriceRepository.save(new TokenPrice(id, price)); + return price; + } + + var underlyingAddress = functionsUtils.callAddressByName(UNDERLYING, vaultAddress, block, network) + .orElseThrow(() -> { + log.error("Can not fetch underlying address for {} {}", vaultAddress, network); + throw new IllegalStateException(); + }); + + var name = functionsUtils.callStrByName(NAME, underlyingAddress, block, network) + .orElse(StringUtils.EMPTY); + + if (UniPairType.isLpUniPair(name)) { + // priceProvider.getLpTokenUsdPrice return price * amount + price = priceProvider.getLpTokenUsdPrice(underlyingAddress, amount.doubleValue(), block, network); + if (!amount.equals(BigDecimal.ZERO)) { + price = price / amount.doubleValue(); + } + } else if (UniPairType.isBalancer(name)) { + price = priceProvider.getBalancerPrice(underlyingAddress, block, network); + log.info("Fetched price balancer: {} {} = {}", underlyingAddress, network, price); + // TODO Curve can not calculate price 0x29780c39164ebbd62e9ddde50c151810070140f2 + } else if (UniPairType.isCurve(name)) { + price = priceProvider.getCurvePrice(underlyingAddress, block, network); + log.info("Fetched price curve: {} {} = {}", underlyingAddress, network, price); + } else { + price = priceProvider.getPriceForCoin(underlyingAddress, block, network); + } + + + tokenPriceRepository.save(new TokenPrice(id, price)); + return price; + } catch (Exception e) { + log.error("Can not get token price - {}", vaultAddress, e); + throw new CanNotFetchPriceException(); + } + } +} diff --git a/src/main/java/pro/belbix/ethparser/service/external/CovalenthqService.java b/src/main/java/pro/belbix/ethparser/service/external/CovalenthqService.java new file mode 100644 index 00000000..6d1d9b1c --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/service/external/CovalenthqService.java @@ -0,0 +1,131 @@ +package pro.belbix.ethparser.service.external; + +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; + +import java.util.Map; +import java.util.Optional; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Retryable; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import pro.belbix.ethparser.model.CovalenthqTransactionHistory; +import pro.belbix.ethparser.model.CovalenthqTransactionHistory.CovalenthqTransactionHistoryItems.CovalenthqTransactionHistoryItem; +import pro.belbix.ethparser.properties.ExternalProperties; +import pro.belbix.ethparser.utils.UrlUtils.Covalenthq.Params; +import pro.belbix.ethparser.utils.UrlUtils.Covalenthq.Url; + +@Service +@RequiredArgsConstructor +@Slf4j +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class CovalenthqService { + + private final static String DEFAULT_CHAIN = "1"; + private final static int TRANSFER_LIMIT = 3; + private final static Map CHAIN_BY_NETWORK = Map.of( + MATIC_NETWORK, "137", + ETH_NETWORK, "1", + BSC_NETWORK, "56" + ); + + ExternalProperties externalProperties; + RestTemplate restTemplate; + + + public long getCreatedBlockByLastTransaction(String address, String network) { + try { + var page = 0; + var result = getTransactionByAddress(address, network, true, false, page, TRANSFER_LIMIT); + if (result == null || result.getData() == null || result.getData().getItems() == null) { + return 0; + } + log.info("Got tx from covalenthq: {}", result.getData().getItems().size()); + + var createdTx = findCovalenthqTransactionItem(result); + + while (createdTx == null) { + Thread.sleep(100); + page++; + result = getTransactionByAddress(address, network, true, true, page, TRANSFER_LIMIT); + if (result == null || result.getData() == null || result.getData().getItems() == null + || result.getData().getItems().isEmpty()) { + return 0; + } + log.info("Got tx from covalenthq: {}", result.getData().getItems().size()); + createdTx = findCovalenthqTransactionItem(result); + } + + return createdTx.getBlockHeight(); + } catch (Exception e) { + log.error("Error during call getCreatedBlockByLastTransaction", e); + return 0; + } + } + + public CovalenthqTransactionHistory getTransactionByAddress(String address, String network, + boolean isSortAsc, boolean isFullLogs, int page, int limit) { + var params = String.join(StringUtils.EMPTY, + Params.QUOTE_CURRENCY, + Params.FORMAT, + String.format(Params.BLOCK_SIGNED_AT_ASC, isSortAsc), + String.format(Params.NO_LOGS, isFullLogs), + String.format(Params.KEY, externalProperties.getCovalenthq().getKey()), + String.format(Params.PAGE_NUMBER, page), + String.format(Params.PAGE_SIZE, limit) + ); + + var url = String.format(Url.TRANSACTION_HISTORY, externalProperties.getCovalenthq().getUrl(), convertToNetwork(network), address, params); + + return getTransactionByAddress(url); + } + + public CovalenthqTransactionHistory getTransactionByAddress(String address, String network, + int page, int limit, long startingBlock, long endingBlock) { + var params = String.join(StringUtils.EMPTY, + Params.QUOTE_CURRENCY, + Params.FORMAT, + String.format(Params.BLOCK_SIGNED_AT_ASC, true), + String.format(Params.NO_LOGS, false), + String.format(Params.KEY, externalProperties.getCovalenthq().getKey()), + String.format(Params.PAGE_NUMBER, page), + String.format(Params.PAGE_SIZE, limit), + String.format(Params.START_BLOCK, startingBlock), + String.format(Params.END_BLOCK, endingBlock) + ); + + var url = String.format(Url.TRANSACTION_HISTORY, externalProperties.getCovalenthq().getUrl(), convertToNetwork(network), address, params); + return getTransactionByAddress(url); + } + + // if 507 response code try increase sleep time + // if 524 response code try to resize page count + @Retryable(value = Exception.class, maxAttempts = 4, backoff = @Backoff(delay = 1000)) + public CovalenthqTransactionHistory getTransactionByAddress(String url) { + try { + return restTemplate.getForObject(url, CovalenthqTransactionHistory.class); + } catch (Exception e) { + log.error("Error during call {}", url, e); + throw new IllegalStateException(e); + } + } + + private String convertToNetwork(String network) { + return Optional.ofNullable(CHAIN_BY_NETWORK.get(network)).orElse(DEFAULT_CHAIN); + } + + private CovalenthqTransactionHistoryItem findCovalenthqTransactionItem( + CovalenthqTransactionHistory result) { + return result.getData().getItems().stream() + .filter(items -> items.getLogs() != null) + .findFirst() + .orElse(null); + } +} + diff --git a/src/main/java/pro/belbix/ethparser/service/external/HarvestService.java b/src/main/java/pro/belbix/ethparser/service/external/HarvestService.java new file mode 100644 index 00000000..9976546c --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/service/external/HarvestService.java @@ -0,0 +1,34 @@ +package pro.belbix.ethparser.service.external; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import pro.belbix.ethparser.model.HarvestPoolInfo; +import pro.belbix.ethparser.model.HarvestVaultInfo; +import pro.belbix.ethparser.properties.ExternalProperties; +import pro.belbix.ethparser.utils.UrlUtils.HarvestUrl; + +@Service +@RequiredArgsConstructor +@Slf4j +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class HarvestService { + ExternalProperties externalProperties; + RestTemplate restTemplate; + + public HarvestPoolInfo getPools() { + var url = String.format(HarvestUrl.POOLS, externalProperties.getHarvest().getUrl(), externalProperties.getHarvest().getKey()); + log.info("Starting get pools from harvest {} ", url); + return restTemplate.getForObject(url, HarvestPoolInfo.class); + } + + public HarvestVaultInfo getVaults() { + var url = String.format(HarvestUrl.VAULTS, externalProperties.getHarvest().getUrl(), externalProperties.getHarvest().getKey()); + log.info("Starting get vaults from harvest {} ", url); + var result = restTemplate.getForEntity(url, HarvestVaultInfo.class); + return result.getBody(); + } +} diff --git a/src/main/java/pro/belbix/ethparser/service/task/CovalenthqTransactionTask.java b/src/main/java/pro/belbix/ethparser/service/task/CovalenthqTransactionTask.java new file mode 100644 index 00000000..bb100950 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/service/task/CovalenthqTransactionTask.java @@ -0,0 +1,169 @@ +package pro.belbix.ethparser.service.task; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import pro.belbix.ethparser.entity.contracts.VaultEntity; +import pro.belbix.ethparser.entity.profit.CovalenthqVaultTransaction; +import pro.belbix.ethparser.error.exceptions.CanNotFetchPriceException; +import pro.belbix.ethparser.model.CovalenthqTransactionHistory.CovalenthqTransactionHistoryItems.CovalenthqTransactionHistoryItem; +import pro.belbix.ethparser.repositories.covalenthq.CovalenthqVaultTransactionRepository; +import pro.belbix.ethparser.repositories.eth.VaultRepository; +import pro.belbix.ethparser.service.SharePriceService; +import pro.belbix.ethparser.service.TokenPriceService; +import pro.belbix.ethparser.service.external.CovalenthqService; +import pro.belbix.ethparser.utils.profit.ParseLogUtils; +import pro.belbix.ethparser.web3.Web3Functions; + +@Service +@RequiredArgsConstructor +@Slf4j +public class CovalenthqTransactionTask { + // max block range in covalenthq + private static final int MAX_BLOCK_RANGE = 1000000; + private static final int MINUS_BLOCK = 10; + private static final int PAGINATION_SIZE = 10000; + private static final int PAGINATION_SIZE_FOR_A_LOT_OF_DATA = 100; + @Value("${task.transaction.enable}") + private Boolean enable; + + @Value("${task.transaction.max-thread-size}") + private Integer maxThreadSize; + + private final VaultRepository vaultRepository; + private final CovalenthqService covalenthqService; + private final CovalenthqVaultTransactionRepository covalenthqVaultTransactionRepository; + private final Web3Functions web3Functions; + private final SharePriceService sharePriceService; + private final TokenPriceService tokenPriceService; + private final ParseLogUtils parseLogUtils; + + @Scheduled(fixedRateString = "${task.transaction.fixedRate}") + public void start() { + if (enable == null || !enable) { + log.info("Disable CovalenthqTransactionTask"); + return; + } + log.info("Begin parse vault tx"); + var executor = Executors.newFixedThreadPool(maxThreadSize); + + vaultRepository.findAll() + .stream() + .map(i -> CompletableFuture.runAsync(() -> getVaultTransaction(i), executor)) + .collect(Collectors.toList()); + } + + private void getVaultTransaction(VaultEntity vault) { + try { + log.info("Run getVaultTransaction for {}", vault); + var contract = vault.getContract(); + var lastTx = covalenthqVaultTransactionRepository.findAllByNetworkAndContractAddress(contract.getNetwork(), contract.getAddress(), + PageRequest.of(0, 1, Sort.by("block").ascending())); + log.info("Got response: {}", lastTx); + var startingBlock = 0L; + + if (!lastTx.isEmpty()) { + startingBlock = Math.max(lastTx.get(0).getBlock() - MINUS_BLOCK, 0); + } + + var currentBlock = web3Functions.fetchCurrentBlock(contract.getNetwork()); + var endingBlock = startingBlock + MAX_BLOCK_RANGE; + + while (currentBlock.longValue() >= endingBlock) { + // TODO Maybe need to add one more block, because can find the same tx + startingBlock = endingBlock; + endingBlock += MAX_BLOCK_RANGE; + fetchTransactions(contract.getAddress(), contract.getNetwork(), startingBlock, endingBlock); + } + + } catch (Exception e) { + log.error("Get error getVaultTransaction", e); + } + } + + private void fetchTransactions(String address, String network, long startingBlock, long endingBlock) { + fetchTransactions(address, network, startingBlock, endingBlock, PAGINATION_SIZE, false); + } + + private void fetchTransactions(String address, String network, long startingBlock, long endingBlock, int limit, boolean isAfterException) { + try { + var page = 0; + var transaction = covalenthqService.getTransactionByAddress(address, network, page, limit, startingBlock, endingBlock); + var result = new LinkedList(transaction.getData().getItems()); + var hasMore = transaction.getData().getPagination().isHasMore(); + log.info("Covalnethq result: {}", result.size()); + while (hasMore) { + page++; + transaction = covalenthqService.getTransactionByAddress(address, network, page, limit, startingBlock, endingBlock); + hasMore = transaction.getData().getPagination().isHasMore(); + result.addAll(transaction.getData().getItems()); + log.info("Covalnethq result: {}", result.size()); + } + + var transactions = result.stream() + .map(CovalenthqTransactionHistoryItem::getLogs) + .flatMap(Collection::stream) + .filter(i -> parseLogUtils.isCorrectLog(i, address)) + .map(log -> parseLogUtils.toCovalenthqVaultTransactionFromTransfer(log, network, address)) + .flatMap(Collection::stream) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + var transactionWithoutDuplicate = new HashSet<>(transactions); + var transactionInDb = covalenthqVaultTransactionRepository.findAllByTransactionHashIn( + transactionWithoutDuplicate.stream() + .map(CovalenthqVaultTransaction::getTransactionHash) + .collect(Collectors.toList())); + transactions = transactionWithoutDuplicate.stream() + .filter(i1 -> + transactionInDb.stream().noneMatch(i2 -> + i1.getNetwork().equals(i2.getNetwork()) + && i1.getTransactionHash().equals(i2.getTransactionHash()) + && i1.getContractAddress().equals(i2.getContractAddress()) + && i1.getOwnerAddress().equals(i2.getOwnerAddress()) + ) + ) + .map(this::fillAdditionalParams) + .collect(Collectors.toList()); + + log.info("Save list covalenthq tx count - {}", transactions.size()); + covalenthqVaultTransactionRepository.saveAll(transactions); + + } catch (CanNotFetchPriceException e) { + log.error("Can not fetch price {} {}", address, network); + throw e; + } catch (IllegalStateException e) { + log.error("Get a lot of data {} {} on block {} - {}", address, network, startingBlock, endingBlock); + if (isAfterException) { + throw e; + } + fetchTransactions(address, network, startingBlock, endingBlock, PAGINATION_SIZE_FOR_A_LOT_OF_DATA, true); + } + } + + private CovalenthqVaultTransaction fillAdditionalParams(CovalenthqVaultTransaction covalenthqVaultTransaction) { + var vaultAddress = covalenthqVaultTransaction.getContractAddress(); + var block = covalenthqVaultTransaction.getBlock(); + var network = covalenthqVaultTransaction.getNetwork(); + covalenthqVaultTransaction.setSharePrice( + sharePriceService.getSharePrice(vaultAddress, block, network) + ); + + covalenthqVaultTransaction.setTokenPrice( + tokenPriceService.getTokenPrice(vaultAddress, covalenthqVaultTransaction.getValue(), block, network) + ); + + return covalenthqVaultTransaction; + } +} diff --git a/src/main/java/pro/belbix/ethparser/service/task/HarvestPoolInfoTask.java b/src/main/java/pro/belbix/ethparser/service/task/HarvestPoolInfoTask.java new file mode 100644 index 00000000..eeda7709 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/service/task/HarvestPoolInfoTask.java @@ -0,0 +1,128 @@ +package pro.belbix.ethparser.service.task; + +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import pro.belbix.ethparser.entity.contracts.ContractEntity; +import pro.belbix.ethparser.entity.contracts.PoolEntity; +import pro.belbix.ethparser.model.HarvestPoolInfo.HarvestPoolItemInfo; +import pro.belbix.ethparser.repositories.eth.ContractRepository; +import pro.belbix.ethparser.repositories.eth.PoolRepository; +import pro.belbix.ethparser.service.external.CovalenthqService; +import pro.belbix.ethparser.service.external.HarvestService; +import pro.belbix.ethparser.web3.EthBlockService; +import pro.belbix.ethparser.web3.abi.FunctionsNames; +import pro.belbix.ethparser.web3.abi.FunctionsUtils; +import pro.belbix.ethparser.web3.contracts.ContractLoader; +import pro.belbix.ethparser.web3.contracts.ContractType; + +@Service +@RequiredArgsConstructor +@Slf4j +public class HarvestPoolInfoTask { + private static final Long INCREASE_BLOCK_WEIGHT = 1000L; + + @Value("${task.pool.enable}") + private Boolean enable; + private final HarvestService harvestService; + private final ContractLoader contractLoader; + private final ContractRepository contractRepository; + private final PoolRepository poolRepository; + private final EthBlockService ethBlockService; + private final FunctionsUtils functionsUtils; + private final CovalenthqService covalenthqService; + + + @Scheduled(fixedRateString = "${task.pool.fixedRate}") + public void start() { + try { + if (enable == null || !enable) { + log.info("HarvestPoolInfoTask disabled"); + return; + } + var response = harvestService.getPools(); + + List>> poolFutures = List.of( + CompletableFuture.supplyAsync(() -> doTaskByAddressAndNetwork(response.getEthereumNetwork(), ETH_NETWORK)), + CompletableFuture.supplyAsync(() -> doTaskByAddressAndNetwork(response.getMaticNetwork(), MATIC_NETWORK)), + CompletableFuture.supplyAsync(() -> doTaskByAddressAndNetwork(response.getBscNetwork(), BSC_NETWORK)) + ); + + var pools = CompletableFuture.allOf(poolFutures.toArray(new CompletableFuture[0])) + .thenApply(i -> + poolFutures.stream().map(CompletableFuture::join) + .collect(Collectors.toList()) + ) + .get() + .stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()); + + log.info("List of pools: {}", pools); + poolRepository.saveAll(pools); + + } catch (Exception e) { + log.error("Error during getting info from harvest", e); + } + } + + private List doTaskByAddressAndNetwork(List items, String network) { + log.info("Begin find pool and insert in network: {}", network); + var existPools = contractRepository.findAllByNetworkAndInAddressAndType(network, + items.stream().map(i -> i.getContractAddress().toLowerCase()).collect(Collectors.toList()), ContractType.POOL.getId()); + + var notSavedPools = items.stream() + .filter(i -> existPools.stream().filter(c -> c.getAddress().equalsIgnoreCase(i.getContractAddress())).findFirst().isEmpty()) + .collect(Collectors.toList()); + log.info("Need insert those {}", notSavedPools); + return notSavedPools.stream() + .map(i -> { + try { + log.info("Try to create pool {}", i); + var pool = new PoolEntity(); + var contract = new ContractEntity(); + var block = covalenthqService.getCreatedBlockByLastTransaction(i.getContractAddress(), network); + var createdBlockDate = ethBlockService.getTimestampSecForBlock(block, network); + + // in some cases created block is incorrect + var isCorrectBlock = true; + var name = functionsUtils.callStrByName(FunctionsNames.NAME, i.getContractAddress(), block, network).orElse(""); + if (name.isEmpty()) { + isCorrectBlock = false; + name = functionsUtils.callStrByName(FunctionsNames.NAME, i.getContractAddress(), null, network).orElse(""); + } + contract.setAddress(i.getContractAddress()); + contract.setCreated(block); + contract.setCreatedDate(createdBlockDate); + contract.setNetwork(network); + contract.setName(name); + contract.setType(ContractType.POOL.getId()); + contract = contractRepository.save(contract); + pool.setContract(contract); + if (isCorrectBlock) { + contractLoader.enrichPool(pool, block, network); + } else { + contractLoader.enrichPool(pool, block + INCREASE_BLOCK_WEIGHT, network); + } + log.info("Pool: {}", pool); + return pool; + } catch (Exception e) { + log.error("Can not create pool, {}", i, e); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/pro/belbix/ethparser/service/task/HarvestUpdateInfoTask.java b/src/main/java/pro/belbix/ethparser/service/task/HarvestUpdateInfoTask.java new file mode 100644 index 00000000..536bffd5 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/service/task/HarvestUpdateInfoTask.java @@ -0,0 +1,45 @@ +package pro.belbix.ethparser.service.task; + +import java.util.Collection; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import pro.belbix.ethparser.service.HarvestVaultDataService; +import pro.belbix.ethparser.service.external.HarvestService; + +@Service +@RequiredArgsConstructor +@Slf4j +public class HarvestUpdateInfoTask { + @Value("${task.info.enable}") + private Boolean enable; + private final HarvestService harvestService; + private final HarvestVaultDataService harvestVaultDataService; + + @Scheduled(fixedRateString = "${task.info.fixedRate}") + public void start() { + if (enable == null || !enable) { + log.info("HarvestUpdateInfoTask disabled"); + return; + } + + log.info("Start save vaults info from harvest API"); + var response = harvestService.getVaults(); + + var vaults = Stream.of( + response.getEthereumNetwork().values(), + response.getBscNetwork().values(), + response.getMaticNetwork().values() + ) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + + + harvestVaultDataService.saveAll(vaults); + log.info("Success saved vaults info from harvest API"); + } +} diff --git a/src/main/java/pro/belbix/ethparser/service/task/HarvestVaultInfoTask.java b/src/main/java/pro/belbix/ethparser/service/task/HarvestVaultInfoTask.java new file mode 100644 index 00000000..3895be38 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/service/task/HarvestVaultInfoTask.java @@ -0,0 +1,139 @@ +package pro.belbix.ethparser.service.task; + +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import pro.belbix.ethparser.entity.contracts.ContractEntity; +import pro.belbix.ethparser.entity.contracts.VaultEntity; +import pro.belbix.ethparser.model.HarvestVaultInfo.HarvestVaultItemInfo; +import pro.belbix.ethparser.repositories.eth.ContractRepository; +import pro.belbix.ethparser.repositories.eth.VaultRepository; +import pro.belbix.ethparser.service.external.CovalenthqService; +import pro.belbix.ethparser.service.external.HarvestService; +import pro.belbix.ethparser.web3.EthBlockService; +import pro.belbix.ethparser.web3.abi.FunctionsNames; +import pro.belbix.ethparser.web3.abi.FunctionsUtils; +import pro.belbix.ethparser.web3.contracts.ContractLoader; +import pro.belbix.ethparser.web3.contracts.ContractType; + +@Service +@RequiredArgsConstructor +@Slf4j +public class HarvestVaultInfoTask { + private static final Long INCREASE_BLOCK_WEIGHT = 1000L; + + @Value("${task.vault.enable}") + private Boolean enable; + private final HarvestService harvestService; + private final ContractLoader contractLoader; + private final ContractRepository contractRepository; + private final VaultRepository vaultRepository; + private final EthBlockService ethBlockService; + private final FunctionsUtils functionsUtils; + private final CovalenthqService covalenthqService; + + + @Scheduled(fixedRateString = "${task.vault.fixedRate}") + public void start() { + try { + if (enable == null || !enable) { + log.info("HarvestPoolInfoTask disabled"); + return; + } + + var response = harvestService.getVaults(); + + List>> vaultFutures = List.of( + CompletableFuture.supplyAsync(() -> doTaskByAddressAndNetwork(response.getEthereumNetwork(), ETH_NETWORK)), + CompletableFuture.supplyAsync(() -> doTaskByAddressAndNetwork(response.getMaticNetwork(), MATIC_NETWORK)), + CompletableFuture.supplyAsync(() -> doTaskByAddressAndNetwork(response.getBscNetwork(), BSC_NETWORK)) + ); + + var vaults = CompletableFuture.allOf(vaultFutures.toArray(new CompletableFuture[0])) + .thenApply(i -> + vaultFutures.stream().map(CompletableFuture::join) + .collect(Collectors.toList()) + ) + .get() + .stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()); + + log.info("List of vaults: {}", vaults); + vaultRepository.saveAll(vaults); + + } catch (Exception e) { + log.error("Error during getting info from harvest", e); + } + } + + @Transactional + public List doTaskByAddressAndNetwork(Map items, String network) { + log.info("Begin find vault and insert in network: {}", network); + var existVaults = contractRepository.findAllByNetworkAndInAddressAndType(network, + items.values().stream().map(i -> i.getVaultAddress().toLowerCase()).collect(Collectors.toList()), ContractType.VAULT.getId()); + log.info("Exist vaults {}", existVaults); + var notSavedVaults = items.values().stream() + .filter(i -> existVaults.stream().filter(c -> c.getAddress().equalsIgnoreCase(i.getVaultAddress())).findFirst().isEmpty()) + .collect(Collectors.toList()); + log.info("Need insert those {}", notSavedVaults); + return notSavedVaults.stream() + .map(i -> { + try { + log.info("Try to create pool {}", i); + var vault = new VaultEntity(); + var contract = new ContractEntity(); + var block = covalenthqService.getCreatedBlockByLastTransaction(i.getVaultAddress(), network); + var createdBlockDate = ethBlockService.getTimestampSecForBlock(block, network); + // in some cases created block is incorrect + var isCorrectBlock = true; + var name = functionsUtils.callStrByName(FunctionsNames.NAME, i.getVaultAddress(), block, network).orElse(""); + if (name.isEmpty()) { + isCorrectBlock = false; + name = functionsUtils.callStrByName(FunctionsNames.NAME, i.getVaultAddress(), null, network).orElse(""); + } + var id = contractRepository.findMaxId() + 1; + log.info("Max contract id is {}", id); + contract.setId(id); + contract.setAddress(i.getVaultAddress()); + contract.setCreated(block); + contract.setCreatedDate(createdBlockDate); + contract.setNetwork(network); + contract.setName(name); + contract.setType(ContractType.VAULT.getId()); + contract = contractRepository.save(contract); + vault.setContract(contract); + + if (isCorrectBlock) { + contractLoader.enrichVault(vault, block, network); + } else { + contractLoader.enrichVaultWithLatestBlock(vault, block + INCREASE_BLOCK_WEIGHT, network); + } + + vault.setId(vaultRepository.findMaxId()); + log.info("Vault: {}", vault); + return vault; + + } catch (Exception e) { + log.error("Can not create pool, {}", i, e); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + +} diff --git a/src/main/java/pro/belbix/ethparser/service/task/UpdateUniPairContractTask.java b/src/main/java/pro/belbix/ethparser/service/task/UpdateUniPairContractTask.java new file mode 100644 index 00000000..0193a2a8 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/service/task/UpdateUniPairContractTask.java @@ -0,0 +1,42 @@ +package pro.belbix.ethparser.service.task; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import pro.belbix.ethparser.entity.contracts.ContractEntity; +import pro.belbix.ethparser.repositories.eth.ContractRepository; +import pro.belbix.ethparser.web3.contracts.ContractLoader; + +@Service +@RequiredArgsConstructor +@Slf4j +public class UpdateUniPairContractTask { + @Value("${task.uni-pair.enable}") + private Boolean enable; + private final ContractRepository contractRepository; + private final ContractLoader contractLoader; + + @Scheduled(fixedRateString = "${task.uni-pair.fixedRate}") + public void start() { + if (enable == null || !enable) { + log.info("UpdateUniPairContractTask disabled"); + return; + } + log.info("Start UpdateUniPairContractTask"); + contractRepository.findAllUniPairContractWithoutData() + .forEach(this::fetchUniPairContact); + } + + public void fetchUniPairContact(ContractEntity contract) { + try { + log.info("Begin fetch uniPairsToToken for {} {}", contract.getAddress(), contract.getNetwork()); + var token = contractLoader.loadToken(contract, contract.getNetwork(), contract.getCreated()); + var uniPairsToToken = contractLoader.linkUniPairsToToken(contract.getAddress(), contract.getCreated(), token, contract.getNetwork()); + log.info("Finish fetch uniPairsToToken - {}", uniPairsToToken); + } catch (Exception e) { + log.error("Can not fetch uniPair token: {}", contract); + } + } +} diff --git a/src/main/java/pro/belbix/ethparser/utils/MockUtils.java b/src/main/java/pro/belbix/ethparser/utils/MockUtils.java index 589babd8..6d56066a 100644 --- a/src/main/java/pro/belbix/ethparser/utils/MockUtils.java +++ b/src/main/java/pro/belbix/ethparser/utils/MockUtils.java @@ -6,7 +6,7 @@ import static pro.belbix.ethparser.model.tx.UniswapTx.REMOVE_LIQ; import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; import static pro.belbix.ethparser.web3.contracts.ContractConstants.BANCOR_CONVERSION_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.FARM_TOKEN; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.FARM_TOKEN; import java.math.BigInteger; import java.time.Instant; diff --git a/src/main/java/pro/belbix/ethparser/utils/ProfitUtils.java b/src/main/java/pro/belbix/ethparser/utils/ProfitUtils.java new file mode 100644 index 00000000..5f5cd647 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/utils/ProfitUtils.java @@ -0,0 +1,12 @@ +package pro.belbix.ethparser.utils; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public class ProfitUtils { + private static final String ID_DELIMITER = "_"; + + public static String toId(String vaultAddress, String block, String network) { + return String.join(ID_DELIMITER, vaultAddress, block, network); + } +} diff --git a/src/main/java/pro/belbix/ethparser/utils/UrlUtils.java b/src/main/java/pro/belbix/ethparser/utils/UrlUtils.java new file mode 100644 index 00000000..4858f194 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/utils/UrlUtils.java @@ -0,0 +1,27 @@ +package pro.belbix.ethparser.utils; + +public class UrlUtils { + + public interface Covalenthq { + interface Url { + String TRANSACTION_HISTORY = "%s%s/address/%s/transactions_v2/?%s"; + } + interface Params { + String QUOTE_CURRENCY = "quote-currency=USD&"; + String FORMAT = "format=JSON&"; + String BLOCK_SIGNED_AT_ASC = "block-signed-at-asc=%s&"; + String NO_LOGS = "no-logs=%s&"; + String KEY = "key=%s&"; + String PAGE_NUMBER = "page-number=%s&"; + String PAGE_SIZE = "page-size=%s&"; + String START_BLOCK = "starting-block=%s&"; + String END_BLOCK = "ending-block=%s&"; + } + + } + + public interface HarvestUrl { + String POOLS = "%spools?key=%s"; + String VAULTS = "%svaults?key=%s"; + } +} diff --git a/src/main/java/pro/belbix/ethparser/utils/profit/ParseLogUtils.java b/src/main/java/pro/belbix/ethparser/utils/profit/ParseLogUtils.java new file mode 100644 index 00000000..a23c9adc --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/utils/profit/ParseLogUtils.java @@ -0,0 +1,228 @@ +package pro.belbix.ethparser.utils.profit; + +import static pro.belbix.ethparser.entity.profit.CovalenthqVaultTransactionType.DEPOSIT; +import static pro.belbix.ethparser.entity.profit.CovalenthqVaultTransactionType.DEPOSIT_UNI; +import static pro.belbix.ethparser.entity.profit.CovalenthqVaultTransactionType.TRANSFER; +import static pro.belbix.ethparser.entity.profit.CovalenthqVaultTransactionType.WITHDRAW; +import static pro.belbix.ethparser.entity.profit.CovalenthqVaultTransactionType.WITHDRAW_UNI; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.web3.contracts.ContractConstants.ZERO_ADDRESS; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.experimental.FieldDefaults; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.web3j.abi.datatypes.Type; +import org.web3j.protocol.core.methods.response.Log; +import pro.belbix.ethparser.entity.profit.CovalenthqVaultTransaction; +import pro.belbix.ethparser.entity.profit.CovalenthqVaultTransactionType; +import pro.belbix.ethparser.error.exceptions.CanNotFetchPriceException; +import pro.belbix.ethparser.model.CovalenthqTransactionHistory.CovalenthqTransactionHistoryItems.CovalenthqTransactionHistoryItem.CovalenthqTransactionHistoryItemLog; +import pro.belbix.ethparser.web3.SimpleDecoder; +import pro.belbix.ethparser.web3.abi.FunctionsUtils; +import pro.belbix.ethparser.web3.contracts.DecodeExcludeConstants; + +@Component +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +@RequiredArgsConstructor +@Slf4j +public class ParseLogUtils { + private static final Map LOG_EVENTS_V1 = Map.of( + "0xf279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568", WITHDRAW_UNI, + "0x884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364", WITHDRAW, + "0x90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15", DEPOSIT_UNI, + "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c", DEPOSIT + ); + + private static final Map LOG_EVENTS_V2 = Map.of( + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", TRANSFER + ); + + FunctionsUtils functionsUtils; + SimpleDecoder simpleDecoder; + + + // filter deposit and withdraw + // filter by contractAddress + public boolean isCorrectLog(CovalenthqTransactionHistoryItemLog log, String contractAddress) { + return log.getTopics() != null + && !log.getTopics().isEmpty() + && log.getSenderAddress().equalsIgnoreCase(contractAddress) + && LOG_EVENTS_V2.get(log.getTopics().get(0)) != null; + } + + // only for event logs deposit and withdraw + public CovalenthqVaultTransaction toCovalenthqVaultTransactionFromDepositOrWithdraw(CovalenthqTransactionHistoryItemLog item, + String network, String contractAddress) { + try { + var transaction = new CovalenthqVaultTransaction(); + var covalenthqType = Optional.ofNullable(toCovalenthqVaultTransactionTypeFromDepositOrWithdraw(item)) + .orElseThrow(() -> { + log.error("Can not find log event name"); + throw new IllegalStateException(); + }); + var value = getParamValue(item, covalenthqType.value, contractAddress, network, covalenthqType.paramSize); + var decimal = item.getContractDecimal() == 0 + ? functionsUtils.getDecimal(contractAddress, network) + : item.getContractDecimal(); + + transaction.setNetwork(network); + transaction.setBlock(item.getBlockHeight()); + transaction.setTransactionHash(item.getTransactionHash()); + transaction.setContractDecimal(decimal); + transaction.setContractAddress(contractAddress); + transaction.setType(covalenthqType.type); + transaction.setOwnerAddress(getParamValue(item, covalenthqType.address, contractAddress, network, covalenthqType.paramSize)); + transaction.setValue( + StringUtils.isEmpty(value) ? BigDecimal.ZERO : new BigDecimal(value) + ); + transaction.setSignedAt(item.getSignedAt()); + + return transaction; + } catch (Exception e) { + log.error("Can not parse covalenthq log: {}", item, e); + throw new CanNotFetchPriceException(); + } + } + + // only for event logs transfer + public List toCovalenthqVaultTransactionFromTransfer(CovalenthqTransactionHistoryItemLog item, + String network, String contractAddress) { + try { + var covalenthqType = Optional.ofNullable(toCovalenthqVaultTransactionTypeFromTransfer(item)) + .orElseThrow(() -> { + log.error("Can not find log event name"); + throw new IllegalStateException(); + }); + var value = getParamValue(item, covalenthqType.value); + var fromAddress = getParamValue(item, covalenthqType.address); + var toAddress = getParamValue(item, covalenthqType.toAddress); + + var transactions = new ArrayList(); + + if (StringUtils.isNotEmpty(fromAddress) && !ZERO_ADDRESS.equalsIgnoreCase(fromAddress)) { + transactions.add(toCovalenthqVaultTransaction(item, contractAddress, network, WITHDRAW.type, fromAddress, value)); + } + + if (StringUtils.isNotEmpty(toAddress) && !ZERO_ADDRESS.equalsIgnoreCase(toAddress)) { + transactions.add(toCovalenthqVaultTransaction(item, contractAddress, network, DEPOSIT.type, toAddress, value)); + } + + return transactions; + } catch (Exception e) { + log.error("Can not parse covalenthq log: {}", item, e); + throw new CanNotFetchPriceException(); + } + } + + private CovalenthqVaultTransaction toCovalenthqVaultTransaction(CovalenthqTransactionHistoryItemLog item, String contractAddress, + String network, String type, String ownerAddress, String value) { + var transaction = new CovalenthqVaultTransaction(); + var decimal = item.getContractDecimal() == 0 + ? functionsUtils.getDecimal(contractAddress, network) + : item.getContractDecimal(); + + transaction.setNetwork(network); + transaction.setBlock(item.getBlockHeight()); + transaction.setTransactionHash(item.getTransactionHash()); + transaction.setContractDecimal(decimal); + transaction.setContractAddress(contractAddress); + transaction.setType(type); + transaction.setOwnerAddress(ownerAddress); + transaction.setValue( + StringUtils.isEmpty(value) ? BigDecimal.ZERO : new BigDecimal(value) + ); + transaction.setSignedAt(item.getSignedAt()); + + return transaction; + } + + private CovalenthqVaultTransactionType toCovalenthqVaultTransactionTypeFromDepositOrWithdraw(CovalenthqTransactionHistoryItemLog log) { + if (log.getTopics() != null && !log.getTopics().isEmpty()) { + return LOG_EVENTS_V1.get(log.getTopics().get(0)); + } + + return null; + } + + private CovalenthqVaultTransactionType toCovalenthqVaultTransactionTypeFromTransfer(CovalenthqTransactionHistoryItemLog log) { + if (log.getTopics() != null && !log.getTopics().isEmpty()) { + return LOG_EVENTS_V2.get(log.getTopics().get(0)); + } + + return null; + } + + private String getParamValue(CovalenthqTransactionHistoryItemLog item, String param, String address, String network, int paramSize) { + var ethLog = new Log(); + ethLog.setTopics(item.getTopics()); + ethLog.setData(item.getData()); + + List result = simpleDecoder.decodeEthLogForDepositAndWithdraw(ethLog, network, paramSize) + .orElseThrow(); + + if (ethLog.getData() == null && DecodeExcludeConstants.DECODE_ONLY_TOPICS.get(ETH_NETWORK).contains(address.toLowerCase())) { + ethLog.setData(StringUtils.EMPTY); + result = simpleDecoder.decodeOnlyTopics(ethLog); + } + + return getParamValue(param, result); + } + + private String getParamValue(CovalenthqTransactionHistoryItemLog item, String param) { + var ethLog = new Log(); + ethLog.setTopics(item.getTopics()); + ethLog.setData(item.getData()); + + List result = simpleDecoder.decodeEthLog(ethLog) + .orElseThrow(); + + return getParamValue(param, result); + } + + private String getParamValue(String param, List result) { + + var indexParam = -1; + var castToString = false; + + switch (param) { + case "account": + case "user": + case "dst": + case "provider": + case "from": + indexParam = 0; + castToString = true; + break; + case "wad": + case "value": + indexParam = 1; + break; + case "to": + indexParam = 1; + castToString = true; + break; + case "amount" : + case "valueTransfer": + case "writeAmount": + indexParam = 2; + break; + } + if (result.isEmpty() || indexParam == -1 || indexParam >= result.size()) { + log.error("Unknown param or can not parse data"); + throw new IllegalStateException(); + } + + return castToString + ? (String) result.get(indexParam).getValue() + : ((BigInteger) result.get(indexParam).getValue()).toString(); + } +} diff --git a/src/main/java/pro/belbix/ethparser/utils/recalculation/TransfersRecalculate.java b/src/main/java/pro/belbix/ethparser/utils/recalculation/TransfersRecalculate.java index b9a3d75d..c81bae8a 100644 --- a/src/main/java/pro/belbix/ethparser/utils/recalculation/TransfersRecalculate.java +++ b/src/main/java/pro/belbix/ethparser/utils/recalculation/TransfersRecalculate.java @@ -1,6 +1,6 @@ package pro.belbix.ethparser.utils.recalculation; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.FARM_TOKEN; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.FARM_TOKEN; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/pro/belbix/ethparser/web3/EthBlockService.java b/src/main/java/pro/belbix/ethparser/web3/EthBlockService.java index dbeb3138..1e06a9b3 100644 --- a/src/main/java/pro/belbix/ethparser/web3/EthBlockService.java +++ b/src/main/java/pro/belbix/ethparser/web3/EthBlockService.java @@ -1,23 +1,24 @@ package pro.belbix.ethparser.web3; +import lombok.AllArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.web3j.protocol.core.methods.response.EthBlock.Block; import pro.belbix.ethparser.entity.v0.BlockCacheEntity; +import pro.belbix.ethparser.properties.NetworkProperties; import pro.belbix.ethparser.repositories.v0.BlockCacheRepository; +import pro.belbix.ethparser.service.AbiProviderService; @Service @Log4j2 +@AllArgsConstructor public class EthBlockService { private final Web3Functions web3; private final BlockCacheRepository blockCacheRepository; - - public EthBlockService(Web3Functions web3, - BlockCacheRepository blockCacheRepository) { - this.web3 = web3; - this.blockCacheRepository = blockCacheRepository; - } + private final NetworkProperties networkProperties; + private final AbiProviderService abiProviderService; public synchronized long getTimestampSecForBlock(long blockNumber, String network) { BlockCacheEntity cachedBlock = @@ -30,6 +31,7 @@ public synchronized long getTimestampSecForBlock(long blockNumber, String networ throw new IllegalStateException("Can't fetch block for " + blockNumber); } + // TODO ERROR: duplicate key value violates unique constraint "block_cache_pkey" cachedBlock = new BlockCacheEntity(); cachedBlock.setBlock(blockNumber); cachedBlock.setBlockDate(extractDateFromBlock(block)); @@ -44,6 +46,12 @@ public synchronized long getTimestampSecForBlock(long blockNumber, String networ return extractDateFromBlock(block); } + @Cacheable("timestamp_block") + public long getBlockFromOtherChain(long block, String networkFrom, String networkTo) { + var timestamp = getTimestampSecForBlock(block, networkFrom); + return abiProviderService.getBlockByTimestamp(String.valueOf(timestamp), networkTo, networkProperties.get(networkTo).getAbiProviderKey()); + } + private static long extractDateFromBlock(Block block) { return block.getTimestamp().longValue(); } diff --git a/src/main/java/pro/belbix/ethparser/web3/MethodDecoder.java b/src/main/java/pro/belbix/ethparser/web3/MethodDecoder.java index 0f64936d..d026043f 100644 --- a/src/main/java/pro/belbix/ethparser/web3/MethodDecoder.java +++ b/src/main/java/pro/belbix/ethparser/web3/MethodDecoder.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import lombok.extern.log4j.Log4j2; import org.web3j.abi.FunctionReturnDecoder; import org.web3j.abi.TypeReference; import org.web3j.abi.datatypes.Address; @@ -32,6 +33,7 @@ import pro.belbix.ethparser.web3.abi.CommonMethods; @SuppressWarnings({"rawtypes", "unchecked"}) +@Log4j2 public abstract class MethodDecoder { protected Map>> parametersByMethodId = new HashMap<>(); diff --git a/src/main/java/pro/belbix/ethparser/web3/SimpleDecoder.java b/src/main/java/pro/belbix/ethparser/web3/SimpleDecoder.java index ec9b9b1c..073e0d55 100644 --- a/src/main/java/pro/belbix/ethparser/web3/SimpleDecoder.java +++ b/src/main/java/pro/belbix/ethparser/web3/SimpleDecoder.java @@ -1,15 +1,25 @@ package pro.belbix.ethparser.web3; +import static org.web3j.abi.FunctionReturnDecoder.decodeIndexedValue; +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; + +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.TreeMap; import java.util.function.Predicate; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.web3j.abi.FunctionReturnDecoder; +import org.web3j.abi.TypeReference; import org.web3j.abi.datatypes.Type; import org.web3j.protocol.core.methods.response.EthLog.LogResult; import org.web3j.protocol.core.methods.response.Log; import org.web3j.protocol.core.methods.response.Transaction; import pro.belbix.ethparser.model.tx.EthTransactionI; +@Component +@Slf4j public class SimpleDecoder extends MethodDecoder { public Optional> decodeEthLog(Log ethLog) { @@ -19,6 +29,56 @@ public Optional> decodeEthLog(Log ethLog) { extractLogIndexedValues(ethLog.getTopics(), ethLog.getData(), parameters)); } + public Optional> decodeEthLogForDepositAndWithdraw(Log ethLog, String network, int paramSize) { + return parseMethodId(ethLog) + .flatMap(this::findParameters) + .map(parameters -> + extractLogIndexedValuesWrapped(ethLog.getTopics(), ethLog.getData(), parameters, network, paramSize)); + } + + // deposit and withdraw + public List decodeOnlyTopics(Log ethLog) { + try { + return extractLogIndexedValues(ethLog.getTopics(), ethLog.getData(), List.of( + TypeReference.makeTypeReference("address", true, false), + TypeReference.makeTypeReference("uint256", true, false) + )); + } catch (Exception e) { + log.error("Error during decodeOnlyTopics", e); + return List.of(); + } + } + + // deposit and withdraw + public List decodeOnlyData(List topics, String data, int paramSize) { + try { + return paramSize > 2 ? + extractLogIndexedValues(topics, data, List.of( + TypeReference.makeTypeReference("address", false, false), + TypeReference.makeTypeReference("uint256", false, false), + TypeReference.makeTypeReference("uint256", false, false) + )) : + extractLogIndexedValues(topics, data, List.of( + TypeReference.makeTypeReference("address", false, false), + TypeReference.makeTypeReference("uint256", false, false) + )); + } catch (Exception e) { + log.error("Error during decodeOnlyTopics", e); + return List.of(); + } + } + + public List extractLogIndexedValuesWrapped(List topics, String data, List> parameters, String network, int paramSize) { + try { + return extractLogIndexedValues(topics, data, parameters); + } catch (Exception e) { + log.error("Get error during parse log: {}", e.getMessage()); + return BSC_NETWORK.equals(network) ? + decodeOnlyData(topics, data, paramSize) : + extractLogIndexValues(topics, data); + } + } + @SuppressWarnings("rawtypes") public Optional findLogByPredicate(List results, Predicate> predicate) { @@ -41,4 +101,38 @@ public EthTransactionI mapTypesToModel(List types, String methodID, Transaction transaction) { throw new UnsupportedOperationException(); } + + // method only for deposit and withdraw + private List extractLogIndexValues(List topics, String data) { + try { + var values = new ArrayList(); + + List> parameters = List.of( + TypeReference.makeTypeReference("uint256"), + TypeReference.makeTypeReference("uint256") + ); + + if (data == null) { + return values; + } + + if (topics == null || topics.size() < 2) { + log.error("Can not extract logs for uniswapv3, topics - {}", topics); + return values; + } + + values.add( + decodeIndexedValue(topics.get(1), TypeReference.makeTypeReference("address", true, false)) + ); + + values.addAll( + FunctionReturnDecoder.decode(data, getNonIndexedParameters(parameters)) + ); + + return values; + } catch (Exception e) { + log.error("Can not extract logs for uniswapv3", e); + throw new IllegalStateException(e); + } + } } diff --git a/src/main/java/pro/belbix/ethparser/web3/Web3TransactionFlowable.java b/src/main/java/pro/belbix/ethparser/web3/Web3TransactionFlowable.java index b8f2330e..f05e9403 100644 --- a/src/main/java/pro/belbix/ethparser/web3/Web3TransactionFlowable.java +++ b/src/main/java/pro/belbix/ethparser/web3/Web3TransactionFlowable.java @@ -73,14 +73,15 @@ public void run() { AtomicInteger counter = new AtomicInteger(0); web3Functions.findBlocksByBlockBatch(from, to, network) .sorted(Comparator.comparing(Block::getNumber)) - .forEach(block -> - transactionConsumers.forEach(queue -> - block.getTransactions().forEach(t -> { - counter.incrementAndGet(); - writeInQueue(queue, (Transaction) t.get(), transactionConsumers.size()); - }) - ) - ); + .forEach(block -> { + log.info("Block info: {}, transactionConsumers: {}", block, transactionConsumers); + transactionConsumers.forEach(queue -> + block.getTransactions().forEach(t -> { + counter.incrementAndGet(); + writeInQueue(queue, (Transaction) t.get(), transactionConsumers.size()); + }) + ); + }); lastParsedBlock = to; saveLast(to); log.info("Parse {} transactions from {} to {} on block: {} - {}", diff --git a/src/main/java/pro/belbix/ethparser/web3/abi/FunctionsNames.java b/src/main/java/pro/belbix/ethparser/web3/abi/FunctionsNames.java index 5ca00dd5..8788d70c 100644 --- a/src/main/java/pro/belbix/ethparser/web3/abi/FunctionsNames.java +++ b/src/main/java/pro/belbix/ethparser/web3/abi/FunctionsNames.java @@ -99,4 +99,11 @@ private FunctionsNames() { public static final String CLAIMED_REWARDS = "claimedRewards"; public static final String VAULT = "vault"; public static final String RATE_BY_PATH = "rateByPath"; + public static final String GET_POOL_ID = "getPoolId"; + public static final String GET_POOL_TOKENS = "getPoolTokens"; + public static final String GET_VAULT = "getVault"; + public static final String BALANCES = "balances"; + public static final String CHECKING_PRICING_TOKEN = "checkPricingToken"; + public static final String GET_BALANCES = "get_balances"; + public static final String SLOT_0 = "slot0"; } diff --git a/src/main/java/pro/belbix/ethparser/web3/abi/FunctionsUtils.java b/src/main/java/pro/belbix/ethparser/web3/abi/FunctionsUtils.java index 41164068..7c585861 100644 --- a/src/main/java/pro/belbix/ethparser/web3/abi/FunctionsUtils.java +++ b/src/main/java/pro/belbix/ethparser/web3/abi/FunctionsUtils.java @@ -4,10 +4,16 @@ import static org.web3j.abi.TypeReference.makeTypeReference; import static org.web3j.protocol.core.DefaultBlockParameterName.LATEST; import static pro.belbix.ethparser.utils.Caller.silentCall; +import static pro.belbix.ethparser.web3.abi.FunctionsNames.BALANCES; import static pro.belbix.ethparser.web3.abi.FunctionsNames.BALANCE_OF; +import static pro.belbix.ethparser.web3.abi.FunctionsNames.CHECKING_PRICING_TOKEN; +import static pro.belbix.ethparser.web3.abi.FunctionsNames.COINS; import static pro.belbix.ethparser.web3.abi.FunctionsNames.DECIMALS; +import static pro.belbix.ethparser.web3.abi.FunctionsNames.GET_POOL_ID; +import static pro.belbix.ethparser.web3.abi.FunctionsNames.GET_POOL_TOKENS; import static pro.belbix.ethparser.web3.abi.FunctionsNames.GET_RESERVES; import static pro.belbix.ethparser.web3.abi.FunctionsNames.NAME; +import static pro.belbix.ethparser.web3.abi.FunctionsNames.SLOT_0; import static pro.belbix.ethparser.web3.abi.FunctionsNames.TOKEN0; import static pro.belbix.ethparser.web3.abi.FunctionsNames.TOKEN1; import static pro.belbix.ethparser.web3.contracts.ContractConstants.ZERO_ADDRESS; @@ -25,6 +31,8 @@ import java.util.Optional; import java.util.stream.Collectors; import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.web3j.abi.TypeReference; import org.web3j.abi.datatypes.Address; @@ -33,6 +41,7 @@ import org.web3j.abi.datatypes.Function; import org.web3j.abi.datatypes.Type; import org.web3j.abi.datatypes.Utf8String; +import org.web3j.abi.datatypes.generated.Bytes32; import org.web3j.abi.datatypes.generated.Bytes4; import org.web3j.abi.datatypes.generated.Uint112; import org.web3j.abi.datatypes.generated.Uint256; @@ -40,12 +49,15 @@ import org.web3j.protocol.core.DefaultBlockParameter; import org.web3j.protocol.core.DefaultBlockParameterNumber; import org.web3j.tuples.generated.Tuple2; +import org.web3j.utils.Numeric; import pro.belbix.ethparser.entity.contracts.ContractEntity; import pro.belbix.ethparser.web3.MethodDecoder; import pro.belbix.ethparser.web3.Web3Functions; import pro.belbix.ethparser.web3.contracts.ContractType; import pro.belbix.ethparser.web3.contracts.ContractUtils; import pro.belbix.ethparser.web3.contracts.db.ContractDbService; +import pro.belbix.ethparser.web3.contracts.models.BalancerPoolTokenInfo; +import pro.belbix.ethparser.web3.contracts.models.CurveTokenInfo; @SuppressWarnings("rawtypes") @Service @@ -60,6 +72,9 @@ public class FunctionsUtils { public final static String TYPE_BOOL = "bool"; private final Map functionsCache = new HashMap<>(); + private final static String EXCLUDE_ONEINCH = "1inch"; + private final static String EXCLUDE_KYBER = "Kyber"; + private final static BigInteger DEFAULT_DECIMAL = BigInteger.valueOf(18); private final Web3Functions web3Functions; private final ContractDbService contractDbService; @@ -76,15 +91,74 @@ public Tuple2 callReserves( Long block, String network) { - String lpName = callStrByName(NAME, lpAddress, block, network).orElse(""); + String lpName = callStrByName(NAME, lpAddress, block, network).orElse(StringUtils.EMPTY); - if (lpName.startsWith("1inch")) { + if (lpName.startsWith(EXCLUDE_ONEINCH)) { return callOneInchReserves(lpAddress, block, network); } else { - return callUniReserves(lpAddress, block, network); + return callUniReservesOrElseGetFromBlockchain(lpAddress, block, network, lpName); } } + @Cacheable("decimal_latest_block") + public int getDecimal(String address, String network) { + return callIntByName(FunctionsNames.DECIMALS, address, null, network) + .orElse(DEFAULT_DECIMAL).intValue(); + } + + @Cacheable("uni_tokens_latest_block") + public Tuple2 callTokensForSwapPlatform(String address, String network) { + String token0 = callAddressByName(TOKEN0, address, null, network) + .orElseThrow(() -> new IllegalStateException("Error get token0 for " + address)); + String token1 = callAddressByName(TOKEN1, address, null, network) + .orElseThrow(() -> new IllegalStateException("Error get token1 for " + address)); + + return new Tuple2<>(token0, token1); + } + + @Cacheable("uni_reserves") + public Tuple2 callUniReservesForSwapPlatform(String address, Long block, String network, List> typeReferences) { + var types = web3Functions.callFunction(new Function( + GET_RESERVES, + Collections.emptyList(), + typeReferences), address, resolveBlock(block), network); + + if (types == null || types.size() < typeReferences.size()) { + log.error("Wrong values for " + address); + return null; + } + + var tokens = callTokensForSwapPlatform(address, network); + var v1 = (BigInteger) types.get(0).getValue(); + var v2 = (BigInteger) types.get(1).getValue(); + + return new Tuple2<>( + parseAmount(v1, tokens.component1(), network), + parseAmount(v2, tokens.component2(), network) + ); + } + + @Cacheable("can_get_token_price") + public boolean canGetTokenPrice(String tokenAddress, String oracleAddress, Long block, String network) { + return callBoolByNameWithAddressArg(CHECKING_PRICING_TOKEN, tokenAddress, oracleAddress, block, network) + .orElse(false); + } + + @Cacheable("uniswap_slot0") + public Optional getSqrtPriceX96(String address, Long block, String network) { + List types = web3Functions.callFunction(new Function( + SLOT_0, + Collections.emptyList(), + List.of( + new TypeReference() {} + )), address, resolveBlock(block), network); + + if (types == null || types.isEmpty()) { + return Optional.empty(); + } + return Optional.ofNullable(((Uint112)types.get(0)).getValue()); + } + private Tuple2 callOneInchReserves(String lpAddress, Long block, String network) { String coin0 = callAddressByName(TOKEN0, lpAddress, block, network) .orElseThrow(() -> new IllegalStateException("Error get token0 for " + lpAddress)); @@ -110,18 +184,12 @@ private Tuple2 callOneInchReserves(String lpAddress, Long block, return new Tuple2<>(coin0Balance, coin1Balance); } - private Tuple2 callUniReserves(String lpAddress, Long block, String network) { + private Tuple2 callUniReserves(String lpAddress, Long block, String network, List> typeReferences) { List types = web3Functions.callFunction(new Function( GET_RESERVES, Collections.emptyList(), - Arrays.asList(new TypeReference() { - }, - new TypeReference() { - }, - new TypeReference() { - } - )), lpAddress, resolveBlock(block), network); - if (types == null || types.size() < 3) { + typeReferences), lpAddress, resolveBlock(block), network); + if (types == null || types.size() < typeReferences.size()) { log.error("Wrong values for " + lpAddress); return null; } @@ -136,6 +204,15 @@ private Tuple2 callUniReserves(String lpAddress, Long block, Str ); } + private Tuple2 callUniReservesOrElseGetFromBlockchain(String lpAddress, Long block, String network, String lpName) { + var types = getTypeReferenceForGetReserves(lpName); + try { + return callUniReserves(lpAddress, block, network, types); + } catch (IllegalStateException e) { + return callUniReservesForSwapPlatform(lpAddress, block, network, types); + } + } + public double fetchUint256Field( String functionName, String address, @@ -343,6 +420,127 @@ public static List> typeReferencesList(String... names) { return types; } + public Optional getPoolId(String address, Long block, String network) { + var result = web3Functions.callFunction(new Function( + GET_POOL_ID, + Collections.emptyList(), + Collections.singletonList(new TypeReference() { + })), + address, + new DefaultBlockParameterNumber(block), + network + ); + + if (result == null || result.isEmpty()) { + return Optional.empty(); + } + var bytes = (Bytes32)result.get(0); + return Optional.of(Numeric.toHexString(bytes.getValue())); + } + + public Optional getPoolTokens(String address, Long block, String network, String poolId) { + var result = web3Functions.callFunction(new Function( + GET_POOL_TOKENS, + Collections.singletonList(new Bytes32(Numeric.hexStringToByteArray(poolId))), + List.of( + new TypeReference>() { + }, + new TypeReference>() { + }, + new TypeReference() { + } + )), + address, + new DefaultBlockParameterNumber(block), + network + ); + + if (result == null || result.size() < 2) { + return Optional.empty(); + } + + return Optional.ofNullable( + BalancerPoolTokenInfo.builder() + .address( + ((List
)result.get(0).getValue()).stream().map(Address::getValue).collect(Collectors.toList()) + ) + .balances( + ((List)result.get(1).getValue()).stream() + .map(Uint256::getValue) + .collect(Collectors.toList()) + ) + .build() + ); + } + + @Cacheable("address_name") + public String getName(String address, String network) { + return callStrByName(NAME, address, null, network) + .orElse(StringUtils.EMPTY); + } + + @Cacheable("curve_vault_size") + public int getCurveVaultSize(String address, String network) { + var index = 0; + List result = null; + do { + result = web3Functions.callFunction(new Function( + BALANCES, + List.of(new Uint256(index)), + List.of( + new TypeReference() { + } + )), + address, + resolveBlock(null), + network + ); + + if (result != null) { + index++; + } + + } while(result != null); + + return index; + } + + public Optional getCurveTokenInfo(String minter, Long block, String network, long i) { + var coin = web3Functions.callFunction(new Function( + COINS, + Collections.singletonList(new Uint256(i)), + Collections.singletonList(new TypeReference
() {})), + minter, + new DefaultBlockParameterNumber(block), + network + ); + + if (coin == null || coin.isEmpty()) { + return Optional.empty(); + } + + var balance = web3Functions.callFunction(new Function( + BALANCES, + Collections.singletonList(new Uint256(i)), + Collections.singletonList(new TypeReference() {}) + ), + minter, + new DefaultBlockParameterNumber(block), + network + ); + + if (balance == null || balance.isEmpty()) { + return Optional.empty(); + } + + return Optional.ofNullable( + CurveTokenInfo.builder() + .address(((Address)coin.get(0)).getValue()) + .balance(((Uint256)balance.get(0)).getValue()) + .build() + ); + } + // ************ PRIVATE METHODS ************************** private Function findSimpleFunction(String name, String returnType) { @@ -420,4 +618,17 @@ private static DefaultBlockParameter resolveBlock(Long block) { } return LATEST; } + + private List> getTypeReferenceForGetReserves(String lpName) { + List> typeReferences = new ArrayList<>(Arrays.asList( + new TypeReference() {}, + new TypeReference() {} + )); + + if (!lpName.startsWith(EXCLUDE_KYBER)) { + typeReferences.add(new TypeReference() {}); + } + + return typeReferences; + } } diff --git a/src/main/java/pro/belbix/ethparser/web3/bancor/BancorLogDecoder.java b/src/main/java/pro/belbix/ethparser/web3/bancor/BancorLogDecoder.java index ee88cca4..746dd453 100644 --- a/src/main/java/pro/belbix/ethparser/web3/bancor/BancorLogDecoder.java +++ b/src/main/java/pro/belbix/ethparser/web3/bancor/BancorLogDecoder.java @@ -3,7 +3,7 @@ import static pro.belbix.ethparser.model.tx.BancorPriceTx.BNT; import static pro.belbix.ethparser.model.tx.BancorPriceTx.FARM; import static pro.belbix.ethparser.web3.contracts.ContractConstants.BANCOR_CONVERSION_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.FARM_TOKEN; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.FARM_TOKEN; import java.math.BigInteger; import java.util.List; diff --git a/src/main/java/pro/belbix/ethparser/web3/bancor/BancorPriceParser.java b/src/main/java/pro/belbix/ethparser/web3/bancor/BancorPriceParser.java index a41f9b8b..2cab4620 100644 --- a/src/main/java/pro/belbix/ethparser/web3/bancor/BancorPriceParser.java +++ b/src/main/java/pro/belbix/ethparser/web3/bancor/BancorPriceParser.java @@ -3,9 +3,9 @@ import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; import static pro.belbix.ethparser.web3.abi.FunctionsNames.RATE_BY_PATH; import static pro.belbix.ethparser.web3.contracts.ContractConstants.BANCOR_CONVERSION_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.BANCOR_USDC_CONVERT_PATH; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV8.BANCOR_USDC_CONVERT_PATH; import static pro.belbix.ethparser.web3.contracts.ContractConstants.D6; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.FARM_TOKEN; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.FARM_TOKEN; import static pro.belbix.ethparser.web3.contracts.ContractConstants.L18; import java.math.BigDecimal; diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstants.java b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstants.java index 35e66e7f..950de2b0 100644 --- a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstants.java +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstants.java @@ -1,13 +1,6 @@ package pro.belbix.ethparser.web3.contracts; -import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; -import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; -import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; - import java.math.BigInteger; -import java.util.List; -import java.util.Map; -import java.util.Set; import org.web3j.protocol.core.DefaultBlockParameter; import org.web3j.protocol.core.DefaultBlockParameterNumber; @@ -31,14 +24,6 @@ public class ContractConstants { public static final DefaultBlockParameterNumber MATIC_BLOCK_NUMBER_06_JUL_2021 = (DefaultBlockParameterNumber) DefaultBlockParameter.valueOf(new BigInteger("16566542")); - public static final String PCS_V1_FACTORY_ADDRESS = "0xbcfccbde45ce874adcb698cc183debcf17952812" - .toLowerCase(); - public static final String PCS_V2_FACTORY_ADDRESS = "0xca143ce32fe78f1f7019d7d551a6402fc5350c73" - .toLowerCase(); - public static final String UNISWAP_FACTORY_ADDRESS = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f" - .toLowerCase(); - public static final String SUSHISWAP_FACTORY_ADDRESS = "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac" - .toLowerCase(); public static final String CURVE_REGISTRY_ADDRESS = "0x7D86446dDb609eD0F5f8684AcF30380a356b2B4c" .toLowerCase(); public static final String BELT_POOL_ADDRESS = "0xF16D312d119c13dD27fD0dC814b0bCdcaAa62dfD" @@ -48,8 +33,7 @@ public class ContractConstants { static final String ONE_INCH_FACTORY_BSC = "0xD41B24bbA51fAc0E4827b6F94C0D6DDeB183cD64" .toLowerCase(); public static final String ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; - public static final String FARM_TOKEN = "0xa0246c9032bc3a600820415ae600c6388619a14d" - .toLowerCase(); + public static final String CURVE_ZERO_ADDRESS = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; public static final String BSC_FARM_TOKEN = "0x4B5C23cac08a567ecf0c1fFcA8372A45a5D33743" .toLowerCase(); public static final String MATIC_FARM_TOKEN = "0xab0b2ddb9c7e440fac8e140a89c0dbcbf2d7bbff" @@ -60,162 +44,8 @@ public class ContractConstants { .toLowerCase(); public static final String PS_V0_ADDRESS = "0x59258F4e15A5fC74A7284055A8094F58108dbD4f" .toLowerCase(); - public static final String iPS_ADDRESS = "0x1571eD0bed4D987fe2b498DdBaE7DFA19519F651" - .toLowerCase(); public static final String ST_PS_ADDRESS = "0x8f5adC58b32D4e5Ca02EAC0E293D35855999436C" .toLowerCase(); - public static final String QUICK_FACTORY_ADDRESS = "0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32" - .toLowerCase(); - public static final String SUSHI_FACTORY_ADDRESS = "0xc35dadb65012ec5796536bd9864ed8773abc74c4" - .toLowerCase(); public static final String BANCOR_CONVERSION_ADDRESS = "0x2f9ec37d6ccfff1cab21733bdadede11c823ccb0"; - - public final static Map DEPLOYERS = Map.of( - ETH_NETWORK, "0xf00dD244228F51547f0563e60bCa65a30FBF5f7f".toLowerCase(), - BSC_NETWORK, "0xf00dd244228f51547f0563e60bca65a30fbf5f7f".toLowerCase(), - MATIC_NETWORK, "0xf00dd244228f51547f0563e60bca65a30fbf5f7f".toLowerCase() - ); - - final static Map> CONTROLLERS = Map.of( - ETH_NETWORK, Map.of( - 10770087L, "0x222412af183BCeAdEFd72e4Cb1b71f1889953b1C".toLowerCase(), - 12652885L, "0x3cc47874dc50d98425ec79e647d83495637c55e3".toLowerCase()), - BSC_NETWORK, Map.of( - 5990839L, "0x222412af183bceadefd72e4cb1b71f1889953b1c".toLowerCase()), - MATIC_NETWORK, Map.of( - 16612698L, "0x2ce34b1bb247f242f1d2a33811e01138968efbff".toLowerCase() - ) - ); - - public final static Map NOTIFY_HELPER = Map.of( - ETH_NETWORK, "0xe20c31e3d08027f5aface84a3a46b7b3b165053c".toLowerCase(), - BSC_NETWORK, "0xf71042c88458ff1702c3870f62f4c764712cc9f0".toLowerCase(), - MATIC_NETWORK, "0xe85c8581e60d7cd32bbfd86303d2a4fa6a951dac".toLowerCase() - ); - - final static Map> ORACLES = Map.of( - ETH_NETWORK, - Map.of(12015724L, "0x48DC32eCA58106f06b41dE514F29780FFA59c279".toLowerCase(), - 12820106L, "0x1358c91D5b25D3eDAc2b7B26A619163d78f1717d".toLowerCase()), - BSC_NETWORK, - Map.of(6442627L, "0xE0e9F05054Ad3a2b6414AD13D768be91a84b47e8".toLowerCase(), - 6952687L, "0x643cF46eef91Bd878D9710ceEB6a7E6F929F2608".toLowerCase(), - 9142012L, "0x0E74303d0D18884Ce2CEb3670e72686645c4f38B".toLowerCase()), - MATIC_NETWORK, - Map.of(16841617L, "0x0E74303d0D18884Ce2CEb3670e72686645c4f38B".toLowerCase()) - ); - - final static Map> ORACLES_BY_FACTORY = Map.of( - ETH_NETWORK, - Map.of(UNISWAP_FACTORY_ADDRESS, - "0x48DC32eCA58106f06b41dE514F29780FFA59c279".toLowerCase()), - BSC_NETWORK, - Map.of(PCS_V1_FACTORY_ADDRESS, - "0xE0e9F05054Ad3a2b6414AD13D768be91a84b47e8".toLowerCase(), // V1 - PCS_V2_FACTORY_ADDRESS, - "0x643cF46eef91Bd878D9710ceEB6a7E6F929F2608".toLowerCase()) // V2 - ); - - static final Map> FULL_PARSABLE_UNI_PAIRS = Map.of( - ETH_NETWORK, Map.of( - "0x514906fc121c7878424a5c928cad1852cc545892".toLowerCase(), 10777067, - // UNI_LP_USDC_FARM - FARM - "0x56feaccb7f750b997b36a68625c7c596f0b41a58".toLowerCase(), 11407437, - // UNI_LP_WETH_FARM - FARM - "0xb9fa44b0911f6d777faab2fa9d8ef103f25ddf49".toLowerCase(), 11407202 - // UNI_LP_GRAIN_FARM - GRAIN - ), - BSC_NETWORK, Map.of(), - MATIC_NETWORK, Map.of() - ); - - public static final List BANCOR_USDC_CONVERT_PATH = List.of( - "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C".toLowerCase(), // BNT - "0x874d8dE5b26c9D9f6aA8d7bab283F9A9c6f777f4".toLowerCase(), // Liquidity Pool (USDCBNT) - "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48".toLowerCase() // USDC - ); - - static final Map> PARSABLE_BANCOR_TRANSACTIONS = Map.of( - ETH_NETWORK, Map.of( - BANCOR_CONVERSION_ADDRESS, 10285676 - ), - BSC_NETWORK, Map.of(), - MATIC_NETWORK, Map.of() - ); - - public static final Set PS_ADDRESSES = Set.of( - "0x8f5adC58b32D4e5Ca02EAC0E293D35855999436C".toLowerCase(), // ST_PS - FARM_TOKEN, // FARM TOKEN - PS_ADDRESS, // PS - PS_V0_ADDRESS // PS_V0 - ); - - public static final Set ONE_DOLLAR_TOKENS = Set.of( - "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48".toLowerCase(), //USDC - "0xe9e7cea3dedca5984780bafc599bd69add087d56".toLowerCase(), //BUSD - "0xdAC17F958D2ee523a2206206994597C13D831ec7".toLowerCase(), //USDT - "0x2791bca1f2de4661ed88a30c99a7a9449aa84174".toLowerCase(), //matic USDC - "0xc2132d05d31c914a87c6611c10748aeb04b58e8f".toLowerCase() //matic USDT - ); - - //Key tokens are used to find liquidity for any given token on Uni, Sushi and Curve. - public static final Map> KEY_TOKENS = Map.of( - ETH_NETWORK, Set.of( - "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48".toLowerCase(), //USDC - "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2".toLowerCase(), //WETH - "0x6B175474E89094C44Da98b954EedeAC495271d0F".toLowerCase(), //DAI - "0xdAC17F958D2ee523a2206206994597C13D831ec7".toLowerCase(), //USDT - "0xa47c8bf37f92aBed4A126BDA807A7b7498661acD".toLowerCase(), //UST - "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599".toLowerCase(), //WBTC - "0xdB25f211AB05b1c97D595516F45794528a807ad8".toLowerCase(), //EURS - "0x514910771AF9Ca656af840dff83E8264EcF986CA".toLowerCase() //LINK - ), - BSC_NETWORK, Set.of( - "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d".toLowerCase(), //USDC - "0x2170Ed0880ac9A755fd29B2688956BD959F933F8".toLowerCase(), //ETH - "0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3".toLowerCase(), //DAI - "0x55d398326f99059fF775485246999027B3197955".toLowerCase(), //USDT - "0x23396cF899Ca06c4472205fC903bDB4de249D6fC".toLowerCase(), //UST - "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c".toLowerCase(), //BTCB - "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56".toLowerCase(), //BUSD - "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c".toLowerCase(), //WBNB - "0x4BD17003473389A42DAF6a0a729f6Fdb328BbBd7".toLowerCase(), //VAI - "0x111111111117dC0aa78b770fA6A738034120C302".toLowerCase() //1INCH - ), - MATIC_NETWORK, Set.of( - "0x2791bca1f2de4661ed88a30c99a7a9449aa84174".toLowerCase(), //USDC - "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619".toLowerCase(), //ETH - "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063".toLowerCase(), //DAI - "0xc2132d05d31c914a87c6611c10748aeb04b58e8f".toLowerCase(), //USDT - "0x692597b009d13c4049a947cab2239b7d6517875f".toLowerCase(), //UST - "0xdab529f40e671a1d4bf91361c21bf9f0c9712ab7".toLowerCase(), //BUSD - "0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6".toLowerCase() //WBTC - ) - ); - - //Pricing tokens are Key tokens with good liquidity with the defined output token on Uniswap. - public static final Map> PRISING_TOKENS = Map.of( - ETH_NETWORK, Set.of( - "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48".toLowerCase(), //USDC - "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2".toLowerCase(), //WETH - "0x6B175474E89094C44Da98b954EedeAC495271d0F".toLowerCase(), //DAI - "0xdAC17F958D2ee523a2206206994597C13D831ec7".toLowerCase(), //USDT - "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599".toLowerCase(), //WBTC - "0xdB25f211AB05b1c97D595516F45794528a807ad8".toLowerCase() //EURS - ) - ); - - public static final Map> UNI_FACTORIES = Map.of( - ETH_NETWORK, Map.of( - UNISWAP_FACTORY_ADDRESS, 10000835, - SUSHISWAP_FACTORY_ADDRESS, 10794229 - ), BSC_NETWORK, Map.of( - PCS_V1_FACTORY_ADDRESS, 586851, - PCS_V2_FACTORY_ADDRESS, 6809737 - ), MATIC_NETWORK, Map.of( - QUICK_FACTORY_ADDRESS, 4931780, - SUSHI_FACTORY_ADDRESS, 11333218 - ) - ); } diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV2.java b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV2.java new file mode 100644 index 00000000..1af8ecfd --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV2.java @@ -0,0 +1,33 @@ +package pro.belbix.ethparser.web3.contracts; + +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; + +import java.util.List; +import java.util.Map; + +public interface ContractConstantsV2 { + + Map> EXCLUDE_ADDRESSES_FOR_PRICE_SHARE_BY_NETWORK = Map.of( + ETH_NETWORK, List.of( + "0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50".toLowerCase(), + "0x59258f4e15a5fc74a7284055a8094f58108dbd4f".toLowerCase() // POOL + ), + BSC_NETWORK, List.of(), + MATIC_NETWORK, List.of() + ); + + Map> FULL_PARSABLE_UNI_PAIRS = Map.of( + ETH_NETWORK, Map.of( + "0x514906fc121c7878424a5c928cad1852cc545892".toLowerCase(), 10777067, + // UNI_LP_USDC_FARM - FARM + "0x56feaccb7f750b997b36a68625c7c596f0b41a58".toLowerCase(), 11407437, + // UNI_LP_WETH_FARM - FARM + "0xb9fa44b0911f6d777faab2fa9d8ef103f25ddf49".toLowerCase(), 11407202 + // UNI_LP_GRAIN_FARM - GRAIN + ), + BSC_NETWORK, Map.of(), + MATIC_NETWORK, Map.of() + ); +} diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV3.java b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV3.java new file mode 100644 index 00000000..2b8fe1ac --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV3.java @@ -0,0 +1,50 @@ +package pro.belbix.ethparser.web3.contracts; + +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; + +import java.util.List; +import java.util.Map; + +public interface ContractConstantsV3 { + + Map> PS_ADDRESSES_BY_NETWORK = Map.of( + ETH_NETWORK, List.of( + "0xd3093e3efbe00f010e8f5efe3f1cb5d9b7fe0eb1".toLowerCase(), // + "0x8f5adC58b32D4e5Ca02EAC0E293D35855999436C".toLowerCase(), // ST_PS + "0xa0246c9032bc3a600820415ae600c6388619a14d".toLowerCase(), // FARM TOKEN + "0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50".toLowerCase(), // PS + "0x59258F4e15A5fC74A7284055A8094F58108dbD4f".toLowerCase() // PS_V0 + ), + BSC_NETWORK, List.of( + "0x4B5C23cac08a567ecf0c1fFcA8372A45a5D33743".toLowerCase() + ), + MATIC_NETWORK, List.of( + "0xab0b2ddb9c7e440fac8e140a89c0dbcbf2d7bbff".toLowerCase() + ) + ); + + Map> ORACLES = Map.of( + ETH_NETWORK, + Map.of(12015724L, "0x48DC32eCA58106f06b41dE514F29780FFA59c279".toLowerCase(), + 12820106L, "0x1358c91D5b25D3eDAc2b7B26A619163d78f1717d".toLowerCase()), + BSC_NETWORK, + Map.of(6442627L, "0xE0e9F05054Ad3a2b6414AD13D768be91a84b47e8".toLowerCase(), + 6952687L, "0x643cF46eef91Bd878D9710ceEB6a7E6F929F2608".toLowerCase(), + 9142012L, "0x0E74303d0D18884Ce2CEb3670e72686645c4f38B".toLowerCase()), + MATIC_NETWORK, + Map.of(16841617L, "0x0E74303d0D18884Ce2CEb3670e72686645c4f38B".toLowerCase()) + ); + + Map> ORACLES_BY_FACTORY = Map.of( + ETH_NETWORK, + Map.of("0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f".toLowerCase(), + "0x48DC32eCA58106f06b41dE514F29780FFA59c279".toLowerCase()), + BSC_NETWORK, + Map.of("0xbcfccbde45ce874adcb698cc183debcf17952812".toLowerCase(), + "0xE0e9F05054Ad3a2b6414AD13D768be91a84b47e8".toLowerCase(), // V1 + "0xca143ce32fe78f1f7019d7d551a6402fc5350c73".toLowerCase(), + "0x643cF46eef91Bd878D9710ceEB6a7E6F929F2608".toLowerCase()) // V2 + ); +} diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV4.java b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV4.java new file mode 100644 index 00000000..c5ad4724 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV4.java @@ -0,0 +1,19 @@ +package pro.belbix.ethparser.web3.contracts; + +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; + +import java.util.List; +import java.util.Map; + +public interface ContractConstantsV4 { + Map> EXCLUDE_JARVIS_STABLECOIN = Map.of( + ETH_NETWORK, List.of(), + BSC_NETWORK, List.of(), + MATIC_NETWORK, List.of( + "0x8ca194A3b22077359b5732DE53373D4afC11DeE3".toLowerCase(), // jCAD + "0x8343091F2499FD4b6174A46D067A920a3b851FF9".toLowerCase() // jJPY + ) + ); +} diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV5.java b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV5.java new file mode 100644 index 00000000..b1c53a8a --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV5.java @@ -0,0 +1,17 @@ +package pro.belbix.ethparser.web3.contracts; + +import java.util.List; + +public interface ContractConstantsV5 { + List ONE_DOLLAR_TOKENS = List.of( + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48".toLowerCase(), //USDC + "0xe9e7cea3dedca5984780bafc599bd69add087d56".toLowerCase(), //BUSD + "0xdAC17F958D2ee523a2206206994597C13D831ec7".toLowerCase(), //USDT + "0x0000000000085d4780B73119b644AE5ecd22b376".toLowerCase(), //TUSD + "0x6B175474E89094C44Da98b954EedeAC495271d0F".toLowerCase(), //DAI + "0x2791bca1f2de4661ed88a30c99a7a9449aa84174".toLowerCase(), //matic USDC + "0xc2132d05d31c914a87c6611c10748aeb04b58e8f".toLowerCase(), //matic USDT + "0xE840B73E5287865EEc17d250bFb1536704B43B21".toLowerCase(), //matic mUSD + "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174".toLowerCase() //matic USDC PoS + ); +} diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV6.java b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV6.java new file mode 100644 index 00000000..a1907ac3 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV6.java @@ -0,0 +1,13 @@ +package pro.belbix.ethparser.web3.contracts; + +import java.util.Set; + +public interface ContractConstantsV6 { + + Set PS_ADDRESSES = Set.of( + "0x8f5adC58b32D4e5Ca02EAC0E293D35855999436C".toLowerCase(), // ST_PS + "0xa0246c9032bc3a600820415ae600c6388619a14d".toLowerCase(), // FARM TOKEN + "0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50".toLowerCase(), // PS + "0x59258F4e15A5fC74A7284055A8094F58108dbD4f".toLowerCase() // PS_V0 + ); +} diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV7.java b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV7.java new file mode 100644 index 00000000..b71dc7ec --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV7.java @@ -0,0 +1,86 @@ +package pro.belbix.ethparser.web3.contracts; + +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; + +import java.util.Map; +import pro.belbix.ethparser.model.TokenInfo; + +public interface ContractConstantsV7 { + String iPS_ADDRESS = "0x1571eD0bed4D987fe2b498DdBaE7DFA19519F651" + .toLowerCase(); + String FARM_TOKEN = "0xa0246c9032bc3a600820415ae600c6388619a14d" + .toLowerCase(); + String BANCOR_CONVERSION_ADDRESS = "0x2f9ec37d6ccfff1cab21733bdadede11c823ccb0"; + + Map COIN_PRICE_IN_OTHER_CHAIN = Map.of( + // Denarius (DEN-0121) to Denarius BSC + TokenInfo.builder() + .address("0xf379CB529aE58E1A03E62d3e31565f4f7c1F2020".toLowerCase()) + .network(MATIC_NETWORK) + .build(), + TokenInfo.builder() + .address("0xf6B53b4c982b9B7e87af9dc5c66C85117A5df303") + .network(BSC_NETWORK) + .build(), + // Denarius (DEN-MAR22) to Denarius BSC + TokenInfo.builder() + .address("0xa286eeDAa5aBbAE98F65b152B5057b8bE9893fbB".toLowerCase()) + .network(MATIC_NETWORK) + .build(), + TokenInfo.builder() + .address("0xf6B53b4c982b9B7e87af9dc5c66C85117A5df303") + .network(BSC_NETWORK) + .build(), + // Aureus (AUR-FEB22) to Aureus + TokenInfo.builder() + .address("0x6Fb2415463e949aF08ce50F83E94b7e008BABf07".toLowerCase()) + .network(MATIC_NETWORK) + .build(), + TokenInfo.builder() + .address("0xe8F6311A615b4E5f50bb2C6071c725518207337d") + .network(BSC_NETWORK) + .build(), + // Aureus (AUR-APR22) to Aureus + TokenInfo.builder() + .address("0xBF06D9b11126B140788D842a6ed8dC7885C722B3".toLowerCase()) + .network(MATIC_NETWORK) + .build(), + TokenInfo.builder() + .address("0xe8F6311A615b4E5f50bb2C6071c725518207337d") + .network(BSC_NETWORK) + .build() + ); + + Map DEPLOYERS = Map.of( + ETH_NETWORK, "0xf00dD244228F51547f0563e60bCa65a30FBF5f7f".toLowerCase(), + BSC_NETWORK, "0xf00dd244228f51547f0563e60bca65a30fbf5f7f".toLowerCase(), + MATIC_NETWORK, "0xf00dd244228f51547f0563e60bca65a30fbf5f7f".toLowerCase() + ); + + Map> CONTROLLERS = Map.of( + ETH_NETWORK, Map.of( + 10770087L, "0x222412af183BCeAdEFd72e4Cb1b71f1889953b1C".toLowerCase(), + 12652885L, "0x3cc47874dc50d98425ec79e647d83495637c55e3".toLowerCase()), + BSC_NETWORK, Map.of( + 5990839L, "0x222412af183bceadefd72e4cb1b71f1889953b1c".toLowerCase()), + MATIC_NETWORK, Map.of( + 16612698L, "0x2ce34b1bb247f242f1d2a33811e01138968efbff".toLowerCase() + ) + ); + + Map NOTIFY_HELPER = Map.of( + ETH_NETWORK, "0xe20c31e3d08027f5aface84a3a46b7b3b165053c".toLowerCase(), + BSC_NETWORK, "0xf71042c88458ff1702c3870f62f4c764712cc9f0".toLowerCase(), + MATIC_NETWORK, "0xe85c8581e60d7cd32bbfd86303d2a4fa6a951dac".toLowerCase() + ); + + Map> PARSABLE_BANCOR_TRANSACTIONS = Map.of( + ETH_NETWORK, Map.of( + BANCOR_CONVERSION_ADDRESS, 10285676 + ), + BSC_NETWORK, Map.of(), + MATIC_NETWORK, Map.of() + ); +} diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV8.java b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV8.java new file mode 100644 index 00000000..3b3163ac --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractConstantsV8.java @@ -0,0 +1,81 @@ +package pro.belbix.ethparser.web3.contracts; + +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface ContractConstantsV8 { + + String QUICK_FACTORY_ADDRESS = "0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32" + .toLowerCase(); + String SUSHI_FACTORY_ADDRESS = "0xc35dadb65012ec5796536bd9864ed8773abc74c4" + .toLowerCase(); + String PCS_V1_FACTORY_ADDRESS = "0xbcfccbde45ce874adcb698cc183debcf17952812" + .toLowerCase(); + String PCS_V2_FACTORY_ADDRESS = "0xca143ce32fe78f1f7019d7d551a6402fc5350c73" + .toLowerCase(); + String UNISWAP_FACTORY_ADDRESS = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f" + .toLowerCase(); + String SUSHISWAP_FACTORY_ADDRESS = "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac" + .toLowerCase(); + + + List BANCOR_USDC_CONVERT_PATH = List.of( + "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C".toLowerCase(), // BNT + "0x874d8dE5b26c9D9f6aA8d7bab283F9A9c6f777f4".toLowerCase(), // Liquidity Pool (USDCBNT) + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48".toLowerCase() // USDC + ); + + + //Key tokens are used to find liquidity for any given token on Uni, Sushi and Curve. + Map> KEY_TOKENS = Map.of( + ETH_NETWORK, Set.of( + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48".toLowerCase(), //USDC + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2".toLowerCase(), //WETH + "0x6B175474E89094C44Da98b954EedeAC495271d0F".toLowerCase(), //DAI + "0xdAC17F958D2ee523a2206206994597C13D831ec7".toLowerCase(), //USDT + "0xa47c8bf37f92aBed4A126BDA807A7b7498661acD".toLowerCase(), //UST + "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599".toLowerCase(), //WBTC + "0xdB25f211AB05b1c97D595516F45794528a807ad8".toLowerCase(), //EURS + "0x514910771AF9Ca656af840dff83E8264EcF986CA".toLowerCase() //LINK + ), + BSC_NETWORK, Set.of( + "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d".toLowerCase(), //USDC + "0x2170Ed0880ac9A755fd29B2688956BD959F933F8".toLowerCase(), //ETH + "0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3".toLowerCase(), //DAI + "0x55d398326f99059fF775485246999027B3197955".toLowerCase(), //USDT + "0x23396cF899Ca06c4472205fC903bDB4de249D6fC".toLowerCase(), //UST + "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c".toLowerCase(), //BTCB + "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56".toLowerCase(), //BUSD + "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c".toLowerCase(), //WBNB + "0x4BD17003473389A42DAF6a0a729f6Fdb328BbBd7".toLowerCase(), //VAI + "0x111111111117dC0aa78b770fA6A738034120C302".toLowerCase() //1INCH + ), + MATIC_NETWORK, Set.of( + "0x2791bca1f2de4661ed88a30c99a7a9449aa84174".toLowerCase(), //USDC + "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619".toLowerCase(), //ETH + "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063".toLowerCase(), //DAI + "0xc2132d05d31c914a87c6611c10748aeb04b58e8f".toLowerCase(), //USDT + "0x692597b009d13c4049a947cab2239b7d6517875f".toLowerCase(), //UST + "0xdab529f40e671a1d4bf91361c21bf9f0c9712ab7".toLowerCase(), //BUSD + "0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6".toLowerCase() //WBTC + ) + ); + + Map> UNI_FACTORIES = Map.of( + ETH_NETWORK, Map.of( + UNISWAP_FACTORY_ADDRESS, 10000835, + SUSHISWAP_FACTORY_ADDRESS, 10794229 + ), BSC_NETWORK, Map.of( + PCS_V1_FACTORY_ADDRESS, 586851, + PCS_V2_FACTORY_ADDRESS, 6809737 + ), MATIC_NETWORK, Map.of( + QUICK_FACTORY_ADDRESS, 4931780, + SUSHI_FACTORY_ADDRESS, 11333218 + ) + ); +} diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractLoader.java b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractLoader.java index 0284f05d..96ebc17e 100644 --- a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractLoader.java +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractLoader.java @@ -1,5 +1,6 @@ package pro.belbix.ethparser.web3.contracts; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; import static pro.belbix.ethparser.web3.abi.FunctionsNames.CONTROLLER; import static pro.belbix.ethparser.web3.abi.FunctionsNames.FACTORY; import static pro.belbix.ethparser.web3.abi.FunctionsNames.GOVERNANCE; @@ -14,10 +15,8 @@ import static pro.belbix.ethparser.web3.contracts.ContractConstants.PAIR_TYPE_ONEINCHE; import static pro.belbix.ethparser.web3.contracts.ContractConstants.PAIR_TYPE_SUSHI; import static pro.belbix.ethparser.web3.contracts.ContractConstants.PAIR_TYPE_UNISWAP; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_V0_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.SUSHISWAP_FACTORY_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.UNISWAP_FACTORY_ADDRESS; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV8.SUSHISWAP_FACTORY_ADDRESS; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV8.UNISWAP_FACTORY_ADDRESS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.ZERO_ADDRESS; import static pro.belbix.ethparser.web3.contracts.ContractType.INFRASTRUCTURE; @@ -26,6 +25,7 @@ import java.util.Map.Entry; import lombok.extern.log4j.Log4j2; import org.apache.logging.log4j.util.Strings; +import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import pro.belbix.ethparser.entity.contracts.ContractEntity; import pro.belbix.ethparser.entity.contracts.PoolEntity; @@ -108,11 +108,16 @@ public ContractEntity load(PureEthContractInfo contractInfo) { public void loadToken(TokenContract contract, String network, long block) { ContractEntity tokenContract = load(contract); + loadToken(tokenContract, network, block); + } + public TokenEntity loadToken(ContractEntity tokenContract, String network, long block) { TokenEntity tokenEntity = tokenRepository .findFirstByAddress(tokenContract.getAddress(), network); if (tokenEntity == null) { tokenEntity = new TokenEntity(); + var maxId = tokenRepository.findMaxId() + 1; + tokenEntity.setId(maxId); tokenEntity.setContract(tokenContract); enrichToken(tokenEntity, block, network); tokenRepository.save(tokenEntity); @@ -120,6 +125,8 @@ public void loadToken(TokenContract contract, String network, long block) { enrichToken(tokenEntity, block, network); tokenRepository.save(tokenEntity); } + + return tokenEntity; } public void loadVault(SimpleContract vault, String network, long block) { @@ -211,6 +218,15 @@ private void enrichToken(TokenEntity tokenEntity, long block, String network) { } public void enrichVault(VaultEntity vaultEntity, long block, String network) { + enrichVault(vaultEntity, block, block, network); + } + + public void enrichVaultWithLatestBlock(VaultEntity vaultEntity, long block, String network) { + enrichVault(vaultEntity, block, null, network); + } + + + public void enrichVault(VaultEntity vaultEntity, Long block, Long blockForProperty, String network) { if (appProperties.isOnlyApi()) { return; } @@ -233,8 +249,7 @@ public void enrichVault(VaultEntity vaultEntity, long block, String network) { network )); //exclude PS vaults - if (address.equalsIgnoreCase(PS_ADDRESS) - || address.equalsIgnoreCase(PS_V0_ADDRESS)) { + if (ContractConstantsV2.EXCLUDE_ADDRESSES_FOR_PRICE_SHARE_BY_NETWORK.get(ETH_NETWORK).contains(address.toLowerCase())) { vaultEntity.setName("PS vault"); vaultEntity.setDecimals(18L); return; @@ -256,14 +271,14 @@ public void enrichVault(VaultEntity vaultEntity, long block, String network) { network )); vaultEntity.setName( - functionsUtils.callStrByName(FunctionsNames.NAME, address, block, network).orElse("")); + functionsUtils.callStrByName(FunctionsNames.NAME, address, blockForProperty, network).orElse("")); vaultEntity.setSymbol( - functionsUtils.callStrByName(FunctionsNames.SYMBOL, address, block, network).orElse("")); + functionsUtils.callStrByName(FunctionsNames.SYMBOL, address, blockForProperty, network).orElse("")); vaultEntity.setDecimals( - functionsUtils.callIntByName(FunctionsNames.DECIMALS, address, block, network) + functionsUtils.callIntByName(FunctionsNames.DECIMALS, address, blockForProperty, network) .orElse(BigInteger.ZERO).longValue()); vaultEntity.setUnderlyingUnit( - functionsUtils.callIntByName(FunctionsNames.UNDERLYING_UNIT, address, block, network) + functionsUtils.callIntByName(FunctionsNames.UNDERLYING_UNIT, address, blockForProperty, network) .orElse(BigInteger.ZERO).longValue()); } @@ -422,6 +437,26 @@ public void linkUniPairsToToken(TokenContract tokenContract, String network) { } } + public TokenToUniPairEntity linkUniPairsToToken(String address, long block, TokenEntity tokenEntity, String network) { + UniPairEntity uniPair; + if (address.startsWith("0x")) { + uniPair = uniPairRepository.findFirstByAddress(address, network); + } else { + uniPair = uniPairRepository.findFirstByName(address, network); + } + + if (uniPair == null) { + log.error("Not found lp for {} on {}", address, network); + return null; + } + + if (tokenEntity == null) { + log.error("Token is null for " + address); + return null; + } + return findOrCreateTokenToUniPair(tokenEntity, uniPair, block, network); + } + private TokenToUniPairEntity findOrCreateTokenToUniPair( TokenEntity token, UniPairEntity uniPair, @@ -446,7 +481,9 @@ private TokenToUniPairEntity findOrCreateTokenToUniPair( if (pairByLp != null && !pairByLp.isEmpty()) { log.info("We already had linked " + uniPair.getContract().getName()); } + var id = tokenToUniPairRepository.findMaxId() + 1; tokenToUniPairEntity = new TokenToUniPairEntity(); + tokenToUniPairEntity.setId(id); tokenToUniPairEntity.setToken(token); tokenToUniPairEntity.setUniPair(uniPair); tokenToUniPairEntity.setBlockStart(blockStart); @@ -492,7 +529,11 @@ ContractEntity findOrCreateContract(String address, || ZERO_ADDRESS.equalsIgnoreCase(address)) { return null; } - ContractEntity entity = contractRepository.findFirstByAddress(address, network); + var result = contractRepository.findFirstByAddress(address, network, PageRequest.of(0, 1)); + if (result == null || result.isEmpty()) { + return null; + } + ContractEntity entity = result.get(0); if (appProperties.isOnlyApi()) { return entity; } diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractUtils.java b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractUtils.java index 33ecd6d2..d130dcde 100644 --- a/src/main/java/pro/belbix/ethparser/web3/contracts/ContractUtils.java +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/ContractUtils.java @@ -5,19 +5,15 @@ import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; import static pro.belbix.ethparser.web3.contracts.ContractConstants.BSC_BLOCK_NUMBER_18_MARCH_2021; import static pro.belbix.ethparser.web3.contracts.ContractConstants.BSC_FARM_TOKEN; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.CONTROLLERS; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.CONTROLLERS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.ETH_BLOCK_NUMBER_30_AUGUST_2020; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.FARM_TOKEN; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.FULL_PARSABLE_UNI_PAIRS; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.FARM_TOKEN; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV2.FULL_PARSABLE_UNI_PAIRS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.MATIC_BLOCK_NUMBER_06_JUL_2021; import static pro.belbix.ethparser.web3.contracts.ContractConstants.MATIC_FARM_TOKEN; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.ONE_DOLLAR_TOKENS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.ONE_INCH_FACTORY_ADDRESS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.ONE_INCH_FACTORY_BSC; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.ORACLES; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.ORACLES_BY_FACTORY; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.PARSABLE_BANCOR_TRANSACTIONS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_ADDRESSES; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.PARSABLE_BANCOR_TRANSACTIONS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_V0_ADDRESS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.ZERO_ADDRESS; @@ -51,17 +47,24 @@ public static boolean isPsAddress(String address) { if (address == null) { return false; } - return PS_ADDRESSES.contains(address.toLowerCase()); + return ContractConstantsV6.PS_ADDRESSES.contains(address.toLowerCase()); + } + + public static boolean isPsAddress(String address, String network) { + if (address == null) { + return false; + } + return ContractConstantsV3.PS_ADDRESSES_BY_NETWORK.get(network).contains(address.toLowerCase()); } public static boolean isFarmAddress(String address) { - return FARM_TOKEN.equalsIgnoreCase(address) - || BSC_FARM_TOKEN.equalsIgnoreCase(address) - || MATIC_FARM_TOKEN.equalsIgnoreCase(address); + return "0xa0246c9032bc3a600820415ae600c6388619a14d".equalsIgnoreCase(address) + || "0x4B5C23cac08a567ecf0c1fFcA8372A45a5D33743".equalsIgnoreCase(address) + || "0xab0b2ddb9c7e440fac8e140a89c0dbcbf2d7bbff".equalsIgnoreCase(address); } public static boolean isStableCoin(String address) { - return ONE_DOLLAR_TOKENS.contains(address.toLowerCase()); + return ContractConstantsV5.ONE_DOLLAR_TOKENS.contains(address.toLowerCase()); } public static String getBaseAddressInsteadOfZero(String address, String network) { @@ -131,7 +134,15 @@ public static String getControllerAddressByBlockAndNetwork(long block, String ne } public static String getPriceOracle(long block, String network) { - Entry entry = new TreeMap<>(ORACLES.get(network)).floorEntry(block); + Entry entry = new TreeMap<>(ContractConstantsV3.ORACLES.get(network)).floorEntry(block); + if (entry == null) { + return null; + } + return entry.getValue(); + } + + public static String getOldPriceOracle(String network) { + Entry entry = new TreeMap<>(ContractConstantsV3.ORACLES.get(network)).firstEntry(); if (entry == null) { return null; } @@ -139,7 +150,7 @@ public static String getPriceOracle(long block, String network) { } public static String getPriceOracleByFactory(String factory, String network) { - String oracle = ORACLES_BY_FACTORY.get(network).get(factory); + String oracle = ContractConstantsV3.ORACLES_BY_FACTORY.get(network).get(factory); if (oracle == null) { throw new IllegalStateException("Factory " + factory + " not found"); } @@ -208,7 +219,7 @@ public static String getFarmAddress(String network) { } public static boolean isFullParsableLp(String address, String network) { - return FULL_PARSABLE_UNI_PAIRS.get(network).containsKey(address.toLowerCase()); + return ContractConstantsV2.FULL_PARSABLE_UNI_PAIRS.get(network).containsKey(address.toLowerCase()); } public static boolean isFullParsableLpAddressAndDate(String address, int date, diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/DecodeExcludeConstants.java b/src/main/java/pro/belbix/ethparser/web3/contracts/DecodeExcludeConstants.java new file mode 100644 index 00000000..cc1a0b0e --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/DecodeExcludeConstants.java @@ -0,0 +1,59 @@ +package pro.belbix.ethparser.web3.contracts; + +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; + +import java.util.List; +import java.util.Map; + +public interface DecodeExcludeConstants { + Map> DECODE_UNISWAP_V3_EVENT = Map.of( + ETH_NETWORK, List.of( + "0xC1aa3966008ef13B9dD2867D41cA21d9C42932A1".toLowerCase(), + "0xf301aF77793322Bbd6C9e7fa4465499cd2bDecDE".toLowerCase(), + "0x0a1AB972612489a1a362f42559bcd281FBEc0786".toLowerCase(), + "0x5c49E0386215077d1A3eCc425CC30ce34Ec08B60".toLowerCase(), + "0xEA46CfcB43D5274991344cF6F56765e39A7Eae1a".toLowerCase(), + "0xc53DaB6fDD18AF6CD5cF37fDE7C941d368f8664f".toLowerCase(), + "0x65383Abd40f9f831018dF243287F7AE3612c62AC".toLowerCase(), + "0xadB16dF01b9474347E8fffD6032360D3B54627fB".toLowerCase(), + "0x2357685B07469eE80A389819C7A41edCD70cd88C".toLowerCase(), + "0x7Fb7E4aE3a174E24e6Fd545BbF6E9fC5a14162Cc".toLowerCase(), + "0xc3426599Ec933FbF657ee44b53e7f01d83Be1f63".toLowerCase(), + "0x45A78dEbfb4d9E94836dC1680d7FAf32b3994a83".toLowerCase(), + "0x0b4C4EA418Cd596B1204C0dd07E419707149C7C6".toLowerCase(), + "0xC74075F5c9aD58C655a6160bA955B4aCD5dE8d0B".toLowerCase(), + "0x3b2ED6013f961404AbA5a030e20A2AceB486832d".toLowerCase(), + "0xe29385F6B90F25082972B75ccBC69900cE8A176A".toLowerCase(), + "0x3F16b084Ff94c8a3f5A1b60834046f1febD15595".toLowerCase(), + "0xd82964732cF95F904FD814511Be08b86d213232E".toLowerCase(), + "0xeC665d477812C11Bf163841C83322FB4743D1Cfa".toLowerCase(), + "0x8e1de189195a76baF8871f70AdcD090aD06a0B58".toLowerCase(), + "0x6bA287890264cFdAb98100E9855b1423328269D2".toLowerCase(), + "0x50dCcf8F83CCE8aA9168637c2Ec0114ae934F6d1".toLowerCase(), + "0x04EdB1420A01547944eA57bBd4EBeBAE04ac116b".toLowerCase(), + "0x25642078C595A7078f150e5E0486364077aE9eBB".toLowerCase(), + "0x503Ea79B73995Cf0C8d323C17782047ED5cC72B2".toLowerCase(), + "0xC905ccc1a1EC21C8bbE0c0b53d3D048D9055D4bB".toLowerCase(), + "0x970CC1E0Bdb3B29a6A12BDE1954A8509acbC9158".toLowerCase(), + "0x8137ac6dF358fe2D0DFbB1b5aA87C110950A16Cd".toLowerCase(), + "0xFb387177fF9Db15294F7Aebb1ea1e941f55695bc".toLowerCase(), + "0x1851A8fA2ca4d8Fb8B5c56EAC1813Fd890998EFc".toLowerCase(), + "0x7095b06C02B66e4133F7B4b078B2720CB4437408".toLowerCase(), + "0x744F77705749541926294f7b35A63f8691374640".toLowerCase(), + "0x41d0Ab2Bf4E8Da44FEE38232E56b089f3Bb00587".toLowerCase() + ), + BSC_NETWORK, List.of(), + MATIC_NETWORK, List.of() + ); + + Map> DECODE_ONLY_TOPICS = Map.of( + ETH_NETWORK, List.of( + // iFARM + "0x1571eD0bed4D987fe2b498DdBaE7DFA19519F651".toLowerCase() + ), + BSC_NETWORK, List.of(), + MATIC_NETWORK, List.of() + ); +} diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/EthTokenAddresses.java b/src/main/java/pro/belbix/ethparser/web3/contracts/EthTokenAddresses.java index 699a2b3c..fa58f6ea 100644 --- a/src/main/java/pro/belbix/ethparser/web3/contracts/EthTokenAddresses.java +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/EthTokenAddresses.java @@ -1,6 +1,6 @@ package pro.belbix.ethparser.web3.contracts; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.FARM_TOKEN; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.FARM_TOKEN; import static pro.belbix.ethparser.web3.contracts.ContractConstants.ZERO_ADDRESS; import static pro.belbix.ethparser.web3.contracts.models.TokenContract.createTokenContracts; diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/EthVaultAddresses.java b/src/main/java/pro/belbix/ethparser/web3/contracts/EthVaultAddresses.java index 2b09b83b..c22c5c7c 100644 --- a/src/main/java/pro/belbix/ethparser/web3/contracts/EthVaultAddresses.java +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/EthVaultAddresses.java @@ -2,7 +2,6 @@ import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_ADDRESS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_V0_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.iPS_ADDRESS; import static pro.belbix.ethparser.web3.contracts.models.SimpleContract.createContracts; import java.util.List; @@ -80,7 +79,7 @@ private EthVaultAddresses() { new SimpleContract(11745394, "CRV_GUSD", "0xB8671E33fcFC7FEA2F7a3Ea4a117F065ec4b009E"), new SimpleContract(11830928, "CRV_AAVE", "0xc3EF8C4043D7cf1D15B6bb4cd307C844E0BA9d42"), new SimpleContract(11777480, "SUSHI_SUSHI_ETH", "0x5aDe382F38A09A1F8759D06fFE2067992ab5c78e"), - new SimpleContract(11775913, "iPS", iPS_ADDRESS), + new SimpleContract(11775913, "iPS", ContractConstantsV7.iPS_ADDRESS), new SimpleContract(11905238, "ONEINCH_ETH_ONEINCH", "0xFCA949E34ecd9dE519542CF02054DE707Cf361cE"), new SimpleContract(11924821, "UNI_WBTC_KLON", "0xB4E3fC276532f27Bd0F738928Ce083A3b064ba61"), new SimpleContract(11924877, "UNI_WBTC_KBTC", "0x5cd9Db40639013A08d797A839C9BECD6EC5DCD4D"), diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/UniPairType.java b/src/main/java/pro/belbix/ethparser/web3/contracts/UniPairType.java new file mode 100644 index 00000000..82aeeaaf --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/UniPairType.java @@ -0,0 +1,48 @@ +package pro.belbix.ethparser.web3.contracts; + +import java.util.stream.Stream; +import lombok.Getter; + +@Getter +public enum UniPairType { + + // 1inch .. + ONE_INCH("1inch"), + // Balancer .. + BALANCER("Balancer"), + // Curve.fi .. + CURVE("Curve.fi"), + // SushiSwap LP Token + SUSHISWAP("SushiSwap"), + // Pancake .. + PANCACKE("Pancake"), + // PancakeSwap .. + PANCACKE_SWAP("PancakeSwap"), + // Kyber + KYBER("Kyber"), + // Ellipsis.finance .. + ELLIPSIS_FINANCE("Ellipsis.finance"), + // Uniswap V2 + UNISWAP("Uniswap"); + + UniPairType(String name) { + this.name = name.toLowerCase(); + } + + private final String name; + + public static boolean isLpUniPair(String name) { + if (Stream.of(PANCACKE_SWAP).anyMatch(i -> name.toLowerCase().startsWith(i.getName()))) { + return false; + } + return Stream.of(ONE_INCH, SUSHISWAP, UNISWAP, PANCACKE, KYBER).anyMatch(i -> name.toLowerCase().startsWith(i.getName())); + } + + public static boolean isBalancer(String name) { + return Stream.of(BALANCER).anyMatch(i -> name.toLowerCase().startsWith(i.getName())); + } + + public static boolean isCurve(String name) { + return Stream.of(CURVE, ELLIPSIS_FINANCE).anyMatch(i -> name.toLowerCase().startsWith(i.getName())); + } +} diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/UniswapV3Pools.java b/src/main/java/pro/belbix/ethparser/web3/contracts/UniswapV3Pools.java new file mode 100644 index 00000000..2414c145 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/UniswapV3Pools.java @@ -0,0 +1,131 @@ +package pro.belbix.ethparser.web3.contracts; + +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; + +import java.util.AbstractMap; +import java.util.List; +import java.util.Map; + +public interface UniswapV3Pools { + + Map> EXCLUDED_STABLE_VAULTS = Map.of( + ETH_NETWORK, List.of( + "0x1851A8fA2ca4d8Fb8B5c56EAC1813Fd890998EFc".toLowerCase(), + "0x7095b06C02B66e4133F7B4b078B2720CB4437408".toLowerCase() + ), + MATIC_NETWORK, List.of(), + BSC_NETWORK, List.of() + ); + + + // if you want to add new value, use contract 0x1f98431c8ad98523631ae4a59f267346ea31f984 getPool + Map> VAULTS_TO_POOLS = Map.of( + ETH_NETWORK, Map.ofEntries( + new AbstractMap.SimpleEntry<>( + "0xC1aa3966008ef13B9dD2867D41cA21d9C42932A1".toLowerCase(), + "0xb6c52095F2df5967B2028724BC6389f83637f582"), + new AbstractMap.SimpleEntry<>( + "0xf301aF77793322Bbd6C9e7fa4465499cd2bDecDE".toLowerCase(), + "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8"), + new AbstractMap.SimpleEntry<>( + "0x0a1AB972612489a1a362f42559bcd281FBEc0786".toLowerCase(), + "0x4e68Ccd3E89f51C3074ca5072bbAC773960dFa36"), + new AbstractMap.SimpleEntry<>( + "0x5c49E0386215077d1A3eCc425CC30ce34Ec08B60".toLowerCase(), + "0x4e68Ccd3E89f51C3074ca5072bbAC773960dFa36"), + new AbstractMap.SimpleEntry<>( + "0xEA46CfcB43D5274991344cF6F56765e39A7Eae1a".toLowerCase(), + "0x4e68Ccd3E89f51C3074ca5072bbAC773960dFa36"), + new AbstractMap.SimpleEntry<>( + "0xc53DaB6fDD18AF6CD5cF37fDE7C941d368f8664f".toLowerCase(), + "0x4e68Ccd3E89f51C3074ca5072bbAC773960dFa36"), + new AbstractMap.SimpleEntry<>( + "0x65383Abd40f9f831018dF243287F7AE3612c62AC".toLowerCase(), + "0x7379e81228514a1D2a6Cf7559203998E20598346"), + new AbstractMap.SimpleEntry<>( + "0xadB16dF01b9474347E8fffD6032360D3B54627fB".toLowerCase(), + "0xE2de090153403b0F0401142d5394da897630dCb7"), + new AbstractMap.SimpleEntry<>( + "0x2357685B07469eE80A389819C7A41edCD70cd88C".toLowerCase(), + "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD"), + new AbstractMap.SimpleEntry<>( + "0x7Fb7E4aE3a174E24e6Fd545BbF6E9fC5a14162Cc".toLowerCase(), + "0x7Cf82E7b1Ee2a8A5D629F21a720740eCE2f8b7CD"), + new AbstractMap.SimpleEntry<>( + "0xc3426599Ec933FbF657ee44b53e7f01d83Be1f63".toLowerCase(), + "0xfab26CFa923360fFC8ffc40827faeE5500988E9C"), + new AbstractMap.SimpleEntry<>( + "0x45A78dEbfb4d9E94836dC1680d7FAf32b3994a83".toLowerCase(), + "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8"), + new AbstractMap.SimpleEntry<>( + "0x0b4C4EA418Cd596B1204C0dd07E419707149C7C6".toLowerCase(), + "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8"), + new AbstractMap.SimpleEntry<>( + "0xC74075F5c9aD58C655a6160bA955B4aCD5dE8d0B".toLowerCase(), + "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8"), + new AbstractMap.SimpleEntry<>( + "0x3b2ED6013f961404AbA5a030e20A2AceB486832d".toLowerCase(), + "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8"), + new AbstractMap.SimpleEntry<>( + "0xe29385F6B90F25082972B75ccBC69900cE8A176A".toLowerCase(), + "0xEe4Cf3b78A74aFfa38C6a926282bCd8B5952818d"), + new AbstractMap.SimpleEntry<>( + "0x3F16b084Ff94c8a3f5A1b60834046f1febD15595".toLowerCase(), + "0x1d42064Fc4Beb5F8aAF85F4617AE8b3b5B8Bd801"), + new AbstractMap.SimpleEntry<>( + "0xd82964732cF95F904FD814511Be08b86d213232E".toLowerCase(), + "0x3Fae0F474145A1A771F36bD188D1Cc7057A91B06"), + new AbstractMap.SimpleEntry<>( + "0xeC665d477812C11Bf163841C83322FB4743D1Cfa".toLowerCase(), + "0x3Fae0F474145A1A771F36bD188D1Cc7057A91B06"), + + new AbstractMap.SimpleEntry<>( + "0x8e1de189195a76baF8871f70AdcD090aD06a0B58".toLowerCase(), + "0xeD1d49B6BadA7Bb00cC7C7351138BD959cf6B8ba"), + new AbstractMap.SimpleEntry<>( + "0x6bA287890264cFdAb98100E9855b1423328269D2".toLowerCase(), + "0xD43b29AaF8aD938CfF4F478A0756defFfb329D07"), + new AbstractMap.SimpleEntry<>( + "0x50dCcf8F83CCE8aA9168637c2Ec0114ae934F6d1".toLowerCase(), + "0x6BFe36d9a664289cE04F32E4F83f1566c4712F96"), + new AbstractMap.SimpleEntry<>( + "0x04EdB1420A01547944eA57bBd4EBeBAE04ac116b".toLowerCase(), + "0x9359c87B38DD25192c5f2b07b351ac91C90E6ca7"), + new AbstractMap.SimpleEntry<>( + "0x25642078C595A7078f150e5E0486364077aE9eBB".toLowerCase(), + "0xE73FE82f905E265d26e4a5A3D36d0D03bC4119Fc"), + new AbstractMap.SimpleEntry<>( + "0x503Ea79B73995Cf0C8d323C17782047ED5cC72B2".toLowerCase(), + "0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8"), + new AbstractMap.SimpleEntry<>( + "0xC905ccc1a1EC21C8bbE0c0b53d3D048D9055D4bB".toLowerCase(), + "0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8"), + new AbstractMap.SimpleEntry<>( + "0x970CC1E0Bdb3B29a6A12BDE1954A8509acbC9158".toLowerCase(), + "0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8"), + new AbstractMap.SimpleEntry<>( + "0x8137ac6dF358fe2D0DFbB1b5aA87C110950A16Cd".toLowerCase(), + "0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8"), + new AbstractMap.SimpleEntry<>( + "0xFb387177fF9Db15294F7Aebb1ea1e941f55695bc".toLowerCase(), + "0xa63b490aA077f541c9d64bFc1Cc0db2a752157b5"), + + // exclude UST_USDT + new AbstractMap.SimpleEntry<>( + "0x1851A8fA2ca4d8Fb8B5c56EAC1813Fd890998EFc".toLowerCase(), + ""), + + // exclued BUSD_USDC + new AbstractMap.SimpleEntry<>( + "0x7095b06C02B66e4133F7B4b078B2720CB4437408".toLowerCase(), + ""), + new AbstractMap.SimpleEntry<>( + "0x2CE57694b635f6Ea0087A341654543E12b082538".toLowerCase(), + "0x02DaA5fdd4B474c13A8D6D141471B87FBd2452cd") + ), + BSC_NETWORK, Map.of(), + MATIC_NETWORK, Map.of() + ); +} diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/db/ContractDbService.java b/src/main/java/pro/belbix/ethparser/web3/contracts/db/ContractDbService.java index 9f5fa6ff..ad49a01e 100644 --- a/src/main/java/pro/belbix/ethparser/web3/contracts/db/ContractDbService.java +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/db/ContractDbService.java @@ -26,7 +26,7 @@ import pro.belbix.ethparser.repositories.eth.TokenToUniPairRepository; import pro.belbix.ethparser.repositories.eth.UniPairRepository; import pro.belbix.ethparser.repositories.eth.VaultRepository; -import pro.belbix.ethparser.web3.contracts.ContractConstants; +import pro.belbix.ethparser.web3.contracts.ContractConstantsV7; import pro.belbix.ethparser.web3.contracts.ContractType; import pro.belbix.ethparser.web3.contracts.ContractUtils; @@ -57,8 +57,10 @@ public ContractDbService( } public Optional getContractByAddress(String address, String network) { - return Optional.ofNullable(contractRepository - .findFirstByAddress(address.toLowerCase(), network)); + return contractRepository + .findFirstByAddress(address.toLowerCase(), network, PageRequest.of(0, 1)) + .stream() + .findFirst(); } public Optional getContractByAddressAndType( @@ -241,7 +243,7 @@ public Optional getTokenByAddress(String address, String network) { public List getSubscriptions() { Set contracts = new HashSet<>(Set.of( - ContractConstants.FARM_TOKEN + ContractConstantsV7.FARM_TOKEN )); contracts.addAll(Objects.requireNonNull(getControllerAddressByNetwork(ETH_NETWORK))); contracts.addAll( @@ -257,4 +259,7 @@ public Optional getBaseContractForNetwork(String network) { ContractUtils.getBaseNetworkWrappedTokenAddress(network), network); } + public List findAllVaultsByNetwork(String network) { + return contractRepository.findAllVaultsByNetwork(network); + } } diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/models/BalancerPoolTokenInfo.java b/src/main/java/pro/belbix/ethparser/web3/contracts/models/BalancerPoolTokenInfo.java new file mode 100644 index 00000000..cebabf44 --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/models/BalancerPoolTokenInfo.java @@ -0,0 +1,20 @@ +package pro.belbix.ethparser.web3.contracts.models; + +import java.math.BigInteger; +import java.util.List; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldDefaults; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BalancerPoolTokenInfo { + List address; + List balances; +} diff --git a/src/main/java/pro/belbix/ethparser/web3/contracts/models/CurveTokenInfo.java b/src/main/java/pro/belbix/ethparser/web3/contracts/models/CurveTokenInfo.java new file mode 100644 index 00000000..ca1e28ab --- /dev/null +++ b/src/main/java/pro/belbix/ethparser/web3/contracts/models/CurveTokenInfo.java @@ -0,0 +1,19 @@ +package pro.belbix.ethparser.web3.contracts.models; + +import java.math.BigInteger; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.FieldDefaults; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CurveTokenInfo { + String address; + BigInteger balance; +} diff --git a/src/main/java/pro/belbix/ethparser/web3/deployer/decoder/DeployerDecoder.java b/src/main/java/pro/belbix/ethparser/web3/deployer/decoder/DeployerDecoder.java index 9440ed72..6664331d 100644 --- a/src/main/java/pro/belbix/ethparser/web3/deployer/decoder/DeployerDecoder.java +++ b/src/main/java/pro/belbix/ethparser/web3/deployer/decoder/DeployerDecoder.java @@ -1,6 +1,6 @@ package pro.belbix.ethparser.web3.deployer.decoder; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.DEPLOYERS; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.DEPLOYERS; import java.util.List; import lombok.extern.log4j.Log4j2; diff --git a/src/main/java/pro/belbix/ethparser/web3/deployer/transform/UniqueTypes.java b/src/main/java/pro/belbix/ethparser/web3/deployer/transform/UniqueTypes.java index f0e31e49..bdecfa57 100644 --- a/src/main/java/pro/belbix/ethparser/web3/deployer/transform/UniqueTypes.java +++ b/src/main/java/pro/belbix/ethparser/web3/deployer/transform/UniqueTypes.java @@ -2,7 +2,7 @@ import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_ADDRESS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_V0_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.iPS_ADDRESS; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.iPS_ADDRESS; import java.util.Map; import pro.belbix.ethparser.web3.contracts.ContractType; diff --git a/src/main/java/pro/belbix/ethparser/web3/erc20/db/TransferDBService.java b/src/main/java/pro/belbix/ethparser/web3/erc20/db/TransferDBService.java index 49410eb7..a30c6525 100644 --- a/src/main/java/pro/belbix/ethparser/web3/erc20/db/TransferDBService.java +++ b/src/main/java/pro/belbix/ethparser/web3/erc20/db/TransferDBService.java @@ -1,8 +1,5 @@ package pro.belbix.ethparser.web3.erc20.db; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_V0_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.ZERO_ADDRESS; import static pro.belbix.ethparser.web3.erc20.TransferType.KEEP_OWNERSHIP; import static pro.belbix.ethparser.web3.erc20.TransferType.LP_BUY; import static pro.belbix.ethparser.web3.erc20.TransferType.LP_SELL; @@ -32,10 +29,10 @@ public class TransferDBService { private static final Set notCheckableAddresses = new HashSet<>(); static { - notCheckableAddresses.add("0x8f5adC58b32D4e5Ca02EAC0E293D35855999436C"); // st_ps - notCheckableAddresses.add(PS_ADDRESS); //ps - notCheckableAddresses.add(PS_V0_ADDRESS); // ps_v0 - notCheckableAddresses.add(ZERO_ADDRESS); + notCheckableAddresses.add("0x8f5adC58b32D4e5Ca02EAC0E293D35855999436C".toLowerCase()); // st_ps + notCheckableAddresses.add("0x25550Cccbd68533Fa04bFD3e3AC4D09f9e00Fc50".toLowerCase()); //ps + notCheckableAddresses.add("0x59258F4e15A5fC74A7284055A8094F58108dbD4f".toLowerCase()); // ps_v0 + notCheckableAddresses.add("0x0000000000000000000000000000000000000000".toLowerCase()); } private final Pageable limitOne = PageRequest.of(0, 1); diff --git a/src/main/java/pro/belbix/ethparser/web3/erc20/parser/TransferParser.java b/src/main/java/pro/belbix/ethparser/web3/erc20/parser/TransferParser.java index 99619e98..454e8777 100644 --- a/src/main/java/pro/belbix/ethparser/web3/erc20/parser/TransferParser.java +++ b/src/main/java/pro/belbix/ethparser/web3/erc20/parser/TransferParser.java @@ -13,7 +13,6 @@ import pro.belbix.ethparser.model.tx.TokenTx; import pro.belbix.ethparser.properties.AppProperties; import pro.belbix.ethparser.properties.NetworkProperties; -import pro.belbix.ethparser.repositories.ErrorsRepository; import pro.belbix.ethparser.web3.EthBlockService; import pro.belbix.ethparser.web3.ParserInfo; import pro.belbix.ethparser.web3.Web3Functions; diff --git a/src/main/java/pro/belbix/ethparser/web3/harvest/db/VaultActionsDBService.java b/src/main/java/pro/belbix/ethparser/web3/harvest/db/VaultActionsDBService.java index 2ce75173..c97604b1 100644 --- a/src/main/java/pro/belbix/ethparser/web3/harvest/db/VaultActionsDBService.java +++ b/src/main/java/pro/belbix/ethparser/web3/harvest/db/VaultActionsDBService.java @@ -5,8 +5,6 @@ import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.MATIC_BLOCK_NUMBER_06_JUL_2021; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.iPS_ADDRESS; import com.fasterxml.jackson.databind.ObjectMapper; import java.math.BigInteger; @@ -24,6 +22,7 @@ import pro.belbix.ethparser.properties.AppProperties; import pro.belbix.ethparser.repositories.v0.HarvestRepository; import pro.belbix.ethparser.repositories.v0.HarvestTvlRepository; +import pro.belbix.ethparser.web3.contracts.ContractConstantsV7; import pro.belbix.ethparser.web3.contracts.ContractUtils; import pro.belbix.ethparser.web3.contracts.db.ContractDbService; import pro.belbix.ethparser.web3.prices.PriceProvider; @@ -31,6 +30,7 @@ @Service @Log4j2 public class VaultActionsDBService { + private final static ObjectMapper objectMapper = new ObjectMapper(); private final HarvestRepository harvestRepository; @@ -91,7 +91,7 @@ public void fillOwnersCount(HarvestDTO dto) { contractDbService.getAllVaults(dto.getNetwork()).stream() .map(v -> v.getContract().getAddress().toLowerCase()) .filter(v -> !ContractUtils.isPsAddress(v)) - .filter(v -> !v.equalsIgnoreCase(iPS_ADDRESS)) + .filter(v -> !v.equalsIgnoreCase(ContractConstantsV7.iPS_ADDRESS)) .collect(Collectors.toList()), dto.getBlockDate(), dto.getNetwork() @@ -195,6 +195,9 @@ private double calculateActualTvl(HarvestDTO dto, Double farmPrice) { } catch (Exception ignored) { } if (tvl == 0.0) { + if (dto.getLastUsdTvl() == null) { + return 0; + } return dto.getLastUsdTvl(); } if (Double.isInfinite(tvl) || Double.isNaN(tvl)) { @@ -212,8 +215,7 @@ public BigInteger lastBlock(String network) { return BigInteger.valueOf(5993570L); } else if (MATIC_NETWORK.equals(network)) { return BigInteger.valueOf(16566542L); - } - else { + } else { return new BigInteger("0"); } } diff --git a/src/main/java/pro/belbix/ethparser/web3/harvest/parser/HardWorkParser.java b/src/main/java/pro/belbix/ethparser/web3/harvest/parser/HardWorkParser.java index b7450af3..828ffa87 100644 --- a/src/main/java/pro/belbix/ethparser/web3/harvest/parser/HardWorkParser.java +++ b/src/main/java/pro/belbix/ethparser/web3/harvest/parser/HardWorkParser.java @@ -205,6 +205,8 @@ public HardWorkDTO parse(Log ethLog, String network) { // // skip old strategies // return null; // } + + log.info("OldSharePrice: {}, NewSharePrice: {}", tx.getOldSharePrice(), tx.getNewSharePrice()); HardWorkDTO dto = new HardWorkDTO(); dto.setNetwork(network); dto.setId(tx.getHash() + "_" + tx.getLogId()); @@ -356,7 +358,7 @@ private void parseRewardAddedEventsEth( return; } double reward = tx.getReward().doubleValue() / D18; - + log.info("parseRewardAddedEventsEth: {} {} reward is {}", dto.getVaultAddress(), dto.getNetwork(), reward); // AutoStake strategies have two RewardAdded events - first for PS and second for stake contract if (autoStake && dto.getFarmBuyback() != 0) { // in this case it is second reward for strategy @@ -364,6 +366,7 @@ private void parseRewardAddedEventsEth( / (1 - requireNonNullElse(dto.getProfitSharingRate(), defaultPsDenominator(network))); // full reward dto.setFullRewardUsd(fullReward); + log.info("fullReward: {}", fullReward); } else { double farmBuybackMultiplier = (1 - requireNonNullElse(dto.getProfitSharingRate(), defaultPsDenominator(network))) @@ -373,6 +376,7 @@ private void parseRewardAddedEventsEth( // PS pool reward dto.setFarmBuyback(reward + (reward * farmBuybackMultiplier)); + log.info("It's autoStake - {}", autoStake); // for non AutoStake strategy we will not have accurate data for strategy reward // just calculate aprox value based on PS reward if (!autoStake) { @@ -380,6 +384,7 @@ private void parseRewardAddedEventsEth( / requireNonNullElse(dto.getProfitSharingRate(), defaultPsDenominator(network))); // full reward dto.setFullRewardUsd(fullReward); + log.info("fullReward: {}", fullReward); } } diff --git a/src/main/java/pro/belbix/ethparser/web3/harvest/parser/RewardParser.java b/src/main/java/pro/belbix/ethparser/web3/harvest/parser/RewardParser.java index 0ae6c720..2119bdb0 100644 --- a/src/main/java/pro/belbix/ethparser/web3/harvest/parser/RewardParser.java +++ b/src/main/java/pro/belbix/ethparser/web3/harvest/parser/RewardParser.java @@ -5,10 +5,10 @@ import static pro.belbix.ethparser.web3.abi.FunctionsNames.PERIOD_FINISH; import static pro.belbix.ethparser.web3.abi.FunctionsNames.REWARD_RATE; import static pro.belbix.ethparser.web3.contracts.ContractConstants.D18; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.NOTIFY_HELPER; import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_ADDRESS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_V0_ADDRESS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.ST_PS_ADDRESS; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.NOTIFY_HELPER; import static pro.belbix.ethparser.web3.contracts.ContractType.POOL; import java.math.BigDecimal; @@ -23,7 +23,6 @@ import pro.belbix.ethparser.model.tx.HarvestTx; import pro.belbix.ethparser.properties.AppProperties; import pro.belbix.ethparser.properties.NetworkProperties; -import pro.belbix.ethparser.repositories.ErrorsRepository; import pro.belbix.ethparser.web3.EthBlockService; import pro.belbix.ethparser.web3.ParserInfo; import pro.belbix.ethparser.web3.Web3Functions; diff --git a/src/main/java/pro/belbix/ethparser/web3/harvest/parser/VaultActionsParser.java b/src/main/java/pro/belbix/ethparser/web3/harvest/parser/VaultActionsParser.java index 3c965bb0..a1c5bbb1 100644 --- a/src/main/java/pro/belbix/ethparser/web3/harvest/parser/VaultActionsParser.java +++ b/src/main/java/pro/belbix/ethparser/web3/harvest/parser/VaultActionsParser.java @@ -5,7 +5,6 @@ import static pro.belbix.ethparser.web3.abi.FunctionsNames.TOTAL_SUPPLY; import static pro.belbix.ethparser.web3.abi.FunctionsNames.UNDERLYING; import static pro.belbix.ethparser.web3.contracts.ContractConstants.ZERO_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.iPS_ADDRESS; import static pro.belbix.ethparser.web3.contracts.ContractType.POOL; import static pro.belbix.ethparser.web3.contracts.ContractType.VAULT; @@ -27,7 +26,6 @@ import pro.belbix.ethparser.model.tx.HarvestTx; import pro.belbix.ethparser.properties.AppProperties; import pro.belbix.ethparser.properties.NetworkProperties; -import pro.belbix.ethparser.repositories.ErrorsRepository; import pro.belbix.ethparser.web3.EthBlockService; import pro.belbix.ethparser.web3.ParserInfo; import pro.belbix.ethparser.web3.Web3Functions; @@ -35,6 +33,7 @@ import pro.belbix.ethparser.web3.Web3Subscriber; import pro.belbix.ethparser.web3.abi.FunctionService; import pro.belbix.ethparser.web3.abi.FunctionsUtils; +import pro.belbix.ethparser.web3.contracts.ContractConstantsV7; import pro.belbix.ethparser.web3.contracts.ContractType; import pro.belbix.ethparser.web3.contracts.ContractUtils; import pro.belbix.ethparser.web3.contracts.db.ContractDbService; @@ -202,7 +201,7 @@ private boolean parsePs(HarvestTx harvestTx, String network) { TransactionReceipt receipt = web3Functions .fetchTransactionReceipt(harvestTx.getHash(), network); String vault = receipt.getTo(); - if (vault.equalsIgnoreCase(iPS_ADDRESS)) { + if (vault.equalsIgnoreCase(ContractConstantsV7.iPS_ADDRESS)) { return false; //not count deposit from iPS } harvestTx.setMethodName("Deposit"); @@ -378,7 +377,7 @@ private void fillUsdValues(HarvestDTO dto, String vaultHash, String network) { dto.setLastTvl(vault); dto.setLastUsdTvl((double) Math.round(vault * priceUnderlying)); dto.setUsdAmount((long) (priceUnderlying * dto.getAmount() * dto.getSharePrice())); - if (iPS_ADDRESS.equalsIgnoreCase(dto.getVaultAddress())) { + if (ContractConstantsV7.iPS_ADDRESS.equalsIgnoreCase(dto.getVaultAddress())) { dto.setTotalAmount(farmTotalAmount(dto.getBlock(), network)); } } diff --git a/src/main/java/pro/belbix/ethparser/web3/prices/LPSeeker.java b/src/main/java/pro/belbix/ethparser/web3/prices/LPSeeker.java index 7f6475b6..b11bab2d 100644 --- a/src/main/java/pro/belbix/ethparser/web3/prices/LPSeeker.java +++ b/src/main/java/pro/belbix/ethparser/web3/prices/LPSeeker.java @@ -4,7 +4,7 @@ import static pro.belbix.ethparser.web3.abi.FunctionsNames.GET_PAIR; import static pro.belbix.ethparser.web3.abi.FunctionsNames.GET_RESERVES; import static pro.belbix.ethparser.web3.abi.FunctionsNames.TOKEN0; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.UNI_FACTORIES; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV8.UNI_FACTORIES; import static pro.belbix.ethparser.web3.contracts.ContractConstants.ZERO_ADDRESS; import java.math.BigDecimal; @@ -20,7 +20,7 @@ import org.web3j.abi.datatypes.Function; import org.web3j.protocol.ObjectMapperFactory; import pro.belbix.ethparser.web3.abi.FunctionsUtils; -import pro.belbix.ethparser.web3.contracts.ContractConstants; +import pro.belbix.ethparser.web3.contracts.ContractConstantsV8; import pro.belbix.ethparser.web3.contracts.ContractUtils; import pro.belbix.ethparser.web3.contracts.db.ContractDbService; import pro.belbix.ethparser.web3.contracts.models.PureEthContractInfo; @@ -70,7 +70,7 @@ private String getUniLargestPool( String network, List contracts ) { - Set tokenList = ContractConstants.KEY_TOKENS.get(network); + Set tokenList = ContractConstantsV8.KEY_TOKENS.get(network); TreeMap pairsLiquidity = new TreeMap<>(); for (String keyToken : tokenList) { for (String factory : UNI_FACTORIES.get(network).keySet()) { @@ -103,7 +103,7 @@ private boolean isEligibleKeyToken(String tokenAddress, String keyToken, String return false; } boolean tokenIsKeyToken = - ContractConstants.KEY_TOKENS.get(network).contains(tokenAddress.toLowerCase()); + ContractConstantsV8.KEY_TOKENS.get(network).contains(tokenAddress.toLowerCase()); boolean keyTokenIsStablecoin = ContractUtils.isStableCoin(keyToken); // for avoid recursion we should have keyToken -> Stablecoin pairs only return !tokenIsKeyToken || keyTokenIsStablecoin; diff --git a/src/main/java/pro/belbix/ethparser/web3/prices/PriceOracle.java b/src/main/java/pro/belbix/ethparser/web3/prices/PriceOracle.java index 384f9a58..df9f1aba 100644 --- a/src/main/java/pro/belbix/ethparser/web3/prices/PriceOracle.java +++ b/src/main/java/pro/belbix/ethparser/web3/prices/PriceOracle.java @@ -17,7 +17,7 @@ import org.web3j.protocol.ObjectMapperFactory; import pro.belbix.ethparser.properties.AppProperties; import pro.belbix.ethparser.web3.abi.FunctionsUtils; -import pro.belbix.ethparser.web3.contracts.ContractConstants; +import pro.belbix.ethparser.web3.contracts.ContractConstantsV8; import pro.belbix.ethparser.web3.contracts.ContractUtils; @Service @@ -48,13 +48,23 @@ public double getPriceForCoinOnChain(String tokenAdr, Long block, String network "Can't fetch price for " + tokenAdr)) .doubleValue(); + if (price == 0 && BSC_NETWORK.equals(network)) { + log.error("Can not get price by oracle address - {}, try use old oracle", oracleAddress); + oracleAddress = ContractUtils.getOldPriceOracle(network); + price = functionsUtils + .callIntByNameWithAddressArg(GET_PRICE, tokenAdr, oracleAddress, block, network) + .orElseThrow(() -> new IllegalStateException( + "Can't fetch price for " + tokenAdr)) + .doubleValue(); + } + return price / D18; } public String getLargestKeyToken(String tokenAddress, long block, String network) { String oracleAddress = getOracleAddress(tokenAddress, block, network); - List
tokenList = ContractConstants.KEY_TOKENS.get(network).stream() + List
tokenList = ContractConstantsV8.KEY_TOKENS.get(network).stream() .map(Address::new) .collect(Collectors.toList()); try { @@ -86,7 +96,7 @@ public String getLargestKeyToken(String tokenAddress, long block, String network } } - private String getOracleAddress(String tokenAddress, long block, String network) { + public String getOracleAddress(String tokenAddress, long block, String network) { if (BSC_NETWORK.equals(network)) { Optional factory = functionsUtils.callStrByName("factory", tokenAddress, block, network); diff --git a/src/main/java/pro/belbix/ethparser/web3/prices/PriceProvider.java b/src/main/java/pro/belbix/ethparser/web3/prices/PriceProvider.java index c5f9b3d0..ef71ff82 100644 --- a/src/main/java/pro/belbix/ethparser/web3/prices/PriceProvider.java +++ b/src/main/java/pro/belbix/ethparser/web3/prices/PriceProvider.java @@ -1,61 +1,186 @@ package pro.belbix.ethparser.web3.prices; import static java.util.Objects.requireNonNullElse; +import static pro.belbix.ethparser.web3.abi.FunctionsNames.GET_VAULT; +import static pro.belbix.ethparser.web3.abi.FunctionsNames.MINTER; +import static pro.belbix.ethparser.web3.abi.FunctionsNames.NAME; import static pro.belbix.ethparser.web3.abi.FunctionsNames.TOTAL_SUPPLY; +import static pro.belbix.ethparser.web3.contracts.ContractConstants.CURVE_ZERO_ADDRESS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.ZERO_ADDRESS; import static pro.belbix.ethparser.web3.contracts.ContractUtils.getBaseNetworkWrappedTokenAddress; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; +import lombok.AllArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.web3j.tuples.generated.Tuple2; import pro.belbix.ethparser.entity.contracts.ContractEntity; +import pro.belbix.ethparser.error.exceptions.CanNotFetchPriceException; +import pro.belbix.ethparser.model.TokenInfo; import pro.belbix.ethparser.properties.AppProperties; -import pro.belbix.ethparser.repositories.v0.PriceRepository; +import pro.belbix.ethparser.web3.EthBlockService; +import pro.belbix.ethparser.web3.abi.FunctionsNames; import pro.belbix.ethparser.web3.abi.FunctionsUtils; +import pro.belbix.ethparser.web3.contracts.ContractConstantsV4; +import pro.belbix.ethparser.web3.contracts.ContractConstantsV7; import pro.belbix.ethparser.web3.contracts.ContractType; import pro.belbix.ethparser.web3.contracts.ContractUtils; +import pro.belbix.ethparser.web3.contracts.UniPairType; +import pro.belbix.ethparser.web3.contracts.UniswapV3Pools; import pro.belbix.ethparser.web3.contracts.db.ContractDbService; @Service @Log4j2 +@AllArgsConstructor public class PriceProvider { - private static final boolean CHECK_BLOCK_CREATED = false; + private final static BigDecimal UNISWAP_V3_VALUE = BigDecimal.valueOf(2).pow(96).pow(2); + private final static Double DEFAULT_RETURN_PRICE = 1D; + private final static int DEFAULT_CURVE_SIZE = 3; + private final static int DEFAULT_DECIMAL = 18; + private final static BigInteger DEFAULT_POW = new BigInteger("10"); + private final static boolean CHECK_BLOCK_CREATED = false; private final Map> lastPrices = new HashMap<>(); private final FunctionsUtils functionsUtils; private final AppProperties appProperties; private final PriceOracle priceOracle; private final ContractDbService contractDbService; + private final EthBlockService ethBlockService; - public PriceProvider(FunctionsUtils functionsUtils, PriceRepository priceRepository, - AppProperties appProperties, PriceOracle priceOracle, - ContractDbService contractDbService) { - this.functionsUtils = functionsUtils; - this.appProperties = appProperties; - this.priceOracle = priceOracle; - this.contractDbService = contractDbService; - } public double getLpTokenUsdPrice(String lpAddress, double amount, long block, String network) { return getLpTokenUsdPriceFromEth(lpAddress, amount, block, network); } + public double getBalancerPrice(String address, Long block, String network) { + var poolId = functionsUtils.getPoolId(address, block, network) + .orElseThrow(() -> { + log.error("Can not get balancer poolId for {} {}", address, network); + throw new CanNotFetchPriceException(); + }); + + var vaultAddress = functionsUtils.callAddressByName(GET_VAULT, address, block, network) + .orElseThrow(() -> { + log.error("Can not get balancer vault for {} {}", address, network); + throw new CanNotFetchPriceException(); + }); + + var poolTokenInfo = functionsUtils.getPoolTokens(vaultAddress, block, network, poolId) + .orElseThrow(() -> { + log.error("Can not get balancer poolTokenInfo for {} {}", address, network); + throw new CanNotFetchPriceException(); + }); + + var totalSupply = functionsUtils.callIntByName(TOTAL_SUPPLY, address, block, network) + .orElseThrow(() -> { + log.error("Can not get totalSupply for {} {}", address, network); + throw new CanNotFetchPriceException(); + }).doubleValue(); + + var price = 0d; + + for (int i = 0; i < poolTokenInfo.getAddress().size(); i++) { + var tokenAddress = poolTokenInfo.getAddress().get(i); + var tokenDecimal = functionsUtils.callIntByName(FunctionsNames.DECIMALS, tokenAddress, block, network) + .orElseThrow(() -> { + log.error("Can not get token decimal for {} {}", tokenAddress, network); + throw new CanNotFetchPriceException(); + }).intValue(); + + var tokenPrice = getPriceForCoin(tokenAddress, block, network); + if (tokenPrice == 0) { + log.error("Can not fetch price for balancer {} {}", address, network); + return 0; + } + + price = price + tokenPrice * normalizePrecision(poolTokenInfo.getBalances().get(i).doubleValue(), tokenDecimal); + } + + return price / totalSupply; + } + + // TODO 0xc27bfe32e0a934a12681c1b35acf0dba0e7460ba has 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee coin address + public double getCurvePrice(String address, Long block, String network) { + var minter = functionsUtils.callAddressByName(MINTER, address, block, network) + .orElse(null); + + if (minter == null) { + var checkAddress = functionsUtils.getCurveTokenInfo(address, block, network, 0); + if (checkAddress.isEmpty()) { + return getPriceForCoin(address, block, network); + } + minter = address; + } + + var size = functionsUtils.getCurveVaultSize(minter, network);; + var decimal = functionsUtils.callIntByName(FunctionsNames.DECIMALS, address, block, network) + .orElseThrow(() -> { + log.error("Can not get decimal for {} {}", address, network); + throw new CanNotFetchPriceException(); + }).intValue(); + + var totalSupply = functionsUtils.callIntByName(TOTAL_SUPPLY, address, block, network) + .orElseThrow(() -> { + log.error("Can not get totalSupply for {} {}", address, network); + throw new CanNotFetchPriceException(); + }).doubleValue(); + + var tvl = Double.valueOf(0); + for (int i = 0; i < size; i++) { + var index = i; + var tokenInfo = functionsUtils.getCurveTokenInfo(minter, block, network, i) + .orElseThrow(() -> { + log.error("Can not get tokeInfo for {} {}, index - {}", address, network, index); + throw new CanNotFetchPriceException(); + }); + + if (tokenInfo.getAddress().equalsIgnoreCase(ZERO_ADDRESS)) { + return 1; + } + + if (tokenInfo.getAddress().equalsIgnoreCase(CURVE_ZERO_ADDRESS)) { + tokenInfo.setAddress(ContractUtils.getBaseNetworkWrappedTokenAddress(network)); + } + + var tokenDecimal = functionsUtils.callIntByName(FunctionsNames.DECIMALS, tokenInfo.getAddress(), block, network) + .orElseThrow(() -> { + log.error("Can not get decimal for {} {}", tokenInfo.getAddress(), network); + throw new CanNotFetchPriceException(); + }).intValue(); + + var tokenPrice = getPriceForCoinOrCurve(tokenInfo.getAddress(), block, network); + + if (tokenPrice == 0) { + log.error("Can not fetch price for curve {} {}", address, network); + return 0; + } + + var balance = normalizePrecision(tokenInfo.getBalance().doubleValue(), tokenDecimal); + tvl = tvl + tokenPrice * balance / DEFAULT_POW.pow(DEFAULT_DECIMAL).doubleValue(); + } + + return tvl * DEFAULT_POW.pow(DEFAULT_DECIMAL).doubleValue() / normalizePrecision(totalSupply, decimal) ; + } + public double getLpTokenUsdPriceFromEth(String lpAddress, double amount, long block, String network) { if (appProperties.isOnlyApi()) { return 0.0; } - if (PriceOracle.isAvailable(block, network)) { + if (PriceOracle.isAvailable(block, network) && functionsUtils.canGetTokenPrice(lpAddress, priceOracle.getOracleAddress(lpAddress, block, network), block, network)) { return amount * priceOracle.getPriceForCoinOnChain(lpAddress, block, network); } + log.info("Oracle not deployed yet, use direct calculation for prices"); Tuple2 lpPooled = functionsUtils.callReserves( lpAddress, block, network); @@ -72,6 +197,35 @@ public double getLpTokenUsdPriceFromEth(String lpAddress, double amount, long bl return usdValue; } + public double getUniswapV3Price(String address, Long block, String network) { + + if (UniswapV3Pools.EXCLUDED_STABLE_VAULTS.get(network).contains(address.toLowerCase())) { + return 1; + } + + var poolAddress = UniswapV3Pools.VAULTS_TO_POOLS.get(network).get(address.toLowerCase()); + if (poolAddress == null) { + log.error("Can not find uniSwapV3 pool address"); + throw new IllegalStateException(); + } + + var sqrt = functionsUtils.getSqrtPriceX96(poolAddress, block, network) + .orElseThrow(() -> new IllegalStateException("Can not get SqrtPriceX96 for : " + poolAddress)); + + var tokens = functionsUtils.callTokensForSwapPlatform(poolAddress, network); + + var decimalOne = functionsUtils.getDecimal(tokens.component1(), network); + var decimalTwo = functionsUtils.getDecimal(tokens.component2(), network); + + sqrt = sqrt.pow(2); + + var decimal = BigDecimal.valueOf(10).pow(decimalOne).divide(BigDecimal.valueOf(10).pow(decimalTwo)); + var price = sqrt.doubleValue() * decimal.doubleValue() / UNISWAP_V3_VALUE.doubleValue(); + + var tokenTwoPrice = getPriceForCoin(tokens.component2(), block, network); + return price * tokenTwoPrice; + } + private double calculateLpTokenPrice(String lpAddress, Tuple2 lpPooled, double lpBalance, @@ -79,8 +233,14 @@ private double calculateLpTokenPrice(String lpAddress, long block, String network ) { - Tuple2 tokensAdr = contractDbService - .tokenAddressesByUniPairAddress(lpAddress, network); + Tuple2 tokensAdr = null; + + try { + tokensAdr = contractDbService + .tokenAddressesByUniPairAddress(lpAddress, network); + } catch (IllegalStateException e) { + tokensAdr = functionsUtils.callTokensForSwapPlatform(lpAddress, network); + } double positionFraction = amount / lpBalance; @@ -157,6 +317,22 @@ private double getPriceForCoinFromEth(String address, Long block, String network if (appProperties.isOnlyApi()) { return 0.0; } + + if (ContractConstantsV4.EXCLUDE_JARVIS_STABLECOIN.get(network).stream().anyMatch(i -> i.equals(address.toLowerCase()))) { + return DEFAULT_RETURN_PRICE; + } + + final TokenInfo tokenInfo = TokenInfo.builder() + .address(address) + .network(network) + .build(); + + if (ContractConstantsV7.COIN_PRICE_IN_OTHER_CHAIN.containsKey(tokenInfo)) { + var tokenInfoInOtherChain = ContractConstantsV7.COIN_PRICE_IN_OTHER_CHAIN.get(tokenInfo); + var otherBlockChain = ethBlockService.getBlockFromOtherChain(block, network, tokenInfoInOtherChain.getNetwork()); + return priceOracle.getPriceForCoinOnChain(tokenInfoInOtherChain.getAddress(), otherBlockChain, tokenInfoInOtherChain.getNetwork()); + } + if (PriceOracle.isAvailable(block, network)) { return priceOracle.getPriceForCoinOnChain(address, block, network); } @@ -203,7 +379,7 @@ private double getPriceForCoinFromEthLegacy( } } catch (Exception ignore) { } - log.error("Not found lp for {}", address); + log.error("Not found lp for {}, block: {}, network: {}", address, block, network); return 0; } @@ -291,4 +467,17 @@ public boolean isDivisionSequenceSecondDividesFirst( } } + private double normalizePrecision(Double amount, int decimal) { + return amount * DEFAULT_POW.pow(DEFAULT_DECIMAL).doubleValue() / DEFAULT_POW.pow(decimal).doubleValue(); + } + + private Double getPriceForCoinOrCurve(String address, Long block, String network) { + var name = functionsUtils.callStrByName(NAME, address, block, network) + .orElse(StringUtils.EMPTY); + if (UniPairType.isCurve(name)) { + return getCurvePrice(address, block, network); + } + + return getPriceForCoin(address, block, network); + } } diff --git a/src/main/java/pro/belbix/ethparser/web3/uniswap/parser/UniswapLpLogParser.java b/src/main/java/pro/belbix/ethparser/web3/uniswap/parser/UniswapLpLogParser.java index 3ff3ade8..6e7f451e 100644 --- a/src/main/java/pro/belbix/ethparser/web3/uniswap/parser/UniswapLpLogParser.java +++ b/src/main/java/pro/belbix/ethparser/web3/uniswap/parser/UniswapLpLogParser.java @@ -33,6 +33,8 @@ @Log4j2 public class UniswapLpLogParser extends Web3Parser { + private final static String SUCCESS_TX_RESULT = "0x1"; + private final UniswapLpLogDecoder uniswapLpLogDecoder = new UniswapLpLogDecoder(); private final Web3Functions web3Functions; private final Web3Subscriber web3Subscriber; @@ -117,6 +119,14 @@ public UniswapDTO parse(Log ethLog, String network) { //enrich owner TransactionReceipt receipt = web3Functions.fetchTransactionReceipt(dto.getHash(), ETH_NETWORK); + if (receipt == null) { + log.error("TransactionReceipt is null for hash: {}", dto.getHash()); + throw new IllegalStateException("TransactionReceipt is null"); + } + + if (!receipt.getStatus().equals(SUCCESS_TX_RESULT)) { + log.warn("Tx {} status is not success", dto.getHash()); + } dto.setOwner(receipt.getFrom()); //enrich date diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7b355191..1e4300da 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -59,5 +59,31 @@ management: enabled: true cache.time-to-live: 10s +external: + covalenthq: + url: https://api.covalenthq.com/v1/ + key: ckey_91bf3242cc5042599af1094b965 + harvest: + url: https://api-ui.harvest.finance/ + key: 41e90ced-d559-4433-b390-af424fdc76d6 + + +task: + pool: + fixedRate: 3600000 # Each hour + enable: false + vault: + fixedRate: 3600000 # Each hour + enable: false + uni-pair: + fixedRate: 86400000 # Everyday + enable: false + transaction: + max-thread-size: 30 + fixedRate: 86400000 # Everyday + enable: false + info: + fixedRate: 3600000 # Each hour + enable: false diff --git a/src/test/java/pro/belbix/ethparser/controllers/ProfitControllerTest.java b/src/test/java/pro/belbix/ethparser/controllers/ProfitControllerTest.java new file mode 100644 index 00000000..99dd9d00 --- /dev/null +++ b/src/test/java/pro/belbix/ethparser/controllers/ProfitControllerTest.java @@ -0,0 +1,53 @@ +package pro.belbix.ethparser.controllers; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import pro.belbix.ethparser.Application; +import pro.belbix.ethparser.repositories.v0.HarvestRepository; + +@SpringBootTest(classes = Application.class) +@ContextConfiguration +@AutoConfigureMockMvc +public class ProfitControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private HarvestRepository harvestRepository; + + + @ParameterizedTest(name = "{index} => address={0}, start={1}, end={2}") + @CsvSource({ + "0x858128d2f83dbb226b6cf29bffe5e7e129c3a128, 1613588289, 1615434501", + "0xf1b85414473eccd659deeacee347bbc17171dd32, 1613588289, 1640168651" + }) + public void testCalculation(String address, String start, String end) throws Exception { + + this.mockMvc.perform( + get("/api/profit/total?address=" + address + "&start=" + start + "&end=" + end)) + .andExpect(status().isOk()); + } + + @ParameterizedTest(name = "{index} => address={0}, start={1}, end={2}") + @CsvSource({ + "0xe7c9d242137896741b70cefef701bbb4dcb158ec, matic, 1613588289, 1640168651" + }) + public void testCalculationByVault(String address, String network, String start, String end) + throws Exception { + + this.mockMvc.perform( + get("/api/profit/vault?address=" + address + + "&network=" + network + "&start=" + start + "&end=" + end)) + .andExpect(status().isOk()); + } + +} diff --git a/src/test/java/pro/belbix/ethparser/service/AbiProviderServiceTest.java b/src/test/java/pro/belbix/ethparser/service/AbiProviderServiceTest.java index 6b408e81..124eb5b8 100644 --- a/src/test/java/pro/belbix/ethparser/service/AbiProviderServiceTest.java +++ b/src/test/java/pro/belbix/ethparser/service/AbiProviderServiceTest.java @@ -1,16 +1,42 @@ package pro.belbix.ethparser.service; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static pro.belbix.ethparser.service.AbiProviderService.BSC_NETWORK; import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; +import static pro.belbix.ethparser.service.AbiProviderService.MATIC_NETWORK; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import pro.belbix.ethparser.Application; +import pro.belbix.ethparser.properties.NetworkProperties; +import pro.belbix.ethparser.web3.EthBlockService; +@SpringBootTest(classes = Application.class) +@ContextConfiguration class AbiProviderServiceTest { + @Autowired + EthBlockService ethBlockService; + @Autowired + AbiProviderService abiProviderService; + @Autowired + NetworkProperties networkProperties; + @Test void contractSourceCode() { AbiProviderService abiProviderService = new AbiProviderService(); assertNotNull(abiProviderService.contractSourceCode( "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413", "YourApiKeyToken", ETH_NETWORK)); } + + @Test + void getBlockInDifferentChainAtTheSameTimeTest() { + var timestamp = ethBlockService.getTimestampSecForBlock(25561696, MATIC_NETWORK); + var result = abiProviderService.getBlockByTimestamp(String.valueOf(timestamp), BSC_NETWORK, networkProperties.get(BSC_NETWORK).getAbiProviderKey()); + + assertEquals(15758669L, result); + } } diff --git a/src/test/java/pro/belbix/ethparser/service/PriceCleanerServiceTest.java b/src/test/java/pro/belbix/ethparser/service/PriceCleanerServiceTest.java new file mode 100644 index 00000000..92e854ed --- /dev/null +++ b/src/test/java/pro/belbix/ethparser/service/PriceCleanerServiceTest.java @@ -0,0 +1,22 @@ +package pro.belbix.ethparser.service; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import pro.belbix.ethparser.Application; + +@SpringBootTest(classes = Application.class) +@ContextConfiguration +public class PriceCleanerServiceTest { + + @Autowired + private PriceCleanerService priceCleanerService; + + @Test + void startPriceCleaner_doesNotThrowJpaSystemException() { + assertDoesNotThrow(() -> priceCleanerService.startPriceCleaner()); + } +} diff --git a/src/test/java/pro/belbix/ethparser/service/external/CovalenthqServiceTest.java b/src/test/java/pro/belbix/ethparser/service/external/CovalenthqServiceTest.java new file mode 100644 index 00000000..05e3aaf5 --- /dev/null +++ b/src/test/java/pro/belbix/ethparser/service/external/CovalenthqServiceTest.java @@ -0,0 +1,92 @@ +package pro.belbix.ethparser.service.external; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static org.apache.http.HttpHeaders.CONTENT_TYPE; +import static org.assertj.core.api.Assertions.assertThat; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; + +import com.github.tomakehurst.wiremock.stubbing.Scenario; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; +import org.springframework.test.context.TestPropertySource; + +@SpringBootTest +@AutoConfigureWireMock(port = 801) +@TestPropertySource(properties = { + "external.covalenthq.url=http://localhost:801/", + "external.covalenthq.key=1" +}) +public class CovalenthqServiceTest { + private static final String TRANSACTION_URL_PART = "/1/address/0x1234/transactions_v2/?quote-currency=USD&format=JSON&block-signed-at-asc=true&no-logs=false&key=1&page-number=0&page-size=3"; + private static final String TRANSACTION_SECOND_PAGE_URL_PART = "/1/address/0x1234/transactions_v2/?quote-currency=USD&format=JSON&block-signed-at-asc=true&no-logs=false&key=1&page-number=1&page-size=3"; + private static final String ADDRESS = "0x1234"; + private static final long BLOCK = 111111; + + @Autowired + private CovalenthqService covalenthqService; + + + @Test + void testNotNull() { + assertThat(covalenthqService).isNotNull(); + } + + @Test + void getCreatedBlockByLastTransaction_successResult() { + stubFor( + get(urlEqualTo(TRANSACTION_URL_PART)) + .willReturn(aResponse() + .withHeader(CONTENT_TYPE, "application/json") + .withBodyFile("transaction-simple-response.json") + ) + ); + var result = covalenthqService.getCreatedBlockByLastTransaction(ADDRESS, ETH_NETWORK); + assertThat(result).isEqualTo(BLOCK); + } + + @Test + void getCreatedBlockByLastTransaction_emptyResult() { + stubFor( + get(urlEqualTo(TRANSACTION_URL_PART)) + .willReturn(aResponse() + .withHeader(CONTENT_TYPE, "application/json") + .withBodyFile("transaction-empty-response.json") + ) + ); + var result = covalenthqService.getCreatedBlockByLastTransaction(ADDRESS, ETH_NETWORK); + assertThat(result).isEqualTo(0); + } + + @Test + void getCreatedBlockByLastTransaction_withTwoCall() { + stubFor( + get(urlEqualTo(TRANSACTION_URL_PART)) + .inScenario("transfer") + .whenScenarioStateIs(Scenario.STARTED) + .willSetStateTo("second") + .willReturn(aResponse() + .withHeader(CONTENT_TYPE, "application/json") + .withBodyFile("transaction-with-transfer-response.json") + ) + ); + stubFor( + get(urlEqualTo(TRANSACTION_SECOND_PAGE_URL_PART)) + .inScenario("transfer") + .whenScenarioStateIs("second") + .willSetStateTo(Scenario.STARTED) + .willReturn(aResponse() + .withHeader(CONTENT_TYPE, "application/json") + .withBodyFile("transaction-simple-response.json") + ) + ); + + var result = covalenthqService.getCreatedBlockByLastTransaction(ADDRESS, ETH_NETWORK); + assertThat(result).isEqualTo(BLOCK); + } + +} diff --git a/src/test/java/pro/belbix/ethparser/web3/PriceProviderTest.java b/src/test/java/pro/belbix/ethparser/web3/PriceProviderTest.java index d9866b49..d65b0404 100644 --- a/src/test/java/pro/belbix/ethparser/web3/PriceProviderTest.java +++ b/src/test/java/pro/belbix/ethparser/web3/PriceProviderTest.java @@ -45,4 +45,30 @@ public void priceForBNB() { double price = priceProvider.getPriceForCoin("WBNB", 6905123, BSC_NETWORK); assertEquals(numberFormat("536.67"), String.format("%.2f", price)); } + + @Test + public void priceForBalancer_80_BAL_20_WETH() { + // B-80BAL-20WETH + double price = priceProvider.getBalancerPrice("0x5c6ee304399dbdb9c8ef030ab642b10820db8f56", 14294907L, ETH_NETWORK); + assertEquals(numberFormat("28.25"), String.format("%.2f", price)); + } + + @Test + public void priceForCurve_Curvefi_USD_BTC_ETH() { + //Curve.fi USD-BTC-ETH + double price = priceProvider.getCurvePrice("0xc4AD29ba4B3c580e6D59105FFf484999997675Ff", 14354885L, ETH_NETWORK); + assertEquals(numberFormat("1459.31"), String.format("%.2f", price)); + } + + @Test + public void getUniswapV3Price_zUSD_WETH() { + double price = priceProvider.getUniswapV3Price("0xC1aa3966008ef13B9dD2867D41cA21d9C42932A1", 14522491L, ETH_NETWORK); + assertEquals(numberFormat("0.35"), String.format("%.2f", price)); + } + + @Test + public void getUniswapV3Price_UST_USDT() { + double price = priceProvider.getUniswapV3Price("0x1851A8fA2ca4d8Fb8B5c56EAC1813Fd890998EFc", 14522491L, ETH_NETWORK); + assertEquals(numberFormat("1.00"), String.format("%.2f", price)); + } } diff --git a/src/test/java/pro/belbix/ethparser/web3/SimpleDecoderTest.java b/src/test/java/pro/belbix/ethparser/web3/SimpleDecoderTest.java new file mode 100644 index 00000000..68752cbd --- /dev/null +++ b/src/test/java/pro/belbix/ethparser/web3/SimpleDecoderTest.java @@ -0,0 +1,68 @@ +package pro.belbix.ethparser.web3; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static pro.belbix.ethparser.entity.profit.CovalenthqVaultTransactionType.DEPOSIT; +import static pro.belbix.ethparser.entity.profit.CovalenthqVaultTransactionType.DEPOSIT_UNI; +import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; + +import java.math.BigInteger; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.web3j.protocol.core.methods.response.Log; + +public class SimpleDecoderTest { + + SimpleDecoder decoder = new SimpleDecoder(); + + + @Test + public void decodeUniswapV3EthLogTest_deposit() { + var log = new Log(); + log.setTopics(List.of( + "0x90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15", + "0x000000000000000000000000b45844826d2757e5bc43518c084e9774d6715f4a" + )); + log.setData("0x00000000000000000000000000000000000000000000005053b0b935131f65ee00000000000000000000000000000000000000000000000000000000578f2940"); + + var result = decoder.decodeEthLogForDepositAndWithdraw(log, ETH_NETWORK, DEPOSIT_UNI.paramSize).orElseThrow(); + + assertThat(result).isNotNull(); + assertThat(result.size()).isEqualTo(3); + assertThat(result.get(2).getValue()).isEqualTo(BigInteger.valueOf(1469000000)); + } + + // txHash = 0x318bd2dff3c071b883cbaee68bde0a306f704c445ceea404916546fbc456199b + @Test + public void decodeOnlyTopics_deposit() { + var log = new Log(); + log.setTopics(List.of( + "0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c", + "0x0000000000000000000000008369e7900ff2359bb36ef1c40a60e5f76373a6ed", + "0x0000000000000000000000000000000000000000000000018493fba64ef00000" + )); + log.setData(""); + + var result = decoder.decodeOnlyTopics(log); + assertThat(result).isNotNull(); + assertThat(result.size()).isEqualTo(2); + assertThat(result.get(1).getValue()).isEqualTo(new BigInteger("28000000000000000000")); + } + + @Test + public void decodeOnlyData_deposit() { + var result = decoder.decodeOnlyData(List.of("0x90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15"), + "0x000000000000000000000000bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c00000000000000000000000000000000000000000000000000572ecd9c5cd68d00000000000000000000000000000000000000000000000000572ecd9c5cd68d", + DEPOSIT_UNI.paramSize); + + assertThat(result).isNotNull(); + } + + @Test + public void decodeOnlyData_deposit_withTwoValueData() { + var result = decoder.decodeOnlyData(List.of("0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"), + "0x0000000000000000000000000cc0f3edcede8504e63d4fe89cd2dcc1d41ae46f000000000000000000000000000000000000000000000000016345785d8a0000", + DEPOSIT.paramSize); + + assertThat(result).isNotNull(); + } +} diff --git a/src/test/java/pro/belbix/ethparser/web3/abi/FunctionsUtilsTest.java b/src/test/java/pro/belbix/ethparser/web3/abi/FunctionsUtilsTest.java new file mode 100644 index 00000000..b12e48c2 --- /dev/null +++ b/src/test/java/pro/belbix/ethparser/web3/abi/FunctionsUtilsTest.java @@ -0,0 +1,29 @@ +package pro.belbix.ethparser.web3.abi; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import pro.belbix.ethparser.Application; + +@SpringBootTest(classes = Application.class) +@ContextConfiguration +public class FunctionsUtilsTest { + + @Autowired + FunctionsUtils functionsUtils; + + @Test + void callReserves_cyber_with_two_outputParam() { + var result = functionsUtils.callReserves("0xa0fb4487c0935f01cbf9f0274fe3cdb21a965340", null, "matic"); + assertThat(result).isNotNull(); + } + + @Test + void callReserves_uniSwap_with_three_outputParam() { + var result = functionsUtils.callReserves("0x853Ee4b2A13f8a742d64C8F088bE7bA2131f670d", null, "matic"); + assertThat(result).isNotNull(); + } + } diff --git a/src/test/java/pro/belbix/ethparser/web3/bancor/BancorPriceParserTest.java b/src/test/java/pro/belbix/ethparser/web3/bancor/BancorPriceParserTest.java index 0ca3415a..23a4fc7f 100644 --- a/src/test/java/pro/belbix/ethparser/web3/bancor/BancorPriceParserTest.java +++ b/src/test/java/pro/belbix/ethparser/web3/bancor/BancorPriceParserTest.java @@ -7,7 +7,7 @@ import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; import static pro.belbix.ethparser.web3.abi.FunctionsNames.RATE_BY_PATH; import static pro.belbix.ethparser.web3.contracts.ContractConstants.BANCOR_CONVERSION_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.BANCOR_USDC_CONVERT_PATH; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV8.BANCOR_USDC_CONVERT_PATH; import static pro.belbix.ethparser.web3.contracts.ContractConstants.D6; import static pro.belbix.ethparser.web3.contracts.ContractConstants.L18; import static pro.belbix.ethparser.web3.contracts.ContractUtils.isParsableBancorTransaction; diff --git a/src/test/java/pro/belbix/ethparser/web3/deployer/DeployerTransactionsParserTest.java b/src/test/java/pro/belbix/ethparser/web3/deployer/DeployerTransactionsParserTest.java index 80d5e988..982e4133 100644 --- a/src/test/java/pro/belbix/ethparser/web3/deployer/DeployerTransactionsParserTest.java +++ b/src/test/java/pro/belbix/ethparser/web3/deployer/DeployerTransactionsParserTest.java @@ -1,12 +1,9 @@ package pro.belbix.ethparser.web3.deployer; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static pro.belbix.ethparser.TestUtils.assertModel; import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.DEPLOYERS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.FARM_TOKEN; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.DEPLOYERS; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.FARM_TOKEN; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/src/test/java/pro/belbix/ethparser/web3/erc20/parser/TransferParserTest.java b/src/test/java/pro/belbix/ethparser/web3/erc20/parser/TransferParserTest.java index d9240543..81128d51 100644 --- a/src/test/java/pro/belbix/ethparser/web3/erc20/parser/TransferParserTest.java +++ b/src/test/java/pro/belbix/ethparser/web3/erc20/parser/TransferParserTest.java @@ -19,7 +19,7 @@ import pro.belbix.ethparser.Application; import pro.belbix.ethparser.dto.v0.TransferDTO; import pro.belbix.ethparser.web3.Web3Functions; -import pro.belbix.ethparser.web3.contracts.ContractConstants; +import pro.belbix.ethparser.web3.contracts.ContractConstantsV7; @SpringBootTest(classes = Application.class) @ContextConfiguration @@ -33,7 +33,7 @@ public class TransferParserTest { // it is a self destructed contract @Test public void testParseFARM_OneInch() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11631545, 0, "FARM", @@ -48,7 +48,7 @@ public void testParseFARM_OneInch() { @Test public void testParseFARM_firstMint() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 10776698, 0, "FARM", @@ -63,7 +63,7 @@ public void testParseFARM_firstMint() { @Test public void testParseFARM_LP_REM2() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11558046, 0, "FARM", @@ -78,7 +78,7 @@ public void testParseFARM_LP_REM2() { @Test public void testParseFARM_LP_REM() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 10931588, 0, "FARM", @@ -93,7 +93,7 @@ public void testParseFARM_LP_REM() { @Test public void testParseFARM_LP_ADD() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 10997139, 0, "FARM", @@ -108,7 +108,7 @@ public void testParseFARM_LP_ADD() { @Test public void testParseFARM_LP_BUY() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11366155, 0, "FARM", @@ -123,7 +123,7 @@ public void testParseFARM_LP_BUY() { @Test public void testParseFARM_LP_SELL() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11373041, 0, "FARM", @@ -138,7 +138,7 @@ public void testParseFARM_LP_SELL() { @Test public void testParseFARM_exit1() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11337723, 0, "FARM", @@ -153,7 +153,7 @@ public void testParseFARM_exit1() { @Test public void testParseFARM_exit2() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11337723, 1, "FARM", @@ -168,7 +168,7 @@ public void testParseFARM_exit2() { @Test public void testParseFARM_exit3() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11337723, 2, "FARM", @@ -183,7 +183,7 @@ public void testParseFARM_exit3() { @Test public void testParseFARM_exit4() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11337723, 5, "FARM", @@ -198,7 +198,7 @@ public void testParseFARM_exit4() { @Test public void testParseFARM_stake1() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11337691, 0, "FARM", @@ -213,7 +213,7 @@ public void testParseFARM_stake1() { @Test public void testParseFARM_stake2() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11337691, 1, "FARM", @@ -228,7 +228,7 @@ public void testParseFARM_stake2() { @Test public void testParseFARM_stake3() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11337691, 6, "FARM", @@ -243,7 +243,7 @@ public void testParseFARM_stake3() { @Test public void testParseFARM_balancer() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 10777054, 1, "FARM", @@ -258,7 +258,7 @@ public void testParseFARM_balancer() { @Test public void testParseFARM_bot() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 10850197, 1, "FARM", @@ -273,7 +273,7 @@ public void testParseFARM_bot() { @Test public void testParseFARM_swap() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11055960, 0, "FARM", @@ -288,7 +288,7 @@ public void testParseFARM_swap() { @Test public void testParseFARM_addLiquidity() { - TransferDTO dto = parserTest(ContractConstants.FARM_TOKEN, + TransferDTO dto = parserTest(ContractConstantsV7.FARM_TOKEN, 11362801, 1, "FARM", @@ -303,7 +303,7 @@ public void testParseFARM_addLiquidity() { @Test public void testParseFARM_transfer() { - parserTest(ContractConstants.FARM_TOKEN, + parserTest(ContractConstantsV7.FARM_TOKEN, 11571359, 0, "FARM", @@ -317,7 +317,7 @@ public void testParseFARM_transfer() { @Test public void testParseFARM_HARD_WORK() { - parserTest(ContractConstants.FARM_TOKEN, + parserTest(ContractConstantsV7.FARM_TOKEN, 11045532, 0, "FARM", @@ -331,7 +331,7 @@ public void testParseFARM_HARD_WORK() { @Test public void testParseFARM_HARD_WORK2() { - parserTest(ContractConstants.FARM_TOKEN, + parserTest(ContractConstantsV7.FARM_TOKEN, 11045532, 1, "FARM", @@ -345,7 +345,7 @@ public void testParseFARM_HARD_WORK2() { @Test public void testParseFARM_swapExactTokensForETH() { - parserTest(ContractConstants.FARM_TOKEN, + parserTest(ContractConstantsV7.FARM_TOKEN, 10777107, 0, "FARM", diff --git a/src/test/java/pro/belbix/ethparser/web3/harvest/parser/ImportantEventsParserTest.java b/src/test/java/pro/belbix/ethparser/web3/harvest/parser/ImportantEventsParserTest.java index d0a7b7bc..b5a2e12d 100644 --- a/src/test/java/pro/belbix/ethparser/web3/harvest/parser/ImportantEventsParserTest.java +++ b/src/test/java/pro/belbix/ethparser/web3/harvest/parser/ImportantEventsParserTest.java @@ -19,7 +19,7 @@ import pro.belbix.ethparser.Application; import pro.belbix.ethparser.dto.v0.ImportantEventsDTO; import pro.belbix.ethparser.web3.Web3Functions; -import pro.belbix.ethparser.web3.contracts.ContractConstants; +import pro.belbix.ethparser.web3.contracts.ContractConstantsV7; @SpringBootTest(classes = Application.class) @ContextConfiguration @@ -59,7 +59,7 @@ public void shouldParseStrategyAnnounce() { @Test public void shouldParseMint() { parserTest( - ContractConstants.FARM_TOKEN, + ContractConstantsV7.FARM_TOKEN, 10776715, 0, "null", diff --git a/src/test/java/pro/belbix/ethparser/web3/harvest/vault/VaultActionsParserEthTest.java b/src/test/java/pro/belbix/ethparser/web3/harvest/vault/VaultActionsParserEthTest.java index 9e33ff4a..6ce722cc 100644 --- a/src/test/java/pro/belbix/ethparser/web3/harvest/vault/VaultActionsParserEthTest.java +++ b/src/test/java/pro/belbix/ethparser/web3/harvest/vault/VaultActionsParserEthTest.java @@ -10,7 +10,7 @@ import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_ADDRESS; import static pro.belbix.ethparser.web3.contracts.ContractConstants.PS_V0_ADDRESS; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.iPS_ADDRESS; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.iPS_ADDRESS; import java.util.List; import org.junit.jupiter.api.Disabled; diff --git a/src/test/java/pro/belbix/ethparser/web3/layers/detector/EthContractDetectorTest.java b/src/test/java/pro/belbix/ethparser/web3/layers/detector/EthContractDetectorTest.java index d68f7683..2ef55147 100644 --- a/src/test/java/pro/belbix/ethparser/web3/layers/detector/EthContractDetectorTest.java +++ b/src/test/java/pro/belbix/ethparser/web3/layers/detector/EthContractDetectorTest.java @@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static pro.belbix.ethparser.TestUtils.assertTwoArrays; import static pro.belbix.ethparser.service.AbiProviderService.ETH_NETWORK; -import static pro.belbix.ethparser.web3.contracts.ContractConstants.FARM_TOKEN; +import static pro.belbix.ethparser.web3.contracts.ContractConstantsV7.FARM_TOKEN; import com.fasterxml.jackson.core.JsonProcessingException; import java.util.ArrayList; diff --git a/src/test/resources/__files/transaction-empty-response.json b/src/test/resources/__files/transaction-empty-response.json new file mode 100644 index 00000000..2a8cbc18 --- /dev/null +++ b/src/test/resources/__files/transaction-empty-response.json @@ -0,0 +1,11 @@ +{ + "data": { + "address": "0x2fee56e039acccefa3cb1f3051ad00fe550a472c", + "updated_at": "2022-02-09T13:31:25.293424937Z", + "next_update_at": "2022-02-09T13:36:25.293425537Z", + "quote_currency": "USD", + "chain_id": 137, + "items": [ + ] + } +} \ No newline at end of file diff --git a/src/test/resources/__files/transaction-simple-response.json b/src/test/resources/__files/transaction-simple-response.json new file mode 100644 index 00000000..ca5f8e1d --- /dev/null +++ b/src/test/resources/__files/transaction-simple-response.json @@ -0,0 +1,52 @@ +{ + "data": { + "address": "0x2fee56e039acccefa3cb1f3051ad00fe550a472c", + "updated_at": "2022-02-09T13:31:25.293424937Z", + "next_update_at": "2022-02-09T13:36:25.293425537Z", + "quote_currency": "USD", + "chain_id": 137, + "items": [ + { + "block_signed_at": "2021-08-16T22:31:56Z", + "block_height": 111111, + "tx_hash": "0x4c60c231cdac354061b9c78b587f67b037636655ffcf1fb527422c2db531d818", + "tx_offset": 173, + "successful": true, + "from_address": "0x36ec37f30459eaa6347a8d5b052c559d131f4c41", + "from_address_label": null, + "to_address": "0x8ae127d224094cb1b27e1b28a472e588cbcc7620", + "to_address_label": null, + "value": "0", + "value_quote": 0.0, + "gas_offered": 51675, + "gas_spent": 51675, + "gas_price": 1000000001, + "gas_quote": 7.534433406675268E-5, + "gas_quote_rate": 1.4580422639846802, + "log_events": [ + { + "block_signed_at": "2021-08-16T22:31:56Z", + "block_height": 111111, + "tx_offset": 173, + "log_offset": 380, + "tx_hash": "0x4c60c231cdac354061b9c78b587f67b037636655ffcf1fb527422c2db531d818", + "raw_log_topics": [ + "0x4dfe1bbbcf077ddc3e01291eea2d5c70c2b422b415d95645b9adcfd678cb1d63", + "0x0000000000000000000000000000000000000000000000000000000000001010", + "0x00000000000000000000000036ec37f30459eaa6347a8d5b052c559d131f4c41", + "0x000000000000000000000000e7e2cb8c81c10ff191a73fe266788c9ce62ec754" + ], + "sender_contract_decimals": 18, + "sender_name": "Matic Token", + "sender_contract_ticker_symbol": "MATIC", + "sender_address": "0x0000000000000000000000000000000000001010", + "sender_address_label": null, + "sender_logo_url": "https://logos.covalenthq.com/tokens/0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0.png", + "raw_log_data": "0x00000000000000000000000000000000000000000000000000002eff860597db00000000000000000000000000000000000000000000000198ecc30b0c3841ea000000000000000000000000000000000000000000000ac6dd8b15f654a4b5c400000000000000000000000000000000000000000000000198ec940b8632aa0f000000000000000000000000000000000000000000000ac6dd8b44f5daaa4d9f", + "decoded": null + } + ] + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/__files/transaction-with-transfer-response.json b/src/test/resources/__files/transaction-with-transfer-response.json new file mode 100644 index 00000000..8b158d17 --- /dev/null +++ b/src/test/resources/__files/transaction-with-transfer-response.json @@ -0,0 +1,78 @@ +{ + "data": { + "address": "0x2fee56e039acccefa3cb1f3051ad00fe550a472c", + "updated_at": "2022-02-09T13:31:25.293424937Z", + "next_update_at": "2022-02-09T13:36:25.293425537Z", + "quote_currency": "USD", + "chain_id": 137, + "items": [ + { + "block_signed_at": "2021-08-16T22:31:56Z", + "block_height": 111111, + "tx_hash": "0x4c60c231cdac354061b9c78b587f67b037636655ffcf1fb527422c2db531d818", + "tx_offset": 173, + "successful": true, + "from_address": "0x36ec37f30459eaa6347a8d5b052c559d131f4c41", + "from_address_label": null, + "to_address": "0x8ae127d224094cb1b27e1b28a472e588cbcc7620", + "to_address_label": null, + "value": "0", + "value_quote": 0.0, + "gas_offered": 51675, + "gas_spent": 51675, + "gas_price": 1000000001, + "gas_quote": 7.534433406675268E-5, + "gas_quote_rate": 1.4580422639846802, + "log_events": [ + { + "block_signed_at": "2021-08-16T22:31:56Z", + "block_height": 111111, + "tx_offset": 173, + "log_offset": 380, + "tx_hash": "0x4c60c231cdac354061b9c78b587f67b037636655ffcf1fb527422c2db531d818", + "raw_log_topics": [ + "0x4dfe1bbbcf077ddc3e01291eea2d5c70c2b422b415d95645b9adcfd678cb1d63", + "0x0000000000000000000000000000000000000000000000000000000000001010", + "0x00000000000000000000000036ec37f30459eaa6347a8d5b052c559d131f4c41", + "0x000000000000000000000000e7e2cb8c81c10ff191a73fe266788c9ce62ec754" + ], + "sender_contract_decimals": 18, + "sender_name": "Matic Token", + "sender_contract_ticker_symbol": "MATIC", + "sender_address": "0x0000000000000000000000000000000000001010", + "sender_address_label": null, + "sender_logo_url": "https://logos.covalenthq.com/tokens/0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0.png", + "raw_log_data": "0x00000000000000000000000000000000000000000000000000002eff860597db00000000000000000000000000000000000000000000000198ecc30b0c3841ea000000000000000000000000000000000000000000000ac6dd8b15f654a4b5c400000000000000000000000000000000000000000000000198ec940b8632aa0f000000000000000000000000000000000000000000000ac6dd8b44f5daaa4d9f", + "decoded": { + "name": "Transfer", + "signature": "Transfer(indexed address from, indexed address to, uint256 value)", + "params": [ + { + "name": "from", + "type": "address", + "indexed": true, + "decoded": true, + "value": "0x36ec37f30459eaa6347a8d5b052c559d131f4c41" + }, + { + "name": "to", + "type": "address", + "indexed": true, + "decoded": true, + "value": "0x2fee56e039acccefa3cb1f3051ad00fe550a472c" + }, + { + "name": "value", + "type": "uint256", + "indexed": false, + "decoded": true, + "value": "26347543096503948200000000000" + } + ] + } + } + ] + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 04d95696..ace194c9 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -47,3 +47,23 @@ logging: level: com.yannbriancon.interceptor.HibernateQueryInterceptor: OFF # pro.belbix: debug + +# only for m1 Mac users +wiremock: + server: + httpsPort: -1 + +task: + pool: + fixedRate: 3600000 # Each hour + enable: false + vault: + fixedRate: 3600000 # Each hour + enable: false + uni-pair: + fixedRate: 86400000 # Everyday + enable: false + transaction: + max-thread-size: 30 + fixedRate: 86400000 # Everyday + enable: false diff --git a/src/test/resources/sql/1_eth_contracts.sql b/src/test/resources/sql/1_eth_contracts.sql index e1cf11bd..57e49c50 100644 --- a/src/test/resources/sql/1_eth_contracts.sql +++ b/src/test/resources/sql/1_eth_contracts.sql @@ -1140,4 +1140,12 @@ INSERT INTO public.eth_contracts (id, address, created, name, network, type, un INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10067, '0xe506e7c500fe6e094c37682dd7ed847306d0bccb', 16572694, 'UNI_LP_WBTC_miFARM', 'matic', 2, null, 16645038, 1625597732, 1625761021); INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10068, '0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6', 16572694, 'WBTC', 'matic', 4, null, 16645038, 1625597732, 1625761021); INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10069, '0xf6a637525402643b0654a54bead2cb9a83c8b498', 16572694, 'UNI_LP_WBTC_USDC', 'matic', 2, null, 16645038, 1625597732, 1625761021); -INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10109, '0x0392f36d2896c966e141c8fd9eca58a7ca9fa8af', 16657130, 'S_UNKNOWN_NAME_#V1', 'matic', 5, null, null, 1625795083, null); \ No newline at end of file +INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10109, '0x0392f36d2896c966e141c8fd9eca58a7ca9fa8af', 16657130, 'S_UNKNOWN_NAME_#V1', 'matic', 5, null, null, 1625795083, null); +INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10443, '0xe7c9d242137896741b70cefef701bbb4dcb158ec', 21718484, 'V_USDC_#V2', 'matic', 0, null, 22597799, 1637697287, 1639671802); +INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10445, '0xea4eca0fbeb0fcaae61d2fa2b2877d435faeee1c', 21718504, 'P_USDC', 'matic', 1, null, 21985528, 1637697327, 1638302127); +INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10452, '0xebafc813f66c3142e7993a88ee3361a1f4bdab16', 0, 'CONTROLLER_#V2', 'matic', 3, null, null, 1590824836, null); +INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10402, '0x7f316d2498a234670554d1a890a364b0bd9d88e2', 20625022, 'V_KyberDMMLPUSDC-jCHF_USDC_jCHF_#V1', 'matic', 0, null, 22250517, 1635236145, 1638903565); +INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10444, '0x08043b010ad2cd72db75878201fde4b03e28e069', 21718489, 'S_USDC_#V2', 'matic', 5, null, null, 1637697297, null); +INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10446, '0x9048a123ae6c86a2b9fdc3ea28ed1911de8363a6', 21718513, 'V_WETH', 'matic', 0, null, 22598271, 1637697345, 1639672774); +INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10447, '0x7ad709df8b897b3b623c5413e92a6f70ca563e1d', 21718518, 'S_WETH', 'matic', 5, null, null, 1637697355, null); +INSERT INTO public.eth_contracts (id, address, created, name, network, type, underlying, updated, created_date, updated_date) VALUES (10448, '0xa0fb4487c0935f01cbf9f0274fe3cdb21a965340', 21718518, 'KyberDMM LP USDC-AUR-0112', 'matic', 2, null, 16645038, 1625597732, 1625761021); diff --git a/src/test/resources/sql/3_eth_vaults.sql b/src/test/resources/sql/3_eth_vaults.sql index b96c51c6..2184458f 100644 --- a/src/test/resources/sql/3_eth_vaults.sql +++ b/src/test/resources/sql/3_eth_vaults.sql @@ -151,4 +151,6 @@ INSERT INTO public.eth_vaults (id, decimals, name, symbol, underlying_unit, upda INSERT INTO public.eth_vaults (id, decimals, name, symbol, underlying_unit, updated_block, contract, controller_id, governance_id, strategy_id, underlying_id) VALUES (10006, 18, 'miFARM_UNI-V2', 'bfUNI-V2', 1000000000000000000, 16644916, 10070, 10071, 10058, null, 10072); INSERT INTO public.eth_vaults (id, decimals, name, symbol, underlying_unit, updated_block, contract, controller_id, governance_id, strategy_id, underlying_id) VALUES (10007, 18, 'miFARM_UNI-V2', 'bfUNI-V2', 1000000000000000000, 16644946, 10077, 10071, 10058, null, 10059); INSERT INTO public.eth_vaults (id, decimals, name, symbol, underlying_unit, updated_block, contract, controller_id, governance_id, strategy_id, underlying_id) VALUES (10039, 18, 'miFARM_SLP', 'bfSLP', 1000000000000000000, 16644978, 10103, 10071, 10058, null, 10104); -INSERT INTO public.eth_vaults (id, decimals, name, symbol, underlying_unit, updated_block, contract, controller_id, governance_id, strategy_id, underlying_id) VALUES (10040, 18, 'miFARM_SLP', 'bfSLP', 1000000000000000000, 16657148, 10105, 10071, 10058, 10107, 10104); \ No newline at end of file +INSERT INTO public.eth_vaults (id, decimals, name, symbol, underlying_unit, updated_block, contract, controller_id, governance_id, strategy_id, underlying_id) VALUES (10040, 18, 'miFARM_SLP', 'bfSLP', 1000000000000000000, 16657148, 10105, 10071, 10058, 10107, 10104); +INSERT INTO public.eth_vaults (id, decimals, name, symbol, underlying_unit, updated_block, contract, controller_id, governance_id, strategy_id, underlying_id) VALUES (10050, 6, 'miFARM_USDC', 'bfUSDC', 1000000, 22597799, 10443, 10452, 10058, 10444, 10063); +INSERT INTO public.eth_vaults (id, decimals, name, symbol, underlying_unit, updated_block, contract, controller_id, governance_id, strategy_id, underlying_id) VALUES (10051, 18, 'miFARM_WETH', 'bfWETH', 1000000000000000000, 22598271, 10446, 10452, 10058, 10447, 10060); diff --git a/src/test/resources/sql/4_eth_pools.sql b/src/test/resources/sql/4_eth_pools.sql index 122505fd..d9772d4d 100644 --- a/src/test/resources/sql/4_eth_pools.sql +++ b/src/test/resources/sql/4_eth_pools.sql @@ -190,4 +190,5 @@ INSERT INTO public.eth_pools (id, updated_block, contract, controller, governanc INSERT INTO public.eth_pools (id, updated_block, contract, controller, governance, lp_token, owner, reward_token) VALUES (10005, 16572694, 10065, 10057, 10058, 10056, 10058, 10066); INSERT INTO public.eth_pools (id, updated_block, contract, controller, governance, lp_token, owner, reward_token) VALUES (10006, 16644936, 10076, 10071, 10058, 10070, 10058, 10066); INSERT INTO public.eth_pools (id, updated_block, contract, controller, governance, lp_token, owner, reward_token) VALUES (10007, 16644965, 10079, 10071, 10058, 10077, 10058, 10066); -INSERT INTO public.eth_pools (id, updated_block, contract, controller, governance, lp_token, owner, reward_token) VALUES (10039, 16645038, 10108, 10071, 10058, 10105, 10058, 10066); \ No newline at end of file +INSERT INTO public.eth_pools (id, updated_block, contract, controller, governance, lp_token, owner, reward_token) VALUES (10039, 16645038, 10108, 10071, 10058, 10105, 10058, 10066); +INSERT INTO public.eth_pools (id, updated_block, contract, controller, governance, lp_token, owner, reward_token) VALUES (10077, 21985528, 10445, 10452, 10058, 10443, 10058, 10066); diff --git a/src/test/resources/sql/5_eth_uni_pairs.sql b/src/test/resources/sql/5_eth_uni_pairs.sql index 74f28eea..f6b81fb2 100644 --- a/src/test/resources/sql/5_eth_uni_pairs.sql +++ b/src/test/resources/sql/5_eth_uni_pairs.sql @@ -152,3 +152,4 @@ INSERT INTO public.eth_uni_pairs (id, decimals, type, updated_block, contract, t INSERT INTO public.eth_uni_pairs (id, decimals, type, updated_block, contract, token0_id, token1_id) VALUES (10010, 18, 0, 16645038, 10069, 10068, 10063); INSERT INTO public.eth_uni_pairs (id, decimals, type, updated_block, contract, token0_id, token1_id) VALUES (10044, 18, 0, 16657148, 10104, 10063, 10060); INSERT INTO public.eth_uni_pairs (id, decimals, type, updated_block, contract, token0_id, token1_id) VALUES (10008, 18, 0, 16657148, 10062, 10063, 10060); +INSERT INTO public.eth_uni_pairs (id, decimals, type, updated_block, contract, token0_id, token1_id) VALUES (10045, 18, 0, 16657148, 10448, 10063, 10060); diff --git a/src/test/resources/sql/harvest.sql b/src/test/resources/sql/harvest.sql index 06f73cd9..de3ab105 100644 --- a/src/test/resources/sql/harvest.sql +++ b/src/test/resources/sql/harvest.sql @@ -93,3 +93,6 @@ VALUES ('0x3457c2b58d728db60d8d4e9fa4931d1819947614917983e5a8e7e9a215095216_154' INSERT INTO public.harvest_tx (id, all_owners_count, all_pools_owners_count, amount, amount_in, block, block_date, confirmed, hash, last_all_usd_tvl, last_gas, last_tvl, last_usd_tvl, lp_stat, method_name, migrated, owner, owner_balance, owner_balance_usd, owner_count, prices, profit, profit_usd, share_price, total_amount, underlying_price, usd_amount, vault, network, vault_address, underlying_address) VALUES ('0x46571abfbf3368e27318f42e14e26384a71f986806fd65daca9b213e3b077d2a_242', 5, 3, 0.000000031272302486, null, 18493981, 1630172828, 1, '0x46571abfbf3368e27318f42e14e26384a71f986806fd65daca9b213e3b077d2a', null, 6.326405532, 0.004576514740784796, 579217, '{"coin1":"USDC","coin1Address":"0x2791bca1f2de4661ed88a30c99a7a9449aa84174","coin2":"WETH","coin2Address":"0x7ceb23fd6bc0add59e62ac25578270cff1b9f619","amount1":289528.1661267551,"amount2":89.13372847530734,"price1":1.0,"price2":3250.052845311043}', 'Deposit', false, '0x5874e4ea2f891ad337faee094363bf5d7afcc61a', 0.000000031613611839, 4.001113961586612, 0, null, null, null, 1.0109141101279588, null, null, 4, 'V_SUSHI_USDC_WETH', 'matic', '0xf76a0c5083b895c76ecbf30121f036849137d545', null); INSERT INTO public.harvest_tx (id, all_owners_count, all_pools_owners_count, amount, amount_in, block, block_date, confirmed, hash, last_all_usd_tvl, last_gas, last_tvl, last_usd_tvl, lp_stat, method_name, migrated, owner, owner_balance, owner_balance_usd, owner_count, prices, profit, profit_usd, share_price, total_amount, underlying_price, usd_amount, vault, network, vault_address, underlying_address) VALUES ('0xb140f7e195b4e4a75a5b97d8a665c22511bf01de20a09ee3d72a3c4ba15d5b0e_778', 6, 3, 0.000000000000015633, null, 18494511, 1630173920, 1, '0xb140f7e195b4e4a75a5b97d8a665c22511bf01de20a09ee3d72a3c4ba15d5b0e', null, 7.02231014, 0.004576514740800599, 579052, '{"coin1":"USDC","coin1Address":"0x2791bca1f2de4661ed88a30c99a7a9449aa84174","coin2":"WETH","coin2Address":"0x7ceb23fd6bc0add59e62ac25578270cff1b9f619","amount1":289578.6683028402,"amount2":89.11838503490969,"price1":1.0,"price2":3248.19017054529}', 'Deposit', false, '0x747276019e3340104c96397bf6537ad01f93d7df', 0.000000000000015803, 0.0000019995042817414836, 0, null, null, null, 1.0109141101279588, null, null, 0, 'V_SUSHI_USDC_WETH', 'matic', '0xf76a0c5083b895c76ecbf30121f036849137d545', null); +INSERT INTO public.harvest_tx (id, all_owners_count, all_pools_owners_count, amount, amount_in, block, block_date, confirmed, hash, last_all_usd_tvl, last_gas, last_tvl, last_usd_tvl, lp_stat, method_name, migrated, owner, owner_balance, owner_balance_usd, owner_count, prices, profit, profit_usd, share_price, total_amount, underlying_price, usd_amount, vault, network, vault_address, underlying_address) VALUES ('0x625a3ee376fb2d2eba5db5f23364594cd87a09f35a6510e5f2274ce48a897db3_767', 1943, 156, 0.00000002465398013, null, 22478088, 1639403843, 1, '0x625a3ee376fb2d2eba5db5f23364594cd87a09f35a6510e5f2274ce48a897db3', null, 30, 0.0007538475609659471, 111093, '{"coin1":"USDC","coin1Address":"0x2791bca1f2de4661ed88a30c99a7a9449aa84174","coin2":"WETH","coin2Address":"0x7ceb23fd6bc0add59e62ac25578270cff1b9f619","amount1":55515.9914631261,"amount2":13.960744455081848,"price1":1.0,"price2":3980.9778353221886}', 'Deposit', false, '0xf1b85414473eccd659deeacee347bbc17171dd32', 0.000000025799754059, 3.8020717889594717, 23, null, null, null, 1.0464741970020397, null, null, 4, 'V_SUSHI_USDC_WETH_#V1', 'matic', '0xf76a0c5083b895c76ecbf30121f036849137d545', null); +INSERT INTO public.harvest_tx (id, all_owners_count, all_pools_owners_count, amount, amount_in, block, block_date, confirmed, hash, last_all_usd_tvl, last_gas, last_tvl, last_usd_tvl, lp_stat, method_name, migrated, owner, owner_balance, owner_balance_usd, owner_count, prices, profit, profit_usd, share_price, total_amount, underlying_price, usd_amount, vault, network, vault_address, underlying_address) VALUES ('0x4e4149e769317c169f3e8e5c3b28e3ec4818c3d4e8010f79c0ff939236d5c849_203', 2006, 161, 0.998017, null, 22636874, 1639756701, 1, '0x4e4149e769317c169f3e8e5c3b28e3ec4818c3d4e8010f79c0ff939236d5c849', null, 30, 531408.6788976708, 531409, null, 'Deposit', false, '0xf1b85414473eccd659deeacee347bbc17171dd32', 0.999999, 0.999999, 38, null, null, null, 1.001986, null, 1, 0, 'V_USDC_#V2', 'matic', '0xe7c9d242137896741b70cefef701bbb4dcb158ec', '0x2791bca1f2de4661ed88a30c99a7a9449aa84174'); +INSERT INTO public.harvest_tx (id, all_owners_count, all_pools_owners_count, amount, amount_in, block, block_date, confirmed, hash, last_all_usd_tvl, last_gas, last_tvl, last_usd_tvl, lp_stat, method_name, migrated, owner, owner_balance, owner_balance_usd, owner_count, prices, profit, profit_usd, share_price, total_amount, underlying_price, usd_amount, vault, network, vault_address, underlying_address) VALUES ('0x27201914f0e4f107709684b57f9e60e660207fbf97e0b748ba9f2e9d05c610af_655', 2024, 166, 0.498892, null, 22750126, 1640005921, 1, '0x27201914f0e4f107709684b57f9e60e660207fbf97e0b748ba9f2e9d05c610af', null, 1.5972011, 979375.2934887275, 979375, null, 'Deposit', false, '0xf1b85414473eccd659deeacee347bbc17171dd32', 0.499999, 0.499999, 43, null, null, null, 1.002219, null, 1, 0, 'V_USDC_#V2', 'matic', '0xe7c9d242137896741b70cefef701bbb4dcb158ec', '0x2791bca1f2de4661ed88a30c99a7a9449aa84174');